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