mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-16 21:28:06 +00:00
refactor: player
This commit is contained in:
parent
0482e6a1ed
commit
f6c36fbcac
22 changed files with 659 additions and 500 deletions
|
|
@ -9,7 +9,7 @@
|
|||
</main>
|
||||
<transition name="slide-up">
|
||||
<Player
|
||||
v-if="this.$store.state.player.enable"
|
||||
v-if="this.$store.state.player.enabled"
|
||||
ref="player"
|
||||
v-show="
|
||||
['mv', 'loginUsername', 'login', 'loginAccount'].includes(
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { playAlbumByID, playPlaylistByID, playArtistByID } from "@/utils/play";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
id: { type: Number, required: true },
|
||||
|
|
@ -74,12 +72,13 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
play() {
|
||||
const player = this.$store.state.player;
|
||||
const playActions = {
|
||||
album: playAlbumByID,
|
||||
playlist: playPlaylistByID,
|
||||
artist: playArtistByID,
|
||||
album: player.playAlbumByID,
|
||||
playlist: player.playPlaylistByID,
|
||||
artist: player.playArtistByID,
|
||||
};
|
||||
playActions[this.type](this.id);
|
||||
playActions[this.type].bind(player)(this.id);
|
||||
},
|
||||
goTo() {
|
||||
this.$router.push({ name: this.type, params: { id: this.id } });
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 账号登录才会显示 like 图标 -->
|
||||
<div class="like-button">
|
||||
<button-icon
|
||||
@click.native="likeCurrentSong"
|
||||
|
|
@ -60,9 +59,9 @@
|
|||
<button-icon
|
||||
class="play"
|
||||
@click.native="play"
|
||||
:title="$t(playing ? 'player.pause' : 'player.play')"
|
||||
:title="$t(player.playing ? 'player.pause' : 'player.play')"
|
||||
>
|
||||
<svg-icon :iconClass="playing ? 'pause' : 'play'"
|
||||
<svg-icon :iconClass="player.playing ? 'pause' : 'play'"
|
||||
/></button-icon>
|
||||
<button-icon @click.native="next" :title="$t('player.next')"
|
||||
><svg-icon icon-class="next"
|
||||
|
|
@ -77,15 +76,18 @@
|
|||
/></button-icon>
|
||||
<button-icon
|
||||
:title="
|
||||
player.repeat === 'one'
|
||||
player.repeatMode === 'one'
|
||||
? $t('player.repeatTrack')
|
||||
: $t('player.repeat')
|
||||
"
|
||||
@click.native="repeat"
|
||||
:class="{ active: player.repeat !== 'off' }"
|
||||
:class="{ active: player.repeatMode !== 'off' }"
|
||||
>
|
||||
<svg-icon icon-class="repeat" v-show="player.repeat !== 'one'" />
|
||||
<svg-icon icon-class="repeat-1" v-show="player.repeat === 'one'" />
|
||||
<svg-icon icon-class="repeat" v-show="player.repeatMode !== 'one'" />
|
||||
<svg-icon
|
||||
icon-class="repeat-1"
|
||||
v-show="player.repeatMode === 'one'"
|
||||
/>
|
||||
</button-icon>
|
||||
<button-icon
|
||||
@click.native="shuffle"
|
||||
|
|
@ -94,7 +96,7 @@
|
|||
><svg-icon icon-class="shuffle"
|
||||
/></button-icon>
|
||||
<div class="volume-control">
|
||||
<button-icon :title="$t('player.mute')" @click.native="mute">
|
||||
<button-icon :title="$t('player.mute')" @click.native="player.mute">
|
||||
<svg-icon icon-class="volume" v-show="volume > 0.5" />
|
||||
<svg-icon icon-class="volume-mute" v-show="volume === 0" />
|
||||
<svg-icon
|
||||
|
|
@ -121,13 +123,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { updateMediaSessionMetaData } from "@/utils/mediaSession";
|
||||
import { mapState, mapMutations, mapActions } from "vuex";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import { userLikedSongsIDs } from "@/api/user";
|
||||
import { likeATrack } from "@/api/track";
|
||||
import "@/assets/css/slider.css";
|
||||
import { Howler } from "howler";
|
||||
|
||||
import ButtonIcon from "@/components/ButtonIcon.vue";
|
||||
import VueSlider from "vue-slider-component";
|
||||
|
|
@ -147,10 +147,7 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
setInterval(() => {
|
||||
// fix 歌曲播放完还设置进度的问题,及 _id 不存在的问题
|
||||
if (this.howler && this.howler._sounds?.[0]?._id) {
|
||||
this.progress = ~~this.howler.seek();
|
||||
}
|
||||
this.progress = ~~this.player.seek();
|
||||
}, 1000);
|
||||
if (isAccountLoggedIn()) {
|
||||
userLikedSongsIDs(this.data.user.userId).then((data) => {
|
||||
|
|
@ -159,7 +156,7 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["player", "howler", "settings", "liked", "data"]),
|
||||
...mapState(["player", "settings", "liked", "data"]),
|
||||
currentTrack() {
|
||||
return this.player.currentTrack;
|
||||
},
|
||||
|
|
@ -168,92 +165,46 @@ export default {
|
|||
return this.player.volume;
|
||||
},
|
||||
set(value) {
|
||||
this.updatePlayerState({ key: "volume", value });
|
||||
Howler.volume(value);
|
||||
this.player.volume = value;
|
||||
},
|
||||
},
|
||||
playing() {
|
||||
if (this.howler) {
|
||||
if (this.howler.state() === "loading") {
|
||||
this.updatePlayerState({ key: "playing", value: true });
|
||||
return true;
|
||||
}
|
||||
const status = this.howler.playing();
|
||||
this.updatePlayerState({ key: "playing", value: status });
|
||||
return status;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return this.player.playing;
|
||||
},
|
||||
progressMax() {
|
||||
let max = ~~(this.currentTrack.dt / 1000);
|
||||
let max = ~~(this.player.currentTrack.dt / 1000);
|
||||
return max > 1 ? max - 1 : max;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([
|
||||
"turnOnShuffleMode",
|
||||
"turnOffShuffleMode",
|
||||
"updatePlayerState",
|
||||
"updateRepeatStatus",
|
||||
"updateLikedSongs",
|
||||
]),
|
||||
...mapActions([
|
||||
"nextTrack",
|
||||
"previousTrack",
|
||||
"playTrackOnListByID",
|
||||
"addNextTrackEvent",
|
||||
"showToast",
|
||||
]),
|
||||
...mapMutations(["updateLikedSongs"]),
|
||||
...mapActions(["showToast"]),
|
||||
play() {
|
||||
if (this.playing) {
|
||||
this.howler.pause();
|
||||
} else {
|
||||
if (this.howler.state() === "unloaded") {
|
||||
this.playTrackOnListByID(this.currentTrack.id);
|
||||
}
|
||||
this.howler.play();
|
||||
if (this.howler._onend.length === 0) {
|
||||
this.addNextTrackEvent();
|
||||
updateMediaSessionMetaData(this.currentTrack);
|
||||
}
|
||||
}
|
||||
this.player.playing ? this.player.pause() : this.player.play();
|
||||
},
|
||||
next() {
|
||||
this.progress = 0;
|
||||
this.nextTrack(true);
|
||||
if (this.player.playNextTrack()) this.progress = 0;
|
||||
},
|
||||
previous() {
|
||||
this.progress = 0;
|
||||
this.previousTrack();
|
||||
if (this.player.playPrevTrack()) this.progress = 0;
|
||||
},
|
||||
shuffle() {
|
||||
if (this.player.shuffle === true) {
|
||||
this.turnOffShuffleMode();
|
||||
} else {
|
||||
this.turnOnShuffleMode();
|
||||
}
|
||||
this.player.shuffle = !this.player.shuffle;
|
||||
console.log(this.player);
|
||||
},
|
||||
repeat() {
|
||||
if (this.player.repeat === "on") {
|
||||
this.updateRepeatStatus("one");
|
||||
} else if (this.player.repeat === "one") {
|
||||
this.updateRepeatStatus("off");
|
||||
console.log(this.player.repeatMode);
|
||||
if (this.player.repeatMode === "on") {
|
||||
this.player.repeatMode = "one";
|
||||
} else if (this.player.repeatMode === "one") {
|
||||
this.player.repeatMode = "off";
|
||||
} else {
|
||||
this.updateRepeatStatus("on");
|
||||
}
|
||||
},
|
||||
mute() {
|
||||
if (this.volume === 0) {
|
||||
this.volume = this.oldVolume;
|
||||
} else {
|
||||
this.oldVolume = this.volume;
|
||||
this.volume = 0;
|
||||
this.player.repeatMode = "on";
|
||||
}
|
||||
},
|
||||
setSeek() {
|
||||
this.progress = this.$refs.progress.getValue();
|
||||
this.howler.seek(this.$refs.progress.getValue());
|
||||
this.player.seek(this.$refs.progress.getValue());
|
||||
},
|
||||
goToNextTracksPage() {
|
||||
this.$route.name === "next"
|
||||
|
|
@ -285,15 +236,20 @@ export default {
|
|||
});
|
||||
},
|
||||
goToList() {
|
||||
if (this.player.listInfo.id === this.data.likedSongPlaylistID)
|
||||
if (this.player.playlistSource.id === this.data.likedSongPlaylistID)
|
||||
this.$router.push({ path: "/library/liked-songs" });
|
||||
else
|
||||
this.$router.push({
|
||||
path: "/" + this.player.listInfo.type + "/" + this.player.listInfo.id,
|
||||
path:
|
||||
"/" +
|
||||
this.player.playlistSource.type +
|
||||
"/" +
|
||||
this.player.playlistSource.id,
|
||||
});
|
||||
},
|
||||
goToAlbum() {
|
||||
this.$router.push({ path: "/album/" + this.currentTrack.al.id });
|
||||
if (this.player.currentTrack.al.id === 0) return;
|
||||
this.$router.push({ path: "/album/" + this.player.currentTrack.al.id });
|
||||
},
|
||||
goToArtist(id) {
|
||||
this.$router.push({ path: "/artist/" + id });
|
||||
|
|
|
|||
|
|
@ -41,12 +41,6 @@
|
|||
<script>
|
||||
import { mapActions, mapMutations, mapState } from "vuex";
|
||||
import { likeATrack } from "@/api/track";
|
||||
import {
|
||||
playPlaylistByID,
|
||||
playAlbumByID,
|
||||
playAList,
|
||||
appendTrackToPlayerList,
|
||||
} from "@/utils/play";
|
||||
import { addOrRemoveTrackFromPlaylist } from "@/api/playlist";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
|
||||
|
|
@ -140,24 +134,32 @@ export default {
|
|||
} else if (this.dbclickTrackFunc === "playTrackOnListByID") {
|
||||
this.playTrackOnListByID(trackID);
|
||||
} else if (this.dbclickTrackFunc === "playPlaylistByID") {
|
||||
playPlaylistByID(this.id, trackID);
|
||||
this.$store.state.player.playPlaylistByID(this.id, trackID);
|
||||
}
|
||||
},
|
||||
playThisListDefault(trackID) {
|
||||
if (this.type === "playlist") {
|
||||
playPlaylistByID(this.id, trackID);
|
||||
this.$store.state.player.playPlaylistByID(this.id, trackID);
|
||||
} else if (this.type === "album") {
|
||||
playAlbumByID(this.id, trackID);
|
||||
this.$store.state.player.playAlbumByID(this.id, trackID);
|
||||
} else if (this.type === "tracklist") {
|
||||
let trackIDs = this.tracks.map((t) => t.id);
|
||||
playAList(trackIDs, this.tracks[0].ar[0].id, "artist", trackID);
|
||||
this.$store.state.player.replacePlaylist(
|
||||
trackIDs,
|
||||
this.id,
|
||||
"artist",
|
||||
trackID
|
||||
);
|
||||
}
|
||||
},
|
||||
play() {
|
||||
appendTrackToPlayerList(this.clickTrack.id, true);
|
||||
this.$store.state.player.addTrackToPlayNext(
|
||||
this.rightClickedTrack.id,
|
||||
true
|
||||
);
|
||||
},
|
||||
playNext() {
|
||||
appendTrackToPlayerList(this.clickTrack.id);
|
||||
this.$store.state.player.addTrackToPlayNext(this.rightClickedTrack.id);
|
||||
},
|
||||
like() {
|
||||
this.likeASong(this.rightClickedTrack.id);
|
||||
|
|
|
|||
|
|
@ -62,9 +62,9 @@ export function createTouchBar(window) {
|
|||
},
|
||||
});
|
||||
|
||||
ipcMain.on("vuex-state", (e, { player, liked }) => {
|
||||
playButton.label = player.playing === true ? "" : "";
|
||||
likeButton.label = liked.songs.includes(player.currentTrack.id) ? "" : "";
|
||||
ipcMain.on("player", (e, { playing, likedCurrentTrack }) => {
|
||||
playButton.label = playing === true ? "" : "";
|
||||
likeButton.label = likedCurrentTrack ? "" : "";
|
||||
});
|
||||
|
||||
const touchBar = new TouchBar({
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import store from "./store";
|
|||
import i18n from "@/locale";
|
||||
import "@/assets/icons";
|
||||
import "@/utils/filters";
|
||||
import { initMediaSession } from "@/utils/mediaSession";
|
||||
import "./registerServiceWorker";
|
||||
import { dailyTask } from "@/utils/common";
|
||||
|
||||
|
|
@ -21,8 +20,6 @@ Vue.use(VueAnalytics, {
|
|||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
initMediaSession();
|
||||
|
||||
if (process.env.VUE_APP_ENABLE_SENTRY === "true") {
|
||||
Sentry.init({
|
||||
dsn:
|
||||
|
|
|
|||
|
|
@ -1,185 +1,4 @@
|
|||
import { updateMediaSessionMetaData } from "@/utils/mediaSession";
|
||||
import { getTrackDetail, scrobble, getMP3 as getMP3Api } from "@/api/track";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import { updateHttps } from "@/utils/common";
|
||||
import localforage from "localforage";
|
||||
import store from "@/store";
|
||||
import { cacheTrack } from "@/utils/db";
|
||||
|
||||
const electron =
|
||||
process.env.IS_ELECTRON === true ? window.require("electron") : null;
|
||||
const ipcRenderer =
|
||||
process.env.IS_ELECTRON === true ? electron.ipcRenderer : null;
|
||||
|
||||
export default {
|
||||
switchTrack(
|
||||
{ state, dispatch, commit },
|
||||
{ id, sort = 0, autoplay = true, ifUnplayableThen = "nextTrack" }
|
||||
) {
|
||||
getTrackDetail(id).then((data) => {
|
||||
let track = data.songs[0];
|
||||
track.sort = sort;
|
||||
|
||||
// 获取当前的播放时间。初始化为 loading 状态时返回 howler 的实例而不是浮点数时间,比如 1.332
|
||||
let time = state.howler ? state.howler.seek() : 0;
|
||||
let currentTime = 0;
|
||||
if (time === 0) {
|
||||
// state.howler._duration 可以获得当前实例的播放时长
|
||||
currentTime = 180;
|
||||
}
|
||||
if (time.toString() === "[object Object]") {
|
||||
currentTime = 0;
|
||||
}
|
||||
if (time > 0) {
|
||||
currentTime = time;
|
||||
}
|
||||
scrobble({
|
||||
id: state.player.currentTrack.id,
|
||||
sourceid: state.player.listInfo.id,
|
||||
time: currentTime,
|
||||
});
|
||||
|
||||
commit("updateCurrentTrack", track);
|
||||
updateMediaSessionMetaData(track);
|
||||
document.title = `${track.name} · ${track.ar[0].name} - YesPlayMusic`;
|
||||
|
||||
let unblockSongUrl = null;
|
||||
if (track.playable === false) {
|
||||
let res = undefined;
|
||||
if (process.env.IS_ELECTRON === true) {
|
||||
res = ipcRenderer.sendSync("unblock-music", track);
|
||||
}
|
||||
if (res?.url) {
|
||||
unblockSongUrl = res.url;
|
||||
} else {
|
||||
dispatch(ifUnplayableThen);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function commitMP3(mp3) {
|
||||
commit("replaceMP3", { mp3, autoplay });
|
||||
state.howler.once("end", () => {
|
||||
dispatch("nextTrack");
|
||||
});
|
||||
}
|
||||
|
||||
function getMP3(id) {
|
||||
return getMP3Api(id).then((data) => {
|
||||
// 未知情况下会没有返回数据导致报错,增加防范逻辑
|
||||
if (data.data[0]) {
|
||||
const url = updateHttps(data.data[0].url);
|
||||
commitMP3(url);
|
||||
return url;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isAccountLoggedIn()) {
|
||||
if (store.state.settings.automaticallyCacheSongs === true) {
|
||||
let tracks = localforage.createInstance({
|
||||
name: "tracks",
|
||||
});
|
||||
tracks
|
||||
.getItem(`${track.id}`)
|
||||
.then((t) => {
|
||||
if (t !== null) {
|
||||
const blob = new Blob([t.mp3]);
|
||||
commitMP3(URL.createObjectURL(blob));
|
||||
} else {
|
||||
if (unblockSongUrl) {
|
||||
commitMP3(unblockSongUrl);
|
||||
cacheTrack(`${track.id}`, unblockSongUrl);
|
||||
} else {
|
||||
getMP3(track.id).then((url) => {
|
||||
cacheTrack(`${track.id}`, url);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err.messaeg);
|
||||
if (unblockSongUrl) {
|
||||
commitMP3(unblockSongUrl);
|
||||
} else {
|
||||
getMP3(track.id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (unblockSongUrl) {
|
||||
commitMP3(unblockSongUrl);
|
||||
} else {
|
||||
getMP3(track.id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
commitMP3(
|
||||
unblockSongUrl ||
|
||||
`https://music.163.com/song/media/outer/url?id=${track.id}`
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
playFirstTrackOnList({ state, dispatch }) {
|
||||
dispatch(
|
||||
"switchTrack",
|
||||
state.player.list.find((t) => t.sort === 0)
|
||||
);
|
||||
},
|
||||
playTrackOnListByID({ state, commit, dispatch }, trackID) {
|
||||
let track = state.player.list.find((t) => t.id === trackID);
|
||||
dispatch("switchTrack", track);
|
||||
if (state.player.shuffle) {
|
||||
// 当随机模式开启时,双击列表的一首歌进行播放,此时要把这首歌的sort调到第一(0),这样用户就能随机播放完整的歌单
|
||||
let otherTrack = state.player.list.find((t) => t.sort === 0);
|
||||
commit("switchSortBetweenTwoTracks", {
|
||||
trackID1: track.id,
|
||||
trackID2: otherTrack.id,
|
||||
});
|
||||
}
|
||||
},
|
||||
nextTrack({ state, dispatch }, realNext = false) {
|
||||
let nextTrack = state.player.list.find(
|
||||
(track) => track.sort === state.player.currentTrack.sort + 1
|
||||
);
|
||||
|
||||
if (state.player.repeat === "one" && realNext === false) {
|
||||
nextTrack = state.player.currentTrack;
|
||||
}
|
||||
|
||||
if (nextTrack === undefined) {
|
||||
if (state.player.repeat !== "off") {
|
||||
nextTrack = state.player.list.find((t) => t.sort === 0);
|
||||
} else {
|
||||
document.title = "YesPlayMusic";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dispatch("switchTrack", nextTrack);
|
||||
},
|
||||
previousTrack({ state, dispatch }) {
|
||||
let previousTrack = state.player.list.find(
|
||||
(track) => track.sort === state.player.currentTrack.sort - 1
|
||||
);
|
||||
if (previousTrack == undefined) {
|
||||
if (state.player.repeat !== "off") {
|
||||
previousTrack = state.player.list.reduce((x, y) => (x > y ? x : y));
|
||||
} else {
|
||||
previousTrack = state.player.list.find((t) => t.sort === 0);
|
||||
}
|
||||
}
|
||||
dispatch("switchTrack", {
|
||||
id: previousTrack.id,
|
||||
sort: previousTrack.sort,
|
||||
ifUnplayableThen: "previousTrack",
|
||||
});
|
||||
},
|
||||
addNextTrackEvent({ state, dispatch }) {
|
||||
state.howler.once("end", () => {
|
||||
dispatch("nextTrack");
|
||||
});
|
||||
},
|
||||
showToast({ state, commit }, text) {
|
||||
if (state.toast.timer !== null) {
|
||||
clearTimeout(state.toast.timer);
|
||||
|
|
|
|||
|
|
@ -3,23 +3,13 @@ import Vuex from "vuex";
|
|||
import state from "./state";
|
||||
import mutations from "./mutations";
|
||||
import actions from "./actions";
|
||||
import initLocalStorage from "./initLocalStorage";
|
||||
import { Howler } from "howler";
|
||||
import { changeAppearance } from "@/utils/common";
|
||||
import updateApp from "@/utils/updateApp";
|
||||
import pkg from "../../package.json";
|
||||
import Player from "@/utils/Player";
|
||||
// vuex 自定义插件
|
||||
import { getBroadcastPlugin } from "./plugins/broadcast";
|
||||
import saveToLocalStorage from "./plugins/localStorage";
|
||||
|
||||
if (localStorage.getItem("appVersion") === null) {
|
||||
localStorage.setItem("player", JSON.stringify(initLocalStorage.player));
|
||||
localStorage.setItem("settings", JSON.stringify(initLocalStorage.settings));
|
||||
localStorage.setItem("data", JSON.stringify(initLocalStorage.data));
|
||||
localStorage.setItem("appVersion", pkg.version);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
updateApp();
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
|
@ -39,19 +29,6 @@ const options = {
|
|||
|
||||
const store = new Vuex.Store(options);
|
||||
|
||||
Howler.volume(store.state.player.volume);
|
||||
// 防止软件第一次打开资源加载2次
|
||||
Howler.autoUnlock = false;
|
||||
|
||||
const currentTrack = store.state?.player?.currentTrack;
|
||||
if (currentTrack?.id) {
|
||||
store.dispatch("switchTrack", {
|
||||
id: currentTrack.id,
|
||||
sort: currentTrack.sort,
|
||||
autoplay: false,
|
||||
});
|
||||
}
|
||||
|
||||
if ([undefined, null].includes(store.state.settings.lang)) {
|
||||
let lang = "en";
|
||||
if (navigator.language.slice(0, 2) === "zh") lang = "zh-CN";
|
||||
|
|
@ -69,4 +46,17 @@ window
|
|||
}
|
||||
});
|
||||
|
||||
let player = new Player();
|
||||
player = new Proxy(player, {
|
||||
set(target, prop, val) {
|
||||
// console.log({ prop, val });
|
||||
target[prop] = val;
|
||||
if (prop === "_howler") return true;
|
||||
target.saveSelfToLocalStorage();
|
||||
target.sendSelfToIpcMain();
|
||||
return true;
|
||||
},
|
||||
});
|
||||
store.state.player = player;
|
||||
|
||||
export default store;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,7 @@
|
|||
import { playlistCategories } from "@/utils/staticData";
|
||||
|
||||
let localStorage = {
|
||||
player: {
|
||||
enable: false,
|
||||
show: true,
|
||||
playing: false,
|
||||
shuffle: false,
|
||||
volume: 1,
|
||||
repeat: "off", // on | off | one
|
||||
currentTrack: {},
|
||||
notShuffledList: [],
|
||||
list: [],
|
||||
listInfo: {
|
||||
type: "",
|
||||
id: "",
|
||||
},
|
||||
},
|
||||
player: {},
|
||||
settings: {
|
||||
playlistCategories,
|
||||
lang: null,
|
||||
|
|
|
|||
|
|
@ -1,100 +1,7 @@
|
|||
import { Howl, Howler } from "howler";
|
||||
import { shuffleAList } from "@/utils/common";
|
||||
|
||||
export default {
|
||||
updatePlayerState(state, { key, value }) {
|
||||
state.player[key] = value;
|
||||
},
|
||||
updateCurrentTrack(state, track) {
|
||||
state.player.currentTrack = track;
|
||||
},
|
||||
replaceMP3(state, { mp3, autoplay }) {
|
||||
if (state.howler) {
|
||||
Howler.unload();
|
||||
}
|
||||
state.howler = new Howl({
|
||||
src: [mp3],
|
||||
autoplay,
|
||||
html5: true,
|
||||
format: ["mp3", "flac"],
|
||||
});
|
||||
},
|
||||
updatePlayerList(state, list) {
|
||||
state.player.list = list;
|
||||
if (state.player.enable !== true) state.player.enable = true;
|
||||
},
|
||||
updateListInfo(state, info) {
|
||||
state.player.listInfo = info;
|
||||
},
|
||||
updateRepeatStatus(state, status) {
|
||||
state.player.repeat = status;
|
||||
},
|
||||
appendTrackToPlayerList(state, { track, playNext = false }) {
|
||||
let existTrack = state.player.list.find((t) => t.id === track.id);
|
||||
if (
|
||||
(existTrack === null || existTrack === undefined) &&
|
||||
playNext === false
|
||||
) {
|
||||
state.player.list.push(track);
|
||||
return;
|
||||
}
|
||||
|
||||
// 把track加入到正在播放歌曲的下一首位置
|
||||
state.player.list = state.player.list.map((t) => {
|
||||
if (t.sort > state.player.currentTrack.sort) {
|
||||
t.sort = t.sort + 1;
|
||||
}
|
||||
return t;
|
||||
});
|
||||
track.sort = state.player.currentTrack.sort + 1;
|
||||
state.player.list.push(track);
|
||||
},
|
||||
turnOnShuffleMode(state) {
|
||||
state.player.notShuffledList = JSON.parse(
|
||||
JSON.stringify(state.player.list)
|
||||
);
|
||||
state.player.shuffle = true;
|
||||
|
||||
let newSorts = shuffleAList(
|
||||
state.player.list.filter((t) => t.sort > state.player.currentTrack.sort)
|
||||
);
|
||||
|
||||
state.player.list = state.player.list.map((track) => {
|
||||
if (newSorts[track.id] !== undefined) track.sort = newSorts[track.id];
|
||||
return track;
|
||||
});
|
||||
},
|
||||
turnOffShuffleMode(state) {
|
||||
state.player.shuffle = false;
|
||||
state.player.list = JSON.parse(
|
||||
JSON.stringify(state.player.notShuffledList)
|
||||
);
|
||||
state.player.currentTrack.sort = state.player.list.find(
|
||||
(t) => t.id === state.player.currentTrack.id
|
||||
).sort;
|
||||
},
|
||||
shuffleTheListBeforePlay(state) {
|
||||
state.player.notShuffledList = JSON.parse(
|
||||
JSON.stringify(state.player.list)
|
||||
);
|
||||
let newSorts = shuffleAList(state.player.list);
|
||||
state.player.list = state.player.list.map((track) => {
|
||||
track.sort = newSorts[track.id];
|
||||
return track;
|
||||
});
|
||||
},
|
||||
updateLikedSongs(state, trackIDs) {
|
||||
state.liked.songs = trackIDs;
|
||||
},
|
||||
switchSortBetweenTwoTracks(state, { trackID1, trackID2 }) {
|
||||
let t1 = state.player.list.find((t) => t.id === trackID1);
|
||||
let t2 = state.player.list.find((t) => t.id === trackID2);
|
||||
let sorts = [t1.sort, t2.sort];
|
||||
state.player.list = state.player.list.map((t) => {
|
||||
if (t.id === t1.id) t.sort = sorts[1];
|
||||
if (t.id === t2.id) t.sort = sorts[0];
|
||||
return t;
|
||||
});
|
||||
state.player.sendSelfToIpcMain();
|
||||
},
|
||||
changeLang(state, lang) {
|
||||
state.settings.lang = lang;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
export default (store) => {
|
||||
store.subscribe((mutation, state) => {
|
||||
// console.log(mutation);
|
||||
localStorage.setItem("player", JSON.stringify(state.player));
|
||||
localStorage.setItem("settings", JSON.stringify(state.settings));
|
||||
localStorage.setItem("data", JSON.stringify(state.data));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,12 @@
|
|||
import initLocalStorage from "./initLocalStorage";
|
||||
import pkg from "../../package.json";
|
||||
|
||||
if (localStorage.getItem("appVersion") === null) {
|
||||
localStorage.setItem("settings", JSON.stringify(initLocalStorage.settings));
|
||||
localStorage.setItem("data", JSON.stringify(initLocalStorage.data));
|
||||
localStorage.setItem("appVersion", pkg.version);
|
||||
}
|
||||
|
||||
export default {
|
||||
howler: null,
|
||||
liked: {
|
||||
|
|
|
|||
377
src/utils/Player.js
Normal file
377
src/utils/Player.js
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
import { getTrackDetail, scrobble, getMP3 } from "@/api/track";
|
||||
import { shuffle } from "lodash";
|
||||
import { Howler, Howl } from "howler";
|
||||
import localforage from "localforage";
|
||||
import { cacheTrack } from "@/utils/db";
|
||||
import { getAlbum } from "@/api/album";
|
||||
import { getPlaylistDetail } from "@/api/playlist";
|
||||
import { getArtist } from "@/api/artist";
|
||||
import store from "@/store";
|
||||
|
||||
const electron =
|
||||
process.env.IS_ELECTRON === true ? window.require("electron") : null;
|
||||
const ipcRenderer =
|
||||
process.env.IS_ELECTRON === true ? electron.ipcRenderer : null;
|
||||
|
||||
export default class {
|
||||
constructor() {
|
||||
this._enabled = false;
|
||||
this._repeatMode = "off"; // off | on | one
|
||||
this._shuffle = false; // true | false
|
||||
this._volume = 1; // 0 to 1
|
||||
this._volumeBeforeMuted = 1; // 用于保存静音前的音量
|
||||
this._list = [];
|
||||
this._current = 0; // current track index
|
||||
this._shuffledList = [];
|
||||
this._shuffledCurrent = 0;
|
||||
this._playlistSource = { type: "album", id: 123 };
|
||||
this._currentTrack = { id: 86827685 };
|
||||
this._playNextList = []; // 当这个list不为空时,会优先播放这个list的歌
|
||||
this._playing = false;
|
||||
|
||||
this._howler = null;
|
||||
Object.defineProperty(this, "_howler", {
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
get repeatMode() {
|
||||
return this._repeatMode;
|
||||
}
|
||||
set repeatMode(mode) {
|
||||
if (!["off", "on", "one"].includes(mode)) {
|
||||
console.warn("repeatMode: invalid args, must be 'on' | 'off' | 'one'");
|
||||
return;
|
||||
}
|
||||
this._repeatMode = mode;
|
||||
}
|
||||
get shuffle() {
|
||||
return this._shuffle;
|
||||
}
|
||||
set shuffle(shuffle) {
|
||||
if (shuffle !== true && shuffle !== false) {
|
||||
console.warn("shuffle: invalid args, must be Boolean");
|
||||
return;
|
||||
}
|
||||
this._shuffle = shuffle;
|
||||
if (shuffle) {
|
||||
this._shuffleTheList();
|
||||
}
|
||||
}
|
||||
get volume() {
|
||||
return this._volume;
|
||||
}
|
||||
set volume(volume) {
|
||||
this._volume = volume;
|
||||
Howler.volume(volume);
|
||||
}
|
||||
get list() {
|
||||
return this.shuffle ? this._shuffledList : this._list;
|
||||
}
|
||||
set list(list) {
|
||||
this._list = list;
|
||||
}
|
||||
get current() {
|
||||
return this.shuffle ? this._shuffledCurrent : this._current;
|
||||
}
|
||||
set current(current) {
|
||||
if (this.shuffle) {
|
||||
this._shuffledCurrent = current;
|
||||
} else {
|
||||
this._current = current;
|
||||
}
|
||||
}
|
||||
get enabled() {
|
||||
return this._enabled;
|
||||
}
|
||||
get playing() {
|
||||
return this._playing;
|
||||
}
|
||||
get currentTrack() {
|
||||
return this._currentTrack;
|
||||
}
|
||||
get playlistSource() {
|
||||
return this._playlistSource;
|
||||
}
|
||||
get playNextList() {
|
||||
return this._playNextList;
|
||||
}
|
||||
|
||||
_init() {
|
||||
Howler.autoUnlock = false;
|
||||
this._loadSelfFromLocalStorage();
|
||||
this._replaceCurrentTrack(this._currentTrack.id, false); // update audio source and init howler
|
||||
this._initMediaSession();
|
||||
}
|
||||
_getNextTrack() {
|
||||
// 返回 [trackID, index]
|
||||
if (this._playNextList.length > 0) {
|
||||
let trackID = this._playNextList.shift();
|
||||
return [trackID, this.current];
|
||||
}
|
||||
if (this.list.length === this.current + 1 && this.repeatMode === "on") {
|
||||
// 当歌曲是列表最后一首 && 循环模式开启
|
||||
return [this.list[0], 0];
|
||||
}
|
||||
return [this.list[this.current + 1], this.current + 1];
|
||||
}
|
||||
_getPrevTrack() {
|
||||
if (this.current === 0 && this.repeatMode === "on") {
|
||||
// 当歌曲是列表第一首 && 循环模式开启
|
||||
return [this.list[this.list.length - 1], this.list.length - 1];
|
||||
}
|
||||
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;
|
||||
this._shuffledList = shuffle(list);
|
||||
if (firstTrackID !== "first") this._shuffledList.unshift(firstTrackID);
|
||||
}
|
||||
async _scrobble(complete = false) {
|
||||
let time = this._howler.seek();
|
||||
if (complete) {
|
||||
time = ~~(this._currentTrack.dt / 100);
|
||||
}
|
||||
scrobble({
|
||||
id: this._currentTrack.id,
|
||||
sourceid: this.playlistSource.id,
|
||||
time,
|
||||
});
|
||||
}
|
||||
_playAudioSource(source, autoplay = true) {
|
||||
Howler.unload();
|
||||
this._howler = new Howl({
|
||||
src: [source],
|
||||
html5: true,
|
||||
format: ["mp3", "flac"],
|
||||
});
|
||||
if (autoplay) this.play();
|
||||
this._howler.once("end", () => {
|
||||
this._nextTrackCallback();
|
||||
});
|
||||
}
|
||||
_getAudioSourceFromCache(id) {
|
||||
let tracks = localforage.createInstance({ name: "tracks" });
|
||||
return tracks.getItem(id).then((t) => {
|
||||
if (t === null) return null;
|
||||
const source = URL.createObjectURL(new Blob([t.mp3]));
|
||||
return source;
|
||||
});
|
||||
}
|
||||
_getAudioSourceFromNetease(track) {
|
||||
return getMP3(track.id).then((data) => {
|
||||
if (!data.data[0]) return null;
|
||||
if (data.data[0].freeTrialInfo !== null) return null; // 跳过只能试听的歌曲
|
||||
const source = data.data[0].url.replace(/^http:/, "https:");
|
||||
if (store.state.settings.automaticallyCacheSongs) {
|
||||
cacheTrack(track.id, source);
|
||||
}
|
||||
return source;
|
||||
});
|
||||
}
|
||||
_getAudioSourceFromUnblockMusic(track) {
|
||||
if (process.env.IS_ELECTRON !== true) return null;
|
||||
const source = ipcRenderer.sendSync("unblock-music", track);
|
||||
return source?.url;
|
||||
}
|
||||
_getAudioSource(track) {
|
||||
return this._getAudioSourceFromCache(String(track.id))
|
||||
.then((source) => {
|
||||
if (!source) return null;
|
||||
return source;
|
||||
})
|
||||
.then((source) => {
|
||||
if (source) return source;
|
||||
return this._getAudioSourceFromNetease(track);
|
||||
})
|
||||
.then((source) => {
|
||||
if (source) return source;
|
||||
return this._getAudioSourceFromUnblockMusic(track);
|
||||
});
|
||||
}
|
||||
_replaceCurrentTrack(
|
||||
id,
|
||||
autoplay = true,
|
||||
ifUnplayableThen = "playNextTrack"
|
||||
) {
|
||||
return getTrackDetail(id).then((data) => {
|
||||
let track = data.songs[0];
|
||||
this._currentTrack = track;
|
||||
this._updateMediaSessionMetaData(track);
|
||||
document.title = `${track.name} · ${track.ar[0].name} - YesPlayMusic`;
|
||||
this._getAudioSource(track).then((source) => {
|
||||
if (source) {
|
||||
this._playAudioSource(source, autoplay);
|
||||
return source;
|
||||
} else {
|
||||
ifUnplayableThen === "playNextTrack"
|
||||
? this.playNextTrack()
|
||||
: this.playPrevTrack();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
_loadSelfFromLocalStorage() {
|
||||
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", () => {
|
||||
this.play();
|
||||
});
|
||||
navigator.mediaSession.setActionHandler("pause", () => {
|
||||
this.pause();
|
||||
});
|
||||
navigator.mediaSession.setActionHandler("previoustrack", () => {
|
||||
this.playPrevTrack();
|
||||
});
|
||||
navigator.mediaSession.setActionHandler("nexttrack", () => {
|
||||
this.playNextTrack();
|
||||
});
|
||||
navigator.mediaSession.setActionHandler("stop", () => {
|
||||
this.pause();
|
||||
});
|
||||
}
|
||||
}
|
||||
_updateMediaSessionMetaData(track) {
|
||||
if ("mediaSession" in navigator === false) {
|
||||
return;
|
||||
}
|
||||
let artists = track.ar.map((a) => a.name);
|
||||
navigator.mediaSession.metadata = new window.MediaMetadata({
|
||||
title: track.name,
|
||||
artist: artists.join(","),
|
||||
album: track.al.name,
|
||||
artwork: [
|
||||
{
|
||||
src: track.al.picUrl + "?param=512y512",
|
||||
type: "image/jpg",
|
||||
sizes: "512x512",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
_nextTrackCallback() {
|
||||
this._scrobble(true);
|
||||
if (this.repeatMode === "one") {
|
||||
this._howler.play();
|
||||
} else {
|
||||
this.playNextTrack();
|
||||
}
|
||||
}
|
||||
|
||||
currentTrackID() {
|
||||
const { list, current } = this._getListAndCurrent();
|
||||
return list[current];
|
||||
}
|
||||
appendTrack(trackID) {
|
||||
this.list.append(trackID);
|
||||
}
|
||||
playNextTrack() {
|
||||
// TODO: 切换歌曲时增加加载中的状态
|
||||
const [trackID, index] = this._getNextTrack();
|
||||
if (trackID === undefined) {
|
||||
this._howler.stop();
|
||||
return false;
|
||||
}
|
||||
this.current = index;
|
||||
this._replaceCurrentTrack(trackID);
|
||||
return true;
|
||||
}
|
||||
playPrevTrack() {
|
||||
const [trackID, index] = this._getPrevTrack();
|
||||
if (trackID === undefined) return false;
|
||||
this.current = index;
|
||||
this._replaceCurrentTrack(trackID, true, "playPrevTrack");
|
||||
return true;
|
||||
}
|
||||
saveSelfToLocalStorage() {
|
||||
let player = {};
|
||||
for (let [key, value] of Object.entries(this)) {
|
||||
if (key === "_playing") continue;
|
||||
player[key] = value;
|
||||
}
|
||||
|
||||
localStorage.setItem("player", JSON.stringify(player));
|
||||
}
|
||||
|
||||
pause() {
|
||||
this._howler.pause();
|
||||
this._playing = false;
|
||||
}
|
||||
play() {
|
||||
this._howler.play();
|
||||
this._playing = true;
|
||||
}
|
||||
seek(time = null) {
|
||||
if (time !== null) this._howler.seek(time);
|
||||
return this._howler.seek();
|
||||
}
|
||||
mute() {
|
||||
if (this.volume === 0) {
|
||||
this.volume = this._volumeBeforeMuted;
|
||||
} else {
|
||||
this._volumeBeforeMuted = this.volume;
|
||||
this.volume = 0;
|
||||
}
|
||||
}
|
||||
|
||||
replacePlaylist(
|
||||
trackIDs,
|
||||
playlistSourceID,
|
||||
playlistSourceType,
|
||||
autoPlayTrackID = "first"
|
||||
) {
|
||||
if (!this._enabled) this._enabled = true;
|
||||
this.list = trackIDs;
|
||||
this.current = 0;
|
||||
this._playlistSource = {
|
||||
type: playlistSourceType,
|
||||
id: playlistSourceID,
|
||||
};
|
||||
if (this.shuffle) this._shuffleTheList(autoPlayTrackID);
|
||||
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);
|
||||
});
|
||||
}
|
||||
playPlaylistByID(id, trackID = "first", noCache = false) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
addTrackToPlayNext(trackID, playNow = false) {
|
||||
this._playNextList.push(trackID);
|
||||
if (playNow) this.playNextTrack();
|
||||
}
|
||||
|
||||
sendSelfToIpcMain() {
|
||||
if (process.env.IS_ELECTRON !== true) return false;
|
||||
ipcRenderer.send("player", {
|
||||
playing: this.playing,
|
||||
likedCurrentTrack: store.state.liked.songs.includes(this.currentTrack.id),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
import store from "@/store";
|
||||
import { getAlbum } from "@/api/album";
|
||||
import { getPlaylistDetail } from "@/api/playlist";
|
||||
import { getArtist } from "@/api/artist";
|
||||
|
||||
export function playAList(list, id, type, trackID = "first") {
|
||||
let filteredList = list.map((id, index) => {
|
||||
return { sort: index, id };
|
||||
});
|
||||
store.commit("updatePlayerList", filteredList);
|
||||
if (store.state.player.shuffle) store.commit("shuffleTheListBeforePlay");
|
||||
|
||||
if (trackID === "first") store.dispatch("playFirstTrackOnList");
|
||||
else store.dispatch("playTrackOnListByID", trackID);
|
||||
|
||||
store.commit("updateListInfo", { type, id });
|
||||
}
|
||||
|
||||
export function playAlbumByID(id, trackID = "first") {
|
||||
getAlbum(id).then((data) => {
|
||||
let trackIDs = data.songs.map((t) => t.id);
|
||||
playAList(trackIDs, id, "album", trackID);
|
||||
});
|
||||
}
|
||||
|
||||
export function playPlaylistByID(id, trackID = "first", noCache = false) {
|
||||
getPlaylistDetail(id, noCache).then((data) => {
|
||||
let trackIDs = data.playlist.trackIds.map((t) => t.id);
|
||||
playAList(trackIDs, id, "playlist", trackID);
|
||||
});
|
||||
}
|
||||
|
||||
export function playArtistByID(id, trackID = "first") {
|
||||
getArtist(id).then((data) => {
|
||||
let trackIDs = data.hotSongs.map((t) => t.id);
|
||||
playAList(trackIDs, id, "artist", trackID);
|
||||
});
|
||||
}
|
||||
|
||||
export function appendTrackToPlayerList(trackID, playNext = false) {
|
||||
let filteredTrack = {
|
||||
sort: 0,
|
||||
id: trackID,
|
||||
};
|
||||
|
||||
store.commit("appendTrackToPlayerList", {
|
||||
track: filteredTrack,
|
||||
playNext,
|
||||
});
|
||||
if (playNext) {
|
||||
store.dispatch("nextTrack", true);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,8 +24,31 @@ const updateData = () => {
|
|||
localStorage.setItem("data", JSON.stringify(data));
|
||||
};
|
||||
|
||||
const updatePlayer = () => {
|
||||
let parsedData = JSON.parse(localStorage.getItem("player"));
|
||||
let appVersion = localStorage.getItem("appVersion");
|
||||
if (appVersion === `"0.2.5"`) parsedData = {}; // 0.2.6版本重构了player
|
||||
const data = {
|
||||
_repeatMode: "off",
|
||||
_shuffle: false,
|
||||
_list: [],
|
||||
_current: 0,
|
||||
_playlistSource: {},
|
||||
_volume: 1,
|
||||
_volumeBeforeMuted: 1,
|
||||
_currentTrack: {},
|
||||
_playNextList: [],
|
||||
_enabled: false,
|
||||
_shuffledList: [],
|
||||
_shuffledCurrent: 0,
|
||||
...parsedData,
|
||||
};
|
||||
localStorage.setItem("player", JSON.stringify(data));
|
||||
};
|
||||
|
||||
export default function () {
|
||||
updateSetting();
|
||||
updateData();
|
||||
updatePlayer();
|
||||
localStorage.setItem("appVersion", JSON.stringify(pkg.version));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,6 @@
|
|||
import { mapMutations, mapActions, mapState } from "vuex";
|
||||
import { getArtistAlbum } from "@/api/artist";
|
||||
import { getTrackDetail } from "@/api/track";
|
||||
import { playAlbumByID } from "@/utils/play";
|
||||
import { getAlbum, albumDynamicDetail, likeAAlbum } from "@/api/album";
|
||||
import { splitSoundtrackAlbumTitle, splitAlbumTitle } from "@/utils/common";
|
||||
import NProgress from "nprogress";
|
||||
|
|
@ -202,7 +201,7 @@ export default {
|
|||
if (this.tracks.find((t) => t.playable !== false) === undefined) {
|
||||
return;
|
||||
}
|
||||
playAlbumByID(id, trackID);
|
||||
this.$store.state.player.playAlbumByID(id, trackID);
|
||||
},
|
||||
likeAlbum(toast = false) {
|
||||
if (!isAccountLoggedIn()) {
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ import {
|
|||
artistMv,
|
||||
followAArtist,
|
||||
} from "@/api/artist";
|
||||
import { playAList } from "@/utils/play";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import NProgress from "nprogress";
|
||||
|
||||
|
|
@ -186,7 +185,12 @@ export default {
|
|||
},
|
||||
playPopularSongs(trackID = "first") {
|
||||
let trackIDs = this.popularTracks.map((t) => t.id);
|
||||
playAList(trackIDs, this.artist.id, "artist", trackID);
|
||||
this.$store.state.player.replacePlaylist(
|
||||
trackIDs,
|
||||
this.artist.id,
|
||||
"artist",
|
||||
trackID
|
||||
);
|
||||
},
|
||||
followArtist() {
|
||||
if (!isAccountLoggedIn()) {
|
||||
|
|
|
|||
|
|
@ -125,7 +125,6 @@ import {
|
|||
import { randomNum, dailyTask } from "@/utils/common";
|
||||
import { getPlaylistDetail } from "@/api/playlist";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import { playPlaylistByID } from "@/utils/play";
|
||||
import NProgress from "nprogress";
|
||||
|
||||
import TrackList from "@/components/TrackList.vue";
|
||||
|
|
@ -193,7 +192,11 @@ export default {
|
|||
...mapActions(["showToast"]),
|
||||
...mapMutations(["updateModal"]),
|
||||
playLikedSongs() {
|
||||
playPlaylistByID(this.playlists[0].id, "first", true);
|
||||
this.$store.state.player.playPlaylistByID(
|
||||
this.playlists[0].id,
|
||||
"first",
|
||||
true
|
||||
);
|
||||
},
|
||||
updateCurrentTab(tab) {
|
||||
if (!isAccountLoggedIn() && tab !== "playlists") {
|
||||
|
|
|
|||
|
|
@ -182,7 +182,6 @@ import {
|
|||
subscribePlaylist,
|
||||
deletePlaylist,
|
||||
} from "@/api/playlist";
|
||||
import { playAList } from "@/utils/play";
|
||||
import { getTrackDetail } from "@/api/track";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
|
||||
|
|
@ -335,7 +334,12 @@ export default {
|
|||
...mapActions(["playFirstTrackOnList", "playTrackOnListByID", "showToast"]),
|
||||
playPlaylistByID(trackID = "first") {
|
||||
let trackIDs = this.playlist.trackIds.map((t) => t.id);
|
||||
playAList(trackIDs, this.playlist.id, "playlist", trackID);
|
||||
this.$store.state.player.replacePlaylist(
|
||||
trackIDs,
|
||||
this.playlist.id,
|
||||
"playlist",
|
||||
trackID
|
||||
);
|
||||
},
|
||||
likePlaylist(toast = false) {
|
||||
if (!isAccountLoggedIn()) {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@
|
|||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import NProgress from "nprogress";
|
||||
import { appendTrackToPlayerList } from "@/utils/play";
|
||||
import { search } from "@/api/others";
|
||||
|
||||
import Cover from "@/components/Cover.vue";
|
||||
|
|
@ -148,7 +147,7 @@ export default {
|
|||
},
|
||||
playTrackInSearchResult(id) {
|
||||
let track = this.tracks.find((t) => t.id === id);
|
||||
appendTrackToPlayerList(track, true);
|
||||
this.$store.state.player.appendTrackToPlayerList(track, true);
|
||||
},
|
||||
getData(keywords) {
|
||||
search({ keywords: keywords, type: 1018 }).then((data) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue