@editframe/elements 0.6.0-beta.16 → 0.6.0-beta.18
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/lib/av/EncodedAsset.cjs +4 -1
- package/dist/lib/av/EncodedAsset.js +4 -1
- package/dist/packages/elements/src/EF_FRAMEGEN.d.ts +1 -1
- package/dist/packages/elements/src/elements/EFMedia.cjs +1 -1
- package/dist/packages/elements/src/elements/EFMedia.d.ts +1 -1
- package/dist/packages/elements/src/elements/EFMedia.js +1 -1
- package/dist/packages/elements/src/elements/EFTimegroup.cjs +38 -31
- package/dist/packages/elements/src/elements/EFTimegroup.d.ts +1 -1
- package/dist/packages/elements/src/elements/EFTimegroup.js +38 -31
- package/dist/packages/elements/src/gui/EFFilmstrip.cjs +128 -27
- package/dist/packages/elements/src/gui/EFFilmstrip.d.ts +7 -6
- package/dist/packages/elements/src/gui/EFFilmstrip.js +129 -28
- package/dist/packages/elements/src/gui/TWMixin.css.cjs +1 -1
- package/dist/packages/elements/src/gui/TWMixin.css.js +1 -1
- package/dist/packages/elements/src/index.cjs +6 -2
- package/dist/packages/elements/src/index.d.ts +2 -2
- package/dist/packages/elements/src/index.js +4 -3
- package/dist/style.css +21 -3
- package/package.json +2 -2
- package/src/elements/CrossUpdateController.ts +22 -0
- package/src/elements/EFAudio.ts +40 -0
- package/src/elements/EFCaptions.ts +188 -0
- package/src/elements/EFImage.ts +68 -0
- package/src/elements/EFMedia.ts +384 -0
- package/src/elements/EFSourceMixin.ts +57 -0
- package/src/elements/EFTemporal.ts +231 -0
- package/src/elements/EFTimegroup.browsertest.ts +333 -0
- package/src/elements/EFTimegroup.ts +389 -0
- package/src/elements/EFTimeline.ts +13 -0
- package/src/elements/EFVideo.ts +103 -0
- package/src/elements/EFWaveform.ts +409 -0
- package/src/elements/FetchMixin.ts +19 -0
- package/src/elements/TimegroupController.ts +25 -0
- package/src/elements/durationConverter.ts +6 -0
- package/src/elements/parseTimeToMs.ts +9 -0
- package/src/elements/util.ts +24 -0
- package/src/gui/EFFilmstrip.ts +878 -0
- package/src/gui/EFWorkbench.ts +231 -0
- package/src/gui/TWMixin.css +3 -0
- package/src/gui/TWMixin.ts +30 -0
|
@@ -436,7 +436,10 @@ const _VideoAsset = class _VideoAsset2 extends ISOFileAsset {
|
|
|
436
436
|
this.removeEventListener("frame", maybeFrame);
|
|
437
437
|
if (frame) {
|
|
438
438
|
if (this.lastSoughtFrame && !this.decodedFrames.includes(this.lastSoughtFrame)) {
|
|
439
|
-
|
|
439
|
+
try {
|
|
440
|
+
this.lastSoughtFrame.close();
|
|
441
|
+
} catch (error) {
|
|
442
|
+
}
|
|
440
443
|
}
|
|
441
444
|
this.lastSoughtFrame = frame;
|
|
442
445
|
}
|
|
@@ -417,7 +417,10 @@ const _VideoAsset = class _VideoAsset2 extends ISOFileAsset {
|
|
|
417
417
|
this.removeEventListener("frame", maybeFrame);
|
|
418
418
|
if (frame) {
|
|
419
419
|
if (this.lastSoughtFrame && !this.decodedFrames.includes(this.lastSoughtFrame)) {
|
|
420
|
-
|
|
420
|
+
try {
|
|
421
|
+
this.lastSoughtFrame.close();
|
|
422
|
+
} catch (error) {
|
|
423
|
+
}
|
|
421
424
|
}
|
|
422
425
|
this.lastSoughtFrame = frame;
|
|
423
426
|
}
|
|
@@ -212,7 +212,7 @@ class EFMedia extends EFSourceMixin.EFSourceMixin(EFTemporal.EFTemporal(FetchMix
|
|
|
212
212
|
if (this.src.startsWith("editframe://") || this.src.startsWith("http")) {
|
|
213
213
|
return `${this.src}/index`;
|
|
214
214
|
}
|
|
215
|
-
return `/@ef-track-fragment-index/${this.
|
|
215
|
+
return `/@ef-track-fragment-index/${this.src ?? ""}`;
|
|
216
216
|
}
|
|
217
217
|
fragmentTrackPath(trackId) {
|
|
218
218
|
if (this.src.startsWith("editframe://") || this.src.startsWith("http")) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LitElement, PropertyValueMap } from 'lit';
|
|
2
2
|
import { Task } from '@lit/task';
|
|
3
3
|
import { MP4File } from '../../../../lib/av/MP4File';
|
|
4
|
-
import { TrackFragmentIndex, TrackSegment } from '../../../assets';
|
|
4
|
+
import { TrackFragmentIndex, TrackSegment } from '../../../assets/src';
|
|
5
5
|
import { VideoAsset } from '../../../../lib/av/EncodedAsset';
|
|
6
6
|
|
|
7
7
|
import type * as MP4Box from "mp4box";
|
|
@@ -210,7 +210,7 @@ class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
|
210
210
|
if (this.src.startsWith("editframe://") || this.src.startsWith("http")) {
|
|
211
211
|
return `${this.src}/index`;
|
|
212
212
|
}
|
|
213
|
-
return `/@ef-track-fragment-index/${this.
|
|
213
|
+
return `/@ef-track-fragment-index/${this.src ?? ""}`;
|
|
214
214
|
}
|
|
215
215
|
fragmentTrackPath(trackId) {
|
|
216
216
|
if (this.src.startsWith("editframe://") || this.src.startsWith("http")) {
|
|
@@ -25,7 +25,8 @@ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot
|
|
|
25
25
|
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
26
26
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
27
27
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
28
|
-
var
|
|
28
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
29
|
+
var _currentTime, _EFTimegroup_instances, addAudioToContext_fn;
|
|
29
30
|
const shallowGetTimegroups = (element, groups = []) => {
|
|
30
31
|
for (const child of Array.from(element.children)) {
|
|
31
32
|
if (child instanceof exports.EFTimegroup) {
|
|
@@ -39,6 +40,7 @@ const shallowGetTimegroups = (element, groups = []) => {
|
|
|
39
40
|
exports.EFTimegroup = class EFTimegroup extends EFTemporal.EFTemporal(lit.LitElement) {
|
|
40
41
|
constructor() {
|
|
41
42
|
super(...arguments);
|
|
43
|
+
__privateAdd(this, _EFTimegroup_instances);
|
|
42
44
|
this._timeGroupContext = this;
|
|
43
45
|
__privateAdd(this, _currentTime, 0);
|
|
44
46
|
this.mode = "sequence";
|
|
@@ -240,42 +242,13 @@ exports.EFTimegroup = class EFTimegroup extends EFTemporal.EFTemporal(lit.LitEle
|
|
|
240
242
|
);
|
|
241
243
|
}
|
|
242
244
|
async renderAudio(fromMs, toMs) {
|
|
243
|
-
await this.waitForMediaDurations();
|
|
244
245
|
const durationMs = toMs - fromMs;
|
|
245
246
|
const audioContext = new OfflineAudioContext(
|
|
246
247
|
2,
|
|
247
248
|
Math.round(48e3 * durationMs / 1e3),
|
|
248
249
|
48e3
|
|
249
250
|
);
|
|
250
|
-
await
|
|
251
|
-
EFMedia.deepGetMediaElements(this).map(async (mediaElement) => {
|
|
252
|
-
await mediaElement.trackFragmentIndexLoader.taskComplete;
|
|
253
|
-
const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;
|
|
254
|
-
const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;
|
|
255
|
-
const mediaOverlaps = mediaStartsBeforeEnd && mediaEndsAfterStart;
|
|
256
|
-
if (!mediaOverlaps || mediaElement.defaultAudioTrackId === void 0) {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
const audio = await mediaElement.fetchAudioSpanningTime(fromMs, toMs);
|
|
260
|
-
if (!audio) {
|
|
261
|
-
throw new Error("Failed to fetch audio");
|
|
262
|
-
}
|
|
263
|
-
const ctxStartMs = Math.max(0, mediaElement.startTimeMs - fromMs);
|
|
264
|
-
const ctxEndMs = Math.min(durationMs, mediaElement.endTimeMs - fromMs);
|
|
265
|
-
const ctxDurationMs = ctxEndMs - ctxStartMs;
|
|
266
|
-
const offset = Math.max(0, fromMs - mediaElement.startTimeMs) - audio.startMs;
|
|
267
|
-
const bufferSource = audioContext.createBufferSource();
|
|
268
|
-
bufferSource.buffer = await audioContext.decodeAudioData(
|
|
269
|
-
await audio.blob.arrayBuffer()
|
|
270
|
-
);
|
|
271
|
-
bufferSource.connect(audioContext.destination);
|
|
272
|
-
bufferSource.start(
|
|
273
|
-
ctxStartMs / 1e3,
|
|
274
|
-
offset / 1e3,
|
|
275
|
-
ctxDurationMs / 1e3
|
|
276
|
-
);
|
|
277
|
-
})
|
|
278
|
-
);
|
|
251
|
+
await __privateMethod(this, _EFTimegroup_instances, addAudioToContext_fn).call(this, audioContext, fromMs, toMs);
|
|
279
252
|
return await audioContext.startRendering();
|
|
280
253
|
}
|
|
281
254
|
async loadMd5Sums() {
|
|
@@ -297,6 +270,40 @@ exports.EFTimegroup = class EFTimegroup extends EFTemporal.EFTemporal(lit.LitEle
|
|
|
297
270
|
}
|
|
298
271
|
};
|
|
299
272
|
_currentTime = /* @__PURE__ */ new WeakMap();
|
|
273
|
+
_EFTimegroup_instances = /* @__PURE__ */ new WeakSet();
|
|
274
|
+
addAudioToContext_fn = async function(audioContext, fromMs, toMs) {
|
|
275
|
+
await this.waitForMediaDurations();
|
|
276
|
+
const durationMs = toMs - fromMs;
|
|
277
|
+
await Promise.all(
|
|
278
|
+
EFMedia.deepGetMediaElements(this).map(async (mediaElement) => {
|
|
279
|
+
await mediaElement.trackFragmentIndexLoader.taskComplete;
|
|
280
|
+
const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;
|
|
281
|
+
const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;
|
|
282
|
+
const mediaOverlaps = mediaStartsBeforeEnd && mediaEndsAfterStart;
|
|
283
|
+
if (!mediaOverlaps || mediaElement.defaultAudioTrackId === void 0) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const audio = await mediaElement.fetchAudioSpanningTime(fromMs, toMs);
|
|
287
|
+
if (!audio) {
|
|
288
|
+
throw new Error("Failed to fetch audio");
|
|
289
|
+
}
|
|
290
|
+
const ctxStartMs = Math.max(0, mediaElement.startTimeMs - fromMs);
|
|
291
|
+
const ctxEndMs = Math.min(durationMs, mediaElement.endTimeMs - fromMs);
|
|
292
|
+
const ctxDurationMs = ctxEndMs - ctxStartMs;
|
|
293
|
+
const offset = Math.max(0, fromMs - mediaElement.startTimeMs) - audio.startMs;
|
|
294
|
+
const bufferSource = audioContext.createBufferSource();
|
|
295
|
+
bufferSource.buffer = await audioContext.decodeAudioData(
|
|
296
|
+
await audio.blob.arrayBuffer()
|
|
297
|
+
);
|
|
298
|
+
bufferSource.connect(audioContext.destination);
|
|
299
|
+
bufferSource.start(
|
|
300
|
+
ctxStartMs / 1e3,
|
|
301
|
+
offset / 1e3,
|
|
302
|
+
ctxDurationMs / 1e3
|
|
303
|
+
);
|
|
304
|
+
})
|
|
305
|
+
);
|
|
306
|
+
};
|
|
300
307
|
exports.EFTimegroup.styles = lit.css`
|
|
301
308
|
:host {
|
|
302
309
|
display: block;
|
|
@@ -20,7 +20,7 @@ export declare class EFTimegroup extends EFTimegroup_base {
|
|
|
20
20
|
get crossoverStartMs(): number;
|
|
21
21
|
get crossoverEndMs(): number;
|
|
22
22
|
get durationMs(): number;
|
|
23
|
-
waitForMediaDurations(): Promise<Record<number, import('../../../assets/
|
|
23
|
+
waitForMediaDurations(): Promise<Record<number, import('../../../assets/src').TrackFragmentIndex>[]>;
|
|
24
24
|
get childTemporals(): import('./EFTemporal').TemporalMixinInterface[];
|
|
25
25
|
protected updated(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
26
26
|
shouldWrapWithWorkbench(): boolean;
|
|
@@ -23,7 +23,8 @@ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot
|
|
|
23
23
|
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
24
24
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
25
25
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
26
|
-
var
|
|
26
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
27
|
+
var _currentTime, _EFTimegroup_instances, addAudioToContext_fn;
|
|
27
28
|
const shallowGetTimegroups = (element, groups = []) => {
|
|
28
29
|
for (const child of Array.from(element.children)) {
|
|
29
30
|
if (child instanceof EFTimegroup) {
|
|
@@ -37,6 +38,7 @@ const shallowGetTimegroups = (element, groups = []) => {
|
|
|
37
38
|
let EFTimegroup = class extends EFTemporal(LitElement) {
|
|
38
39
|
constructor() {
|
|
39
40
|
super(...arguments);
|
|
41
|
+
__privateAdd(this, _EFTimegroup_instances);
|
|
40
42
|
this._timeGroupContext = this;
|
|
41
43
|
__privateAdd(this, _currentTime, 0);
|
|
42
44
|
this.mode = "sequence";
|
|
@@ -238,42 +240,13 @@ let EFTimegroup = class extends EFTemporal(LitElement) {
|
|
|
238
240
|
);
|
|
239
241
|
}
|
|
240
242
|
async renderAudio(fromMs, toMs) {
|
|
241
|
-
await this.waitForMediaDurations();
|
|
242
243
|
const durationMs = toMs - fromMs;
|
|
243
244
|
const audioContext = new OfflineAudioContext(
|
|
244
245
|
2,
|
|
245
246
|
Math.round(48e3 * durationMs / 1e3),
|
|
246
247
|
48e3
|
|
247
248
|
);
|
|
248
|
-
await
|
|
249
|
-
deepGetMediaElements(this).map(async (mediaElement) => {
|
|
250
|
-
await mediaElement.trackFragmentIndexLoader.taskComplete;
|
|
251
|
-
const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;
|
|
252
|
-
const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;
|
|
253
|
-
const mediaOverlaps = mediaStartsBeforeEnd && mediaEndsAfterStart;
|
|
254
|
-
if (!mediaOverlaps || mediaElement.defaultAudioTrackId === void 0) {
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
const audio = await mediaElement.fetchAudioSpanningTime(fromMs, toMs);
|
|
258
|
-
if (!audio) {
|
|
259
|
-
throw new Error("Failed to fetch audio");
|
|
260
|
-
}
|
|
261
|
-
const ctxStartMs = Math.max(0, mediaElement.startTimeMs - fromMs);
|
|
262
|
-
const ctxEndMs = Math.min(durationMs, mediaElement.endTimeMs - fromMs);
|
|
263
|
-
const ctxDurationMs = ctxEndMs - ctxStartMs;
|
|
264
|
-
const offset = Math.max(0, fromMs - mediaElement.startTimeMs) - audio.startMs;
|
|
265
|
-
const bufferSource = audioContext.createBufferSource();
|
|
266
|
-
bufferSource.buffer = await audioContext.decodeAudioData(
|
|
267
|
-
await audio.blob.arrayBuffer()
|
|
268
|
-
);
|
|
269
|
-
bufferSource.connect(audioContext.destination);
|
|
270
|
-
bufferSource.start(
|
|
271
|
-
ctxStartMs / 1e3,
|
|
272
|
-
offset / 1e3,
|
|
273
|
-
ctxDurationMs / 1e3
|
|
274
|
-
);
|
|
275
|
-
})
|
|
276
|
-
);
|
|
249
|
+
await __privateMethod(this, _EFTimegroup_instances, addAudioToContext_fn).call(this, audioContext, fromMs, toMs);
|
|
277
250
|
return await audioContext.startRendering();
|
|
278
251
|
}
|
|
279
252
|
async loadMd5Sums() {
|
|
@@ -295,6 +268,40 @@ let EFTimegroup = class extends EFTemporal(LitElement) {
|
|
|
295
268
|
}
|
|
296
269
|
};
|
|
297
270
|
_currentTime = /* @__PURE__ */ new WeakMap();
|
|
271
|
+
_EFTimegroup_instances = /* @__PURE__ */ new WeakSet();
|
|
272
|
+
addAudioToContext_fn = async function(audioContext, fromMs, toMs) {
|
|
273
|
+
await this.waitForMediaDurations();
|
|
274
|
+
const durationMs = toMs - fromMs;
|
|
275
|
+
await Promise.all(
|
|
276
|
+
deepGetMediaElements(this).map(async (mediaElement) => {
|
|
277
|
+
await mediaElement.trackFragmentIndexLoader.taskComplete;
|
|
278
|
+
const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;
|
|
279
|
+
const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;
|
|
280
|
+
const mediaOverlaps = mediaStartsBeforeEnd && mediaEndsAfterStart;
|
|
281
|
+
if (!mediaOverlaps || mediaElement.defaultAudioTrackId === void 0) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const audio = await mediaElement.fetchAudioSpanningTime(fromMs, toMs);
|
|
285
|
+
if (!audio) {
|
|
286
|
+
throw new Error("Failed to fetch audio");
|
|
287
|
+
}
|
|
288
|
+
const ctxStartMs = Math.max(0, mediaElement.startTimeMs - fromMs);
|
|
289
|
+
const ctxEndMs = Math.min(durationMs, mediaElement.endTimeMs - fromMs);
|
|
290
|
+
const ctxDurationMs = ctxEndMs - ctxStartMs;
|
|
291
|
+
const offset = Math.max(0, fromMs - mediaElement.startTimeMs) - audio.startMs;
|
|
292
|
+
const bufferSource = audioContext.createBufferSource();
|
|
293
|
+
bufferSource.buffer = await audioContext.decodeAudioData(
|
|
294
|
+
await audio.blob.arrayBuffer()
|
|
295
|
+
);
|
|
296
|
+
bufferSource.connect(audioContext.destination);
|
|
297
|
+
bufferSource.start(
|
|
298
|
+
ctxStartMs / 1e3,
|
|
299
|
+
offset / 1e3,
|
|
300
|
+
ctxDurationMs / 1e3
|
|
301
|
+
);
|
|
302
|
+
})
|
|
303
|
+
);
|
|
304
|
+
};
|
|
298
305
|
EFTimegroup.styles = css`
|
|
299
306
|
:host {
|
|
300
307
|
display: block;
|
|
@@ -32,7 +32,8 @@ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot
|
|
|
32
32
|
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), member.get(obj));
|
|
33
33
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
34
34
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
35
|
-
var
|
|
35
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
36
|
+
var _handleKeyPress, _lastTick, _playbackAudioContext, _playbackAnimationFrameRequest, _AUDIO_PLAYBACK_SLICE_MS, _EFFilmstrip_instances, syncPlayheadToAudioContext_fn, stopPlayback_fn, startPlayback_fn;
|
|
36
37
|
class ElementFilmstripController {
|
|
37
38
|
constructor(host, filmstrip) {
|
|
38
39
|
this.host = host;
|
|
@@ -94,7 +95,7 @@ class FilmstripItem extends TWMixin.TWMixin(lit.LitElement) {
|
|
|
94
95
|
}
|
|
95
96
|
}}
|
|
96
97
|
?data-focused=${this.isFocused}
|
|
97
|
-
class="border-outset relative mb-[1px] block h-[1.
|
|
98
|
+
class="border-outset relative mb-[1px] block h-[1.1rem] text-nowrap border border-slate-500 bg-blue-200 text-sm data-[focused]:bg-slate-400"
|
|
98
99
|
>
|
|
99
100
|
${this.animations()}
|
|
100
101
|
</div>
|
|
@@ -102,7 +103,7 @@ class FilmstripItem extends TWMixin.TWMixin(lit.LitElement) {
|
|
|
102
103
|
</div>`;
|
|
103
104
|
}
|
|
104
105
|
renderChildren() {
|
|
105
|
-
return
|
|
106
|
+
return renderFilmstripChildren(
|
|
106
107
|
Array.from(this.element.children),
|
|
107
108
|
this.pixelsPerMs
|
|
108
109
|
);
|
|
@@ -221,7 +222,7 @@ exports.EFTimegroupFilmstrip = class EFTimegroupFilmstrip extends FilmstripItem
|
|
|
221
222
|
contents() {
|
|
222
223
|
return lit.html`
|
|
223
224
|
<span>TIME GROUP</span>
|
|
224
|
-
${
|
|
225
|
+
${renderFilmstripChildren(
|
|
225
226
|
Array.from(this.element.children || []),
|
|
226
227
|
this.pixelsPerMs
|
|
227
228
|
)}
|
|
@@ -236,7 +237,7 @@ exports.EFHTMLFilmstrip = class EFHTMLFilmstrip extends FilmstripItem {
|
|
|
236
237
|
contents() {
|
|
237
238
|
return lit.html`
|
|
238
239
|
<span>${this.element.tagName}</span>
|
|
239
|
-
${
|
|
240
|
+
${renderFilmstripChildren(
|
|
240
241
|
Array.from(this.element.children || []),
|
|
241
242
|
this.pixelsPerMs
|
|
242
243
|
)}
|
|
@@ -266,8 +267,8 @@ let EFHierarchyItem = class extends TWMixin.TWMixin(lit.LitElement) {
|
|
|
266
267
|
<div
|
|
267
268
|
?data-focused=${this.isFocused}
|
|
268
269
|
class="peer
|
|
269
|
-
flex h-[1.
|
|
270
|
-
bg-slate-200 pl-2 text-
|
|
270
|
+
flex h-[1.1rem] items-center overflow-hidden text-nowrap border border-slate-500
|
|
271
|
+
bg-slate-200 pl-2 text-xs font-mono hover:bg-slate-400 data-[focused]:bg-slate-400"
|
|
271
272
|
@mouseenter=${() => {
|
|
272
273
|
if (this.focusContext) {
|
|
273
274
|
this.focusContext.focusedElement = this.element;
|
|
@@ -320,7 +321,7 @@ let EFAudioHierarchyItem = class extends EFHierarchyItem {
|
|
|
320
321
|
return "🔊";
|
|
321
322
|
}
|
|
322
323
|
displayLabel() {
|
|
323
|
-
return this.element.
|
|
324
|
+
return this.element.src ?? "(no src)";
|
|
324
325
|
}
|
|
325
326
|
};
|
|
326
327
|
EFAudioHierarchyItem = __decorateClass([
|
|
@@ -331,7 +332,7 @@ let EFVideoHierarchyItem = class extends EFHierarchyItem {
|
|
|
331
332
|
return "📼";
|
|
332
333
|
}
|
|
333
334
|
displayLabel() {
|
|
334
|
-
return this.element.
|
|
335
|
+
return this.element.src ?? "(no src)";
|
|
335
336
|
}
|
|
336
337
|
};
|
|
337
338
|
EFVideoHierarchyItem = __decorateClass([
|
|
@@ -369,7 +370,7 @@ let EFImageHierarchyItem = class extends EFHierarchyItem {
|
|
|
369
370
|
return "🖼️";
|
|
370
371
|
}
|
|
371
372
|
displayLabel() {
|
|
372
|
-
return this.element.
|
|
373
|
+
return this.element.src ?? "(no src)";
|
|
373
374
|
}
|
|
374
375
|
};
|
|
375
376
|
EFImageHierarchyItem = __decorateClass([
|
|
@@ -425,7 +426,7 @@ const renderHierarchyChildren = (children) => {
|
|
|
425
426
|
></ef-html-hierarchy-item>`;
|
|
426
427
|
});
|
|
427
428
|
};
|
|
428
|
-
const
|
|
429
|
+
const renderFilmstripChildren = (children, pixelsPerMs) => {
|
|
429
430
|
return children.map((child) => {
|
|
430
431
|
if (child instanceof EFTimegroup.EFTimegroup) {
|
|
431
432
|
return lit.html`<ef-timegroup-filmstrip
|
|
@@ -470,16 +471,33 @@ const renderFilmStripChildren = (children, pixelsPerMs) => {
|
|
|
470
471
|
></ef-html-filmstrip>`;
|
|
471
472
|
});
|
|
472
473
|
};
|
|
473
|
-
exports.
|
|
474
|
+
exports.EFFilmstrip = class EFFilmstrip extends TWMixin.TWMixin(lit.LitElement) {
|
|
474
475
|
constructor() {
|
|
475
476
|
super(...arguments);
|
|
477
|
+
__privateAdd(this, _EFFilmstrip_instances);
|
|
476
478
|
this.pixelsPerMs = 0.04;
|
|
477
479
|
this.currentTimeMs = 0;
|
|
478
480
|
this.targetSelector = "";
|
|
479
481
|
this.scrubbing = false;
|
|
480
482
|
this.playing = false;
|
|
481
483
|
this.timelineScrolltop = 0;
|
|
484
|
+
__privateAdd(this, _handleKeyPress, (event) => {
|
|
485
|
+
if (event.key === " ") {
|
|
486
|
+
const interactiveSelector = "input, textarea, button, select, a, [contenteditable]";
|
|
487
|
+
const closestInteractive = event.target?.closest(
|
|
488
|
+
interactiveSelector
|
|
489
|
+
);
|
|
490
|
+
if (closestInteractive) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
event.preventDefault();
|
|
494
|
+
this.playing = !this.playing;
|
|
495
|
+
}
|
|
496
|
+
});
|
|
482
497
|
__privateAdd(this, _lastTick);
|
|
498
|
+
__privateAdd(this, _playbackAudioContext, null);
|
|
499
|
+
__privateAdd(this, _playbackAnimationFrameRequest, null);
|
|
500
|
+
__privateAdd(this, _AUDIO_PLAYBACK_SLICE_MS, 1e3);
|
|
483
501
|
this.advancePlayhead = (tick) => {
|
|
484
502
|
if (__privateGet(this, _lastTick) && tick && this.target) {
|
|
485
503
|
this.target.currentTimeMs += tick - __privateGet(this, _lastTick);
|
|
@@ -503,6 +521,11 @@ exports.EFFilmStrip = class EFFilmStrip extends TWMixin.TWMixin(lit.LitElement)
|
|
|
503
521
|
this.timegroupController = new TimegroupController.TimegroupController(target, this);
|
|
504
522
|
this.currentTimeMs = target.currentTimeMs;
|
|
505
523
|
}
|
|
524
|
+
window.addEventListener("keypress", __privateGet(this, _handleKeyPress));
|
|
525
|
+
}
|
|
526
|
+
disconnectedCallback() {
|
|
527
|
+
super.disconnectedCallback();
|
|
528
|
+
window.removeEventListener("keypress", __privateGet(this, _handleKeyPress));
|
|
506
529
|
}
|
|
507
530
|
syncGutterScroll() {
|
|
508
531
|
if (this.gutter && this.hierarchyRef.value) {
|
|
@@ -643,7 +666,7 @@ exports.EFFilmStrip = class EFFilmStrip extends TWMixin.TWMixin(lit.LitElement)
|
|
|
643
666
|
${ref_js.ref(this.playheadRef)}
|
|
644
667
|
></div>
|
|
645
668
|
|
|
646
|
-
${
|
|
669
|
+
${renderFilmstripChildren(
|
|
647
670
|
Array.from(target?.children || []),
|
|
648
671
|
this.pixelsPerMs
|
|
649
672
|
)}
|
|
@@ -654,7 +677,9 @@ exports.EFFilmStrip = class EFFilmStrip extends TWMixin.TWMixin(lit.LitElement)
|
|
|
654
677
|
update(changedProperties) {
|
|
655
678
|
if (changedProperties.has("playing")) {
|
|
656
679
|
if (this.playing) {
|
|
657
|
-
this.
|
|
680
|
+
__privateMethod(this, _EFFilmstrip_instances, startPlayback_fn).call(this);
|
|
681
|
+
} else {
|
|
682
|
+
__privateMethod(this, _EFFilmstrip_instances, stopPlayback_fn).call(this);
|
|
658
683
|
}
|
|
659
684
|
}
|
|
660
685
|
super.update(changedProperties);
|
|
@@ -680,40 +705,116 @@ exports.EFFilmStrip = class EFFilmStrip extends TWMixin.TWMixin(lit.LitElement)
|
|
|
680
705
|
return void 0;
|
|
681
706
|
}
|
|
682
707
|
};
|
|
708
|
+
_handleKeyPress = /* @__PURE__ */ new WeakMap();
|
|
683
709
|
_lastTick = /* @__PURE__ */ new WeakMap();
|
|
710
|
+
_playbackAudioContext = /* @__PURE__ */ new WeakMap();
|
|
711
|
+
_playbackAnimationFrameRequest = /* @__PURE__ */ new WeakMap();
|
|
712
|
+
_AUDIO_PLAYBACK_SLICE_MS = /* @__PURE__ */ new WeakMap();
|
|
713
|
+
_EFFilmstrip_instances = /* @__PURE__ */ new WeakSet();
|
|
714
|
+
syncPlayheadToAudioContext_fn = function(target, startMs) {
|
|
715
|
+
target.currentTimeMs = startMs + (__privateGet(this, _playbackAudioContext)?.currentTime ?? 0) * 1e3;
|
|
716
|
+
__privateSet(this, _playbackAnimationFrameRequest, requestAnimationFrame(() => {
|
|
717
|
+
__privateMethod(this, _EFFilmstrip_instances, syncPlayheadToAudioContext_fn).call(this, target, startMs);
|
|
718
|
+
}));
|
|
719
|
+
};
|
|
720
|
+
stopPlayback_fn = async function() {
|
|
721
|
+
if (__privateGet(this, _playbackAudioContext)) {
|
|
722
|
+
if (__privateGet(this, _playbackAudioContext).state !== "closed") {
|
|
723
|
+
await __privateGet(this, _playbackAudioContext).close();
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
if (__privateGet(this, _playbackAnimationFrameRequest)) {
|
|
727
|
+
cancelAnimationFrame(__privateGet(this, _playbackAnimationFrameRequest));
|
|
728
|
+
}
|
|
729
|
+
__privateSet(this, _playbackAudioContext, null);
|
|
730
|
+
};
|
|
731
|
+
startPlayback_fn = async function() {
|
|
732
|
+
await __privateMethod(this, _EFFilmstrip_instances, stopPlayback_fn).call(this);
|
|
733
|
+
const timegroup = this.target;
|
|
734
|
+
if (!timegroup) {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
let currentMs = timegroup.currentTimeMs;
|
|
738
|
+
let bufferCount = 0;
|
|
739
|
+
__privateSet(this, _playbackAudioContext, new AudioContext({
|
|
740
|
+
latencyHint: "playback"
|
|
741
|
+
}));
|
|
742
|
+
if (__privateGet(this, _playbackAnimationFrameRequest)) {
|
|
743
|
+
cancelAnimationFrame(__privateGet(this, _playbackAnimationFrameRequest));
|
|
744
|
+
}
|
|
745
|
+
__privateMethod(this, _EFFilmstrip_instances, syncPlayheadToAudioContext_fn).call(this, timegroup, currentMs);
|
|
746
|
+
const playbackContext = __privateGet(this, _playbackAudioContext);
|
|
747
|
+
await playbackContext.suspend();
|
|
748
|
+
const fillBuffer = async () => {
|
|
749
|
+
if (bufferCount > 1) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
const canFillBuffer = await queueBufferSource();
|
|
753
|
+
if (canFillBuffer) {
|
|
754
|
+
fillBuffer();
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
const fromMs = currentMs;
|
|
758
|
+
const toMs = timegroup.endTimeMs;
|
|
759
|
+
const queueBufferSource = async () => {
|
|
760
|
+
if (currentMs >= toMs) {
|
|
761
|
+
return false;
|
|
762
|
+
}
|
|
763
|
+
const startMs = currentMs;
|
|
764
|
+
const endMs = currentMs + __privateGet(this, _AUDIO_PLAYBACK_SLICE_MS);
|
|
765
|
+
currentMs += __privateGet(this, _AUDIO_PLAYBACK_SLICE_MS);
|
|
766
|
+
const audioBuffer = await timegroup.renderAudio(startMs, endMs);
|
|
767
|
+
bufferCount++;
|
|
768
|
+
const source = playbackContext.createBufferSource();
|
|
769
|
+
source.buffer = audioBuffer;
|
|
770
|
+
source.connect(playbackContext.destination);
|
|
771
|
+
source.start((startMs - fromMs) / 1e3);
|
|
772
|
+
source.onended = () => {
|
|
773
|
+
bufferCount--;
|
|
774
|
+
if (endMs >= toMs) {
|
|
775
|
+
this.playing = false;
|
|
776
|
+
} else {
|
|
777
|
+
fillBuffer();
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
return true;
|
|
781
|
+
};
|
|
782
|
+
await fillBuffer();
|
|
783
|
+
await playbackContext.resume();
|
|
784
|
+
};
|
|
684
785
|
__decorateClass([
|
|
685
786
|
decorators_js.property({ type: Number })
|
|
686
|
-
], exports.
|
|
787
|
+
], exports.EFFilmstrip.prototype, "pixelsPerMs", 2);
|
|
687
788
|
__decorateClass([
|
|
688
789
|
decorators_js.property({ type: Number })
|
|
689
|
-
], exports.
|
|
790
|
+
], exports.EFFilmstrip.prototype, "currentTimeMs", 2);
|
|
690
791
|
__decorateClass([
|
|
691
792
|
decorators_js.property({ type: String, attribute: "target" })
|
|
692
|
-
], exports.
|
|
793
|
+
], exports.EFFilmstrip.prototype, "targetSelector", 2);
|
|
693
794
|
__decorateClass([
|
|
694
795
|
decorators_js.state()
|
|
695
|
-
], exports.
|
|
796
|
+
], exports.EFFilmstrip.prototype, "scrubbing", 2);
|
|
696
797
|
__decorateClass([
|
|
697
798
|
decorators_js.state()
|
|
698
|
-
], exports.
|
|
799
|
+
], exports.EFFilmstrip.prototype, "playing", 2);
|
|
699
800
|
__decorateClass([
|
|
700
801
|
decorators_js.state()
|
|
701
|
-
], exports.
|
|
802
|
+
], exports.EFFilmstrip.prototype, "timelineScrolltop", 2);
|
|
702
803
|
__decorateClass([
|
|
703
804
|
decorators_js.eventOptions({ passive: false })
|
|
704
|
-
], exports.
|
|
805
|
+
], exports.EFFilmstrip.prototype, "syncGutterScroll", 1);
|
|
705
806
|
__decorateClass([
|
|
706
807
|
decorators_js.eventOptions({ passive: false })
|
|
707
|
-
], exports.
|
|
808
|
+
], exports.EFFilmstrip.prototype, "syncHierarchyScroll", 1);
|
|
708
809
|
__decorateClass([
|
|
709
810
|
decorators_js.eventOptions({ capture: false })
|
|
710
|
-
], exports.
|
|
811
|
+
], exports.EFFilmstrip.prototype, "scrub", 1);
|
|
711
812
|
__decorateClass([
|
|
712
813
|
decorators_js.eventOptions({ capture: false })
|
|
713
|
-
], exports.
|
|
814
|
+
], exports.EFFilmstrip.prototype, "startScrub", 1);
|
|
714
815
|
__decorateClass([
|
|
715
816
|
decorators_js.eventOptions({ passive: false })
|
|
716
|
-
], exports.
|
|
717
|
-
exports.
|
|
817
|
+
], exports.EFFilmstrip.prototype, "scrollScrub", 1);
|
|
818
|
+
exports.EFFilmstrip = __decorateClass([
|
|
718
819
|
decorators_js.customElement("ef-filmstrip")
|
|
719
|
-
], exports.
|
|
820
|
+
], exports.EFFilmstrip);
|
|
@@ -71,11 +71,11 @@ declare class EFTimegroupHierarchyItem extends EFHierarchyItem<EFTimegroup> {
|
|
|
71
71
|
}
|
|
72
72
|
declare class EFAudioHierarchyItem extends EFHierarchyItem {
|
|
73
73
|
get icon(): string;
|
|
74
|
-
displayLabel():
|
|
74
|
+
displayLabel(): any;
|
|
75
75
|
}
|
|
76
76
|
declare class EFVideoHierarchyItem extends EFHierarchyItem {
|
|
77
77
|
get icon(): string;
|
|
78
|
-
displayLabel():
|
|
78
|
+
displayLabel(): any;
|
|
79
79
|
}
|
|
80
80
|
declare class EFCaptionsHierarchyItem extends EFHierarchyItem {
|
|
81
81
|
get icon(): string;
|
|
@@ -89,13 +89,13 @@ declare class EFWaveformHierarchyItem extends EFHierarchyItem {
|
|
|
89
89
|
}
|
|
90
90
|
declare class EFImageHierarchyItem extends EFHierarchyItem {
|
|
91
91
|
get icon(): string;
|
|
92
|
-
displayLabel():
|
|
92
|
+
displayLabel(): any;
|
|
93
93
|
}
|
|
94
94
|
declare class EFHTMLHierarchyItem extends EFHierarchyItem {
|
|
95
95
|
get icon(): TemplateResult<1>;
|
|
96
96
|
}
|
|
97
|
-
declare const
|
|
98
|
-
export declare class
|
|
97
|
+
declare const EFFilmstrip_base: typeof LitElement;
|
|
98
|
+
export declare class EFFilmstrip extends EFFilmstrip_base {
|
|
99
99
|
#private;
|
|
100
100
|
pixelsPerMs: number;
|
|
101
101
|
currentTimeMs: number;
|
|
@@ -105,6 +105,7 @@ export declare class EFFilmStrip extends EFFilmStrip_base {
|
|
|
105
105
|
timelineScrolltop: number;
|
|
106
106
|
timegroupController?: TimegroupController;
|
|
107
107
|
connectedCallback(): void;
|
|
108
|
+
disconnectedCallback(): void;
|
|
108
109
|
syncGutterScroll(): void;
|
|
109
110
|
syncHierarchyScroll(): void;
|
|
110
111
|
advancePlayhead: (tick?: DOMHighResTimeStamp) => void;
|
|
@@ -122,7 +123,7 @@ export declare class EFFilmStrip extends EFFilmStrip_base {
|
|
|
122
123
|
}
|
|
123
124
|
declare global {
|
|
124
125
|
interface HTMLElementTagNameMap {
|
|
125
|
-
"ef-filmstrip":
|
|
126
|
+
"ef-filmstrip": EFFilmstrip;
|
|
126
127
|
"ef-timegroup-hierarchy-item": EFTimegroupHierarchyItem;
|
|
127
128
|
"ef-audio-hierarchy-item": EFAudioHierarchyItem;
|
|
128
129
|
"ef-video-hierarchy-item": EFVideoHierarchyItem;
|