我会在另一个 Stack Overflow question 上看看这个答案,但是我做了一些修改以匹配您的问题:
var audioFileURLs= [];
function preloadAudio(url) {
var audio = new Audio();
// once this file loads, it will call loadedAudio()
// the file will be kept by the browser as cache
audio.addEventListener('canplaythrough', loadedAudio, false);
audio.src = url;
}
var loaded = 0;
function loadedAudio() {
// this will be called every time an audio file is loaded
// we keep track of the loaded files vs the requested files
loaded++;
if (loaded == audioFileURLs.length){
// all have loaded
init();
}
}
var player = document.getElementById('player');
function play(index) {
player.src = audioFiles[index];
player.play();
}
function init() {
// do your stuff here, audio has been loaded
// for example, play all files one after the other
var i = 0;
// once the player ends, play the next one
player.onended = function() {
i++;
if (i >= audioFiles.length) {
// end
return;
}
play(i);
};
// play the first file
play(i);
}
// call node/express server to get a list of links we can hit to retrieve each audio file
fetch('/getAudioUrls/#BookNameOrIdHere#')
.then(r => r.json())
.then(arrayOfURLs => {
audioFileURLs = arrayOfURLs
arrayOfURLs.map(url => preloadAudio(URL))
})
然后在屏幕上添加一个 id 为“播放器”的音频元素,例如<audio id="player"></audio>
不过,有了这个答案,arrayOfURLs 数组必须包含指向您服务器上的 API 的 URL,该 API 将打开 zip 文件并返回指定的 mp3 数据。您可能还只想将此答案作为一般参考,而不是完整的解决方案,因为需要进行优化。您可能应该只加载第一个音频文件,在第一个文件结束前 5 分钟左右,您可能想要开始预加载下一个文件,然后对整个文件重复此过程......这一切都取决于你,但这应该让你站起来。
您也可能会遇到音频元素的问题,因为它只会显示当前音频片段的长度,而不是有声读物的完整长度。我会选择相信这个 zip 文件按章节分隔的书是否正确?如果是这样,您可以创建一个章节选择器,这几乎可以让您跳转到特定章节,即 getAudioUrls URL。
我希望这会有所帮助!
给您的另一个注意事项...阅读您对下方潜在答案的评论,您可以使用某种节点模块将所有音频文件合并为一个(audioconcat 是我在快速谷歌搜索后找到的)并将该文件返回给客户端。但是,我个人不会采取这条路线,因为整个有声读物在组合它们时将在服务器的内存中,直到它返回给客户端。这可能会导致一些内存问题,所以如果可以的话,我会避免它。但是,我承认这个选项可能会很好,因为有声读物的完整长度将显示在音频元素时间轴中。
最好的选择可能是将书籍全长和章节长度存储在 zip 文件中的 details.json 文件中,并在第一次 API 调用中将其连同每个音频文件的 URL 一起发送到客户端。这将使您能够构建一个漂亮的用户界面。