@editframe/elements 0.5.0-beta.4 → 0.5.0-beta.5

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 (32) hide show
  1. package/dist/editor/msToTimeCode.mjs +15 -0
  2. package/dist/editor/util/EncodedAsset/EncodedAsset.mjs +537 -0
  3. package/dist/editor/util/MP4File.mjs +161 -0
  4. package/dist/elements/elements/CrossUpdateController.mjs +16 -0
  5. package/dist/elements/elements/EFAudio.mjs +37 -0
  6. package/dist/elements/elements/EFCaptions.mjs +172 -0
  7. package/dist/elements/elements/EFImage.mjs +67 -0
  8. package/dist/elements/elements/EFMedia.mjs +255 -0
  9. package/dist/elements/elements/EFSourceMixin.mjs +57 -0
  10. package/dist/elements/elements/EFTemporal.mjs +186 -0
  11. package/dist/elements/elements/EFTimegroup.mjs +230 -0
  12. package/dist/elements/elements/EFTimeline.mjs +12 -0
  13. package/dist/elements/elements/EFVideo.mjs +123 -0
  14. package/dist/elements/elements/EFWaveform.mjs +203 -0
  15. package/dist/elements/elements/FetchMixin.mjs +30 -0
  16. package/dist/elements/elements/TimegroupController.mjs +20 -0
  17. package/dist/elements/elements/durationConverter.mjs +8 -0
  18. package/dist/elements/elements/parseTimeToMs.mjs +13 -0
  19. package/dist/elements/elements/util.mjs +11 -0
  20. package/dist/elements/elements.css.mjs +1 -0
  21. package/dist/elements/elements.mjs +11 -0
  22. package/dist/elements/gui/EFFilmstrip.mjs +680 -0
  23. package/dist/elements/gui/EFWorkbench.mjs +234 -0
  24. package/dist/elements/gui/TWMixin.css.mjs +4 -0
  25. package/dist/elements/gui/TWMixin.mjs +27 -0
  26. package/dist/style.css +754 -0
  27. package/dist/util/awaitMicrotask.mjs +8 -0
  28. package/dist/util/memoize.mjs +15 -0
  29. package/package.json +9 -2
  30. package/dist/editframe-elements.css +0 -1
  31. package/dist/editframe-elements.mjs +0 -9089
  32. package/dist/editframe-elements.umd.js +0 -288
@@ -0,0 +1,186 @@
1
+ import { createContext, consume } from "@lit/context";
2
+ import { property, state } from "lit/decorators.js";
3
+ import { EFTimegroup } from "./EFTimegroup.mjs";
4
+ import { durationConverter } from "./durationConverter.mjs";
5
+ import { Task } from "@lit/task";
6
+ var __defProp = Object.defineProperty;
7
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
+ var __decorateClass = (decorators, target, key, kind) => {
9
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
10
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
11
+ if (decorator = decorators[i])
12
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
13
+ if (kind && result)
14
+ __defProp(target, key, result);
15
+ return result;
16
+ };
17
+ const timegroupContext = createContext(
18
+ Symbol("timeGroupContext")
19
+ );
20
+ const isEFTemporal = (obj) => obj[EF_TEMPORAL];
21
+ const EF_TEMPORAL = Symbol("EF_TEMPORAL");
22
+ const deepGetTemporalElements = (element, temporals = []) => {
23
+ for (const child of element.children) {
24
+ if (isEFTemporal(child)) {
25
+ temporals.push(child);
26
+ }
27
+ deepGetTemporalElements(child, temporals);
28
+ }
29
+ return temporals;
30
+ };
31
+ const shallowGetTemporalElements = (element, temporals = []) => {
32
+ for (const child of element.children) {
33
+ if (isEFTemporal(child)) {
34
+ temporals.push(child);
35
+ } else {
36
+ shallowGetTemporalElements(child, temporals);
37
+ }
38
+ }
39
+ return temporals;
40
+ };
41
+ const shallowGetTimegroups = (element, groups = []) => {
42
+ for (const child of element.children) {
43
+ if (child instanceof EFTimegroup) {
44
+ groups.push(child);
45
+ } else {
46
+ shallowGetTimegroups(child, groups);
47
+ }
48
+ }
49
+ return groups;
50
+ };
51
+ class OwnCurrentTimeController {
52
+ constructor(host, temporal) {
53
+ this.host = host;
54
+ this.temporal = temporal;
55
+ host.addController(this);
56
+ }
57
+ hostUpdated() {
58
+ this.temporal.requestUpdate("ownCurrentTimeMs");
59
+ }
60
+ remove() {
61
+ this.host.removeController(this);
62
+ }
63
+ }
64
+ const EFTemporal = (superClass) => {
65
+ class TemporalMixinClass extends superClass {
66
+ constructor() {
67
+ super(...arguments);
68
+ this._offsetMs = 0;
69
+ this.rootTimegroup = this.getRootTimegroup();
70
+ this.frameTask = new Task(this, {
71
+ autoRun: false,
72
+ args: () => [this.ownCurrentTimeMs],
73
+ task: async ([], { signal }) => {
74
+ }
75
+ });
76
+ }
77
+ #parentTimegroup;
78
+ set parentTimegroup(value) {
79
+ this.#parentTimegroup = value;
80
+ this.ownCurrentTimeController?.remove();
81
+ this.rootTimegroup = this.getRootTimegroup();
82
+ if (this.rootTimegroup) {
83
+ this.ownCurrentTimeController = new OwnCurrentTimeController(
84
+ this.rootTimegroup,
85
+ this
86
+ );
87
+ }
88
+ }
89
+ get parentTimegroup() {
90
+ return this.#parentTimegroup;
91
+ }
92
+ get offsetAppliesToDuration() {
93
+ return !(this.parentTimegroup?.mode === "sequence");
94
+ }
95
+ getRootTimegroup() {
96
+ let parent = this instanceof EFTimegroup ? this : this.parentTimegroup;
97
+ while (parent?.parentTimegroup) {
98
+ parent = parent.parentTimegroup;
99
+ }
100
+ return parent;
101
+ }
102
+ get hasOwnDuration() {
103
+ return false;
104
+ }
105
+ // Defining this as a getter to a private property allows us to
106
+ // override it classes that include this mixin.
107
+ get durationMs() {
108
+ let durationMs = this._durationMs || this.parentTimegroup?.durationMs || 0;
109
+ return durationMs || 0;
110
+ }
111
+ get offsetMs() {
112
+ return this._offsetMs || 0;
113
+ }
114
+ get startTimeMs() {
115
+ const parentTimegroup = this.parentTimegroup;
116
+ if (!parentTimegroup) {
117
+ return 0;
118
+ }
119
+ switch (parentTimegroup.mode) {
120
+ case "sequence": {
121
+ const siblingTemorals = shallowGetTemporalElements(parentTimegroup);
122
+ const ownIndex = siblingTemorals.indexOf(this);
123
+ if (ownIndex === 0) {
124
+ return parentTimegroup.startTimeMs;
125
+ }
126
+ const previous = siblingTemorals[ownIndex - 1];
127
+ if (!previous) {
128
+ throw new Error("Previous temporal element not found");
129
+ }
130
+ return previous.startTimeMs + previous.durationMs;
131
+ }
132
+ case "contain":
133
+ case "fixed":
134
+ return parentTimegroup.startTimeMs + this.offsetMs;
135
+ default:
136
+ throw new Error(`Invalid time mode: ${parentTimegroup.mode}`);
137
+ }
138
+ }
139
+ get endTimeMs() {
140
+ return this.startTimeMs + this.durationMs;
141
+ }
142
+ get ownCurrentTimeMs() {
143
+ if (this.rootTimegroup) {
144
+ return Math.min(
145
+ Math.max(0, this.rootTimegroup.currentTimeMs - this.startTimeMs),
146
+ this.durationMs
147
+ );
148
+ }
149
+ return 0;
150
+ }
151
+ }
152
+ __decorateClass([
153
+ consume({ context: timegroupContext, subscribe: true }),
154
+ property({ attribute: false })
155
+ ], TemporalMixinClass.prototype, "parentTimegroup", 1);
156
+ __decorateClass([
157
+ property({
158
+ type: String,
159
+ attribute: "offset",
160
+ converter: durationConverter
161
+ })
162
+ ], TemporalMixinClass.prototype, "_offsetMs", 2);
163
+ __decorateClass([
164
+ property({
165
+ type: Number,
166
+ attribute: "duration",
167
+ converter: durationConverter
168
+ })
169
+ ], TemporalMixinClass.prototype, "_durationMs", 2);
170
+ __decorateClass([
171
+ state()
172
+ ], TemporalMixinClass.prototype, "rootTimegroup", 2);
173
+ Object.defineProperty(TemporalMixinClass.prototype, EF_TEMPORAL, {
174
+ value: true
175
+ });
176
+ return TemporalMixinClass;
177
+ };
178
+ export {
179
+ EFTemporal,
180
+ OwnCurrentTimeController,
181
+ deepGetTemporalElements,
182
+ isEFTemporal,
183
+ shallowGetTemporalElements,
184
+ shallowGetTimegroups,
185
+ timegroupContext
186
+ };
@@ -0,0 +1,230 @@
1
+ import { html, css, LitElement } from "lit";
2
+ import { provide } from "@lit/context";
3
+ import { property, customElement } from "lit/decorators.js";
4
+ import { EFTemporal, shallowGetTemporalElements, isEFTemporal, timegroupContext } from "./EFTemporal.mjs";
5
+ import { TimegroupController } from "./TimegroupController.mjs";
6
+ var __defProp = Object.defineProperty;
7
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
+ var __decorateClass = (decorators, target, key, kind) => {
9
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
10
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
11
+ if (decorator = decorators[i])
12
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
13
+ if (kind && result)
14
+ __defProp(target, key, result);
15
+ return result;
16
+ };
17
+ var __accessCheck = (obj, member, msg) => {
18
+ if (!member.has(obj))
19
+ throw TypeError("Cannot " + msg);
20
+ };
21
+ var __privateGet = (obj, member, getter) => {
22
+ __accessCheck(obj, member, "read from private field");
23
+ return getter ? getter.call(obj) : member.get(obj);
24
+ };
25
+ var __privateAdd = (obj, member, value) => {
26
+ if (member.has(obj))
27
+ throw TypeError("Cannot add the same private member more than once");
28
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
29
+ };
30
+ var __privateSet = (obj, member, value, setter) => {
31
+ __accessCheck(obj, member, "write to private field");
32
+ setter ? setter.call(obj, value) : member.set(obj, value);
33
+ return value;
34
+ };
35
+ var _currentTime;
36
+ let EFTimegroup = class extends EFTemporal(LitElement) {
37
+ constructor() {
38
+ super(...arguments);
39
+ this._timeGroupContext = this;
40
+ __privateAdd(this, _currentTime, 0);
41
+ this.mode = "sequence";
42
+ this.crossoverMs = 0;
43
+ }
44
+ set currentTime(time) {
45
+ __privateSet(this, _currentTime, Math.max(0, Math.min(time, this.durationMs / 1e3)));
46
+ try {
47
+ if (this.id) {
48
+ if (this.isConnected) {
49
+ localStorage.setItem(this.storageKey, time.toString());
50
+ }
51
+ }
52
+ } catch (error) {
53
+ console.warn("Failed to save time to localStorage", error);
54
+ }
55
+ }
56
+ get currentTime() {
57
+ return __privateGet(this, _currentTime);
58
+ }
59
+ get currentTimeMs() {
60
+ return this.currentTime * 1e3;
61
+ }
62
+ set currentTimeMs(ms) {
63
+ this.currentTime = ms / 1e3;
64
+ }
65
+ render() {
66
+ return html`<slot></slot> `;
67
+ }
68
+ maybeLoadTimeFromLocalStorage() {
69
+ if (this.id) {
70
+ try {
71
+ return parseFloat(localStorage.getItem(this.storageKey) || "0");
72
+ } catch (error) {
73
+ console.warn("Failed to load time from localStorage", error);
74
+ }
75
+ }
76
+ return 0;
77
+ }
78
+ connectedCallback() {
79
+ super.connectedCallback();
80
+ if (this.id) {
81
+ __privateSet(this, _currentTime, this.maybeLoadTimeFromLocalStorage());
82
+ }
83
+ if (this.parentTimegroup) {
84
+ new TimegroupController(this.parentTimegroup, this);
85
+ }
86
+ }
87
+ get storageKey() {
88
+ if (!this.id) {
89
+ throw new Error("Timegroup must have an id to use localStorage.");
90
+ }
91
+ return `ef-timegroup-${this.id}`;
92
+ }
93
+ get crossoverStartMs() {
94
+ const parentTimeGroup = this.parentTimegroup;
95
+ if (!parentTimeGroup || !this.previousElementSibling) {
96
+ return 0;
97
+ }
98
+ return parentTimeGroup.crossoverMs;
99
+ }
100
+ get crossoverEndMs() {
101
+ const parentTimeGroup = this.parentTimegroup;
102
+ if (!parentTimeGroup || !this.nextElementSibling) {
103
+ return 0;
104
+ }
105
+ return parentTimeGroup.crossoverMs;
106
+ }
107
+ get durationMs() {
108
+ switch (this.mode) {
109
+ case "fixed":
110
+ return super.durationMs || 0;
111
+ case "sequence":
112
+ let duration = 0;
113
+ this.childTemporals.forEach((node) => {
114
+ duration += node.durationMs;
115
+ });
116
+ return duration;
117
+ case "contain":
118
+ let maxDuration = 0;
119
+ this.childTemporals.forEach((node) => {
120
+ if (node.hasOwnDuration) {
121
+ maxDuration = Math.max(maxDuration, node.durationMs);
122
+ }
123
+ });
124
+ return maxDuration;
125
+ default:
126
+ throw new Error(`Invalid time mode: ${this.mode}`);
127
+ }
128
+ }
129
+ get childTemporals() {
130
+ return shallowGetTemporalElements(this);
131
+ }
132
+ updated(changedProperties) {
133
+ super.updated(changedProperties);
134
+ if (changedProperties.has("currentTime") || changedProperties.has("ownCurrentTimeMs")) {
135
+ const animations = this.getAnimations({ subtree: true });
136
+ this.style.setProperty(
137
+ "--ef-duration",
138
+ `${this.durationMs + this.crossoverEndMs + this.crossoverStartMs}ms`
139
+ );
140
+ animations.forEach((animation) => {
141
+ if (animation.playState === "running") {
142
+ animation.pause();
143
+ }
144
+ const effect = animation.effect;
145
+ if (!(effect && effect instanceof KeyframeEffect)) {
146
+ return;
147
+ }
148
+ const target = effect.target;
149
+ if (isEFTemporal(target)) {
150
+ const timing = effect.getTiming();
151
+ const duration = Number(timing.duration) ?? 0;
152
+ const delay = Number(timing.delay);
153
+ const newTime = Math.floor(
154
+ Math.min(target.ownCurrentTimeMs, duration - 1 + delay)
155
+ );
156
+ if (Number.isNaN(newTime)) {
157
+ return;
158
+ }
159
+ animation.currentTime = newTime;
160
+ } else if (target) {
161
+ const nearestTimegroup = target.closest("ef-timegroup");
162
+ if (!nearestTimegroup) {
163
+ return;
164
+ }
165
+ const timing = effect.getTiming();
166
+ const duration = Number(timing.duration) ?? 0;
167
+ const delay = Number(timing.delay);
168
+ const newTime = Math.floor(
169
+ Math.min(nearestTimegroup.ownCurrentTimeMs, duration - 1 + delay)
170
+ );
171
+ if (Number.isNaN(newTime)) {
172
+ return;
173
+ }
174
+ animation.currentTime = newTime;
175
+ }
176
+ });
177
+ }
178
+ }
179
+ get hasOwnDuration() {
180
+ return true;
181
+ }
182
+ };
183
+ _currentTime = /* @__PURE__ */ new WeakMap();
184
+ EFTimegroup.styles = css`
185
+ :host {
186
+ display: block;
187
+ width: 100%;
188
+ height: 100%;
189
+ position: relative;
190
+ top: 0;
191
+ }
192
+ `;
193
+ __decorateClass([
194
+ provide({ context: timegroupContext })
195
+ ], EFTimegroup.prototype, "_timeGroupContext", 2);
196
+ __decorateClass([
197
+ property({
198
+ type: String,
199
+ attribute: "mode"
200
+ })
201
+ ], EFTimegroup.prototype, "mode", 2);
202
+ __decorateClass([
203
+ property({ type: Number })
204
+ ], EFTimegroup.prototype, "currentTime", 1);
205
+ __decorateClass([
206
+ property({
207
+ attribute: "crossover",
208
+ converter: {
209
+ fromAttribute: (value) => {
210
+ if (value.endsWith("ms")) {
211
+ return parseFloat(value);
212
+ }
213
+ if (value.endsWith("s")) {
214
+ return parseFloat(value) * 1e3;
215
+ } else {
216
+ throw new Error(
217
+ "`crossover` MUST be in milliseconds or seconds (10s, 10000ms)"
218
+ );
219
+ }
220
+ },
221
+ toAttribute: (value) => `${value}ms`
222
+ }
223
+ })
224
+ ], EFTimegroup.prototype, "crossoverMs", 2);
225
+ EFTimegroup = __decorateClass([
226
+ customElement("ef-timegroup")
227
+ ], EFTimegroup);
228
+ export {
229
+ EFTimegroup
230
+ };
@@ -0,0 +1,12 @@
1
+ class EFTimeline extends HTMLElement {
2
+ get durationMs() {
3
+ let duration = 0;
4
+ this.childNodes.forEach((node) => {
5
+ if ("durationMs" in node && typeof node.durationMs === "number") {
6
+ duration += node.durationMs;
7
+ }
8
+ });
9
+ return duration;
10
+ }
11
+ }
12
+ window.customElements.define("ef-timeline", EFTimeline);
@@ -0,0 +1,123 @@
1
+ import { css, html } from "lit";
2
+ import { Task } from "@lit/task";
3
+ import { createRef, ref } from "lit/directives/ref.js";
4
+ import { customElement } from "lit/decorators.js";
5
+ import { EFMedia } from "./EFMedia.mjs";
6
+ import { TWMixin } from "../gui/TWMixin.mjs";
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __decorateClass = (decorators, target, key, kind) => {
10
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
11
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
12
+ if (decorator = decorators[i])
13
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
14
+ if (kind && result)
15
+ __defProp(target, key, result);
16
+ return result;
17
+ };
18
+ var __accessCheck = (obj, member, msg) => {
19
+ if (!member.has(obj))
20
+ throw TypeError("Cannot " + msg);
21
+ };
22
+ var __privateGet = (obj, member, getter) => {
23
+ __accessCheck(obj, member, "read from private field");
24
+ return getter ? getter.call(obj) : member.get(obj);
25
+ };
26
+ var __privateAdd = (obj, member, value) => {
27
+ if (member.has(obj))
28
+ throw TypeError("Cannot add the same private member more than once");
29
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
30
+ };
31
+ var __privateSet = (obj, member, value, setter) => {
32
+ __accessCheck(obj, member, "write to private field");
33
+ setter ? setter.call(obj, value) : member.set(obj, value);
34
+ return value;
35
+ };
36
+ var _decoderLock;
37
+ let EFVideo = class extends TWMixin(EFMedia) {
38
+ constructor() {
39
+ super(...arguments);
40
+ this.canvasRef = createRef();
41
+ __privateAdd(this, _decoderLock, false);
42
+ this.frameTask = new Task(this, {
43
+ args: () => [
44
+ this.trackFragmentIndexLoader.status,
45
+ this.initSegmentsLoader.status,
46
+ this.seekTask.status,
47
+ this.fetchSeekTask.status,
48
+ this.videoAssetTask.status,
49
+ this.paintTask.status
50
+ ],
51
+ task: async () => {
52
+ await this.trackFragmentIndexLoader.taskComplete;
53
+ await this.initSegmentsLoader.taskComplete;
54
+ await this.seekTask.taskComplete;
55
+ await this.fetchSeekTask.taskComplete;
56
+ await this.videoAssetTask.taskComplete;
57
+ await this.paintTask.taskComplete;
58
+ this.rootTimegroup?.requestUpdate();
59
+ }
60
+ });
61
+ this.paintTask = new Task(this, {
62
+ args: () => [this.videoAssetTask.value, this.desiredSeekTimeMs],
63
+ task: async ([videoAsset, seekToMs], {
64
+ signal: _signal
65
+ }) => {
66
+ if (!videoAsset) {
67
+ return;
68
+ }
69
+ if (__privateGet(this, _decoderLock)) {
70
+ return;
71
+ }
72
+ try {
73
+ __privateSet(this, _decoderLock, true);
74
+ const frame = await videoAsset.seekToTime(seekToMs / 1e3);
75
+ if (!this.canvasElement) {
76
+ return;
77
+ }
78
+ const ctx = this.canvasElement.getContext("2d");
79
+ if (!(frame && ctx)) {
80
+ return;
81
+ }
82
+ this.canvasElement.width = frame?.codedWidth;
83
+ this.canvasElement.height = frame?.codedHeight;
84
+ ctx.drawImage(
85
+ frame,
86
+ 0,
87
+ 0,
88
+ this.canvasElement.width,
89
+ this.canvasElement.height
90
+ );
91
+ return seekToMs;
92
+ } catch (error) {
93
+ console.trace("Unexpected error while seeking video", error);
94
+ } finally {
95
+ __privateSet(this, _decoderLock, false);
96
+ }
97
+ }
98
+ });
99
+ }
100
+ render() {
101
+ return html` <canvas
102
+ class="h-full w-full object-fill"
103
+ ${ref(this.canvasRef)}
104
+ ></canvas>`;
105
+ }
106
+ get canvasElement() {
107
+ return this.canvasRef.value;
108
+ }
109
+ };
110
+ _decoderLock = /* @__PURE__ */ new WeakMap();
111
+ EFVideo.styles = [
112
+ css`
113
+ :host {
114
+ display: block;
115
+ }
116
+ `
117
+ ];
118
+ EFVideo = __decorateClass([
119
+ customElement("ef-video")
120
+ ], EFVideo);
121
+ export {
122
+ EFVideo
123
+ };