refactor: library page

This commit is contained in:
qier222 2021-04-26 15:29:47 +08:00
parent b537081f2a
commit 603e39f362
No known key found for this signature in database
GPG key ID: 9C85007ED905F14D
10 changed files with 474 additions and 462 deletions

View file

@ -1,17 +1,17 @@
import { getTrackDetail, scrobble, getMP3 } from "@/api/track";
import shuffle from "lodash/shuffle";
import { Howler, Howl } from "howler";
import { cacheTrackSource, getTrackSource } from "@/utils/db";
import { getAlbum } from "@/api/album";
import { getPlaylistDetail } from "@/api/playlist";
import { getArtist } from "@/api/artist";
import { personalFM, fmTrash } from "@/api/others";
import store from "@/store";
import { isAccountLoggedIn } from "@/utils/auth";
import { trackUpdateNowPlaying, trackScrobble } from "@/api/lastfm";
import { getTrackDetail, scrobble, getMP3 } from '@/api/track';
import shuffle from 'lodash/shuffle';
import { Howler, Howl } from 'howler';
import { cacheTrackSource, getTrackSource } from '@/utils/db';
import { getAlbum } from '@/api/album';
import { getPlaylistDetail } from '@/api/playlist';
import { getArtist } from '@/api/artist';
import { personalFM, fmTrash } from '@/api/others';
import store from '@/store';
import { isAccountLoggedIn } from '@/utils/auth';
import { trackUpdateNowPlaying, trackScrobble } from '@/api/lastfm';
const electron =
process.env.IS_ELECTRON === true ? window.require("electron") : null;
process.env.IS_ELECTRON === true ? window.require('electron') : null;
const ipcRenderer =
process.env.IS_ELECTRON === true ? electron.ipcRenderer : null;
@ -21,7 +21,7 @@ export default class {
this._playing = false; // 是否正在播放中
this._progress = 0; // 当前播放歌曲的进度
this._enabled = false; // 是否启用Player
this._repeatMode = "off"; // off | on | one
this._repeatMode = 'off'; // off | on | one
this._shuffle = false; // true | false
this._volume = 1; // 0 to 1
this._volumeBeforeMuted = 1; // 用于保存静音前的音量
@ -31,7 +31,7 @@ export default class {
this._current = 0; // 当前播放歌曲在播放列表里的index
this._shuffledList = []; // 被随机打乱的播放列表,随机播放模式下会使用此播放列表
this._shuffledCurrent = 0; // 当前播放歌曲在随机列表里面的index
this._playlistSource = { type: "album", id: 123 }; // 当前播放列表的信息
this._playlistSource = { type: 'album', id: 123 }; // 当前播放列表的信息
this._currentTrack = { id: 86827685 }; // 当前播放歌曲的详细信息
this._playNextList = []; // 当这个list不为空时会优先播放这个list的歌
this._isPersonalFM = false; // 是否是私人FM模式
@ -40,7 +40,7 @@ export default class {
// howler (https://github.com/goldfire/howler.js)
this._howler = null;
Object.defineProperty(this, "_howler", {
Object.defineProperty(this, '_howler', {
enumerable: false,
});
@ -48,7 +48,7 @@ export default class {
this._init();
// for debug
if (process.env.NODE_ENV === "development") {
if (process.env.NODE_ENV === 'development') {
window.player = this;
}
}
@ -58,7 +58,7 @@ export default class {
}
set repeatMode(mode) {
if (this._isPersonalFM) return;
if (!["off", "on", "one"].includes(mode)) {
if (!['off', 'on', 'one'].includes(mode)) {
console.warn("repeatMode: invalid args, must be 'on' | 'off' | 'one'");
return;
}
@ -70,7 +70,7 @@ export default class {
set shuffle(shuffle) {
if (this._isPersonalFM) return;
if (shuffle !== true && shuffle !== false) {
console.warn("shuffle: invalid args, must be Boolean");
console.warn('shuffle: invalid args, must be Boolean');
return;
}
this._shuffle = shuffle;
@ -148,11 +148,11 @@ export default class {
if (this._enabled) {
// 恢复当前播放歌曲
this._replaceCurrentTrack(this._currentTrack.id, false).then(() => {
this._howler?.seek(localStorage.getItem("playerCurrentTrackTime") ?? 0);
this._howler?.seek(localStorage.getItem('playerCurrentTrackTime') ?? 0);
setInterval(
() =>
localStorage.setItem(
"playerCurrentTrackTime",
'playerCurrentTrackTime',
this._howler?.seek()
),
1000
@ -164,7 +164,7 @@ export default class {
// 初始化私人FM
if (this._personalFMTrack.id === 0 || this._personalFMNextTrack.id === 0) {
personalFM().then((result) => {
personalFM().then(result => {
this._personalFMTrack = result.data[0];
this._personalFMNextTrack = result.data[1];
return this._personalFMTrack;
@ -185,7 +185,7 @@ export default class {
}
// 当歌曲是列表最后一首 && 循环模式开启
if (this.list.length === this.current + 1 && this.repeatMode === "on") {
if (this.list.length === this.current + 1 && this.repeatMode === 'on') {
return [this.list[0], 0];
}
@ -194,7 +194,7 @@ export default class {
}
_getPrevTrack() {
// 当歌曲是列表第一首 && 循环模式开启
if (this.current === 0 && this.repeatMode === "on") {
if (this.current === 0 && this.repeatMode === 'on') {
return [this.list[this.list.length - 1], this.list.length - 1];
}
@ -202,10 +202,10 @@ export default class {
return [this.list[this.current - 1], this.current - 1];
}
async _shuffleTheList(firstTrackID = this._currentTrack.id) {
let list = this._list.filter((tid) => tid !== firstTrackID);
if (firstTrackID === "first") list = this._list;
let list = this._list.filter(tid => tid !== firstTrackID);
if (firstTrackID === 'first') list = this._list;
this._shuffledList = shuffle(list);
if (firstTrackID !== "first") this._shuffledList.unshift(firstTrackID);
if (firstTrackID !== 'first') this._shuffledList.unshift(firstTrackID);
}
async _scrobble(track, time, completed = false) {
console.debug(
@ -238,19 +238,19 @@ export default class {
this._howler = new Howl({
src: [source],
html5: true,
format: ["mp3", "flac"],
format: ['mp3', 'flac'],
});
if (autoplay) {
this.play();
document.title = `${this._currentTrack.name} · ${this._currentTrack.ar[0].name} - YesPlayMusic`;
}
this.setOutputDevice();
this._howler.once("end", () => {
this._howler.once('end', () => {
this._nextTrackCallback();
});
}
_getAudioSourceFromCache(id) {
return getTrackSource(id).then((t) => {
return getTrackSource(id).then(t => {
if (!t) return null;
const source = URL.createObjectURL(new Blob([t.source]));
return source;
@ -258,18 +258,18 @@ export default class {
}
_getAudioSourceFromNetease(track) {
if (isAccountLoggedIn()) {
return getMP3(track.id).then((result) => {
return getMP3(track.id).then(result => {
if (!result.data[0]) return null;
if (!result.data[0].url) return null;
if (result.data[0].freeTrialInfo !== null) return null; // 跳过只能试听的歌曲
const source = result.data[0].url.replace(/^http:/, "https:");
const source = result.data[0].url.replace(/^http:/, 'https:');
if (store.state.settings.automaticallyCacheSongs) {
cacheTrackSource(track, source, result.data[0].br);
}
return source;
});
} else {
return new Promise((resolve) => {
return new Promise(resolve => {
resolve(`https://music.163.com/song/media/outer/url?id=${track.id}`);
});
}
@ -282,42 +282,42 @@ export default class {
) {
return null;
}
const source = ipcRenderer.sendSync("unblock-music", track);
const source = ipcRenderer.sendSync('unblock-music', track);
if (store.state.settings.automaticallyCacheSongs && source?.url) {
// TODO: 将unblockMusic字样换成真正的来源比如酷我咪咕等
cacheTrackSource(track, source.url, 128000, "unblockMusic");
cacheTrackSource(track, source.url, 128000, 'unblockMusic');
}
return source?.url;
}
_getAudioSource(track) {
return this._getAudioSourceFromCache(String(track.id))
.then((source) => {
.then(source => {
return source ?? this._getAudioSourceFromNetease(track);
})
.then((source) => {
.then(source => {
return source ?? this._getAudioSourceFromUnblockMusic(track);
});
}
_replaceCurrentTrack(
id,
autoplay = true,
ifUnplayableThen = "playNextTrack"
ifUnplayableThen = 'playNextTrack'
) {
if (autoplay && this._currentTrack.name) {
this._scrobble(this.currentTrack, this._howler?.seek());
}
return getTrackDetail(id).then((data) => {
return getTrackDetail(id).then(data => {
let track = data.songs[0];
this._currentTrack = track;
this._updateMediaSessionMetaData(track);
return this._getAudioSource(track).then((source) => {
return this._getAudioSource(track).then(source => {
if (source) {
this._playAudioSource(source, autoplay);
this._cacheNextTrack();
return source;
} else {
store.dispatch("showToast", `无法播放 ${track.name}`);
ifUnplayableThen === "playNextTrack"
store.dispatch('showToast', `无法播放 ${track.name}`);
ifUnplayableThen === 'playNextTrack'
? this.playNextTrack()
: this.playPrevTrack();
}
@ -329,72 +329,72 @@ export default class {
? this._personalFMNextTrack.id
: this._getNextTrack()[0];
if (!nextTrackID) return;
getTrackDetail(nextTrackID).then((data) => {
getTrackDetail(nextTrackID).then(data => {
let track = data.songs[0];
this._getAudioSource(track);
});
}
_loadSelfFromLocalStorage() {
const player = JSON.parse(localStorage.getItem("player"));
const player = JSON.parse(localStorage.getItem('player'));
if (!player) return;
for (const [key, value] of Object.entries(player)) {
this[key] = value;
}
}
_initMediaSession() {
if ("mediaSession" in navigator) {
navigator.mediaSession.setActionHandler("play", () => {
if ('mediaSession' in navigator) {
navigator.mediaSession.setActionHandler('play', () => {
this.play();
});
navigator.mediaSession.setActionHandler("pause", () => {
navigator.mediaSession.setActionHandler('pause', () => {
this.pause();
});
navigator.mediaSession.setActionHandler("previoustrack", () => {
navigator.mediaSession.setActionHandler('previoustrack', () => {
this.playPrevTrack();
});
navigator.mediaSession.setActionHandler("nexttrack", () => {
navigator.mediaSession.setActionHandler('nexttrack', () => {
this.playNextTrack();
});
navigator.mediaSession.setActionHandler("stop", () => {
navigator.mediaSession.setActionHandler('stop', () => {
this.pause();
});
navigator.mediaSession.setActionHandler("seekto", (event) => {
navigator.mediaSession.setActionHandler('seekto', event => {
this.seek(event.seekTime);
this._updateMediaSessionPositionState();
});
navigator.mediaSession.setActionHandler("seekbackward", (event) => {
navigator.mediaSession.setActionHandler('seekbackward', event => {
this.seek(this.seek() - (event.seekOffset || 10));
this._updateMediaSessionPositionState();
});
navigator.mediaSession.setActionHandler("seekforward", (event) => {
navigator.mediaSession.setActionHandler('seekforward', event => {
this.seek(this.seek() + (event.seekOffset || 10));
this._updateMediaSessionPositionState();
});
}
}
_updateMediaSessionMetaData(track) {
if ("mediaSession" in navigator === false) {
if ('mediaSession' in navigator === false) {
return;
}
let artists = track.ar.map((a) => a.name);
let artists = track.ar.map(a => a.name);
navigator.mediaSession.metadata = new window.MediaMetadata({
title: track.name,
artist: artists.join(","),
artist: artists.join(','),
album: track.al.name,
artwork: [
{
src: track.al.picUrl + "?param=512y512",
type: "image/jpg",
sizes: "512x512",
src: track.al.picUrl + '?param=512y512',
type: 'image/jpg',
sizes: '512x512',
},
],
});
}
_updateMediaSessionPositionState() {
if ("mediaSession" in navigator === false) {
if ('mediaSession' in navigator === false) {
return;
}
if ("setPositionState" in navigator.mediaSession) {
if ('setPositionState' in navigator.mediaSession) {
navigator.mediaSession.setPositionState({
duration: ~~(this.currentTrack.dt / 1000),
playbackRate: 1.0,
@ -404,14 +404,14 @@ export default class {
}
_nextTrackCallback() {
this._scrobble(this._currentTrack, 0, true);
if (!this.isPersonalFM && this.repeatMode === "one") {
if (!this.isPersonalFM && this.repeatMode === 'one') {
this._replaceCurrentTrack(this._currentTrack.id);
} else {
this.playNextTrack();
}
}
_loadPersonalFMNextTrack() {
return personalFM().then((result) => {
return personalFM().then(result => {
this._personalFMNextTrack = result.data[0];
return this._personalFMNextTrack;
});
@ -425,7 +425,7 @@ export default class {
}
let copyTrack = { ...track };
copyTrack.dt -= seekTime * 1000;
ipcRenderer.send("playDiscordPresence", copyTrack);
ipcRenderer.send('playDiscordPresence', copyTrack);
}
_pauseDiscordPresence(track) {
if (
@ -434,7 +434,7 @@ export default class {
) {
return null;
}
ipcRenderer.send("pauseDiscordPresence", track);
ipcRenderer.send('pauseDiscordPresence', track);
}
currentTrackID() {
@ -467,23 +467,23 @@ 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, 'playPrevTrack');
return true;
}
saveSelfToLocalStorage() {
let player = {};
for (let [key, value] of Object.entries(this)) {
if (key === "_playing") continue;
if (key === '_playing') continue;
player[key] = value;
}
localStorage.setItem("player", JSON.stringify(player));
localStorage.setItem('player', JSON.stringify(player));
}
pause() {
this._howler?.pause();
this._playing = false;
document.title = "YesPlayMusic";
document.title = 'YesPlayMusic';
this._pauseDiscordPresence(this._currentTrack);
}
play() {
@ -536,7 +536,7 @@ export default class {
trackIDs,
playlistSourceID,
playlistSourceType,
autoPlayTrackID = "first"
autoPlayTrackID = 'first'
) {
this._isPersonalFM = false;
if (!this._enabled) this._enabled = true;
@ -547,37 +547,37 @@ export default class {
id: playlistSourceID,
};
if (this.shuffle) this._shuffleTheList(autoPlayTrackID);
if (autoPlayTrackID === "first") {
if (autoPlayTrackID === 'first') {
this._replaceCurrentTrack(this.list[0]);
} else {
this.current = trackIDs.indexOf(autoPlayTrackID);
this._replaceCurrentTrack(autoPlayTrackID);
}
}
playAlbumByID(id, trackID = "first") {
getAlbum(id).then((data) => {
let trackIDs = data.songs.map((t) => t.id);
this.replacePlaylist(trackIDs, id, "album", trackID);
playAlbumByID(id, trackID = 'first') {
getAlbum(id).then(data => {
let trackIDs = data.songs.map(t => t.id);
this.replacePlaylist(trackIDs, id, 'album', trackID);
});
}
playPlaylistByID(id, trackID = "first", noCache = false) {
playPlaylistByID(id, trackID = 'first', noCache = false) {
console.debug(
`[debug][Player.js] playPlaylistByID 👉 id:${id} trackID:${trackID} noCache:${noCache}`
);
getPlaylistDetail(id, noCache).then((data) => {
let trackIDs = data.playlist.trackIds.map((t) => t.id);
this.replacePlaylist(trackIDs, id, "playlist", trackID);
getPlaylistDetail(id, noCache).then(data => {
let trackIDs = data.playlist.trackIds.map(t => t.id);
this.replacePlaylist(trackIDs, id, 'playlist', trackID);
});
}
playArtistByID(id, trackID = "first") {
getArtist(id).then((data) => {
let trackIDs = data.hotSongs.map((t) => t.id);
this.replacePlaylist(trackIDs, id, "artist", trackID);
playArtistByID(id, trackID = 'first') {
getArtist(id).then(data => {
let trackIDs = data.hotSongs.map(t => t.id);
this.replacePlaylist(trackIDs, id, 'artist', trackID);
});
}
playTrackOnListByID(id, listName = "default") {
if (listName === "default") {
this._current = this._list.findIndex((t) => t === id);
playTrackOnListByID(id, listName = 'default') {
if (listName === 'default') {
this._current = this._list.findIndex(t => t === id);
}
this._replaceCurrentTrack(id);
}
@ -604,19 +604,19 @@ export default class {
sendSelfToIpcMain() {
if (process.env.IS_ELECTRON !== true) return false;
ipcRenderer.send("player", {
ipcRenderer.send('player', {
playing: this.playing,
likedCurrentTrack: store.state.liked.songs.includes(this.currentTrack.id),
});
}
switchRepeatMode() {
if (this._repeatMode === "on") {
this.repeatMode = "one";
} else if (this._repeatMode === "one") {
this.repeatMode = "off";
if (this._repeatMode === 'on') {
this.repeatMode = 'one';
} else if (this._repeatMode === 'one') {
this.repeatMode = 'off';
} else {
this.repeatMode = "on";
this.repeatMode = 'on';
}
}
switchShuffle() {