@editframe/elements 0.5.0-beta.8 → 0.6.0-beta.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.
Files changed (109) hide show
  1. package/dist/lib/av/EncodedAsset.cjs +561 -0
  2. package/dist/{editor/util/EncodedAsset/EncodedAsset.mjs → lib/av/EncodedAsset.js} +40 -33
  3. package/dist/lib/av/MP4File.cjs +182 -0
  4. package/dist/{editor/util/MP4File.mjs → lib/av/MP4File.js} +55 -51
  5. package/dist/lib/av/msToTimeCode.cjs +15 -0
  6. package/dist/lib/util/awaitMicrotask.cjs +8 -0
  7. package/dist/lib/util/memoize.cjs +14 -0
  8. package/dist/{util/memoize.mjs → lib/util/memoize.js} +1 -2
  9. package/dist/packages/elements/src/EF_FRAMEGEN.cjs +197 -0
  10. package/dist/packages/elements/src/EF_FRAMEGEN.d.ts +45 -0
  11. package/dist/packages/elements/src/EF_FRAMEGEN.js +197 -0
  12. package/dist/packages/elements/src/EF_INTERACTIVE.cjs +4 -0
  13. package/dist/packages/elements/src/EF_INTERACTIVE.d.ts +1 -0
  14. package/dist/packages/elements/src/elements/CrossUpdateController.cjs +16 -0
  15. package/dist/packages/elements/src/elements/CrossUpdateController.d.ts +9 -0
  16. package/dist/packages/elements/src/elements/EFAudio.cjs +53 -0
  17. package/dist/packages/elements/src/elements/EFAudio.d.ts +10 -0
  18. package/dist/{elements/src/elements/EFAudio.mjs → packages/elements/src/elements/EFAudio.js} +2 -5
  19. package/dist/packages/elements/src/elements/EFCaptions.cjs +171 -0
  20. package/dist/packages/elements/src/elements/EFCaptions.d.ts +39 -0
  21. package/dist/{elements/src/elements/EFCaptions.mjs → packages/elements/src/elements/EFCaptions.js} +18 -20
  22. package/dist/packages/elements/src/elements/EFImage.cjs +79 -0
  23. package/dist/packages/elements/src/elements/EFImage.d.ts +14 -0
  24. package/dist/{elements/src/elements/EFImage.mjs → packages/elements/src/elements/EFImage.js} +8 -7
  25. package/dist/packages/elements/src/elements/EFMedia.cjs +334 -0
  26. package/dist/packages/elements/src/elements/EFMedia.d.ts +61 -0
  27. package/dist/{elements/src/elements/EFMedia.mjs → packages/elements/src/elements/EFMedia.js} +40 -38
  28. package/dist/packages/elements/src/elements/EFSourceMixin.cjs +55 -0
  29. package/dist/packages/elements/src/elements/EFSourceMixin.d.ts +12 -0
  30. package/dist/{elements/src/elements/EFSourceMixin.mjs → packages/elements/src/elements/EFSourceMixin.js} +6 -8
  31. package/dist/packages/elements/src/elements/EFTemporal.cjs +198 -0
  32. package/dist/packages/elements/src/elements/EFTemporal.d.ts +36 -0
  33. package/dist/{elements/src/elements/EFTemporal.mjs → packages/elements/src/elements/EFTemporal.js} +6 -22
  34. package/dist/packages/elements/src/elements/EFTimegroup.browsertest.d.ts +12 -0
  35. package/{src/elements/EFTimegroup.ts → dist/packages/elements/src/elements/EFTimegroup.cjs} +162 -213
  36. package/dist/packages/elements/src/elements/EFTimegroup.d.ts +39 -0
  37. package/dist/{elements/src/elements/EFTimegroup.mjs → packages/elements/src/elements/EFTimegroup.js} +55 -65
  38. package/{src/elements/EFTimeline.ts → dist/packages/elements/src/elements/EFTimeline.cjs} +5 -3
  39. package/dist/packages/elements/src/elements/EFTimeline.d.ts +3 -0
  40. package/dist/{elements/src/elements/EFTimeline.mjs → packages/elements/src/elements/EFTimeline.js} +5 -2
  41. package/dist/packages/elements/src/elements/EFVideo.cjs +110 -0
  42. package/dist/packages/elements/src/elements/EFVideo.d.ts +14 -0
  43. package/dist/{elements/src/elements/EFVideo.mjs → packages/elements/src/elements/EFVideo.js} +10 -32
  44. package/dist/packages/elements/src/elements/EFWaveform.cjs +235 -0
  45. package/dist/packages/elements/src/elements/EFWaveform.d.ts +28 -0
  46. package/dist/{elements/src/elements/EFWaveform.mjs → packages/elements/src/elements/EFWaveform.js} +15 -16
  47. package/dist/packages/elements/src/elements/FetchMixin.cjs +28 -0
  48. package/dist/packages/elements/src/elements/FetchMixin.d.ts +8 -0
  49. package/dist/{elements/src/elements/FetchMixin.mjs → packages/elements/src/elements/FetchMixin.js} +5 -7
  50. package/dist/packages/elements/src/elements/TimegroupController.cjs +20 -0
  51. package/dist/packages/elements/src/elements/TimegroupController.d.ts +14 -0
  52. package/dist/packages/elements/src/elements/durationConverter.cjs +8 -0
  53. package/dist/packages/elements/src/elements/durationConverter.d.ts +4 -0
  54. package/dist/{elements/src/elements/durationConverter.mjs → packages/elements/src/elements/durationConverter.js} +1 -1
  55. package/dist/packages/elements/src/elements/parseTimeToMs.cjs +12 -0
  56. package/dist/packages/elements/src/elements/parseTimeToMs.d.ts +1 -0
  57. package/dist/packages/elements/src/elements/parseTimeToMs.js +12 -0
  58. package/dist/packages/elements/src/elements/util.cjs +11 -0
  59. package/dist/packages/elements/src/elements/util.d.ts +4 -0
  60. package/dist/{elements/src/elements/util.mjs → packages/elements/src/elements/util.js} +1 -1
  61. package/dist/packages/elements/src/gui/EFFilmstrip.cjs +675 -0
  62. package/dist/packages/elements/src/gui/EFFilmstrip.d.ts +138 -0
  63. package/dist/{elements/src/gui/EFFilmstrip.mjs → packages/elements/src/gui/EFFilmstrip.js} +57 -55
  64. package/dist/packages/elements/src/gui/EFWorkbench.cjs +199 -0
  65. package/dist/packages/elements/src/gui/EFWorkbench.d.ts +44 -0
  66. package/dist/{elements/src/gui/EFWorkbench.mjs → packages/elements/src/gui/EFWorkbench.js} +27 -49
  67. package/{src/gui/TWMixin.ts → dist/packages/elements/src/gui/TWMixin.cjs} +11 -10
  68. package/dist/packages/elements/src/gui/TWMixin.css.cjs +3 -0
  69. package/dist/packages/elements/src/gui/TWMixin.css.js +4 -0
  70. package/dist/packages/elements/src/gui/TWMixin.d.ts +3 -0
  71. package/dist/{elements/src/gui/TWMixin.mjs → packages/elements/src/gui/TWMixin.js} +4 -3
  72. package/dist/packages/elements/src/index.cjs +47 -0
  73. package/dist/packages/elements/src/index.d.ts +10 -0
  74. package/dist/packages/elements/src/index.js +23 -0
  75. package/dist/style.css +13 -4
  76. package/package.json +23 -4
  77. package/dist/elements/src/EF_FRAMEGEN.mjs +0 -130
  78. package/dist/elements/src/elements/parseTimeToMs.mjs +0 -13
  79. package/dist/elements/src/elements.mjs +0 -12
  80. package/dist/elements/src/gui/TWMixin.css.mjs +0 -4
  81. package/dist/util/awaitAnimationFrame.mjs +0 -11
  82. package/docker-compose.yaml +0 -17
  83. package/src/EF_FRAMEGEN.ts +0 -208
  84. package/src/EF_INTERACTIVE.ts +0 -2
  85. package/src/elements/CrossUpdateController.ts +0 -18
  86. package/src/elements/EFAudio.ts +0 -42
  87. package/src/elements/EFCaptions.ts +0 -202
  88. package/src/elements/EFImage.ts +0 -70
  89. package/src/elements/EFMedia.ts +0 -395
  90. package/src/elements/EFSourceMixin.ts +0 -57
  91. package/src/elements/EFTemporal.ts +0 -246
  92. package/src/elements/EFTimegroup.browsertest.ts +0 -360
  93. package/src/elements/EFVideo.ts +0 -114
  94. package/src/elements/EFWaveform.ts +0 -407
  95. package/src/elements/FetchMixin.ts +0 -18
  96. package/src/elements/TimegroupController.ts +0 -25
  97. package/src/elements/buildLitFixture.ts +0 -13
  98. package/src/elements/durationConverter.ts +0 -6
  99. package/src/elements/parseTimeToMs.ts +0 -10
  100. package/src/elements/util.ts +0 -24
  101. package/src/gui/EFFilmstrip.ts +0 -702
  102. package/src/gui/EFWorkbench.ts +0 -242
  103. package/src/gui/TWMixin.css +0 -3
  104. package/src/util.d.ts +0 -1
  105. /package/dist/{editor/msToTimeCode.mjs → lib/av/msToTimeCode.js} +0 -0
  106. /package/dist/{util/awaitMicrotask.mjs → lib/util/awaitMicrotask.js} +0 -0
  107. /package/dist/{elements/src/EF_INTERACTIVE.mjs → packages/elements/src/EF_INTERACTIVE.js} +0 -0
  108. /package/dist/{elements/src/elements/CrossUpdateController.mjs → packages/elements/src/elements/CrossUpdateController.js} +0 -0
  109. /package/dist/{elements/src/elements/TimegroupController.mjs → packages/elements/src/elements/TimegroupController.js} +0 -0
@@ -1,17 +0,0 @@
1
-
2
- services:
3
- build-elements:
4
- image: telecine-runner
5
- depends_on:
6
- - runner
7
- working_dir: /app
8
- env_file:
9
- - ${PWD}/.env
10
- volumes_from:
11
- - runner
12
- stop_grace_period: 1s
13
- init: true
14
- scale: 1
15
- environment:
16
- - SHELL=/bin/sh
17
- command: npx vite build --config lib/elements/vite.config.ts lib/elements/src/elements.ts --watch
@@ -1,208 +0,0 @@
1
- import type { VideoRenderOptions } from "services/render/app/RenderOptions";
2
- import {
3
- deepGetElementsWithFrameTasks,
4
- shallowGetTimegroups,
5
- } from "./elements/EFTemporal";
6
- import { awaitAnimationFrame } from "@/util/awaitAnimationFrame";
7
- import { awaitMicrotask } from "@/util/awaitMicrotask";
8
-
9
- declare global {
10
- interface Window {
11
- EF_FRAMEGEN?: {
12
- onRender: (
13
- callback: (
14
- renderId: string,
15
- traceCarrier: unknown,
16
- renderOptions: VideoRenderOptions,
17
- ) => void,
18
- ) => void;
19
- onBegin: (
20
- callback: (
21
- traceCarrier: unknown,
22
- frame: number,
23
- isLast: boolean,
24
- ) => void,
25
- ) => void;
26
- frameReady: (
27
- renderId: string,
28
- frameNumber: number,
29
- samples: ArrayBufferLike,
30
- ) => void;
31
- onPaint: (callback: () => void) => void;
32
- didPaint: (renderId: string) => void;
33
- end: (renderId: string) => void;
34
- error: (renderId: string, error: string) => void;
35
- };
36
- }
37
- }
38
-
39
- class TriggerCanvas {
40
- private canvas: HTMLCanvasElement;
41
- private ctx: CanvasRenderingContext2D;
42
-
43
- constructor() {
44
- this.canvas = document.createElement("canvas");
45
- this.canvas.width = 1;
46
- this.canvas.height = 1;
47
- Object.assign(this.canvas.style, {
48
- position: "absolute",
49
- top: "0px",
50
- left: "0px",
51
- width: `1px`,
52
- height: `1px`,
53
- zIndex: "100000",
54
- });
55
- document.body.prepend(this.canvas);
56
- this.ctx = this.canvas.getContext("2d")!;
57
- }
58
-
59
- trigger() {
60
- console.log("Triggering");
61
- this.ctx.fillStyle = "rgba(50, 0, 0, .8)";
62
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
63
- this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
64
- }
65
- }
66
-
67
- if (window.EF_FRAMEGEN !== undefined) {
68
- const EF_FRAMEGEN = window.EF_FRAMEGEN;
69
- EF_FRAMEGEN.onRender((renderId, traceCarrier, renderOptions) => {
70
- const crashOnUnhandledError = (error: string) => {
71
- EF_FRAMEGEN.error(renderId, error);
72
- };
73
- window.addEventListener("error", (error) => {
74
- console.warn("Crashing due to unhandled error", error);
75
- crashOnUnhandledError(error.message);
76
- });
77
- window.addEventListener("unhandledrejection", (error) => {
78
- console.warn("Crashing due to unhandled rejection", error);
79
- crashOnUnhandledError(error.reason);
80
- });
81
-
82
- const triggerCanvas = new TriggerCanvas();
83
- EF_FRAMEGEN.onPaint(async () => {
84
- // Calls clearRect on the canvas to force the compositor to emit a frame.
85
- triggerCanvas.trigger();
86
-
87
- // We must wait for 2 frames to ensure offscreen render capture doesn't capture
88
- // a stale frame. This is terrible. Fixing this requires contribiting to electron.js
89
- // On the positive side, were' running at 240fps so it only burns 8ms max.
90
- // (electron can't go faster thon 240fps)
91
- // In practice it's less because we're already some ms into the next frame budget.
92
- await awaitAnimationFrame();
93
- await awaitAnimationFrame();
94
-
95
- EF_FRAMEGEN.didPaint(renderId);
96
- });
97
- const workbench = document.querySelector("ef-workbench")!;
98
- workbench.rendering = true;
99
- const timegroups = shallowGetTimegroups(workbench);
100
- const temporals = deepGetElementsWithFrameTasks(workbench);
101
- const firstGroup = timegroups[0];
102
- if (!firstGroup) {
103
- throw new Error("No temporal elements found");
104
- }
105
- firstGroup.currentTimeMs = renderOptions.encoderOptions.fromMs;
106
-
107
- const frameDurationMs = 1000 / renderOptions.encoderOptions.video.framerate;
108
-
109
- const initialBusyTasks = Promise.all(
110
- temporals
111
- // .filter((temporal) => temporal.frameTask.status < TaskStatus.COMPLETE)
112
- .map((temporal) => temporal.frameTask)
113
- .map((task) => task.taskComplete),
114
- );
115
-
116
- const frameBox = document.createElement("div");
117
- Object.assign(frameBox.style, {
118
- width: "200px",
119
- height: "100px",
120
- font: "30px Arial",
121
- backgroundColor: "white",
122
- position: "absolute",
123
- top: "0px",
124
- left: "0px",
125
- zIndex: "100000",
126
- });
127
-
128
- let time = 0;
129
- // document.body.prepend(frameBox);
130
- // TODO: terminate if no workbench found
131
- console.log("onRender", renderId, traceCarrier, renderOptions);
132
-
133
- const audioBufferPromise = firstGroup.renderAudio(
134
- renderOptions.encoderOptions.alignedFromUs / 1000,
135
- renderOptions.encoderOptions.alignedToUs / 1000,
136
- // renderOptions.encoderOptions.fromMs,
137
- // renderOptions.encoderOptions.toMs,
138
- );
139
- EF_FRAMEGEN.onBegin(async (traceCarrier, frame, isLast) => {
140
- time = firstGroup.currentTimeMs =
141
- renderOptions.encoderOptions.fromMs + frame * frameDurationMs;
142
- console.log("FRAME #", frame);
143
- frameBox.innerHTML = `
144
- <div>Frame #${frame}</div>
145
- <div>${time.toFixed(4)}</div>
146
- `;
147
-
148
- await initialBusyTasks;
149
-
150
- console.log("TIME", time.toFixed(4));
151
- await awaitMicrotask();
152
- console.log("After microtask");
153
-
154
- const now = performance.now();
155
- console.log(`frame:${frame} Awaiting busyTasks`);
156
- await Promise.all(
157
- temporals
158
- // .filter((temporal) => temporal.frameTask.status < TaskStatus.COMPLETE)
159
- .map((temporal) => {
160
- console.log(
161
- "Awaiting",
162
- temporal.tagName,
163
- temporal.frameTask.status,
164
- temporal.frameTask.taskComplete,
165
- );
166
- return temporal.frameTask;
167
- })
168
- .map((task) => task.taskComplete),
169
- );
170
- console.log(
171
- `frame:${frame} All tasks complete ${performance.now() - now}ms`,
172
- );
173
-
174
- await awaitAnimationFrame();
175
- await awaitAnimationFrame();
176
-
177
- // Trigger a small canvas paint to force the compositor to emit a frame.
178
- triggerCanvas.trigger();
179
-
180
- if (isLast) {
181
- // Currently we emit the audio in one belch at the end of the render.
182
- // This is not ideal, but it's the simplest thing that could possibly work.
183
- // We could either emit it slices, or in parallel with the video.
184
- // But in any case, it's fine for now.
185
- const renderedAudio = await audioBufferPromise;
186
-
187
- const channelCount = renderedAudio.numberOfChannels;
188
-
189
- const interleavedSamples = new Float32Array(
190
- channelCount * renderedAudio.length,
191
- );
192
-
193
- for (let i = 0; i < renderedAudio.length; i++) {
194
- for (let j = 0; j < channelCount; j++) {
195
- interleavedSamples.set(
196
- renderedAudio.getChannelData(j).slice(i, i + 1),
197
- i * channelCount + j,
198
- );
199
- }
200
- }
201
-
202
- EF_FRAMEGEN.frameReady(renderId, frame, interleavedSamples.buffer);
203
- } else {
204
- EF_FRAMEGEN.frameReady(renderId, frame, new Float32Array(0).buffer);
205
- }
206
- });
207
- });
208
- }
@@ -1,2 +0,0 @@
1
- export const EF_INTERACTIVE =
2
- !window.location.search.includes("EF_NONINTERACTIVE");
@@ -1,18 +0,0 @@
1
- import { LitElement, ReactiveController, ReactiveControllerHost } from "lit";
2
-
3
- export class CrossUpdateController implements ReactiveController {
4
- constructor(
5
- private host: ReactiveControllerHost,
6
- private target: LitElement,
7
- ) {
8
- this.host.addController(this);
9
- }
10
-
11
- hostUpdate(): void {
12
- this.target.requestUpdate();
13
- }
14
-
15
- remove(): void {
16
- this.host.removeController(this);
17
- }
18
- }
@@ -1,42 +0,0 @@
1
- import { html } from "lit";
2
- import { createRef, ref } from "lit/directives/ref.js";
3
- import { customElement, property } from "lit/decorators.js";
4
- import { EFMedia } from "./EFMedia";
5
- import { Task } from "@lit/task";
6
-
7
- @customElement("ef-audio")
8
- export class EFAudio extends EFMedia {
9
- audioElementRef = createRef<HTMLAudioElement>();
10
-
11
- @property({ type: String })
12
- src = "";
13
-
14
- render() {
15
- return html`<audio ${ref(this.audioElementRef)}></audio>`;
16
- }
17
-
18
- get audioElement() {
19
- return this.audioElementRef.value;
20
- }
21
-
22
- frameTask = new Task(this, {
23
- args: () =>
24
- [
25
- this.trackFragmentIndexLoader.status,
26
- this.initSegmentsLoader.status,
27
- this.seekTask.status,
28
- this.fetchSeekTask.status,
29
- this.videoAssetTask.status,
30
- ] as const,
31
- task: async () => {
32
- console.log("EFAudio frameTask", this.ownCurrentTimeMs);
33
- await this.trackFragmentIndexLoader.taskComplete;
34
- await this.initSegmentsLoader.taskComplete;
35
- await this.seekTask.taskComplete;
36
- await this.fetchSeekTask.taskComplete;
37
- await this.videoAssetTask.taskComplete;
38
- console.log("EFAudio frameTask complete", this.ownCurrentTimeMs);
39
- this.rootTimegroup?.requestUpdate();
40
- },
41
- });
42
- }
@@ -1,202 +0,0 @@
1
- import { EFAudio } from "./EFAudio";
2
- import { LitElement, PropertyValueMap, html, css } from "lit";
3
- import { Task } from "@lit/task";
4
- import { customElement, property } from "lit/decorators.js";
5
- import { EFVideo } from "./EFVideo";
6
- import { EFTemporal } from "./EFTemporal";
7
- import { CrossUpdateController } from "./CrossUpdateController";
8
- import { FetchMixin } from "./FetchMixin";
9
- import { EFSourceMixin } from "./EFSourceMixin";
10
- import { EF_INTERACTIVE } from "../EF_INTERACTIVE";
11
-
12
- interface Word {
13
- text: string;
14
- start: number;
15
- end: number;
16
- confidence: number;
17
- }
18
-
19
- interface Segment {
20
- start: number;
21
- end: number;
22
- text: string;
23
- confidence: number;
24
- words: Word[];
25
- }
26
-
27
- interface Caption {
28
- text: string;
29
- segments: Segment[];
30
- language: string;
31
- }
32
-
33
- @customElement("ef-captions-active-word")
34
- export class EFCaptionsActiveWord extends EFTemporal(LitElement) {
35
- static styles = [
36
- css`
37
- :host {
38
- display: inline-block;
39
- }
40
- `,
41
- ];
42
-
43
- render() {
44
- return html`${this.wordText}`;
45
- }
46
-
47
- @property({ type: Number, attribute: false })
48
- wordStartMs = 0;
49
-
50
- @property({ type: Number, attribute: false })
51
- wordEndMs = 0;
52
-
53
- @property({ type: String, attribute: false })
54
- wordText = "";
55
-
56
- get startTimeMs() {
57
- return this.wordStartMs || 0;
58
- }
59
-
60
- get durationMs(): number {
61
- return this.wordEndMs - this.wordStartMs;
62
- }
63
- }
64
-
65
- @customElement("ef-captions")
66
- export class EFCaptions extends EFSourceMixin(
67
- EFTemporal(FetchMixin(LitElement)),
68
- { assetType: "caption_files" },
69
- ) {
70
- static styles = [
71
- css`
72
- :host {
73
- display: block;
74
- }
75
- `,
76
- ];
77
-
78
- @property({ type: String, attribute: "target" })
79
- target = null;
80
-
81
- @property({ attribute: "word-style" })
82
- wordStyle = "";
83
-
84
- activeWordContainers = this.getElementsByTagName("ef-captions-active-word");
85
-
86
- captionsPath() {
87
- const src = this.targetElement.getAttribute("src");
88
- if (src?.startsWith("http")) {
89
- return src.replace("isobmff", "caption");
90
- }
91
- return `/@ef-captions/${this.targetElement.getAttribute("src") ?? ""}`;
92
- }
93
-
94
- protected md5SumLoader = new Task(this, {
95
- autoRun: false,
96
- args: () => [this.target] as const,
97
- task: async ([], { signal }) => {
98
- const md5Path = `/@ef-asset/${this.targetElement.getAttribute("src") ?? ""}`;
99
- const response = await fetch(md5Path, { method: "HEAD", signal });
100
- return response.headers.get("etag") ?? undefined;
101
- },
102
- });
103
-
104
- productionSrc() {
105
- if (!this.md5SumLoader.value) {
106
- throw new Error(
107
- `MD5 sum not available for ${this}. Cannot generate production URL`,
108
- );
109
- }
110
- return `http://localhost:3000/api/video2/caption_files/${this.md5SumLoader.value}`;
111
- }
112
-
113
- // get requiredAssets() {
114
- // return { [this.md5SumLoader.value]: [this.captionsPath()] };
115
- // }
116
-
117
- private captionsDataTask = new Task(this, {
118
- autoRun: EF_INTERACTIVE,
119
- args: () => [this.captionsPath(), this.fetch] as const,
120
- task: async ([captionsPath, fetch], { signal }) => {
121
- const response = await fetch(captionsPath, { signal });
122
- return response.json() as any as Caption;
123
- },
124
- });
125
-
126
- frameTask = new Task(this, {
127
- autoRun: EF_INTERACTIVE,
128
- args: () => [this.captionsDataTask.status],
129
- task: async () => {
130
- await this.captionsDataTask.taskComplete;
131
- },
132
- });
133
-
134
- connectedCallback() {
135
- super.connectedCallback();
136
- if (this.targetElement) {
137
- new CrossUpdateController(this.targetElement, this);
138
- }
139
- }
140
-
141
- render() {
142
- return this.captionsDataTask.render({
143
- pending: () => html`<div>Generating captions data...</div>`,
144
- error: () => html`<div>🚫 Error generating captions data</div>`,
145
- complete: () => html`<slot></slot>`,
146
- });
147
- }
148
-
149
- protected updated(
150
- _changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,
151
- ): void {
152
- this.updateActiveWord();
153
- }
154
-
155
- updateActiveWord() {
156
- const caption = this.captionsDataTask.value;
157
- if (!caption) {
158
- return;
159
- }
160
- const words: string[] = [];
161
- let startMs: number;
162
- let endMs: number;
163
- caption.segments.forEach((segment) => {
164
- if (
165
- this.targetElement.ownCurrentTimeMs >= segment.start * 1000 &&
166
- this.targetElement.ownCurrentTimeMs <= segment.end * 1000
167
- ) {
168
- return segment.words.map((word) => {
169
- if (
170
- this.targetElement.ownCurrentTimeMs >= word.start * 1000 &&
171
- this.targetElement.ownCurrentTimeMs <= word.end * 1000
172
- ) {
173
- words.push(word.text);
174
- startMs = word.start * 1000;
175
- endMs = word.end * 1000;
176
- } else {
177
- }
178
- });
179
- }
180
- });
181
- Array.from(this.activeWordContainers).forEach((container) => {
182
- container.wordText = words.join(" ");
183
- container.wordStartMs = startMs;
184
- container.wordEndMs = endMs;
185
- });
186
- }
187
-
188
- get targetElement() {
189
- const target = document.querySelector(this.getAttribute("target") ?? "");
190
- if (target instanceof EFAudio || target instanceof EFVideo) {
191
- return target;
192
- }
193
- throw new Error("Invalid target, must be an EFAudio or EFVideo element");
194
- }
195
- }
196
-
197
- declare global {
198
- interface HTMLElementTagNameMap {
199
- "ef-captions": EFCaptions;
200
- "ef-captions-active-word": EFCaptionsActiveWord;
201
- }
202
- }
@@ -1,70 +0,0 @@
1
- import { Task } from "@lit/task";
2
- import { LitElement, html, css } from "lit";
3
- import { customElement } from "lit/decorators.js";
4
- import { createRef, ref } from "lit/directives/ref.js";
5
- import { FetchMixin } from "./FetchMixin";
6
- import { EFSourceMixin } from "./EFSourceMixin";
7
- import { EF_INTERACTIVE } from "../EF_INTERACTIVE";
8
-
9
- @customElement("ef-image")
10
- export class EFImage extends EFSourceMixin(FetchMixin(LitElement), {
11
- assetType: "image_files",
12
- }) {
13
- static styles = [
14
- css`
15
- :host {
16
- display: block;
17
- }
18
- canvas {
19
- display: block;
20
- width: 100%;
21
- height: 100%;
22
- object-fit: fill;
23
- object-position: center;
24
- }
25
- `,
26
- ];
27
-
28
- imageRef = createRef<HTMLImageElement>();
29
- canvasRef = createRef<HTMLCanvasElement>();
30
-
31
- render() {
32
- return html`<canvas ${ref(this.canvasRef)}></canvas>`;
33
- }
34
-
35
- assetPath() {
36
- if (this.src.startsWith("http")) {
37
- return this.src;
38
- }
39
- return `/@ef-image/${this.src}`;
40
- }
41
-
42
- // get requiredAssets() {
43
- // return { [this.md5SumLoader.value]: [this.assetPath()] };
44
- // }
45
-
46
- fetchImage = new Task(this, {
47
- autoRun: EF_INTERACTIVE,
48
- args: () => [this.assetPath(), this.fetch] as const,
49
- task: async ([assetPath, fetch], { signal }) => {
50
- const response = await fetch(assetPath, { signal });
51
- const image = new Image();
52
- image.src = URL.createObjectURL(await response.blob());
53
- await new Promise((resolve) => {
54
- image.onload = resolve;
55
- });
56
- this.canvasRef.value!.width = image.width;
57
- this.canvasRef.value!.height = image.height;
58
- const ctx = this.canvasRef.value!.getContext("2d")!;
59
- ctx.drawImage(image, 0, 0);
60
- },
61
- });
62
-
63
- frameTask = new Task(this, {
64
- autoRun: EF_INTERACTIVE,
65
- args: () => [this.fetchImage.status] as const,
66
- task: async () => {
67
- await this.fetchImage.taskComplete;
68
- },
69
- });
70
- }