Skip to content

Commit 4ec00b9

Browse files
author
Roger
committed
stream photos back-to-back on long press
1 parent 06e7dfb commit 4ec00b9

File tree

2 files changed

+44
-18
lines changed

2 files changed

+44
-18
lines changed

bun.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { AppServer, AppSession, ViewType, AuthenticatedRequest, PhotoData } from '@mentra/sdk';
22
import { Request, Response } from 'express';
3+
import * as ejs from 'ejs';
4+
import * as path from 'path';
35

46
/**
57
* Interface representing a stored photo with metadata
@@ -26,6 +28,7 @@ class ExampleMentraOSApp extends AppServer {
2628
private photos: Map<string, StoredPhoto> = new Map(); // Store photos by userId
2729
private latestPhotoTimestamp: Map<string, number> = new Map(); // Track latest photo timestamp per user
2830
private isStreamingPhotos: Map<string, boolean> = new Map(); // Track if we are streaming photos for a user
31+
private nextPhotoTime: Map<string, number> = new Map(); // Track next photo time for a user
2932

3033
constructor() {
3134
super({
@@ -39,16 +42,11 @@ class ExampleMentraOSApp extends AppServer {
3942
}
4043

4144
/**
42-
* Configure EJS as the view engine for Express
45+
* Configure EJS for manual template rendering
4346
*/
4447
private setupEJS(): void {
45-
const app = this.getExpressApp();
46-
47-
// Set EJS as the view engine
48-
app.set('view engine', 'ejs');
49-
50-
// Set the views directory
51-
app.set('views', './views');
48+
// EJS will be used manually to avoid module resolution issues
49+
// No Express view engine configuration needed
5250
}
5351

5452
/**
@@ -58,7 +56,7 @@ class ExampleMentraOSApp extends AppServer {
5856
const app = this.getExpressApp();
5957

6058
// Main webview route - displays the photo viewer interface
61-
app.get('/webview', (req: any, res: any) => {
59+
app.get('/webview', async (req: any, res: any) => {
6260
const userId = (req as AuthenticatedRequest).authUserId;
6361

6462
if (!userId) {
@@ -73,8 +71,23 @@ class ExampleMentraOSApp extends AppServer {
7371
return;
7472
}
7573

76-
// Render the photo viewer EJS template
77-
res.render('photo-viewer');
74+
try {
75+
// Manually render the EJS template to avoid module resolution issues
76+
const templatePath = path.join(process.cwd(), 'views', 'photo-viewer.ejs');
77+
const html = await ejs.renderFile(templatePath, {});
78+
res.send(html);
79+
} catch (error) {
80+
this.logger.error(`Error rendering EJS template: ${error}`);
81+
res.status(500).send(`
82+
<html>
83+
<head><title>Photo Viewer - Error</title></head>
84+
<body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
85+
<h1>Error loading photo viewer</h1>
86+
<p>Please try refreshing the page</p>
87+
</body>
88+
</html>
89+
`);
90+
}
7891
});
7992

8093
// API endpoint to get the latest photo for the authenticated user
@@ -131,6 +144,7 @@ class ExampleMentraOSApp extends AppServer {
131144
protected async onSession(session: AppSession, sessionId: string, userId: string): Promise<void> {
132145
this.logger.info(`Session started for user ${userId}`);
133146
this.isStreamingPhotos.set(userId, false);
147+
this.nextPhotoTime.set(userId, Date.now());
134148

135149
session.events.onButtonPress(async (button) => {
136150
this.logger.info(`Button pressed: ${button.buttonId}, type: ${button.pressType}`);
@@ -140,25 +154,37 @@ class ExampleMentraOSApp extends AppServer {
140154
this.logger.info(`Streaming photos for user ${userId} is now ${this.isStreamingPhotos.get(userId)}`);
141155
return;
142156
} else {
157+
try {
143158
const photoRequest = session.camera.requestPhoto();
144159
photoRequest.catch((error) => this.logger.error(`Error taking photo: ${error}`));
145160
photoRequest.then((photo) => {
146-
this.logger.info(`Photo taken for user ${userId}, timestamp: ${photo.timestamp}`);
147-
this.cachePhoto(photo, userId);
148-
});
161+
this.logger.info(`Photo taken for user ${userId}, timestamp: ${photo.timestamp}`);
162+
this.cachePhoto(photo, userId);
163+
});
164+
} catch (error) {
165+
this.logger.error(`Error taking photo: ${error}`);
166+
}
149167
}
150168
});
151169

152170
setInterval(async () => {
153-
if (this.isStreamingPhotos.get(userId)) {
171+
if (this.isStreamingPhotos.get(userId) && Date.now() > (this.nextPhotoTime.get(userId) ?? 0)) {
154172
try {
173+
this.nextPhotoTime.set(userId, Date.now() + 30000);
155174
const photo = await session.camera.requestPhoto();
175+
this.nextPhotoTime.set(userId, Date.now()-1);
156176
this.cachePhoto(photo, userId);
157177
} catch (error) {
158178
this.logger.error(`Error auto-taking photo: ${error}`);
159179
}
160180
}
161-
}, 1000);
181+
}, 2000);
182+
}
183+
184+
protected async onStop(sessionId: string, userId: string, reason: string): Promise<void> {
185+
this.isStreamingPhotos.set(userId, false);
186+
this.nextPhotoTime.delete(userId);
187+
this.logger.info(`Session stopped for user ${userId}, reason: ${reason}`);
162188
}
163189

164190
private async cachePhoto(photo: PhotoData, userId: string) {

0 commit comments

Comments
 (0)