@editframe/elements 0.5.0-beta.3 → 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,16 @@
1
+ class CrossUpdateController {
2
+ constructor(host, target) {
3
+ this.host = host;
4
+ this.target = target;
5
+ this.host.addController(this);
6
+ }
7
+ hostUpdate() {
8
+ this.target.requestUpdate();
9
+ }
10
+ remove() {
11
+ this.host.removeController(this);
12
+ }
13
+ }
14
+ export {
15
+ CrossUpdateController
16
+ };
@@ -0,0 +1,37 @@
1
+ import { html } from "lit";
2
+ import { createRef, ref } from "lit/directives/ref.js";
3
+ import { property, customElement } from "lit/decorators.js";
4
+ import { EFMedia } from "./EFMedia.mjs";
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __decorateClass = (decorators, target, key, kind) => {
8
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
9
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
10
+ if (decorator = decorators[i])
11
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
12
+ if (kind && result)
13
+ __defProp(target, key, result);
14
+ return result;
15
+ };
16
+ let EFAudio = class extends EFMedia {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.audioElementRef = createRef();
20
+ this.src = "";
21
+ }
22
+ render() {
23
+ return html`<audio ${ref(this.audioElementRef)}></audio>`;
24
+ }
25
+ get audioElement() {
26
+ return this.audioElementRef.value;
27
+ }
28
+ };
29
+ __decorateClass([
30
+ property({ type: String })
31
+ ], EFAudio.prototype, "src", 2);
32
+ EFAudio = __decorateClass([
33
+ customElement("ef-audio")
34
+ ], EFAudio);
35
+ export {
36
+ EFAudio
37
+ };
@@ -0,0 +1,172 @@
1
+ import { EFAudio } from "./EFAudio.mjs";
2
+ import { html, css, LitElement } from "lit";
3
+ import { Task } from "@lit/task";
4
+ import { property, customElement } from "lit/decorators.js";
5
+ import { EFVideo } from "./EFVideo.mjs";
6
+ import { EFTemporal } from "./EFTemporal.mjs";
7
+ import { CrossUpdateController } from "./CrossUpdateController.mjs";
8
+ import { FetchMixin } from "./FetchMixin.mjs";
9
+ import { EFSourceMixin } from "./EFSourceMixin.mjs";
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __decorateClass = (decorators, target, key, kind) => {
13
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
14
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
15
+ if (decorator = decorators[i])
16
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
17
+ if (kind && result)
18
+ __defProp(target, key, result);
19
+ return result;
20
+ };
21
+ let EFCaptionsActiveWord = class extends EFTemporal(LitElement) {
22
+ constructor() {
23
+ super(...arguments);
24
+ this.wordStartMs = 0;
25
+ this.wordEndMs = 0;
26
+ this.wordText = "";
27
+ }
28
+ render() {
29
+ return html`${this.wordText}`;
30
+ }
31
+ get startTimeMs() {
32
+ return this.wordStartMs || 0;
33
+ }
34
+ get durationMs() {
35
+ return this.wordEndMs - this.wordStartMs;
36
+ }
37
+ };
38
+ EFCaptionsActiveWord.styles = [
39
+ css`
40
+ :host {
41
+ display: inline-block;
42
+ }
43
+ `
44
+ ];
45
+ __decorateClass([
46
+ property({ type: Number, attribute: false })
47
+ ], EFCaptionsActiveWord.prototype, "wordStartMs", 2);
48
+ __decorateClass([
49
+ property({ type: Number, attribute: false })
50
+ ], EFCaptionsActiveWord.prototype, "wordEndMs", 2);
51
+ __decorateClass([
52
+ property({ type: String, attribute: false })
53
+ ], EFCaptionsActiveWord.prototype, "wordText", 2);
54
+ EFCaptionsActiveWord = __decorateClass([
55
+ customElement("ef-captions-active-word")
56
+ ], EFCaptionsActiveWord);
57
+ let EFCaptions = class extends EFSourceMixin(
58
+ EFTemporal(FetchMixin(LitElement)),
59
+ { assetType: "caption_files" }
60
+ ) {
61
+ constructor() {
62
+ super(...arguments);
63
+ this.target = null;
64
+ this.wordStyle = "";
65
+ this.activeWordContainers = this.getElementsByTagName("ef-captions-active-word");
66
+ this.md5SumLoader = new Task(this, {
67
+ autoRun: false,
68
+ args: () => [this.target],
69
+ task: async ([], { signal }) => {
70
+ const md5Path = `/@ef-asset/${this.targetElement.getAttribute("src") ?? ""}`;
71
+ const response = await fetch(md5Path, { method: "HEAD", signal });
72
+ return response.headers.get("etag") ?? void 0;
73
+ }
74
+ });
75
+ this.captionsDataTask = new Task(this, {
76
+ args: () => [this.captionsPath(), this.fetch],
77
+ task: async ([captionsPath, fetch2], { signal }) => {
78
+ const response = await fetch2(captionsPath, { signal });
79
+ return response.json();
80
+ }
81
+ });
82
+ this.frameTask = new Task(this, {
83
+ args: () => [this.captionsDataTask.status],
84
+ task: async () => {
85
+ await this.captionsDataTask.taskComplete;
86
+ }
87
+ });
88
+ }
89
+ captionsPath() {
90
+ const src = this.targetElement.getAttribute("src");
91
+ if (src?.startsWith("http")) {
92
+ return src.replace("isobmff", "caption");
93
+ }
94
+ return `/@ef-captions/${this.targetElement.getAttribute("src") ?? ""}`;
95
+ }
96
+ productionSrc() {
97
+ if (!this.md5SumLoader.value) {
98
+ throw new Error(
99
+ `MD5 sum not available for ${this}. Cannot generate production URL`
100
+ );
101
+ }
102
+ return `http://localhost:3000/api/video2/caption_files/${this.md5SumLoader.value}`;
103
+ }
104
+ connectedCallback() {
105
+ super.connectedCallback();
106
+ if (this.targetElement) {
107
+ new CrossUpdateController(this.targetElement, this);
108
+ }
109
+ }
110
+ render() {
111
+ return this.captionsDataTask.render({
112
+ pending: () => html`<div>Generating captions data...</div>`,
113
+ error: () => html`<div>🚫 Error generating captions data</div>`,
114
+ complete: () => html`<slot></slot>`
115
+ });
116
+ }
117
+ updated(_changedProperties) {
118
+ this.updateActiveWord();
119
+ }
120
+ updateActiveWord() {
121
+ const caption = this.captionsDataTask.value;
122
+ if (!caption) {
123
+ return;
124
+ }
125
+ const words = [];
126
+ let startMs;
127
+ let endMs;
128
+ caption.segments.forEach((segment) => {
129
+ if (this.targetElement.ownCurrentTimeMs >= segment.start * 1e3 && this.targetElement.ownCurrentTimeMs <= segment.end * 1e3) {
130
+ return segment.words.map((word) => {
131
+ if (this.targetElement.ownCurrentTimeMs >= word.start * 1e3 && this.targetElement.ownCurrentTimeMs <= word.end * 1e3) {
132
+ words.push(word.text);
133
+ startMs = word.start * 1e3;
134
+ endMs = word.end * 1e3;
135
+ }
136
+ });
137
+ }
138
+ });
139
+ Array.from(this.activeWordContainers).forEach((container) => {
140
+ container.wordText = words.join(" ");
141
+ container.wordStartMs = startMs;
142
+ container.wordEndMs = endMs;
143
+ });
144
+ }
145
+ get targetElement() {
146
+ const target = document.querySelector(this.getAttribute("target") ?? "");
147
+ if (target instanceof EFAudio || target instanceof EFVideo) {
148
+ return target;
149
+ }
150
+ throw new Error("Invalid target, must be an EFAudio or EFVideo element");
151
+ }
152
+ };
153
+ EFCaptions.styles = [
154
+ css`
155
+ :host {
156
+ display: block;
157
+ }
158
+ `
159
+ ];
160
+ __decorateClass([
161
+ property({ type: String, attribute: "target" })
162
+ ], EFCaptions.prototype, "target", 2);
163
+ __decorateClass([
164
+ property({ attribute: "word-style" })
165
+ ], EFCaptions.prototype, "wordStyle", 2);
166
+ EFCaptions = __decorateClass([
167
+ customElement("ef-captions")
168
+ ], EFCaptions);
169
+ export {
170
+ EFCaptions,
171
+ EFCaptionsActiveWord
172
+ };
@@ -0,0 +1,67 @@
1
+ import { Task } from "@lit/task";
2
+ import { html, css, LitElement } from "lit";
3
+ import { customElement } from "lit/decorators.js";
4
+ import { createRef, ref } from "lit/directives/ref.js";
5
+ import { FetchMixin } from "./FetchMixin.mjs";
6
+ import { EFSourceMixin } from "./EFSourceMixin.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
+ let EFImage = class extends EFSourceMixin(FetchMixin(LitElement), {
19
+ assetType: "image_files"
20
+ }) {
21
+ constructor() {
22
+ super(...arguments);
23
+ this.imageRef = createRef();
24
+ this.fetchImage = new Task(this, {
25
+ args: () => [this.assetPath(), this.fetch],
26
+ task: async ([assetPath, fetch], { signal }) => {
27
+ const response = await fetch(assetPath, { signal });
28
+ return URL.createObjectURL(await response.blob());
29
+ }
30
+ });
31
+ this.frameTask = new Task(this, {
32
+ args: () => [this.fetchImage.status],
33
+ task: async () => {
34
+ await this.fetchImage.taskComplete;
35
+ }
36
+ });
37
+ }
38
+ render() {
39
+ return html`<img ${ref(this.imageRef)} src="${this.fetchImage.value}" />`;
40
+ }
41
+ assetPath() {
42
+ if (this.src.startsWith("http")) {
43
+ return this.src;
44
+ }
45
+ return `/@ef-image/${this.src}`;
46
+ }
47
+ };
48
+ EFImage.styles = [
49
+ css`
50
+ :host {
51
+ display: block;
52
+ }
53
+ img {
54
+ display: block;
55
+ width: 100%;
56
+ height: 100%;
57
+ object-fit: fill;
58
+ object-position: center;
59
+ }
60
+ `
61
+ ];
62
+ EFImage = __decorateClass([
63
+ customElement("ef-image")
64
+ ], EFImage);
65
+ export {
66
+ EFImage
67
+ };
@@ -0,0 +1,255 @@
1
+ import { css, LitElement } from "lit";
2
+ import { EFTemporal } from "./EFTemporal.mjs";
3
+ import { property, state } from "lit/decorators.js";
4
+ import { deepArrayEquals } from "@lit/task/deep-equals.js";
5
+ import { Task } from "@lit/task";
6
+ import { MP4File } from "../../editor/util/MP4File.mjs";
7
+ import { getStartTimeMs } from "./util.mjs";
8
+ import { VideoAsset } from "../../editor/util/EncodedAsset/EncodedAsset.mjs";
9
+ import { FetchMixin } from "./FetchMixin.mjs";
10
+ import { apiHostContext } from "../gui/EFWorkbench.mjs";
11
+ import { consume } from "@lit/context";
12
+ import { EFSourceMixin } from "./EFSourceMixin.mjs";
13
+ var __defProp = Object.defineProperty;
14
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
15
+ var __decorateClass = (decorators, target, key, kind) => {
16
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
17
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
18
+ if (decorator = decorators[i])
19
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
20
+ if (kind && result)
21
+ __defProp(target, key, result);
22
+ return result;
23
+ };
24
+ class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
25
+ assetType: "isobmff_files"
26
+ }) {
27
+ constructor() {
28
+ super(...arguments);
29
+ this.currentTimeMs = 0;
30
+ this.trackFragmentIndexLoader = new Task(this, {
31
+ args: () => [this.fragmentIndexPath(), this.fetch],
32
+ task: async ([fragmentIndexPath, fetch], { signal }) => {
33
+ const response = await fetch(fragmentIndexPath, { signal });
34
+ return await response.json();
35
+ },
36
+ onComplete: () => {
37
+ this.requestUpdate("ownCurrentTimeMs");
38
+ this.parentTimegroup?.requestUpdate("ownCurrentTimeMs");
39
+ }
40
+ });
41
+ this.initSegmentsLoader = new Task(this, {
42
+ args: () => [this.trackFragmentIndexLoader.value, this.src, this.fetch],
43
+ task: async ([fragmentIndex, _src, fetch], { signal }) => {
44
+ if (!fragmentIndex) {
45
+ return;
46
+ }
47
+ return await Promise.all(
48
+ Object.entries(fragmentIndex).map(async ([trackId, track]) => {
49
+ const start = track.initSegment.offset;
50
+ const end = track.initSegment.offset + track.initSegment.size - 1;
51
+ const response = await fetch(this.fragmentTrackPath(trackId), {
52
+ signal,
53
+ headers: { Range: `bytes=${start}-${end}` }
54
+ });
55
+ const buffer = await response.arrayBuffer();
56
+ buffer.fileStart = 0;
57
+ const mp4File = new MP4File();
58
+ mp4File.appendBuffer(buffer, true);
59
+ mp4File.flush();
60
+ await mp4File.readyPromise;
61
+ return { trackId, buffer, mp4File };
62
+ })
63
+ );
64
+ }
65
+ });
66
+ this.seekTask = new Task(this, {
67
+ args: () => [
68
+ this.desiredSeekTimeMs,
69
+ this.trackFragmentIndexLoader.value,
70
+ this.initSegmentsLoader.value
71
+ ],
72
+ task: async ([seekToMs, fragmentIndex, initSegments], { signal }) => {
73
+ if (fragmentIndex === void 0) {
74
+ return;
75
+ }
76
+ if (initSegments === void 0) {
77
+ return;
78
+ }
79
+ const result = {};
80
+ Object.values(fragmentIndex).forEach((index) => {
81
+ const track = initSegments.find((segment2) => segment2.trackId === String(index.track))?.mp4File.getInfo().tracks[0];
82
+ if (!track) {
83
+ throw new Error("Could not finding matching track");
84
+ }
85
+ const segment = index.segments.toReversed().find((segment2) => {
86
+ return segment2.dts / track.timescale * 1e3 <= seekToMs;
87
+ });
88
+ if (!segment) {
89
+ return;
90
+ }
91
+ result[index.track] = { segment, track };
92
+ });
93
+ return result;
94
+ }
95
+ });
96
+ this.fetchSeekTask = new Task(this, {
97
+ argsEqual: deepArrayEquals,
98
+ args: () => [this.initSegmentsLoader.value, this.seekTask.value, this.fetch],
99
+ task: async ([initSegments, seekResult, fetch], { signal }) => {
100
+ if (!initSegments) {
101
+ return;
102
+ }
103
+ if (!seekResult) {
104
+ return;
105
+ }
106
+ const files = {};
107
+ for (const [trackId, { segment, track }] of Object.entries(seekResult)) {
108
+ const start = segment.offset;
109
+ const end = segment.offset + segment.size;
110
+ const response = await fetch(this.fragmentTrackPath(trackId), {
111
+ signal,
112
+ headers: { Range: `bytes=${start}-${end}` }
113
+ });
114
+ const initSegment = Object.values(initSegments).find(
115
+ (initSegment2) => initSegment2.trackId === String(track.id)
116
+ );
117
+ const initBuffer = initSegment.buffer;
118
+ const mediaBuffer = await response.arrayBuffer();
119
+ files[trackId] = new File([initBuffer, mediaBuffer], "video.mp4", {
120
+ type: "video/mp4"
121
+ });
122
+ }
123
+ return files;
124
+ }
125
+ });
126
+ this.videoAssetTask = new Task(this, {
127
+ args: () => [this.fetchSeekTask.value],
128
+ task: async ([files], { signal }) => {
129
+ if (!files) {
130
+ return;
131
+ }
132
+ if (!this.defaultVideoTrackId) {
133
+ return;
134
+ }
135
+ const videoFile = files[this.defaultVideoTrackId];
136
+ if (!videoFile) {
137
+ return;
138
+ }
139
+ return await VideoAsset.createFromReadableStream(
140
+ "video.mp4",
141
+ videoFile.stream(),
142
+ videoFile
143
+ );
144
+ }
145
+ });
146
+ this.desiredSeekTimeMs = 0;
147
+ this.#audioContext = new OfflineAudioContext(1, 48e3 / 30, 48e3);
148
+ this.audioBufferTask = new Task(this, {
149
+ args: () => [this.fetchSeekTask.value, this.seekTask.value],
150
+ task: async ([files, segments], { signal }) => {
151
+ if (!files) {
152
+ return;
153
+ }
154
+ if (!segments) {
155
+ return;
156
+ }
157
+ if (!this.defaultAudioTrackId) {
158
+ return;
159
+ }
160
+ const segment = segments[this.defaultAudioTrackId];
161
+ if (!segment) {
162
+ return;
163
+ }
164
+ const audioFile = files[this.defaultAudioTrackId];
165
+ if (!audioFile) {
166
+ return;
167
+ }
168
+ return {
169
+ buffer: await this.#audioContext.decodeAudioData(
170
+ await audioFile.arrayBuffer()
171
+ ),
172
+ startOffsetMs: segment.segment.cts / segment.track.timescale * 1e3
173
+ };
174
+ }
175
+ });
176
+ }
177
+ static {
178
+ this.styles = [
179
+ css`
180
+ :host {
181
+ display: block;
182
+ position: relative;
183
+ overflow: hidden;
184
+ }
185
+ `
186
+ ];
187
+ }
188
+ // get requiredAssets() {
189
+ // return { [this.md5SumLoader.value]: this.requiredAssetsPath.value ?? [] };
190
+ // }
191
+ fragmentIndexPath() {
192
+ if (this.getAttribute("src")?.startsWith("http")) {
193
+ return this.getAttribute("src") + "/index";
194
+ }
195
+ return `/@ef-track-fragment-index/${this.getAttribute("src") ?? ""}`;
196
+ }
197
+ fragmentTrackPath(trackId) {
198
+ if (this.getAttribute("src")?.startsWith("http")) {
199
+ return this.getAttribute("src").replace("files", "tracks") + `/${trackId}`;
200
+ }
201
+ return `/@ef-track/${this.getAttribute("src") ?? ""}?trackId=${trackId}`;
202
+ }
203
+ get defaultVideoTrackId() {
204
+ return Object.values(this.trackFragmentIndexLoader.value ?? {}).find(
205
+ (track) => track.type === "video"
206
+ )?.track;
207
+ }
208
+ get defaultAudioTrackId() {
209
+ return Object.values(this.trackFragmentIndexLoader.value ?? {}).find(
210
+ (track) => track.type === "audio"
211
+ )?.track;
212
+ }
213
+ async executeSeek(seekToMs) {
214
+ this.desiredSeekTimeMs = seekToMs;
215
+ }
216
+ updated(changedProperties) {
217
+ if (changedProperties.has("ownCurrentTimeMs")) {
218
+ this.executeSeek(this.ownCurrentTimeMs);
219
+ }
220
+ }
221
+ get hasOwnDuration() {
222
+ return true;
223
+ }
224
+ get durationMs() {
225
+ if (!this.trackFragmentIndexLoader.value) {
226
+ return 0;
227
+ }
228
+ const durations = Object.values(this.trackFragmentIndexLoader.value).map(
229
+ (track) => {
230
+ return track.duration / track.timescale * 1e3;
231
+ }
232
+ );
233
+ if (durations.length === 0) {
234
+ return 0;
235
+ }
236
+ return Math.max(...durations);
237
+ }
238
+ get startTimeMs() {
239
+ return getStartTimeMs(this);
240
+ }
241
+ #audioContext;
242
+ }
243
+ __decorateClass([
244
+ property({ type: Number })
245
+ ], EFMedia.prototype, "currentTimeMs", 2);
246
+ __decorateClass([
247
+ consume({ context: apiHostContext, subscribe: true }),
248
+ state()
249
+ ], EFMedia.prototype, "efHost", 2);
250
+ __decorateClass([
251
+ state()
252
+ ], EFMedia.prototype, "desiredSeekTimeMs", 2);
253
+ export {
254
+ EFMedia
255
+ };
@@ -0,0 +1,57 @@
1
+ import { consume } from "@lit/context";
2
+ import { apiHostContext } from "../gui/EFWorkbench.mjs";
3
+ import { state } from "lit/decorators/state.js";
4
+ import { Task } from "@lit/task";
5
+ import { property } from "lit/decorators/property.js";
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
+ function EFSourceMixin(superClass, options) {
18
+ class EFSourceElement extends superClass {
19
+ constructor() {
20
+ super(...arguments);
21
+ this.src = "";
22
+ this.md5SumLoader = new Task(this, {
23
+ autoRun: false,
24
+ args: () => [this.src],
25
+ task: async ([src], { signal }) => {
26
+ const md5Path = `/@ef-asset/${src}`;
27
+ const response = await fetch(md5Path, { method: "HEAD", signal });
28
+ return response.headers.get("etag") ?? void 0;
29
+ }
30
+ });
31
+ }
32
+ productionSrc() {
33
+ if (!this.md5SumLoader.value) {
34
+ throw new Error(
35
+ `MD5 sum not available for ${this}. Cannot generate production URL`
36
+ );
37
+ }
38
+ if (!this.efHost) {
39
+ throw new Error(
40
+ `efHost not available for ${this}. Cannot generate production URL`
41
+ );
42
+ }
43
+ return `${this.efHost}/api/video2/${options.assetType}/${this.md5SumLoader.value}`;
44
+ }
45
+ }
46
+ __decorateClass([
47
+ consume({ context: apiHostContext, subscribe: true }),
48
+ state()
49
+ ], EFSourceElement.prototype, "efHost", 2);
50
+ __decorateClass([
51
+ property({ type: String })
52
+ ], EFSourceElement.prototype, "src", 2);
53
+ return EFSourceElement;
54
+ }
55
+ export {
56
+ EFSourceMixin
57
+ };