@editframe/elements 0.18.27-beta.0 → 0.19.2-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.
Files changed (73) hide show
  1. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +10 -0
  2. package/dist/elements/EFMedia/AssetMediaEngine.js +13 -1
  3. package/dist/elements/EFMedia/JitMediaEngine.d.ts +10 -0
  4. package/dist/elements/EFMedia/JitMediaEngine.js +12 -0
  5. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +16 -12
  6. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.d.ts +1 -1
  7. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +0 -4
  8. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +1 -1
  9. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +0 -4
  10. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +1 -1
  11. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +3 -2
  12. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +16 -12
  13. package/dist/elements/EFMedia.d.ts +2 -3
  14. package/dist/elements/EFMedia.js +0 -4
  15. package/dist/elements/EFTemporal.d.ts +9 -6
  16. package/dist/elements/EFTemporal.js +15 -12
  17. package/dist/elements/EFTimegroup.browsertest.d.ts +26 -0
  18. package/dist/elements/EFTimegroup.d.ts +12 -9
  19. package/dist/elements/EFTimegroup.js +114 -65
  20. package/dist/elements/EFVideo.d.ts +5 -1
  21. package/dist/elements/EFVideo.js +16 -8
  22. package/dist/elements/EFWaveform.js +2 -3
  23. package/dist/elements/FetchContext.browsertest.d.ts +0 -0
  24. package/dist/elements/FetchMixin.js +14 -9
  25. package/dist/elements/TimegroupController.js +2 -1
  26. package/dist/elements/updateAnimations.browsertest.d.ts +0 -0
  27. package/dist/elements/updateAnimations.d.ts +19 -9
  28. package/dist/elements/updateAnimations.js +64 -25
  29. package/dist/gui/ContextMixin.js +34 -27
  30. package/dist/gui/EFConfiguration.d.ts +1 -1
  31. package/dist/gui/EFConfiguration.js +1 -0
  32. package/dist/gui/EFFilmstrip.d.ts +1 -0
  33. package/dist/gui/EFFilmstrip.js +12 -14
  34. package/dist/gui/TWMixin.js +1 -1
  35. package/dist/style.css +1 -1
  36. package/dist/transcoding/cache/URLTokenDeduplicator.d.ts +38 -0
  37. package/dist/transcoding/cache/URLTokenDeduplicator.js +66 -0
  38. package/dist/transcoding/cache/URLTokenDeduplicator.test.d.ts +1 -0
  39. package/dist/transcoding/types/index.d.ts +10 -0
  40. package/package.json +2 -2
  41. package/src/elements/EFMedia/AssetMediaEngine.ts +16 -2
  42. package/src/elements/EFMedia/JitMediaEngine.ts +14 -0
  43. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -1
  44. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +11 -4
  45. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -4
  46. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +4 -1
  47. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -5
  48. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +2 -2
  49. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +7 -3
  50. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +11 -4
  51. package/src/elements/EFMedia.browsertest.ts +13 -4
  52. package/src/elements/EFMedia.ts +6 -10
  53. package/src/elements/EFTemporal.ts +21 -26
  54. package/src/elements/EFTimegroup.browsertest.ts +186 -2
  55. package/src/elements/EFTimegroup.ts +190 -94
  56. package/src/elements/EFVideo.browsertest.ts +53 -132
  57. package/src/elements/EFVideo.ts +26 -13
  58. package/src/elements/EFWaveform.ts +2 -3
  59. package/src/elements/FetchContext.browsertest.ts +396 -0
  60. package/src/elements/FetchMixin.ts +25 -8
  61. package/src/elements/TimegroupController.ts +2 -1
  62. package/src/elements/updateAnimations.browsertest.ts +559 -0
  63. package/src/elements/updateAnimations.ts +113 -50
  64. package/src/gui/ContextMixin.browsertest.ts +4 -9
  65. package/src/gui/ContextMixin.ts +52 -33
  66. package/src/gui/EFConfiguration.ts +1 -1
  67. package/src/gui/EFFilmstrip.ts +15 -18
  68. package/src/transcoding/cache/URLTokenDeduplicator.test.ts +182 -0
  69. package/src/transcoding/cache/URLTokenDeduplicator.ts +101 -0
  70. package/src/transcoding/types/index.ts +11 -0
  71. package/test/EFVideo.framegen.browsertest.ts +1 -1
  72. package/test/setup.ts +2 -0
  73. package/types.json +1 -1
@@ -8,6 +8,7 @@ import "../gui/EFWorkbench.js";
8
8
  import "../gui/EFPreview.js";
9
9
  import "./EFTimegroup.js";
10
10
 
11
+ import { TaskStatus } from "@lit/task";
11
12
  import type { EFTimegroup } from "./EFTimegroup.js";
12
13
 
13
14
  // Helper to wait for task completion but ignore abort errors
@@ -30,6 +31,10 @@ beforeAll(async () => {
30
31
  });
31
32
  });
32
33
 
34
+ beforeEach(() => {
35
+ localStorage.clear();
36
+ });
37
+
33
38
  // Extend the base test with fixtures following EFMedia.browsertest.ts pattern
34
39
  const test = baseTest.extend<{
35
40
  timegroup: EFTimegroup;
@@ -49,6 +54,7 @@ const test = baseTest.extend<{
49
54
  const apiHost = "http://localhost:63315";
50
55
  configuration.setAttribute("api-host", apiHost);
51
56
  configuration.apiHost = apiHost;
57
+ configuration.signingURL = ""; // Disable URL signing for tests
52
58
  document.body.appendChild(configuration);
53
59
  await use(configuration);
54
60
  },
@@ -68,18 +74,26 @@ const test = baseTest.extend<{
68
74
  use(video);
69
75
  },
70
76
  barsNtoneTimegroup: async ({}, use) => {
77
+ // Clear localStorage to prevent test contamination
78
+ localStorage.removeItem("ef-timegroup-barsNtoneTimegroup");
79
+
71
80
  const container = document.createElement("div");
72
81
  render(
73
82
  html`
74
83
  <ef-configuration api-host="http://localhost:63315">
75
- <ef-timegroup mode="sequence"
76
- class="relative h-[500px] w-[1000px] overflow-hidden bg-slate-500">
77
- <ef-video src="bars-n-tone.mp4"></ef-video>
84
+ <ef-preview>
85
+ <ef-timegroup mode="sequence" id="barsNtoneTimegroup"
86
+ class="relative h-[500px] w-[1000px] overflow-hidden bg-slate-500">
87
+ <ef-video src="bars-n-tone.mp4" id="barsNtoneVideo"></ef-video>
88
+ </ef-timegroup>
89
+ </ef-preview>
78
90
  </ef-configuration>
79
91
  `,
80
92
  container,
81
93
  );
82
94
  document.body.appendChild(container);
95
+ const configuration = container.querySelector("ef-configuration") as any;
96
+ configuration.signingURL = ""; // Disable URL signing for tests
83
97
  const timegroup = container.querySelector("ef-timegroup") as EFTimegroup;
84
98
  await timegroup.updateComplete;
85
99
  await use(timegroup);
@@ -91,23 +105,27 @@ const test = baseTest.extend<{
91
105
  render(
92
106
  html`
93
107
  <ef-configuration api-host="http://localhost:63315">
94
- <ef-timegroup mode="sequence"
95
- class="relative h-[500px] w-[1000px] overflow-hidden bg-slate-500">
96
-
97
- <ef-timegroup mode="contain" class="absolute w-full h-full">
98
- <ef-video src="bars-n-tone.mp4" class="size-full object-fit absolute top-0 left-0"></ef-video>
99
- </ef-timegroup>
100
-
101
- <ef-timegroup mode="contain" class="absolute w-full h-full">
102
- <ef-video src="bars-n-tone.mp4" class="size-full object-fit absolute top-0 left-0"></ef-video>
108
+ <ef-preview>
109
+ <ef-timegroup mode="sequence"
110
+ class="relative h-[500px] w-[1000px] overflow-hidden bg-slate-500">
111
+
112
+ <ef-timegroup mode="contain" class="absolute w-full h-full">
113
+ <ef-video src="bars-n-tone.mp4" class="size-full object-fit absolute top-0 left-0"></ef-video>
114
+ </ef-timegroup>
115
+
116
+ <ef-timegroup mode="contain" class="absolute w-full h-full">
117
+ <ef-video src="bars-n-tone.mp4" class="size-full object-fit absolute top-0 left-0"></ef-video>
118
+ </ef-timegroup>
119
+
103
120
  </ef-timegroup>
104
-
105
- </ef-timegroup>
121
+ </ef-preview>
106
122
  </ef-configuration>
107
123
  `,
108
124
  container,
109
125
  );
110
126
  document.body.appendChild(container);
127
+ const configuration = container.querySelector("ef-configuration") as any;
128
+ configuration.signingURL = ""; // Disable URL signing for tests
111
129
  const timegroup = container.querySelector("ef-timegroup") as EFTimegroup;
112
130
  await timegroup.updateComplete;
113
131
  await use(timegroup);
@@ -814,27 +832,24 @@ describe("EFVideo", () => {
814
832
  );
815
833
  });
816
834
 
817
- test("seeks backwards from 8000ms to 2000ms", async ({
835
+ test("seeks backward from 8000ms to 2000ms", async ({
818
836
  barsNtone,
819
837
  barsNtoneTimegroup,
838
+ expect,
820
839
  }) => {
821
- await waitForTaskIgnoringAborts(
822
- barsNtone.unifiedVideoSeekTask.taskComplete,
823
- );
824
- await waitForTaskIgnoringAborts(barsNtone.audioSeekTask.taskComplete);
825
- // First seek forward
826
840
  barsNtoneTimegroup.currentTimeMs = 8000;
827
- await barsNtone.updateComplete;
828
- await waitForTaskIgnoringAborts(
829
- barsNtone.unifiedVideoSeekTask.taskComplete,
841
+ await barsNtoneTimegroup.seekTask.taskComplete;
842
+ expect(barsNtone.unifiedVideoSeekTask.value?.timestamp).toBeCloseTo(
843
+ 8.066,
830
844
  );
831
845
 
846
+ expect(barsNtoneTimegroup.seekTask.status).toBe(TaskStatus.COMPLETE);
832
847
  // Then seek backward
833
848
  barsNtoneTimegroup.currentTimeMs = 2000;
834
- await barsNtone.updateComplete;
835
- await waitForTaskIgnoringAborts(barsNtone.audioSeekTask.taskComplete);
836
- await waitForTaskIgnoringAborts(
837
- barsNtone.unifiedVideoSeekTask.taskComplete,
849
+ expect(barsNtoneTimegroup.seekTask.status).toBe(TaskStatus.PENDING);
850
+ await barsNtoneTimegroup.seekTask.taskComplete;
851
+ expect(barsNtone.unifiedVideoSeekTask.value?.timestamp).toBeCloseTo(
852
+ 2.066,
838
853
  );
839
854
  });
840
855
 
@@ -842,9 +857,6 @@ describe("EFVideo", () => {
842
857
  barsNtone,
843
858
  barsNtoneTimegroup,
844
859
  }) => {
845
- await waitForTaskIgnoringAborts(
846
- barsNtone.unifiedVideoSeekTask.taskComplete,
847
- );
848
860
  await waitForTaskIgnoringAborts(barsNtone.audioSeekTask.taskComplete);
849
861
 
850
862
  // Use seek points that are within the actual media duration
@@ -1013,24 +1025,10 @@ describe("EFVideo", () => {
1013
1025
  barsNtoneTimegroup,
1014
1026
  expect,
1015
1027
  }) => {
1016
- await barsNtoneTimegroup.frameTask.taskComplete;
1028
+ // await barsNtoneTimegroup.frameTask.taskComplete;
1017
1029
  barsNtoneTimegroup.currentTimeMs = 7975;
1018
- await barsNtoneTimegroup.waitForNestedUpdates();
1019
- console.log(
1020
- "barsNtoneTimegroup.currentTime",
1021
- barsNtoneTimegroup.currentTime,
1022
- );
1023
- console.log("barsNtone.ownCurrentTimeMs", barsNtone.ownCurrentTimeMs);
1024
- console.log(
1025
- "barsNtone.currentSourceTimeMs",
1026
- barsNtone.currentSourceTimeMs,
1027
- );
1028
- console.log("barsNtone.desiredSeekTimeMs", barsNtone.desiredSeekTimeMs);
1029
- console.log(
1030
- "barsNtone.unifiedVideoSeekResult",
1031
- barsNtone.unifiedVideoSeekTask.value,
1032
- );
1033
- await barsNtoneTimegroup.frameTask.taskComplete;
1030
+ await barsNtoneTimegroup.seekTask.taskComplete;
1031
+
1034
1032
  expect(barsNtone.unifiedVideoSeekTask.value?.timestamp).toBeCloseTo(
1035
1033
  8.033,
1036
1034
  );
@@ -1087,47 +1085,24 @@ describe("EFVideo", () => {
1087
1085
  });
1088
1086
 
1089
1087
  test("seeks to 1000ms", async ({ timegroup, headMoov480p, expect }) => {
1090
- await waitForTaskIgnoringAborts(
1091
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1092
- );
1093
- await waitForTaskIgnoringAborts(headMoov480p.audioSeekTask.taskComplete);
1094
-
1095
1088
  timegroup.currentTimeMs = 1000;
1096
1089
  await timegroup.seekTask.taskComplete;
1097
-
1098
1090
  expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBe(1);
1099
1091
  });
1100
1092
 
1101
1093
  test("seeks to 3000ms", async ({ timegroup, headMoov480p, expect }) => {
1102
- await waitForTaskIgnoringAborts(
1103
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1104
- );
1105
- await waitForTaskIgnoringAborts(headMoov480p.audioSeekTask.taskComplete);
1106
-
1107
1094
  timegroup.currentTimeMs = 3000;
1108
1095
  await timegroup.seekTask.taskComplete;
1109
-
1110
1096
  expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBe(3);
1111
1097
  });
1112
1098
 
1113
1099
  test("seeks to 5000ms", async ({ timegroup, headMoov480p, expect }) => {
1114
- await waitForTaskIgnoringAborts(
1115
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1116
- );
1117
- await waitForTaskIgnoringAborts(headMoov480p.audioSeekTask.taskComplete);
1118
-
1119
1100
  timegroup.currentTimeMs = 5000;
1120
1101
  await timegroup.seekTask.taskComplete;
1121
-
1122
1102
  expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBe(5);
1123
1103
  });
1124
1104
 
1125
1105
  test("seeks to 7500ms", async ({ timegroup, headMoov480p, expect }) => {
1126
- await waitForTaskIgnoringAborts(
1127
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1128
- );
1129
- await waitForTaskIgnoringAborts(headMoov480p.audioSeekTask.taskComplete);
1130
-
1131
1106
  timegroup.currentTimeMs = 7500;
1132
1107
  await timegroup.seekTask.taskComplete;
1133
1108
 
@@ -1139,11 +1114,6 @@ describe("EFVideo", () => {
1139
1114
  });
1140
1115
 
1141
1116
  test("seeks to 8500ms", async ({ timegroup, headMoov480p, expect }) => {
1142
- await waitForTaskIgnoringAborts(
1143
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1144
- );
1145
- await waitForTaskIgnoringAborts(headMoov480p.audioSeekTask.taskComplete);
1146
-
1147
1117
  timegroup.currentTimeMs = 8500;
1148
1118
  await timegroup.seekTask.taskComplete;
1149
1119
 
@@ -1158,11 +1128,6 @@ describe("EFVideo", () => {
1158
1128
  headMoov480p,
1159
1129
  expect,
1160
1130
  }) => {
1161
- await waitForTaskIgnoringAborts(
1162
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1163
- );
1164
- await waitForTaskIgnoringAborts(headMoov480p.audioSeekTask.taskComplete);
1165
-
1166
1131
  timegroup.currentTimeMs = 9000;
1167
1132
  await timegroup.seekTask.taskComplete;
1168
1133
 
@@ -1174,32 +1139,13 @@ describe("EFVideo", () => {
1174
1139
  headMoov480p,
1175
1140
  expect,
1176
1141
  }) => {
1177
- await waitForTaskIgnoringAborts(
1178
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1179
- );
1180
- await waitForTaskIgnoringAborts(headMoov480p.audioSeekTask.taskComplete);
1142
+ timegroup.currentTime = 7;
1143
+ await expect(timegroup.seekTask.taskComplete).resolves.toBe(7);
1144
+ expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBe(7);
1181
1145
 
1182
- // First seek forward
1183
- timegroup.currentTimeMs = 7000;
1184
- await timegroup.seekTask.taskComplete;
1185
- await waitForTaskIgnoringAborts(
1186
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1187
- );
1188
- expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBeCloseTo(
1189
- 7,
1190
- 1,
1191
- );
1192
-
1193
- // Then seek backward
1194
- timegroup.currentTimeMs = 2000;
1195
- await timegroup.seekTask.taskComplete;
1196
- await waitForTaskIgnoringAborts(
1197
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1198
- );
1199
- expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBeCloseTo(
1200
- 2,
1201
- 1,
1202
- );
1146
+ timegroup.currentTime = 2;
1147
+ await expect(timegroup.seekTask.taskComplete).resolves.toBe(2);
1148
+ expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBe(2);
1203
1149
  });
1204
1150
 
1205
1151
  test("seeks to multiple points in sequence", async ({
@@ -1207,20 +1153,12 @@ describe("EFVideo", () => {
1207
1153
  headMoov480p,
1208
1154
  expect,
1209
1155
  }) => {
1210
- await waitForTaskIgnoringAborts(
1211
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1212
- );
1213
- await waitForTaskIgnoringAborts(headMoov480p.audioSeekTask.taskComplete);
1214
-
1215
1156
  const seekPoints = [1000, 3000, 5000, 2000, 6000, 0];
1216
1157
  const expectedTimestamps = [1, 3, 5, 2, 6, 0];
1217
1158
 
1218
1159
  for (let i = 0; i < seekPoints.length; i++) {
1219
1160
  timegroup.currentTimeMs = seekPoints[i]!;
1220
1161
  await timegroup.seekTask.taskComplete;
1221
- await waitForTaskIgnoringAborts(
1222
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1223
- );
1224
1162
  expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBeCloseTo(
1225
1163
  expectedTimestamps[i]!,
1226
1164
  1,
@@ -1233,24 +1171,16 @@ describe("EFVideo", () => {
1233
1171
  headMoov480p,
1234
1172
  expect,
1235
1173
  }) => {
1236
- await waitForTaskIgnoringAborts(
1237
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1238
- );
1239
- await waitForTaskIgnoringAborts(headMoov480p.audioSeekTask.taskComplete);
1240
-
1241
1174
  const fractionalTimes = [1234.567, 3456.789, 5678.901];
1242
1175
  const expectedTimestamps = [1.234567, 3.456789, 5.678901];
1243
1176
 
1244
1177
  for (let i = 0; i < fractionalTimes.length; i++) {
1245
1178
  timegroup.currentTimeMs = fractionalTimes[i]!;
1246
1179
  await timegroup.seekTask.taskComplete;
1247
- await waitForTaskIgnoringAborts(
1248
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1249
- );
1250
1180
  expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBeCloseTo(
1251
1181
  expectedTimestamps[i]!,
1252
1182
  1,
1253
- ); // Reduced precision for JIT
1183
+ );
1254
1184
  }
1255
1185
  });
1256
1186
 
@@ -1259,10 +1189,8 @@ describe("EFVideo", () => {
1259
1189
  headMoov480p,
1260
1190
  expect,
1261
1191
  }) => {
1192
+ timegroup.currentTimeMs = 0;
1262
1193
  await timegroup.seekTask.taskComplete;
1263
- await waitForTaskIgnoringAborts(
1264
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1265
- );
1266
1194
  expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBeCloseTo(
1267
1195
  0,
1268
1196
  1,
@@ -1270,9 +1198,6 @@ describe("EFVideo", () => {
1270
1198
 
1271
1199
  timegroup.currentTimeMs = 1000;
1272
1200
  await timegroup.seekTask.taskComplete;
1273
- await waitForTaskIgnoringAborts(
1274
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1275
- );
1276
1201
  expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBeCloseTo(
1277
1202
  1,
1278
1203
  1,
@@ -1280,9 +1205,6 @@ describe("EFVideo", () => {
1280
1205
 
1281
1206
  timegroup.currentTimeMs = 4000;
1282
1207
  await timegroup.seekTask.taskComplete;
1283
- await waitForTaskIgnoringAborts(
1284
- headMoov480p.unifiedVideoSeekTask.taskComplete,
1285
- );
1286
1208
  expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBeCloseTo(
1287
1209
  4,
1288
1210
  1,
@@ -1294,7 +1216,6 @@ describe("EFVideo", () => {
1294
1216
  headMoov480p,
1295
1217
  expect,
1296
1218
  }) => {
1297
- await timegroup.waitForMediaDurations();
1298
1219
  timegroup.currentTimeMs = 1000;
1299
1220
  await timegroup.seekTask.taskComplete;
1300
1221
  expect(headMoov480p.unifiedVideoSeekTask.value?.timestamp).toBe(1);
@@ -5,7 +5,6 @@ import { customElement, property, state } from "lit/decorators.js";
5
5
  import { createRef, ref } from "lit/directives/ref.js";
6
6
 
7
7
  import { DelayedLoadingState } from "../DelayedLoadingState.js";
8
- import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
9
8
  import { TWMixin } from "../gui/TWMixin.js";
10
9
  import { makeScrubVideoBufferTask } from "./EFMedia/videoTasks/makeScrubVideoBufferTask.ts";
11
10
  import { makeScrubVideoInitSegmentFetchTask } from "./EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts";
@@ -155,6 +154,15 @@ export class EFVideo extends TWMixin(EFMedia) {
155
154
  );
156
155
  }
157
156
 
157
+ protected updated(
158
+ changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,
159
+ ): void {
160
+ super.updated(changedProperties);
161
+
162
+ // No need to clear canvas - displayFrame() overwrites it completely
163
+ // and clearing creates blank frame gaps during transitions
164
+ }
165
+
158
166
  render() {
159
167
  return html`
160
168
  <canvas ${ref(this.canvasRef)}></canvas>
@@ -189,27 +197,20 @@ export class EFVideo extends TWMixin(EFMedia) {
189
197
  }
190
198
 
191
199
  frameTask = new Task(this, {
192
- autoRun: EF_INTERACTIVE,
200
+ autoRun: false,
193
201
  args: () => [this.desiredSeekTimeMs] as const,
194
202
  onError: (error) => {
195
203
  console.error("frameTask error", error);
196
204
  },
197
205
  onComplete: () => {},
198
- task: async ([_desiredSeekTimeMs], { signal }) => {
206
+ task: async ([_desiredSeekTimeMs]) => {
207
+ this.unifiedVideoSeekTask.run();
199
208
  await this.unifiedVideoSeekTask.taskComplete;
209
+ this.paintTask.run();
200
210
  await this.paintTask.taskComplete;
201
- if (signal.aborted) {
202
- return;
203
- }
204
211
  },
205
212
  });
206
213
 
207
- protected updated(
208
- changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,
209
- ): void {
210
- super.updated(changedProperties);
211
- }
212
-
213
214
  /**
214
215
  * Start a delayed loading operation for testing
215
216
  */
@@ -244,6 +245,7 @@ export class EFVideo extends TWMixin(EFMedia) {
244
245
  }
245
246
 
246
247
  paintTask = new Task(this, {
248
+ autoRun: false,
247
249
  args: () => [this.desiredSeekTimeMs] as const,
248
250
  onError: (error) => {
249
251
  console.error("paintTask error", error);
@@ -301,6 +303,18 @@ export class EFVideo extends TWMixin(EFMedia) {
301
303
  },
302
304
  });
303
305
 
306
+ /**
307
+ * Clear the canvas when element becomes inactive
308
+ */
309
+ clearCanvas(): void {
310
+ if (!this.canvasElement) return;
311
+
312
+ const ctx = this.canvasElement.getContext("2d");
313
+ if (ctx) {
314
+ ctx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);
315
+ }
316
+ }
317
+
304
318
  /**
305
319
  * Display a video frame on the canvas
306
320
  */
@@ -342,7 +356,6 @@ export class EFVideo extends TWMixin(EFMedia) {
342
356
  );
343
357
  }
344
358
 
345
- log("trace: drawing frame to canvas", frame.timestamp / 1000);
346
359
  ctx.drawImage(
347
360
  frame,
348
361
  0,
@@ -3,7 +3,6 @@ import { Task } from "@lit/task";
3
3
  import { css, html, LitElement, type PropertyValueMap } from "lit";
4
4
  import { customElement, property, state } from "lit/decorators.js";
5
5
  import { createRef, type Ref, ref } from "lit/directives/ref.js";
6
- import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
7
6
  import { EF_RENDERING } from "../EF_RENDERING.js";
8
7
  import { TWMixin } from "../gui/TWMixin.js";
9
8
  import { CrossUpdateController } from "./CrossUpdateController.js";
@@ -38,7 +37,7 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
38
37
  private mutationObserver?: MutationObserver;
39
38
 
40
39
  render() {
41
- return html`<canvas ${ref(this.canvasRef)}></canvas><div class="text-5xl inline-block bg-red-500">`;
40
+ return html`<canvas ${ref(this.canvasRef)}></canvas>`;
42
41
  }
43
42
 
44
43
  @property({
@@ -465,7 +464,7 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
465
464
  }
466
465
 
467
466
  frameTask = new Task(this, {
468
- autoRun: EF_INTERACTIVE,
467
+ autoRun: false,
469
468
  args: () => {
470
469
  return [
471
470
  this.targetElement,