@editframe/elements 0.20.1-beta.0 → 0.20.3-beta.0

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.
@@ -41,7 +41,7 @@ var AssetIdMediaEngine = class AssetIdMediaEngine extends AssetMediaEngine {
41
41
  return `${this.apiHost}/api/v1/isobmff_tracks/${this.assetId}/${trackId}`;
42
42
  }
43
43
  convertToSegmentRelativeTimestamps(globalTimestamps, segmentId, rendition) {
44
- if (!rendition.trackId) throw new Error("Track ID is required for asset metadata");
44
+ if (!rendition.trackId) throw new Error("[convertToSegmentRelativeTimestamps] Track ID is required for asset metadata");
45
45
  const trackData = this.data[rendition.trackId];
46
46
  if (!trackData) throw new Error("Track not found");
47
47
  const segment = trackData.segments?.[segmentId];
@@ -63,7 +63,7 @@ var AssetMediaEngine = class AssetMediaEngine extends BaseMediaEngine {
63
63
  return `/@ef-track/${this.src}?trackId=${trackId}&segmentId=${segmentId}`;
64
64
  }
65
65
  async fetchInitSegment(rendition, signal) {
66
- if (!rendition.trackId) throw new Error("Track ID is required for asset metadata");
66
+ if (!rendition.trackId) throw new Error("[fetchInitSegment] Track ID is required for asset metadata");
67
67
  const url = this.buildInitSegmentUrl(rendition.trackId);
68
68
  const initSegment = this.data[rendition.trackId]?.initSegment;
69
69
  if (!initSegment) throw new Error("Init segment not found");
@@ -71,7 +71,7 @@ var AssetMediaEngine = class AssetMediaEngine extends BaseMediaEngine {
71
71
  return this.fetchMediaWithHeaders(url, headers, signal);
72
72
  }
73
73
  async fetchMediaSegment(segmentId, rendition, signal) {
74
- if (!rendition.trackId) throw new Error("Track ID is required for asset metadata");
74
+ if (!rendition.trackId) throw new Error("[fetchMediaSegment] Track ID is required for asset metadata");
75
75
  if (segmentId === void 0) throw new Error("Segment ID is not available");
76
76
  const url = this.buildMediaSegmentUrl(rendition.trackId, segmentId);
77
77
  const mediaSegment = this.data[rendition.trackId]?.segments[segmentId];
@@ -113,7 +113,10 @@ var AssetMediaEngine = class AssetMediaEngine extends BaseMediaEngine {
113
113
  return segmentRanges;
114
114
  }
115
115
  computeSegmentId(seekTimeMs, rendition) {
116
- if (!rendition.trackId) throw new Error("Track ID is required for asset metadata");
116
+ if (!rendition.trackId) {
117
+ console.warn(`computeSegmentId: trackId not found for rendition ${JSON.stringify(rendition)}`);
118
+ throw new Error("[computeSegmentId] Track ID is required for asset metadata");
119
+ }
117
120
  const track = this.data[rendition.trackId];
118
121
  if (!track) throw new Error("Track not found");
119
122
  const { timescale, segments } = track;
@@ -159,7 +162,7 @@ var AssetMediaEngine = class AssetMediaEngine extends BaseMediaEngine {
159
162
  }
160
163
  convertToSegmentRelativeTimestamps(globalTimestamps, segmentId, rendition) {
161
164
  {
162
- if (!rendition.trackId) throw new Error("Track ID is required for asset metadata");
165
+ if (!rendition.trackId) throw new Error("[convertToSegmentRelativeTimestamps] Track ID is required for asset metadata");
163
166
  const trackData = this.data[rendition.trackId];
164
167
  if (!trackData) throw new Error("Track not found");
165
168
  const segment = trackData.segments?.[segmentId];
@@ -1,3 +1,4 @@
1
+ import { EF_RENDERING } from "../../../EF_RENDERING.js";
1
2
  import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
2
3
  import { Task } from "@lit/task";
3
4
  const makeScrubVideoInitSegmentFetchTask = (host) => {
@@ -8,6 +9,7 @@ const makeScrubVideoInitSegmentFetchTask = (host) => {
8
9
  },
9
10
  onComplete: (_value) => {},
10
11
  task: async ([_mediaEngine], { signal }) => {
12
+ if (EF_RENDERING()) return /* @__PURE__ */ new ArrayBuffer(0);
11
13
  const mediaEngine = await getLatestMediaEngine(host, signal);
12
14
  const scrubRendition = mediaEngine.getScrubVideoRendition();
13
15
  if (!scrubRendition) throw new Error("No scrub rendition available");
@@ -1,3 +1,4 @@
1
+ import { EF_RENDERING } from "../../../EF_RENDERING.js";
1
2
  import { BufferedSeekingInput } from "../BufferedSeekingInput.js";
2
3
  import { EFMedia } from "../../EFMedia.js";
3
4
  import { Task } from "@lit/task";
@@ -9,6 +10,7 @@ const makeScrubVideoInputTask = (host) => {
9
10
  },
10
11
  onComplete: (_value) => {},
11
12
  task: async () => {
13
+ if (EF_RENDERING()) console.info("Scrub not available in rendering mode");
12
14
  const initSegment = await host.scrubVideoInitSegmentFetchTask.taskComplete;
13
15
  const segment = await host.scrubVideoSegmentFetchTask.taskComplete;
14
16
  if (!initSegment || !segment) throw new Error("Scrub init segment or segment is not available");
@@ -1,3 +1,4 @@
1
+ import { EF_RENDERING } from "../../../EF_RENDERING.js";
1
2
  import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
2
3
  import { Task } from "@lit/task";
3
4
  const makeScrubVideoSegmentFetchTask = (host) => {
@@ -8,6 +9,7 @@ const makeScrubVideoSegmentFetchTask = (host) => {
8
9
  },
9
10
  onComplete: (_value) => {},
10
11
  task: async (_, { signal }) => {
12
+ if (EF_RENDERING()) return /* @__PURE__ */ new ArrayBuffer(0);
11
13
  const mediaEngine = await getLatestMediaEngine(host, signal);
12
14
  const segmentId = await host.scrubVideoSegmentIdTask.taskComplete;
13
15
  if (segmentId === void 0) throw new Error("Scrub segment ID is not available for video");
@@ -1,3 +1,4 @@
1
+ import { EF_RENDERING } from "../../../EF_RENDERING.js";
1
2
  import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
2
3
  import { Task } from "@lit/task";
3
4
  const makeScrubVideoSegmentIdTask = (host) => {
@@ -8,6 +9,7 @@ const makeScrubVideoSegmentIdTask = (host) => {
8
9
  },
9
10
  onComplete: (_value) => {},
10
11
  task: async ([, targetSeekTimeMs], { signal }) => {
12
+ if (EF_RENDERING()) return void 0;
11
13
  const mediaEngine = await getLatestMediaEngine(host, signal);
12
14
  signal.throwIfAborted();
13
15
  const scrubRendition = mediaEngine.getScrubVideoRendition();
@@ -1,3 +1,4 @@
1
+ import { EF_RENDERING } from "../../../EF_RENDERING.js";
1
2
  import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
2
3
  import { ScrubInputCache } from "./ScrubInputCache.js";
3
4
  import { Task } from "@lit/task";
@@ -13,6 +14,7 @@ const makeUnifiedVideoSeekTask = (host) => {
13
14
  task: async ([desiredSeekTimeMs], { signal }) => {
14
15
  const mediaEngine = await getLatestMediaEngine(host, signal);
15
16
  if (!mediaEngine) return void 0;
17
+ if (EF_RENDERING()) return await getMainVideoSample(host, mediaEngine, desiredSeekTimeMs, signal);
16
18
  const mainRendition = mediaEngine.videoRendition;
17
19
  if (mainRendition) {
18
20
  const mainSegmentId = mediaEngine.computeSegmentId(desiredSeekTimeMs, mainRendition);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/elements",
3
- "version": "0.20.1-beta.0",
3
+ "version": "0.20.3-beta.0",
4
4
  "description": "",
5
5
  "exports": {
6
6
  ".": {
@@ -27,7 +27,7 @@
27
27
  "license": "UNLICENSED",
28
28
  "dependencies": {
29
29
  "@bramus/style-observer": "^1.3.0",
30
- "@editframe/assets": "0.20.1-beta.0",
30
+ "@editframe/assets": "0.20.3-beta.0",
31
31
  "@lit/context": "^1.1.2",
32
32
  "@lit/task": "^1.0.1",
33
33
  "d3": "^7.9.0",
@@ -87,7 +87,9 @@ export class AssetIdMediaEngine
87
87
  rendition: VideoRendition,
88
88
  ): number[] {
89
89
  if (!rendition.trackId) {
90
- throw new Error("Track ID is required for asset metadata");
90
+ throw new Error(
91
+ "[convertToSegmentRelativeTimestamps] Track ID is required for asset metadata",
92
+ );
91
93
  }
92
94
  // For AssetMediaEngine, we need to calculate the actual segment start time
93
95
  // using the precise segment boundaries from the track fragment index
@@ -110,7 +110,9 @@ export class AssetMediaEngine extends BaseMediaEngine implements MediaEngine {
110
110
  signal: AbortSignal,
111
111
  ) {
112
112
  if (!rendition.trackId) {
113
- throw new Error("Track ID is required for asset metadata");
113
+ throw new Error(
114
+ "[fetchInitSegment] Track ID is required for asset metadata",
115
+ );
114
116
  }
115
117
  const url = this.buildInitSegmentUrl(rendition.trackId);
116
118
  const initSegment = this.data[rendition.trackId]?.initSegment;
@@ -132,7 +134,9 @@ export class AssetMediaEngine extends BaseMediaEngine implements MediaEngine {
132
134
  signal?: AbortSignal,
133
135
  ) {
134
136
  if (!rendition.trackId) {
135
- throw new Error("Track ID is required for asset metadata");
137
+ throw new Error(
138
+ "[fetchMediaSegment] Track ID is required for asset metadata",
139
+ );
136
140
  }
137
141
  if (segmentId === undefined) {
138
142
  throw new Error("Segment ID is not available");
@@ -217,7 +221,14 @@ export class AssetMediaEngine extends BaseMediaEngine implements MediaEngine {
217
221
 
218
222
  computeSegmentId(seekTimeMs: number, rendition: MediaRendition) {
219
223
  if (!rendition.trackId) {
220
- throw new Error("Track ID is required for asset metadata");
224
+ console.warn(
225
+ `computeSegmentId: trackId not found for rendition ${JSON.stringify(
226
+ rendition,
227
+ )}`,
228
+ );
229
+ throw new Error(
230
+ "[computeSegmentId] Track ID is required for asset metadata",
231
+ );
221
232
  }
222
233
  const track = this.data[rendition.trackId];
223
234
  if (!track) {
@@ -311,7 +322,9 @@ export class AssetMediaEngine extends BaseMediaEngine implements MediaEngine {
311
322
  // This is because Asset segments are independent timeline fragments
312
323
 
313
324
  if (!rendition.trackId) {
314
- throw new Error("Track ID is required for asset metadata");
325
+ throw new Error(
326
+ "[convertToSegmentRelativeTimestamps] Track ID is required for asset metadata",
327
+ );
315
328
  }
316
329
  // For AssetMediaEngine, we need to calculate the actual segment start time
317
330
  // using the precise segment boundaries from the track fragment index
@@ -1,4 +1,5 @@
1
1
  import { Task } from "@lit/task";
2
+ import { EF_RENDERING } from "../../../EF_RENDERING";
2
3
  import type { MediaEngine } from "../../../transcoding/types";
3
4
  import type { EFVideo } from "../../EFVideo";
4
5
  import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
@@ -13,6 +14,10 @@ export const makeScrubVideoInitSegmentFetchTask = (
13
14
  },
14
15
  onComplete: (_value) => {},
15
16
  task: async ([_mediaEngine], { signal }) => {
17
+ if (EF_RENDERING()) {
18
+ return new ArrayBuffer(0);
19
+ }
20
+
16
21
  const mediaEngine = await getLatestMediaEngine(host, signal);
17
22
 
18
23
  // Get scrub rendition using the proper interface method
@@ -1,5 +1,5 @@
1
1
  import { Task } from "@lit/task";
2
-
2
+ import { EF_RENDERING } from "../../../EF_RENDERING";
3
3
  import { EFMedia } from "../../EFMedia";
4
4
  import type { EFVideo } from "../../EFVideo";
5
5
  import { BufferedSeekingInput } from "../BufferedSeekingInput";
@@ -20,6 +20,10 @@ export const makeScrubVideoInputTask = (host: EFVideo): InputTask => {
20
20
  },
21
21
  onComplete: (_value) => {},
22
22
  task: async () => {
23
+ if (EF_RENDERING()) {
24
+ console.info("Scrub not available in rendering mode");
25
+ }
26
+
23
27
  const initSegment =
24
28
  await host.scrubVideoInitSegmentFetchTask.taskComplete;
25
29
  const segment = await host.scrubVideoSegmentFetchTask.taskComplete;
@@ -1,4 +1,5 @@
1
1
  import { Task } from "@lit/task";
2
+ import { EF_RENDERING } from "../../../EF_RENDERING";
2
3
  import type { MediaEngine } from "../../../transcoding/types";
3
4
  import type { EFVideo } from "../../EFVideo";
4
5
  import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
@@ -17,6 +18,10 @@ export const makeScrubVideoSegmentFetchTask = (
17
18
  },
18
19
  onComplete: (_value) => {},
19
20
  task: async (_, { signal }) => {
21
+ if (EF_RENDERING()) {
22
+ return new ArrayBuffer(0);
23
+ }
24
+
20
25
  const mediaEngine = await getLatestMediaEngine(host, signal);
21
26
  const segmentId = await host.scrubVideoSegmentIdTask.taskComplete;
22
27
  if (segmentId === undefined) {
@@ -1,4 +1,5 @@
1
1
  import { Task } from "@lit/task";
2
+ import { EF_RENDERING } from "../../../EF_RENDERING";
2
3
  import type { MediaEngine } from "../../../transcoding/types";
3
4
  import type { EFVideo } from "../../EFVideo";
4
5
  import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
@@ -13,6 +14,10 @@ export const makeScrubVideoSegmentIdTask = (
13
14
  },
14
15
  onComplete: (_value) => {},
15
16
  task: async ([, targetSeekTimeMs], { signal }) => {
17
+ if (EF_RENDERING()) {
18
+ return undefined;
19
+ }
20
+
16
21
  const mediaEngine = await getLatestMediaEngine(host, signal);
17
22
  signal.throwIfAborted(); // Abort if a new seek started
18
23
 
@@ -1,5 +1,6 @@
1
1
  import { Task } from "@lit/task";
2
2
  import type { VideoSample } from "mediabunny";
3
+ import { EF_RENDERING } from "../../../EF_RENDERING";
3
4
  import type { VideoRendition } from "../../../transcoding/types";
4
5
  import type { EFVideo } from "../../EFVideo";
5
6
  import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
@@ -24,6 +25,16 @@ export const makeUnifiedVideoSeekTask = (
24
25
  const mediaEngine = await getLatestMediaEngine(host, signal);
25
26
  if (!mediaEngine) return undefined;
26
27
 
28
+ // In rendering mode, skip all the scrub logic and go straight to main
29
+ if (EF_RENDERING()) {
30
+ return await getMainVideoSample(
31
+ host,
32
+ mediaEngine,
33
+ desiredSeekTimeMs,
34
+ signal,
35
+ );
36
+ }
37
+
27
38
  // FIRST: Check if main quality content is already cached
28
39
  const mainRendition = mediaEngine.videoRendition;
29
40
  if (mainRendition) {
@@ -244,7 +244,9 @@ describe("OrderedLRUCache", () => {
244
244
  expect(allItems.map((item) => item.key)).toEqual([100, 200, 300]); // 200±500 = [-300,700] contains all
245
245
  });
246
246
 
247
- test("performance characteristics with large dataset", () => {
247
+ // This test is cute, but we can't perfectly controll wall time
248
+ // in CI, so it's flaky.
249
+ test.skip("performance characteristics with large dataset", () => {
248
250
  const cache = new OrderedLRUCache<number, string>(1000);
249
251
 
250
252
  // Insert many items