mirror of
https://github.com/qier222/YesPlayMusic.git
synced 2025-03-27 17:58:56 +08:00
refactor: library page
This commit is contained in:
parent
b537081f2a
commit
603e39f362
72
src/App.vue
72
src/App.vue
@ -20,18 +20,18 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalAddTrackToPlaylist from "./components/ModalAddTrackToPlaylist.vue";
|
||||
import ModalNewPlaylist from "./components/ModalNewPlaylist.vue";
|
||||
import Navbar from "./components/Navbar.vue";
|
||||
import Player from "./components/Player.vue";
|
||||
import Toast from "./components/Toast.vue";
|
||||
import { ipcRenderer } from "./electron/ipcRenderer";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import Lyrics from "./views/lyrics.vue";
|
||||
import { mapState } from "vuex";
|
||||
import ModalAddTrackToPlaylist from './components/ModalAddTrackToPlaylist.vue';
|
||||
import ModalNewPlaylist from './components/ModalNewPlaylist.vue';
|
||||
import Navbar from './components/Navbar.vue';
|
||||
import Player from './components/Player.vue';
|
||||
import Toast from './components/Toast.vue';
|
||||
import { ipcRenderer } from './electron/ipcRenderer';
|
||||
import { isAccountLoggedIn, isLooseLoggedIn } from '@/utils/auth';
|
||||
import Lyrics from './views/lyrics.vue';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
name: 'App',
|
||||
components: {
|
||||
Navbar,
|
||||
Player,
|
||||
@ -46,50 +46,60 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["showLyrics", "showLibraryDefault", "player"]),
|
||||
...mapState(['showLyrics', 'showLibraryDefault', 'player']),
|
||||
isAccountLoggedIn() {
|
||||
return isAccountLoggedIn();
|
||||
},
|
||||
showPlayer() {
|
||||
return (
|
||||
[
|
||||
"mv",
|
||||
"loginUsername",
|
||||
"login",
|
||||
"loginAccount",
|
||||
"lastfmCallback",
|
||||
'mv',
|
||||
'loginUsername',
|
||||
'login',
|
||||
'loginAccount',
|
||||
'lastfmCallback',
|
||||
].includes(this.$route.name) === false
|
||||
);
|
||||
},
|
||||
enablePlayer() {
|
||||
return this.player.enabled && this.$route.name !== "lastfmCallback";
|
||||
return this.player.enabled && this.$route.name !== 'lastfmCallback';
|
||||
},
|
||||
showNavbar() {
|
||||
return this.$route.name !== "lastfmCallback";
|
||||
return this.$route.name !== 'lastfmCallback';
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.showLibraryDefault && this.$router.push("/library");
|
||||
if (this.isElectron) {
|
||||
ipcRenderer(this);
|
||||
}
|
||||
window.addEventListener("keydown", this.handleKeydown);
|
||||
this.showLibraryDefault && this.$router.push('/library');
|
||||
if (this.isElectron) ipcRenderer(this);
|
||||
window.addEventListener('keydown', this.handleKeydown);
|
||||
this.fetchData();
|
||||
},
|
||||
methods: {
|
||||
handleKeydown(e) {
|
||||
if (e.code === "Space") {
|
||||
if (e.target.tagName === "INPUT") return false;
|
||||
if (this.$route.name === "mv") return false;
|
||||
if (e.code === 'Space') {
|
||||
if (e.target.tagName === 'INPUT') return false;
|
||||
if (this.$route.name === 'mv') return false;
|
||||
e.preventDefault();
|
||||
this.player.play();
|
||||
}
|
||||
},
|
||||
fetchData() {
|
||||
if (!isLooseLoggedIn()) return;
|
||||
this.$store.dispatch('fetchLikedSongs');
|
||||
this.$store.dispatch('fetchLikedSongsWithDetails');
|
||||
this.$store.dispatch('fetchLikedPlaylist');
|
||||
if (isAccountLoggedIn()) {
|
||||
this.$store.dispatch('fetchLikedAlbums');
|
||||
this.$store.dispatch('fetchLikedArtists');
|
||||
this.$store.dispatch('fetchLikedMVs');
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import url("https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,500;0,600;0,700;0,800;0,900;1,500;1,600;1,700;1,800;1,900&display=swap");
|
||||
@import url('https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,500;0,600;0,700;0,800;0,900;1,500;1,600;1,700;1,800;1,900&display=swap');
|
||||
|
||||
:root {
|
||||
--color-body-bg: #ffffff;
|
||||
@ -103,7 +113,7 @@ export default {
|
||||
--color-secondary-bg-for-transparent: rgba(209, 209, 214, 0.28);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
[data-theme='dark'] {
|
||||
--color-body-bg: #222222;
|
||||
--color-text: #ffffff;
|
||||
--color-primary: #335eea;
|
||||
@ -121,7 +131,7 @@ export default {
|
||||
}
|
||||
#app,
|
||||
input {
|
||||
font-family: "Barlow", -apple-system, BlinkMacSystemFont, Helvetica Neue,
|
||||
font-family: 'Barlow', -apple-system, BlinkMacSystemFont, Helvetica Neue,
|
||||
PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC,
|
||||
WenQuanYi Micro Hei, sans-serif;
|
||||
}
|
||||
@ -195,7 +205,7 @@ main::-webkit-scrollbar {
|
||||
background: rgba(128, 128, 128, 0.38);
|
||||
}
|
||||
|
||||
[data-theme="dark"] ::-webkit-scrollbar-thumb {
|
||||
[data-theme='dark'] ::-webkit-scrollbar-thumb {
|
||||
background: var(--color-secondary-bg);
|
||||
}
|
||||
|
||||
@ -207,7 +217,7 @@ main::-webkit-scrollbar {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
[data-electron="yes"] {
|
||||
[data-electron='yes'] {
|
||||
button,
|
||||
.navigation-links a,
|
||||
.playlist-info .description {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import request from "@/utils/request";
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 获取用户详情
|
||||
@ -8,10 +8,11 @@ import request from "@/utils/request";
|
||||
*/
|
||||
export function userDetail(uid) {
|
||||
return request({
|
||||
url: "/user/detail",
|
||||
method: "get",
|
||||
url: '/user/detail',
|
||||
method: 'get',
|
||||
params: {
|
||||
uid,
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -22,8 +23,8 @@ export function userDetail(uid) {
|
||||
*/
|
||||
export function userAccount() {
|
||||
return request({
|
||||
url: "/user/account",
|
||||
method: "get",
|
||||
url: '/user/account',
|
||||
method: 'get',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
@ -43,8 +44,8 @@ export function userAccount() {
|
||||
*/
|
||||
export function userPlaylist(params) {
|
||||
return request({
|
||||
url: "/user/playlist",
|
||||
method: "get",
|
||||
url: '/user/playlist',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
@ -57,8 +58,8 @@ export function userPlaylist(params) {
|
||||
*/
|
||||
export function userLikedSongsIDs(uid) {
|
||||
return request({
|
||||
url: "/likelist",
|
||||
method: "get",
|
||||
url: '/likelist',
|
||||
method: 'get',
|
||||
params: {
|
||||
uid,
|
||||
timestamp: new Date().getTime(),
|
||||
@ -74,8 +75,8 @@ export function userLikedSongsIDs(uid) {
|
||||
*/
|
||||
export function dailySignin(type = 0) {
|
||||
return request({
|
||||
url: "/daily_signin",
|
||||
method: "post",
|
||||
url: '/daily_signin',
|
||||
method: 'post',
|
||||
params: {
|
||||
type,
|
||||
timestamp: new Date().getTime(),
|
||||
@ -94,8 +95,8 @@ export function dailySignin(type = 0) {
|
||||
*/
|
||||
export function likedAlbums() {
|
||||
return request({
|
||||
url: "/album/sublist",
|
||||
method: "get",
|
||||
url: '/album/sublist',
|
||||
method: 'get',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
@ -108,8 +109,8 @@ export function likedAlbums() {
|
||||
*/
|
||||
export function likedArtists() {
|
||||
return request({
|
||||
url: "/artist/sublist",
|
||||
method: "get",
|
||||
url: '/artist/sublist',
|
||||
method: 'get',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
@ -122,8 +123,8 @@ export function likedArtists() {
|
||||
*/
|
||||
export function likedMVs() {
|
||||
return request({
|
||||
url: "/mv/sublist",
|
||||
method: "get",
|
||||
url: '/mv/sublist',
|
||||
method: 'get',
|
||||
params: {
|
||||
timestamp: new Date().getTime(),
|
||||
},
|
||||
|
@ -11,14 +11,14 @@
|
||||
<vue-slider
|
||||
v-model="player.progress"
|
||||
:min="0"
|
||||
:max="player.currentTrackDuration"
|
||||
:max="player.currentTrackDuration + 1"
|
||||
:interval="1"
|
||||
:drag-on-click="true"
|
||||
:duration="0"
|
||||
:dot-size="12"
|
||||
:height="2"
|
||||
:tooltip-formatter="formatTrackTime"
|
||||
@drag-end="player.seek"
|
||||
:lazy="true"
|
||||
></vue-slider>
|
||||
</div>
|
||||
<div class="controls">
|
||||
@ -167,20 +167,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations, mapActions } from "vuex";
|
||||
import "@/assets/css/slider.css";
|
||||
import { mapState, mapMutations, mapActions } from 'vuex';
|
||||
import '@/assets/css/slider.css';
|
||||
|
||||
import ButtonIcon from "@/components/ButtonIcon.vue";
|
||||
import VueSlider from "vue-slider-component";
|
||||
import ButtonIcon from '@/components/ButtonIcon.vue';
|
||||
import VueSlider from 'vue-slider-component';
|
||||
|
||||
export default {
|
||||
name: "Player",
|
||||
name: 'Player',
|
||||
components: {
|
||||
ButtonIcon,
|
||||
VueSlider,
|
||||
},
|
||||
computed: {
|
||||
...mapState(["player", "settings", "data"]),
|
||||
...mapState(['player', 'settings', 'data']),
|
||||
currentTrack() {
|
||||
return this.player.currentTrack;
|
||||
},
|
||||
@ -196,47 +196,47 @@ export default {
|
||||
return this.player.playing;
|
||||
},
|
||||
audioSource() {
|
||||
return this.player._howler?._src.includes("kuwo.cn")
|
||||
? "音源来自酷我音乐"
|
||||
: "";
|
||||
return this.player._howler?._src.includes('kuwo.cn')
|
||||
? '音源来自酷我音乐'
|
||||
: '';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["toggleLyrics"]),
|
||||
...mapActions(["showToast", "likeASong"]),
|
||||
...mapMutations(['toggleLyrics']),
|
||||
...mapActions(['showToast', 'likeASong']),
|
||||
goToNextTracksPage() {
|
||||
if (this.player.isPersonalFM) return;
|
||||
this.$route.name === "next"
|
||||
this.$route.name === 'next'
|
||||
? this.$router.go(-1)
|
||||
: this.$router.push({ name: "next" });
|
||||
: this.$router.push({ name: 'next' });
|
||||
},
|
||||
formatTrackTime(value) {
|
||||
if (!value) return "";
|
||||
if (!value) return '';
|
||||
let min = ~~((value / 60) % 60);
|
||||
let sec = (~~(value % 60)).toString().padStart(2, "0");
|
||||
let sec = (~~(value % 60)).toString().padStart(2, '0');
|
||||
return `${min}:${sec}`;
|
||||
},
|
||||
goToList() {
|
||||
if (this.player.playlistSource.id === this.data.likedSongPlaylistID) {
|
||||
this.$router.push({ path: "/library/liked-songs" });
|
||||
} else if (this.player.playlistSource.type === "url") {
|
||||
this.$router.push({ path: '/library/liked-songs' });
|
||||
} else if (this.player.playlistSource.type === 'url') {
|
||||
this.$router.push({ path: this.player.playlistSource.id });
|
||||
} else {
|
||||
this.$router.push({
|
||||
path:
|
||||
"/" +
|
||||
'/' +
|
||||
this.player.playlistSource.type +
|
||||
"/" +
|
||||
'/' +
|
||||
this.player.playlistSource.id,
|
||||
});
|
||||
}
|
||||
},
|
||||
goToAlbum() {
|
||||
if (this.player.currentTrack.al.id === 0) return;
|
||||
this.$router.push({ path: "/album/" + this.player.currentTrack.al.id });
|
||||
this.$router.push({ path: '/album/' + this.player.currentTrack.al.id });
|
||||
},
|
||||
goToArtist(id) {
|
||||
this.$router.push({ path: "/artist/" + id });
|
||||
this.$router.push({ path: '/artist/' + id });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -9,14 +9,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="item" @click="play">{{ $t("contextMenu.play") }}</div>
|
||||
<div class="item" @click="playNext">{{ $t("contextMenu.playNext") }}</div>
|
||||
<div class="item" @click="play">{{ $t('contextMenu.play') }}</div>
|
||||
<div class="item" @click="playNext">{{ $t('contextMenu.playNext') }}</div>
|
||||
<hr />
|
||||
<div class="item" @click="like" v-show="!isRightClickedTrackLiked">
|
||||
{{ $t("contextMenu.saveToMyLikedSongs") }}
|
||||
{{ $t('contextMenu.saveToMyLikedSongs') }}
|
||||
</div>
|
||||
<div class="item" @click="like" v-show="isRightClickedTrackLiked">
|
||||
{{ $t("contextMenu.removeFromMyLikedSongs") }}
|
||||
{{ $t('contextMenu.removeFromMyLikedSongs') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="extraContextMenuItem.includes('removeTrackFromPlaylist')"
|
||||
@ -40,16 +40,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapMutations, mapState } from "vuex";
|
||||
import { likeATrack } from "@/api/track";
|
||||
import { addOrRemoveTrackFromPlaylist } from "@/api/playlist";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import { mapActions, mapMutations, mapState } from 'vuex';
|
||||
import { likeATrack } from '@/api/track';
|
||||
import { addOrRemoveTrackFromPlaylist } from '@/api/playlist';
|
||||
import { isAccountLoggedIn } from '@/utils/auth';
|
||||
|
||||
import TrackListItem from "@/components/TrackListItem.vue";
|
||||
import ContextMenu from "@/components/ContextMenu.vue";
|
||||
import TrackListItem from '@/components/TrackListItem.vue';
|
||||
import ContextMenu from '@/components/ContextMenu.vue';
|
||||
|
||||
export default {
|
||||
name: "TrackList",
|
||||
name: 'TrackList',
|
||||
components: {
|
||||
TrackListItem,
|
||||
ContextMenu,
|
||||
@ -60,14 +60,14 @@ export default {
|
||||
id: Number,
|
||||
dbclickTrackFunc: {
|
||||
type: String,
|
||||
default: "default",
|
||||
default: 'default',
|
||||
},
|
||||
albumObject: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
artist: {
|
||||
name: "",
|
||||
name: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
@ -88,38 +88,38 @@ export default {
|
||||
},
|
||||
itemKey: {
|
||||
type: String,
|
||||
default: "id",
|
||||
default: 'id',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rightClickedTrack: {
|
||||
id: 0,
|
||||
name: "",
|
||||
ar: [{ name: "" }],
|
||||
al: { picUrl: "" },
|
||||
name: '',
|
||||
ar: [{ name: '' }],
|
||||
al: { picUrl: '' },
|
||||
},
|
||||
listStyles: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.type === "tracklist") {
|
||||
if (this.type === 'tracklist') {
|
||||
this.listStyles = {
|
||||
display: "grid",
|
||||
gap: "4px",
|
||||
display: 'grid',
|
||||
gap: '4px',
|
||||
gridTemplateColumns: `repeat(${this.columnNumber}, 1fr)`,
|
||||
};
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["liked"]),
|
||||
...mapState(['liked']),
|
||||
isRightClickedTrackLiked() {
|
||||
return this.liked.songs.includes(this.rightClickedTrack?.id);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["updateLikedSongs", "updateModal"]),
|
||||
...mapActions(["nextTrack", "showToast"]),
|
||||
...mapMutations(['updateLikedSongs', 'updateModal']),
|
||||
...mapActions(['nextTrack', 'showToast']),
|
||||
openMenu(e, track) {
|
||||
this.rightClickedTrack = track;
|
||||
this.$refs.menu.openMenu(e);
|
||||
@ -127,49 +127,49 @@ export default {
|
||||
closeMenu() {
|
||||
this.rightClickedTrack = {
|
||||
id: 0,
|
||||
name: "",
|
||||
ar: [{ name: "" }],
|
||||
al: { picUrl: "" },
|
||||
name: '',
|
||||
ar: [{ name: '' }],
|
||||
al: { picUrl: '' },
|
||||
};
|
||||
},
|
||||
playThisList(trackID) {
|
||||
if (this.dbclickTrackFunc === "default") {
|
||||
if (this.dbclickTrackFunc === 'default') {
|
||||
this.playThisListDefault(trackID);
|
||||
} else if (this.dbclickTrackFunc === "none") {
|
||||
} else if (this.dbclickTrackFunc === 'none') {
|
||||
// do nothing
|
||||
} else if (this.dbclickTrackFunc === "playTrackOnListByID") {
|
||||
} else if (this.dbclickTrackFunc === 'playTrackOnListByID') {
|
||||
this.$store.state.player.playTrackOnListByID(trackID);
|
||||
} else if (this.dbclickTrackFunc === "playPlaylistByID") {
|
||||
} else if (this.dbclickTrackFunc === 'playPlaylistByID') {
|
||||
this.$store.state.player.playPlaylistByID(this.id, trackID);
|
||||
} else if (this.dbclickTrackFunc === "playAList") {
|
||||
let trackIDs = this.tracks.map((t) => t.id);
|
||||
} else if (this.dbclickTrackFunc === 'playAList') {
|
||||
let trackIDs = this.tracks.map(t => t.id);
|
||||
this.$store.state.player.replacePlaylist(
|
||||
trackIDs,
|
||||
this.id,
|
||||
"artist",
|
||||
'artist',
|
||||
trackID
|
||||
);
|
||||
} else if (this.dbclickTrackFunc === "dailyTracks") {
|
||||
let trackIDs = this.tracks.map((t) => t.id);
|
||||
} else if (this.dbclickTrackFunc === 'dailyTracks') {
|
||||
let trackIDs = this.tracks.map(t => t.id);
|
||||
this.$store.state.player.replacePlaylist(
|
||||
trackIDs,
|
||||
"/daily/songs",
|
||||
"url",
|
||||
'/daily/songs',
|
||||
'url',
|
||||
trackID
|
||||
);
|
||||
}
|
||||
},
|
||||
playThisListDefault(trackID) {
|
||||
if (this.type === "playlist") {
|
||||
if (this.type === 'playlist') {
|
||||
this.$store.state.player.playPlaylistByID(this.id, trackID);
|
||||
} else if (this.type === "album") {
|
||||
} else if (this.type === 'album') {
|
||||
this.$store.state.player.playAlbumByID(this.id, trackID);
|
||||
} else if (this.type === "tracklist") {
|
||||
let trackIDs = this.tracks.map((t) => t.id);
|
||||
} else if (this.type === 'tracklist') {
|
||||
let trackIDs = this.tracks.map(t => t.id);
|
||||
this.$store.state.player.replacePlaylist(
|
||||
trackIDs,
|
||||
this.id,
|
||||
"artist",
|
||||
'artist',
|
||||
trackID
|
||||
);
|
||||
}
|
||||
@ -188,19 +188,19 @@ export default {
|
||||
},
|
||||
likeASong(id) {
|
||||
if (!isAccountLoggedIn()) {
|
||||
this.showToast("此操作需要登录网易云账号");
|
||||
this.showToast('此操作需要登录网易云账号');
|
||||
return;
|
||||
}
|
||||
let like = true;
|
||||
let likedSongs = this.liked.songs;
|
||||
if (likedSongs.includes(id)) like = false;
|
||||
likeATrack({ id, like }).then((data) => {
|
||||
likeATrack({ id, like }).then(data => {
|
||||
if (data.code !== 200) return;
|
||||
if (like === false) {
|
||||
this.showToast(this.$t("toast.removedFromMyLikedSongs"));
|
||||
this.updateLikedSongs(likedSongs.filter((d) => d !== id));
|
||||
this.showToast(this.$t('toast.removedFromMyLikedSongs'));
|
||||
this.updateLikedSongs(likedSongs.filter(d => d !== id));
|
||||
} else {
|
||||
this.showToast(this.$t("toast.savedToMyLikedSongs"));
|
||||
this.showToast(this.$t('toast.savedToMyLikedSongs'));
|
||||
likedSongs.push(id);
|
||||
this.updateLikedSongs(likedSongs);
|
||||
}
|
||||
@ -208,34 +208,34 @@ export default {
|
||||
},
|
||||
addTrackToPlaylist() {
|
||||
if (!isAccountLoggedIn()) {
|
||||
this.showToast("此操作需要登录网易云账号");
|
||||
this.showToast('此操作需要登录网易云账号');
|
||||
return;
|
||||
}
|
||||
this.updateModal({
|
||||
modalName: "addTrackToPlaylistModal",
|
||||
key: "show",
|
||||
modalName: 'addTrackToPlaylistModal',
|
||||
key: 'show',
|
||||
value: true,
|
||||
});
|
||||
this.updateModal({
|
||||
modalName: "addTrackToPlaylistModal",
|
||||
key: "selectedTrackID",
|
||||
modalName: 'addTrackToPlaylistModal',
|
||||
key: 'selectedTrackID',
|
||||
value: this.rightClickedTrack.id,
|
||||
});
|
||||
},
|
||||
removeTrackFromPlaylist() {
|
||||
if (!isAccountLoggedIn()) {
|
||||
this.showToast("此操作需要登录网易云账号");
|
||||
this.showToast('此操作需要登录网易云账号');
|
||||
return;
|
||||
}
|
||||
if (confirm(`确定要从歌单删除 ${this.rightClickedTrack.name}?`)) {
|
||||
let trackID = this.rightClickedTrack.id;
|
||||
addOrRemoveTrackFromPlaylist({
|
||||
op: "del",
|
||||
op: 'del',
|
||||
pid: this.id,
|
||||
tracks: trackID,
|
||||
}).then((data) => {
|
||||
}).then(data => {
|
||||
this.showToast(
|
||||
data.body.code === 200 ? "已从歌单中删除" : data.body.message
|
||||
data.body.code === 200 ? '已从歌单中删除' : data.body.message
|
||||
);
|
||||
this.$parent.removeTrack(trackID);
|
||||
});
|
||||
|
@ -1,18 +1,27 @@
|
||||
// import store, { state, dispatch, commit } from "@/store";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import { likeATrack } from "@/api/track";
|
||||
import { isAccountLoggedIn, isLooseLoggedIn } from '@/utils/auth';
|
||||
import { likeATrack } from '@/api/track';
|
||||
import { getPlaylistDetail } from '@/api/playlist';
|
||||
import { getTrackDetail } from '@/api/track';
|
||||
import {
|
||||
userPlaylist,
|
||||
userLikedSongsIDs,
|
||||
likedAlbums,
|
||||
likedArtists,
|
||||
likedMVs,
|
||||
} from '@/api/user';
|
||||
|
||||
export default {
|
||||
showToast({ state, commit }, text) {
|
||||
if (state.toast.timer !== null) {
|
||||
clearTimeout(state.toast.timer);
|
||||
commit("updateToast", { show: false, text: "", timer: null });
|
||||
commit('updateToast', { show: false, text: '', timer: null });
|
||||
}
|
||||
commit("updateToast", {
|
||||
commit('updateToast', {
|
||||
show: true,
|
||||
text,
|
||||
timer: setTimeout(() => {
|
||||
commit("updateToast", {
|
||||
commit('updateToast', {
|
||||
show: false,
|
||||
text: state.toast.text,
|
||||
timer: null,
|
||||
@ -20,23 +29,117 @@ export default {
|
||||
}, 3200),
|
||||
});
|
||||
},
|
||||
likeASong({ state, commit, dispatch }, id) {
|
||||
likeATrack({ state, commit, dispatch }, id) {
|
||||
if (!isAccountLoggedIn()) {
|
||||
dispatch("showToast", "此操作需要登录网易云账号");
|
||||
dispatch('showToast', '此操作需要登录网易云账号');
|
||||
return;
|
||||
}
|
||||
let like = true;
|
||||
if (state.liked.songs.includes(id)) like = false;
|
||||
likeATrack({ id, like }).then(() => {
|
||||
if (like === false) {
|
||||
commit(
|
||||
"updateLikedSongs",
|
||||
state.liked.songs.filter((d) => d !== id)
|
||||
);
|
||||
commit('updateLikedXXX', {
|
||||
name: 'songs',
|
||||
data: state.liked.songs.filter(d => d !== id),
|
||||
});
|
||||
} else {
|
||||
let newLikeSongs = state.liked.songs;
|
||||
newLikeSongs.push(id);
|
||||
commit("updateLikedSongs", newLikeSongs);
|
||||
commit('updateLikedXXX', {
|
||||
name: 'songs',
|
||||
data: newLikeSongs,
|
||||
});
|
||||
}
|
||||
dispatch('fetchLikedSongsWithDetails');
|
||||
});
|
||||
},
|
||||
fetchLikedSongs: ({ state, commit }) => {
|
||||
if (!isLooseLoggedIn()) return;
|
||||
console.debug('[debug][actions.js] fetchLikedSongs');
|
||||
if (isAccountLoggedIn()) {
|
||||
return userLikedSongsIDs({ uid: state.data.user.userId }).then(result => {
|
||||
if (result.ids) {
|
||||
commit('updateLikedXXX', {
|
||||
name: 'songs',
|
||||
data: result.ids,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// TODO:搜索ID登录的用户
|
||||
}
|
||||
},
|
||||
fetchLikedSongsWithDetails: ({ state, commit }) => {
|
||||
console.debug('[debug][actions.js] fetchLikedSongsWithDetails');
|
||||
return getPlaylistDetail(state.data.likedSongPlaylistID, true).then(
|
||||
result => {
|
||||
return getTrackDetail(
|
||||
result.playlist.trackIds
|
||||
.slice(0, 12)
|
||||
.map(t => t.id)
|
||||
.join(',')
|
||||
).then(result => {
|
||||
commit('updateLikedXXX', {
|
||||
name: 'songsWithDetails',
|
||||
data: result.songs,
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
fetchLikedPlaylist: ({ state, commit }) => {
|
||||
if (!isLooseLoggedIn()) return;
|
||||
console.debug('[debug][actions.js] fetchLikedPlaylist');
|
||||
if (isAccountLoggedIn()) {
|
||||
return userPlaylist({
|
||||
uid: state.data.user.userId,
|
||||
limit: 2000, // 最多只加载2000个歌单(等有用户反馈问题再修)
|
||||
timestamp: new Date().getTime(),
|
||||
}).then(result => {
|
||||
if (result.playlist) {
|
||||
commit('updateLikedXXX', {
|
||||
name: 'playlists',
|
||||
data: result.playlist,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// TODO:搜索ID登录的用户
|
||||
}
|
||||
},
|
||||
fetchLikedAlbums: ({ commit }) => {
|
||||
if (!isAccountLoggedIn()) return;
|
||||
console.debug('[debug][actions.js] fetchLikedAlbums');
|
||||
return likedAlbums({ limit: 2000 }).then(result => {
|
||||
if (result.data) {
|
||||
commit('updateLikedXXX', {
|
||||
name: 'albums',
|
||||
data: result.data,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
fetchLikedArtists: ({ commit }) => {
|
||||
if (!isAccountLoggedIn()) return;
|
||||
console.debug('[debug][actions.js] fetchLikedArtists');
|
||||
return likedArtists().then(result => {
|
||||
if (result.data) {
|
||||
commit('updateLikedXXX', {
|
||||
name: 'artists',
|
||||
data: result.data,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
fetchLikedMVs: ({ commit }) => {
|
||||
if (!isAccountLoggedIn()) return;
|
||||
console.debug('[debug][actions.js] fetchLikedMVs');
|
||||
return likedMVs().then(result => {
|
||||
if (result.data) {
|
||||
commit('updateLikedXXX', {
|
||||
name: 'mvs',
|
||||
data: result.data,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -1,7 +1,9 @@
|
||||
export default {
|
||||
updateLikedSongs(state, trackIDs) {
|
||||
state.liked.songs = trackIDs;
|
||||
state.player.sendSelfToIpcMain();
|
||||
updateLikedXXX(state, { name, data }) {
|
||||
state.liked[name] = data;
|
||||
if (name === 'songs') {
|
||||
state.player.sendSelfToIpcMain();
|
||||
}
|
||||
},
|
||||
changeLang(state, lang) {
|
||||
state.settings.lang = lang;
|
||||
@ -23,11 +25,11 @@ export default {
|
||||
},
|
||||
togglePlaylistCategory(state, name) {
|
||||
const index = state.settings.enabledPlaylistCategories.findIndex(
|
||||
(c) => c === name
|
||||
c => c === name
|
||||
);
|
||||
if (index !== -1) {
|
||||
state.settings.enabledPlaylistCategories = state.settings.enabledPlaylistCategories.filter(
|
||||
(c) => c !== name
|
||||
c => c !== name
|
||||
);
|
||||
} else {
|
||||
state.settings.enabledPlaylistCategories.push(name);
|
||||
|
@ -1,11 +1,11 @@
|
||||
import initLocalStorage from "./initLocalStorage";
|
||||
import pkg from "../../package.json";
|
||||
import updateApp from "@/utils/updateApp";
|
||||
import initLocalStorage from './initLocalStorage';
|
||||
import pkg from '../../package.json';
|
||||
import updateApp from '@/utils/updateApp';
|
||||
|
||||
if (localStorage.getItem("appVersion") === null) {
|
||||
localStorage.setItem("settings", JSON.stringify(initLocalStorage.settings));
|
||||
localStorage.setItem("data", JSON.stringify(initLocalStorage.data));
|
||||
localStorage.setItem("appVersion", pkg.version);
|
||||
if (localStorage.getItem('appVersion') === null) {
|
||||
localStorage.setItem('settings', JSON.stringify(initLocalStorage.settings));
|
||||
localStorage.setItem('data', JSON.stringify(initLocalStorage.data));
|
||||
localStorage.setItem('appVersion', pkg.version);
|
||||
}
|
||||
|
||||
updateApp();
|
||||
@ -14,6 +14,11 @@ export default {
|
||||
showLyrics: false,
|
||||
liked: {
|
||||
songs: [],
|
||||
songsWithDetails: [], // 只有前12首
|
||||
playlists: [],
|
||||
albums: [],
|
||||
artists: [],
|
||||
mvs: [],
|
||||
},
|
||||
contextMenu: {
|
||||
clickObjectID: 0,
|
||||
@ -21,7 +26,7 @@ export default {
|
||||
},
|
||||
toast: {
|
||||
show: false,
|
||||
text: "",
|
||||
text: '',
|
||||
timer: null,
|
||||
},
|
||||
modals: {
|
||||
@ -35,8 +40,8 @@ export default {
|
||||
},
|
||||
},
|
||||
dailyTracks: [],
|
||||
lastfm: JSON.parse(localStorage.getItem("lastfm")) || {},
|
||||
player: JSON.parse(localStorage.getItem("player")),
|
||||
settings: JSON.parse(localStorage.getItem("settings")),
|
||||
data: JSON.parse(localStorage.getItem("data")),
|
||||
lastfm: JSON.parse(localStorage.getItem('lastfm')) || {},
|
||||
player: JSON.parse(localStorage.getItem('player')),
|
||||
settings: JSON.parse(localStorage.getItem('settings')),
|
||||
data: JSON.parse(localStorage.getItem('data')),
|
||||
};
|
||||
|
@ -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() {
|
||||
|
@ -3,7 +3,7 @@
|
||||
<h1>
|
||||
<img class="avatar" :src="data.user.avatarUrl | resizeImage" />{{
|
||||
data.user.nickname
|
||||
}}{{ $t("library.sLibrary") }}
|
||||
}}{{ $t('library.sLibrary') }}
|
||||
</h1>
|
||||
<div class="section-one">
|
||||
<div class="liked-songs" @click="goToLikedSongsList">
|
||||
@ -11,17 +11,17 @@
|
||||
<p>
|
||||
<span
|
||||
v-for="(line, index) in pickedLyric"
|
||||
:key="`${line}${index}`"
|
||||
v-show="line !== ''"
|
||||
:key="`${line}${index}`"
|
||||
>{{ line }}<br
|
||||
/></span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="titles">
|
||||
<div class="title">{{ $t("library.likedSongs") }}</div>
|
||||
<div class="title">{{ $t('library.likedSongs') }}</div>
|
||||
<div class="sub-title">
|
||||
{{ likedSongsPlaylist.trackCount }} {{ $t("common.songs") }}
|
||||
{{ liked.songs.length }} {{ $t('common.songs') }}
|
||||
</div>
|
||||
</div>
|
||||
<button @click.stop="playLikedSongs">
|
||||
@ -31,16 +31,16 @@
|
||||
</div>
|
||||
<div class="songs">
|
||||
<TrackList
|
||||
:tracks="likedSongs"
|
||||
:type="'tracklist'"
|
||||
:id="likedSongsPlaylist.id"
|
||||
dbclickTrackFunc="playPlaylistByID"
|
||||
:columnNumber="3"
|
||||
:id="liked.playlist ? liked.playlist[0].id : 0"
|
||||
:tracks="liked.songsWithDetails"
|
||||
:column-number="3"
|
||||
type="tracklist"
|
||||
dbclick-track-func="playPlaylistByID"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-two" id="liked">
|
||||
<div id="liked" class="section-two">
|
||||
<div class="tabs-row">
|
||||
<div class="tabs">
|
||||
<div
|
||||
@ -48,157 +48,104 @@
|
||||
:class="{ active: currentTab === 'playlists' }"
|
||||
@click="updateCurrentTab('playlists')"
|
||||
>
|
||||
{{ $t("library.playlists") }}
|
||||
{{ $t('library.playlists') }}
|
||||
</div>
|
||||
<div
|
||||
class="tab"
|
||||
:class="{ active: currentTab === 'albums' }"
|
||||
@click="updateCurrentTab('albums')"
|
||||
>
|
||||
{{ $t("library.albums") }}
|
||||
{{ $t('library.albums') }}
|
||||
</div>
|
||||
<div
|
||||
class="tab"
|
||||
:class="{ active: currentTab === 'artists' }"
|
||||
@click="updateCurrentTab('artists')"
|
||||
>
|
||||
{{ $t("library.artists") }}
|
||||
{{ $t('library.artists') }}
|
||||
</div>
|
||||
<div
|
||||
class="tab"
|
||||
:class="{ active: currentTab === 'mvs' }"
|
||||
@click="updateCurrentTab('mvs')"
|
||||
>
|
||||
{{ $t("library.mvs") }}
|
||||
{{ $t('library.mvs') }}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
v-show="currentTab === 'playlists'"
|
||||
class="add-playlist"
|
||||
icon="plus"
|
||||
v-show="currentTab === 'playlists'"
|
||||
@click="openAddPlaylistModal"
|
||||
><svg-icon icon-class="plus" />{{ $t("library.newPlayList") }}</button
|
||||
><svg-icon icon-class="plus" />{{ $t('library.newPlayList') }}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div v-show="currentTab === 'playlists'">
|
||||
<div v-if="playlists.length > 1">
|
||||
<div v-if="liked.playlists.length > 1">
|
||||
<CoverRow
|
||||
:items="playlists.slice(1)"
|
||||
:items="liked.playlists.slice(1)"
|
||||
type="playlist"
|
||||
subText="creator"
|
||||
:showPlayButton="true"
|
||||
sub-text="creator"
|
||||
:show-play-button="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="currentTab === 'albums'">
|
||||
<CoverRow
|
||||
:items="albums"
|
||||
:items="liked.albums"
|
||||
type="album"
|
||||
subText="artist"
|
||||
:showPlayButton="true"
|
||||
sub-text="artist"
|
||||
:show-play-button="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-show="currentTab === 'artists'">
|
||||
<CoverRow :items="artists" type="artist" :showPlayButton="true" />
|
||||
<CoverRow
|
||||
:items="liked.artists"
|
||||
type="artist"
|
||||
:show-play-button="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-show="currentTab === 'mvs'">
|
||||
<MvRow :mvs="mvs" />
|
||||
<MvRow :mvs="liked.mvs" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapMutations, mapState } from "vuex";
|
||||
import { getTrackDetail, getLyric } from "@/api/track";
|
||||
import {
|
||||
userDetail,
|
||||
userAccount,
|
||||
userPlaylist,
|
||||
likedAlbums,
|
||||
likedArtists,
|
||||
likedMVs,
|
||||
} from "@/api/user";
|
||||
import { randomNum, dailyTask } from "@/utils/common";
|
||||
import { getPlaylistDetail } from "@/api/playlist";
|
||||
import { isAccountLoggedIn } from "@/utils/auth";
|
||||
import NProgress from "nprogress";
|
||||
import { mapActions, mapMutations, mapState } from 'vuex';
|
||||
import { getLyric } from '@/api/track';
|
||||
import { randomNum, dailyTask } from '@/utils/common';
|
||||
import { isAccountLoggedIn } from '@/utils/auth';
|
||||
import NProgress from 'nprogress';
|
||||
|
||||
import TrackList from "@/components/TrackList.vue";
|
||||
import CoverRow from "@/components/CoverRow.vue";
|
||||
import SvgIcon from "@/components/SvgIcon.vue";
|
||||
import MvRow from "@/components/MvRow.vue";
|
||||
import TrackList from '@/components/TrackList.vue';
|
||||
import CoverRow from '@/components/CoverRow.vue';
|
||||
import SvgIcon from '@/components/SvgIcon.vue';
|
||||
import MvRow from '@/components/MvRow.vue';
|
||||
|
||||
export default {
|
||||
name: "Library",
|
||||
name: 'Library',
|
||||
components: { SvgIcon, CoverRow, TrackList, MvRow },
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
playlists: [],
|
||||
hasMorePlaylists: true,
|
||||
likedSongsPlaylist: {
|
||||
id: 0,
|
||||
trackCount: 0,
|
||||
},
|
||||
likedSongs: [],
|
||||
likedSongIDs: [],
|
||||
lyric: undefined,
|
||||
currentTab: "playlists",
|
||||
albums: [],
|
||||
artists: [],
|
||||
mvs: [],
|
||||
currentTab: 'playlists',
|
||||
};
|
||||
},
|
||||
created() {
|
||||
NProgress.start();
|
||||
if (isAccountLoggedIn()) {
|
||||
userAccount().then((result) => {
|
||||
this.$store.commit("updateData", {
|
||||
key: "user",
|
||||
value: result.profile,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
userDetail(this.data.user.userId).then((result) => {
|
||||
this.$store.commit("updateData", {
|
||||
key: "user",
|
||||
value: result.profile,
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
if (!this.data.likedSongPlaylistID) {
|
||||
userPlaylist({
|
||||
uid: this.data.user.userId,
|
||||
limit: 1,
|
||||
}).then((data) => {
|
||||
this.updateData({
|
||||
key: "likedSongPlaylistID",
|
||||
value: data.playlist[0].id,
|
||||
});
|
||||
this.loadData();
|
||||
});
|
||||
} else {
|
||||
this.loadData();
|
||||
}
|
||||
dailyTask();
|
||||
},
|
||||
computed: {
|
||||
...mapState(["data"]),
|
||||
likedSongsInState() {
|
||||
return this.$store.state.liked.songs;
|
||||
},
|
||||
...mapState(['data', 'liked']),
|
||||
pickedLyric() {
|
||||
if (this.lyric === undefined) return "";
|
||||
let lyric = this.lyric.split("\n");
|
||||
lyric = lyric.filter((l) => {
|
||||
if (l.includes("作词") || l.includes("作曲")) {
|
||||
if (this.lyric === undefined) return '';
|
||||
let lyric = this.lyric.split('\n');
|
||||
lyric = lyric.filter(l => {
|
||||
if (l.includes('作词') || l.includes('作曲')) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -208,132 +155,76 @@ export default {
|
||||
lineIndex = randomNum(0, lyric.length - 1);
|
||||
}
|
||||
return [
|
||||
lyric[lineIndex].split("]")[1],
|
||||
lyric[lineIndex + 1].split("]")[1],
|
||||
lyric[lineIndex + 2].split("]")[1],
|
||||
lyric[lineIndex].split(']')[1],
|
||||
lyric[lineIndex + 1].split(']')[1],
|
||||
lyric[lineIndex + 2].split(']')[1],
|
||||
];
|
||||
},
|
||||
},
|
||||
created() {
|
||||
NProgress.start();
|
||||
},
|
||||
activated() {
|
||||
if (this.liked.songsWithDetails.length > 0) {
|
||||
NProgress.done();
|
||||
this.show = true;
|
||||
this.getRandomLyric();
|
||||
} else {
|
||||
this.$store.dispatch('fetchLikedSongsWithDetails').then(() => {
|
||||
NProgress.done();
|
||||
this.show = true;
|
||||
this.getRandomLyric();
|
||||
});
|
||||
}
|
||||
this.$store.dispatch('fetchLikedSongs');
|
||||
this.$store.dispatch('fetchLikedPlaylist');
|
||||
this.$store.dispatch('fetchLikedAlbums');
|
||||
this.$store.dispatch('fetchLikedArtists');
|
||||
this.$store.dispatch('fetchLikedMVs');
|
||||
dailyTask();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(["showToast"]),
|
||||
...mapMutations(["updateModal", "updateData"]),
|
||||
...mapActions(['showToast']),
|
||||
...mapMutations(['updateModal', 'updateData']),
|
||||
playLikedSongs() {
|
||||
this.$store.state.player.playPlaylistByID(
|
||||
this.playlists[0].id,
|
||||
"first",
|
||||
this.liked.playlists[0].id,
|
||||
'first',
|
||||
true
|
||||
);
|
||||
},
|
||||
updateCurrentTab(tab) {
|
||||
if (!isAccountLoggedIn() && tab !== "playlists") {
|
||||
this.showToast("此操作需要登录网易云账号");
|
||||
if (!isAccountLoggedIn() && tab !== 'playlists') {
|
||||
this.showToast('此操作需要登录网易云账号');
|
||||
return;
|
||||
}
|
||||
this.currentTab = tab;
|
||||
document
|
||||
.getElementById("liked")
|
||||
.scrollIntoView({ block: "start", behavior: "smooth" });
|
||||
if (tab === "albums") {
|
||||
if (this.albums.length === 0) this.loadLikedAlbums();
|
||||
} else if (tab === "artists") {
|
||||
if (this.artists.length === 0) this.loadLikedArtists();
|
||||
} else if (tab === "mvs") {
|
||||
if (this.mvs.length === 0) this.loadLikedMVs();
|
||||
}
|
||||
.getElementById('liked')
|
||||
.scrollIntoView({ block: 'start', behavior: 'smooth' });
|
||||
},
|
||||
goToLikedSongsList() {
|
||||
this.$router.push({ path: "/library/liked-songs" });
|
||||
},
|
||||
loadData() {
|
||||
if (this.hasMorePlaylists && this.currentTab === "playlists") {
|
||||
this.getUserPlaylists();
|
||||
}
|
||||
if (this.currentTab === "albums") {
|
||||
this.loadLikedAlbums();
|
||||
} else if (this.currentTab === "artists") {
|
||||
this.loadLikedArtists();
|
||||
} else if (this.currentTab === "mvs") {
|
||||
this.loadLikedMVs();
|
||||
}
|
||||
this.getLikedSongs();
|
||||
},
|
||||
getUserPlaylists(replace = false) {
|
||||
userPlaylist({
|
||||
uid: this.data.user.userId,
|
||||
offset: this.playlists.length === 0 ? 0 : this.playlists.length - 1,
|
||||
timestamp: new Date().getTime(),
|
||||
}).then((data) => {
|
||||
if (replace) {
|
||||
this.playlists = data.playlist;
|
||||
} else {
|
||||
this.playlists.push(...data.playlist);
|
||||
}
|
||||
this.hasMorePlaylists = data.more;
|
||||
});
|
||||
},
|
||||
getLikedSongs(getLyric = true) {
|
||||
getPlaylistDetail(this.data.likedSongPlaylistID, true).then((data) => {
|
||||
this.likedSongsPlaylist = data.playlist;
|
||||
if (data.playlist.trackIds.length === 0) {
|
||||
NProgress.done();
|
||||
this.show = true;
|
||||
return;
|
||||
}
|
||||
let TrackIDs = data.playlist.trackIds.slice(0, 12).map((t) => t.id);
|
||||
this.likedSongIDs = TrackIDs;
|
||||
getTrackDetail(this.likedSongIDs.join(",")).then((data) => {
|
||||
this.likedSongs = data.songs;
|
||||
NProgress.done();
|
||||
this.show = true;
|
||||
});
|
||||
if (getLyric) this.getRandomLyric();
|
||||
});
|
||||
this.$router.push({ path: '/library/liked-songs' });
|
||||
},
|
||||
getRandomLyric() {
|
||||
getLyric(
|
||||
this.likedSongIDs[randomNum(0, this.likedSongIDs.length - 1)]
|
||||
).then((data) => {
|
||||
this.liked.songs[randomNum(0, this.liked.songs.length - 1)]
|
||||
).then(data => {
|
||||
if (data.lrc !== undefined) this.lyric = data.lrc.lyric;
|
||||
});
|
||||
},
|
||||
loadLikedAlbums() {
|
||||
NProgress.start();
|
||||
likedAlbums().then((data) => {
|
||||
this.albums = data.data;
|
||||
NProgress.done();
|
||||
});
|
||||
},
|
||||
loadLikedArtists() {
|
||||
NProgress.start();
|
||||
likedArtists().then((data) => {
|
||||
this.artists = data.data;
|
||||
NProgress.done();
|
||||
});
|
||||
},
|
||||
loadLikedMVs() {
|
||||
NProgress.start();
|
||||
likedMVs().then((data) => {
|
||||
this.mvs = data.data;
|
||||
NProgress.done();
|
||||
});
|
||||
},
|
||||
openAddPlaylistModal() {
|
||||
if (!isAccountLoggedIn()) {
|
||||
this.showToast("此操作需要登录网易云账号");
|
||||
this.showToast('此操作需要登录网易云账号');
|
||||
return;
|
||||
}
|
||||
this.updateModal({
|
||||
modalName: "newPlaylistModal",
|
||||
key: "show",
|
||||
modalName: 'newPlaylistModal',
|
||||
key: 'show',
|
||||
value: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
likedSongsInState() {
|
||||
this.getLikedSongs(false);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -69,19 +69,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<span>{{ formatTrackTime(player.progress) || "0:00" }}</span>
|
||||
<span>{{ formatTrackTime(player.progress) || '0:00' }}</span>
|
||||
<div class="slider">
|
||||
<vue-slider
|
||||
v-model="player.progress"
|
||||
:min="0"
|
||||
:max="player.currentTrackDuration"
|
||||
:max="player.currentTrackDuration + 1"
|
||||
:interval="1"
|
||||
:drag-on-click="true"
|
||||
:duration="0"
|
||||
:dot-size="12"
|
||||
:height="2"
|
||||
:tooltip-formatter="formatTrackTime"
|
||||
@drag-end="player.seek"
|
||||
:lazy="true"
|
||||
></vue-slider>
|
||||
</div>
|
||||
<span>{{ formatTrackTime(player.currentTrackDuration) }}</span>
|
||||
@ -184,15 +184,15 @@
|
||||
// The lyrics page of Apple Music is so gorgeous, so I copy the design.
|
||||
// Some of the codes are from https://github.com/sl1673495/vue-netease-music
|
||||
|
||||
import { mapState, mapMutations, mapActions } from "vuex";
|
||||
import VueSlider from "vue-slider-component";
|
||||
import { formatTrackTime } from "@/utils/common";
|
||||
import { getLyric } from "@/api/track";
|
||||
import { lyricParser } from "@/utils/lyrics";
|
||||
import ButtonIcon from "@/components/ButtonIcon.vue";
|
||||
import { mapState, mapMutations, mapActions } from 'vuex';
|
||||
import VueSlider from 'vue-slider-component';
|
||||
import { formatTrackTime } from '@/utils/common';
|
||||
import { getLyric } from '@/api/track';
|
||||
import { lyricParser } from '@/utils/lyrics';
|
||||
import ButtonIcon from '@/components/ButtonIcon.vue';
|
||||
|
||||
export default {
|
||||
name: "Lyrics",
|
||||
name: 'Lyrics',
|
||||
components: {
|
||||
VueSlider,
|
||||
ButtonIcon,
|
||||
@ -207,12 +207,12 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["player", "settings", "showLyrics"]),
|
||||
...mapState(['player', 'settings', 'showLyrics']),
|
||||
currentTrack() {
|
||||
return this.player.currentTrack;
|
||||
},
|
||||
imageUrl() {
|
||||
return this.player.currentTrack?.al?.picUrl + "?param=1024y1024";
|
||||
return this.player.currentTrack?.al?.picUrl + '?param=1024y1024';
|
||||
},
|
||||
lyricWithTranslation() {
|
||||
let ret = [];
|
||||
@ -222,7 +222,7 @@ export default {
|
||||
);
|
||||
// content统一转换数组形式
|
||||
if (lyricFiltered.length) {
|
||||
lyricFiltered.forEach((l) => {
|
||||
lyricFiltered.forEach(l => {
|
||||
const { rawTime, time, content } = l;
|
||||
const lyricItem = { time, content, contents: [content] };
|
||||
const sameTimeTLyric = this.tlyric.find(
|
||||
@ -259,10 +259,10 @@ export default {
|
||||
artist() {
|
||||
return this.currentTrack?.ar
|
||||
? this.currentTrack.ar[0]
|
||||
: { id: 0, name: "unknown" };
|
||||
: { id: 0, name: 'unknown' };
|
||||
},
|
||||
album() {
|
||||
return this.currentTrack?.al || { id: 0, name: "unknown" };
|
||||
return this.currentTrack?.al || { id: 0, name: 'unknown' };
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
@ -284,11 +284,11 @@ export default {
|
||||
clearInterval(this.lyricsInterval);
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["toggleLyrics"]),
|
||||
...mapActions(["likeASong"]),
|
||||
...mapMutations(['toggleLyrics']),
|
||||
...mapActions(['likeASong']),
|
||||
getLyric() {
|
||||
if (!this.currentTrack.id) return;
|
||||
return getLyric(this.currentTrack.id).then((data) => {
|
||||
return getLyric(this.currentTrack.id).then(data => {
|
||||
if (!data?.lrc?.lyric) {
|
||||
this.lyric = [];
|
||||
this.tlyric = [];
|
||||
@ -327,8 +327,8 @@ export default {
|
||||
const el = document.getElementById(`line${this.highlightLyricIndex}`);
|
||||
if (el)
|
||||
el.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
});
|
||||
}
|
||||
}, 50);
|
||||
@ -341,7 +341,7 @@ export default {
|
||||
} else if (line.contents[0] !== undefined) {
|
||||
return `<span>${line.contents[0]}</span>`;
|
||||
}
|
||||
return "unknown";
|
||||
return 'unknown';
|
||||
},
|
||||
moveToFMTrash() {
|
||||
this.player.moveToFMTrash();
|
||||
@ -367,7 +367,7 @@ export default {
|
||||
--brightness-dynamic-background: 150%;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .dynamic-background {
|
||||
[data-theme='dark'] .dynamic-background {
|
||||
--contrast-dynamic-background: 125%;
|
||||
--brightness-dynamic-background: 50%;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user