Stabilize playback state and cursor feedback
This commit is contained in:
@@ -32,6 +32,7 @@ class AudioManagerModule extends BaseModule {
|
||||
this.ttsQueueEmpty = true;
|
||||
this.pendingMusicPlayback = null;
|
||||
this.currentMusicState = null;
|
||||
this.mediaPreloadTimeoutMs = 60000;
|
||||
this.assetRoots = {
|
||||
images: '/images/',
|
||||
music: '/music/',
|
||||
@@ -493,6 +494,10 @@ class AudioManagerModule extends BaseModule {
|
||||
.then(audio => {
|
||||
this.setMediaVolume(audio, this.getSfxVolume());
|
||||
return audio;
|
||||
})
|
||||
.catch(error => {
|
||||
this.sfxCache.delete(url);
|
||||
throw error;
|
||||
});
|
||||
this.sfxCache.set(url, promise);
|
||||
return promise;
|
||||
@@ -505,6 +510,10 @@ class AudioManagerModule extends BaseModule {
|
||||
.then(audio => {
|
||||
this.setMediaVolume(audio, this.getMusicVolume());
|
||||
return audio;
|
||||
})
|
||||
.catch(error => {
|
||||
this.musicCache.delete(url);
|
||||
throw error;
|
||||
});
|
||||
this.musicCache.set(url, promise);
|
||||
return promise;
|
||||
@@ -517,14 +526,24 @@ class AudioManagerModule extends BaseModule {
|
||||
const finish = (result, error = null) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(timeoutId);
|
||||
audio.removeEventListener('canplaythrough', onReady);
|
||||
audio.removeEventListener('loadeddata', onReady);
|
||||
audio.removeEventListener('error', onError);
|
||||
if (error) reject(error);
|
||||
else resolve(result);
|
||||
if (error) {
|
||||
audio.pause();
|
||||
audio.removeAttribute('src');
|
||||
audio.load();
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
};
|
||||
const onReady = () => finish(audio);
|
||||
const onError = () => finish(null, new Error(`Failed to preload ${label}: ${url}`));
|
||||
const timeoutId = setTimeout(() => {
|
||||
finish(null, new Error(`Timed out preloading ${label}: ${url}`));
|
||||
}, this.mediaPreloadTimeoutMs);
|
||||
audio.preload = 'auto';
|
||||
audio.addEventListener('canplaythrough', onReady, { once: true });
|
||||
audio.addEventListener('loadeddata', onReady, { once: true });
|
||||
@@ -538,16 +557,36 @@ class AudioManagerModule extends BaseModule {
|
||||
if (this.imageCache.has(url)) return this.imageCache.get(url);
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const image = new Image();
|
||||
let settled = false;
|
||||
const finish = (result, error = null) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(timeoutId);
|
||||
image.onload = null;
|
||||
image.onerror = null;
|
||||
if (error) {
|
||||
image.src = '';
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
};
|
||||
image.decoding = 'async';
|
||||
image.onload = () => {
|
||||
if (typeof image.decode === 'function') {
|
||||
image.decode().catch(() => null).then(() => resolve(image));
|
||||
image.decode().catch(() => null).then(() => finish(image));
|
||||
} else {
|
||||
resolve(image);
|
||||
finish(image);
|
||||
}
|
||||
};
|
||||
image.onerror = () => reject(new Error(`Failed to preload image: ${url}`));
|
||||
image.onerror = () => finish(null, new Error(`Failed to preload image: ${url}`));
|
||||
const timeoutId = setTimeout(() => {
|
||||
finish(null, new Error(`Timed out preloading image: ${url}`));
|
||||
}, this.mediaPreloadTimeoutMs);
|
||||
image.src = url;
|
||||
}).catch(error => {
|
||||
this.imageCache.delete(url);
|
||||
throw error;
|
||||
});
|
||||
this.imageCache.set(url, promise);
|
||||
return promise;
|
||||
@@ -571,7 +610,7 @@ class AudioManagerModule extends BaseModule {
|
||||
throw error;
|
||||
}));
|
||||
|
||||
await Promise.all(tasks);
|
||||
return Promise.all(tasks);
|
||||
}
|
||||
|
||||
handleMediaCue(cue) {
|
||||
|
||||
Reference in New Issue
Block a user