自動播放下一首

要實現自動播放下一首並不困難,其實就是我們要用關掉前的那個 Audio 實例來播放音樂,如果在關掉之後使用了new Audio()基本上就不行了,考慮以下的程式碼,因為我是用 Vuex 在管理狀態以及實例,因此以下我就直接使用 Vuex 來寫:

export default new Vuex.Store({
    state: {
        audio: new Audio(),
        playNextHooked: false
    },
    actions: {
        play({state, dispatch}, payload) {
            // 為了避免 ended 事件被多次 hook
            // 所以在這裡加上一個 playNextHooked 
            // 使得 stats.audio 的 ended 事件只會被 hook 一次
            if(!state.playNextHooked) {
                state.playNextHooked = true;
                state.audio.addEventListener("ended", () => {
                    // 發送 playNext 指令
                    dispatch("playNext")
                })
            }
                     
            // 直接將要播放的音樂連結置換當前正在播放的音樂
            // 我的猜測是只有當在瀏覽器處於前台狀態
            // 那時候的 audio 才有被播放的權限,
            // 而當瀏覽器最小化之後才 new Audio() 的話,
            // 新的 Audio 實例就沒有辦法被播放,
            // 也許跟安全性也有一些關係,
            // 否則如果在背景可以任意的 new Audio() 並且 play() 的話
            // 完全就可以做一個惡意網站 然後有時候就突然在你手機背景播音樂
            state.audio.src = payload.url;
            // 時間歸零
            state.audio.currentTime = 0;
            // 播放
            state.audio.play();
        },
        playNext({dispatch}) {
            // ... 自訂策略找出下一首要播放的曲目
            // 發送播放指令
            dispatch("play", {url: "https://foo.bar/music.url"})
        }
    }
})

狀態列顯示音樂資訊並控制

顯示音樂資訊的部份就要使用到 MeidaSession 的功能了,MediaSession 可以協助開發者將當前的多媒體資訊顯示到手機的通知列中,並且可以在螢幕鎖定後不用解鎖就可以控制多媒體:

if('mediaSession' in navigator) {
    // eslint-disable-next-line no-undef
    navigator.mediaSession.metadata = new MediaMetadata({
        title: "音樂標題",
        artist: "音樂家",
        album: "專輯名稱",
        artwork: [{ src: "專輯封面或音樂的圖片連結" }]
    })
    
    navigator.mediaSession.setActionHandler('play', () => {
        // 控制繼續播放音樂
        // 也可以直接呼叫 vuex 的 action,如下:
        // dispatch("continuePlay");
    })
    navigator.mediaSession.setActionHandler('pause', () => {
        // 控制暫停當前播放的音樂
        // 也可以直接呼叫 vuex 的 action,如下:
        // dispatch("pauseCurrent");
    })
    navigator.mediaSession.setActionHandler('nexttrack', () => {
        // 控制暫停當前播放的音樂
        // 也可以直接呼叫 vuex 的 action,如下:
        // dispatch("playNext");
    })
    
    // 其他的 Handler 還有:
    // seekbackward 快退
    // seekforward 快進
    // previousTrack 前一首曲子
    
}

以範例的程式碼來說,如果我只有設定playpause以及nexttrack這三個 Handler 的話,狀態列也就只會顯示有設定 Handler 的按鈕,如下: