@twick/2d 0.13.0 → 0.14.0
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/LICENSE +21 -0
- package/editor/editor/tsconfig.build.tsbuildinfo +1 -1
- package/lib/components/Audio.d.ts.map +1 -1
- package/lib/components/Audio.js +3 -33
- package/lib/components/CodeBlock.d.ts +1 -1
- package/lib/components/Media.d.ts +0 -6
- package/lib/components/Media.d.ts.map +1 -1
- package/lib/components/Media.js +39 -255
- package/lib/components/Node.d.ts +1 -1
- package/lib/components/Path.d.ts +1 -1
- package/lib/components/SVG.d.ts +1 -1
- package/lib/components/Shape.d.ts +1 -1
- package/lib/components/Video.d.ts +1 -0
- package/lib/components/Video.d.ts.map +1 -1
- package/lib/components/Video.js +65 -70
- package/lib/components/utils/waitUntil.d.ts +7 -0
- package/lib/components/utils/waitUntil.d.ts.map +1 -0
- package/lib/components/utils/waitUntil.js +15 -0
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/lib/utils/waitUntil.d.ts +7 -0
- package/lib/utils/waitUntil.d.ts.map +1 -0
- package/lib/utils/waitUntil.js +15 -0
- package/package.json +5 -4
- package/src/lib/components/Audio.ts +2 -39
- package/src/lib/components/Media.ts +45 -275
- package/src/lib/components/Video.ts +78 -77
- package/src/lib/utils/waitUntil.ts +18 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twick/2d",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "A 2D renderer for twick",
|
|
5
5
|
"author": "twick",
|
|
6
6
|
"homepage": "https://re.video/",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@preact/signals": "^1.2.1",
|
|
32
32
|
"@rollup/plugin-node-resolve": "^15.2.4",
|
|
33
33
|
"@rollup/plugin-typescript": "^12.1.0",
|
|
34
|
-
"@twick/ui": "^0.
|
|
34
|
+
"@twick/ui": "^0.14.0",
|
|
35
35
|
"clsx": "^2.0.0",
|
|
36
36
|
"jsdom": "^22.1.0",
|
|
37
37
|
"preact": "^10.19.2",
|
|
@@ -42,11 +42,12 @@
|
|
|
42
42
|
"@lezer/common": "^1.2.1",
|
|
43
43
|
"@lezer/highlight": "^1.2.0",
|
|
44
44
|
"@rive-app/canvas-advanced": "2.7.3",
|
|
45
|
-
"@twick/core": "^0.
|
|
45
|
+
"@twick/core": "^0.14.0",
|
|
46
46
|
"code-fns": "^0.8.2",
|
|
47
47
|
"hls.js": "^1.5.11",
|
|
48
48
|
"mathjax-full": "^3.2.2",
|
|
49
49
|
"mp4box": "^0.5.2",
|
|
50
50
|
"parse-svg-path": "^0.1.2"
|
|
51
|
-
}
|
|
51
|
+
},
|
|
52
|
+
"gitHead": "54bd58bf80ea6afec48c27c061e6d455c1e0a885"
|
|
52
53
|
}
|
|
@@ -26,47 +26,13 @@ export class Audio extends Media {
|
|
|
26
26
|
@computed()
|
|
27
27
|
protected audio(): HTMLAudioElement {
|
|
28
28
|
const src = this.src();
|
|
29
|
-
|
|
30
|
-
// Use a temporary key for undefined src to avoid conflicts
|
|
31
|
-
const key = `${this.key}/${src || 'pending'}`;
|
|
32
|
-
|
|
29
|
+
const key = `${this.key}/${src}`;
|
|
33
30
|
let audio = Audio.pool[key];
|
|
34
31
|
if (!audio) {
|
|
35
32
|
audio = document.createElement('audio');
|
|
36
33
|
audio.crossOrigin = 'anonymous';
|
|
37
|
-
|
|
38
|
-
// Only set src if it's valid, otherwise leave it empty
|
|
39
|
-
if (src && src !== 'undefined') {
|
|
40
|
-
audio.src = src;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
Audio.pool[key] = audio;
|
|
44
|
-
} else if (src && src !== 'undefined' && audio.src !== src) {
|
|
45
|
-
// Update existing audio element if src has changed and is now valid
|
|
46
34
|
audio.src = src;
|
|
47
|
-
|
|
48
|
-
// Move audio to correct pool key
|
|
49
|
-
delete Audio.pool[key];
|
|
50
|
-
const newKey = `${this.key}/${src}`;
|
|
51
|
-
Audio.pool[newKey] = audio;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// If src is still undefined, wait for it to become available
|
|
55
|
-
if (!src || src === 'undefined') {
|
|
56
|
-
DependencyContext.collectPromise(
|
|
57
|
-
new Promise<void>(resolve => {
|
|
58
|
-
// Check periodically for valid src
|
|
59
|
-
const checkSrc = () => {
|
|
60
|
-
const currentSrc = this.src();
|
|
61
|
-
if (currentSrc && currentSrc !== 'undefined') {
|
|
62
|
-
resolve();
|
|
63
|
-
} else {
|
|
64
|
-
setTimeout(checkSrc, 10);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
checkSrc();
|
|
68
|
-
}),
|
|
69
|
-
);
|
|
35
|
+
Audio.pool[key] = audio;
|
|
70
36
|
}
|
|
71
37
|
|
|
72
38
|
const weNeedToWait = this.waitForCanPlayNecessary(audio);
|
|
@@ -150,9 +116,6 @@ export class Audio extends Media {
|
|
|
150
116
|
}
|
|
151
117
|
|
|
152
118
|
protected override async draw(context: CanvasRenderingContext2D) {
|
|
153
|
-
// Auto-start playback if Revideo is playing but media isn't
|
|
154
|
-
this.autoPlayBasedOnRevideo();
|
|
155
|
-
|
|
156
119
|
const playbackState = this.view().playbackState();
|
|
157
120
|
|
|
158
121
|
playbackState === PlaybackState.Playing ||
|
|
@@ -48,7 +48,6 @@ video.playbackRate(mySignal());
|
|
|
48
48
|
|
|
49
49
|
@nodeName('Media')
|
|
50
50
|
export abstract class Media extends Rect {
|
|
51
|
-
@initial('')
|
|
52
51
|
@signal()
|
|
53
52
|
public declare readonly src: SimpleSignal<string, this>;
|
|
54
53
|
|
|
@@ -90,11 +89,9 @@ export abstract class Media extends Rect {
|
|
|
90
89
|
}
|
|
91
90
|
> = {};
|
|
92
91
|
protected lastTime = -1;
|
|
93
|
-
private isSchedulingPlay = false;
|
|
94
92
|
|
|
95
93
|
public constructor(props: MediaProps) {
|
|
96
94
|
super(props);
|
|
97
|
-
|
|
98
95
|
if (!this.awaitCanPlay()) {
|
|
99
96
|
this.scheduleSeek(this.time());
|
|
100
97
|
}
|
|
@@ -103,12 +100,9 @@ export abstract class Media extends Rect {
|
|
|
103
100
|
this.play();
|
|
104
101
|
}
|
|
105
102
|
this.volume = props.volume ?? 1;
|
|
106
|
-
|
|
107
|
-
if (!this.awaitCanPlay()) {
|
|
108
|
-
this.setVolume(this.volume);
|
|
109
|
-
}
|
|
103
|
+
this.setVolume(this.volume);
|
|
110
104
|
}
|
|
111
|
-
|
|
105
|
+
|
|
112
106
|
public isPlaying(): boolean {
|
|
113
107
|
return this.playing();
|
|
114
108
|
}
|
|
@@ -118,15 +112,7 @@ export abstract class Media extends Rect {
|
|
|
118
112
|
}
|
|
119
113
|
|
|
120
114
|
public getDuration(): number {
|
|
121
|
-
|
|
122
|
-
const mElement = this.mediaElement();
|
|
123
|
-
const isVideo = (mElement instanceof HTMLVideoElement);
|
|
124
|
-
const isAudio = (mElement instanceof HTMLAudioElement);
|
|
125
|
-
return (this.isIOS() && (isVideo || isAudio)) ? 2 /** dummy duration for iOS */ : mElement.duration;
|
|
126
|
-
} catch (error) {
|
|
127
|
-
// If media element is not ready yet, return a default duration
|
|
128
|
-
return 0;
|
|
129
|
-
}
|
|
115
|
+
return this.mediaElement().duration;
|
|
130
116
|
}
|
|
131
117
|
|
|
132
118
|
public getVolume(): number {
|
|
@@ -134,18 +120,11 @@ export abstract class Media extends Rect {
|
|
|
134
120
|
}
|
|
135
121
|
|
|
136
122
|
public getUrl(): string {
|
|
137
|
-
|
|
138
|
-
return this.mediaElement().src;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
// If media element is not ready yet, return the src signal value
|
|
141
|
-
return this.src();
|
|
142
|
-
}
|
|
123
|
+
return this.mediaElement().src;
|
|
143
124
|
}
|
|
144
125
|
|
|
145
126
|
public override dispose() {
|
|
146
|
-
|
|
147
|
-
this.playing(false);
|
|
148
|
-
this.time.save();
|
|
127
|
+
this.pause();
|
|
149
128
|
this.remove();
|
|
150
129
|
super.dispose();
|
|
151
130
|
}
|
|
@@ -166,26 +145,21 @@ export abstract class Media extends Rect {
|
|
|
166
145
|
): Promise<void>;
|
|
167
146
|
|
|
168
147
|
protected setCurrentTime(value: number) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
} catch (error) {
|
|
187
|
-
// If media element is not ready yet, just update the lastTime
|
|
188
|
-
this.lastTime = value;
|
|
148
|
+
const media = this.mediaElement();
|
|
149
|
+
if (media.readyState < 2) return;
|
|
150
|
+
|
|
151
|
+
media.currentTime = value;
|
|
152
|
+
this.lastTime = value;
|
|
153
|
+
if (media.seeking) {
|
|
154
|
+
DependencyContext.collectPromise(
|
|
155
|
+
new Promise<void>(resolve => {
|
|
156
|
+
const listener = () => {
|
|
157
|
+
resolve();
|
|
158
|
+
media.removeEventListener('seeked', listener);
|
|
159
|
+
};
|
|
160
|
+
media.addEventListener('seeked', listener);
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
189
163
|
}
|
|
190
164
|
}
|
|
191
165
|
|
|
@@ -195,26 +169,17 @@ export abstract class Media extends Rect {
|
|
|
195
169
|
`volumes cannot be negative - the value will be clamped to 0.`,
|
|
196
170
|
);
|
|
197
171
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (volume > 1) {
|
|
207
|
-
if (this.allowVolumeAmplificationInPreview()) {
|
|
208
|
-
this.amplify(media, volume);
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
console.warn(
|
|
212
|
-
`you have set the volume of node ${this.key} to ${volume} - your video will be exported with the correct volume, but the browser does not support volumes higher than 1 by default. To enable volume amplification in the preview, set the "allowVolumeAmplificationInPreview" of your <Video/> or <Audio/> tag to true. Note that amplification for previews will not work if you use autoplay within the player due to browser autoplay policies: https://developer.chrome.com/blog/autoplay/#webaudio.`,
|
|
213
|
-
);
|
|
172
|
+
const media = this.mediaElement();
|
|
173
|
+
media.volume = Math.min(Math.max(volume, 0), 1);
|
|
174
|
+
|
|
175
|
+
if (volume > 1) {
|
|
176
|
+
if (this.allowVolumeAmplificationInPreview()) {
|
|
177
|
+
this.amplify(media, volume);
|
|
178
|
+
return;
|
|
214
179
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
180
|
+
console.warn(
|
|
181
|
+
`you have set the volume of node ${this.key} to ${volume} - your video will be exported with the correct volume, but the browser does not support volumes higher than 1 by default. To enable volume amplification in the preview, set the "allowVolumeAmplificationInPreview" of your <Video/> or <Audio/> tag to true. Note that amplification for previews will not work if you use autoplay within the player due to browser autoplay policies: https://developer.chrome.com/blog/autoplay/#webaudio.`,
|
|
182
|
+
);
|
|
218
183
|
}
|
|
219
184
|
}
|
|
220
185
|
|
|
@@ -277,22 +242,12 @@ export abstract class Media extends Rect {
|
|
|
277
242
|
}
|
|
278
243
|
|
|
279
244
|
protected scheduleSeek(time: number) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
this.waitForCanPlay(media, () => {
|
|
287
|
-
// Wait until the media is ready to seek again as
|
|
288
|
-
// setting the time before the video doesn't work reliably.
|
|
289
|
-
media.currentTime = time;
|
|
290
|
-
});
|
|
291
|
-
} catch (error) {
|
|
292
|
-
// If media element is not ready yet, retry after a longer delay
|
|
293
|
-
setTimeout(() => this.scheduleSeek(time), 50);
|
|
294
|
-
}
|
|
295
|
-
}, 0);
|
|
245
|
+
this.waitForCanPlay(this.mediaElement(), () => {
|
|
246
|
+
const media = this.mediaElement();
|
|
247
|
+
// Wait until the media is ready to seek again as
|
|
248
|
+
// setting the time before the video doesn't work reliably.
|
|
249
|
+
media.currentTime = time;
|
|
250
|
+
});
|
|
296
251
|
}
|
|
297
252
|
|
|
298
253
|
/**
|
|
@@ -303,32 +258,23 @@ export abstract class Media extends Rect {
|
|
|
303
258
|
* @returns
|
|
304
259
|
*/
|
|
305
260
|
protected waitForCanPlay(media: HTMLMediaElement, onCanPlay: () => void) {
|
|
306
|
-
|
|
307
|
-
if (media.readyState >= 3) {
|
|
261
|
+
if (media.readyState >= 2) {
|
|
308
262
|
onCanPlay();
|
|
309
263
|
return;
|
|
310
264
|
}
|
|
311
|
-
|
|
265
|
+
|
|
312
266
|
const onCanPlayWrapper = () => {
|
|
313
267
|
onCanPlay();
|
|
314
268
|
media.removeEventListener('canplay', onCanPlayWrapper);
|
|
315
|
-
media.removeEventListener('canplaythrough', onCanPlayWrapper);
|
|
316
269
|
};
|
|
317
270
|
|
|
318
271
|
const onError = () => {
|
|
319
272
|
const reason = this.getErrorReason(media.error?.code);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
console.log(`ERROR: Error loading video: src="${srcValue}", ${reason}`);
|
|
323
|
-
console.log(`Media element src: "${media.src}"`);
|
|
273
|
+
console.log(`ERROR: Error loading video: ${this.src()}, ${reason}`);
|
|
324
274
|
media.removeEventListener('error', onError);
|
|
325
|
-
media.removeEventListener('canplay', onCanPlayWrapper);
|
|
326
|
-
media.removeEventListener('canplaythrough', onCanPlayWrapper);
|
|
327
275
|
};
|
|
328
276
|
|
|
329
|
-
// Listen for both canplay and canplaythrough events
|
|
330
277
|
media.addEventListener('canplay', onCanPlayWrapper);
|
|
331
|
-
media.addEventListener('canplaythrough', onCanPlayWrapper);
|
|
332
278
|
media.addEventListener('error', onError);
|
|
333
279
|
}
|
|
334
280
|
|
|
@@ -347,164 +293,18 @@ export abstract class Media extends Rect {
|
|
|
347
293
|
}
|
|
348
294
|
|
|
349
295
|
public play() {
|
|
350
|
-
|
|
351
|
-
|
|
296
|
+
const time = useThread().time;
|
|
297
|
+
const start = time();
|
|
298
|
+
const offset = this.time();
|
|
299
|
+
const playbackRate = this.playbackRate();
|
|
352
300
|
this.playing(true);
|
|
353
|
-
|
|
354
|
-
// Schedule the actual play operation for when media is ready
|
|
355
|
-
this.schedulePlay();
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
protected schedulePlay() {
|
|
359
|
-
// Prevent recursive calls
|
|
360
|
-
if (this.isSchedulingPlay) {
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
this.isSchedulingPlay = true;
|
|
365
|
-
|
|
366
|
-
// Check if thread context is available before accessing it
|
|
367
|
-
let timeFunction: (() => number) | null = null;
|
|
368
|
-
try {
|
|
369
|
-
const time = useThread().time;
|
|
370
|
-
timeFunction = time;
|
|
371
|
-
} catch (error) {
|
|
372
|
-
// Reset flag and use simple play without thread time
|
|
373
|
-
this.isSchedulingPlay = false;
|
|
374
|
-
this.simplePlay();
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// We need to wait for the media to be ready before we can play it
|
|
379
|
-
// Use a setTimeout to defer the operation and avoid immediate async property access
|
|
380
|
-
setTimeout(() => {
|
|
381
|
-
// Check if we're still supposed to be playing (avoid race conditions)
|
|
382
|
-
const isPlaying = this.playing();
|
|
383
|
-
if (!isPlaying) {
|
|
384
|
-
this.isSchedulingPlay = false;
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// Add another timeout to further defer media element access
|
|
389
|
-
setTimeout(() => {
|
|
390
|
-
try {
|
|
391
|
-
const media = this.mediaElement();
|
|
392
|
-
|
|
393
|
-
// Always use waitForCanPlay to ensure media is ready
|
|
394
|
-
this.waitForCanPlay(media, () => {
|
|
395
|
-
// Double-check we're still playing before calling actuallyPlay
|
|
396
|
-
if (this.playing() && timeFunction) {
|
|
397
|
-
this.actuallyPlay(media, timeFunction);
|
|
398
|
-
}
|
|
399
|
-
// Reset the flag when done
|
|
400
|
-
this.isSchedulingPlay = false;
|
|
401
|
-
});
|
|
402
|
-
} catch (error) {
|
|
403
|
-
// Reset flag before retry
|
|
404
|
-
this.isSchedulingPlay = false;
|
|
405
|
-
// If media is not ready yet, retry after a longer delay
|
|
406
|
-
setTimeout(() => this.schedulePlay(), 100);
|
|
407
|
-
}
|
|
408
|
-
}, 10);
|
|
409
|
-
}, 0);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
private simplePlay() {
|
|
413
|
-
setTimeout(() => {
|
|
414
|
-
try {
|
|
415
|
-
const media = this.mediaElement();
|
|
416
|
-
|
|
417
|
-
// Guard against undefined src
|
|
418
|
-
if (!media.src || media.src.includes('undefined')) {
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (media.paused && this.playing()) {
|
|
423
|
-
media.playbackRate = this.playbackRate();
|
|
424
|
-
const playPromise = media.play();
|
|
425
|
-
if (playPromise !== undefined) {
|
|
426
|
-
playPromise.then(() => {
|
|
427
|
-
console.log('Simple play started successfully');
|
|
428
|
-
}).catch(error => {
|
|
429
|
-
if (error.name !== 'AbortError') {
|
|
430
|
-
console.warn('Error in simple play:', error);
|
|
431
|
-
}
|
|
432
|
-
this.playing(false);
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
} catch (error) {
|
|
437
|
-
// Stop retries for errors
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
}, 10);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
private actuallyPlay(media: HTMLMediaElement, timeFunction: () => number) {
|
|
444
|
-
console.log('=== actuallyPlay called ===');
|
|
445
|
-
console.log('Media element:', media);
|
|
446
|
-
console.log('Media src:', media.src);
|
|
447
|
-
console.log('Media paused:', media.paused);
|
|
448
|
-
console.log('Media readyState:', media.readyState);
|
|
449
|
-
|
|
450
|
-
// Make sure we're still supposed to be playing
|
|
451
|
-
if (!this.playing()) {
|
|
452
|
-
console.log('Playing state is false, aborting actuallyPlay');
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Set playback rate on media element
|
|
457
|
-
media.playbackRate = this.playbackRate();
|
|
458
|
-
|
|
459
|
-
// Ensure the media is ready to play
|
|
460
|
-
if (media.paused) {
|
|
461
|
-
console.log('Media is paused, calling play()');
|
|
462
|
-
// Start playing the media element
|
|
463
|
-
const playPromise = media.play();
|
|
464
|
-
if (playPromise !== undefined) {
|
|
465
|
-
playPromise.then(() => {
|
|
466
|
-
console.log('Media play() promise resolved - should be playing now');
|
|
467
|
-
console.log('Post-play media paused:', media.paused);
|
|
468
|
-
console.log('Post-play media currentTime:', media.currentTime);
|
|
469
|
-
}).catch(error => {
|
|
470
|
-
// Don't warn about AbortError - it's normal when play() is interrupted by pause()
|
|
471
|
-
if (error.name !== 'AbortError') {
|
|
472
|
-
console.warn('Error playing media:', error);
|
|
473
|
-
}
|
|
474
|
-
this.playing(false);
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
} else {
|
|
478
|
-
console.log('Media is already playing');
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// Set up time synchronization
|
|
482
|
-
const start = timeFunction();
|
|
483
|
-
const offset = media.currentTime;
|
|
484
|
-
|
|
485
|
-
// Update time signal
|
|
486
|
-
this.time(() => {
|
|
487
|
-
const newTime = this.clampTime(offset + (timeFunction() - start) * this.playbackRate());
|
|
488
|
-
return newTime;
|
|
489
|
-
});
|
|
301
|
+
this.time(() => this.clampTime(offset + (time() - start) * playbackRate));
|
|
490
302
|
}
|
|
491
303
|
|
|
492
304
|
public pause() {
|
|
493
|
-
// Set the playing state first
|
|
494
305
|
this.playing(false);
|
|
495
306
|
this.time.save();
|
|
496
|
-
|
|
497
|
-
// Try to pause the media element if it's available
|
|
498
|
-
// Use setTimeout to defer access and avoid async property issues
|
|
499
|
-
setTimeout(() => {
|
|
500
|
-
try {
|
|
501
|
-
const media = this.mediaElement();
|
|
502
|
-
media.pause();
|
|
503
|
-
} catch (error) {
|
|
504
|
-
// If media element is not ready yet, just update the state
|
|
505
|
-
// The media won't be playing anyway if it's not ready
|
|
506
|
-
}
|
|
507
|
-
}, 0);
|
|
307
|
+
this.mediaElement().pause();
|
|
508
308
|
}
|
|
509
309
|
|
|
510
310
|
public clampTime(time: number): number {
|
|
@@ -518,27 +318,6 @@ export abstract class Media extends Rect {
|
|
|
518
318
|
protected override collectAsyncResources() {
|
|
519
319
|
super.collectAsyncResources();
|
|
520
320
|
this.seekedMedia();
|
|
521
|
-
// Ensure volume is set when media becomes available
|
|
522
|
-
this.setVolume(this.volume);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
protected autoPlayBasedOnRevideo() {
|
|
526
|
-
// Auto-start/stop playback based on Revideo's playback state
|
|
527
|
-
const playbackState = this.view().playbackState();
|
|
528
|
-
// console.log('autoPlayBasedOnRevideo called:', {
|
|
529
|
-
// playbackState,
|
|
530
|
-
// currentlyPlaying: this.playing(),
|
|
531
|
-
// shouldAutoPlay: (playbackState === PlaybackState.Playing || playbackState === PlaybackState.Presenting) && !this.playing(),
|
|
532
|
-
// shouldAutoPause: playbackState === PlaybackState.Paused && this.playing()
|
|
533
|
-
// });
|
|
534
|
-
|
|
535
|
-
if ((playbackState === PlaybackState.Playing || playbackState === PlaybackState.Presenting) && !this.playing()) {
|
|
536
|
-
console.log('Auto-starting media playback via play() method');
|
|
537
|
-
this.play(); // Call the full play() method instead of just setting playing(true)
|
|
538
|
-
} else if (playbackState === PlaybackState.Paused && this.playing()) {
|
|
539
|
-
console.log('Auto-pausing media playback via pause() method');
|
|
540
|
-
this.pause(); // Call the full pause() method
|
|
541
|
-
}
|
|
542
321
|
}
|
|
543
322
|
|
|
544
323
|
protected getErrorReason(errCode?: number) {
|
|
@@ -564,13 +343,4 @@ export abstract class Media extends Rect {
|
|
|
564
343
|
|
|
565
344
|
return reason;
|
|
566
345
|
}
|
|
567
|
-
|
|
568
|
-
// Helper method to check if running on iOS
|
|
569
|
-
protected isIOS(): boolean {
|
|
570
|
-
if (typeof navigator === 'undefined') return false;
|
|
571
|
-
const isIos = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
572
|
-
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
|
|
573
|
-
|
|
574
|
-
return isIos;
|
|
575
|
-
}
|
|
576
346
|
}
|