@editframe/elements 0.11.0-beta.8 → 0.12.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.
- package/dist/EF_FRAMEGEN.d.ts +8 -15
- package/dist/elements/EFCaptions.d.ts +50 -6
- package/dist/elements/EFMedia.d.ts +1 -1
- package/dist/elements/EFTimegroup.browsertest.d.ts +4 -0
- package/dist/elements/EFTimegroup.d.ts +23 -2
- package/dist/elements/EFWaveform.d.ts +17 -13
- package/dist/elements/src/EF_FRAMEGEN.js +24 -26
- package/dist/elements/src/elements/EFCaptions.js +295 -42
- package/dist/elements/src/elements/EFImage.js +3 -13
- package/dist/elements/src/elements/EFMedia.js +0 -5
- package/dist/elements/src/elements/EFTemporal.js +13 -10
- package/dist/elements/src/elements/EFTimegroup.js +37 -12
- package/dist/elements/src/elements/EFVideo.js +7 -7
- package/dist/elements/src/elements/EFWaveform.js +262 -149
- package/dist/elements/src/gui/ContextMixin.js +36 -7
- package/dist/elements/src/gui/EFFilmstrip.js +16 -3
- package/dist/elements/src/gui/EFScrubber.js +142 -0
- package/dist/elements/src/gui/EFTimeDisplay.js +81 -0
- package/dist/elements/src/gui/EFTogglePlay.js +14 -14
- package/dist/elements/src/gui/EFWorkbench.js +1 -24
- package/dist/elements/src/gui/TWMixin.css.js +1 -1
- package/dist/elements/src/index.js +8 -1
- package/dist/gui/ContextMixin.d.ts +2 -1
- package/dist/gui/EFScrubber.d.ts +23 -0
- package/dist/gui/EFTimeDisplay.d.ts +17 -0
- package/dist/gui/EFTogglePlay.d.ts +1 -1
- package/dist/gui/EFWorkbench.d.ts +0 -1
- package/dist/index.d.ts +3 -1
- package/dist/style.css +6 -801
- package/package.json +2 -2
- package/src/elements/EFCaptions.browsertest.ts +6 -6
- package/src/elements/EFCaptions.ts +325 -56
- package/src/elements/EFImage.browsertest.ts +4 -17
- package/src/elements/EFImage.ts +4 -14
- package/src/elements/EFMedia.browsertest.ts +8 -19
- package/src/elements/EFMedia.ts +1 -6
- package/src/elements/EFTemporal.browsertest.ts +14 -0
- package/src/elements/EFTemporal.ts +14 -0
- package/src/elements/EFTimegroup.browsertest.ts +37 -0
- package/src/elements/EFTimegroup.ts +42 -17
- package/src/elements/EFVideo.ts +7 -8
- package/src/elements/EFWaveform.ts +349 -319
- package/src/gui/ContextMixin.browsertest.ts +28 -2
- package/src/gui/ContextMixin.ts +41 -9
- package/src/gui/EFFilmstrip.ts +16 -3
- package/src/gui/EFScrubber.ts +145 -0
- package/src/gui/EFTimeDisplay.ts +81 -0
- package/src/gui/EFTogglePlay.ts +21 -21
- package/src/gui/EFWorkbench.ts +3 -36
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { html, css, LitElement } from "lit";
|
|
2
1
|
import { Task } from "@lit/task";
|
|
2
|
+
import { html, css, LitElement } from "lit";
|
|
3
3
|
import { property, customElement } from "lit/decorators.js";
|
|
4
|
-
import {
|
|
4
|
+
import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
|
|
5
|
+
import { EF_RENDERING } from "../EF_RENDERING.js";
|
|
6
|
+
import { CrossUpdateController } from "./CrossUpdateController.js";
|
|
5
7
|
import { EFAudio } from "./EFAudio.js";
|
|
8
|
+
import { EFSourceMixin } from "./EFSourceMixin.js";
|
|
6
9
|
import { EFTemporal } from "./EFTemporal.js";
|
|
7
|
-
import {
|
|
10
|
+
import { EFVideo } from "./EFVideo.js";
|
|
8
11
|
import { FetchMixin } from "./FetchMixin.js";
|
|
9
|
-
import { EFSourceMixin } from "./EFSourceMixin.js";
|
|
10
|
-
import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
|
|
11
|
-
import { EF_RENDERING } from "../EF_RENDERING.js";
|
|
12
12
|
var __defProp = Object.defineProperty;
|
|
13
13
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
14
14
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
@@ -19,15 +19,22 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
19
19
|
if (kind && result) __defProp(target, key, result);
|
|
20
20
|
return result;
|
|
21
21
|
};
|
|
22
|
+
const stopWords = /* @__PURE__ */ new Set(["", ".", "!", "?", ","]);
|
|
22
23
|
let EFCaptionsActiveWord = class extends EFTemporal(LitElement) {
|
|
23
24
|
constructor() {
|
|
24
25
|
super(...arguments);
|
|
25
26
|
this.wordStartMs = 0;
|
|
26
27
|
this.wordEndMs = 0;
|
|
27
28
|
this.wordText = "";
|
|
29
|
+
this.hidden = false;
|
|
28
30
|
}
|
|
29
31
|
render() {
|
|
30
|
-
|
|
32
|
+
if (stopWords.has(this.wordText)) {
|
|
33
|
+
this.hidden = true;
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
this.hidden = false;
|
|
37
|
+
return html` ${this.wordText.trim()} `;
|
|
31
38
|
}
|
|
32
39
|
get startTimeMs() {
|
|
33
40
|
return this.wordStartMs || 0;
|
|
@@ -40,6 +47,10 @@ EFCaptionsActiveWord.styles = [
|
|
|
40
47
|
css`
|
|
41
48
|
:host {
|
|
42
49
|
display: inline-block;
|
|
50
|
+
white-space: pre;
|
|
51
|
+
}
|
|
52
|
+
:host([hidden]) {
|
|
53
|
+
display: none;
|
|
43
54
|
}
|
|
44
55
|
`
|
|
45
56
|
];
|
|
@@ -52,18 +63,176 @@ __decorateClass([
|
|
|
52
63
|
__decorateClass([
|
|
53
64
|
property({ type: String, attribute: false })
|
|
54
65
|
], EFCaptionsActiveWord.prototype, "wordText", 2);
|
|
66
|
+
__decorateClass([
|
|
67
|
+
property({ type: Boolean, reflect: true })
|
|
68
|
+
], EFCaptionsActiveWord.prototype, "hidden", 2);
|
|
55
69
|
EFCaptionsActiveWord = __decorateClass([
|
|
56
70
|
customElement("ef-captions-active-word")
|
|
57
71
|
], EFCaptionsActiveWord);
|
|
72
|
+
let EFCaptionsSegment = class extends EFTemporal(LitElement) {
|
|
73
|
+
constructor() {
|
|
74
|
+
super(...arguments);
|
|
75
|
+
this.segmentStartMs = 0;
|
|
76
|
+
this.segmentEndMs = 0;
|
|
77
|
+
this.segmentText = "";
|
|
78
|
+
this.hidden = false;
|
|
79
|
+
}
|
|
80
|
+
render() {
|
|
81
|
+
if (stopWords.has(this.segmentText)) {
|
|
82
|
+
this.hidden = true;
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
this.hidden = false;
|
|
86
|
+
return html`${this.segmentText}`;
|
|
87
|
+
}
|
|
88
|
+
get startTimeMs() {
|
|
89
|
+
return this.segmentStartMs || 0;
|
|
90
|
+
}
|
|
91
|
+
get durationMs() {
|
|
92
|
+
return this.segmentEndMs - this.segmentStartMs;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
EFCaptionsSegment.styles = [
|
|
96
|
+
css`
|
|
97
|
+
:host {
|
|
98
|
+
display: block;
|
|
99
|
+
}
|
|
100
|
+
:host([hidden]) {
|
|
101
|
+
display: none;
|
|
102
|
+
}
|
|
103
|
+
`
|
|
104
|
+
];
|
|
105
|
+
__decorateClass([
|
|
106
|
+
property({ type: Number, attribute: false })
|
|
107
|
+
], EFCaptionsSegment.prototype, "segmentStartMs", 2);
|
|
108
|
+
__decorateClass([
|
|
109
|
+
property({ type: Number, attribute: false })
|
|
110
|
+
], EFCaptionsSegment.prototype, "segmentEndMs", 2);
|
|
111
|
+
__decorateClass([
|
|
112
|
+
property({ type: String, attribute: false })
|
|
113
|
+
], EFCaptionsSegment.prototype, "segmentText", 2);
|
|
114
|
+
__decorateClass([
|
|
115
|
+
property({ type: Boolean, reflect: true })
|
|
116
|
+
], EFCaptionsSegment.prototype, "hidden", 2);
|
|
117
|
+
EFCaptionsSegment = __decorateClass([
|
|
118
|
+
customElement("ef-captions-segment")
|
|
119
|
+
], EFCaptionsSegment);
|
|
120
|
+
let EFCaptionsBeforeActiveWord = class extends EFCaptionsSegment {
|
|
121
|
+
constructor() {
|
|
122
|
+
super(...arguments);
|
|
123
|
+
this.hidden = false;
|
|
124
|
+
this.segmentText = "";
|
|
125
|
+
this.segmentStartMs = 0;
|
|
126
|
+
this.segmentEndMs = 0;
|
|
127
|
+
}
|
|
128
|
+
render() {
|
|
129
|
+
if (stopWords.has(this.segmentText)) {
|
|
130
|
+
this.hidden = true;
|
|
131
|
+
return void 0;
|
|
132
|
+
}
|
|
133
|
+
this.hidden = false;
|
|
134
|
+
return html` ${this.segmentText}`;
|
|
135
|
+
}
|
|
136
|
+
get startTimeMs() {
|
|
137
|
+
return this.segmentStartMs || 0;
|
|
138
|
+
}
|
|
139
|
+
get durationMs() {
|
|
140
|
+
return this.segmentEndMs - this.segmentStartMs;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
EFCaptionsBeforeActiveWord.styles = [
|
|
144
|
+
css`
|
|
145
|
+
:host {
|
|
146
|
+
display: inline-block;
|
|
147
|
+
white-space: pre;
|
|
148
|
+
}
|
|
149
|
+
:host([hidden]) {
|
|
150
|
+
display: none;
|
|
151
|
+
}
|
|
152
|
+
`
|
|
153
|
+
];
|
|
154
|
+
__decorateClass([
|
|
155
|
+
property({ type: Boolean, reflect: true })
|
|
156
|
+
], EFCaptionsBeforeActiveWord.prototype, "hidden", 2);
|
|
157
|
+
__decorateClass([
|
|
158
|
+
property({ type: String, attribute: false })
|
|
159
|
+
], EFCaptionsBeforeActiveWord.prototype, "segmentText", 2);
|
|
160
|
+
__decorateClass([
|
|
161
|
+
property({ type: Number, attribute: false })
|
|
162
|
+
], EFCaptionsBeforeActiveWord.prototype, "segmentStartMs", 2);
|
|
163
|
+
__decorateClass([
|
|
164
|
+
property({ type: Number, attribute: false })
|
|
165
|
+
], EFCaptionsBeforeActiveWord.prototype, "segmentEndMs", 2);
|
|
166
|
+
EFCaptionsBeforeActiveWord = __decorateClass([
|
|
167
|
+
customElement("ef-captions-before-active-word")
|
|
168
|
+
], EFCaptionsBeforeActiveWord);
|
|
169
|
+
let EFCaptionsAfterActiveWord = class extends EFCaptionsSegment {
|
|
170
|
+
constructor() {
|
|
171
|
+
super(...arguments);
|
|
172
|
+
this.hidden = false;
|
|
173
|
+
this.segmentText = "";
|
|
174
|
+
this.segmentStartMs = 0;
|
|
175
|
+
this.segmentEndMs = 0;
|
|
176
|
+
}
|
|
177
|
+
render() {
|
|
178
|
+
if (stopWords.has(this.segmentText)) {
|
|
179
|
+
this.hidden = true;
|
|
180
|
+
return void 0;
|
|
181
|
+
}
|
|
182
|
+
this.hidden = false;
|
|
183
|
+
return html`${this.segmentText} `;
|
|
184
|
+
}
|
|
185
|
+
get startTimeMs() {
|
|
186
|
+
return this.segmentStartMs || 0;
|
|
187
|
+
}
|
|
188
|
+
get durationMs() {
|
|
189
|
+
return this.segmentEndMs - this.segmentStartMs;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
EFCaptionsAfterActiveWord.styles = [
|
|
193
|
+
css`
|
|
194
|
+
:host {
|
|
195
|
+
display: inline-block;
|
|
196
|
+
white-space: pre;
|
|
197
|
+
}
|
|
198
|
+
:host([hidden]) {
|
|
199
|
+
display: none;
|
|
200
|
+
}
|
|
201
|
+
`
|
|
202
|
+
];
|
|
203
|
+
__decorateClass([
|
|
204
|
+
property({ type: Boolean, reflect: true })
|
|
205
|
+
], EFCaptionsAfterActiveWord.prototype, "hidden", 2);
|
|
206
|
+
__decorateClass([
|
|
207
|
+
property({ type: String, attribute: false })
|
|
208
|
+
], EFCaptionsAfterActiveWord.prototype, "segmentText", 2);
|
|
209
|
+
__decorateClass([
|
|
210
|
+
property({ type: Number, attribute: false })
|
|
211
|
+
], EFCaptionsAfterActiveWord.prototype, "segmentStartMs", 2);
|
|
212
|
+
__decorateClass([
|
|
213
|
+
property({ type: Number, attribute: false })
|
|
214
|
+
], EFCaptionsAfterActiveWord.prototype, "segmentEndMs", 2);
|
|
215
|
+
EFCaptionsAfterActiveWord = __decorateClass([
|
|
216
|
+
customElement("ef-captions-after-active-word")
|
|
217
|
+
], EFCaptionsAfterActiveWord);
|
|
58
218
|
let EFCaptions = class extends EFSourceMixin(
|
|
59
219
|
EFTemporal(FetchMixin(LitElement)),
|
|
60
220
|
{ assetType: "caption_files" }
|
|
61
221
|
) {
|
|
62
222
|
constructor() {
|
|
63
223
|
super(...arguments);
|
|
224
|
+
this.displayMode = "segment";
|
|
225
|
+
this.contextWords = 3;
|
|
64
226
|
this.targetSelector = "";
|
|
65
227
|
this.wordStyle = "";
|
|
66
228
|
this.activeWordContainers = this.getElementsByTagName("ef-captions-active-word");
|
|
229
|
+
this.segmentContainers = this.getElementsByTagName("ef-captions-segment");
|
|
230
|
+
this.beforeActiveWordContainers = this.getElementsByTagName(
|
|
231
|
+
"ef-captions-before-active-word"
|
|
232
|
+
);
|
|
233
|
+
this.afterActiveWordContainers = this.getElementsByTagName(
|
|
234
|
+
"ef-captions-after-active-word"
|
|
235
|
+
);
|
|
67
236
|
this.md5SumLoader = new Task(this, {
|
|
68
237
|
autoRun: false,
|
|
69
238
|
args: () => [this.target, this.fetch],
|
|
@@ -73,25 +242,72 @@ let EFCaptions = class extends EFSourceMixin(
|
|
|
73
242
|
return response.headers.get("etag") ?? void 0;
|
|
74
243
|
}
|
|
75
244
|
});
|
|
76
|
-
this.
|
|
245
|
+
this.transcriptionDataTask = new Task(this, {
|
|
246
|
+
autoRun: EF_INTERACTIVE,
|
|
247
|
+
args: () => [this.transcriptionsPath(), this.fetch],
|
|
248
|
+
task: async ([transcriptionsPath, fetch], { signal }) => {
|
|
249
|
+
if (!transcriptionsPath) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const response = await fetch(transcriptionsPath, { signal });
|
|
253
|
+
return response.json();
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
this.fragmentIndexTask = new Task(this, {
|
|
257
|
+
autoRun: EF_INTERACTIVE,
|
|
258
|
+
args: () => [this.transcriptionDataTask.value, this.ownCurrentTimeMs],
|
|
259
|
+
task: async ([transcription, ownCurrentTimeMs]) => {
|
|
260
|
+
if (!transcription) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
const fragmentIndex = Math.floor(
|
|
264
|
+
ownCurrentTimeMs / transcription.work_slice_ms
|
|
265
|
+
);
|
|
266
|
+
return fragmentIndex;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
this.transcriptionFragmentDataTask = new Task(this, {
|
|
77
270
|
autoRun: EF_INTERACTIVE,
|
|
78
|
-
args: () => [
|
|
79
|
-
|
|
80
|
-
|
|
271
|
+
args: () => [
|
|
272
|
+
this.transcriptionDataTask.value,
|
|
273
|
+
this.fragmentIndexTask.value,
|
|
274
|
+
this.fetch
|
|
275
|
+
],
|
|
276
|
+
task: async ([transcription, fragmentIndex, fetch], { signal }) => {
|
|
277
|
+
if (transcription === null || transcription === void 0 || fragmentIndex === null || fragmentIndex === void 0) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
const fragmentPath = this.transcriptionFragmentPath(
|
|
281
|
+
transcription.id,
|
|
282
|
+
fragmentIndex
|
|
283
|
+
);
|
|
284
|
+
const response = await fetch(fragmentPath, { signal });
|
|
81
285
|
return response.json();
|
|
82
286
|
}
|
|
83
287
|
});
|
|
84
288
|
this.frameTask = new Task(this, {
|
|
85
289
|
autoRun: EF_INTERACTIVE,
|
|
86
|
-
args: () => [this.
|
|
290
|
+
args: () => [this.transcriptionFragmentDataTask.status],
|
|
87
291
|
task: async () => {
|
|
88
|
-
await this.
|
|
292
|
+
await this.transcriptionFragmentDataTask.taskComplete;
|
|
89
293
|
}
|
|
90
294
|
});
|
|
91
295
|
}
|
|
92
296
|
set target(value) {
|
|
93
297
|
this.targetSelector = value;
|
|
94
298
|
}
|
|
299
|
+
render() {
|
|
300
|
+
return html`<slot></slot>`;
|
|
301
|
+
}
|
|
302
|
+
transcriptionsPath() {
|
|
303
|
+
if (this.targetElement.assetId) {
|
|
304
|
+
if (EF_RENDERING()) {
|
|
305
|
+
return `editframe://api/v1/isobmff_files/${this.targetElement.assetId}/transcription`;
|
|
306
|
+
}
|
|
307
|
+
return `${this.apiHost}/api/v1/isobmff_files/${this.targetElement.assetId}/transcription`;
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
95
311
|
captionsPath() {
|
|
96
312
|
if (this.targetElement.assetId) {
|
|
97
313
|
if (EF_RENDERING()) {
|
|
@@ -102,45 +318,66 @@ let EFCaptions = class extends EFSourceMixin(
|
|
|
102
318
|
const targetSrc = this.targetElement.src;
|
|
103
319
|
return `/@ef-captions/${targetSrc}`;
|
|
104
320
|
}
|
|
321
|
+
transcriptionFragmentPath(transcriptionId, fragmentIndex) {
|
|
322
|
+
return `${this.apiHost}/api/v1/transcriptions/${transcriptionId}/fragments/${fragmentIndex}`;
|
|
323
|
+
}
|
|
105
324
|
connectedCallback() {
|
|
106
325
|
super.connectedCallback();
|
|
107
326
|
if (this.targetElement) {
|
|
108
327
|
new CrossUpdateController(this.targetElement, this);
|
|
109
328
|
}
|
|
110
329
|
}
|
|
111
|
-
render() {
|
|
112
|
-
return this.captionsDataTask.render({
|
|
113
|
-
pending: () => html`<div>Generating captions data...</div>`,
|
|
114
|
-
error: () => html`<div>🚫 Error generating captions data</div>`,
|
|
115
|
-
complete: () => html`<slot></slot>`
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
330
|
updated(_changedProperties) {
|
|
119
|
-
this.
|
|
331
|
+
this.updateTextContainers();
|
|
120
332
|
}
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
if (!
|
|
333
|
+
updateTextContainers() {
|
|
334
|
+
const transcriptionFragment = this.transcriptionFragmentDataTask.value;
|
|
335
|
+
if (!transcriptionFragment) {
|
|
124
336
|
return;
|
|
125
337
|
}
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
338
|
+
const currentTimeMs = this.targetElement.trimAdjustedOwnCurrentTimeMs;
|
|
339
|
+
const currentTimeSec = currentTimeMs / 1e3;
|
|
340
|
+
const currentWord = transcriptionFragment.word_segments.find(
|
|
341
|
+
(word) => currentTimeSec >= word.start && currentTimeSec <= word.end
|
|
342
|
+
);
|
|
343
|
+
const currentSegment = transcriptionFragment.segments.find(
|
|
344
|
+
(segment) => currentTimeSec >= segment.start && currentTimeSec <= segment.end
|
|
345
|
+
);
|
|
346
|
+
for (const wordContainer of this.activeWordContainers) {
|
|
347
|
+
if (currentWord) {
|
|
348
|
+
wordContainer.wordText = currentWord.text;
|
|
349
|
+
wordContainer.wordStartMs = currentWord.start * 1e3;
|
|
350
|
+
wordContainer.wordEndMs = currentWord.end * 1e3;
|
|
138
351
|
}
|
|
139
352
|
}
|
|
140
|
-
for (const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
353
|
+
for (const segmentContainer of this.segmentContainers) {
|
|
354
|
+
if (currentSegment) {
|
|
355
|
+
segmentContainer.segmentText = currentSegment.text;
|
|
356
|
+
segmentContainer.segmentStartMs = currentSegment.start * 1e3;
|
|
357
|
+
segmentContainer.segmentEndMs = currentSegment.end * 1e3;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (currentWord && currentSegment) {
|
|
361
|
+
const segmentWords = transcriptionFragment.word_segments.filter(
|
|
362
|
+
(word) => word.start >= currentSegment.start && word.end <= currentSegment.end
|
|
363
|
+
);
|
|
364
|
+
const currentWordIndex = segmentWords.findIndex(
|
|
365
|
+
(word) => word.start === currentWord.start && word.end === currentWord.end
|
|
366
|
+
);
|
|
367
|
+
if (currentWordIndex !== -1) {
|
|
368
|
+
const beforeWords = segmentWords.slice(0, currentWordIndex).map((w) => w.text.trim()).join(" ");
|
|
369
|
+
const afterWords = segmentWords.slice(currentWordIndex + 1).map((w) => w.text.trim()).join(" ");
|
|
370
|
+
for (const container of this.beforeActiveWordContainers) {
|
|
371
|
+
container.segmentText = beforeWords;
|
|
372
|
+
container.segmentStartMs = currentSegment.start * 1e3;
|
|
373
|
+
container.segmentEndMs = currentWord.start * 1e3;
|
|
374
|
+
}
|
|
375
|
+
for (const container of this.afterActiveWordContainers) {
|
|
376
|
+
container.segmentText = afterWords;
|
|
377
|
+
container.segmentStartMs = currentWord.end * 1e3;
|
|
378
|
+
container.segmentEndMs = currentSegment.end * 1e3;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
144
381
|
}
|
|
145
382
|
}
|
|
146
383
|
get targetElement() {
|
|
@@ -154,10 +391,23 @@ let EFCaptions = class extends EFSourceMixin(
|
|
|
154
391
|
EFCaptions.styles = [
|
|
155
392
|
css`
|
|
156
393
|
:host {
|
|
157
|
-
display:
|
|
394
|
+
display: flex;
|
|
395
|
+
flex-wrap: wrap;
|
|
396
|
+
align-items: baseline;
|
|
397
|
+
width: fit-content;
|
|
398
|
+
}
|
|
399
|
+
::slotted(*) {
|
|
400
|
+
margin: 0;
|
|
401
|
+
padding: 0;
|
|
158
402
|
}
|
|
159
403
|
`
|
|
160
404
|
];
|
|
405
|
+
__decorateClass([
|
|
406
|
+
property({ type: String, attribute: "display-mode", reflect: true })
|
|
407
|
+
], EFCaptions.prototype, "displayMode", 2);
|
|
408
|
+
__decorateClass([
|
|
409
|
+
property({ type: Number, attribute: "context-words", reflect: true })
|
|
410
|
+
], EFCaptions.prototype, "contextWords", 2);
|
|
161
411
|
__decorateClass([
|
|
162
412
|
property({ type: String, attribute: "target", reflect: true })
|
|
163
413
|
], EFCaptions.prototype, "targetSelector", 2);
|
|
@@ -169,5 +419,8 @@ EFCaptions = __decorateClass([
|
|
|
169
419
|
], EFCaptions);
|
|
170
420
|
export {
|
|
171
421
|
EFCaptions,
|
|
172
|
-
EFCaptionsActiveWord
|
|
422
|
+
EFCaptionsActiveWord,
|
|
423
|
+
EFCaptionsAfterActiveWord,
|
|
424
|
+
EFCaptionsBeforeActiveWord,
|
|
425
|
+
EFCaptionsSegment
|
|
173
426
|
};
|
|
@@ -2,10 +2,10 @@ import { Task } from "@lit/task";
|
|
|
2
2
|
import { html, css, LitElement } from "lit";
|
|
3
3
|
import { property, customElement } from "lit/decorators.js";
|
|
4
4
|
import { createRef, ref } from "lit/directives/ref.js";
|
|
5
|
-
import { FetchMixin } from "./FetchMixin.js";
|
|
6
|
-
import { EFSourceMixin } from "./EFSourceMixin.js";
|
|
7
5
|
import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
|
|
8
6
|
import { EF_RENDERING } from "../EF_RENDERING.js";
|
|
7
|
+
import { EFSourceMixin } from "./EFSourceMixin.js";
|
|
8
|
+
import { FetchMixin } from "./FetchMixin.js";
|
|
9
9
|
var __defProp = Object.defineProperty;
|
|
10
10
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
11
|
var __typeError = (msg) => {
|
|
@@ -59,12 +59,6 @@ let EFImage = class extends EFSourceMixin(FetchMixin(LitElement), {
|
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
set assetId(value) {
|
|
62
|
-
if (!value?.match(/^.{8}-.{4}-.{4}-.{4}-.{12}:.*$/)) {
|
|
63
|
-
console.error(`EFMedia ${value} is not valid asset-id`);
|
|
64
|
-
throw new Error(
|
|
65
|
-
"EFMedia: asset-id must match <uuid>:<basename>. (like: 550e8400-e29b-41d4-a716-446655440000:example.mp4)"
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
62
|
__privateSet(this, _assetId, value);
|
|
69
63
|
}
|
|
70
64
|
get assetId() {
|
|
@@ -90,11 +84,7 @@ EFImage.styles = [
|
|
|
90
84
|
display: block;
|
|
91
85
|
}
|
|
92
86
|
canvas {
|
|
93
|
-
|
|
94
|
-
width: 100%;
|
|
95
|
-
height: 100%;
|
|
96
|
-
object-fit: fill;
|
|
97
|
-
object-position: center;
|
|
87
|
+
all: inherit
|
|
98
88
|
}
|
|
99
89
|
`
|
|
100
90
|
];
|
|
@@ -230,11 +230,6 @@ class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
|
230
230
|
}
|
|
231
231
|
#assetId;
|
|
232
232
|
set assetId(value) {
|
|
233
|
-
if (!value?.match(/^.{8}-.{4}-.{4}-.{4}-.{12}:.*$/)) {
|
|
234
|
-
throw new Error(
|
|
235
|
-
"EFMedia: asset-id must match <uuid>:<basename>. (like: 550e8400-e29b-41d4-a716-446655440000:example.mp4)"
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
233
|
this.#assetId = value;
|
|
239
234
|
}
|
|
240
235
|
get assetId() {
|
|
@@ -18,15 +18,6 @@ const timegroupContext = createContext(
|
|
|
18
18
|
);
|
|
19
19
|
const isEFTemporal = (obj) => obj[EF_TEMPORAL];
|
|
20
20
|
const EF_TEMPORAL = Symbol("EF_TEMPORAL");
|
|
21
|
-
const deepGetTemporalElements = (element, temporals = []) => {
|
|
22
|
-
for (const child of element.children) {
|
|
23
|
-
if (isEFTemporal(child)) {
|
|
24
|
-
temporals.push(child);
|
|
25
|
-
}
|
|
26
|
-
deepGetTemporalElements(child, temporals);
|
|
27
|
-
}
|
|
28
|
-
return temporals;
|
|
29
|
-
};
|
|
30
21
|
const deepGetElementsWithFrameTasks = (element, elements = []) => {
|
|
31
22
|
for (const child of element.children) {
|
|
32
23
|
if ("frameTask" in child && child.frameTask instanceof Task) {
|
|
@@ -115,10 +106,20 @@ const EFTemporal = (superClass) => {
|
|
|
115
106
|
get parentTimegroup() {
|
|
116
107
|
return this.#parentTimegroup;
|
|
117
108
|
}
|
|
109
|
+
set duration(value) {
|
|
110
|
+
if (value !== void 0) {
|
|
111
|
+
this.setAttribute("duration", value);
|
|
112
|
+
} else {
|
|
113
|
+
this.removeAttribute("duration");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
118
116
|
get trimStartMs() {
|
|
119
117
|
return this._trimStartMs;
|
|
120
118
|
}
|
|
121
119
|
set trimStartMs(value) {
|
|
120
|
+
if (this._trimStartMs === value) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
122
123
|
this._trimStartMs = value;
|
|
123
124
|
this.setAttribute(
|
|
124
125
|
"trimstart",
|
|
@@ -136,6 +137,9 @@ const EFTemporal = (superClass) => {
|
|
|
136
137
|
return this._trimEndMs;
|
|
137
138
|
}
|
|
138
139
|
set trimEndMs(value) {
|
|
140
|
+
if (this._trimEndMs === value) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
139
143
|
this._trimEndMs = value;
|
|
140
144
|
this.setAttribute("trimend", durationConverter.toAttribute(value / 1e3));
|
|
141
145
|
}
|
|
@@ -366,7 +370,6 @@ export {
|
|
|
366
370
|
EFTemporal,
|
|
367
371
|
OwnCurrentTimeController,
|
|
368
372
|
deepGetElementsWithFrameTasks,
|
|
369
|
-
deepGetTemporalElements,
|
|
370
373
|
isEFTemporal,
|
|
371
374
|
shallowGetTemporalElements,
|
|
372
375
|
timegroupContext
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { html, css, LitElement } from "lit";
|
|
2
1
|
import { provide } from "@lit/context";
|
|
3
2
|
import { Task } from "@lit/task";
|
|
4
|
-
import { property, customElement } from "lit/decorators.js";
|
|
5
3
|
import debug from "debug";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { html, css, LitElement } from "lit";
|
|
5
|
+
import { property, customElement } from "lit/decorators.js";
|
|
8
6
|
import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
|
|
7
|
+
import { isContextMixin } from "../gui/ContextMixin.js";
|
|
9
8
|
import { deepGetMediaElements } from "./EFMedia.js";
|
|
9
|
+
import { EFTemporal, shallowGetTemporalElements, isEFTemporal, timegroupContext } from "./EFTemporal.js";
|
|
10
|
+
import { TimegroupController } from "./TimegroupController.js";
|
|
10
11
|
import { durationConverter } from "./durationConverter.js";
|
|
11
12
|
var __defProp = Object.defineProperty;
|
|
12
13
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -138,10 +139,16 @@ let EFTimegroup = class extends EFTemporal(LitElement) {
|
|
|
138
139
|
throw new Error(`Invalid time mode: ${this.mode}`);
|
|
139
140
|
}
|
|
140
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Wait for all media elements to load their initial segments.
|
|
144
|
+
* Ideally we would only need the extracted index json data, but
|
|
145
|
+
* that caused issues with constructing audio data. We had negative durations
|
|
146
|
+
* in calculations and it was not clear why.
|
|
147
|
+
*/
|
|
141
148
|
async waitForMediaDurations() {
|
|
142
149
|
return await Promise.all(
|
|
143
150
|
deepGetMediaElements(this).map(
|
|
144
|
-
(media) => media.
|
|
151
|
+
(media) => media.initSegmentsLoader.taskComplete
|
|
145
152
|
)
|
|
146
153
|
);
|
|
147
154
|
}
|
|
@@ -212,8 +219,28 @@ let EFTimegroup = class extends EFTemporal(LitElement) {
|
|
|
212
219
|
}
|
|
213
220
|
}
|
|
214
221
|
}
|
|
222
|
+
get contextProvider() {
|
|
223
|
+
let parent = this.parentNode;
|
|
224
|
+
while (parent) {
|
|
225
|
+
if (isContextMixin(parent)) {
|
|
226
|
+
return parent;
|
|
227
|
+
}
|
|
228
|
+
parent = parent.parentNode;
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Returns true if the timegroup should be wrapped with a workbench.
|
|
234
|
+
*
|
|
235
|
+
* A timegroup should be wrapped with a workbench if it is the root-most timegroup
|
|
236
|
+
* and EF_INTERACTIVE is true.
|
|
237
|
+
*
|
|
238
|
+
* If the timegroup is already wrappedin a context provider like ef-preview,
|
|
239
|
+
* it should NOT be wrapped in a workbench.
|
|
240
|
+
*
|
|
241
|
+
*/
|
|
215
242
|
shouldWrapWithWorkbench() {
|
|
216
|
-
return EF_INTERACTIVE && this.closest("ef-timegroup") === this && this.closest("ef-
|
|
243
|
+
return EF_INTERACTIVE && this.closest("ef-timegroup") === this && this.closest("ef-preview") === null && this.closest("ef-workbench") === null && this.closest("test-context") === null;
|
|
217
244
|
}
|
|
218
245
|
wrapWithWorkbench() {
|
|
219
246
|
const workbench = document.createElement("ef-workbench");
|
|
@@ -270,10 +297,8 @@ _currentTime = /* @__PURE__ */ new WeakMap();
|
|
|
270
297
|
_EFTimegroup_instances = /* @__PURE__ */ new WeakSet();
|
|
271
298
|
addAudioToContext_fn = async function(audioContext, fromMs, toMs) {
|
|
272
299
|
await this.waitForMediaDurations();
|
|
273
|
-
const durationMs = toMs - fromMs;
|
|
274
300
|
await Promise.all(
|
|
275
301
|
deepGetMediaElements(this).map(async (mediaElement) => {
|
|
276
|
-
await mediaElement.trackFragmentIndexLoader.taskComplete;
|
|
277
302
|
const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;
|
|
278
303
|
const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;
|
|
279
304
|
const mediaOverlaps = mediaStartsBeforeEnd && mediaEndsAfterStart;
|
|
@@ -284,15 +309,15 @@ addAudioToContext_fn = async function(audioContext, fromMs, toMs) {
|
|
|
284
309
|
if (!audio) {
|
|
285
310
|
throw new Error("Failed to fetch audio");
|
|
286
311
|
}
|
|
287
|
-
const ctxStartMs = Math.max(0, mediaElement.startTimeMs - fromMs);
|
|
288
|
-
const ctxEndMs = Math.min(durationMs, mediaElement.endTimeMs - fromMs);
|
|
289
|
-
const ctxDurationMs = ctxEndMs - ctxStartMs;
|
|
290
|
-
const offset = Math.max(0, fromMs - mediaElement.startTimeMs) - audio.startMs;
|
|
291
312
|
const bufferSource = audioContext.createBufferSource();
|
|
292
313
|
bufferSource.buffer = await audioContext.decodeAudioData(
|
|
293
314
|
await audio.blob.arrayBuffer()
|
|
294
315
|
);
|
|
295
316
|
bufferSource.connect(audioContext.destination);
|
|
317
|
+
const ctxStartMs = Math.max(0, mediaElement.startTimeMs - fromMs);
|
|
318
|
+
const ctxEndMs = mediaElement.endTimeMs - fromMs;
|
|
319
|
+
const ctxDurationMs = ctxEndMs - ctxStartMs;
|
|
320
|
+
const offset = Math.max(0, fromMs - mediaElement.startTimeMs) - audio.startMs;
|
|
296
321
|
bufferSource.start(
|
|
297
322
|
ctxStartMs / 1e3,
|
|
298
323
|
offset / 1e3,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { css, html } from "lit";
|
|
2
1
|
import { Task } from "@lit/task";
|
|
3
|
-
import {
|
|
2
|
+
import { css, html } from "lit";
|
|
4
3
|
import { customElement } from "lit/decorators.js";
|
|
5
|
-
import {
|
|
4
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
6
5
|
import { TWMixin } from "../gui/TWMixin.js";
|
|
6
|
+
import { EFMedia } from "./EFMedia.js";
|
|
7
7
|
var __defProp = Object.defineProperty;
|
|
8
8
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
9
|
var __typeError = (msg) => {
|
|
@@ -85,10 +85,7 @@ let EFVideo = class extends TWMixin(EFMedia) {
|
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
render() {
|
|
88
|
-
return html` <canvas
|
|
89
|
-
class="h-full w-full object-fill"
|
|
90
|
-
${ref(this.canvasRef)}
|
|
91
|
-
></canvas>`;
|
|
88
|
+
return html` <canvas ${ref(this.canvasRef)}></canvas>`;
|
|
92
89
|
}
|
|
93
90
|
get canvasElement() {
|
|
94
91
|
return this.canvasRef.value;
|
|
@@ -100,6 +97,9 @@ EFVideo.styles = [
|
|
|
100
97
|
:host {
|
|
101
98
|
display: block;
|
|
102
99
|
}
|
|
100
|
+
canvas {
|
|
101
|
+
all: inherit;
|
|
102
|
+
}
|
|
103
103
|
`
|
|
104
104
|
];
|
|
105
105
|
EFVideo = __decorateClass([
|