@editframe/elements 0.8.0-beta.1 → 0.8.0-beta.2
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.
|
@@ -29,6 +29,7 @@ export declare class EFMedia extends EFMedia_base {
|
|
|
29
29
|
}[] | undefined], Record<string, {
|
|
30
30
|
segment: TrackSegment;
|
|
31
31
|
track: MP4Box.TrackInfo;
|
|
32
|
+
nextSegment?: TrackSegment;
|
|
32
33
|
}> | undefined>;
|
|
33
34
|
fetchSeekTask: Task<readonly [{
|
|
34
35
|
trackId: string;
|
|
@@ -37,6 +38,7 @@ export declare class EFMedia extends EFMedia_base {
|
|
|
37
38
|
}[] | undefined, Record<string, {
|
|
38
39
|
segment: TrackSegment;
|
|
39
40
|
track: MP4Box.TrackInfo;
|
|
41
|
+
nextSegment?: TrackSegment;
|
|
40
42
|
}> | undefined, typeof fetch], Record<string, File> | undefined>;
|
|
41
43
|
videoAssetTask: Task<readonly [Record<string, File> | undefined], VideoAsset | undefined>;
|
|
42
44
|
desiredSeekTimeMs: number;
|
|
@@ -48,6 +50,7 @@ export declare class EFMedia extends EFMedia_base {
|
|
|
48
50
|
audioBufferTask: Task<readonly [Record<string, File> | undefined, Record<string, {
|
|
49
51
|
segment: TrackSegment;
|
|
50
52
|
track: MP4Box.TrackInfo;
|
|
53
|
+
nextSegment?: TrackSegment;
|
|
51
54
|
}> | undefined], {
|
|
52
55
|
buffer: AudioBuffer;
|
|
53
56
|
startOffsetMs: number;
|
|
@@ -101,10 +101,13 @@ class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
|
101
101
|
const segment = index.segments.toReversed().find((segment2) => {
|
|
102
102
|
return segment2.dts / track.timescale * 1e3 <= seekToMs;
|
|
103
103
|
});
|
|
104
|
+
const nextSegment = index.segments.find((segment2) => {
|
|
105
|
+
return segment2.dts / track.timescale * 1e3 > seekToMs;
|
|
106
|
+
});
|
|
104
107
|
if (!segment) {
|
|
105
108
|
return;
|
|
106
109
|
}
|
|
107
|
-
result[index.track] = { segment, track };
|
|
110
|
+
result[index.track] = { segment, track, nextSegment };
|
|
108
111
|
}
|
|
109
112
|
return result;
|
|
110
113
|
}
|
|
@@ -121,13 +124,27 @@ class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
|
121
124
|
return;
|
|
122
125
|
}
|
|
123
126
|
const files = {};
|
|
124
|
-
for (const [trackId, { segment, track }] of Object.entries(
|
|
127
|
+
for (const [trackId, { segment, track, nextSegment }] of Object.entries(
|
|
128
|
+
seekResult
|
|
129
|
+
)) {
|
|
125
130
|
const start = segment.offset;
|
|
126
131
|
const end = segment.offset + segment.size;
|
|
127
132
|
const response = await fetch(this.fragmentTrackPath(trackId), {
|
|
128
133
|
signal,
|
|
129
134
|
headers: { Range: `bytes=${start}-${end}` }
|
|
130
135
|
});
|
|
136
|
+
if (nextSegment) {
|
|
137
|
+
const nextStart = nextSegment.offset;
|
|
138
|
+
const nextEnd = nextSegment.offset + nextSegment.size;
|
|
139
|
+
fetch(this.fragmentTrackPath(trackId), {
|
|
140
|
+
signal,
|
|
141
|
+
headers: { Range: `bytes=${nextStart}-${nextEnd}` }
|
|
142
|
+
}).then(() => {
|
|
143
|
+
log("Prefetched next segment");
|
|
144
|
+
}).catch((error) => {
|
|
145
|
+
log("Failed to prefetch next segment", error);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
131
148
|
const initSegment = Object.values(initSegments).find(
|
|
132
149
|
(initSegment2) => initSegment2.trackId === String(track.id)
|
|
133
150
|
);
|
|
@@ -32,25 +32,27 @@ function ContextMixin(superClass) {
|
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
if (this.signingURL) {
|
|
35
|
-
if (!this.#
|
|
36
|
-
this.#
|
|
35
|
+
if (!this.#URLTokens[url]) {
|
|
36
|
+
this.#URLTokens[url] = fetch(this.signingURL, {
|
|
37
37
|
method: "POST",
|
|
38
38
|
body: JSON.stringify({ url })
|
|
39
39
|
}).then(async (response) => {
|
|
40
40
|
if (response.ok) {
|
|
41
|
-
return (await response.json()).
|
|
41
|
+
return (await response.json()).token;
|
|
42
42
|
}
|
|
43
43
|
throw new Error(
|
|
44
44
|
`Failed to sign URL: ${url}. SigningURL: ${this.signingURL} ${response.status} ${response.statusText}`
|
|
45
45
|
);
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
|
-
const
|
|
49
|
-
|
|
48
|
+
const urlToken = await this.#URLTokens[url];
|
|
49
|
+
Object.assign(init.headers, {
|
|
50
|
+
authorization: `Bearer ${urlToken}`
|
|
51
|
+
});
|
|
50
52
|
}
|
|
51
53
|
return fetch(url, init);
|
|
52
54
|
};
|
|
53
|
-
this.#
|
|
55
|
+
this.#URLTokens = {};
|
|
54
56
|
this.apiHost = "";
|
|
55
57
|
this.playing = false;
|
|
56
58
|
this.loop = false;
|
|
@@ -93,7 +95,7 @@ function ContextMixin(superClass) {
|
|
|
93
95
|
this.#playbackAnimationFrameRequest = null;
|
|
94
96
|
this.#AUDIO_PLAYBACK_SLICE_MS = 1e3;
|
|
95
97
|
}
|
|
96
|
-
#
|
|
98
|
+
#URLTokens;
|
|
97
99
|
connectedCallback() {
|
|
98
100
|
super.connectedCallback();
|
|
99
101
|
requestAnimationFrame(this.setStageScale);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@editframe/elements",
|
|
3
|
-
"version": "0.8.0-beta.
|
|
3
|
+
"version": "0.8.0-beta.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"author": "",
|
|
21
21
|
"license": "UNLICENSED",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@editframe/assets": "0.8.0-beta.
|
|
23
|
+
"@editframe/assets": "0.8.0-beta.2",
|
|
24
24
|
"@lit/context": "^1.1.2",
|
|
25
25
|
"@lit/task": "^1.0.1",
|
|
26
26
|
"d3": "^7.9.0",
|
package/src/elements/EFMedia.ts
CHANGED
|
@@ -145,7 +145,11 @@ export class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
|
145
145
|
|
|
146
146
|
const result: Record<
|
|
147
147
|
string,
|
|
148
|
-
{
|
|
148
|
+
{
|
|
149
|
+
segment: TrackSegment;
|
|
150
|
+
track: MP4Box.TrackInfo;
|
|
151
|
+
nextSegment?: TrackSegment;
|
|
152
|
+
}
|
|
149
153
|
> = {};
|
|
150
154
|
|
|
151
155
|
for (const index of Object.values(fragmentIndex)) {
|
|
@@ -161,11 +165,15 @@ export class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
|
161
165
|
return (segment.dts / track.timescale) * 1000 <= seekToMs;
|
|
162
166
|
});
|
|
163
167
|
|
|
168
|
+
const nextSegment = index.segments.find((segment) => {
|
|
169
|
+
return (segment.dts / track.timescale) * 1000 > seekToMs;
|
|
170
|
+
});
|
|
171
|
+
|
|
164
172
|
if (!segment) {
|
|
165
173
|
return;
|
|
166
174
|
}
|
|
167
175
|
|
|
168
|
-
result[index.track] = { segment, track };
|
|
176
|
+
result[index.track] = { segment, track, nextSegment };
|
|
169
177
|
}
|
|
170
178
|
|
|
171
179
|
return result;
|
|
@@ -187,7 +195,9 @@ export class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
|
187
195
|
|
|
188
196
|
const files: Record<string, File> = {};
|
|
189
197
|
|
|
190
|
-
for (const [trackId, { segment, track }] of Object.entries(
|
|
198
|
+
for (const [trackId, { segment, track, nextSegment }] of Object.entries(
|
|
199
|
+
seekResult,
|
|
200
|
+
)) {
|
|
191
201
|
const start = segment.offset;
|
|
192
202
|
const end = segment.offset + segment.size;
|
|
193
203
|
|
|
@@ -196,6 +206,21 @@ export class EFMedia extends EFSourceMixin(EFTemporal(FetchMixin(LitElement)), {
|
|
|
196
206
|
headers: { Range: `bytes=${start}-${end}` },
|
|
197
207
|
});
|
|
198
208
|
|
|
209
|
+
if (nextSegment) {
|
|
210
|
+
const nextStart = nextSegment.offset;
|
|
211
|
+
const nextEnd = nextSegment.offset + nextSegment.size;
|
|
212
|
+
fetch(this.fragmentTrackPath(trackId), {
|
|
213
|
+
signal,
|
|
214
|
+
headers: { Range: `bytes=${nextStart}-${nextEnd}` },
|
|
215
|
+
})
|
|
216
|
+
.then(() => {
|
|
217
|
+
log("Prefetched next segment");
|
|
218
|
+
})
|
|
219
|
+
.catch((error) => {
|
|
220
|
+
log("Failed to prefetch next segment", error);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
199
224
|
const initSegment = Object.values(initSegments).find(
|
|
200
225
|
(initSegment) => initSegment.trackId === String(track.id),
|
|
201
226
|
);
|
package/src/gui/ContextMixin.ts
CHANGED
|
@@ -51,13 +51,13 @@ export function ContextMixin<T extends Constructor<LitElement>>(superClass: T) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
if (this.signingURL) {
|
|
54
|
-
if (!this.#
|
|
55
|
-
this.#
|
|
54
|
+
if (!this.#URLTokens[url]) {
|
|
55
|
+
this.#URLTokens[url] = fetch(this.signingURL, {
|
|
56
56
|
method: "POST",
|
|
57
57
|
body: JSON.stringify({ url }),
|
|
58
58
|
}).then(async (response) => {
|
|
59
59
|
if (response.ok) {
|
|
60
|
-
return (await response.json()).
|
|
60
|
+
return (await response.json()).token;
|
|
61
61
|
}
|
|
62
62
|
throw new Error(
|
|
63
63
|
`Failed to sign URL: ${url}. SigningURL: ${this.signingURL} ${response.status} ${response.statusText}`,
|
|
@@ -65,15 +65,17 @@ export function ContextMixin<T extends Constructor<LitElement>>(superClass: T) {
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const
|
|
68
|
+
const urlToken = await this.#URLTokens[url];
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
Object.assign(init.headers, {
|
|
71
|
+
authorization: `Bearer ${urlToken}`,
|
|
72
|
+
});
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
return fetch(url, init);
|
|
74
76
|
};
|
|
75
77
|
|
|
76
|
-
#
|
|
78
|
+
#URLTokens: Record<string, Promise<string>> = {};
|
|
77
79
|
|
|
78
80
|
@property({ type: String })
|
|
79
81
|
signingURL?: string;
|