@effing/create 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/app/routes/pff.$effieId.tsx +31 -12
- package/template/package.json +8 -8
- package/template/public/sw.js +18 -12
package/package.json
CHANGED
|
@@ -42,7 +42,7 @@ const RENDER_SCALES = [
|
|
|
42
42
|
|
|
43
43
|
type RenderSuccess = {
|
|
44
44
|
renderSuccess: true;
|
|
45
|
-
|
|
45
|
+
progressUrl: string;
|
|
46
46
|
renderScale: number;
|
|
47
47
|
};
|
|
48
48
|
type RenderError = {
|
|
@@ -113,7 +113,7 @@ export async function loader({ request, params }: Route.LoaderArgs) {
|
|
|
113
113
|
|
|
114
114
|
if (warmupResponse.ok) {
|
|
115
115
|
const warmupData = await warmupResponse.json();
|
|
116
|
-
warmupStreamUrl = warmupData.
|
|
116
|
+
warmupStreamUrl = warmupData.progressUrl;
|
|
117
117
|
}
|
|
118
118
|
} catch {
|
|
119
119
|
// Warmup is best-effort, don't fail the page load
|
|
@@ -212,8 +212,8 @@ async function handleRender(
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
const {
|
|
216
|
-
return { renderSuccess: true, renderScale: scale,
|
|
215
|
+
const { progressUrl } = await createResponse.json();
|
|
216
|
+
return { renderSuccess: true, renderScale: scale, progressUrl };
|
|
217
217
|
} catch (err) {
|
|
218
218
|
return {
|
|
219
219
|
renderSuccess: false,
|
|
@@ -274,15 +274,34 @@ export default function EffiePreviewPage() {
|
|
|
274
274
|
return () => clearInterval(interval);
|
|
275
275
|
}, [render.startTime, render.playbackTime]);
|
|
276
276
|
|
|
277
|
-
//
|
|
277
|
+
// Connect to SSE progress when render action completes
|
|
278
278
|
useEffect(() => {
|
|
279
|
-
if (actionData
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
279
|
+
if (!actionData || !isRenderSuccess(actionData)) return;
|
|
280
|
+
|
|
281
|
+
const { progressUrl, renderScale } = actionData;
|
|
282
|
+
const eventSource = new EventSource(progressUrl);
|
|
283
|
+
|
|
284
|
+
eventSource.addEventListener("ready", (e) => {
|
|
285
|
+
try {
|
|
286
|
+
const { videoUrl } = JSON.parse(e.data);
|
|
287
|
+
setRender((prev) => ({
|
|
288
|
+
...prev,
|
|
289
|
+
videoUrl,
|
|
290
|
+
scale: renderScale,
|
|
291
|
+
}));
|
|
292
|
+
} catch {
|
|
293
|
+
// Ignore parse errors
|
|
294
|
+
}
|
|
295
|
+
eventSource.close();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
eventSource.addEventListener("error", () => {
|
|
299
|
+
eventSource.close();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
return () => {
|
|
303
|
+
eventSource.close();
|
|
304
|
+
};
|
|
286
305
|
}, [actionData]);
|
|
287
306
|
|
|
288
307
|
const warmup = useEffieWarmup(warmupStreamUrl);
|
package/template/package.json
CHANGED
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
"typecheck": "react-router typegen && tsc"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@effing/annie": "^0.
|
|
15
|
-
"@effing/annie-player": "^0.
|
|
16
|
-
"@effing/effie": "^0.
|
|
17
|
-
"@effing/effie-preview": "^0.
|
|
18
|
-
"@effing/ffs": "^0.
|
|
19
|
-
"@effing/satori": "^0.
|
|
20
|
-
"@effing/serde": "^0.
|
|
21
|
-
"@effing/tween": "^0.
|
|
14
|
+
"@effing/annie": "^0.7.1",
|
|
15
|
+
"@effing/annie-player": "^0.7.1",
|
|
16
|
+
"@effing/effie": "^0.7.1",
|
|
17
|
+
"@effing/effie-preview": "^0.7.1",
|
|
18
|
+
"@effing/ffs": "^0.7.1",
|
|
19
|
+
"@effing/satori": "^0.7.1",
|
|
20
|
+
"@effing/serde": "^0.7.1",
|
|
21
|
+
"@effing/tween": "^0.7.1",
|
|
22
22
|
"@react-router/node": "^7.0.0",
|
|
23
23
|
"@react-router/serve": "^7.0.0",
|
|
24
24
|
"cross-env": "^7.0.3",
|
package/template/public/sw.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
/* global clients */
|
|
2
2
|
|
|
3
3
|
// FFS render URLs (GET /render/{uuid}) are one-time-consumption — the server
|
|
4
|
-
// deletes the job after streaming the response. However,
|
|
5
|
-
// requests to the same URL (e.g. to re-read the moov atom for seeking),
|
|
6
|
-
// return 404 and cause the video to
|
|
7
|
-
//
|
|
4
|
+
// deletes the job after streaming the response. However, browsers might make
|
|
5
|
+
// follow-up requests to the same URL (e.g. to re-read the moov atom for seeking),
|
|
6
|
+
// which return 404 and cause the video to break. This service worker caches the
|
|
7
|
+
// response so those subsequent requests are served from cache.
|
|
8
8
|
|
|
9
9
|
const CACHE_NAME = "ffs-render-cache";
|
|
10
|
-
const RENDER_URL_PATTERN = /\/render\/[0-9a-f-]{36}$/;
|
|
10
|
+
const RENDER_URL_PATTERN = /\/render\/[0-9a-f-]{36}\/video$/;
|
|
11
11
|
const MAX_ENTRIES = 5;
|
|
12
12
|
|
|
13
|
+
/** @type {string[]} URLs in insertion order — reset on activate alongside the cache. */
|
|
14
|
+
const cacheOrder = [];
|
|
15
|
+
|
|
13
16
|
self.addEventListener("activate", (event) => {
|
|
17
|
+
cacheOrder.length = 0;
|
|
14
18
|
event.waitUntil(caches.delete(CACHE_NAME).then(() => clients.claim()));
|
|
15
19
|
});
|
|
16
20
|
|
|
@@ -25,14 +29,16 @@ self.addEventListener("fetch", (event) => {
|
|
|
25
29
|
const response = await fetch(event.request);
|
|
26
30
|
if (response.ok) {
|
|
27
31
|
await cache.put(event.request, response.clone());
|
|
32
|
+
cacheOrder.push(event.request.url);
|
|
28
33
|
|
|
29
|
-
// Prune oldest entries
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// Prune oldest entries in the background — don't block the response.
|
|
35
|
+
if (cacheOrder.length > MAX_ENTRIES) {
|
|
36
|
+
const toDelete = cacheOrder.splice(
|
|
37
|
+
0,
|
|
38
|
+
cacheOrder.length - MAX_ENTRIES,
|
|
39
|
+
);
|
|
40
|
+
event.waitUntil(
|
|
41
|
+
Promise.all(toDelete.map((url) => cache.delete(url))),
|
|
36
42
|
);
|
|
37
43
|
}
|
|
38
44
|
}
|