diff --git a/src/utils/Player.js b/src/utils/Player.js index c81212e..21c90f6 100644 --- a/src/utils/Player.js +++ b/src/utils/Player.js @@ -14,6 +14,15 @@ import { decode as base642Buffer } from '@/utils/base64'; const PLAY_PAUSE_FADE_DURATION = 200; +/** + * @readonly + * @enum {string} + */ +const UNPLAYABLE_CONDITION = { + PLAY_NEXT_TRACK: 'playNextTrack', + PLAY_PREV_TRACK: 'playPrevTrack', +}; + const electron = process.env.IS_ELECTRON === true ? window.require('electron') : null; const ipcRenderer = @@ -164,6 +173,9 @@ export default class { get currentTrack() { return this._currentTrack; } + get currentTrackID() { + return this._currentTrack?.id ?? 0; + } get playlistSource() { return this._playlistSource; } @@ -199,7 +211,7 @@ export default class { if (this._enabled) { // 恢复当前播放歌曲 - this._replaceCurrentTrack(this._currentTrack.id, false).then(() => { + this._replaceCurrentTrack(this.currentTrackID, false).then(() => { this._howler?.seek(localStorage.getItem('playerCurrentTrackTime') ?? 0); }); // update audio source and init howler this._initMediaSession(); @@ -278,7 +290,7 @@ export default class { // 返回 [trackID, index] return [this.list[next], next]; } - async _shuffleTheList(firstTrackID = this._currentTrack.id) { + async _shuffleTheList(firstTrackID = this.currentTrackID) { let list = this._list.filter(tid => tid !== firstTrackID); if (firstTrackID === 'first') list = this._list; this._shuffledList = shuffle(list); @@ -321,6 +333,25 @@ export default class { this._nextTrackCallback(); }, }); + this._howler.on('loaderror', (_, errCode) => { + // https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code + // code 3: MEDIA_ERR_DECODE + if (errCode === 3) { + this._playNextTrack(this._isPersonalFM); + } else { + const t = this.progress; + this._replaceCurrentTrackAudio(this.currentTrack, false, false).then( + replaced => { + // 如果 replaced 为 false,代表当前的 track 已经不是这里想要替换的track + // 此时则不修改当前的歌曲进度 + if (replaced) { + this._howler?.seek(t); + this.play(); + } + } + ); + } + }); if (autoplay) { this.play(); if (this._currentTrack.name) { @@ -452,33 +483,61 @@ export default class { _replaceCurrentTrack( id, autoplay = true, - ifUnplayableThen = 'playNextTrack' + ifUnplayableThen = UNPLAYABLE_CONDITION.PLAY_NEXT_TRACK ) { if (autoplay && this._currentTrack.name) { this._scrobble(this.currentTrack, this._howler?.seek()); } return getTrackDetail(id).then(data => { - let track = data.songs[0]; + const track = data.songs[0]; this._currentTrack = track; this._updateMediaSessionMetaData(track); - return this._getAudioSource(track).then(source => { - if (source) { + return this._replaceCurrentTrackAudio( + track, + autoplay, + true, + ifUnplayableThen + ); + }); + } + /** + * @returns 是否成功加载音频,并使用加载完成的音频替换了howler实例 + */ + _replaceCurrentTrackAudio( + track, + autoplay, + isCacheNextTrack, + ifUnplayableThen = UNPLAYABLE_CONDITION.PLAY_NEXT_TRACK + ) { + return this._getAudioSource(track).then(source => { + if (source) { + let replaced = false; + if (track.id === this.currentTrackID) { this._playAudioSource(source, autoplay); + replaced = true; + } + if (isCacheNextTrack) { this._cacheNextTrack(); - return source; - } else { - store.dispatch('showToast', `无法播放 ${track.name}`); - if (ifUnplayableThen === 'playNextTrack') { - if (this.isPersonalFM) { - this.playNextFMTrack(); - } else { - this.playNextTrack(); - } - } else { + } + return replaced; + } else { + store.dispatch('showToast', `无法播放 ${track.name}`); + switch (ifUnplayableThen) { + case UNPLAYABLE_CONDITION.PLAY_NEXT_TRACK: + this._playNextTrack(this.isPersonalFM); + break; + case UNPLAYABLE_CONDITION.PLAY_PREV_TRACK: this.playPrevTrack(); - } + break; + default: + store.dispatch( + 'showToast', + `undefined Unplayable condition: ${ifUnplayableThen}` + ); + break; } - }); + return false; + } }); } _cacheNextTrack() { @@ -511,11 +570,7 @@ export default class { this.playPrevTrack(); }); navigator.mediaSession.setActionHandler('nexttrack', () => { - if (this.isPersonalFM) { - this.playNextFMTrack(); - } else { - this.playNextTrack(); - } + this._playNextTrack(this.isPersonalFM); }); navigator.mediaSession.setActionHandler('stop', () => { this.pause(); @@ -579,11 +634,9 @@ export default class { _nextTrackCallback() { this._scrobble(this._currentTrack, 0, true); if (!this.isPersonalFM && this.repeatMode === 'one') { - this._replaceCurrentTrack(this._currentTrack.id); - } else if (this.isPersonalFM) { - this.playNextFMTrack(); + this._replaceCurrentTrack(this.currentTrackID); } else { - this.playNextTrack(); + this._playNextTrack(this.isPersonalFM); } } _loadPersonalFMNextTrack() { @@ -628,11 +681,14 @@ export default class { } ipcRenderer?.send('pauseDiscordPresence', track); } - - currentTrackID() { - const { list, current } = this._getListAndCurrent(); - return list[current]; + _playNextTrack(isPersonal) { + if (isPersonal) { + this.playNextFMTrack(); + } else { + this.playNextTrack(); + } } + appendTrack(trackID) { this.list.append(trackID); } @@ -697,7 +753,11 @@ export default class { const [trackID, index] = this._getPrevTrack(); if (trackID === undefined) return false; this.current = index; - this._replaceCurrentTrack(trackID, true, 'playPrevTrack'); + this._replaceCurrentTrack( + trackID, + true, + UNPLAYABLE_CONDITION.PLAY_PREV_TRACK + ); return true; } saveSelfToLocalStorage() { @@ -838,20 +898,14 @@ export default class { addTrackToPlayNext(trackID, playNow = false) { this._playNextList.push(trackID); if (playNow) { - if (this.isPersonalFM) { - this.playNextFMTrack(); - } else { - this.playNextTrack(); - } + this.playNextTrack(); } } playPersonalFM() { this._isPersonalFM = true; if (!this._enabled) this._enabled = true; - if (this._currentTrack.id !== this._personalFMTrack.id) { - this._replaceCurrentTrack(this._personalFMTrack.id).then(() => - this.playOrPause() - ); + if (this.currentTrackID !== this._personalFMTrack.id) { + this._replaceCurrentTrack(this._personalFMTrack.id, true); } else { this.playOrPause(); }