chore: format codes

master
qier222 4 years ago
parent 6922c716e2
commit 9351f6bc89
No known key found for this signature in database
GPG Key ID: 9C85007ED905F14D

@ -1,15 +1,15 @@
// node module // node module
const fs = require("fs"); const fs = require('fs');
const https = require("https"); const https = require('https');
const resolve = require("path").resolve; const resolve = require('path').resolve;
const join = require("path").resolve; const join = require('path').resolve;
const extract = require("extract-zip"); const extract = require('extract-zip');
// 函数参数 // 函数参数
const dest = resolve(__dirname, "../"); const dest = resolve(__dirname, '../');
const fileName = "NeteaseCloudMusicApi-master.zip"; const fileName = 'NeteaseCloudMusicApi-master.zip';
const options = { const options = {
hostname: "github.91chifun.workers.dev", hostname: 'github.91chifun.workers.dev',
path: `//https://github.com/Binaryify/NeteaseCloudMusicApi/archive/master.zip`, path: `//https://github.com/Binaryify/NeteaseCloudMusicApi/archive/master.zip`,
}; };
@ -27,13 +27,13 @@ function fix2(number) {
async function download(options, fileName, callback) { async function download(options, fileName, callback) {
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
const destPath = join(__dirname, "../" + fileName); const destPath = join(__dirname, '../' + fileName);
// Check if exist // Check if exist
if (fs.existsSync(destPath)) return resolve(destPath); if (fs.existsSync(destPath)) return resolve(destPath);
const file = fs.createWriteStream(destPath); const file = fs.createWriteStream(destPath);
const request = https.get(options, (res) => { const request = https.get(options, res => {
let len = res.headers && parseInt(res.headers["content-length"], 10); let len = res.headers && parseInt(res.headers['content-length'], 10);
let cur = 0; let cur = 0;
// 1048576 - bytes in 1Megabyte // 1048576 - bytes in 1Megabyte
const MEGA = 1048576; const MEGA = 1048576;
@ -43,10 +43,10 @@ async function download(options, fileName, callback) {
} }
if (!len) { if (!len) {
console.log( console.log(
"Downloading, but can not get content-length, please be patient." 'Downloading, but can not get content-length, please be patient.'
); );
} }
res.on("data", (chunk) => { res.on('data', chunk => {
if (len) { if (len) {
cur += chunk.length; cur += chunk.length;
console.log( console.log(
@ -56,22 +56,22 @@ async function download(options, fileName, callback) {
); );
} }
}); });
res.on("end", () => { res.on('end', () => {
callback("Downloading complete!"); callback('Downloading complete!');
}); });
res.pipe(file); res.pipe(file);
file.on("finish", () => { file.on('finish', () => {
file.close(() => { file.close(() => {
callback("File wrote complete!"); callback('File wrote complete!');
resolve(destPath); resolve(destPath);
}); });
}); });
file.on("error", (err) => { file.on('error', err => {
fs.unlink(destPath); fs.unlink(destPath);
reject(err); reject(err);
}); });
request.on("error", (err) => { request.on('error', err => {
console.log("Error: " + err.message); console.log('Error: ' + err.message);
}); });
}); });
}); });
@ -82,21 +82,21 @@ async function unzip(source, target) {
await extract(source, { await extract(source, {
dir: target, dir: target,
}); });
console.log("Extraction complete"); console.log('Extraction complete');
return true; return true;
} catch (err) { } catch (err) {
// handle any errors // handle any errors
if (err.message === "end of central directory record signature not found") { if (err.message === 'end of central directory record signature not found') {
console.log("Not a full_downloaded zip file, removed!"); console.log('Not a full_downloaded zip file, removed!');
fs.unlinkSync(source); fs.unlinkSync(source);
} }
return false; return false;
} }
} }
// Download process // Download process
download(options, fileName, (text) => { download(options, fileName, text => {
console.log(text); console.log(text);
}).then((path) => { }).then(path => {
console.log(path); console.log(path);
// Unzip process // Unzip process
return unzip(path, dest); return unzip(path, dest);

@ -1,5 +1,5 @@
import request from "@/utils/request"; import request from '@/utils/request';
import { mapTrackPlayableStatus } from "@/utils/common"; import { mapTrackPlayableStatus } from '@/utils/common';
/** /**
* 获取专辑内容 * 获取专辑内容
@ -8,12 +8,12 @@ import { mapTrackPlayableStatus } from "@/utils/common";
*/ */
export function getAlbum(id) { export function getAlbum(id) {
return request({ return request({
url: "/album", url: '/album',
method: "get", method: 'get',
params: { params: {
id, id,
}, },
}).then((data) => { }).then(data => {
data.songs = mapTrackPlayableStatus(data.songs); data.songs = mapTrackPlayableStatus(data.songs);
return data; return data;
}); });
@ -32,8 +32,8 @@ export function getAlbum(id) {
*/ */
export function newAlbums(params) { export function newAlbums(params) {
return request({ return request({
url: "/album/new", url: '/album/new',
method: "get", method: 'get',
params, params,
}); });
} }
@ -46,8 +46,8 @@ export function newAlbums(params) {
*/ */
export function albumDynamicDetail(id) { export function albumDynamicDetail(id) {
return request({ return request({
url: "/album/detail/dynamic", url: '/album/detail/dynamic',
method: "get", method: 'get',
params: { id, timestamp: new Date().getTime() }, params: { id, timestamp: new Date().getTime() },
}); });
} }
@ -63,8 +63,8 @@ export function albumDynamicDetail(id) {
*/ */
export function likeAAlbum(params) { export function likeAAlbum(params) {
return request({ return request({
url: "/album/sub", url: '/album/sub',
method: "post", method: 'post',
params, params,
}); });
} }

@ -1,5 +1,5 @@
import request from "@/utils/request"; import request from '@/utils/request';
import { mapTrackPlayableStatus } from "@/utils/common"; import { mapTrackPlayableStatus } from '@/utils/common';
/** /**
* 获取歌手单曲 * 获取歌手单曲
@ -8,13 +8,13 @@ import { mapTrackPlayableStatus } from "@/utils/common";
*/ */
export function getArtist(id) { export function getArtist(id) {
return request({ return request({
url: "/artists", url: '/artists',
method: "get", method: 'get',
params: { params: {
id, id,
timestamp: new Date().getTime(), timestamp: new Date().getTime(),
}, },
}).then((data) => { }).then(data => {
data.hotSongs = mapTrackPlayableStatus(data.hotSongs); data.hotSongs = mapTrackPlayableStatus(data.hotSongs);
return data; return data;
}); });
@ -33,8 +33,8 @@ export function getArtist(id) {
*/ */
export function getArtistAlbum(params) { export function getArtistAlbum(params) {
return request({ return request({
url: "/artist/album", url: '/artist/album',
method: "get", method: 'get',
params, params,
}); });
} }
@ -51,8 +51,8 @@ export function getArtistAlbum(params) {
*/ */
export function toplistOfArtists(type = null) { export function toplistOfArtists(type = null) {
return request({ return request({
url: "/toplist/artist", url: '/toplist/artist',
method: "get", method: 'get',
params: { params: {
type, type,
}, },
@ -67,8 +67,8 @@ export function toplistOfArtists(type = null) {
*/ */
export function artistMv(params) { export function artistMv(params) {
return request({ return request({
url: "/artist/mv", url: '/artist/mv',
method: "get", method: 'get',
params, params,
}); });
} }
@ -84,8 +84,8 @@ export function artistMv(params) {
*/ */
export function followAArtist(params) { export function followAArtist(params) {
return request({ return request({
url: "/artist/sub", url: '/artist/sub',
method: "post", method: 'post',
params, params,
}); });
} }
@ -98,8 +98,8 @@ export function followAArtist(params) {
*/ */
export function similarArtists(id) { export function similarArtists(id) {
return request({ return request({
url: "/simi/artist", url: '/simi/artist',
method: "post", method: 'post',
params: { id }, params: { id },
}); });
} }

@ -1,4 +1,4 @@
import request from "@/utils/request"; import request from '@/utils/request';
/** /**
* 手机登录 * 手机登录
@ -14,8 +14,8 @@ import request from "@/utils/request";
*/ */
export function loginWithPhone(params) { export function loginWithPhone(params) {
return request({ return request({
url: "/login/cellphone", url: '/login/cellphone',
method: "post", method: 'post',
params, params,
}); });
} }
@ -31,8 +31,8 @@ export function loginWithPhone(params) {
*/ */
export function loginWithEmail(params) { export function loginWithEmail(params) {
return request({ return request({
url: "/login", url: '/login',
method: "post", method: 'post',
params, params,
}); });
} }
@ -44,8 +44,8 @@ export function loginWithEmail(params) {
*/ */
export function refreshCookie() { export function refreshCookie() {
return request({ return request({
url: "/login/refresh", url: '/login/refresh',
method: "post", method: 'post',
}); });
} }
@ -55,7 +55,7 @@ export function refreshCookie() {
*/ */
export function logout() { export function logout() {
return request({ return request({
url: "/logout", url: '/logout',
method: "post", method: 'post',
}); });
} }

@ -1,20 +1,20 @@
// Last.fm API documents 👉 https://www.last.fm/api // Last.fm API documents 👉 https://www.last.fm/api
import axios from "axios"; import axios from 'axios';
import md5 from "crypto-js/md5"; import md5 from 'crypto-js/md5';
const apiKey = process.env.VUE_APP_LASTFM_API_KEY; const apiKey = process.env.VUE_APP_LASTFM_API_KEY;
const apiSharedSecret = process.env.VUE_APP_LASTFM_API_SHARED_SECRET; const apiSharedSecret = process.env.VUE_APP_LASTFM_API_SHARED_SECRET;
const baseUrl = window.location.origin; const baseUrl = window.location.origin;
const url = "https://ws.audioscrobbler.com/2.0/"; const url = 'https://ws.audioscrobbler.com/2.0/';
const sign = (params) => { const sign = params => {
const sortParamsKeys = Object.keys(params).sort(); const sortParamsKeys = Object.keys(params).sort();
const sortedParams = sortParamsKeys.reduce((acc, key) => { const sortedParams = sortParamsKeys.reduce((acc, key) => {
acc[key] = params[key]; acc[key] = params[key];
return acc; return acc;
}, {}); }, {});
let signature = ""; let signature = '';
for (const [key, value] of Object.entries(sortedParams)) { for (const [key, value] of Object.entries(sortedParams)) {
signature += `${key}${value}`; signature += `${key}${value}`;
} }
@ -33,10 +33,10 @@ export function authGetSession(token) {
).toString(); ).toString();
return axios({ return axios({
url, url,
method: "GET", method: 'GET',
params: { params: {
method: "auth.getSession", method: 'auth.getSession',
format: "json", format: 'json',
api_key: apiKey, api_key: apiKey,
api_sig: signature, api_sig: signature,
token, token,
@ -46,34 +46,34 @@ export function authGetSession(token) {
export function trackUpdateNowPlaying(params) { export function trackUpdateNowPlaying(params) {
params.api_key = apiKey; params.api_key = apiKey;
params.method = "track.updateNowPlaying"; params.method = 'track.updateNowPlaying';
params.sk = JSON.parse(localStorage.getItem("lastfm"))["key"]; params.sk = JSON.parse(localStorage.getItem('lastfm'))['key'];
const signature = sign(params); const signature = sign(params);
return axios({ return axios({
url, url,
method: "POST", method: 'POST',
params: { params: {
...params, ...params,
api_sig: signature, api_sig: signature,
format: "json", format: 'json',
}, },
}); });
} }
export function trackScrobble(params) { export function trackScrobble(params) {
params.api_key = apiKey; params.api_key = apiKey;
params.method = "track.scrobble"; params.method = 'track.scrobble';
params.sk = JSON.parse(localStorage.getItem("lastfm"))["key"]; params.sk = JSON.parse(localStorage.getItem('lastfm'))['key'];
const signature = sign(params); const signature = sign(params);
return axios({ return axios({
url, url,
method: "POST", method: 'POST',
params: { params: {
...params, ...params,
api_sig: signature, api_sig: signature,
format: "json", format: 'json',
}, },
}); });
} }

@ -1,4 +1,4 @@
import request from "@/utils/request"; import request from '@/utils/request';
/** /**
* 获取 mv 数据 * 获取 mv 数据
@ -9,8 +9,8 @@ import request from "@/utils/request";
*/ */
export function mvDetail(mvid) { export function mvDetail(mvid) {
return request({ return request({
url: "/mv/detail", url: '/mv/detail',
method: "get", method: 'get',
params: { params: {
mvid, mvid,
timestamp: new Date().getTime(), timestamp: new Date().getTime(),
@ -30,8 +30,8 @@ export function mvDetail(mvid) {
*/ */
export function mvUrl(params) { export function mvUrl(params) {
return request({ return request({
url: "/mv/url", url: '/mv/url',
method: "get", method: 'get',
params, params,
}); });
} }
@ -43,8 +43,8 @@ export function mvUrl(params) {
*/ */
export function simiMv(mvid) { export function simiMv(mvid) {
return request({ return request({
url: "/simi/mv", url: '/simi/mv',
method: "get", method: 'get',
params: { mvid }, params: { mvid },
}); });
} }
@ -62,8 +62,8 @@ export function simiMv(mvid) {
export function likeAMV(params) { export function likeAMV(params) {
params.timestamp = new Date().getTime(); params.timestamp = new Date().getTime();
return request({ return request({
url: "/mv/sub", url: '/mv/sub',
method: "post", method: 'post',
params, params,
}); });
} }

@ -1,5 +1,5 @@
import request from "@/utils/request"; import request from '@/utils/request';
import { mapTrackPlayableStatus } from "@/utils/common"; import { mapTrackPlayableStatus } from '@/utils/common';
/** /**
* 搜索 * 搜索
@ -18,10 +18,10 @@ import { mapTrackPlayableStatus } from "@/utils/common";
*/ */
export function search(params) { export function search(params) {
return request({ return request({
url: "/search", url: '/search',
method: "get", method: 'get',
params, params,
}).then((data) => { }).then(data => {
if (data.result?.song !== undefined) if (data.result?.song !== undefined)
data.result.song.songs = mapTrackPlayableStatus(data.result.song.songs); data.result.song.songs = mapTrackPlayableStatus(data.result.song.songs);
return data; return data;
@ -30,8 +30,8 @@ export function search(params) {
export function personalFM() { export function personalFM() {
return request({ return request({
url: "/personal_fm", url: '/personal_fm',
method: "get", method: 'get',
params: { params: {
timestamp: new Date().getTime(), timestamp: new Date().getTime(),
}, },
@ -40,8 +40,8 @@ export function personalFM() {
export function fmTrash(id) { export function fmTrash(id) {
return request({ return request({
url: "/fm_trash", url: '/fm_trash',
method: "post", method: 'post',
params: { params: {
timestamp: new Date().getTime(), timestamp: new Date().getTime(),
id, id,

@ -1,5 +1,5 @@
import request from "@/utils/request"; import request from '@/utils/request';
import { mapTrackPlayableStatus } from "@/utils/common"; import { mapTrackPlayableStatus } from '@/utils/common';
/** /**
* 推荐歌单 * 推荐歌单
@ -11,8 +11,8 @@ import { mapTrackPlayableStatus } from "@/utils/common";
*/ */
export function recommendPlaylist(params) { export function recommendPlaylist(params) {
return request({ return request({
url: "/personalized", url: '/personalized',
method: "get", method: 'get',
params, params,
}); });
} }
@ -24,8 +24,8 @@ export function recommendPlaylist(params) {
*/ */
export function dailyRecommendPlaylist(params) { export function dailyRecommendPlaylist(params) {
return request({ return request({
url: "/recommend/resource", url: '/recommend/resource',
method: "get", method: 'get',
params, params,
}); });
} }
@ -43,10 +43,10 @@ export function getPlaylistDetail(id, noCache = false) {
let params = { id }; let params = { id };
if (noCache) params.timestamp = new Date().getTime(); if (noCache) params.timestamp = new Date().getTime();
return request({ return request({
url: "/playlist/detail", url: '/playlist/detail',
method: "get", method: 'get',
params, params,
}).then((data) => { }).then(data => {
data.playlist.tracks = mapTrackPlayableStatus( data.playlist.tracks = mapTrackPlayableStatus(
data.playlist.tracks, data.playlist.tracks,
data.privileges || [] data.privileges || []
@ -67,8 +67,8 @@ export function getPlaylistDetail(id, noCache = false) {
*/ */
export function highQualityPlaylist(params) { export function highQualityPlaylist(params) {
return request({ return request({
url: "/top/playlist/highquality", url: '/top/playlist/highquality',
method: "get", method: 'get',
params, params,
}); });
} }
@ -86,8 +86,8 @@ export function highQualityPlaylist(params) {
*/ */
export function topPlaylist(params) { export function topPlaylist(params) {
return request({ return request({
url: "/top/playlist", url: '/top/playlist',
method: "get", method: 'get',
params, params,
}); });
} }
@ -98,8 +98,8 @@ export function topPlaylist(params) {
*/ */
export function playlistCatlist() { export function playlistCatlist() {
return request({ return request({
url: "/playlist/catlist", url: '/playlist/catlist',
method: "get", method: 'get',
}); });
} }
@ -109,8 +109,8 @@ export function playlistCatlist() {
*/ */
export function toplists() { export function toplists() {
return request({ return request({
url: "/toplist", url: '/toplist',
method: "get", method: 'get',
}); });
} }
@ -126,8 +126,8 @@ export function toplists() {
export function subscribePlaylist(params) { export function subscribePlaylist(params) {
params.timestamp = new Date().getTime(); params.timestamp = new Date().getTime();
return request({ return request({
url: "/playlist/subscribe", url: '/playlist/subscribe',
method: "post", method: 'post',
params, params,
}); });
} }
@ -140,8 +140,8 @@ export function subscribePlaylist(params) {
*/ */
export function deletePlaylist(id) { export function deletePlaylist(id) {
return request({ return request({
url: "/playlist/delete", url: '/playlist/delete',
method: "post", method: 'post',
params: { id }, params: { id },
}); });
} }
@ -160,8 +160,8 @@ export function deletePlaylist(id) {
export function createPlaylist(params) { export function createPlaylist(params) {
params.timestamp = new Date().getTime(); params.timestamp = new Date().getTime();
return request({ return request({
url: "/playlist/create", url: '/playlist/create',
method: "post", method: 'post',
params, params,
}); });
} }
@ -178,8 +178,8 @@ export function createPlaylist(params) {
export function addOrRemoveTrackFromPlaylist(params) { export function addOrRemoveTrackFromPlaylist(params) {
params.timestamp = new Date().getTime(); params.timestamp = new Date().getTime();
return request({ return request({
url: "/playlist/tracks", url: '/playlist/tracks',
method: "post", method: 'post',
params, params,
}); });
} }
@ -193,10 +193,10 @@ export function addOrRemoveTrackFromPlaylist(params) {
*/ */
export function dailyRecommendTracks() { export function dailyRecommendTracks() {
return request({ return request({
url: "/recommend/songs", url: '/recommend/songs',
method: "post", method: 'post',
params: { timestamp: new Date().getTime() }, params: { timestamp: new Date().getTime() },
}).then((result) => { }).then(result => {
result.data.dailySongs = mapTrackPlayableStatus( result.data.dailySongs = mapTrackPlayableStatus(
result.data.dailySongs, result.data.dailySongs,
result.data.privileges result.data.privileges

@ -1,6 +1,6 @@
import store from "@/store"; import store from '@/store';
import request from "@/utils/request"; import request from '@/utils/request';
import { mapTrackPlayableStatus } from "@/utils/common"; import { mapTrackPlayableStatus } from '@/utils/common';
/** /**
* 获取音乐 url * 获取音乐 url
* 说明 : 使用歌单详情接口后 , 能得到的音乐的 id, 但不能得到的音乐 url, 调用此接口, 传入的音乐 id( 可多个 , 用逗号隔开 ), 可以获取对应的音乐的 url, * 说明 : 使用歌单详情接口后 , 能得到的音乐的 id, 但不能得到的音乐 url, 调用此接口, 传入的音乐 id( 可多个 , 用逗号隔开 ), 可以获取对应的音乐的 url,
@ -13,8 +13,8 @@ export function getMP3(id) {
? store.state.settings.musicQuality ? store.state.settings.musicQuality
: 320000; : 320000;
return request({ return request({
url: "/song/url", url: '/song/url',
method: "get", method: 'get',
params: { params: {
id, id,
br, br,
@ -28,12 +28,12 @@ export function getMP3(id) {
*/ */
export function getTrackDetail(ids) { export function getTrackDetail(ids) {
return request({ return request({
url: "/song/detail", url: '/song/detail',
method: "get", method: 'get',
params: { params: {
ids, ids,
}, },
}).then((data) => { }).then(data => {
data.songs = mapTrackPlayableStatus(data.songs, data.privileges); data.songs = mapTrackPlayableStatus(data.songs, data.privileges);
return data; return data;
}); });
@ -46,8 +46,8 @@ export function getTrackDetail(ids) {
export function getLyric(id) { export function getLyric(id) {
return request({ return request({
url: "/lyric", url: '/lyric',
method: "get", method: 'get',
params: { params: {
id, id,
}, },
@ -60,8 +60,8 @@ export function getLyric(id) {
*/ */
export function topSong(type) { export function topSong(type) {
return request({ return request({
url: "/top/song", url: '/top/song',
method: "get", method: 'get',
params: { params: {
type, type,
}, },
@ -79,8 +79,8 @@ export function topSong(type) {
export function likeATrack(params) { export function likeATrack(params) {
params.timestamp = new Date().getTime(); params.timestamp = new Date().getTime();
return request({ return request({
url: "/like", url: '/like',
method: "get", method: 'get',
params, params,
}); });
} }
@ -99,8 +99,8 @@ export function likeATrack(params) {
export function scrobble(params) { export function scrobble(params) {
params.timestamp = new Date().getTime(); params.timestamp = new Date().getTime();
return request({ return request({
url: "/scrobble", url: '/scrobble',
method: "get", method: 'get',
params, params,
}); });
} }

@ -265,17 +265,17 @@ a.plyr__control::before {
display: none; display: none;
} }
.plyr [data-plyr="airplay"], .plyr [data-plyr='airplay'],
.plyr [data-plyr="captions"], .plyr [data-plyr='captions'],
.plyr [data-plyr="fullscreen"], .plyr [data-plyr='fullscreen'],
.plyr [data-plyr="pip"] { .plyr [data-plyr='pip'] {
display: none; display: none;
} }
.plyr--airplay-supported [data-plyr="airplay"], .plyr--airplay-supported [data-plyr='airplay'],
.plyr--captions-enabled [data-plyr="captions"], .plyr--captions-enabled [data-plyr='captions'],
.plyr--fullscreen-enabled [data-plyr="fullscreen"], .plyr--fullscreen-enabled [data-plyr='fullscreen'],
.plyr--pip-supported [data-plyr="pip"] { .plyr--pip-supported [data-plyr='pip'] {
display: inline-block; display: inline-block;
} }
@ -288,11 +288,11 @@ a.plyr__control::before {
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
.plyr__menu .plyr__control[aria-expanded="true"] svg { .plyr__menu .plyr__control[aria-expanded='true'] svg {
transform: rotate(90deg); transform: rotate(90deg);
} }
.plyr__menu .plyr__control[aria-expanded="true"] .plyr__tooltip { .plyr__menu .plyr__control[aria-expanded='true'] .plyr__tooltip {
display: none; display: none;
} }
@ -327,7 +327,7 @@ a.plyr__control::before {
border: var(--plyr-menu-arrow-size, 4px) solid transparent; border: var(--plyr-menu-arrow-size, 4px) solid transparent;
border-top-color: rgba(255, 255, 255, 0.9); border-top-color: rgba(255, 255, 255, 0.9);
border-top-color: var(--plyr-menu-background, rgba(255, 255, 255, 0.9)); border-top-color: var(--plyr-menu-background, rgba(255, 255, 255, 0.9));
content: ""; content: '';
height: 0; height: 0;
position: absolute; position: absolute;
right: calc(((18px / 2) + calc(10px * 0.7)) - (4px / 2)); right: calc(((18px / 2) + calc(10px * 0.7)) - (4px / 2));
@ -341,18 +341,18 @@ a.plyr__control::before {
width: 0; width: 0;
} }
.plyr__menu__container [role="menu"] { .plyr__menu__container [role='menu'] {
padding: calc(10px * 0.7); padding: calc(10px * 0.7);
padding: calc(var(--plyr-control-spacing, 10px) * 0.7); padding: calc(var(--plyr-control-spacing, 10px) * 0.7);
} }
.plyr__menu__container [role="menuitem"], .plyr__menu__container [role='menuitem'],
.plyr__menu__container [role="menuitemradio"] { .plyr__menu__container [role='menuitemradio'] {
margin-top: 2px; margin-top: 2px;
} }
.plyr__menu__container [role="menuitem"]:first-child, .plyr__menu__container [role='menuitem']:first-child,
.plyr__menu__container [role="menuitemradio"]:first-child { .plyr__menu__container [role='menuitemradio']:first-child {
margin-top: 0; margin-top: 0;
} }
@ -386,7 +386,7 @@ a.plyr__control::before {
.plyr__menu__container .plyr__control::after { .plyr__menu__container .plyr__control::after {
border: 4px solid transparent; border: 4px solid transparent;
border: var(--plyr-menu-item-arrow-size, 4px) solid transparent; border: var(--plyr-menu-item-arrow-size, 4px) solid transparent;
content: ""; content: '';
position: absolute; position: absolute;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
@ -441,7 +441,7 @@ a.plyr__control::before {
background: var(--plyr-menu-back-border-color, #dcdfe5); background: var(--plyr-menu-back-border-color, #dcdfe5);
box-shadow: 0 1px 0 #fff; box-shadow: 0 1px 0 #fff;
box-shadow: 0 1px 0 var(--plyr-menu-back-border-shadow-color, #fff); box-shadow: 0 1px 0 var(--plyr-menu-back-border-shadow-color, #fff);
content: ""; content: '';
height: 1px; height: 1px;
left: 0; left: 0;
margin-top: calc(calc(10px * 0.7) / 2); margin-top: calc(calc(10px * 0.7) / 2);
@ -457,19 +457,19 @@ a.plyr__control::before {
border-right-color: currentColor; border-right-color: currentColor;
} }
.plyr__menu__container .plyr__control[role="menuitemradio"] { .plyr__menu__container .plyr__control[role='menuitemradio'] {
padding-left: calc(10px * 0.7); padding-left: calc(10px * 0.7);
padding-left: calc(var(--plyr-control-spacing, 10px) * 0.7); padding-left: calc(var(--plyr-control-spacing, 10px) * 0.7);
} }
.plyr__menu__container .plyr__control[role="menuitemradio"]::after, .plyr__menu__container .plyr__control[role='menuitemradio']::after,
.plyr__menu__container .plyr__control[role="menuitemradio"]::before { .plyr__menu__container .plyr__control[role='menuitemradio']::before {
border-radius: 100%; border-radius: 100%;
} }
.plyr__menu__container .plyr__control[role="menuitemradio"]::before { .plyr__menu__container .plyr__control[role='menuitemradio']::before {
background: rgba(0, 0, 0, 0.1); background: rgba(0, 0, 0, 0.1);
content: ""; content: '';
display: block; display: block;
flex-shrink: 0; flex-shrink: 0;
height: 16px; height: 16px;
@ -479,7 +479,7 @@ a.plyr__control::before {
width: 16px; width: 16px;
} }
.plyr__menu__container .plyr__control[role="menuitemradio"]::after { .plyr__menu__container .plyr__control[role='menuitemradio']::after {
background: #fff; background: #fff;
border: 0; border: 0;
height: 6px; height: 6px;
@ -492,7 +492,7 @@ a.plyr__control::before {
} }
.plyr__menu__container .plyr__menu__container
.plyr__control[role="menuitemradio"][aria-checked="true"]::before { .plyr__control[role='menuitemradio'][aria-checked='true']::before {
background: #00b3ff; background: #00b3ff;
background: var( background: var(
--plyr-control-toggle-checked-background, --plyr-control-toggle-checked-background,
@ -501,14 +501,14 @@ a.plyr__control::before {
} }
.plyr__menu__container .plyr__menu__container
.plyr__control[role="menuitemradio"][aria-checked="true"]::after { .plyr__control[role='menuitemradio'][aria-checked='true']::after {
opacity: 1; opacity: 1;
transform: translateY(-50%) scale(1); transform: translateY(-50%) scale(1);
} }
.plyr__menu__container .plyr__menu__container
.plyr__control[role="menuitemradio"].plyr__tab-focus::before, .plyr__control[role='menuitemradio'].plyr__tab-focus::before,
.plyr__menu__container .plyr__control[role="menuitemradio"]:hover::before { .plyr__menu__container .plyr__control[role='menuitemradio']:hover::before {
background: rgba(35, 40, 47, 0.1); background: rgba(35, 40, 47, 0.1);
} }
@ -524,7 +524,7 @@ a.plyr__control::before {
pointer-events: none; pointer-events: none;
} }
.plyr--full-ui input[type="range"] { .plyr--full-ui input[type='range'] {
-webkit-appearance: none; -webkit-appearance: none;
background: 0 0; background: 0 0;
border: 0; border: 0;
@ -547,7 +547,7 @@ a.plyr__control::before {
width: 100%; width: 100%;
} }
.plyr--full-ui input[type="range"]::-webkit-slider-runnable-track { .plyr--full-ui input[type='range']::-webkit-slider-runnable-track {
background: 0 0; background: 0 0;
border: 0; border: 0;
border-radius: calc(5px / 2); border-radius: calc(5px / 2);
@ -566,7 +566,7 @@ a.plyr__control::before {
); );
} }
.plyr--full-ui input[type="range"]::-webkit-slider-thumb { .plyr--full-ui input[type='range']::-webkit-slider-thumb {
background: #fff; background: #fff;
background: var(--plyr-range-thumb-background, #fff); background: var(--plyr-range-thumb-background, #fff);
border: 0; border: 0;
@ -596,7 +596,7 @@ a.plyr__control::before {
); );
} }
.plyr--full-ui input[type="range"]::-moz-range-track { .plyr--full-ui input[type='range']::-moz-range-track {
background: 0 0; background: 0 0;
border: 0; border: 0;
border-radius: calc(5px / 2); border-radius: calc(5px / 2);
@ -608,7 +608,7 @@ a.plyr__control::before {
user-select: none; user-select: none;
} }
.plyr--full-ui input[type="range"]::-moz-range-thumb { .plyr--full-ui input[type='range']::-moz-range-thumb {
background: #fff; background: #fff;
background: var(--plyr-range-thumb-background, #fff); background: var(--plyr-range-thumb-background, #fff);
border: 0; border: 0;
@ -628,7 +628,7 @@ a.plyr__control::before {
width: var(--plyr-range-thumb-height, 13px); width: var(--plyr-range-thumb-height, 13px);
} }
.plyr--full-ui input[type="range"]::-moz-range-progress { .plyr--full-ui input[type='range']::-moz-range-progress {
background: currentColor; background: currentColor;
border-radius: calc(5px / 2); border-radius: calc(5px / 2);
border-radius: calc(var(--plyr-range-track-height, 5px) / 2); border-radius: calc(var(--plyr-range-track-height, 5px) / 2);
@ -636,7 +636,7 @@ a.plyr__control::before {
height: var(--plyr-range-track-height, 5px); height: var(--plyr-range-track-height, 5px);
} }
.plyr--full-ui input[type="range"]::-ms-track { .plyr--full-ui input[type='range']::-ms-track {
background: 0 0; background: 0 0;
border: 0; border: 0;
border-radius: calc(5px / 2); border-radius: calc(5px / 2);
@ -650,7 +650,7 @@ a.plyr__control::before {
color: transparent; color: transparent;
} }
.plyr--full-ui input[type="range"]::-ms-fill-upper { .plyr--full-ui input[type='range']::-ms-fill-upper {
background: 0 0; background: 0 0;
border: 0; border: 0;
border-radius: calc(5px / 2); border-radius: calc(5px / 2);
@ -663,7 +663,7 @@ a.plyr__control::before {
user-select: none; user-select: none;
} }
.plyr--full-ui input[type="range"]::-ms-fill-lower { .plyr--full-ui input[type='range']::-ms-fill-lower {
background: 0 0; background: 0 0;
border: 0; border: 0;
border-radius: calc(5px / 2); border-radius: calc(5px / 2);
@ -677,7 +677,7 @@ a.plyr__control::before {
background: currentColor; background: currentColor;
} }
.plyr--full-ui input[type="range"]::-ms-thumb { .plyr--full-ui input[type='range']::-ms-thumb {
background: #fff; background: #fff;
background: var(--plyr-range-thumb-background, #fff); background: var(--plyr-range-thumb-background, #fff);
border: 0; border: 0;
@ -698,20 +698,20 @@ a.plyr__control::before {
margin-top: 0; margin-top: 0;
} }
.plyr--full-ui input[type="range"]::-ms-tooltip { .plyr--full-ui input[type='range']::-ms-tooltip {
display: none; display: none;
} }
.plyr--full-ui input[type="range"]:focus { .plyr--full-ui input[type='range']:focus {
outline: 0; outline: 0;
} }
.plyr--full-ui input[type="range"]::-moz-focus-outer { .plyr--full-ui input[type='range']::-moz-focus-outer {
border: 0; border: 0;
} }
.plyr--full-ui .plyr--full-ui
input[type="range"].plyr__tab-focus::-webkit-slider-runnable-track { input[type='range'].plyr__tab-focus::-webkit-slider-runnable-track {
outline-color: #00b3ff; outline-color: #00b3ff;
outline-color: var( outline-color: var(
--plyr-tab-focus-color, --plyr-tab-focus-color,
@ -722,7 +722,7 @@ a.plyr__control::before {
outline-width: 3px; outline-width: 3px;
} }
.plyr--full-ui input[type="range"].plyr__tab-focus::-moz-range-track { .plyr--full-ui input[type='range'].plyr__tab-focus::-moz-range-track {
outline-color: #00b3ff; outline-color: #00b3ff;
outline-color: var( outline-color: var(
--plyr-tab-focus-color, --plyr-tab-focus-color,
@ -733,7 +733,7 @@ a.plyr__control::before {
outline-width: 3px; outline-width: 3px;
} }
.plyr--full-ui input[type="range"].plyr__tab-focus::-ms-track { .plyr--full-ui input[type='range'].plyr__tab-focus::-ms-track {
outline-color: #00b3ff; outline-color: #00b3ff;
outline-color: var( outline-color: var(
--plyr-tab-focus-color, --plyr-tab-focus-color,
@ -769,7 +769,7 @@ a.plyr__control::before {
} }
.plyr__time + .plyr__time::before { .plyr__time + .plyr__time::before {
content: "\2044"; content: '\2044';
margin-right: 10px; margin-right: 10px;
margin-right: var(--plyr-control-spacing, 10px); margin-right: var(--plyr-control-spacing, 10px);
} }
@ -821,7 +821,7 @@ a.plyr__control::before {
var(--plyr-tooltip-background, rgba(255, 255, 255, 0.9)); var(--plyr-tooltip-background, rgba(255, 255, 255, 0.9));
bottom: calc(4px * -1); bottom: calc(4px * -1);
bottom: calc(var(--plyr-tooltip-arrow-size, 4px) * -1); bottom: calc(var(--plyr-tooltip-arrow-size, 4px) * -1);
content: ""; content: '';
height: 0; height: 0;
left: 50%; left: 50%;
position: absolute; position: absolute;
@ -906,7 +906,7 @@ a.plyr__control::before {
position: relative; position: relative;
} }
.plyr__progress input[type="range"], .plyr__progress input[type='range'],
.plyr__progress__buffer { .plyr__progress__buffer {
margin-left: calc(13px * -0.5); margin-left: calc(13px * -0.5);
margin-left: calc(var(--plyr-range-thumb-height, 13px) * -0.5); margin-left: calc(var(--plyr-range-thumb-height, 13px) * -0.5);
@ -916,7 +916,7 @@ a.plyr__control::before {
width: calc(100% + var(--plyr-range-thumb-height, 13px)); width: calc(100% + var(--plyr-range-thumb-height, 13px));
} }
.plyr__progress input[type="range"] { .plyr__progress input[type='range'] {
position: relative; position: relative;
z-index: 2; z-index: 2;
} }
@ -1024,7 +1024,7 @@ a.plyr__control::before {
width: 20%; width: 20%;
} }
.plyr__volume input[type="range"] { .plyr__volume input[type='range'] {
margin-left: calc(10px / 2); margin-left: calc(10px / 2);
margin-left: calc(var(--plyr-control-spacing, 10px) / 2); margin-left: calc(var(--plyr-control-spacing, 10px) / 2);
margin-right: calc(10px / 2); margin-right: calc(10px / 2);
@ -1054,7 +1054,7 @@ a.plyr__control::before {
.plyr--audio .plyr__control.plyr__tab-focus, .plyr--audio .plyr__control.plyr__tab-focus,
.plyr--audio .plyr__control:hover, .plyr--audio .plyr__control:hover,
.plyr--audio .plyr__control[aria-expanded="true"] { .plyr--audio .plyr__control[aria-expanded='true'] {
background: #00b3ff; background: #00b3ff;
background: var( background: var(
--plyr-audio-control-background-hover, --plyr-audio-control-background-hover,
@ -1064,7 +1064,7 @@ a.plyr__control::before {
color: var(--plyr-audio-control-color-hover, #fff); color: var(--plyr-audio-control-color-hover, #fff);
} }
.plyr--full-ui.plyr--audio input[type="range"]::-webkit-slider-runnable-track { .plyr--full-ui.plyr--audio input[type='range']::-webkit-slider-runnable-track {
background-color: rgba(193, 200, 209, 0.6); background-color: rgba(193, 200, 209, 0.6);
background-color: var( background-color: var(
--plyr-audio-range-track-background, --plyr-audio-range-track-background,
@ -1072,7 +1072,7 @@ a.plyr__control::before {
); );
} }
.plyr--full-ui.plyr--audio input[type="range"]::-moz-range-track { .plyr--full-ui.plyr--audio input[type='range']::-moz-range-track {
background-color: rgba(193, 200, 209, 0.6); background-color: rgba(193, 200, 209, 0.6);
background-color: var( background-color: var(
--plyr-audio-range-track-background, --plyr-audio-range-track-background,
@ -1080,7 +1080,7 @@ a.plyr__control::before {
); );
} }
.plyr--full-ui.plyr--audio input[type="range"]::-ms-track { .plyr--full-ui.plyr--audio input[type='range']::-ms-track {
background-color: rgba(193, 200, 209, 0.6); background-color: rgba(193, 200, 209, 0.6);
background-color: var( background-color: var(
--plyr-audio-range-track-background, --plyr-audio-range-track-background,
@ -1088,7 +1088,7 @@ a.plyr__control::before {
); );
} }
.plyr--full-ui.plyr--audio input[type="range"]:active::-webkit-slider-thumb { .plyr--full-ui.plyr--audio input[type='range']:active::-webkit-slider-thumb {
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2), box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
0 0 0 3px rgba(35, 40, 47, 0.1); 0 0 0 3px rgba(35, 40, 47, 0.1);
box-shadow: var( box-shadow: var(
@ -1100,7 +1100,7 @@ a.plyr__control::before {
var(--plyr-audio-range-thumb-active-shadow-color, rgba(35, 40, 47, 0.1)); var(--plyr-audio-range-thumb-active-shadow-color, rgba(35, 40, 47, 0.1));
} }
.plyr--full-ui.plyr--audio input[type="range"]:active::-moz-range-thumb { .plyr--full-ui.plyr--audio input[type='range']:active::-moz-range-thumb {
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2), box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
0 0 0 3px rgba(35, 40, 47, 0.1); 0 0 0 3px rgba(35, 40, 47, 0.1);
box-shadow: var( box-shadow: var(
@ -1112,7 +1112,7 @@ a.plyr__control::before {
var(--plyr-audio-range-thumb-active-shadow-color, rgba(35, 40, 47, 0.1)); var(--plyr-audio-range-thumb-active-shadow-color, rgba(35, 40, 47, 0.1));
} }
.plyr--full-ui.plyr--audio input[type="range"]:active::-ms-thumb { .plyr--full-ui.plyr--audio input[type='range']:active::-ms-thumb {
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2), box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
0 0 0 3px rgba(35, 40, 47, 0.1); 0 0 0 3px rgba(35, 40, 47, 0.1);
box-shadow: var( box-shadow: var(
@ -1207,7 +1207,7 @@ a.plyr__control::before {
.plyr--video .plyr__control.plyr__tab-focus, .plyr--video .plyr__control.plyr__tab-focus,
.plyr--video .plyr__control:hover, .plyr--video .plyr__control:hover,
.plyr--video .plyr__control[aria-expanded="true"] { .plyr--video .plyr__control[aria-expanded='true'] {
background: #00b3ff; background: #00b3ff;
background: var( background: var(
--plyr-video-control-background-hover, --plyr-video-control-background-hover,
@ -1258,7 +1258,7 @@ a.plyr__control::before {
display: block; display: block;
} }
.plyr--full-ui.plyr--video input[type="range"]::-webkit-slider-runnable-track { .plyr--full-ui.plyr--video input[type='range']::-webkit-slider-runnable-track {
background-color: rgba(255, 255, 255, 0.25); background-color: rgba(255, 255, 255, 0.25);
background-color: var( background-color: var(
--plyr-video-range-track-background, --plyr-video-range-track-background,
@ -1266,7 +1266,7 @@ a.plyr__control::before {
); );
} }
.plyr--full-ui.plyr--video input[type="range"]::-moz-range-track { .plyr--full-ui.plyr--video input[type='range']::-moz-range-track {
background-color: rgba(255, 255, 255, 0.25); background-color: rgba(255, 255, 255, 0.25);
background-color: var( background-color: var(
--plyr-video-range-track-background, --plyr-video-range-track-background,
@ -1274,7 +1274,7 @@ a.plyr__control::before {
); );
} }
.plyr--full-ui.plyr--video input[type="range"]::-ms-track { .plyr--full-ui.plyr--video input[type='range']::-ms-track {
background-color: rgba(255, 255, 255, 0.25); background-color: rgba(255, 255, 255, 0.25);
background-color: var( background-color: var(
--plyr-video-range-track-background, --plyr-video-range-track-background,
@ -1282,7 +1282,7 @@ a.plyr__control::before {
); );
} }
.plyr--full-ui.plyr--video input[type="range"]:active::-webkit-slider-thumb { .plyr--full-ui.plyr--video input[type='range']:active::-webkit-slider-thumb {
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2), box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
0 0 0 3px rgba(255, 255, 255, 0.5); 0 0 0 3px rgba(255, 255, 255, 0.5);
box-shadow: var( box-shadow: var(
@ -1297,7 +1297,7 @@ a.plyr__control::before {
); );
} }
.plyr--full-ui.plyr--video input[type="range"]:active::-moz-range-thumb { .plyr--full-ui.plyr--video input[type='range']:active::-moz-range-thumb {
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2), box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
0 0 0 3px rgba(255, 255, 255, 0.5); 0 0 0 3px rgba(255, 255, 255, 0.5);
box-shadow: var( box-shadow: var(
@ -1312,7 +1312,7 @@ a.plyr__control::before {
); );
} }
.plyr--full-ui.plyr--video input[type="range"]:active::-ms-thumb { .plyr--full-ui.plyr--video input[type='range']:active::-ms-thumb {
box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2), box-shadow: 0 1px 1px rgba(35, 40, 47, 0.15), 0 0 0 1px rgba(35, 40, 47, 0.2),
0 0 0 3px rgba(255, 255, 255, 0.5); 0 0 0 3px rgba(255, 255, 255, 0.5);
box-shadow: var( box-shadow: var(
@ -1713,7 +1713,7 @@ a.plyr__control::before {
var(--plyr-tooltip-background, rgba(255, 255, 255, 0.9)); var(--plyr-tooltip-background, rgba(255, 255, 255, 0.9));
bottom: calc(4px * -1); bottom: calc(4px * -1);
bottom: calc(var(--plyr-tooltip-arrow-size, 4px) * -1); bottom: calc(var(--plyr-tooltip-arrow-size, 4px) * -1);
content: ""; content: '';
height: 0; height: 0;
left: 50%; left: 50%;
position: absolute; position: absolute;

@ -89,7 +89,7 @@
} }
.nyancat .vue-slider-dot-handle { .nyancat .vue-slider-dot-handle {
background: url("/img/logos/nyancat.gif"); background: url('/img/logos/nyancat.gif');
background-size: 36px; background-size: 36px;
width: 36px; width: 36px;
height: 24px; height: 24px;
@ -101,7 +101,7 @@
} }
.nyancat-stop .vue-slider-dot-handle { .nyancat-stop .vue-slider-dot-handle {
background-image: url("/img/logos/nyancat-stop.png"); background-image: url('/img/logos/nyancat-stop.png');
transition: 300ms; transition: 300ms;
} }
@ -126,10 +126,10 @@
display: none; display: none;
} }
body[data-theme="dark"] .lyrics-page .vue-slider-process { body[data-theme='dark'] .lyrics-page .vue-slider-process {
background-color: #fafafa; background-color: #fafafa;
} }
body[data-theme="dark"] .lyrics-page .vue-slider-dot-handle { body[data-theme='dark'] .lyrics-page .vue-slider-dot-handle {
background-color: #fff; background-color: #fff;
} }

@ -1,8 +1,7 @@
import Vue from "vue"; import Vue from 'vue';
import SvgIcon from "@/components/SvgIcon"; import SvgIcon from '@/components/SvgIcon';
Vue.component("svg-icon", SvgIcon); Vue.component('svg-icon', SvgIcon);
const requireAll = (requireContext) => const requireAll = requireContext => requireContext.keys().map(requireContext);
requireContext.keys().map(requireContext); const req = require.context('./', true, /\.svg$/);
const req = require.context("./", true, /\.svg$/);
requireAll(req); requireAll(req);

@ -1,4 +1,4 @@
"use strict"; 'use strict';
import { import {
app, app,
protocol, protocol,
@ -7,20 +7,20 @@ import {
dialog, dialog,
globalShortcut, globalShortcut,
nativeTheme, nativeTheme,
} from "electron"; } from 'electron';
import { createProtocol } from "vue-cli-plugin-electron-builder/lib"; import { createProtocol } from 'vue-cli-plugin-electron-builder/lib';
import { startNeteaseMusicApi } from "./electron/services"; import { startNeteaseMusicApi } from './electron/services';
import { initIpcMain } from "./electron/ipcMain.js"; import { initIpcMain } from './electron/ipcMain.js';
import { createMenu } from "./electron/menu"; import { createMenu } from './electron/menu';
import { createTray } from "@/electron/tray"; import { createTray } from '@/electron/tray';
import { createTouchBar } from "./electron/touchBar"; import { createTouchBar } from './electron/touchBar';
import { createDockMenu } from "./electron/dockMenu"; import { createDockMenu } from './electron/dockMenu';
import { registerGlobalShortcut } from "./electron/globalShortcut"; import { registerGlobalShortcut } from './electron/globalShortcut';
import { autoUpdater } from "electron-updater"; import { autoUpdater } from 'electron-updater';
import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer"; import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';
import express from "express"; import express from 'express';
import expressProxy from "express-http-proxy"; import expressProxy from 'express-http-proxy';
import Store from "electron-store"; import Store from 'electron-store';
class Background { class Background {
constructor() { constructor() {
@ -28,19 +28,19 @@ class Background {
this.tray = null; this.tray = null;
this.store = new Store({ this.store = new Store({
windowWidth: { windowWidth: {
width: { type: "number", default: 1440 }, width: { type: 'number', default: 1440 },
height: { type: "number", default: 840 }, height: { type: 'number', default: 840 },
}, },
}); });
this.neteaseMusicAPI = null; this.neteaseMusicAPI = null;
this.expressApp = null; this.expressApp = null;
this.willQuitApp = process.platform === "darwin" ? false : true; this.willQuitApp = process.platform === 'darwin' ? false : true;
this.init(); this.init();
} }
init() { init() {
console.log("initializing"); console.log('initializing');
// Make sure the app is singleton. // Make sure the app is singleton.
if (!app.requestSingleInstanceLock()) return app.quit(); if (!app.requestSingleInstanceLock()) return app.quit();
@ -53,7 +53,7 @@ class Background {
// Scheme must be registered before the app is ready // Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([ protocol.registerSchemesAsPrivileged([
{ scheme: "app", privileges: { secure: true, standard: true } }, { scheme: 'app', privileges: { secure: true, standard: true } },
]); ]);
// handle app events // handle app events
@ -65,45 +65,45 @@ class Background {
try { try {
await installExtension(VUEJS_DEVTOOLS); await installExtension(VUEJS_DEVTOOLS);
} catch (e) { } catch (e) {
console.error("Vue Devtools failed to install:", e.toString()); console.error('Vue Devtools failed to install:', e.toString());
} }
// Exit cleanly on request from parent process in development mode. // Exit cleanly on request from parent process in development mode.
if (process.platform === "win32") { if (process.platform === 'win32') {
process.on("message", (data) => { process.on('message', data => {
if (data === "graceful-exit") { if (data === 'graceful-exit') {
app.quit(); app.quit();
} }
}); });
} else { } else {
process.on("SIGTERM", () => { process.on('SIGTERM', () => {
app.quit(); app.quit();
}); });
} }
} }
createExpressApp() { createExpressApp() {
console.log("creating express app"); console.log('creating express app');
const expressApp = express(); const expressApp = express();
expressApp.use("/", express.static(__dirname + "/")); expressApp.use('/', express.static(__dirname + '/'));
expressApp.use("/api", expressProxy("http://127.0.0.1:10754")); expressApp.use('/api', expressProxy('http://127.0.0.1:10754'));
this.expressApp = expressApp.listen(27232); this.expressApp = expressApp.listen(27232);
} }
createWindow() { createWindow() {
console.log("creating app window"); console.log('creating app window');
const appearance = this.store.get("settings.appearance"); const appearance = this.store.get('settings.appearance');
this.window = new BrowserWindow({ this.window = new BrowserWindow({
width: this.store.get("window.width") || 1440, width: this.store.get('window.width') || 1440,
height: this.store.get("window.height") || 840, height: this.store.get('window.height') || 840,
minWidth: 1080, minWidth: 1080,
minHeight: 720, minHeight: 720,
titleBarStyle: "hiddenInset", titleBarStyle: 'hiddenInset',
frame: process.platform !== "win32", frame: process.platform !== 'win32',
title: "YesPlayMusic", title: 'YesPlayMusic',
webPreferences: { webPreferences: {
webSecurity: false, webSecurity: false,
nodeIntegration: true, nodeIntegration: true,
@ -111,11 +111,11 @@ class Background {
contextIsolation: false, contextIsolation: false,
}, },
backgroundColor: backgroundColor:
((appearance === undefined || appearance === "auto") && ((appearance === undefined || appearance === 'auto') &&
nativeTheme.shouldUseDarkColors) || nativeTheme.shouldUseDarkColors) ||
appearance === "dark" appearance === 'dark'
? "#222" ? '#222'
: "#fff", : '#fff',
}); });
// hide menu bar on Microsoft Windows and Linux // hide menu bar on Microsoft Windows and Linux
@ -126,46 +126,46 @@ class Background {
this.window.loadURL(process.env.WEBPACK_DEV_SERVER_URL); this.window.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
if (!process.env.IS_TEST) this.window.webContents.openDevTools(); if (!process.env.IS_TEST) this.window.webContents.openDevTools();
} else { } else {
createProtocol("app"); createProtocol('app');
this.window.loadURL("http://localhost:27232"); this.window.loadURL('http://localhost:27232');
} }
} }
checkForUpdates() { checkForUpdates() {
autoUpdater.checkForUpdatesAndNotify(); autoUpdater.checkForUpdatesAndNotify();
const showNewVersionMessage = (info) => { const showNewVersionMessage = info => {
dialog dialog
.showMessageBox({ .showMessageBox({
title: "发现新版本 v" + info.version, title: '发现新版本 v' + info.version,
message: "发现新版本 v" + info.version, message: '发现新版本 v' + info.version,
detail: "是否前往 GitHub 下载新版本安装包?", detail: '是否前往 GitHub 下载新版本安装包?',
buttons: ["下载", "取消"], buttons: ['下载', '取消'],
type: "question", type: 'question',
noLink: true, noLink: true,
}) })
.then((result) => { .then(result => {
if (result.response === 0) { if (result.response === 0) {
shell.openExternal( shell.openExternal(
"https://github.com/qier222/YesPlayMusic/releases" 'https://github.com/qier222/YesPlayMusic/releases'
); );
} }
}); });
}; };
autoUpdater.on("update-available", (info) => { autoUpdater.on('update-available', info => {
showNewVersionMessage(info); showNewVersionMessage(info);
}); });
} }
handleWindowEvents() { handleWindowEvents() {
this.window.once("ready-to-show", () => { this.window.once('ready-to-show', () => {
console.log("windows ready-to-show event"); console.log('windows ready-to-show event');
this.window.show(); this.window.show();
}); });
this.window.on("close", (e) => { this.window.on('close', e => {
console.log("windows close event"); console.log('windows close event');
if (this.willQuitApp) { if (this.willQuitApp) {
/* the user tried to quit the app */ /* the user tried to quit the app */
this.window = null; this.window = null;
@ -177,31 +177,31 @@ class Background {
} }
}); });
this.window.on("resize", () => { this.window.on('resize', () => {
let { height, width } = this.window.getBounds(); let { height, width } = this.window.getBounds();
this.store.set("window", { height, width }); this.store.set('window', { height, width });
}); });
this.window.on("minimize", () => { this.window.on('minimize', () => {
if ( if (
["win32", "linux"].includes(process.platform) && ['win32', 'linux'].includes(process.platform) &&
this.store.get("settings.minimizeToTray") this.store.get('settings.minimizeToTray')
) { ) {
this.window.hide(); this.window.hide();
} }
}); });
this.window.webContents.on("new-window", function (e, url) { this.window.webContents.on('new-window', function (e, url) {
e.preventDefault(); e.preventDefault();
console.log("open url"); console.log('open url');
const excludeHosts = ["www.last.fm"]; const excludeHosts = ['www.last.fm'];
const exclude = excludeHosts.find((host) => url.includes(host)); const exclude = excludeHosts.find(host => url.includes(host));
if (exclude) { if (exclude) {
const newWindow = new BrowserWindow({ const newWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
titleBarStyle: "default", titleBarStyle: 'default',
title: "YesPlayMusic", title: 'YesPlayMusic',
webPreferences: { webPreferences: {
webSecurity: false, webSecurity: false,
nodeIntegration: true, nodeIntegration: true,
@ -217,14 +217,14 @@ class Background {
} }
handleAppEvents() { handleAppEvents() {
app.on("ready", async () => { app.on('ready', async () => {
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
console.log("app ready event"); console.log('app ready event');
// for development // for development
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== 'production') {
this.initDevtools(); this.initDevtools();
} }
@ -242,7 +242,7 @@ class Background {
createMenu(this.window); createMenu(this.window);
// create tray // create tray
if (["win32", "linux"].includes(process.platform)) { if (['win32', 'linux'].includes(process.platform)) {
this.tray = createTray(this.window); this.tray = createTray(this.window);
} }
@ -253,15 +253,15 @@ class Background {
this.window.setTouchBar(createTouchBar(this.window)); this.window.setTouchBar(createTouchBar(this.window));
// register global shortcuts // register global shortcuts
if (this.store.get("settings.enableGlobalShortcut")) { if (this.store.get('settings.enableGlobalShortcut')) {
registerGlobalShortcut(this.window); registerGlobalShortcut(this.window);
} }
}); });
app.on("activate", () => { app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the // On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open. // dock icon is clicked and there are no other windows open.
console.log("app activate event"); console.log('app activate event');
if (this.window === null) { if (this.window === null) {
this.createWindow(); this.createWindow();
} else { } else {
@ -269,21 +269,21 @@ class Background {
} }
}); });
app.on("window-all-closed", () => { app.on('window-all-closed', () => {
if (process.platform !== "darwin") { if (process.platform !== 'darwin') {
app.quit(); app.quit();
} }
}); });
app.on("before-quit", () => { app.on('before-quit', () => {
this.willQuitApp = true; this.willQuitApp = true;
}); });
app.on("quit", () => { app.on('quit', () => {
this.expressApp.close(); this.expressApp.close();
}); });
app.on("will-quit", () => { app.on('will-quit', () => {
// unregister all global shortcuts // unregister all global shortcuts
globalShortcut.unregisterAll(); globalShortcut.unregisterAll();
}); });

@ -10,7 +10,7 @@
<script> <script>
export default { export default {
name: "ArtistInLine", name: 'ArtistInLine',
props: { props: {
artists: { artists: {
type: Array, type: Array,
@ -18,20 +18,20 @@ export default {
}, },
exclude: { exclude: {
type: String, type: String,
default: "", default: '',
}, },
prefix: { prefix: {
type: String, type: String,
default: "", default: '',
}, },
}, },
computed: { computed: {
filteredArtists() { filteredArtists() {
return this.artists.filter((a) => a.name !== this.exclude); return this.artists.filter(a => a.name !== this.exclude);
}, },
computedPrefix() { computedPrefix() {
if (this.filteredArtists.length !== 0) return this.prefix; if (this.filteredArtists.length !== 0) return this.prefix;
else return ""; else return '';
}, },
}, },
}; };

@ -4,7 +4,7 @@
<script> <script>
export default { export default {
name: "ButtonIcon", name: 'ButtonIcon',
}; };
</script> </script>

@ -2,7 +2,7 @@
<button :style="buttonStyle" :class="color"> <button :style="buttonStyle" :class="color">
<svg-icon <svg-icon
v-if="iconClass !== null" v-if="iconClass !== null"
:iconClass="iconClass" :icon-class="iconClass"
:style="{ marginRight: iconButton ? '0px' : '8px' }" :style="{ marginRight: iconButton ? '0px' : '8px' }"
/> />
<slot></slot> <slot></slot>
@ -11,7 +11,7 @@
<script> <script>
export default { export default {
name: "ButtonTwoTone", name: 'ButtonTwoTone',
props: { props: {
iconClass: { iconClass: {
type: String, type: String,
@ -27,32 +27,32 @@ export default {
}, },
color: { color: {
type: String, type: String,
default: "blue", default: 'blue',
}, },
backgroundColor: { backgroundColor: {
type: String, type: String,
default: "", default: '',
}, },
textColor: { textColor: {
type: String, type: String,
default: "", default: '',
}, },
shape: { shape: {
type: String, type: String,
default: "square", default: 'square',
}, },
}, },
computed: { computed: {
buttonStyle() { buttonStyle() {
let styles = { let styles = {
borderRadius: this.shape === "round" ? "50%" : "8px", borderRadius: this.shape === 'round' ? '50%' : '8px',
padding: `8px ${this.horizontalPadding}px`, padding: `8px ${this.horizontalPadding}px`,
// height: "38px", // height: "38px",
width: this.shape === "round" ? "38px" : "auto", width: this.shape === 'round' ? '38px' : 'auto',
}; };
if (this.backgroundColor !== "") if (this.backgroundColor !== '')
styles.backgroundColor = this.backgroundColor; styles.backgroundColor = this.backgroundColor;
if (this.textColor !== "") styles.color = this.textColor; if (this.textColor !== '') styles.color = this.textColor;
return styles; return styles;
}, },
}, },

@ -1,12 +1,12 @@
<template> <template>
<div class="context-menu" ref="contextMenu"> <div ref="contextMenu" class="context-menu">
<div <div
v-if="showMenu"
ref="menu"
class="menu" class="menu"
tabindex="-1" tabindex="-1"
ref="menu"
v-if="showMenu"
@blur="closeMenu"
:style="{ top: top, left: left }" :style="{ top: top, left: left }"
@blur="closeMenu"
@click="closeMenu" @click="closeMenu"
> >
<slot></slot> <slot></slot>
@ -16,12 +16,12 @@
<script> <script>
export default { export default {
name: "ContextMenu", name: 'ContextMenu',
data() { data() {
return { return {
showMenu: false, showMenu: false,
top: "0px", top: '0px',
left: "0px", left: '0px',
}; };
}, },
methods: { methods: {
@ -31,8 +31,8 @@ export default {
let largestWidth = window.innerWidth - this.$refs.menu.offsetWidth - 25; let largestWidth = window.innerWidth - this.$refs.menu.offsetWidth - 25;
if (top > largestHeight) top = largestHeight; if (top > largestHeight) top = largestHeight;
if (left > largestWidth) left = largestWidth; if (left > largestWidth) left = largestWidth;
this.top = top + "px"; this.top = top + 'px';
this.left = left + "px"; this.left = left + 'px';
}, },
closeMenu() { closeMenu() {
@ -82,7 +82,7 @@ export default {
} }
} }
[data-theme="dark"] { [data-theme='dark'] {
.menu { .menu {
background: rgba(36, 36, 36, 0.78); background: rgba(36, 36, 36, 0.78);
backdrop-filter: blur(16px) contrast(120%); backdrop-filter: blur(16px) contrast(120%);

@ -1,9 +1,9 @@
<template> <template>
<div <div
class="cover" class="cover"
:class="{ 'cover-hover': coverHover }"
@mouseover="focus = true" @mouseover="focus = true"
@mouseleave="focus = false" @mouseleave="focus = false"
:class="{ 'cover-hover': coverHover }"
@click="clickCoverToPlay ? play() : goTo()" @click="clickCoverToPlay ? play() : goTo()"
> >
<div class="cover-container"> <div class="cover-container">
@ -11,16 +11,16 @@
<button <button
v-show="focus" v-show="focus"
class="play-button" class="play-button"
@click.stop="play()"
:style="playButtonStyles" :style="playButtonStyles"
@click.stop="play()"
><svg-icon icon-class="play" /> ><svg-icon icon-class="play" />
</button> </button>
</div> </div>
<img :src="imageUrl" :style="imageStyles" /> <img :src="imageUrl" :style="imageStyles" />
<transition name="fade" v-if="coverHover || alwaysShowShadow"> <transition v-if="coverHover || alwaysShowShadow" name="fade">
<div <div
class="shadow"
v-show="focus || alwaysShowShadow" v-show="focus || alwaysShowShadow"
class="shadow"
:style="shadowStyles" :style="shadowStyles"
></div> ></div>
</transition> </transition>
@ -52,22 +52,22 @@ export default {
imageStyles() { imageStyles() {
let styles = {}; let styles = {};
if (this.fixedSize !== 0) { if (this.fixedSize !== 0) {
styles.width = this.fixedSize + "px"; styles.width = this.fixedSize + 'px';
styles.height = this.fixedSize + "px"; styles.height = this.fixedSize + 'px';
} }
if (this.type === "artist") styles.borderRadius = "50%"; if (this.type === 'artist') styles.borderRadius = '50%';
return styles; return styles;
}, },
playButtonStyles() { playButtonStyles() {
let styles = {}; let styles = {};
styles.width = this.playButtonSize + "%"; styles.width = this.playButtonSize + '%';
styles.height = this.playButtonSize + "%"; styles.height = this.playButtonSize + '%';
return styles; return styles;
}, },
shadowStyles() { shadowStyles() {
let styles = {}; let styles = {};
styles.backgroundImage = `url(${this.imageUrl})`; styles.backgroundImage = `url(${this.imageUrl})`;
if (this.type === "artist") styles.borderRadius = "50%"; if (this.type === 'artist') styles.borderRadius = '50%';
return styles; return styles;
}, },
}, },

@ -1,19 +1,19 @@
<template> <template>
<div class="cover-row" :style="rowStyles"> <div class="cover-row" :style="rowStyles">
<div <div
class="item"
v-for="item in items" v-for="item in items"
:key="item.id" :key="item.id"
class="item"
:class="{ artist: type === 'artist' }" :class="{ artist: type === 'artist' }"
> >
<Cover <Cover
:imageUrl="getImageUrl(item)"
:type="type"
:id="item.id" :id="item.id"
:playButtonSize="type === 'artist' ? 26 : playButtonSize" :image-url="getImageUrl(item)"
:type="type"
:play-button-size="type === 'artist' ? 26 : playButtonSize"
/> />
<div class="text"> <div class="text">
<div class="info" v-if="showPlayCount"> <div v-if="showPlayCount" class="info">
<span class="play-count" <span class="play-count"
><svg-icon icon-class="play" />{{ ><svg-icon icon-class="play" />{{
item.playCount | formatPlayCount item.playCount | formatPlayCount
@ -21,15 +21,15 @@
</span> </span>
</div> </div>
<div class="title" :style="{ fontSize: subTextFontSize }"> <div class="title" :style="{ fontSize: subTextFontSize }">
<span class="explicit-symbol" v-if="isExplicit(item)" <span v-if="isExplicit(item)" class="explicit-symbol"
><ExplicitSymbol ><ExplicitSymbol
/></span> /></span>
<span class="lock-icon" v-if="isPrivacy(item)"> <span v-if="isPrivacy(item)" class="lock-icon">
<svg-icon icon-class="lock" <svg-icon icon-class="lock"
/></span> /></span>
<router-link :to="getTitleLink(item)">{{ item.name }}</router-link> <router-link :to="getTitleLink(item)">{{ item.name }}</router-link>
</div> </div>
<div class="info" v-if="type !== 'artist' && subText !== 'none'"> <div v-if="type !== 'artist' && subText !== 'none'" class="info">
<span v-html="getSubText(item)"></span> <span v-html="getSubText(item)"></span>
</div> </div>
</div> </div>
@ -38,11 +38,11 @@
</template> </template>
<script> <script>
import Cover from "@/components/Cover.vue"; import Cover from '@/components/Cover.vue';
import ExplicitSymbol from "@/components/ExplicitSymbol.vue"; import ExplicitSymbol from '@/components/ExplicitSymbol.vue';
export default { export default {
name: "CoverRow", name: 'CoverRow',
components: { components: {
Cover, Cover,
ExplicitSymbol, ExplicitSymbol,
@ -50,64 +50,64 @@ export default {
props: { props: {
items: { type: Array, required: true }, items: { type: Array, required: true },
type: { type: String, required: true }, type: { type: String, required: true },
subText: { type: String, default: "null" }, subText: { type: String, default: 'null' },
subTextFontSize: { type: String, default: "16px" }, subTextFontSize: { type: String, default: '16px' },
showPlayCount: { type: Boolean, default: false }, showPlayCount: { type: Boolean, default: false },
columnNumber: { type: Number, default: 5 }, columnNumber: { type: Number, default: 5 },
gap: { type: String, default: "44px 24px" }, gap: { type: String, default: '44px 24px' },
playButtonSize: { type: Number, default: 22 }, playButtonSize: { type: Number, default: 22 },
}, },
computed: { computed: {
rowStyles() { rowStyles() {
return { return {
"grid-template-columns": `repeat(${this.columnNumber}, 1fr)`, 'grid-template-columns': `repeat(${this.columnNumber}, 1fr)`,
gap: this.gap, gap: this.gap,
}; };
}, },
}, },
methods: { methods: {
getSubText(item) { getSubText(item) {
if (this.subText === "copywriter") return item.copywriter; if (this.subText === 'copywriter') return item.copywriter;
if (this.subText === "description") return item.description; if (this.subText === 'description') return item.description;
if (this.subText === "updateFrequency") return item.updateFrequency; if (this.subText === 'updateFrequency') return item.updateFrequency;
if (this.subText === "creator") return "by " + item.creator.nickname; if (this.subText === 'creator') return 'by ' + item.creator.nickname;
if (this.subText === "releaseYear") if (this.subText === 'releaseYear')
return new Date(item.publishTime).getFullYear(); return new Date(item.publishTime).getFullYear();
if (this.subText === "artist") { if (this.subText === 'artist') {
if (item.artist !== undefined) if (item.artist !== undefined)
return `<a href="/#/artist/${item.artist.id}">${item.artist.name}</a>`; return `<a href="/#/artist/${item.artist.id}">${item.artist.name}</a>`;
if (item.artists !== undefined) if (item.artists !== undefined)
return `<a href="/#/artist/${item.artists[0].id}">${item.artists[0].name}</a>`; return `<a href="/#/artist/${item.artists[0].id}">${item.artists[0].name}</a>`;
} }
if (this.subText === "albumType+releaseYear") { if (this.subText === 'albumType+releaseYear') {
let albumType = item.type; let albumType = item.type;
if (item.type === "EP/Single") { if (item.type === 'EP/Single') {
albumType = item.size === 1 ? "Single" : "EP"; albumType = item.size === 1 ? 'Single' : 'EP';
} else if (item.type === "Single") { } else if (item.type === 'Single') {
albumType = "Single"; albumType = 'Single';
} else if (item.type === "专辑") { } else if (item.type === '专辑') {
albumType = "Album"; albumType = 'Album';
} }
return `${albumType} · ${new Date(item.publishTime).getFullYear()}`; return `${albumType} · ${new Date(item.publishTime).getFullYear()}`;
} }
if (this.subText === "appleMusic") return "by Apple Music"; if (this.subText === 'appleMusic') return 'by Apple Music';
}, },
isPrivacy(item) { isPrivacy(item) {
return this.type === "playlist" && item.privacy === 10; return this.type === 'playlist' && item.privacy === 10;
}, },
isExplicit(item) { isExplicit(item) {
return this.type === "album" && item.mark === 1056768; return this.type === 'album' && item.mark === 1056768;
}, },
getTitleLink(item) { getTitleLink(item) {
return `/${this.type}/${item.id}`; return `/${this.type}/${item.id}`;
}, },
getImageUrl(item) { getImageUrl(item) {
if (item.img1v1Url) { if (item.img1v1Url) {
let img1v1ID = item.img1v1Url.split("/"); let img1v1ID = item.img1v1Url.split('/');
img1v1ID = img1v1ID[img1v1ID.length - 1]; img1v1ID = img1v1ID[img1v1ID.length - 1];
if (img1v1ID === "5639395138885805.jpg") { if (img1v1ID === '5639395138885805.jpg') {
// img1v1Url 😅😅😅 // img1v1Url 😅😅😅
return "https://p2.music.126.net/VnZiScyynLG7atLIZ2YPkw==/18686200114669622.jpg?param=512y512"; return 'https://p2.music.126.net/VnZiScyynLG7atLIZ2YPkw==/18686200114669622.jpg?param=512y512';
} }
} }
let img = item.img1v1Url || item.picUrl || item.coverImgUrl; let img = item.img1v1Url || item.picUrl || item.coverImgUrl;

@ -18,24 +18,24 @@
</template> </template>
<script> <script>
import { mapMutations, mapState, mapActions } from "vuex"; import { mapMutations, mapState, mapActions } from 'vuex';
import { dailyRecommendTracks } from "@/api/playlist"; import { dailyRecommendTracks } from '@/api/playlist';
import { isAccountLoggedIn } from "@/utils/auth"; import { isAccountLoggedIn } from '@/utils/auth';
import sample from "lodash/sample"; import sample from 'lodash/sample';
const defaultCovers = [ const defaultCovers = [
"https://p2.music.126.net/0-Ybpa8FrDfRgKYCTJD8Xg==/109951164796696795.jpg", 'https://p2.music.126.net/0-Ybpa8FrDfRgKYCTJD8Xg==/109951164796696795.jpg',
"https://p2.music.126.net/QxJA2mr4hhb9DZyucIOIQw==/109951165422200291.jpg", 'https://p2.music.126.net/QxJA2mr4hhb9DZyucIOIQw==/109951165422200291.jpg',
"https://p1.music.126.net/AhYP9TET8l-VSGOpWAKZXw==/109951165134386387.jpg", 'https://p1.music.126.net/AhYP9TET8l-VSGOpWAKZXw==/109951165134386387.jpg',
]; ];
export default { export default {
name: "DailyTracksCard", name: 'DailyTracksCard',
data() { data() {
return { useAnimation: false }; return { useAnimation: false };
}, },
computed: { computed: {
...mapState(["dailyTracks"]), ...mapState(['dailyTracks']),
coverUrl() { coverUrl() {
return `${ return `${
this.dailyTracks[0]?.al.picUrl || sample(defaultCovers) this.dailyTracks[0]?.al.picUrl || sample(defaultCovers)
@ -46,29 +46,29 @@ export default {
if (this.dailyTracks.length === 0) this.loadDailyTracks(); if (this.dailyTracks.length === 0) this.loadDailyTracks();
}, },
methods: { methods: {
...mapActions(["showToast"]), ...mapActions(['showToast']),
...mapMutations(["updateDailyTracks"]), ...mapMutations(['updateDailyTracks']),
loadDailyTracks() { loadDailyTracks() {
if (!isAccountLoggedIn()) return; if (!isAccountLoggedIn()) return;
dailyRecommendTracks() dailyRecommendTracks()
.then((result) => { .then(result => {
this.updateDailyTracks(result.data.dailySongs); this.updateDailyTracks(result.data.dailySongs);
}) })
.catch(() => {}); .catch(() => {});
}, },
goToDailyTracks() { goToDailyTracks() {
this.$router.push({ name: "dailySongs" }); this.$router.push({ name: 'dailySongs' });
}, },
playDailyTracks() { playDailyTracks() {
if (!isAccountLoggedIn()) { if (!isAccountLoggedIn()) {
this.showToast("此操作需要登录网易云账号"); this.showToast('此操作需要登录网易云账号');
return; return;
} }
let trackIDs = this.dailyTracks.map((t) => t.id); let trackIDs = this.dailyTracks.map(t => t.id);
this.$store.state.player.replacePlaylist( this.$store.state.player.replacePlaylist(
trackIDs, trackIDs,
"/daily/songs", '/daily/songs',
"url", 'url',
this.dailyTracks[0].id this.dailyTracks[0].id
); );
}, },

@ -3,10 +3,10 @@
</template> </template>
<script> <script>
import SvgIcon from "@/components/SvgIcon.vue"; import SvgIcon from '@/components/SvgIcon.vue';
export default { export default {
name: "ExplicitSymbol", name: 'ExplicitSymbol',
components: { components: {
SvgIcon, SvgIcon,
}, },
@ -23,8 +23,8 @@ export default {
}, },
created() { created() {
this.svgStyle = { this.svgStyle = {
height: this.size + "px", height: this.size + 'px',
width: this.size + "px", width: this.size + 'px',
}; };
}, },
}; };

@ -12,17 +12,17 @@
</div> </div>
<div class="controls"> <div class="controls">
<div class="buttons"> <div class="buttons">
<button-icon @click.native="moveToFMTrash" title="不喜欢" <button-icon title="不喜欢" @click.native="moveToFMTrash"
><svg-icon icon-class="thumbs-down" id="thumbs-down" ><svg-icon id="thumbs-down" icon-class="thumbs-down"
/></button-icon> /></button-icon>
<button-icon <button-icon
:title="$t(isPlaying ? 'player.pause' : 'player.play')"
class="play" class="play"
@click.native="play" @click.native="play"
:title="$t(isPlaying ? 'player.pause' : 'player.play')"
> >
<svg-icon :iconClass="isPlaying ? 'pause' : 'play'" <svg-icon :icon-class="isPlaying ? 'pause' : 'play'"
/></button-icon> /></button-icon>
<button-icon @click.native="next" :title="$t('player.next')" <button-icon :title="$t('player.next')" @click.native="next"
><svg-icon icon-class="next" /></button-icon ><svg-icon icon-class="next" /></button-icon
></div> ></div>
<div class="card-name"><svg-icon icon-class="fm" />私人FM</div> <div class="card-name"><svg-icon icon-class="fm" />私人FM</div>
@ -32,15 +32,15 @@
</template> </template>
<script> <script>
import ButtonIcon from "@/components/ButtonIcon.vue"; import ButtonIcon from '@/components/ButtonIcon.vue';
import ArtistsInLine from "@/components/ArtistsInLine.vue"; import ArtistsInLine from '@/components/ArtistsInLine.vue';
import { mapState } from "vuex"; import { mapState } from 'vuex';
export default { export default {
name: "FMCard", name: 'FMCard',
components: { ButtonIcon, ArtistsInLine }, components: { ButtonIcon, ArtistsInLine },
computed: { computed: {
...mapState(["player"]), ...mapState(['player']),
track() { track() {
return this.player.personalFMTrack; return this.player.personalFMTrack;
}, },
@ -60,7 +60,7 @@ export default {
}, },
goToAlbum() { goToAlbum() {
if (this.track.album.id === 0) return; if (this.track.album.id === 0) return;
this.$router.push({ path: "/album/" + this.track.album.id }); this.$router.push({ path: '/album/' + this.track.album.id });
}, },
moveToFMTrash() { moveToFMTrash() {
this.player.moveToFMTrash(); this.player.moveToFMTrash();

@ -1,6 +1,6 @@
<template> <template>
<div class="shade" @click="clickOutside" v-show="show"> <div v-show="show" class="shade" @click="clickOutside">
<div class="modal" @click.stop :style="modalStyles"> <div class="modal" :style="modalStyles" @click.stop>
<div class="header"> <div class="header">
<div class="title">{{ title }}</div> <div class="title">{{ title }}</div>
<button class="close" @click="close" <button class="close" @click="close"
@ -8,7 +8,7 @@
/></button> /></button>
</div> </div>
<div class="content"><slot></slot></div> <div class="content"><slot></slot></div>
<div class="footer" v-if="showFooter"> <div v-if="showFooter" class="footer">
<!-- <button>取消</button> <!-- <button>取消</button>
<button class="primary">确定</button> --> <button class="primary">确定</button> -->
<slot name="footer"></slot> <slot name="footer"></slot>
@ -19,13 +19,13 @@
<script> <script>
export default { export default {
name: "Modal", name: 'Modal',
props: { props: {
show: Boolean, show: Boolean,
close: Function, close: Function,
title: { title: {
type: String, type: String,
default: "Title", default: 'Title',
}, },
showFooter: { showFooter: {
type: Boolean, type: Boolean,
@ -33,7 +33,7 @@ export default {
}, },
width: { width: {
type: String, type: String,
default: "50vw", default: '50vw',
}, },
clickOutsideHide: { clickOutsideHide: {
type: Boolean, type: Boolean,
@ -173,7 +173,7 @@ export default {
} }
} }
[data-theme="dark"] { [data-theme='dark'] {
.shade { .shade {
background: rgba(0, 0, 0, 0.38); background: rgba(0, 0, 0, 0.38);
color: var(--color-text); color: var(--color-text);

@ -3,7 +3,7 @@
class="add-track-to-playlist-modal" class="add-track-to-playlist-modal"
:show="show" :show="show"
:close="close" :close="close"
:showFooter="false" :show-footer="false"
title="添加到歌单" title="添加到歌单"
width="25vw" width="25vw"
> >
@ -12,9 +12,9 @@
><svg-icon icon-class="plus" />新建歌单</div ><svg-icon icon-class="plus" />新建歌单</div
> >
<div <div
class="playlist"
v-for="playlist in ownPlaylists" v-for="playlist in ownPlaylists"
:key="playlist.id" :key="playlist.id"
class="playlist"
@click="addTrackToPlaylist(playlist.id)" @click="addTrackToPlaylist(playlist.id)"
> >
<img :src="playlist.coverImgUrl | resizeImage(224)" /> <img :src="playlist.coverImgUrl | resizeImage(224)" />
@ -28,13 +28,13 @@
</template> </template>
<script> <script>
import { mapActions, mapMutations, mapState } from "vuex"; import { mapActions, mapMutations, mapState } from 'vuex';
import Modal from "@/components/Modal.vue"; import Modal from '@/components/Modal.vue';
import { userPlaylist } from "@/api/user"; import { userPlaylist } from '@/api/user';
import { addOrRemoveTrackFromPlaylist } from "@/api/playlist"; import { addOrRemoveTrackFromPlaylist } from '@/api/playlist';
export default { export default {
name: "ModalAddTrackToPlaylist", name: 'ModalAddTrackToPlaylist',
components: { components: {
Modal, Modal,
}, },
@ -44,22 +44,22 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["modals", "data"]), ...mapState(['modals', 'data']),
show: { show: {
get() { get() {
return this.modals.addTrackToPlaylistModal.show; return this.modals.addTrackToPlaylistModal.show;
}, },
set(value) { set(value) {
this.updateModal({ this.updateModal({
modalName: "addTrackToPlaylistModal", modalName: 'addTrackToPlaylistModal',
key: "show", key: 'show',
value, value,
}); });
}, },
}, },
ownPlaylists() { ownPlaylists() {
return this.playlists.filter( return this.playlists.filter(
(p) => p =>
p.creator.userId === this.data.user.userId && p.creator.userId === this.data.user.userId &&
p.id !== this.data.likedSongPlaylistID p.id !== this.data.likedSongPlaylistID
); );
@ -69,8 +69,8 @@ export default {
this.getUserPlaylists(); this.getUserPlaylists();
}, },
methods: { methods: {
...mapMutations(["updateModal"]), ...mapMutations(['updateModal']),
...mapActions(["showToast"]), ...mapActions(['showToast']),
close() { close() {
this.show = false; this.show = false;
}, },
@ -79,19 +79,19 @@ export default {
timestamp: new Date().getTime(), timestamp: new Date().getTime(),
limit: 1000, limit: 1000,
uid: this.data.user.userId, uid: this.data.user.userId,
}).then((data) => { }).then(data => {
this.playlists = data.playlist; this.playlists = data.playlist;
}); });
}, },
addTrackToPlaylist(playlistID) { addTrackToPlaylist(playlistID) {
addOrRemoveTrackFromPlaylist({ addOrRemoveTrackFromPlaylist({
op: "add", op: 'add',
pid: playlistID, pid: playlistID,
tracks: this.modals.addTrackToPlaylistModal.selectedTrackID, tracks: this.modals.addTrackToPlaylistModal.selectedTrackID,
}).then((data) => { }).then(data => {
if (data.body.code === 200) { if (data.body.code === 200) {
this.show = false; this.show = false;
this.showToast("已添加到歌单"); this.showToast('已添加到歌单');
} else { } else {
this.showToast(data.body.message); this.showToast(data.body.message);
} }
@ -99,14 +99,14 @@ export default {
}, },
newPlaylist() { newPlaylist() {
this.updateModal({ this.updateModal({
modalName: "newPlaylistModal", modalName: 'newPlaylistModal',
key: "afterCreateAddTrackID", key: 'afterCreateAddTrackID',
value: this.modals.addTrackToPlaylistModal.selectedTrackID, value: this.modals.addTrackToPlaylistModal.selectedTrackID,
}); });
this.close(); this.close();
this.updateModal({ this.updateModal({
modalName: "newPlaylistModal", modalName: 'newPlaylistModal',
key: "show", key: 'show',
value: true, value: true,
}); });
}, },

@ -8,16 +8,16 @@
> >
<template slot="default"> <template slot="default">
<input <input
v-model="title"
type="text" type="text"
placeholder="歌单标题" placeholder="歌单标题"
maxlength="40" maxlength="40"
v-model="title"
/> />
<div class="checkbox"> <div class="checkbox">
<input <input
type="checkbox"
id="checkbox-private" id="checkbox-private"
v-model="privatePlaylist" v-model="privatePlaylist"
type="checkbox"
/> />
<label for="checkbox-private">设置为隐私歌单</label> <label for="checkbox-private">设置为隐私歌单</label>
</div> </div>
@ -29,57 +29,57 @@
</template> </template>
<script> <script>
import Modal from "@/components/Modal.vue"; import Modal from '@/components/Modal.vue';
import { mapMutations, mapState } from "vuex"; import { mapMutations, mapState } from 'vuex';
import { createPlaylist, addOrRemoveTrackFromPlaylist } from "@/api/playlist"; import { createPlaylist, addOrRemoveTrackFromPlaylist } from '@/api/playlist';
export default { export default {
name: "ModalNewPlaylist", name: 'ModalNewPlaylist',
components: { components: {
Modal, Modal,
}, },
data() { data() {
return { return {
title: "", title: '',
privatePlaylist: false, privatePlaylist: false,
}; };
}, },
computed: { computed: {
...mapState(["modals"]), ...mapState(['modals']),
show: { show: {
get() { get() {
return this.modals.newPlaylistModal.show; return this.modals.newPlaylistModal.show;
}, },
set(value) { set(value) {
this.updateModal({ this.updateModal({
modalName: "newPlaylistModal", modalName: 'newPlaylistModal',
key: "show", key: 'show',
value, value,
}); });
}, },
}, },
}, },
methods: { methods: {
...mapMutations(["updateModal"]), ...mapMutations(['updateModal']),
close() { close() {
this.show = false; this.show = false;
this.title = ""; this.title = '';
this.privatePlaylist = false; this.privatePlaylist = false;
this.resetAfterCreateAddTrackID(); this.resetAfterCreateAddTrackID();
}, },
createPlaylist() { createPlaylist() {
let params = { name: this.title }; let params = { name: this.title };
if (this.private) params.type = 10; if (this.private) params.type = 10;
createPlaylist(params).then((data) => { createPlaylist(params).then(data => {
if (data.code === 200) { if (data.code === 200) {
if (this.modals.newPlaylistModal.afterCreateAddTrackID !== 0) { if (this.modals.newPlaylistModal.afterCreateAddTrackID !== 0) {
addOrRemoveTrackFromPlaylist({ addOrRemoveTrackFromPlaylist({
op: "add", op: 'add',
pid: data.id, pid: data.id,
tracks: this.modals.newPlaylistModal.afterCreateAddTrackID, tracks: this.modals.newPlaylistModal.afterCreateAddTrackID,
}).then((data) => { }).then(data => {
if (data.body.code === 200) { if (data.body.code === 200) {
this.showToast("已添加到歌单"); this.showToast('已添加到歌单');
} else { } else {
this.showToast(data.body.message); this.showToast(data.body.message);
} }
@ -87,14 +87,14 @@ export default {
}); });
} }
this.close(); this.close();
this.showToast("成功创建歌单"); this.showToast('成功创建歌单');
} }
}); });
}, },
resetAfterCreateAddTrackID() { resetAfterCreateAddTrackID() {
this.updateModal({ this.updateModal({
modalName: "newPlaylistModal", modalName: 'newPlaylistModal',
key: "AfterCreateAddTrackID", key: 'AfterCreateAddTrackID',
value: 0, value: 0,
}); });
}, },
@ -110,7 +110,7 @@ export default {
input { input {
margin-bottom: 12px; margin-bottom: 12px;
} }
input[type="text"] { input[type='text'] {
width: calc(100% - 24px); width: calc(100% - 24px);
flex: 1; flex: 1;
background: var(--color-secondary-bg-for-transparent); background: var(--color-secondary-bg-for-transparent);
@ -125,12 +125,12 @@ export default {
background: var(--color-primary-bg-for-transparent); background: var(--color-primary-bg-for-transparent);
opacity: 1; opacity: 1;
} }
[data-theme="light"] &:focus { [data-theme='light'] &:focus {
color: var(--color-primary); color: var(--color-primary);
} }
} }
.checkbox { .checkbox {
input[type="checkbox" i] { input[type='checkbox' i] {
margin: 3px 3px 3px 4px; margin: 3px 3px 3px 4px;
} }
display: flex; display: flex;

@ -1,6 +1,6 @@
<template> <template>
<div class="mv-row" :class="{ 'without-padding': withoutPadding }"> <div class="mv-row" :class="{ 'without-padding': withoutPadding }">
<div class="mv" v-for="mv in mvs" :key="getID(mv)"> <div v-for="mv in mvs" :key="getID(mv)" class="mv">
<div <div
class="cover" class="cover"
@mouseover="hoverVideoID = getID(mv)" @mouseover="hoverVideoID = getID(mv)"
@ -10,8 +10,8 @@
<img :src="getUrl(mv)" /> <img :src="getUrl(mv)" />
<transition name="fade"> <transition name="fade">
<div <div
class="shadow"
v-show="hoverVideoID === getID(mv)" v-show="hoverVideoID === getID(mv)"
class="shadow"
:style="{ background: 'url(' + getUrl(mv) + ')' }" :style="{ background: 'url(' + getUrl(mv) + ')' }"
></div> ></div>
</transition> </transition>
@ -28,12 +28,12 @@
<script> <script>
export default { export default {
name: "CoverVideo", name: 'CoverVideo',
props: { props: {
mvs: Array, mvs: Array,
subtitle: { subtitle: {
type: String, type: String,
default: "artist", default: 'artist',
}, },
withoutPadding: { type: Boolean, default: false }, withoutPadding: { type: Boolean, default: false },
}, },
@ -48,11 +48,11 @@ export default {
if (this.$parent.player !== undefined) { if (this.$parent.player !== undefined) {
query = { autoplay: this.$parent.player.playing }; query = { autoplay: this.$parent.player.playing };
} }
this.$router.push({ path: "/mv/" + id, query }); this.$router.push({ path: '/mv/' + id, query });
}, },
getUrl(mv) { getUrl(mv) {
let url = mv.imgurl16v9 ?? mv.cover ?? mv.coverUrl; let url = mv.imgurl16v9 ?? mv.cover ?? mv.coverUrl;
return url.replace(/^http:/, "https:") + "?param=464y260"; return url.replace(/^http:/, 'https:') + '?param=464y260';
}, },
getID(mv) { getID(mv) {
if (mv.id !== undefined) return mv.id; if (mv.id !== undefined) return mv.id;
@ -63,8 +63,8 @@ export default {
if (mv.title !== undefined) return mv.title; if (mv.title !== undefined) return mv.title;
}, },
getSubtitle(mv) { getSubtitle(mv) {
if (this.subtitle === "artist") { if (this.subtitle === 'artist') {
let artistName = "null"; let artistName = 'null';
let artistID = 0; let artistID = 0;
if (mv.artistName !== undefined) { if (mv.artistName !== undefined) {
artistName = mv.artistName; artistName = mv.artistName;
@ -74,7 +74,7 @@ export default {
artistID = mv.creator[0].userId; artistID = mv.creator[0].userId;
} }
return `<a href="/#/artist/${artistID}">${artistName}</a>`; return `<a href="/#/artist/${artistID}">${artistName}</a>`;
} else if (this.subtitle === "publishTime") { } else if (this.subtitle === 'publishTime') {
return mv.publishTime; return mv.publishTime;
} }
}, },

@ -10,11 +10,11 @@
></div> ></div>
<div <div
class="button max-restore codicon" class="button max-restore codicon"
@click="windowMaxRestore"
:class="{ :class="{
'codicon-chrome-restore': !isWindowMaximized, 'codicon-chrome-restore': !isWindowMaximized,
'codicon-chrome-maximize': isWindowMaximized, 'codicon-chrome-maximize': isWindowMaximized,
}" }"
@click="windowMaxRestore"
></div> ></div>
<div <div
class="button close codicon codicon-chrome-close" class="button close codicon codicon-chrome-close"
@ -32,17 +32,17 @@
</div> </div>
<div class="navigation-links"> <div class="navigation-links">
<router-link to="/" :class="{ active: this.$route.name === 'home' }">{{ <router-link to="/" :class="{ active: this.$route.name === 'home' }">{{
$t("nav.home") $t('nav.home')
}}</router-link> }}</router-link>
<router-link <router-link
to="/explore" to="/explore"
:class="{ active: this.$route.name === 'explore' }" :class="{ active: this.$route.name === 'explore' }"
>{{ $t("nav.explore") }}</router-link >{{ $t('nav.explore') }}</router-link
> >
<router-link <router-link
to="/library" to="/library"
:class="{ active: this.$route.name === 'library' }" :class="{ active: this.$route.name === 'library' }"
>{{ $t("nav.library") }}</router-link >{{ $t('nav.library') }}</router-link
> >
</div> </div>
<div class="right-part"> <div class="right-part">
@ -52,8 +52,8 @@
<div class="input"> <div class="input">
<input <input
ref="searchInput" ref="searchInput"
:placeholder="inputFocus ? '' : $t('nav.search')"
v-model="keywords" v-model="keywords"
:placeholder="inputFocus ? '' : $t('nav.search')"
@keydown.enter="doSearch" @keydown.enter="doSearch"
@focus="inputFocus = true" @focus="inputFocus = true"
@blur="inputFocus = false" @blur="inputFocus = false"
@ -68,43 +68,43 @@
<ContextMenu ref="userProfileMenu"> <ContextMenu ref="userProfileMenu">
<div class="item" @click="toSettings"> <div class="item" @click="toSettings">
<svg-icon icon-class="settings" /> <svg-icon icon-class="settings" />
{{ $t("library.userProfileMenu.settings") }} {{ $t('library.userProfileMenu.settings') }}
</div> </div>
<div class="item" @click="toLogin" v-if="!isLooseLoggedIn"> <div v-if="!isLooseLoggedIn" class="item" @click="toLogin">
<svg-icon icon-class="login" /> <svg-icon icon-class="login" />
{{ $t("login.login") }} {{ $t('login.login') }}
</div> </div>
<div class="item" @click="logout" v-if="isLooseLoggedIn"> <div v-if="isLooseLoggedIn" class="item" @click="logout">
<svg-icon icon-class="logout" /> <svg-icon icon-class="logout" />
{{ $t("library.userProfileMenu.logout") }} {{ $t('library.userProfileMenu.logout') }}
</div> </div>
<hr /> <hr />
<div class="item" @click="toGitHub"> <div class="item" @click="toGitHub">
<svg-icon icon-class="github" /> <svg-icon icon-class="github" />
{{ $t("nav.github") }} {{ $t('nav.github') }}
</div> </div>
</ContextMenu> </ContextMenu>
</div> </div>
</template> </template>
<script> <script>
import { mapState } from "vuex"; import { mapState } from 'vuex';
import { isLooseLoggedIn, doLogout } from "@/utils/auth"; import { isLooseLoggedIn, doLogout } from '@/utils/auth';
// import icons for win32 title bar // import icons for win32 title bar
// icons by https://github.com/microsoft/vscode-codicons // icons by https://github.com/microsoft/vscode-codicons
import "vscode-codicons/dist/codicon.css"; import 'vscode-codicons/dist/codicon.css';
import ContextMenu from "@/components/ContextMenu.vue"; import ContextMenu from '@/components/ContextMenu.vue';
import ButtonIcon from "@/components/ButtonIcon.vue"; import ButtonIcon from '@/components/ButtonIcon.vue';
const electron = const electron =
process.env.IS_ELECTRON === true ? window.require("electron") : null; process.env.IS_ELECTRON === true ? window.require('electron') : null;
const ipcRenderer = const ipcRenderer =
process.env.IS_ELECTRON === true ? electron.ipcRenderer : null; process.env.IS_ELECTRON === true ? electron.ipcRenderer : null;
export default { export default {
name: "Navbar", name: 'Navbar',
components: { components: {
ButtonIcon, ButtonIcon,
ContextMenu, ContextMenu,
@ -112,44 +112,44 @@ export default {
data() { data() {
return { return {
inputFocus: false, inputFocus: false,
langs: ["zh-CN", "en", "tr"], langs: ['zh-CN', 'en', 'tr'],
keywords: "", keywords: '',
isWindowMaximized: false, isWindowMaximized: false,
}; };
}, },
computed: { computed: {
...mapState(["settings", "data"]), ...mapState(['settings', 'data']),
isLooseLoggedIn() { isLooseLoggedIn() {
return isLooseLoggedIn(); return isLooseLoggedIn();
}, },
avatarUrl() { avatarUrl() {
return this.data?.user?.avatarUrl && this.isLooseLoggedIn return this.data?.user?.avatarUrl && this.isLooseLoggedIn
? `${this.data?.user?.avatarUrl}?param=512y512` ? `${this.data?.user?.avatarUrl}?param=512y512`
: "http://s4.music.126.net/style/web2/img/default/default_avatar.jpg?param=60y60"; : 'http://s4.music.126.net/style/web2/img/default/default_avatar.jpg?param=60y60';
}, },
}, },
created() { created() {
if (process.env.IS_ELECTRON === true) { if (process.env.IS_ELECTRON === true) {
ipcRenderer.on("isMaximized", (event, value) => { ipcRenderer.on('isMaximized', (event, value) => {
this.isWindowMaximized = value; this.isWindowMaximized = value;
}); });
} }
}, },
methods: { methods: {
go(where) { go(where) {
if (where === "back") this.$router.go(-1); if (where === 'back') this.$router.go(-1);
else this.$router.go(1); else this.$router.go(1);
}, },
doSearch() { doSearch() {
if (!this.keywords) return; if (!this.keywords) return;
if ( if (
this.$route.name === "search" && this.$route.name === 'search' &&
this.$route.params.keywords === this.keywords this.$route.params.keywords === this.keywords
) { ) {
return; return;
} }
this.$router.push({ this.$router.push({
name: "search", name: 'search',
params: { keywords: this.keywords }, params: { keywords: this.keywords },
}); });
}, },
@ -157,31 +157,31 @@ export default {
this.$refs.userProfileMenu.openMenu(e); this.$refs.userProfileMenu.openMenu(e);
}, },
logout() { logout() {
if (!confirm("确定要退出登录吗?")) return; if (!confirm('确定要退出登录吗?')) return;
doLogout(); doLogout();
this.$router.push({ name: "home" }); this.$router.push({ name: 'home' });
}, },
toSettings() { toSettings() {
this.$router.push({ name: "settings" }); this.$router.push({ name: 'settings' });
}, },
toGitHub() { toGitHub() {
window.open("https://github.com/qier222/YesPlayMusic"); window.open('https://github.com/qier222/YesPlayMusic');
}, },
toLogin() { toLogin() {
if (process.env.IS_ELECTRON === true) { if (process.env.IS_ELECTRON === true) {
this.$router.push({ name: "loginAccount" }); this.$router.push({ name: 'loginAccount' });
} else { } else {
this.$router.push({ name: "login" }); this.$router.push({ name: 'login' });
} }
}, },
windowMinimize() { windowMinimize() {
ipcRenderer.send("minimize"); ipcRenderer.send('minimize');
}, },
windowMaxRestore() { windowMaxRestore() {
ipcRenderer.send("maximizeOrUnmaximize"); ipcRenderer.send('maximizeOrUnmaximize');
}, },
windowClose() { windowClose() {
ipcRenderer.send("close"); ipcRenderer.send('close');
}, },
}, },
}; };
@ -224,7 +224,7 @@ nav {
display: none; display: none;
} }
[data-electron-os="win32"] { [data-electron-os='win32'] {
nav { nav {
padding-top: 20px; padding-top: 20px;
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
@ -244,7 +244,7 @@ nav {
.title { .title {
padding: 8px; padding: 8px;
font-size: 12px; font-size: 12px;
font-family: "Segoe UI", "Microsoft YaHei UI", "Microsoft YaHei", font-family: 'Segoe UI', 'Microsoft YaHei UI', 'Microsoft YaHei',
sans-serif; sans-serif;
} }
.controls { .controls {
@ -278,7 +278,7 @@ nav {
} }
} }
} }
&[data-theme="dark"] .win32-titlebar { &[data-theme='dark'] .win32-titlebar {
--hover: #191919; --hover: #191919;
--active: #333333; --active: #333333;
} }
@ -387,7 +387,7 @@ nav {
} }
} }
[data-theme="dark"] { [data-theme='dark'] {
.search-box { .search-box {
.active { .active {
input, input,

@ -6,7 +6,7 @@
<script> <script>
export default { export default {
name: "SvgIcon", name: 'SvgIcon',
props: { props: {
iconClass: { iconClass: {
type: String, type: String,
@ -14,7 +14,7 @@ export default {
}, },
className: { className: {
type: String, type: String,
default: "", default: '',
}, },
}, },
computed: { computed: {
@ -23,9 +23,9 @@ export default {
}, },
svgClass() { svgClass() {
if (this.className) { if (this.className) {
return "svg-icon " + this.className; return 'svg-icon ' + this.className;
} else { } else {
return "svg-icon"; return 'svg-icon';
} }
}, },
}, },

@ -1,16 +1,16 @@
<template> <template>
<transition name="fade"> <transition name="fade">
<div class="toast" v-show="toast.show">{{ toast.text }}</div> <div v-show="toast.show" class="toast">{{ toast.text }}</div>
</transition> </transition>
</template> </template>
<script> <script>
import { mapState } from "vuex"; import { mapState } from 'vuex';
export default { export default {
name: "Toast", name: 'Toast',
computed: { computed: {
...mapState(["toast"]), ...mapState(['toast']),
}, },
}; };
</script> </script>
@ -33,7 +33,7 @@ export default {
z-index: 100; z-index: 100;
} }
[data-theme="dark"] { [data-theme='dark'] {
.toast { .toast {
background: rgba(46, 46, 46, 0.68); background: rgba(46, 46, 46, 0.68);
backdrop-filter: blur(16px) contrast(120%); backdrop-filter: blur(16px) contrast(120%);

@ -8,12 +8,12 @@
@mouseleave="hover = false" @mouseleave="hover = false"
> >
<img <img
:src="imgUrl"
v-if="!isAlbum" v-if="!isAlbum"
@click="goToAlbum" :src="imgUrl"
:class="{ hover: focus }" :class="{ hover: focus }"
@click="goToAlbum"
/> />
<div class="no" v-if="isAlbum"> <div v-if="isAlbum" class="no">
<button v-show="focus && track.playable && !isPlaying" @click="playTrack"> <button v-show="focus && track.playable && !isPlaying" @click="playTrack">
<svg-icon <svg-icon
icon-class="play" icon-class="play"
@ -34,7 +34,7 @@
<div class="container"> <div class="container">
<div class="title"> <div class="title">
{{ track.name }} {{ track.name }}
<span class="featured" v-if="isAlbum"> <span v-if="isAlbum" class="featured">
<ArtistsInLine <ArtistsInLine
:artists="track.ar" :artists="track.ar"
:exclude="this.$parent.albumObject.artist.name" :exclude="this.$parent.albumObject.artist.name"
@ -44,7 +44,7 @@
><ExplicitSymbol ><ExplicitSymbol
/></span> /></span>
</div> </div>
<div class="artist" v-if="!isAlbum"> <div v-if="!isAlbum" class="artist">
<span <span
v-if="track.mark === 1318912" v-if="track.mark === 1318912"
class="explicit-symbol before-artist" class="explicit-symbol before-artist"
@ -55,11 +55,11 @@
</div> </div>
<div></div> <div></div>
</div> </div>
<div class="album" v-if="!isTracklist && !isAlbum"> <div v-if="!isTracklist && !isAlbum" class="album">
<router-link :to="`/album/${album.id}`">{{ album.name }}</router-link> <router-link :to="`/album/${album.id}`">{{ album.name }}</router-link>
<div></div> <div></div>
</div> </div>
<div class="actions" v-if="!isTracklist"> <div v-if="!isTracklist" class="actions">
<button @click="likeThisSong"> <button @click="likeThisSong">
<svg-icon <svg-icon
icon-class="heart" icon-class="heart"
@ -67,10 +67,10 @@
visibility: focus && !isLiked ? 'visible' : 'hidden', visibility: focus && !isLiked ? 'visible' : 'hidden',
}" }"
></svg-icon> ></svg-icon>
<svg-icon icon-class="heart-solid" v-show="isLiked"></svg-icon> <svg-icon v-show="isLiked" icon-class="heart-solid"></svg-icon>
</button> </button>
</div> </div>
<div class="time" v-if="!isTracklist"> <div v-if="!isTracklist" class="time">
{{ track.dt | formatTime }} {{ track.dt | formatTime }}
</div> </div>
</div> </div>

@ -1,24 +1,24 @@
const { Menu } = require("electron"); const { Menu } = require('electron');
export function createDockMenu(win) { export function createDockMenu(win) {
return Menu.buildFromTemplate([ return Menu.buildFromTemplate([
{ {
label: "Play", label: 'Play',
click() { click() {
win.webContents.send("play"); win.webContents.send('play');
}, },
}, },
{ type: "separator" }, { type: 'separator' },
{ {
label: "Next", label: 'Next',
click() { click() {
win.webContents.send("next"); win.webContents.send('next');
}, },
}, },
{ {
label: "Previous", label: 'Previous',
click() { click() {
win.webContents.send("previous"); win.webContents.send('previous');
}, },
}, },
]); ]);

@ -1,22 +1,22 @@
const { globalShortcut } = require("electron"); const { globalShortcut } = require('electron');
export function registerGlobalShortcut(win) { export function registerGlobalShortcut(win) {
globalShortcut.register("Alt+CommandOrControl+P", () => { globalShortcut.register('Alt+CommandOrControl+P', () => {
win.webContents.send("play"); win.webContents.send('play');
}); });
globalShortcut.register("Alt+CommandOrControl+Right", () => { globalShortcut.register('Alt+CommandOrControl+Right', () => {
win.webContents.send("next"); win.webContents.send('next');
}); });
globalShortcut.register("Alt+CommandOrControl+Left", () => { globalShortcut.register('Alt+CommandOrControl+Left', () => {
win.webContents.send("previous"); win.webContents.send('previous');
}); });
globalShortcut.register("Alt+CommandOrControl+Up", () => { globalShortcut.register('Alt+CommandOrControl+Up', () => {
win.webContents.send("increaseVolume"); win.webContents.send('increaseVolume');
}); });
globalShortcut.register("Alt+CommandOrControl+Down", () => { globalShortcut.register('Alt+CommandOrControl+Down', () => {
win.webContents.send("decreaseVolume"); win.webContents.send('decreaseVolume');
}); });
globalShortcut.register("Alt+CommandOrControl+L", () => { globalShortcut.register('Alt+CommandOrControl+L', () => {
win.webContents.send("like"); win.webContents.send('like');
}); });
} }

@ -1,48 +1,48 @@
import { app, dialog, globalShortcut, ipcMain } from "electron"; import { app, dialog, globalShortcut, ipcMain } from 'electron';
import match from "@njzy/unblockneteasemusic"; import match from '@njzy/unblockneteasemusic';
import { registerGlobalShortcut } from "@/electron/globalShortcut"; import { registerGlobalShortcut } from '@/electron/globalShortcut';
const client = require("discord-rich-presence")("818936529484906596"); const client = require('discord-rich-presence')('818936529484906596');
export function initIpcMain(win, store) { export function initIpcMain(win, store) {
ipcMain.on("unblock-music", (event, track) => { ipcMain.on('unblock-music', (event, track) => {
// 兼容 unblockneteasemusic 所使用的 api 字段 // 兼容 unblockneteasemusic 所使用的 api 字段
track.alias = track.alia || []; track.alias = track.alia || [];
track.duration = track.dt || 0; track.duration = track.dt || 0;
track.album = track.al || []; track.album = track.al || [];
track.artists = track.ar || []; track.artists = track.ar || [];
const matchPromise = match(track.id, ["qq", "kuwo", "migu"], track); const matchPromise = match(track.id, ['qq', 'kuwo', 'migu'], track);
const timeoutPromise = new Promise((_, reject) => { const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => { setTimeout(() => {
reject("timeout"); reject('timeout');
}, 3000); }, 3000);
}); });
Promise.race([matchPromise, timeoutPromise]) Promise.race([matchPromise, timeoutPromise])
.then((res) => { .then(res => {
event.returnValue = res; event.returnValue = res;
}) })
.catch((err) => { .catch(err => {
console.log("unblock music error: ", err); console.log('unblock music error: ', err);
event.returnValue = null; event.returnValue = null;
}); });
}); });
ipcMain.on("close", (e) => { ipcMain.on('close', e => {
if (process.platform == "darwin") { if (process.platform == 'darwin') {
win.hide(); win.hide();
} }
e.preventDefault(); //阻止默认行为 e.preventDefault(); //阻止默认行为
dialog dialog
.showMessageBox({ .showMessageBox({
type: "info", type: 'info',
title: "Information", title: 'Information',
cancelId: 2, cancelId: 2,
defaultId: 0, defaultId: 0,
message: "确定要关闭吗?", message: '确定要关闭吗?',
buttons: ["最小化", "直接退出"], buttons: ['最小化', '直接退出'],
}) })
.then((result) => { .then(result => {
if (result.response == 0) { if (result.response == 0) {
e.preventDefault(); //阻止默认行为 e.preventDefault(); //阻止默认行为
win.minimize(); //调用 最小化实例方法 win.minimize(); //调用 最小化实例方法
@ -52,25 +52,25 @@ export function initIpcMain(win, store) {
app.exit(); //exit()直接关闭客户端不会执行quit(); app.exit(); //exit()直接关闭客户端不会执行quit();
} }
}) })
.catch((err) => { .catch(err => {
console.log(err); console.log(err);
}); });
}); });
ipcMain.on("minimize", () => { ipcMain.on('minimize', () => {
win.minimize(); win.minimize();
}); });
ipcMain.on("maximizeOrUnmaximize", () => { ipcMain.on('maximizeOrUnmaximize', () => {
const isMaximized = win.isMaximized(); const isMaximized = win.isMaximized();
isMaximized ? win.unmaximize() : win.maximize(); isMaximized ? win.unmaximize() : win.maximize();
win.webContents.send("isMaximized", isMaximized); win.webContents.send('isMaximized', isMaximized);
}); });
ipcMain.on("settings", (event, options) => { ipcMain.on('settings', (event, options) => {
store.set("settings", options); store.set('settings', options);
const isRegisterShortcut = globalShortcut.isRegistered( const isRegisterShortcut = globalShortcut.isRegistered(
"Alt+CommandOrControl+P" 'Alt+CommandOrControl+P'
); );
if (options.enableGlobalShortcut) { if (options.enableGlobalShortcut) {
!isRegisterShortcut && registerGlobalShortcut(win); !isRegisterShortcut && registerGlobalShortcut(win);
@ -79,27 +79,27 @@ export function initIpcMain(win, store) {
} }
}); });
ipcMain.on("playDiscordPresence", (event, track) => { ipcMain.on('playDiscordPresence', (event, track) => {
client.updatePresence({ client.updatePresence({
details: track.name + " - " + track.ar.map((ar) => ar.name).join(","), details: track.name + ' - ' + track.ar.map(ar => ar.name).join(','),
state: track.al.name, state: track.al.name,
endTimestamp: Date.now() + track.dt, endTimestamp: Date.now() + track.dt,
largeImageKey: "logo", largeImageKey: 'logo',
largeImageText: "Listening " + track.name, largeImageText: 'Listening ' + track.name,
smallImageKey: "play", smallImageKey: 'play',
smallImageText: "Playing", smallImageText: 'Playing',
instance: true, instance: true,
}); });
}); });
ipcMain.on("pauseDiscordPresence", (event, track) => { ipcMain.on('pauseDiscordPresence', (event, track) => {
client.updatePresence({ client.updatePresence({
details: track.name + " - " + track.ar.map((ar) => ar.name).join(","), details: track.name + ' - ' + track.ar.map(ar => ar.name).join(','),
state: track.al.name, state: track.al.name,
largeImageKey: "logo", largeImageKey: 'logo',
largeImageText: "YesPlayMusic", largeImageText: 'YesPlayMusic',
smallImageKey: "pause", smallImageKey: 'pause',
smallImageText: "Pause", smallImageText: 'Pause',
instance: true, instance: true,
}); });
}); });

@ -1,8 +1,8 @@
const { app, Menu } = require("electron"); const { app, Menu } = require('electron');
// import { autoUpdater } from "electron-updater" // import { autoUpdater } from "electron-updater"
// const version = app.getVersion(); // const version = app.getVersion();
const isMac = process.platform === "darwin"; const isMac = process.platform === 'darwin';
export function createMenu(win) { export function createMenu(win) {
let menu = null; let menu = null;
@ -12,142 +12,142 @@ export function createMenu(win) {
{ {
label: app.name, label: app.name,
submenu: [ submenu: [
{ role: "about" }, { role: 'about' },
{ type: "separator" }, { type: 'separator' },
{ role: "services" }, { role: 'services' },
{ type: "separator" }, { type: 'separator' },
{ type: "separator" }, { type: 'separator' },
{ {
label: "Preferences...", label: 'Preferences...',
accelerator: (() => (isMac ? "CmdOrCtrl+," : "Ctrl+,"))(), accelerator: (() => (isMac ? 'CmdOrCtrl+,' : 'Ctrl+,'))(),
click: () => { click: () => {
win.webContents.send("changeRouteTo", "/settings"); win.webContents.send('changeRouteTo', '/settings');
}, },
role: "preferences", role: 'preferences',
}, },
{ type: "separator" }, { type: 'separator' },
{ role: "hide" }, { role: 'hide' },
{ role: "hideothers" }, { role: 'hideothers' },
{ role: "unhide" }, { role: 'unhide' },
{ type: "separator" }, { type: 'separator' },
{ role: "quit" }, { role: 'quit' },
], ],
}, },
] ]
: []), : []),
{ {
label: "Edit", label: 'Edit',
submenu: [ submenu: [
{ role: "undo" }, { role: 'undo' },
{ role: "redo" }, { role: 'redo' },
{ type: "separator" }, { type: 'separator' },
{ role: "cut" }, { role: 'cut' },
{ role: "copy" }, { role: 'copy' },
{ role: "paste" }, { role: 'paste' },
...(isMac ...(isMac
? [ ? [
{ role: "delete" }, { role: 'delete' },
{ role: "selectAll" }, { role: 'selectAll' },
{ type: "separator" }, { type: 'separator' },
{ {
label: "Speech", label: 'Speech',
submenu: [{ role: "startspeaking" }, { role: "stopspeaking" }], submenu: [{ role: 'startspeaking' }, { role: 'stopspeaking' }],
}, },
] ]
: [{ role: "delete" }, { type: "separator" }, { role: "selectAll" }]), : [{ role: 'delete' }, { type: 'separator' }, { role: 'selectAll' }]),
{ {
label: "Search", label: 'Search',
accelerator: "CmdOrCtrl+F", accelerator: 'CmdOrCtrl+F',
click: () => { click: () => {
win.webContents.send("search"); win.webContents.send('search');
}, },
}, },
], ],
}, },
{ {
label: "Controls", label: 'Controls',
submenu: [ submenu: [
{ {
label: "Play", label: 'Play',
click: () => { click: () => {
win.webContents.send("play"); win.webContents.send('play');
}, },
}, },
{ {
label: "Next", label: 'Next',
accelerator: "CmdOrCtrl+Right", accelerator: 'CmdOrCtrl+Right',
click: () => { click: () => {
win.webContents.send("next"); win.webContents.send('next');
}, },
}, },
{ {
label: "Previous", label: 'Previous',
accelerator: "CmdOrCtrl+Left", accelerator: 'CmdOrCtrl+Left',
click: () => { click: () => {
win.webContents.send("previous"); win.webContents.send('previous');
}, },
}, },
{ {
label: "Increase Volume", label: 'Increase Volume',
accelerator: "CmdOrCtrl+Up", accelerator: 'CmdOrCtrl+Up',
click: () => { click: () => {
win.webContents.send("increaseVolume"); win.webContents.send('increaseVolume');
}, },
}, },
{ {
label: "Decrease Volume", label: 'Decrease Volume',
accelerator: "CmdOrCtrl+Down", accelerator: 'CmdOrCtrl+Down',
click: () => { click: () => {
win.webContents.send("decreaseVolume"); win.webContents.send('decreaseVolume');
}, },
}, },
{ {
label: "Like", label: 'Like',
accelerator: "CmdOrCtrl+L", accelerator: 'CmdOrCtrl+L',
click: () => { click: () => {
win.webContents.send("like"); win.webContents.send('like');
}, },
}, },
{ {
label: "Repeat", label: 'Repeat',
accelerator: "Alt+R", accelerator: 'Alt+R',
click: () => { click: () => {
win.webContents.send("repeat"); win.webContents.send('repeat');
}, },
}, },
{ {
label: "Shuffle", label: 'Shuffle',
accelerator: "Alt+S", accelerator: 'Alt+S',
click: () => { click: () => {
win.webContents.send("shuffle"); win.webContents.send('shuffle');
}, },
}, },
], ],
}, },
{ {
label: "Window", label: 'Window',
submenu: [ submenu: [
{ role: "close" }, { role: 'close' },
{ role: "minimize" }, { role: 'minimize' },
{ role: "zoom" }, { role: 'zoom' },
{ role: "reload" }, { role: 'reload' },
{ role: "forcereload" }, { role: 'forcereload' },
{ role: "toggledevtools" }, { role: 'toggledevtools' },
{ type: "separator" }, { type: 'separator' },
{ role: "togglefullscreen" }, { role: 'togglefullscreen' },
...(isMac ...(isMac
? [ ? [
{ type: "separator" }, { type: 'separator' },
{ role: "front" }, { role: 'front' },
{ type: "separator" }, { type: 'separator' },
{ {
role: "window", role: 'window',
id: "window", id: 'window',
label: "YesPlayMusic", label: 'YesPlayMusic',
type: "checkbox", type: 'checkbox',
checked: true, checked: true,
click: () => { click: () => {
const current = menu.getMenuItemById("window"); const current = menu.getMenuItemById('window');
if (current.checked === false) { if (current.checked === false) {
win.hide(); win.hide();
} else { } else {
@ -156,29 +156,29 @@ export function createMenu(win) {
}, },
}, },
] ]
: [{ role: "close" }]), : [{ role: 'close' }]),
], ],
}, },
{ {
label: "Help", label: 'Help',
submenu: [ submenu: [
{ {
label: "GitHub", label: 'GitHub',
click: async () => { click: async () => {
const { shell } = require("electron"); const { shell } = require('electron');
await shell.openExternal("https://github.com/qier222/YesPlayMusic"); await shell.openExternal('https://github.com/qier222/YesPlayMusic');
}, },
}, },
{ {
label: "Electron", label: 'Electron',
click: async () => { click: async () => {
const { shell } = require("electron"); const { shell } = require('electron');
await shell.openExternal("https://electronjs.org"); await shell.openExternal('https://electronjs.org');
}, },
}, },
{ {
label: "开发者工具", label: '开发者工具',
accelerator: "F12", accelerator: 'F12',
click: () => { click: () => {
win.webContents.openDevTools(); win.webContents.openDevTools();
}, },

@ -1,6 +1,6 @@
const { TouchBar, nativeImage, ipcMain } = require("electron"); const { TouchBar, nativeImage, ipcMain } = require('electron');
const { TouchBarButton, TouchBarSpacer } = TouchBar; const { TouchBarButton, TouchBarSpacer } = TouchBar;
const path = require("path"); const path = require('path');
export function createTouchBar(window) { export function createTouchBar(window) {
const renderer = window.webContents; const renderer = window.webContents;
@ -11,72 +11,72 @@ export function createTouchBar(window) {
function getNativeIcon(name) { function getNativeIcon(name) {
return nativeImage.createFromPath( return nativeImage.createFromPath(
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
path.join(__static, "img/touchbar/", name) path.join(__static, 'img/touchbar/', name)
); );
} }
const previousPage = new TouchBarButton({ const previousPage = new TouchBarButton({
click: () => { click: () => {
renderer.send("routerGo", "back"); renderer.send('routerGo', 'back');
}, },
icon: getNativeIcon("page_prev.png"), icon: getNativeIcon('page_prev.png'),
}); });
const nextPage = new TouchBarButton({ const nextPage = new TouchBarButton({
click: () => { click: () => {
renderer.send("routerGo", "forward"); renderer.send('routerGo', 'forward');
}, },
icon: getNativeIcon("page_next.png"), icon: getNativeIcon('page_next.png'),
}); });
const searchButton = new TouchBarButton({ const searchButton = new TouchBarButton({
click: () => { click: () => {
renderer.send("search"); renderer.send('search');
}, },
icon: getNativeIcon("search.png"), icon: getNativeIcon('search.png'),
}); });
const playButton = new TouchBarButton({ const playButton = new TouchBarButton({
click: () => { click: () => {
renderer.send("play"); renderer.send('play');
}, },
icon: getNativeIcon("play.png"), icon: getNativeIcon('play.png'),
}); });
const previousTrackButton = new TouchBarButton({ const previousTrackButton = new TouchBarButton({
click: () => { click: () => {
renderer.send("previous"); renderer.send('previous');
}, },
icon: getNativeIcon("backward.png"), icon: getNativeIcon('backward.png'),
}); });
const nextTrackButton = new TouchBarButton({ const nextTrackButton = new TouchBarButton({
click: () => { click: () => {
renderer.send("next"); renderer.send('next');
}, },
icon: getNativeIcon("forward.png"), icon: getNativeIcon('forward.png'),
}); });
const likeButton = new TouchBarButton({ const likeButton = new TouchBarButton({
click: () => { click: () => {
renderer.send("like"); renderer.send('like');
}, },
icon: getNativeIcon("like.png"), icon: getNativeIcon('like.png'),
}); });
const nextUpButton = new TouchBarButton({ const nextUpButton = new TouchBarButton({
click: () => { click: () => {
renderer.send("nextUp"); renderer.send('nextUp');
}, },
icon: getNativeIcon("next_up.png"), icon: getNativeIcon('next_up.png'),
}); });
ipcMain.on("player", (e, { playing, likedCurrentTrack }) => { ipcMain.on('player', (e, { playing, likedCurrentTrack }) => {
playButton.icon = playButton.icon =
playing === true ? getNativeIcon("pause.png") : getNativeIcon("play.png"); playing === true ? getNativeIcon('pause.png') : getNativeIcon('play.png');
likeButton.icon = likedCurrentTrack likeButton.icon = likedCurrentTrack
? getNativeIcon("like_fill.png") ? getNativeIcon('like_fill.png')
: getNativeIcon("like.png"); : getNativeIcon('like.png');
}); });
const touchBar = new TouchBar({ const touchBar = new TouchBar({
@ -84,11 +84,11 @@ export function createTouchBar(window) {
previousPage, previousPage,
nextPage, nextPage,
searchButton, searchButton,
new TouchBarSpacer({ size: "flexible" }), new TouchBarSpacer({ size: 'flexible' }),
previousTrackButton, previousTrackButton,
playButton, playButton,
nextTrackButton, nextTrackButton,
new TouchBarSpacer({ size: "flexible" }), new TouchBarSpacer({ size: 'flexible' }),
likeButton, likeButton,
nextUpButton, nextUpButton,
], ],

@ -1,79 +1,79 @@
/* global __static */ /* global __static */
import path from "path"; import path from 'path';
import { app, nativeImage, Tray, Menu } from "electron"; import { app, nativeImage, Tray, Menu } from 'electron';
export function createTray(win) { export function createTray(win) {
let icon = nativeImage let icon = nativeImage
.createFromPath(path.join(__static, "img/icons/menu@88.png")) .createFromPath(path.join(__static, 'img/icons/menu@88.png'))
.resize({ .resize({
height: 20, height: 20,
width: 20, width: 20,
}); });
let tray = new Tray(icon); let tray = new Tray(icon);
tray.setToolTip("YesPlayMusic"); tray.setToolTip('YesPlayMusic');
tray.on("click", () => { tray.on('click', () => {
win.show(); win.show();
}); });
tray.on("right-click", () => { tray.on('right-click', () => {
const contextMenu = Menu.buildFromTemplate([ const contextMenu = Menu.buildFromTemplate([
{ {
label: "播放/暂停", label: '播放/暂停',
icon: nativeImage.createFromPath( icon: nativeImage.createFromPath(
path.join(__static, "img/icons/play.png") path.join(__static, 'img/icons/play.png')
), ),
click: () => { click: () => {
win.webContents.send("play"); win.webContents.send('play');
}, },
}, },
{ {
label: "上一首", label: '上一首',
icon: nativeImage.createFromPath( icon: nativeImage.createFromPath(
path.join(__static, "img/icons/left.png") path.join(__static, 'img/icons/left.png')
), ),
accelerator: "CmdOrCtrl+Left", accelerator: 'CmdOrCtrl+Left',
click: () => { click: () => {
win.webContents.send("previous"); win.webContents.send('previous');
}, },
}, },
{ {
label: "下一首", label: '下一首',
icon: nativeImage.createFromPath( icon: nativeImage.createFromPath(
path.join(__static, "img/icons/right.png") path.join(__static, 'img/icons/right.png')
), ),
accelerator: "CmdOrCtrl+Right", accelerator: 'CmdOrCtrl+Right',
click: () => { click: () => {
win.webContents.send("next"); win.webContents.send('next');
}, },
}, },
{ {
label: "循环播放", label: '循环播放',
icon: nativeImage.createFromPath( icon: nativeImage.createFromPath(
path.join(__static, "img/icons/repeat.png") path.join(__static, 'img/icons/repeat.png')
), ),
accelerator: "Alt+R", accelerator: 'Alt+R',
click: () => { click: () => {
win.webContents.send("repeat"); win.webContents.send('repeat');
}, },
}, },
{ {
label: "加入喜欢", label: '加入喜欢',
icon: nativeImage.createFromPath( icon: nativeImage.createFromPath(
path.join(__static, "img/icons/like.png") path.join(__static, 'img/icons/like.png')
), ),
accelerator: "CmdOrCtrl+L", accelerator: 'CmdOrCtrl+L',
click: () => { click: () => {
win.webContents.send("like"); win.webContents.send('like');
}, },
}, },
{ {
label: "退出", label: '退出',
icon: nativeImage.createFromPath( icon: nativeImage.createFromPath(
path.join(__static, "img/icons/exit.png") path.join(__static, 'img/icons/exit.png')
), ),
accelerator: "CmdOrCtrl+W", accelerator: 'CmdOrCtrl+W',
click: () => { click: () => {
app.exit(); app.exit();
}, },

@ -1,10 +1,10 @@
import Vue from "vue"; import Vue from 'vue';
import VueI18n from "vue-i18n"; import VueI18n from 'vue-i18n';
import store from "@/store"; import store from '@/store';
import en from "./lang/en.js"; import en from './lang/en.js';
import zhCN from "./lang/zh-CN.js"; import zhCN from './lang/zh-CN.js';
import tr from "./lang/tr.js"; import tr from './lang/tr.js';
Vue.use(VueI18n); Vue.use(VueI18n);
@ -12,7 +12,7 @@ const i18n = new VueI18n({
locale: store.state.settings.lang, locale: store.state.settings.lang,
messages: { messages: {
en, en,
"zh-CN": zhCN, 'zh-CN': zhCN,
tr, tr,
}, },
silentTranslationWarn: true, silentTranslationWarn: true,

@ -1,81 +1,81 @@
export default { export default {
common: { common: {
play: "PLAY", play: 'PLAY',
songs: "Songs", songs: 'Songs',
}, },
nav: { nav: {
home: "Home", home: 'Home',
explore: "Explore", explore: 'Explore',
library: "Library", library: 'Library',
search: "Search", search: 'Search',
github: "GitHub Repo", github: 'GitHub Repo',
}, },
footer: { footer: {
settings: "Settings", settings: 'Settings',
}, },
home: { home: {
recommendPlaylist: "Recommended Playlists", recommendPlaylist: 'Recommended Playlists',
recommendArtist: "Recommended Artists", recommendArtist: 'Recommended Artists',
newAlbum: "Latest Albums", newAlbum: 'Latest Albums',
seeMore: "SEE MORE", seeMore: 'SEE MORE',
charts: "Charts", charts: 'Charts',
}, },
library: { library: {
sLibrary: "'s Library", sLibrary: "'s Library",
likedSongs: "Liked Songs", likedSongs: 'Liked Songs',
sLikedSongs: "'s Liked Songs", sLikedSongs: "'s Liked Songs",
playlists: "Playlists", playlists: 'Playlists',
albums: "Albums", albums: 'Albums',
artists: "Artists", artists: 'Artists',
mvs: "MVs", mvs: 'MVs',
newPlayList: "New Playlist", newPlayList: 'New Playlist',
userProfileMenu: { userProfileMenu: {
settings: "Settings", settings: 'Settings',
logout: "Logout", logout: 'Logout',
}, },
}, },
explore: { explore: {
explore: "Explore", explore: 'Explore',
loadMore: "Load More", loadMore: 'Load More',
}, },
artist: { artist: {
latestRelease: "Latest Releases", latestRelease: 'Latest Releases',
popularSongs: "Popular Songs", popularSongs: 'Popular Songs',
showMore: "SHOW MORE", showMore: 'SHOW MORE',
showLess: "SHOW LESS", showLess: 'SHOW LESS',
EPsSingles: "EPs & Singles", EPsSingles: 'EPs & Singles',
albums: "Albums", albums: 'Albums',
withAlbums: "Albums", withAlbums: 'Albums',
artist: "Artist", artist: 'Artist',
videos: "Music Videos", videos: 'Music Videos',
following: "Following", following: 'Following',
follow: "Follow", follow: 'Follow',
}, },
album: { album: {
released: "Released", released: 'Released',
}, },
playlist: { playlist: {
playlist: "Playlists", playlist: 'Playlists',
updatedAt: "Updated at", updatedAt: 'Updated at',
search: "Search in playlist", search: 'Search in playlist',
}, },
login: { login: {
accessToAll: "Access to all data", accessToAll: 'Access to all data',
loginText: "Login to Netease", loginText: 'Login to Netease',
search: "Search account", search: 'Search account',
readonly: "Only access to public data", readonly: 'Only access to public data',
usernameLogin: "Username Login", usernameLogin: 'Username Login',
searchHolder: "Your account username", searchHolder: 'Your account username',
enterTip: "Press 'enter' to search", enterTip: "Press 'enter' to search",
choose: "Choose your account", choose: 'Choose your account',
confirm: "Confirm", confirm: 'Confirm',
countryCode: "Country code", countryCode: 'Country code',
phone: "Phone", phone: 'Phone',
email: "Email address", email: 'Email address',
password: "Password", password: 'Password',
login: "Login", login: 'Login',
loginWithEmail: "Login with Email", loginWithEmail: 'Login with Email',
loginWithPhone: "Login with Phone", loginWithPhone: 'Login with Phone',
notice: `YesPlayMusic promises not to save any of your account information to the cloud.<br /> notice: `YesPlayMusic promises not to save any of your account information to the cloud.<br />
Your password will be MD5 encrypted locally and then transmitted to NetEase Music API.<br /> Your password will be MD5 encrypted locally and then transmitted to NetEase Music API.<br />
YesPlayMusic is not the official website of NetEase Music, please consider carefully before entering account information. You can also go to <a href="https://github.com/qier222/YesPlayMusic">YesPlayMusic's GitHub repository</a> to build and use the self-hosted NetEase Music API.`, YesPlayMusic is not the official website of NetEase Music, please consider carefully before entering account information. You can also go to <a href="https://github.com/qier222/YesPlayMusic">YesPlayMusic's GitHub repository</a> to build and use the self-hosted NetEase Music API.`,
@ -83,85 +83,85 @@ export default {
YesPlayMusic promises not to save any of your account information to the cloud.<br />`, YesPlayMusic promises not to save any of your account information to the cloud.<br />`,
}, },
mv: { mv: {
moreVideo: "More Videos", moreVideo: 'More Videos',
}, },
next: { next: {
nowPlaying: "Now Playing", nowPlaying: 'Now Playing',
nextUp: "Next Up", nextUp: 'Next Up',
}, },
player: { player: {
like: "Like", like: 'Like',
previous: "Previous Song", previous: 'Previous Song',
next: "Next Song", next: 'Next Song',
repeat: "Repeat", repeat: 'Repeat',
repeatTrack: "Repeat Track", repeatTrack: 'Repeat Track',
shuffle: "Shuffle", shuffle: 'Shuffle',
play: "Play", play: 'Play',
pause: "Pause", pause: 'Pause',
mute: "Mute", mute: 'Mute',
nextUp: "Next Up", nextUp: 'Next Up',
}, },
modal: { modal: {
close: "Close", close: 'Close',
}, },
search: { search: {
artist: "Artists", artist: 'Artists',
album: "Albums", album: 'Albums',
song: "Songs", song: 'Songs',
mv: "Music Videos", mv: 'Music Videos',
playlist: "Playlists", playlist: 'Playlists',
noResult: "No Results", noResult: 'No Results',
searchFor: "Search for", searchFor: 'Search for',
}, },
settings: { settings: {
settings: "Settings", settings: 'Settings',
logout: "LOGOUT", logout: 'LOGOUT',
language: "Languages", language: 'Languages',
musicQuality: { musicQuality: {
text: "Music Quality", text: 'Music Quality',
low: "Low", low: 'Low',
medium: "Medium", medium: 'Medium',
high: "High", high: 'High',
lossless: "Lossless", lossless: 'Lossless',
}, },
cacheLimit: { cacheLimit: {
text: "Songs Cache limit", text: 'Songs Cache limit',
none: "None", none: 'None',
}, },
lyricFontSize: { lyricFontSize: {
text: "Lyric Font Size", text: 'Lyric Font Size',
small: "Small", small: 'Small',
medium: "Medium", medium: 'Medium',
large: "Large(Default)", large: 'Large(Default)',
xlarge: "X-Large", xlarge: 'X-Large',
}, },
deviceSelector: "Audio Output Device", deviceSelector: 'Audio Output Device',
permissionRequired: "Microphone Permission Required", permissionRequired: 'Microphone Permission Required',
appearance: { appearance: {
text: "Appearance", text: 'Appearance',
auto: "Auto", auto: 'Auto',
light: "Light", light: 'Light',
dark: "Dark", dark: 'Dark',
}, },
automaticallyCacheSongs: "Automatically cache songs", automaticallyCacheSongs: 'Automatically cache songs',
clearSongsCache: "Clear Songs Cache", clearSongsCache: 'Clear Songs Cache',
cacheCount: "Cached {song} songs ({size})", cacheCount: 'Cached {song} songs ({size})',
showLyricsTranslation: "Show lyrics translation", showLyricsTranslation: 'Show lyrics translation',
showLyricsDynamicBackground: "Show lyrics dynamic background", showLyricsDynamicBackground: 'Show lyrics dynamic background',
minimizeToTray: "Minimize to tray", minimizeToTray: 'Minimize to tray',
showPlaylistsByAppleMusic: "Show playlists by Apple Music", showPlaylistsByAppleMusic: 'Show playlists by Apple Music',
enableDiscordRichPresence: "Enable Discord Rich Presence", enableDiscordRichPresence: 'Enable Discord Rich Presence',
enableGlobalShortcut: "Enable Global Shortcut", enableGlobalShortcut: 'Enable Global Shortcut',
showLibraryDefault: "Show library default", showLibraryDefault: 'Show library default',
}, },
contextMenu: { contextMenu: {
play: "Play", play: 'Play',
playNext: "Play Next", playNext: 'Play Next',
saveToMyLikedSongs: "Save to my Liked Songs", saveToMyLikedSongs: 'Save to my Liked Songs',
removeFromMyLikedSongs: "Remove from my Liked Songs", removeFromMyLikedSongs: 'Remove from my Liked Songs',
}, },
toast: { toast: {
savedToMyLikedSongs: "Saved to my Liked Songs", savedToMyLikedSongs: 'Saved to my Liked Songs',
removedFromMyLikedSongs: "Removed from my Liked Songs", removedFromMyLikedSongs: 'Removed from my Liked Songs',
}, },
}; };

@ -1,81 +1,81 @@
export default { export default {
common: { common: {
play: "OYNAT", play: 'OYNAT',
songs: "Müzikler", songs: 'Müzikler',
}, },
nav: { nav: {
home: "Anasayfa", home: 'Anasayfa',
explore: "Keşfet", explore: 'Keşfet',
library: "Kitaplık", library: 'Kitaplık',
search: "Ara", search: 'Ara',
github: "GitHub Repo", github: 'GitHub Repo',
}, },
footer: { footer: {
settings: "Ayarlar", settings: 'Ayarlar',
}, },
home: { home: {
recommendPlaylist: "Önerilen Çalma Listeier", recommendPlaylist: 'Önerilen Çalma Listeier',
recommendArtist: "Önerilen Sanatçılar", recommendArtist: 'Önerilen Sanatçılar',
newAlbum: "Son Çıkan Albümler", newAlbum: 'Son Çıkan Albümler',
seeMore: "DAHA FAZLASI", seeMore: 'DAHA FAZLASI',
charts: "Listeler", charts: 'Listeler',
}, },
library: { library: {
sLibrary: "'in Kütüphanesi", sLibrary: "'in Kütüphanesi",
likedSongs: "Beğenilen Müzikler", likedSongs: 'Beğenilen Müzikler',
sLikedSongs: "'in Beğendiği Müzikler", sLikedSongs: "'in Beğendiği Müzikler",
playlists: "Çalma Listeleri", playlists: 'Çalma Listeleri',
albums: "Albümler", albums: 'Albümler',
artists: "Sanatçılar", artists: 'Sanatçılar',
mvs: "MVs", mvs: 'MVs',
newPlayList: "Yeni Çalma Listesi", newPlayList: 'Yeni Çalma Listesi',
userProfileMenu: { userProfileMenu: {
settings: "Ayarlar", settings: 'Ayarlar',
logout: ıkış Yap", logout: ıkış Yap',
}, },
}, },
explore: { explore: {
explore: "Keşfet", explore: 'Keşfet',
loadMore: "Daha Fazlası", loadMore: 'Daha Fazlası',
}, },
artist: { artist: {
latestRelease: "Son Çıkanlar", latestRelease: 'Son Çıkanlar',
popularSongs: "Popüler Müzikler", popularSongs: 'Popüler Müzikler',
showMore: "Daha Fazlası", showMore: 'Daha Fazlası',
showLess: "Daha Azı", showLess: 'Daha Azı',
EPsSingles: "EPs & Singles", EPsSingles: 'EPs & Singles',
albums: "Albümler", albums: 'Albümler',
withAlbums: "Albümler", withAlbums: 'Albümler',
artist: "Sanatçı", artist: 'Sanatçı',
videos: "Müzik Videoları", videos: 'Müzik Videoları',
following: "Takip Ediyor", following: 'Takip Ediyor',
follow: "Takip Et", follow: 'Takip Et',
}, },
album: { album: {
released: "Yayınlandı", released: 'Yayınlandı',
}, },
playlist: { playlist: {
playlist: "Çalma Listeleri", playlist: 'Çalma Listeleri',
updatedAt: "Tarihinde Güncellendş", updatedAt: 'Tarihinde Güncellendş',
search: "Çalma Listesinde Ara", search: 'Çalma Listesinde Ara',
}, },
login: { login: {
accessToAll: "Tüm verilere eriş", accessToAll: 'Tüm verilere eriş',
loginText: "Netease'e giriş yap", loginText: "Netease'e giriş yap",
search: "Hesap ara", search: 'Hesap ara',
readonly: "Sadece halka açık verilere erişir", readonly: 'Sadece halka açık verilere erişir',
usernameLogin: "Kullanıcı adı giriş", usernameLogin: 'Kullanıcı adı giriş',
searchHolder: "Hesabının kullanıcı adı", searchHolder: 'Hesabının kullanıcı adı',
enterTip: "Aramak için 'enter'e basınız", enterTip: "Aramak için 'enter'e basınız",
choose: "Hesabını seç", choose: 'Hesabını seç',
confirm: "Onayla", confirm: 'Onayla',
countryCode: "Ülke kodu", countryCode: 'Ülke kodu',
phone: "Telefon", phone: 'Telefon',
email: "Email adresi", email: 'Email adresi',
password: "Şifre", password: 'Şifre',
login: "Giriş Yap", login: 'Giriş Yap',
loginWithEmail: "Email ile giriş yap", loginWithEmail: 'Email ile giriş yap',
loginWithPhone: "Phone ile giriş yap", loginWithPhone: 'Phone ile giriş yap',
notice: `YesPlayMusic hesabınızın hiçbir bilgisini kaydetmeyeceğine dair söz veriyor<br /> notice: `YesPlayMusic hesabınızın hiçbir bilgisini kaydetmeyeceğine dair söz veriyor<br />
Şifren MD5 şifreleme ile yerel olarak şifrelenir ve daha sonra NetEase Müzik API'sine gönderilir<br /> Şifren MD5 şifreleme ile yerel olarak şifrelenir ve daha sonra NetEase Müzik API'sine gönderilir<br />
YesPlayMusic, NetEase Music'in resmi websitesi değildir, lütfen hesap bilgilerinizi girmeden önce dikkatlice düşününüz. Aynı zamanda, Kendi NetEase Musix API'nızı host etmek için <a href="https://github.com/qier222/YesPlayMusic">YesPlayMusic'in GitHub Repo'suna</a> gidebilirsiniz.`, YesPlayMusic, NetEase Music'in resmi websitesi değildir, lütfen hesap bilgilerinizi girmeden önce dikkatlice düşününüz. Aynı zamanda, Kendi NetEase Musix API'nızı host etmek için <a href="https://github.com/qier222/YesPlayMusic">YesPlayMusic'in GitHub Repo'suna</a> gidebilirsiniz.`,
@ -83,84 +83,84 @@ export default {
Şifren MD5 şifreleme ile yerel olarak şifrelenir ve daha sonra NetEase Müzik API'sine gönderilir<br />`, Şifren MD5 şifreleme ile yerel olarak şifrelenir ve daha sonra NetEase Müzik API'sine gönderilir<br />`,
}, },
mv: { mv: {
moreVideo: "Daha Fazla Video", moreVideo: 'Daha Fazla Video',
}, },
next: { next: {
nowPlaying: "Şuan çalıyor", nowPlaying: 'Şuan çalıyor',
nextUp: "Sıradaki", nextUp: 'Sıradaki',
}, },
player: { player: {
like: "Beğen", like: 'Beğen',
previous: "Önceki Müzik", previous: 'Önceki Müzik',
next: "Sonraki Müzik", next: 'Sonraki Müzik',
repeat: "Tekrarla", repeat: 'Tekrarla',
repeatTrack: "Parçayı Tekrarla", repeatTrack: 'Parçayı Tekrarla',
shuffle: "Karıştır", shuffle: 'Karıştır',
play: "Oynat", play: 'Oynat',
pause: "Durdur", pause: 'Durdur',
mute: "Sesi kapat", mute: 'Sesi kapat',
nextUp: "Sıradaki", nextUp: 'Sıradaki',
}, },
modal: { modal: {
close: "Kapat", close: 'Kapat',
}, },
search: { search: {
artist: "Sanatçılar", artist: 'Sanatçılar',
album: "Albümler", album: 'Albümler',
song: "Müzikler", song: 'Müzikler',
mv: "Müzik Videoları", mv: 'Müzik Videoları',
playlist: "Çalma Listeleri", playlist: 'Çalma Listeleri',
noResult: "Sonuç Bulunamadı", noResult: 'Sonuç Bulunamadı',
searchFor: "Search for", searchFor: 'Search for',
}, },
settings: { settings: {
settings: "Ayarlar", settings: 'Ayarlar',
logout: "ÇIKIŞ YAP", logout: 'ÇIKIŞ YAP',
language: "Diller", language: 'Diller',
musicQuality: { musicQuality: {
text: "Müzik Kalitesi", text: 'Müzik Kalitesi',
low: "Düşük", low: 'Düşük',
medium: "Orta", medium: 'Orta',
high: "Yüksek", high: 'Yüksek',
lossless: "Kaliteli", lossless: 'Kaliteli',
}, },
cacheLimit: { cacheLimit: {
text: "Şarkılar Önbellek sınırı", text: 'Şarkılar Önbellek sınırı',
none: "Yok", none: 'Yok',
}, },
lyricFontSize: { lyricFontSize: {
text: "Şarkı Sözleri Yazı Boyutu", text: 'Şarkı Sözleri Yazı Boyutu',
small: "Küçük", small: 'Küçük',
medium: "Orta", medium: 'Orta',
large: "Büyük(Varsayılan)", large: 'Büyük(Varsayılan)',
xlarge: "Çok-Büyük", xlarge: 'Çok-Büyük',
}, },
deviceSelector: "Ses Çıkış Cihazı", deviceSelector: 'Ses Çıkış Cihazı',
permissionRequired: "Mikrofon izni gerekiyor", permissionRequired: 'Mikrofon izni gerekiyor',
appearance: { appearance: {
text: "Görünüş", text: 'Görünüş',
auto: "Otomatik", auto: 'Otomatik',
light: "Aydınlık", light: 'Aydınlık',
dark: "Karanlık", dark: 'Karanlık',
}, },
automaticallyCacheSongs: "Müzikleri otomatik çerezle", automaticallyCacheSongs: 'Müzikleri otomatik çerezle',
clearSongsCache: "Müzik çerezlerini temizle", clearSongsCache: 'Müzik çerezlerini temizle',
cacheCount: "Çerezlenen {song} Müzikler ({size})", cacheCount: 'Çerezlenen {song} Müzikler ({size})',
showLyricsTranslation: "Müzik sözlerinin çevirilerini göster", showLyricsTranslation: 'Müzik sözlerinin çevirilerini göster',
showLyricsDynamicBackground: "Dinamik arkaplanda müzik sözlerini gsöter", showLyricsDynamicBackground: 'Dinamik arkaplanda müzik sözlerini gsöter',
minimizeToTray: "Küçült", minimizeToTray: 'Küçült',
showPlaylistsByAppleMusic: "Apple Music'in Çalma Listelerini Göster", showPlaylistsByAppleMusic: "Apple Music'in Çalma Listelerini Göster",
enableDiscordRichPresence: "Discord gösterimini aktifleştir", enableDiscordRichPresence: 'Discord gösterimini aktifleştir',
showLibraryDefault: "Kitaplık Varsayılanını göster", showLibraryDefault: 'Kitaplık Varsayılanını göster',
}, },
contextMenu: { contextMenu: {
play: "Oynat", play: 'Oynat',
playNext: "Sonrakini Oynat", playNext: 'Sonrakini Oynat',
saveToMyLikedSongs: "Beğendiğim Müziklere Kaydet", saveToMyLikedSongs: 'Beğendiğim Müziklere Kaydet',
removeFromMyLikedMüzikler: "Beğendiğim Müziklerden Kaldır", removeFromMyLikedMüzikler: 'Beğendiğim Müziklerden Kaldır',
}, },
toast: { toast: {
savedToMyLikedSongs: "Beğendiğim Müziklere Kaydet", savedToMyLikedSongs: 'Beğendiğim Müziklere Kaydet',
removedFromMyLikedSongs: "Beğendiğim Müziklerden Kaldır", removedFromMyLikedSongs: 'Beğendiğim Müziklerden Kaldır',
}, },
}; };

@ -1,78 +1,78 @@
export default { export default {
common: { common: {
play: "播放", play: '播放',
songs: "首歌", songs: '首歌',
}, },
nav: { nav: {
home: "首页", home: '首页',
explore: "发现", explore: '发现',
library: "音乐库", library: '音乐库',
search: "搜索", search: '搜索',
github: "GitHub 仓库", github: 'GitHub 仓库',
}, },
home: { home: {
recommendPlaylist: "推荐歌单", recommendPlaylist: '推荐歌单',
recommendArtist: "推荐艺人", recommendArtist: '推荐艺人',
newAlbum: "新专速递", newAlbum: '新专速递',
seeMore: "查看全部", seeMore: '查看全部',
charts: "排行榜", charts: '排行榜',
}, },
library: { library: {
sLibrary: "的音乐库", sLibrary: '的音乐库',
likedSongs: "我喜欢的音乐", likedSongs: '我喜欢的音乐',
sLikedSongs: "喜欢的音乐", sLikedSongs: '喜欢的音乐',
playlists: "歌单", playlists: '歌单',
albums: "专辑", albums: '专辑',
artists: "艺人", artists: '艺人',
mvs: "MV", mvs: 'MV',
newPlayList: "新建歌单", newPlayList: '新建歌单',
userProfileMenu: { userProfileMenu: {
settings: "设置", settings: '设置',
logout: "登出", logout: '登出',
}, },
}, },
explore: { explore: {
explore: "发现", explore: '发现',
loadMore: "加载更多", loadMore: '加载更多',
}, },
artist: { artist: {
latestRelease: "最新发布", latestRelease: '最新发布',
popularSongs: "热门歌曲", popularSongs: '热门歌曲',
showMore: "显示更多", showMore: '显示更多',
showLess: "收起", showLess: '收起',
EPsSingles: "EP和单曲", EPsSingles: 'EP和单曲',
albums: "专辑", albums: '专辑',
withAlbums: "张专辑", withAlbums: '张专辑',
artist: "艺人", artist: '艺人',
videos: "个MV", videos: '个MV',
following: "已关注", following: '已关注',
follow: "关注", follow: '关注',
}, },
album: { album: {
released: "发行于", released: '发行于',
}, },
playlist: { playlist: {
playlist: "歌单", playlist: '歌单',
updatedAt: "最后更新于", updatedAt: '最后更新于',
search: "搜索歌单音乐", search: '搜索歌单音乐',
}, },
login: { login: {
accessToAll: "可访问全部数据", accessToAll: '可访问全部数据',
loginText: "登录网易云账号", loginText: '登录网易云账号',
search: "搜索网易云账号", search: '搜索网易云账号',
readonly: "只能读取账号公开数据", readonly: '只能读取账号公开数据',
usernameLogin: "用户名登录", usernameLogin: '用户名登录',
searchHolder: "请输入你的网易云用户名", searchHolder: '请输入你的网易云用户名',
enterTip: "按 Enter 搜索", enterTip: '按 Enter 搜索',
choose: "在列表中选中你的账号", choose: '在列表中选中你的账号',
confirm: "确认", confirm: '确认',
countryCode: "国际区号", countryCode: '国际区号',
phone: "手机号", phone: '手机号',
email: "邮箱", email: '邮箱',
password: "密码", password: '密码',
login: "登录", login: '登录',
loginWithEmail: "使用邮箱登录", loginWithEmail: '使用邮箱登录',
loginWithPhone: "使用手机号登录", loginWithPhone: '使用手机号登录',
notice: `YesPlayMusic 承诺不会保存你的任何账号信息到云端。<br /> notice: `YesPlayMusic 承诺不会保存你的任何账号信息到云端。<br />
你的密码会在本地进行 MD5 加密后再传输到网易云 API<br /> 你的密码会在本地进行 MD5 加密后再传输到网易云 API<br />
YesPlayMusic 并非网易云官方网站输入账号信息前请慎重考虑 你也可以前往 YesPlayMusic 并非网易云官方网站输入账号信息前请慎重考虑 你也可以前往
@ -84,85 +84,85 @@ export default {
YesPlayMusic 不会传输你的账号数据到任何非网易云音乐官方的服务器<br />`, YesPlayMusic 不会传输你的账号数据到任何非网易云音乐官方的服务器<br />`,
}, },
mv: { mv: {
moreVideo: "更多视频", moreVideo: '更多视频',
}, },
next: { next: {
nowPlaying: "正在播放", nowPlaying: '正在播放',
nextUp: "即将播放", nextUp: '即将播放',
}, },
player: { player: {
like: "喜欢", like: '喜欢',
previous: "上一首", previous: '上一首',
next: "下一首", next: '下一首',
repeat: "循环播放", repeat: '循环播放',
repeatTrack: "单曲循环", repeatTrack: '单曲循环',
shuffle: "随机播放", shuffle: '随机播放',
play: "播放", play: '播放',
pause: "暂停", pause: '暂停',
mute: "静音", mute: '静音',
nextUp: "播放列表", nextUp: '播放列表',
}, },
modal: { modal: {
close: "关闭", close: '关闭',
}, },
search: { search: {
artist: "艺人", artist: '艺人',
album: "专辑", album: '专辑',
song: "歌曲", song: '歌曲',
mv: "视频", mv: '视频',
playlist: "歌单", playlist: '歌单',
noResult: "暂无结果", noResult: '暂无结果',
searchFor: "搜索", searchFor: '搜索',
}, },
settings: { settings: {
settings: "设置", settings: '设置',
logout: "登出", logout: '登出',
language: "语言", language: '语言',
musicQuality: { musicQuality: {
text: "音质选择", text: '音质选择',
low: "普通", low: '普通',
medium: "较高", medium: '较高',
high: "极高", high: '极高',
lossless: "无损", lossless: '无损',
}, },
cacheLimit: { cacheLimit: {
text: "歌曲缓存上限", text: '歌曲缓存上限',
none: "无限制", none: '无限制',
}, },
lyricFontSize: { lyricFontSize: {
text: "歌词字体大小", text: '歌词字体大小',
small: "小", small: '小',
medium: "中", medium: '中',
large: "大(默认)", large: '大(默认)',
xlarge: "超大", xlarge: '超大',
}, },
deviceSelector: "音频输出设备", deviceSelector: '音频输出设备',
permissionRequired: "需要麦克风权限", permissionRequired: '需要麦克风权限',
appearance: { appearance: {
text: "外观", text: '外观',
auto: "自动", auto: '自动',
light: "浅色", light: '浅色',
dark: "深色", dark: '深色',
}, },
automaticallyCacheSongs: "自动缓存歌曲", automaticallyCacheSongs: '自动缓存歌曲',
clearSongsCache: "清除歌曲缓存", clearSongsCache: '清除歌曲缓存',
cacheCount: "已缓存 {song} 首 ({size})", cacheCount: '已缓存 {song} 首 ({size})',
showLyricsTranslation: "显示歌词翻译", showLyricsTranslation: '显示歌词翻译',
showLyricsDynamicBackground: "显示歌词动态背景", showLyricsDynamicBackground: '显示歌词动态背景',
minimizeToTray: "最小化到托盘", minimizeToTray: '最小化到托盘',
showPlaylistsByAppleMusic: "首页显示来自 Apple Music 的歌单", showPlaylistsByAppleMusic: '首页显示来自 Apple Music 的歌单',
enableDiscordRichPresence: "启用 Discord Rich Presence", enableDiscordRichPresence: '启用 Discord Rich Presence',
enableGlobalShortcut: "启用全局快捷键", enableGlobalShortcut: '启用全局快捷键',
showLibraryDefault: "启动后显示音乐库", showLibraryDefault: '启动后显示音乐库',
}, },
contextMenu: { contextMenu: {
play: "播放", play: '播放',
playNext: "下一首播放", playNext: '下一首播放',
saveToMyLikedSongs: "添加到我喜欢的音乐", saveToMyLikedSongs: '添加到我喜欢的音乐',
removeFromMyLikedSongs: "从喜欢的音乐中删除", removeFromMyLikedSongs: '从喜欢的音乐中删除',
}, },
toast: { toast: {
savedToMyLikedSongs: "已添加到我喜欢的音乐", savedToMyLikedSongs: '已添加到我喜欢的音乐',
removedFromMyLikedSongs: "已从喜欢的音乐中删除", removedFromMyLikedSongs: '已从喜欢的音乐中删除',
}, },
}; };

@ -1,33 +1,33 @@
import Vue from "vue"; import Vue from 'vue';
import VueAnalytics from "vue-analytics"; import VueAnalytics from 'vue-analytics';
import App from "./App.vue"; import App from './App.vue';
import router from "./router"; import router from './router';
import store from "./store"; import store from './store';
import i18n from "@/locale"; import i18n from '@/locale';
import "@/assets/icons"; import '@/assets/icons';
import "@/utils/filters"; import '@/utils/filters';
import "./registerServiceWorker"; import './registerServiceWorker';
import { dailyTask } from "@/utils/common"; import { dailyTask } from '@/utils/common';
window.resetApp = () => { window.resetApp = () => {
localStorage.clear(); localStorage.clear();
indexedDB.deleteDatabase("yesplaymusic"); indexedDB.deleteDatabase('yesplaymusic');
document.cookie.split(";").forEach(function (c) { document.cookie.split(';').forEach(function (c) {
document.cookie = c document.cookie = c
.replace(/^ +/, "") .replace(/^ +/, '')
.replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/"); .replace(/=.*/, '=;expires=' + new Date().toUTCString() + ';path=/');
}); });
return "已重置应用请刷新页面按Ctrl/Command + R"; return '已重置应用请刷新页面按Ctrl/Command + R';
}; };
console.log( console.log(
"如出现问题,可尝试在本页输入 %cresetApp()%c 然后按回车重置应用。", '如出现问题,可尝试在本页输入 %cresetApp()%c 然后按回车重置应用。',
"background: #eaeffd;color:#335eea;padding: 4px 6px;border-radius:3px;", 'background: #eaeffd;color:#335eea;padding: 4px 6px;border-radius:3px;',
"background:unset;color:unset;" 'background:unset;color:unset;'
); );
Vue.use(VueAnalytics, { Vue.use(VueAnalytics, {
id: "UA-180189423-1", id: 'UA-180189423-1',
router, router,
}); });
@ -39,5 +39,5 @@ new Vue({
i18n, i18n,
store, store,
router, router,
render: (h) => h(App), render: h => h(App),
}).$mount("#app"); }).$mount('#app');

@ -1,6 +1,6 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
import { register } from "register-service-worker"; import { register } from 'register-service-worker';
register(`${process.env.BASE_URL}service-worker.js`, { register(`${process.env.BASE_URL}service-worker.js`, {
ready() { ready() {
@ -27,6 +27,6 @@ register(`${process.env.BASE_URL}service-worker.js`, {
// ); // );
}, },
error(error) { error(error) {
console.error("Error during service worker registration:", error); console.error('Error during service worker registration:', error);
}, },
}); });

@ -1,135 +1,135 @@
import Vue from "vue"; import Vue from 'vue';
import VueRouter from "vue-router"; import VueRouter from 'vue-router';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import "@/assets/css/nprogress.css"; import '@/assets/css/nprogress.css';
import { isLooseLoggedIn, isAccountLoggedIn } from "@/utils/auth"; import { isLooseLoggedIn, isAccountLoggedIn } from '@/utils/auth';
NProgress.configure({ showSpinner: false, trickleSpeed: 100 }); NProgress.configure({ showSpinner: false, trickleSpeed: 100 });
Vue.use(VueRouter); Vue.use(VueRouter);
const routes = [ const routes = [
{ {
path: "/", path: '/',
name: "home", name: 'home',
component: () => import("@/views/home.vue"), component: () => import('@/views/home.vue'),
meta: { meta: {
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
path: "/login", path: '/login',
name: "login", name: 'login',
component: () => import("@/views/login.vue"), component: () => import('@/views/login.vue'),
}, },
{ {
path: "/login/username", path: '/login/username',
name: "loginUsername", name: 'loginUsername',
component: () => import("@/views/loginUsername.vue"), component: () => import('@/views/loginUsername.vue'),
}, },
{ {
path: "/login/account", path: '/login/account',
name: "loginAccount", name: 'loginAccount',
component: () => import("@/views/loginAccount.vue"), component: () => import('@/views/loginAccount.vue'),
}, },
{ {
path: "/playlist/:id", path: '/playlist/:id',
name: "playlist", name: 'playlist',
component: () => import("@/views/playlist.vue"), component: () => import('@/views/playlist.vue'),
}, },
{ {
path: "/album/:id", path: '/album/:id',
name: "album", name: 'album',
component: () => import("@/views/album.vue"), component: () => import('@/views/album.vue'),
}, },
{ {
path: "/artist/:id", path: '/artist/:id',
name: "artist", name: 'artist',
component: () => import("@/views/artist.vue"), component: () => import('@/views/artist.vue'),
meta: { meta: {
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
path: "/artist/:id/mv", path: '/artist/:id/mv',
name: "artistMV", name: 'artistMV',
component: () => import("@/views/artistMV.vue"), component: () => import('@/views/artistMV.vue'),
meta: { meta: {
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
path: "/mv/:id", path: '/mv/:id',
name: "mv", name: 'mv',
component: () => import("@/views/mv.vue"), component: () => import('@/views/mv.vue'),
}, },
{ {
path: "/next", path: '/next',
name: "next", name: 'next',
component: () => import("@/views/next.vue"), component: () => import('@/views/next.vue'),
meta: { meta: {
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
path: "/search/:keywords?", path: '/search/:keywords?',
name: "search", name: 'search',
component: () => import("@/views/search.vue"), component: () => import('@/views/search.vue'),
meta: { meta: {
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
path: "/search/:keywords/:type", path: '/search/:keywords/:type',
name: "searchType", name: 'searchType',
component: () => import("@/views/searchType.vue"), component: () => import('@/views/searchType.vue'),
}, },
{ {
path: "/new-album", path: '/new-album',
name: "newAlbum", name: 'newAlbum',
component: () => import("@/views/newAlbum.vue"), component: () => import('@/views/newAlbum.vue'),
}, },
{ {
path: "/explore", path: '/explore',
name: "explore", name: 'explore',
component: () => import("@/views/explore.vue"), component: () => import('@/views/explore.vue'),
meta: { meta: {
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
path: "/library", path: '/library',
name: "library", name: 'library',
component: () => import("@/views/library.vue"), component: () => import('@/views/library.vue'),
meta: { meta: {
requireLogin: true, requireLogin: true,
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
path: "/library/liked-songs", path: '/library/liked-songs',
name: "likedSongs", name: 'likedSongs',
component: () => import("@/views/playlist.vue"), component: () => import('@/views/playlist.vue'),
meta: { meta: {
requireLogin: true, requireLogin: true,
}, },
}, },
{ {
path: "/settings", path: '/settings',
name: "settings", name: 'settings',
component: () => import("@/views/settings.vue"), component: () => import('@/views/settings.vue'),
}, },
{ {
path: "/daily/songs", path: '/daily/songs',
name: "dailySongs", name: 'dailySongs',
component: () => import("@/views/dailyTracks.vue"), component: () => import('@/views/dailyTracks.vue'),
meta: { meta: {
requireAccountLogin: true, requireAccountLogin: true,
}, },
}, },
{ {
path: "/lastfm/callback", path: '/lastfm/callback',
name: "lastfmCallback", name: 'lastfmCallback',
component: () => import("@/views/lastfmCallback.vue"), component: () => import('@/views/lastfmCallback.vue'),
}, },
]; ];
const router = new VueRouter({ const router = new VueRouter({
@ -145,7 +145,7 @@ const router = new VueRouter({
const originalPush = VueRouter.prototype.push; const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) { VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch((err) => err); return originalPush.call(this, location).catch(err => err);
}; };
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
@ -154,7 +154,7 @@ router.beforeEach((to, from, next) => {
if (isAccountLoggedIn()) { if (isAccountLoggedIn()) {
next(); next();
} else { } else {
next({ path: "/login/account" }); next({ path: '/login/account' });
} }
} }
if (to.meta.requireLogin) { if (to.meta.requireLogin) {
@ -162,9 +162,9 @@ router.beforeEach((to, from, next) => {
next(); next();
} else { } else {
if (process.env.IS_ELECTRON === true) { if (process.env.IS_ELECTRON === true) {
next({ path: "/login/account" }); next({ path: '/login/account' });
} else { } else {
next({ path: "/login" }); next({ path: '/login' });
} }
} }
} else { } else {
@ -172,10 +172,10 @@ router.beforeEach((to, from, next) => {
} }
}); });
router.afterEach((to) => { router.afterEach(to => {
if ( if (
to.matched.some((record) => !record.meta.keepAlive) && to.matched.some(record => !record.meta.keepAlive) &&
!["settings", "dailySongs", "lastfmCallback"].includes(to.name) !['settings', 'dailySongs', 'lastfmCallback'].includes(to.name)
) { ) {
NProgress.start(); NProgress.start();
} }

@ -1,13 +1,13 @@
import Vue from "vue"; import Vue from 'vue';
import Vuex from "vuex"; import Vuex from 'vuex';
import state from "./state"; import state from './state';
import mutations from "./mutations"; import mutations from './mutations';
import actions from "./actions"; import actions from './actions';
import { changeAppearance } from "@/utils/common"; import { changeAppearance } from '@/utils/common';
import Player from "@/utils/Player"; import Player from '@/utils/Player';
// vuex 自定义插件 // vuex 自定义插件
import saveToLocalStorage from "./plugins/localStorage"; import saveToLocalStorage from './plugins/localStorage';
import { getSendSettingsPlugin } from "./plugins/sendSettings"; import { getSendSettingsPlugin } from './plugins/sendSettings';
Vue.use(Vuex); Vue.use(Vuex);
@ -26,22 +26,22 @@ const options = {
const store = new Vuex.Store(options); const store = new Vuex.Store(options);
if ([undefined, null].includes(store.state.settings.lang)) { if ([undefined, null].includes(store.state.settings.lang)) {
const defaultLang = "en"; const defaultLang = 'en';
const langMapper = new Map() const langMapper = new Map()
.set("zh", "zh-CN") .set('zh', 'zh-CN')
.set("en", "en") .set('en', 'en')
.set("tr", "tr"); .set('tr', 'tr');
store.state.settings.lang = store.state.settings.lang =
langMapper.get(navigator.language.slice(0, 2)) || defaultLang; langMapper.get(navigator.language.slice(0, 2)) || defaultLang;
localStorage.setItem("settings", JSON.stringify(store.state.settings)); localStorage.setItem('settings', JSON.stringify(store.state.settings));
} }
changeAppearance(store.state.settings.appearance); changeAppearance(store.state.settings.appearance);
window window
.matchMedia("(prefers-color-scheme: dark)") .matchMedia('(prefers-color-scheme: dark)')
.addEventListener("change", () => { .addEventListener('change', () => {
if (store.state.settings.appearance === "auto") { if (store.state.settings.appearance === 'auto') {
changeAppearance(store.state.settings.appearance); changeAppearance(store.state.settings.appearance);
} }
}); });
@ -51,7 +51,7 @@ player = new Proxy(player, {
set(target, prop, val) { set(target, prop, val) {
// console.log({ prop, val }); // console.log({ prop, val });
target[prop] = val; target[prop] = val;
if (prop === "_howler") return true; if (prop === '_howler') return true;
target.saveSelfToLocalStorage(); target.saveSelfToLocalStorage();
target.sendSelfToIpcMain(); target.sendSelfToIpcMain();
return true; return true;

@ -1,18 +1,18 @@
import { playlistCategories } from "@/utils/staticData"; import { playlistCategories } from '@/utils/staticData';
console.log("[debug][initLocalStorage.js]"); console.log('[debug][initLocalStorage.js]');
const enabledPlaylistCategories = playlistCategories const enabledPlaylistCategories = playlistCategories
.filter((c) => c.enable) .filter(c => c.enable)
.map((c) => c.name); .map(c => c.name);
let localStorage = { let localStorage = {
player: {}, player: {},
settings: { settings: {
lang: null, lang: null,
appearance: "auto", appearance: 'auto',
musicQuality: 320000, musicQuality: 320000,
lyricFontSize: 28, lyricFontSize: 28,
outputDevice: "default", outputDevice: 'default',
showPlaylistsByAppleMusic: true, showPlaylistsByAppleMusic: true,
enableUnblockNeteaseMusic: true, enableUnblockNeteaseMusic: true,
automaticallyCacheSongs: false, automaticallyCacheSongs: false,

@ -1,7 +1,7 @@
export default (store) => { export default store => {
store.subscribe((mutation, state) => { store.subscribe((mutation, state) => {
// console.log(mutation); // console.log(mutation);
localStorage.setItem("settings", JSON.stringify(state.settings)); localStorage.setItem('settings', JSON.stringify(state.settings));
localStorage.setItem("data", JSON.stringify(state.data)); localStorage.setItem('data', JSON.stringify(state.data));
}); });
}; };

@ -1,11 +1,11 @@
export function getSendSettingsPlugin() { export function getSendSettingsPlugin() {
const electron = window.require("electron"); const electron = window.require('electron');
const ipcRenderer = electron.ipcRenderer; const ipcRenderer = electron.ipcRenderer;
return (store) => { return store => {
store.subscribe((mutation, state) => { store.subscribe((mutation, state) => {
// console.log(mutation); // console.log(mutation);
if (mutation.type !== "updateSettings") return; if (mutation.type !== 'updateSettings') return;
ipcRenderer.send("settings", { ipcRenderer.send('settings', {
minimizeToTray: state.settings.minimizeToTray, minimizeToTray: state.settings.minimizeToTray,
enableGlobalShortcut: state.settings.enableGlobalShortcut, enableGlobalShortcut: state.settings.enableGlobalShortcut,
appearance: state.settings.appearance, appearance: state.settings.appearance,

@ -1,35 +1,35 @@
import Cookies from "js-cookie"; import Cookies from 'js-cookie';
import { logout } from "@/api/auth"; import { logout } from '@/api/auth';
import store from "@/store"; import store from '@/store';
export function doLogout() { export function doLogout() {
logout(); logout();
// 网易云的接口会自动移除该 cookies // 网易云的接口会自动移除该 cookies
// Cookies.remove("MUSIC_U"); // Cookies.remove("MUSIC_U");
// 更新状态仓库中的用户信息 // 更新状态仓库中的用户信息
store.commit("updateData", { key: "user", value: {} }); store.commit('updateData', { key: 'user', value: {} });
// 更新状态仓库中的登录状态 // 更新状态仓库中的登录状态
store.commit("updateData", { key: "loginMode", value: null }); store.commit('updateData', { key: 'loginMode', value: null });
// 更新状态仓库中的喜欢列表 // 更新状态仓库中的喜欢列表
store.commit("updateData", { key: "likedSongPlaylistID", value: undefined }); store.commit('updateData', { key: 'likedSongPlaylistID', value: undefined });
} }
// MUSIC_U 只有在账户登录的情况下才有 // MUSIC_U 只有在账户登录的情况下才有
export function isLoggedIn() { export function isLoggedIn() {
return Cookies.get("MUSIC_U") !== undefined ? true : false; return Cookies.get('MUSIC_U') !== undefined ? true : false;
} }
// 账号登录 // 账号登录
export function isAccountLoggedIn() { export function isAccountLoggedIn() {
return ( return (
Cookies.get("MUSIC_U") !== undefined && Cookies.get('MUSIC_U') !== undefined &&
store.state.data.loginMode === "account" store.state.data.loginMode === 'account'
); );
} }
// 用户名搜索(用户数据为只读) // 用户名搜索(用户数据为只读)
export function isUsernameLoggedIn() { export function isUsernameLoggedIn() {
return store.state.data.loginMode === "username"; return store.state.data.loginMode === 'username';
} }
// 账户登录或者用户名搜索都判断为登录,宽松检查 // 账户登录或者用户名搜索都判断为登录,宽松检查
@ -38,15 +38,15 @@ export function isLooseLoggedIn() {
} }
export function getMusicU(string) { export function getMusicU(string) {
const temp = string.split(";"); const temp = string.split(';');
if (!temp.length) { if (!temp.length) {
return undefined; return undefined;
} }
const MUSIC_U = temp.find((item) => item.includes("MUSIC_U")); const MUSIC_U = temp.find(item => item.includes('MUSIC_U'));
if (MUSIC_U) { if (MUSIC_U) {
return MUSIC_U.split("=")[1]; return MUSIC_U.split('=')[1];
} }
return ""; return '';
} }
export function setMusicU(key, value) { export function setMusicU(key, value) {
@ -54,8 +54,8 @@ export function setMusicU(key, value) {
} }
export function setCookies(string) { export function setCookies(string) {
const cookies = string.split(";;"); const cookies = string.split(';;');
cookies.map((cookie) => { cookies.map(cookie => {
document.cookie = cookie; document.cookie = cookie;
console.log(cookie); console.log(cookie);
}); });

@ -1,13 +1,13 @@
import { isAccountLoggedIn } from "./auth"; import { isAccountLoggedIn } from './auth';
import { refreshCookie } from "@/api/auth"; import { refreshCookie } from '@/api/auth';
import { dailySignin } from "@/api/user"; import { dailySignin } from '@/api/user';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import store from "@/store"; import store from '@/store';
export function isTrackPlayable(track) { export function isTrackPlayable(track) {
let result = { let result = {
playable: true, playable: true,
reason: "", reason: '',
}; };
// cloud storage judgement logic // cloud storage judgement logic
if (isAccountLoggedIn() && track?.privilege?.cs) { if (isAccountLoggedIn() && track?.privilege?.cs) {
@ -18,27 +18,27 @@ export function isTrackPlayable(track) {
result.playable = true; result.playable = true;
} else { } else {
result.playable = false; result.playable = false;
result.reason = "VIP Only"; result.reason = 'VIP Only';
} }
} else if (track.fee === 4 || track.privilege?.fee === 4) { } else if (track.fee === 4 || track.privilege?.fee === 4) {
result.playable = false; result.playable = false;
result.reason = "付费专辑"; result.reason = '付费专辑';
} else if ( } else if (
track.noCopyrightRcmd !== null && track.noCopyrightRcmd !== null &&
track.noCopyrightRcmd !== undefined track.noCopyrightRcmd !== undefined
) { ) {
result.playable = false; result.playable = false;
result.reason = "无版权"; result.reason = '无版权';
} else if (track.privilege?.st < 0 && isAccountLoggedIn()) { } else if (track.privilege?.st < 0 && isAccountLoggedIn()) {
result.playable = false; result.playable = false;
result.reason = "已下架"; result.reason = '已下架';
} }
return result; return result;
} }
export function mapTrackPlayableStatus(tracks, privileges = []) { export function mapTrackPlayableStatus(tracks, privileges = []) {
return tracks.map((t) => { return tracks.map(t => {
const privilege = privileges.find((item) => item.id === t.id) || {}; const privilege = privileges.find(item => item.id === t.id) || {};
if (t.privilege) { if (t.privilege) {
Object.assign(t.privilege, privilege); Object.assign(t.privilege, privilege);
} else { } else {
@ -63,13 +63,13 @@ export function randomNum(minNum, maxNum) {
} }
export function shuffleAList(list) { export function shuffleAList(list) {
let sortsList = list.map((t) => t.sort); let sortsList = list.map(t => t.sort);
for (let i = 1; i < sortsList.length; i++) { for (let i = 1; i < sortsList.length; i++) {
const random = Math.floor(Math.random() * (i + 1)); const random = Math.floor(Math.random() * (i + 1));
[sortsList[i], sortsList[random]] = [sortsList[random], sortsList[i]]; [sortsList[i], sortsList[random]] = [sortsList[random], sortsList[i]];
} }
let newSorts = {}; let newSorts = {};
list.map((track) => { list.map(track => {
newSorts[track.id] = sortsList.pop(); newSorts[track.id] = sortsList.pop();
}); });
return newSorts; return newSorts;
@ -88,8 +88,8 @@ export function throttle(fn, time) {
} }
export function updateHttps(url) { export function updateHttps(url) {
if (!url) return ""; if (!url) return '';
return url.replace(/^http:/, "https:"); return url.replace(/^http:/, 'https:');
} }
export function dailyTask() { export function dailyTask() {
@ -98,9 +98,9 @@ export function dailyTask() {
isAccountLoggedIn() && isAccountLoggedIn() &&
(lastDate === undefined || lastDate !== dayjs().date()) (lastDate === undefined || lastDate !== dayjs().date())
) { ) {
console.log("execute dailyTask"); console.log('execute dailyTask');
store.commit("updateData", { store.commit('updateData', {
key: "lastRefreshCookieDate", key: 'lastRefreshCookieDate',
value: dayjs().date(), value: dayjs().date(),
}); });
refreshCookie(); refreshCookie();
@ -110,85 +110,85 @@ export function dailyTask() {
} }
export function changeAppearance(appearance) { export function changeAppearance(appearance) {
if (appearance === "auto" || appearance === undefined) { if (appearance === 'auto' || appearance === undefined) {
appearance = window.matchMedia("(prefers-color-scheme: dark)").matches appearance = window.matchMedia('(prefers-color-scheme: dark)').matches
? "dark" ? 'dark'
: "light"; : 'light';
} }
document.body.setAttribute("data-theme", appearance); document.body.setAttribute('data-theme', appearance);
document document
.querySelector('meta[name="theme-color"]') .querySelector('meta[name="theme-color"]')
.setAttribute("content", appearance === "dark" ? "#222" : "#fff"); .setAttribute('content', appearance === 'dark' ? '#222' : '#fff');
} }
export function splitSoundtrackAlbumTitle(title) { export function splitSoundtrackAlbumTitle(title) {
let keywords = [ let keywords = [
"Music from the Original Motion Picture Score", 'Music from the Original Motion Picture Score',
"The Original Motion Picture Soundtrack", 'The Original Motion Picture Soundtrack',
"Original MGM Motion Picture Soundtrack", 'Original MGM Motion Picture Soundtrack',
"Complete Original Motion Picture Score", 'Complete Original Motion Picture Score',
"Original Music From The Motion Picture", 'Original Music From The Motion Picture',
"Music From The Disney+ Original Movie", 'Music From The Disney+ Original Movie',
"Original Music From The Netflix Film", 'Original Music From The Netflix Film',
"Original Score to the Motion Picture", 'Original Score to the Motion Picture',
"Original Motion Picture Soundtrack", 'Original Motion Picture Soundtrack',
"Soundtrack from the Motion Picture", 'Soundtrack from the Motion Picture',
"Original Television Soundtrack", 'Original Television Soundtrack',
"Original Motion Picture Score", 'Original Motion Picture Score',
"Music From the Motion Picture", 'Music From the Motion Picture',
"Music From The Motion Picture", 'Music From The Motion Picture',
"Complete Motion Picture Score", 'Complete Motion Picture Score',
"Music from the Motion Picture", 'Music from the Motion Picture',
"Original Videogame Soundtrack", 'Original Videogame Soundtrack',
"La Bande Originale du Film", 'La Bande Originale du Film',
"Music from the Miniseries", 'Music from the Miniseries',
"Bande Originale du Film", 'Bande Originale du Film',
"Die Original Filmmusik", 'Die Original Filmmusik',
"Original Soundtrack", 'Original Soundtrack',
"Complete Score", 'Complete Score',
"Original Score", 'Original Score',
]; ];
for (let keyword of keywords) { for (let keyword of keywords) {
if (title.includes(keyword) === false) continue; if (title.includes(keyword) === false) continue;
return { return {
title: title title: title
.replace(`(${keyword})`, "") .replace(`(${keyword})`, '')
.replace(`: ${keyword}`, "") .replace(`: ${keyword}`, '')
.replace(`[${keyword}]`, "") .replace(`[${keyword}]`, '')
.replace(`- ${keyword}`, "") .replace(`- ${keyword}`, '')
.replace(`${keyword}`, ""), .replace(`${keyword}`, ''),
subtitle: keyword, subtitle: keyword,
}; };
} }
return { return {
title: title, title: title,
subtitle: "", subtitle: '',
}; };
} }
export function splitAlbumTitle(title) { export function splitAlbumTitle(title) {
let keywords = [ let keywords = [
"Bonus Tracks Edition", 'Bonus Tracks Edition',
"Complete Edition", 'Complete Edition',
"Deluxe Edition", 'Deluxe Edition',
"Deluxe Version", 'Deluxe Version',
"Tour Edition", 'Tour Edition',
]; ];
for (let keyword of keywords) { for (let keyword of keywords) {
if (title.includes(keyword) === false) continue; if (title.includes(keyword) === false) continue;
return { return {
title: title title: title
.replace(`(${keyword})`, "") .replace(`(${keyword})`, '')
.replace(`: ${keyword}`, "") .replace(`: ${keyword}`, '')
.replace(`[${keyword}]`, "") .replace(`[${keyword}]`, '')
.replace(`- ${keyword}`, "") .replace(`- ${keyword}`, '')
.replace(`${keyword}`, ""), .replace(`${keyword}`, ''),
subtitle: keyword, subtitle: keyword,
}; };
} }
return { return {
title: title, title: title,
subtitle: "", subtitle: '',
}; };
} }
@ -201,17 +201,17 @@ export function bytesToSize(bytes) {
let lang = store.state.settings.lang; let lang = store.state.settings.lang;
if (bytes < kiloBytes) return bytes + (lang === "en" ? " Bytes" : "字节"); if (bytes < kiloBytes) return bytes + (lang === 'en' ? ' Bytes' : '字节');
else if (bytes < megaBytes) else if (bytes < megaBytes)
return (bytes / kiloBytes).toFixed(decimal) + " KB"; return (bytes / kiloBytes).toFixed(decimal) + ' KB';
else if (bytes < gigaBytes) else if (bytes < gigaBytes)
return (bytes / megaBytes).toFixed(decimal) + " MB"; return (bytes / megaBytes).toFixed(decimal) + ' MB';
else return (bytes / gigaBytes).toFixed(decimal) + " GB"; else return (bytes / gigaBytes).toFixed(decimal) + ' GB';
} }
export function formatTrackTime(value) { export function formatTrackTime(value) {
if (!value) return ""; if (!value) return '';
let min = ~~((value / 60) % 60); let min = ~~((value / 60) % 60);
let sec = (~~(value % 60)).toString().padStart(2, "0"); let sec = (~~(value % 60)).toString().padStart(2, '0');
return `${min}:${sec}`; return `${min}:${sec}`;
} }

@ -1,26 +1,25 @@
import axios from "axios"; import axios from 'axios';
import Dexie from "dexie"; import Dexie from 'dexie';
import store from "@/store"; import store from '@/store';
// import pkg from "../../package.json"; // import pkg from "../../package.json";
const db = new Dexie("yesplaymusic"); const db = new Dexie('yesplaymusic');
db.version(2) db.version(2)
.stores({ .stores({
trackSources: "&id", trackSources: '&id',
}) })
.upgrade((tx) => .upgrade(tx =>
tx tx
.table("trackSources") .table('trackSources')
.toCollection() .toCollection()
.modify( .modify(
(track) => track => !track.createTime && (track.createTime = new Date().getTime())
!track.createTime && (track.createTime = new Date().getTime())
) )
); );
db.version(1).stores({ db.version(1).stores({
trackSources: "&id", trackSources: '&id',
}); });
let tracksCacheBytes = 0; let tracksCacheBytes = 0;
@ -33,7 +32,7 @@ async function deleteExcessCache() {
return; return;
} }
try { try {
const delCache = await db.trackSources.orderBy("createTime").first(); const delCache = await db.trackSources.orderBy('createTime').first();
await db.trackSources.delete(delCache.id); await db.trackSources.delete(delCache.id);
tracksCacheBytes -= delCache.source.byteLength; tracksCacheBytes -= delCache.source.byteLength;
console.debug( console.debug(
@ -41,18 +40,18 @@ async function deleteExcessCache() {
); );
deleteExcessCache(); deleteExcessCache();
} catch (error) { } catch (error) {
console.debug("[debug][db.js] deleteExcessCacheFailed", error); console.debug('[debug][db.js] deleteExcessCacheFailed', error);
} }
} }
export function cacheTrackSource(trackInfo, url, bitRate, from = "netease") { export function cacheTrackSource(trackInfo, url, bitRate, from = 'netease') {
const name = trackInfo.name; const name = trackInfo.name;
const artist = trackInfo.ar[0]?.name || trackInfo.artists[0]?.name; const artist = trackInfo.ar[0]?.name || trackInfo.artists[0]?.name;
return axios return axios
.get(url, { .get(url, {
responseType: "arraybuffer", responseType: 'arraybuffer',
}) })
.then((response) => { .then(response => {
db.trackSources.put({ db.trackSources.put({
id: trackInfo.id, id: trackInfo.id,
source: response.data, source: response.data,
@ -70,7 +69,7 @@ export function cacheTrackSource(trackInfo, url, bitRate, from = "netease") {
} }
export function getTrackSource(id) { export function getTrackSource(id) {
return db.trackSources.get(Number(id)).then((track) => { return db.trackSources.get(Number(id)).then(track => {
if (!track) return null; if (!track) return null;
console.debug( console.debug(
`[debug][db.js] get track from cache 👉 ${track.name} by ${track.artist}` `[debug][db.js] get track from cache 👉 ${track.name} by ${track.artist}`
@ -82,7 +81,7 @@ export function getTrackSource(id) {
export function countDBSize() { export function countDBSize() {
const trackSizes = []; const trackSizes = [];
return db.trackSources return db.trackSources
.each((track) => { .each(track => {
trackSizes.push(track.source.byteLength); trackSizes.push(track.source.byteLength);
}) })
.then(() => { .then(() => {
@ -99,7 +98,7 @@ export function countDBSize() {
} }
export function clearDB() { export function clearDB() {
return new Promise((resolve) => { return new Promise(resolve => {
db.tables.forEach(function (table) { db.tables.forEach(function (table) {
table.clear(); table.clear();
}); });

@ -1,63 +1,63 @@
import Vue from "vue"; import Vue from 'vue';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import duration from "dayjs/plugin/duration"; import duration from 'dayjs/plugin/duration';
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from 'dayjs/plugin/relativeTime';
import locale from "@/locale"; import locale from '@/locale';
Vue.filter("formatTime", (Milliseconds, format = "HH:MM:SS") => { Vue.filter('formatTime', (Milliseconds, format = 'HH:MM:SS') => {
if (!Milliseconds) return ""; if (!Milliseconds) return '';
dayjs.extend(duration); dayjs.extend(duration);
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
let time = dayjs.duration(Milliseconds); let time = dayjs.duration(Milliseconds);
let hours = time.hours().toString(); let hours = time.hours().toString();
let mins = time.minutes().toString(); let mins = time.minutes().toString();
let seconds = time.seconds().toString().padStart(2, "0"); let seconds = time.seconds().toString().padStart(2, '0');
if (format === "HH:MM:SS") { if (format === 'HH:MM:SS') {
return hours !== "0" return hours !== '0'
? `${hours}:${mins.padStart(2, "0")}:${seconds}` ? `${hours}:${mins.padStart(2, '0')}:${seconds}`
: `${mins}:${seconds}`; : `${mins}:${seconds}`;
} else if (format === "Human") { } else if (format === 'Human') {
const hoursUnit = locale.locale === "zh-CN" ? "小时" : "hr"; const hoursUnit = locale.locale === 'zh-CN' ? '小时' : 'hr';
const minitesUnit = locale.locale === "zh-CN" ? "分钟" : "min"; const minitesUnit = locale.locale === 'zh-CN' ? '分钟' : 'min';
return hours !== "0" return hours !== '0'
? `${hours} ${hoursUnit} ${mins} ${minitesUnit}` ? `${hours} ${hoursUnit} ${mins} ${minitesUnit}`
: `${mins} ${minitesUnit}`; : `${mins} ${minitesUnit}`;
} }
}); });
Vue.filter("formatDate", (timestamp, format = "MMM D, YYYY") => { Vue.filter('formatDate', (timestamp, format = 'MMM D, YYYY') => {
if (!timestamp) return ""; if (!timestamp) return '';
if (locale.locale === "zh-CN") format = "YYYY年MM月DD日"; if (locale.locale === 'zh-CN') format = 'YYYY年MM月DD日';
return dayjs(timestamp).format(format); return dayjs(timestamp).format(format);
}); });
Vue.filter("formatAlbumType", (type, album) => { Vue.filter('formatAlbumType', (type, album) => {
if (!type) return ""; if (!type) return '';
if (type === "EP/Single") { if (type === 'EP/Single') {
return album.size === 1 ? "Single" : "EP"; return album.size === 1 ? 'Single' : 'EP';
} else if (type === "Single") { } else if (type === 'Single') {
return "Single"; return 'Single';
} else if (type === "专辑") { } else if (type === '专辑') {
return "Album"; return 'Album';
} else { } else {
return type; return type;
} }
}); });
Vue.filter("resizeImage", (imgUrl, size = 512) => { Vue.filter('resizeImage', (imgUrl, size = 512) => {
if (!imgUrl) return ""; if (!imgUrl) return '';
let httpsImgUrl = imgUrl; let httpsImgUrl = imgUrl;
if (imgUrl.slice(0, 5) !== "https") { if (imgUrl.slice(0, 5) !== 'https') {
httpsImgUrl = "https" + imgUrl.slice(4); httpsImgUrl = 'https' + imgUrl.slice(4);
} }
return `${httpsImgUrl}?param=${size}y${size}`; return `${httpsImgUrl}?param=${size}y${size}`;
}); });
Vue.filter("formatPlayCount", (count) => { Vue.filter('formatPlayCount', count => {
if (!count) return ""; if (!count) return '';
if (locale.locale === "zh-CN") { if (locale.locale === 'zh-CN') {
if (count > 100000000) { if (count > 100000000) {
return `${Math.floor((count / 100000000) * 100) / 100}亿`; // 2.32 亿 return `${Math.floor((count / 100000000) * 100) / 100}亿`; // 2.32 亿
} }
@ -82,7 +82,7 @@ Vue.filter("formatPlayCount", (count) => {
} }
}); });
Vue.filter("toHttps", (url) => { Vue.filter('toHttps', url => {
if (!url) return ""; if (!url) return '';
return url.replace(/^http:/, "https:"); return url.replace(/^http:/, 'https:');
}); });

@ -2,29 +2,29 @@
export function lyricParser(lrc) { export function lyricParser(lrc) {
return { return {
lyric: parseLyric(lrc?.lrc?.lyric || ""), lyric: parseLyric(lrc?.lrc?.lyric || ''),
tlyric: parseLyric(lrc?.tlyric?.lyric || ""), tlyric: parseLyric(lrc?.tlyric?.lyric || ''),
lyricuser: lrc.lyricUser, lyricuser: lrc.lyricUser,
transuser: lrc.transUser, transuser: lrc.transUser,
}; };
} }
export function parseLyric(lrc) { export function parseLyric(lrc) {
const lyrics = lrc.split("\n"); const lyrics = lrc.split('\n');
const lrcObj = []; const lrcObj = [];
for (let i = 0; i < lyrics.length; i++) { for (let i = 0; i < lyrics.length; i++) {
const lyric = decodeURIComponent(lyrics[i]); const lyric = decodeURIComponent(lyrics[i]);
const timeReg = /\[\d*:\d*((\.|:)\d*)*\]/g; const timeReg = /\[\d*:\d*((\.|:)\d*)*\]/g;
const timeRegExpArr = lyric.match(timeReg); const timeRegExpArr = lyric.match(timeReg);
if (!timeRegExpArr) continue; if (!timeRegExpArr) continue;
const content = lyric.replace(timeReg, ""); const content = lyric.replace(timeReg, '');
for (let k = 0, h = timeRegExpArr.length; k < h; k++) { for (let k = 0, h = timeRegExpArr.length; k < h; k++) {
const t = timeRegExpArr[k]; const t = timeRegExpArr[k];
const min = Number(String(t.match(/\[\d*/i)).slice(1)); const min = Number(String(t.match(/\[\d*/i)).slice(1));
const sec = Number(String(t.match(/:\d*/i)).slice(1)); const sec = Number(String(t.match(/:\d*/i)).slice(1));
const ms = Number(t.match(/\d*\]/i)[0].slice(0, 2)) / 100; const ms = Number(t.match(/\d*\]/i)[0].slice(0, 2)) / 100;
const time = min * 60 + sec + ms; const time = min * 60 + sec + ms;
if (content !== "") { if (content !== '') {
lrcObj.push({ time: time, rawTime: timeRegExpArr[0], content }); lrcObj.push({ time: time, rawTime: timeRegExpArr[0], content });
} }
} }

@ -15,11 +15,11 @@ const nativeAlert = (() => {
if (process.env.IS_ELECTRON === true) { if (process.env.IS_ELECTRON === true) {
const { const {
remote: { dialog }, remote: { dialog },
} = require("electron"); } = require('electron');
if (dialog) { if (dialog) {
return (message) => { return message => {
var options = { var options = {
type: "warning", type: 'warning',
detail: message, detail: message,
}; };
dialog.showMessageBoxSync(null, options); dialog.showMessageBoxSync(null, options);

@ -1,41 +1,41 @@
export const byAppleMusic = [ export const byAppleMusic = [
{ {
coverImgUrl: coverImgUrl:
"https://p2.music.126.net/GvYQoflE99eoeGi9jG4Bsw==/109951165375336156.jpg", 'https://p2.music.126.net/GvYQoflE99eoeGi9jG4Bsw==/109951165375336156.jpg',
name: "Happy Hits", name: 'Happy Hits',
id: 5278068783, id: 5278068783,
}, },
{ {
coverImgUrl: coverImgUrl:
"https://p2.music.126.net/5CJeYN35LnzRDsv5Lcs0-Q==/109951165374966765.jpg", 'https://p2.music.126.net/5CJeYN35LnzRDsv5Lcs0-Q==/109951165374966765.jpg',
name: "\u4e2d\u563b\u5408\u74a7", name: '\u4e2d\u563b\u5408\u74a7',
id: 5277771961, id: 5277771961,
}, },
{ {
coverImgUrl: coverImgUrl:
"https://p1.music.126.net/cPaBXr1wZSg86ddl47AK7Q==/109951165375130918.jpg", 'https://p1.music.126.net/cPaBXr1wZSg86ddl47AK7Q==/109951165375130918.jpg',
name: "Heartbreak Pop", name: 'Heartbreak Pop',
id: 5277965913, id: 5277965913,
}, },
{ {
coverImgUrl: coverImgUrl:
"https://p2.music.126.net/FDtX55P2NjccDna-LBj9PA==/109951165375065973.jpg", 'https://p2.music.126.net/FDtX55P2NjccDna-LBj9PA==/109951165375065973.jpg',
name: "Festival Bangers", name: 'Festival Bangers',
id: 5277969451, id: 5277969451,
}, },
{ {
coverImgUrl: coverImgUrl:
"https://p2.music.126.net/hC0q2dGbOWHVfg4nkhIXPg==/109951165374881177.jpg", 'https://p2.music.126.net/hC0q2dGbOWHVfg4nkhIXPg==/109951165374881177.jpg',
name: "Bedtime Beats", name: 'Bedtime Beats',
id: 5277778542, id: 5277778542,
}, },
]; ];
export const playlistCategories = [ export const playlistCategories = [
{ {
name: "全部", name: '全部',
enable: true, enable: true,
bigCat: "static", bigCat: 'static',
}, },
// { // {
// name: "For You", // name: "For You",
@ -43,9 +43,9 @@ export const playlistCategories = [
// bigCat: "static", // bigCat: "static",
// }, // },
{ {
name: "推荐歌单", name: '推荐歌单',
enable: true, enable: true,
bigCat: "static", bigCat: 'static',
}, },
// { // {
// name: "最新专辑", // name: "最新专辑",
@ -53,368 +53,368 @@ export const playlistCategories = [
// bigCat: "static", // bigCat: "static",
// }, // },
{ {
name: "精品歌单", name: '精品歌单',
enable: true, enable: true,
bigCat: "static", bigCat: 'static',
}, },
{ {
name: "官方", name: '官方',
enable: true, enable: true,
bigCat: "static", bigCat: 'static',
}, },
{ {
name: "排行榜", name: '排行榜',
enable: true, enable: true,
bigCat: "static", bigCat: 'static',
}, },
{ {
name: "华语", name: '华语',
enable: false, enable: false,
bigCat: "语种", bigCat: '语种',
}, },
{ {
name: "欧美", name: '欧美',
enable: true, enable: true,
bigCat: "语种", bigCat: '语种',
}, },
{ {
name: "日语", name: '日语',
enable: false, enable: false,
bigCat: "语种", bigCat: '语种',
}, },
{ {
name: "韩语", name: '韩语',
enable: false, enable: false,
bigCat: "语种", bigCat: '语种',
}, },
{ {
name: "粤语", name: '粤语',
enable: false, enable: false,
bigCat: "语种", bigCat: '语种',
}, },
{ {
name: "流行", name: '流行',
enable: true, enable: true,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "摇滚", name: '摇滚',
enable: true, enable: true,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "民谣", name: '民谣',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "电子", name: '电子',
enable: true, enable: true,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "舞曲", name: '舞曲',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "说唱", name: '说唱',
enable: true, enable: true,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "轻音乐", name: '轻音乐',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "爵士", name: '爵士',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "乡村", name: '乡村',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "R&B/Soul", name: 'R&B/Soul',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "古典", name: '古典',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "民族", name: '民族',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "英伦", name: '英伦',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "金属", name: '金属',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "朋克", name: '朋克',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "蓝调", name: '蓝调',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "雷鬼", name: '雷鬼',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "世界音乐", name: '世界音乐',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "拉丁", name: '拉丁',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "New Age", name: 'New Age',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "古风", name: '古风',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "后摇", name: '后摇',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "Bossa Nova", name: 'Bossa Nova',
enable: false, enable: false,
bigCat: "风格", bigCat: '风格',
}, },
{ {
name: "清晨", name: '清晨',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "夜晚", name: '夜晚',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "学习", name: '学习',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "工作", name: '工作',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "午休", name: '午休',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "下午茶", name: '下午茶',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "地铁", name: '地铁',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "驾车", name: '驾车',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "运动", name: '运动',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "旅行", name: '旅行',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "散步", name: '散步',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "酒吧", name: '酒吧',
enable: false, enable: false,
bigCat: "场景", bigCat: '场景',
}, },
{ {
name: "怀旧", name: '怀旧',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "清新", name: '清新',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "浪漫", name: '浪漫',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "伤感", name: '伤感',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "治愈", name: '治愈',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "放松", name: '放松',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "孤独", name: '孤独',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "感动", name: '感动',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "兴奋", name: '兴奋',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "快乐", name: '快乐',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "安静", name: '安静',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "思念", name: '思念',
enable: false, enable: false,
bigCat: "情感", bigCat: '情感',
}, },
{ {
name: "综艺", name: '综艺',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "影视原声", name: '影视原声',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "ACG", name: 'ACG',
enable: true, enable: true,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "儿童", name: '儿童',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "校园", name: '校园',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "游戏", name: '游戏',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "70后", name: '70后',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "80后", name: '80后',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "90后", name: '90后',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "网络歌曲", name: '网络歌曲',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "KTV", name: 'KTV',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "经典", name: '经典',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "翻唱", name: '翻唱',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "吉他", name: '吉他',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "钢琴", name: '钢琴',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "器乐", name: '器乐',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "榜单", name: '榜单',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
{ {
name: "00后", name: '00后',
enable: false, enable: false,
bigCat: "主题", bigCat: '主题',
}, },
]; ];

@ -1,30 +1,30 @@
import initLocalStorage from "@/store/initLocalStorage.js"; import initLocalStorage from '@/store/initLocalStorage.js';
import pkg from "../../package.json"; import pkg from '../../package.json';
const updateSetting = () => { const updateSetting = () => {
const parsedSettings = JSON.parse(localStorage.getItem("settings")); const parsedSettings = JSON.parse(localStorage.getItem('settings'));
const settings = { const settings = {
...initLocalStorage.settings, ...initLocalStorage.settings,
...parsedSettings, ...parsedSettings,
}; };
localStorage.setItem("settings", JSON.stringify(settings)); localStorage.setItem('settings', JSON.stringify(settings));
}; };
const updateData = () => { const updateData = () => {
const parsedData = JSON.parse(localStorage.getItem("data")); const parsedData = JSON.parse(localStorage.getItem('data'));
const data = { const data = {
...parsedData, ...parsedData,
}; };
localStorage.setItem("data", JSON.stringify(data)); localStorage.setItem('data', JSON.stringify(data));
}; };
const updatePlayer = () => { const updatePlayer = () => {
let parsedData = JSON.parse(localStorage.getItem("player")); let parsedData = JSON.parse(localStorage.getItem('player'));
let appVersion = localStorage.getItem("appVersion"); let appVersion = localStorage.getItem('appVersion');
if (appVersion === `"0.2.5"`) parsedData = {}; // 0.2.6版本重构了player if (appVersion === `"0.2.5"`) parsedData = {}; // 0.2.6版本重构了player
const data = { const data = {
_repeatMode: "off", _repeatMode: 'off',
_shuffle: false, _shuffle: false,
_list: [], _list: [],
_current: 0, _current: 0,
@ -38,12 +38,12 @@ const updatePlayer = () => {
_shuffledCurrent: 0, _shuffledCurrent: 0,
...parsedData, ...parsedData,
}; };
localStorage.setItem("player", JSON.stringify(data)); localStorage.setItem('player', JSON.stringify(data));
}; };
const removeOldStuff = () => { const removeOldStuff = () => {
// remove old indexedDB databases created by localforage // remove old indexedDB databases created by localforage
indexedDB.deleteDatabase("tracks"); indexedDB.deleteDatabase('tracks');
}; };
export default function () { export default function () {
@ -51,5 +51,5 @@ export default function () {
updateData(); updateData();
updatePlayer(); updatePlayer();
removeOldStuff(); removeOldStuff();
localStorage.setItem("appVersion", JSON.stringify(pkg.version)); localStorage.setItem('appVersion', JSON.stringify(pkg.version));
} }

@ -1,21 +1,21 @@
<template> <template>
<div class="album" v-show="show"> <div v-show="show" class="album">
<div class="playlist-info"> <div class="playlist-info">
<Cover <Cover
:imageUrl="album.picUrl | resizeImage(1024)"
:showPlayButton="true"
:alwaysShowShadow="true"
:clickCoverToPlay="true"
:fixedSize="288"
type="album"
:id="album.id" :id="album.id"
:coverHover="false" :image-url="album.picUrl | resizeImage(1024)"
:playButtonSize="18" :show-play-button="true"
:always-show-shadow="true"
:click-cover-to-play="true"
:fixed-size="288"
type="album"
:cover-hover="false"
:play-button-size="18"
@click.right.native="openMenu" @click.right.native="openMenu"
/> />
<div class="info"> <div class="info">
<div class="title" @click.right="openMenu"> {{ title }}</div> <div class="title" @click.right="openMenu"> {{ title }}</div>
<div class="subtitle" v-if="subtitle !== ''" @click.right="openMenu">{{ <div v-if="subtitle !== ''" class="subtitle" @click.right="openMenu">{{
subtitle subtitle
}}</div> }}</div>
<div class="artist"> <div class="artist">
@ -28,42 +28,42 @@
<span v-else>Compilation by Various Artists</span> <span v-else>Compilation by Various Artists</span>
</div> </div>
<div class="date-and-count"> <div class="date-and-count">
<span class="explicit-symbol" v-if="album.mark === 1056768" <span v-if="album.mark === 1056768" class="explicit-symbol"
><ExplicitSymbol ><ExplicitSymbol
/></span> /></span>
<span :title="album.publishTime | formatDate">{{ <span :title="album.publishTime | formatDate">{{
new Date(album.publishTime).getFullYear() new Date(album.publishTime).getFullYear()
}}</span> }}</span>
<span> · {{ album.size }} {{ $t("common.songs") }}</span <span> · {{ album.size }} {{ $t('common.songs') }}</span
>, >,
{{ albumTime | formatTime("Human") }} {{ albumTime | formatTime('Human') }}
</div> </div>
<div class="description" @click="showFullDescription = true"> <div class="description" @click="showFullDescription = true">
{{ album.description }} {{ album.description }}
</div> </div>
<div class="buttons" style="margin-top: 32px"> <div class="buttons" style="margin-top: 32px">
<ButtonTwoTone <ButtonTwoTone
icon-class="play"
@click.native="playAlbumByID(album.id)" @click.native="playAlbumByID(album.id)"
iconClass="play"
> >
{{ $t("common.play") }} {{ $t('common.play') }}
</ButtonTwoTone> </ButtonTwoTone>
<ButtonTwoTone <ButtonTwoTone
:iconClass="dynamicDetail.isSub ? 'heart-solid' : 'heart'" :icon-class="dynamicDetail.isSub ? 'heart-solid' : 'heart'"
:iconButton="true" :icon-button="true"
:horizontalPadding="0" :horizontal-padding="0"
:color="dynamicDetail.isSub ? 'blue' : 'grey'" :color="dynamicDetail.isSub ? 'blue' : 'grey'"
:textColor="dynamicDetail.isSub ? '#335eea' : ''" :text-color="dynamicDetail.isSub ? '#335eea' : ''"
:backgroundColor=" :background-color="
dynamicDetail.isSub ? 'var(--color-secondary-bg)' : '' dynamicDetail.isSub ? 'var(--color-secondary-bg)' : ''
" "
@click.native="likeAlbum" @click.native="likeAlbum"
> >
</ButtonTwoTone> </ButtonTwoTone>
<ButtonTwoTone <ButtonTwoTone
iconClass="more" icon-class="more"
:iconButton="true" :icon-button="true"
:horizontalPadding="0" :horizontal-padding="0"
color="grey" color="grey"
@click.native="openMenu" @click.native="openMenu"
> >
@ -72,22 +72,22 @@
</div> </div>
</div> </div>
<TrackList <TrackList
:id="album.id"
:tracks="tracks" :tracks="tracks"
:type="'album'" :type="'album'"
:id="album.id" :album-object="album"
:albumObject="album"
/> />
<div class="extra-info"> <div class="extra-info">
<div class="album-time"></div> <div class="album-time"></div>
<div class="release-date"> <div class="release-date">
{{ $t("album.released") }} {{ $t('album.released') }}
{{ album.publishTime | formatDate("MMMM D, YYYY") }} {{ album.publishTime | formatDate('MMMM D, YYYY') }}
</div> </div>
<div class="copyright" v-if="album.company !== null"> <div v-if="album.company !== null" class="copyright">
© {{ album.company }} © {{ album.company }}
</div> </div>
</div> </div>
<div class="more-by" v-if="filteredMoreAlbums.length !== 0"> <div v-if="filteredMoreAlbums.length !== 0" class="more-by">
<div class="section-title"> <div class="section-title">
More by More by
<router-link :to="`/artist/${album.artist.id}`" <router-link :to="`/artist/${album.artist.id}`"
@ -98,15 +98,15 @@
<CoverRow <CoverRow
type="album" type="album"
:items="filteredMoreAlbums" :items="filteredMoreAlbums"
subText="albumType+releaseYear" sub-text="albumType+releaseYear"
/> />
</div> </div>
</div> </div>
<Modal <Modal
:show="showFullDescription" :show="showFullDescription"
:close="() => (showFullDescription = false)" :close="() => (showFullDescription = false)"
:showFooter="false" :show-footer="false"
:clickOutsideHide="true" :click-outside-hide="true"
title="专辑介绍" title="专辑介绍"
> >
<p class="description-fulltext"> <p class="description-fulltext">
@ -114,9 +114,9 @@
</p> </p>
</Modal> </Modal>
<ContextMenu ref="albumMenu"> <ContextMenu ref="albumMenu">
<div class="item">{{ $t("contextMenu.playNext") }}</div> <div class="item">{{ $t('contextMenu.playNext') }}</div>
<div class="item" @click="likeAlbum(true)">{{ <div class="item" @click="likeAlbum(true)">{{
dynamicDetail.isSub ? "从音乐库删除" : "保存到音乐库" dynamicDetail.isSub ? '从音乐库删除' : '保存到音乐库'
}}</div> }}</div>
<div class="item">添加到歌单</div> <div class="item">添加到歌单</div>
</ContextMenu> </ContextMenu>
@ -124,24 +124,24 @@
</template> </template>
<script> <script>
import { mapMutations, mapActions, mapState } from "vuex"; import { mapMutations, mapActions, mapState } from 'vuex';
import { getArtistAlbum } from "@/api/artist"; import { getArtistAlbum } from '@/api/artist';
import { getTrackDetail } from "@/api/track"; import { getTrackDetail } from '@/api/track';
import { getAlbum, albumDynamicDetail, likeAAlbum } from "@/api/album"; import { getAlbum, albumDynamicDetail, likeAAlbum } from '@/api/album';
import { splitSoundtrackAlbumTitle, splitAlbumTitle } from "@/utils/common"; import { splitSoundtrackAlbumTitle, splitAlbumTitle } from '@/utils/common';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import { isAccountLoggedIn } from "@/utils/auth"; import { isAccountLoggedIn } from '@/utils/auth';
import ExplicitSymbol from "@/components/ExplicitSymbol.vue"; import ExplicitSymbol from '@/components/ExplicitSymbol.vue';
import ButtonTwoTone from "@/components/ButtonTwoTone.vue"; import ButtonTwoTone from '@/components/ButtonTwoTone.vue';
import ContextMenu from "@/components/ContextMenu.vue"; import ContextMenu from '@/components/ContextMenu.vue';
import TrackList from "@/components/TrackList.vue"; import TrackList from '@/components/TrackList.vue';
import CoverRow from "@/components/CoverRow.vue"; import CoverRow from '@/components/CoverRow.vue';
import Cover from "@/components/Cover.vue"; import Cover from '@/components/Cover.vue';
import Modal from "@/components/Modal.vue"; import Modal from '@/components/Modal.vue';
export default { export default {
name: "Album", name: 'Album',
components: { components: {
Cover, Cover,
ButtonTwoTone, ButtonTwoTone,
@ -151,11 +151,16 @@ export default {
Modal, Modal,
ContextMenu, ContextMenu,
}, },
beforeRouteUpdate(to, from, next) {
NProgress.start();
this.loadData(to.params.id);
next();
},
data() { data() {
return { return {
album: { album: {
id: 0, id: 0,
picUrl: "", picUrl: '',
artist: { artist: {
id: 0, id: 0,
}, },
@ -165,27 +170,27 @@ export default {
show: false, show: false,
moreAlbums: [], moreAlbums: [],
dynamicDetail: {}, dynamicDetail: {},
subtitle: "", subtitle: '',
title: "", title: '',
}; };
}, },
computed: { computed: {
...mapState(["player", "data"]), ...mapState(['player', 'data']),
albumTime() { albumTime() {
let time = 0; let time = 0;
this.tracks.map((t) => (time = time + t.dt)); this.tracks.map(t => (time = time + t.dt));
return time; return time;
}, },
filteredMoreAlbums() { filteredMoreAlbums() {
let moreAlbums = this.moreAlbums.filter((a) => a.id !== this.album.id); let moreAlbums = this.moreAlbums.filter(a => a.id !== this.album.id);
let realAlbums = moreAlbums.filter((a) => a.type === "专辑"); let realAlbums = moreAlbums.filter(a => a.type === '专辑');
let eps = moreAlbums.filter( let eps = moreAlbums.filter(
(a) => a.type === "EP" || (a.type === "EP/Single" && a.size > 1) a => a.type === 'EP' || (a.type === 'EP/Single' && a.size > 1)
); );
let restItems = moreAlbums.filter( let restItems = moreAlbums.filter(
(a) => a =>
realAlbums.find((a1) => a1.id === a.id) === undefined && realAlbums.find(a1 => a1.id === a.id) === undefined &&
eps.find((a1) => a1.id === a.id) === undefined eps.find(a1 => a1.id === a.id) === undefined
); );
if (realAlbums.length === 0) { if (realAlbums.length === 0) {
return [...realAlbums, ...eps, ...restItems].slice(0, 5); return [...realAlbums, ...eps, ...restItems].slice(0, 5);
@ -198,31 +203,31 @@ export default {
this.loadData(this.$route.params.id); this.loadData(this.$route.params.id);
}, },
methods: { methods: {
...mapMutations(["appendTrackToPlayerList"]), ...mapMutations(['appendTrackToPlayerList']),
...mapActions(["playFirstTrackOnList", "playTrackOnListByID", "showToast"]), ...mapActions(['playFirstTrackOnList', 'playTrackOnListByID', 'showToast']),
playAlbumByID(id, trackID = "first") { playAlbumByID(id, trackID = 'first') {
this.$store.state.player.playAlbumByID(id, trackID); this.$store.state.player.playAlbumByID(id, trackID);
}, },
likeAlbum(toast = false) { likeAlbum(toast = false) {
if (!isAccountLoggedIn()) { if (!isAccountLoggedIn()) {
this.showToast("此操作需要登录网易云账号"); this.showToast('此操作需要登录网易云账号');
return; return;
} }
likeAAlbum({ likeAAlbum({
id: this.album.id, id: this.album.id,
t: this.dynamicDetail.isSub ? 0 : 1, t: this.dynamicDetail.isSub ? 0 : 1,
}) })
.then((data) => { .then(data => {
if (data.code === 200) { if (data.code === 200) {
this.dynamicDetail.isSub = !this.dynamicDetail.isSub; this.dynamicDetail.isSub = !this.dynamicDetail.isSub;
if (toast === true) if (toast === true)
this.showToast( this.showToast(
this.dynamicDetail.isSub ? "已保存到音乐库" : "已从音乐库删除" this.dynamicDetail.isSub ? '已保存到音乐库' : '已从音乐库删除'
); );
} }
console.log(data); console.log(data);
}) })
.catch((error) => { .catch(error => {
this.showToast(`${error.response.data.message || error}`); this.showToast(`${error.response.data.message || error}`);
}); });
}, },
@ -230,17 +235,17 @@ export default {
let splitTitle = splitSoundtrackAlbumTitle(this.album.name); let splitTitle = splitSoundtrackAlbumTitle(this.album.name);
let splitTitle2 = splitAlbumTitle(splitTitle.title); let splitTitle2 = splitAlbumTitle(splitTitle.title);
this.title = splitTitle2.title; this.title = splitTitle2.title;
if (splitTitle.subtitle !== "" && splitTitle2.subtitle !== "") { if (splitTitle.subtitle !== '' && splitTitle2.subtitle !== '') {
this.subtitle = splitTitle.subtitle + " · " + splitTitle2.subtitle; this.subtitle = splitTitle.subtitle + ' · ' + splitTitle2.subtitle;
} else { } else {
this.subtitle = this.subtitle =
splitTitle.subtitle === "" splitTitle.subtitle === ''
? splitTitle2.subtitle ? splitTitle2.subtitle
: splitTitle.subtitle; : splitTitle.subtitle;
} }
}, },
loadData(id) { loadData(id) {
getAlbum(id).then((data) => { getAlbum(id).then(data => {
this.album = data.album; this.album = data.album;
this.tracks = data.songs; this.tracks = data.songs;
this.formatTitle(); this.formatTitle();
@ -248,19 +253,17 @@ export default {
this.show = true; this.show = true;
// to get explicit mark // to get explicit mark
let trackIDs = this.tracks.map((t) => t.id); let trackIDs = this.tracks.map(t => t.id);
getTrackDetail(trackIDs.join(",")).then((data) => { getTrackDetail(trackIDs.join(',')).then(data => {
this.tracks = data.songs; this.tracks = data.songs;
}); });
// get more album by this artist // get more album by this artist
getArtistAlbum({ id: this.album.artist.id, limit: 100 }).then( getArtistAlbum({ id: this.album.artist.id, limit: 100 }).then(data => {
(data) => {
this.moreAlbums = data.hotAlbums; this.moreAlbums = data.hotAlbums;
}
);
}); });
albumDynamicDetail(id).then((data) => { });
albumDynamicDetail(id).then(data => {
this.dynamicDetail = data; this.dynamicDetail = data;
}); });
}, },
@ -268,11 +271,6 @@ export default {
this.$refs.albumMenu.openMenu(e); this.$refs.albumMenu.openMenu(e);
}, },
}, },
beforeRouteUpdate(to, from, next) {
NProgress.start();
this.loadData(to.params.id);
next();
},
}; };
</script> </script>

@ -1,46 +1,46 @@
<template> <template>
<div class="artist" v-show="show"> <div v-show="show" class="artist">
<div class="artist-info"> <div class="artist-info">
<div class="head"> <div class="head">
<img :src="artist.img1v1Url | resizeImage(1024)" /> <img :src="artist.img1v1Url | resizeImage(1024)" />
</div> </div>
<div> <div>
<div class="name">{{ artist.name }}</div> <div class="name">{{ artist.name }}</div>
<div class="artist">{{ $t("artist.artist") }}</div> <div class="artist">{{ $t('artist.artist') }}</div>
<div class="statistics"> <div class="statistics">
<a @click="scrollTo('popularTracks')" <a @click="scrollTo('popularTracks')"
>{{ artist.musicSize }} {{ $t("common.songs") }}</a >{{ artist.musicSize }} {{ $t('common.songs') }}</a
> >
· ·
<a @click="scrollTo('seeMore', 'start')" <a @click="scrollTo('seeMore', 'start')"
>{{ artist.albumSize }} {{ $t("artist.withAlbums") }}</a >{{ artist.albumSize }} {{ $t('artist.withAlbums') }}</a
> >
· ·
<a @click="scrollTo('mvs')" <a @click="scrollTo('mvs')"
>{{ artist.mvSize }} {{ $t("artist.videos") }}</a >{{ artist.mvSize }} {{ $t('artist.videos') }}</a
> >
</div> </div>
<div class="buttons"> <div class="buttons">
<ButtonTwoTone @click.native="playPopularSongs()" :iconClass="`play`"> <ButtonTwoTone :icon-class="play" @click.native="playPopularSongs()">
{{ $t("common.play") }} {{ $t('common.play') }}
</ButtonTwoTone> </ButtonTwoTone>
<ButtonTwoTone @click.native="followArtist" color="grey"> <ButtonTwoTone color="grey" @click.native="followArtist">
<span v-if="artist.followed">{{ $t("artist.following") }}</span> <span v-if="artist.followed">{{ $t('artist.following') }}</span>
<span v-else>{{ $t("artist.follow") }}</span> <span v-else>{{ $t('artist.follow') }}</span>
</ButtonTwoTone> </ButtonTwoTone>
</div> </div>
</div> </div>
</div> </div>
<div class="latest-release"> <div class="latest-release">
<div class="section-title">{{ $t("artist.latestRelease") }}</div> <div class="section-title">{{ $t('artist.latestRelease') }}</div>
<div class="release"> <div class="release">
<div class="container"> <div class="container">
<Cover <Cover
:imageUrl="latestRelease.picUrl | resizeImage"
type="album"
:id="latestRelease.id" :id="latestRelease.id"
:fixedSize="128" :image-url="latestRelease.picUrl | resizeImage"
:playButtonSize="30" type="album"
:fixed-size="128"
:play-button-size="30"
/> />
<div class="info"> <div class="info">
<div class="name"> <div class="name">
@ -53,60 +53,60 @@
</div> </div>
<div class="type"> <div class="type">
{{ latestRelease.type | formatAlbumType(latestRelease) }} · {{ latestRelease.type | formatAlbumType(latestRelease) }} ·
{{ latestRelease.size }} {{ $t("common.songs") }} {{ latestRelease.size }} {{ $t('common.songs') }}
</div> </div>
</div> </div>
</div> </div>
<div></div> <div></div>
</div> </div>
</div> </div>
<div class="popular-tracks" id="popularTracks"> <div id="popularTracks" class="popular-tracks">
<div class="section-title">{{ $t("artist.popularSongs") }}</div> <div class="section-title">{{ $t('artist.popularSongs') }}</div>
<TrackList <TrackList
:tracks="popularTracks.slice(0, showMorePopTracks ? 24 : 12)" :tracks="popularTracks.slice(0, showMorePopTracks ? 24 : 12)"
:type="'tracklist'" :type="'tracklist'"
/> />
<div class="show-more" id="seeMore"> <div id="seeMore" class="show-more">
<button @click="showMorePopTracks = !showMorePopTracks"> <button @click="showMorePopTracks = !showMorePopTracks">
<span v-show="!showMorePopTracks">{{ $t("artist.showMore") }}</span> <span v-show="!showMorePopTracks">{{ $t('artist.showMore') }}</span>
<span v-show="showMorePopTracks">{{ $t("artist.showLess") }}</span> <span v-show="showMorePopTracks">{{ $t('artist.showLess') }}</span>
</button> </button>
</div> </div>
</div> </div>
<div class="albums" id="albums" v-if="albums.length !== 0"> <div v-if="albums.length !== 0" id="albums" class="albums">
<div class="section-title">{{ $t("artist.albums") }}</div> <div class="section-title">{{ $t('artist.albums') }}</div>
<CoverRow <CoverRow
:type="'album'" :type="'album'"
:items="albums" :items="albums"
:subText="'releaseYear'" :sub-text="'releaseYear'"
:showPlayButton="true" :show-play-button="true"
/> />
</div> </div>
<div class="mvs" id="mvs" v-if="mvs.length !== 0"> <div v-if="mvs.length !== 0" id="mvs" class="mvs">
<div class="section-title" <div class="section-title"
>MVs >MVs
<router-link v-show="hasMoreMV" :to="`/artist/${this.artist.id}/mv`">{{ <router-link v-show="hasMoreMV" :to="`/artist/${this.artist.id}/mv`">{{
$t("home.seeMore") $t('home.seeMore')
}}</router-link> }}</router-link>
</div> </div>
<MvRow :mvs="mvs" subtitle="publishTime" /> <MvRow :mvs="mvs" subtitle="publishTime" />
</div> </div>
<div class="eps" v-if="eps.length !== 0"> <div v-if="eps.length !== 0" class="eps">
<div class="section-title">{{ $t("artist.EPsSingles") }}</div> <div class="section-title">{{ $t('artist.EPsSingles') }}</div>
<CoverRow <CoverRow
:type="'album'" :type="'album'"
:items="eps" :items="eps"
:subText="'albumType+releaseYear'" :sub-text="'albumType+releaseYear'"
:showPlayButton="true" :show-play-button="true"
/> />
</div> </div>
<div class="similar-artists" v-if="similarArtists.length !== 0"> <div v-if="similarArtists.length !== 0" class="similar-artists">
<div class="section-title">相似艺人</div> <div class="section-title">相似艺人</div>
<CoverRow <CoverRow
type="artist" type="artist"
:columnNumber="6" :column-number="6"
gap="36px 28px" gap="36px 28px"
:items="similarArtists.slice(0, 12)" :items="similarArtists.slice(0, 12)"
/> />
@ -115,42 +115,48 @@
</template> </template>
<script> <script>
import { mapMutations, mapActions, mapState } from "vuex"; import { mapMutations, mapActions, mapState } from 'vuex';
import { import {
getArtist, getArtist,
getArtistAlbum, getArtistAlbum,
artistMv, artistMv,
followAArtist, followAArtist,
similarArtists, similarArtists,
} from "@/api/artist"; } from '@/api/artist';
import { isAccountLoggedIn } from "@/utils/auth"; import { isAccountLoggedIn } from '@/utils/auth';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import ButtonTwoTone from "@/components/ButtonTwoTone.vue"; import ButtonTwoTone from '@/components/ButtonTwoTone.vue';
import TrackList from "@/components/TrackList.vue"; import TrackList from '@/components/TrackList.vue';
import CoverRow from "@/components/CoverRow.vue"; import CoverRow from '@/components/CoverRow.vue';
import Cover from "@/components/Cover.vue"; import Cover from '@/components/Cover.vue';
import MvRow from "@/components/MvRow.vue"; import MvRow from '@/components/MvRow.vue';
export default { export default {
name: "Artist", name: 'Artist',
components: { Cover, ButtonTwoTone, TrackList, CoverRow, MvRow }, components: { Cover, ButtonTwoTone, TrackList, CoverRow, MvRow },
beforeRouteUpdate(to, from, next) {
NProgress.start();
this.artist.img1v1Url =
'https://p1.music.126.net/VnZiScyynLG7atLIZ2YPkw==/18686200114669622.jpg';
this.loadData(to.params.id, next);
},
data() { data() {
return { return {
show: false, show: false,
artist: { artist: {
img1v1Url: img1v1Url:
"https://p1.music.126.net/VnZiScyynLG7atLIZ2YPkw==/18686200114669622.jpg", 'https://p1.music.126.net/VnZiScyynLG7atLIZ2YPkw==/18686200114669622.jpg',
}, },
popularTracks: [], popularTracks: [],
albumsData: [], albumsData: [],
latestRelease: { latestRelease: {
picUrl: "", picUrl: '',
publishTime: 0, publishTime: 0,
id: 0, id: 0,
name: "", name: '',
type: "", type: '',
size: "", size: '',
}, },
showMorePopTracks: false, showMorePopTracks: false,
mvs: [], mvs: [],
@ -159,91 +165,85 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["player"]), ...mapState(['player']),
albums() { albums() {
return this.albumsData.filter((a) => a.type === "专辑"); return this.albumsData.filter(a => a.type === '专辑');
}, },
eps() { eps() {
return this.albumsData.filter((a) => return this.albumsData.filter(a =>
["EP/Single", "EP", "Single"].includes(a.type) ['EP/Single', 'EP', 'Single'].includes(a.type)
); );
}, },
}, },
created() {
this.loadData(this.$route.params.id);
},
activated() {
if (this.show) {
if (this.artist.id.toString() !== this.$route.params.id) {
this.show = false;
NProgress.start();
this.loadData(this.$route.params.id);
}
}
},
methods: { methods: {
...mapMutations(["appendTrackToPlayerList"]), ...mapMutations(['appendTrackToPlayerList']),
...mapActions(["playFirstTrackOnList", "playTrackOnListByID"]), ...mapActions(['playFirstTrackOnList', 'playTrackOnListByID']),
loadData(id, next = undefined) { loadData(id, next = undefined) {
getArtist(id).then((data) => { getArtist(id).then(data => {
this.artist = data.artist; this.artist = data.artist;
this.popularTracks = data.hotSongs; this.popularTracks = data.hotSongs;
if (next !== undefined) next(); if (next !== undefined) next();
NProgress.done(); NProgress.done();
this.show = true; this.show = true;
}); });
getArtistAlbum({ id: id, limit: 200 }).then((data) => { getArtistAlbum({ id: id, limit: 200 }).then(data => {
this.albumsData = data.hotAlbums; this.albumsData = data.hotAlbums;
this.latestRelease = data.hotAlbums[0]; this.latestRelease = data.hotAlbums[0];
}); });
artistMv({ id }).then((data) => { artistMv({ id }).then(data => {
this.mvs = data.mvs; this.mvs = data.mvs;
this.hasMoreMV = data.hasMore; this.hasMoreMV = data.hasMore;
}); });
similarArtists(id).then((data) => { similarArtists(id).then(data => {
this.similarArtists = data.artists; this.similarArtists = data.artists;
}); });
}, },
goToAlbum(id) { goToAlbum(id) {
this.$router.push({ this.$router.push({
name: "album", name: 'album',
params: { id }, params: { id },
}); });
}, },
playPopularSongs(trackID = "first") { playPopularSongs(trackID = 'first') {
let trackIDs = this.popularTracks.map((t) => t.id); let trackIDs = this.popularTracks.map(t => t.id);
this.$store.state.player.replacePlaylist( this.$store.state.player.replacePlaylist(
trackIDs, trackIDs,
this.artist.id, this.artist.id,
"artist", 'artist',
trackID trackID
); );
}, },
followArtist() { followArtist() {
if (!isAccountLoggedIn()) { if (!isAccountLoggedIn()) {
this.showToast("此操作需要登录网易云账号"); this.showToast('此操作需要登录网易云账号');
return; return;
} }
followAArtist({ followAArtist({
id: this.artist.id, id: this.artist.id,
t: this.artist.followed ? 0 : 1, t: this.artist.followed ? 0 : 1,
}).then((data) => { }).then(data => {
if (data.code === 200) this.artist.followed = !this.artist.followed; if (data.code === 200) this.artist.followed = !this.artist.followed;
}); });
}, },
scrollTo(div, block = "center") { scrollTo(div, block = 'center') {
document.getElementById(div).scrollIntoView({ document.getElementById(div).scrollIntoView({
behavior: "smooth", behavior: 'smooth',
block, block,
}); });
}, },
}, },
created() {
this.loadData(this.$route.params.id);
},
activated() {
if (this.show) {
if (this.artist.id.toString() !== this.$route.params.id) {
this.show = false;
NProgress.start();
this.loadData(this.$route.params.id);
}
}
},
beforeRouteUpdate(to, from, next) {
NProgress.start();
this.artist.img1v1Url =
"https://p1.music.126.net/VnZiScyynLG7atLIZ2YPkw==/18686200114669622.jpg";
this.loadData(to.params.id, next);
},
}; };
</script> </script>

@ -7,26 +7,32 @@
</h1> </h1>
<MvRow :mvs="mvs" subtitle="publishTime" /> <MvRow :mvs="mvs" subtitle="publishTime" />
<div class="load-more"> <div class="load-more">
<ButtonTwoTone v-show="hasMore" @click.native="loadMVs" color="grey">{{ <ButtonTwoTone v-show="hasMore" color="grey" @click.native="loadMVs">{{
$t("explore.loadMore") $t('explore.loadMore')
}}</ButtonTwoTone> }}</ButtonTwoTone>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { artistMv, getArtist } from "@/api/artist"; import { artistMv, getArtist } from '@/api/artist';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import ButtonTwoTone from "@/components/ButtonTwoTone.vue"; import ButtonTwoTone from '@/components/ButtonTwoTone.vue';
import MvRow from "@/components/MvRow.vue"; import MvRow from '@/components/MvRow.vue';
export default { export default {
name: "artistMV", name: 'artistMV',
components: { components: {
MvRow, MvRow,
ButtonTwoTone, ButtonTwoTone,
}, },
beforeRouteUpdate(to, from, next) {
NProgress.start();
this.id = to.params.id;
this.loadData();
next();
},
data() { data() {
return { return {
id: 0, id: 0,
@ -36,16 +42,30 @@ export default {
mvs: [], mvs: [],
}; };
}, },
created() {
this.id = this.$route.params.id;
this.loadData();
},
activated() {
if (this.$route.params.id !== this.id) {
this.id = this.$route.params.id;
this.mvs = [];
this.artist = {};
this.show = false;
this.hasMore = true;
this.loadData();
}
},
methods: { methods: {
loadData() { loadData() {
getArtist(this.id).then((data) => { getArtist(this.id).then(data => {
this.artist = data.artist; this.artist = data.artist;
}); });
this.loadMVs(); this.loadMVs();
}, },
loadMVs() { loadMVs() {
artistMv({ id: this.id, limit: 100, offset: this.mvs.length }).then( artistMv({ id: this.id, limit: 100, offset: this.mvs.length }).then(
(data) => { data => {
this.mvs.push(...data.mvs); this.mvs.push(...data.mvs);
this.hasMore = data.hasMore; this.hasMore = data.hasMore;
NProgress.done(); NProgress.done();
@ -54,26 +74,6 @@ export default {
); );
}, },
}, },
created() {
this.id = this.$route.params.id;
this.loadData();
},
activated() {
if (this.$route.params.id !== this.id) {
this.id = this.$route.params.id;
this.mvs = [];
this.artist = {};
this.show = false;
this.hasMore = true;
this.loadData();
}
},
beforeRouteUpdate(to, from, next) {
NProgress.start();
this.id = to.params.id;
this.loadData();
next();
},
}; };
</script> </script>

@ -8,20 +8,20 @@
<TrackList <TrackList
:tracks="dailyTracks" :tracks="dailyTracks"
type="playlist" type="playlist"
dbclickTrackFunc="dailyTracks" dbclick-track-func="dailyTracks"
/> />
</div> </div>
</template> </template>
<script> <script>
import { mapMutations, mapState } from "vuex"; import { mapMutations, mapState } from 'vuex';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import { dailyRecommendTracks } from "@/api/playlist"; import { dailyRecommendTracks } from '@/api/playlist';
import TrackList from "@/components/TrackList.vue"; import TrackList from '@/components/TrackList.vue';
export default { export default {
name: "dailyTracks", name: 'dailyTracks',
components: { components: {
TrackList, TrackList,
}, },
@ -30,6 +30,9 @@ export default {
show: false, show: false,
}; };
}, },
computed: {
...mapState(['player', 'data', 'dailyTracks']),
},
created() { created() {
if (this.dailyTracks.length === 0) { if (this.dailyTracks.length === 0) {
NProgress.start(); NProgress.start();
@ -38,13 +41,10 @@ export default {
this.show = true; this.show = true;
} }
}, },
computed: {
...mapState(["player", "data", "dailyTracks"]),
},
methods: { methods: {
...mapMutations(["updateDailyTracks"]), ...mapMutations(['updateDailyTracks']),
loadDailyTracks() { loadDailyTracks() {
dailyRecommendTracks().then((result) => { dailyRecommendTracks().then(result => {
this.updateDailyTracks(result.data.dailySongs); this.updateDailyTracks(result.data.dailySongs);
NProgress.done(); NProgress.done();
this.show = true; this.show = true;

@ -1,6 +1,6 @@
<template> <template>
<div class="explore"> <div class="explore">
<h1>{{ $t("explore.explore") }}</h1> <h1>{{ $t('explore.explore') }}</h1>
<div class="buttons"> <div class="buttons">
<div <div
v-for="category in settings.enabledPlaylistCategories" v-for="category in settings.enabledPlaylistCategories"
@ -57,29 +57,29 @@
color="grey" color="grey"
:loading="loadingMore" :loading="loadingMore"
@click.native="getPlaylist" @click.native="getPlaylist"
>{{ $t("explore.loadMore") }}</ButtonTwoTone >{{ $t('explore.loadMore') }}</ButtonTwoTone
> >
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { mapState, mapMutations } from "vuex"; import { mapState, mapMutations } from 'vuex';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import { import {
topPlaylist, topPlaylist,
highQualityPlaylist, highQualityPlaylist,
recommendPlaylist, recommendPlaylist,
toplists, toplists,
} from "@/api/playlist"; } from '@/api/playlist';
import { playlistCategories } from "@/utils/staticData"; import { playlistCategories } from '@/utils/staticData';
import ButtonTwoTone from "@/components/ButtonTwoTone.vue"; import ButtonTwoTone from '@/components/ButtonTwoTone.vue';
import CoverRow from "@/components/CoverRow.vue"; import CoverRow from '@/components/CoverRow.vue';
import SvgIcon from "@/components/SvgIcon.vue"; import SvgIcon from '@/components/SvgIcon.vue';
export default { export default {
name: "Explore", name: 'Explore',
components: { components: {
CoverRow, CoverRow,
ButtonTwoTone, ButtonTwoTone,
@ -99,38 +99,38 @@ export default {
return { return {
show: false, show: false,
playlists: [], playlists: [],
activeCategory: "全部", activeCategory: '全部',
loadingMore: false, loadingMore: false,
showLoadMoreButton: false, showLoadMoreButton: false,
hasMore: true, hasMore: true,
allBigCats: ["语种", "风格", "场景", "情感", "主题"], allBigCats: ['语种', '风格', '场景', '情感', '主题'],
showCatOptions: false, showCatOptions: false,
}; };
}, },
computed: { computed: {
...mapState(["settings"]), ...mapState(['settings']),
subText() { subText() {
if (this.activeCategory === "排行榜") return "updateFrequency"; if (this.activeCategory === '排行榜') return 'updateFrequency';
if (this.activeCategory === "推荐歌单") return "copywriter"; if (this.activeCategory === '推荐歌单') return 'copywriter';
return "none"; return 'none';
}, },
}, },
activated() { activated() {
this.loadData(); this.loadData();
}, },
methods: { methods: {
...mapMutations(["togglePlaylistCategory"]), ...mapMutations(['togglePlaylistCategory']),
loadData() { loadData() {
if (!this.show) NProgress.start(); if (!this.show) NProgress.start();
this.activeCategory = this.activeCategory =
this.$route.query.category === undefined this.$route.query.category === undefined
? "全部" ? '全部'
: this.$route.query.category; : this.$route.query.category;
this.getPlaylist(); this.getPlaylist();
}, },
goToCategory(Category) { goToCategory(Category) {
this.showCatOptions = false; this.showCatOptions = false;
this.$router.push({ path: "/explore?category=" + Category }); this.$router.push({ path: '/explore?category=' + Category });
}, },
updatePlaylist(playlists) { updatePlaylist(playlists) {
this.playlists.push(...playlists); this.playlists.push(...playlists);
@ -141,19 +141,19 @@ export default {
}, },
getPlaylist() { getPlaylist() {
this.loadingMore = true; this.loadingMore = true;
if (this.activeCategory === "推荐歌单") { if (this.activeCategory === '推荐歌单') {
return this.getRecommendPlayList(); return this.getRecommendPlayList();
} }
if (this.activeCategory === "精品歌单") { if (this.activeCategory === '精品歌单') {
return this.getHighQualityPlaylist(); return this.getHighQualityPlaylist();
} }
if (this.activeCategory === "排行榜") { if (this.activeCategory === '排行榜') {
return this.getTopLists(); return this.getTopLists();
} }
return this.getTopPlayList(); return this.getTopPlayList();
}, },
getRecommendPlayList() { getRecommendPlayList() {
recommendPlaylist({ limit: 100 }).then((data) => { recommendPlaylist({ limit: 100 }).then(data => {
this.playlists = []; this.playlists = [];
this.updatePlaylist(data.result); this.updatePlaylist(data.result);
}); });
@ -162,13 +162,13 @@ export default {
let playlists = this.playlists; let playlists = this.playlists;
let before = let before =
playlists.length !== 0 ? playlists[playlists.length - 1].updateTime : 0; playlists.length !== 0 ? playlists[playlists.length - 1].updateTime : 0;
highQualityPlaylist({ limit: 50, before }).then((data) => { highQualityPlaylist({ limit: 50, before }).then(data => {
this.updatePlaylist(data.playlists); this.updatePlaylist(data.playlists);
this.hasMore = data.more; this.hasMore = data.more;
}); });
}, },
getTopLists() { getTopLists() {
toplists().then((data) => { toplists().then(data => {
this.playlists = []; this.playlists = [];
this.updatePlaylist(data.list); this.updatePlaylist(data.list);
}); });
@ -177,13 +177,13 @@ export default {
topPlaylist({ topPlaylist({
cat: this.activeCategory, cat: this.activeCategory,
offset: this.playlists.length, offset: this.playlists.length,
}).then((data) => { }).then(data => {
this.updatePlaylist(data.playlists); this.updatePlaylist(data.playlists);
this.hasMore = data.more; this.hasMore = data.more;
}); });
}, },
getCatsByBigCat(name) { getCatsByBigCat(name) {
return playlistCategories.filter((c) => c.bigCat === name); return playlistCategories.filter(c => c.bigCat === name);
}, },
toggleCat(name) { toggleCat(name) {
this.togglePlaylistCategory(name); this.togglePlaylistCategory(name);

@ -1,25 +1,25 @@
<template> <template>
<div class="home" v-show="show"> <div v-show="show" class="home">
<div class="index-row" v-if="settings.showPlaylistsByAppleMusic !== false"> <div v-if="settings.showPlaylistsByAppleMusic !== false" class="index-row">
<div class="title"> by Apple Music </div> <div class="title"> by Apple Music </div>
<CoverRow <CoverRow
:type="'playlist'" :type="'playlist'"
:items="byAppleMusic" :items="byAppleMusic"
:subText="'appleMusic'" sub-text="appleMusic"
:imageSize="1024" :image-size="1024"
/> />
</div> </div>
<div class="index-row"> <div class="index-row">
<div class="title"> <div class="title">
{{ $t("home.recommendPlaylist") }} {{ $t('home.recommendPlaylist') }}
<router-link to="/explore?category=推荐歌单">{{ <router-link to="/explore?category=推荐歌单">{{
$t("home.seeMore") $t('home.seeMore')
}}</router-link> }}</router-link>
</div> </div>
<CoverRow <CoverRow
:type="'playlist'" :type="'playlist'"
:items="recommendPlaylist.items" :items="recommendPlaylist.items"
:subText="'copywriter'" sub-text="copywriter"
/> />
</div> </div>
<div class="index-row"> <div class="index-row">
@ -30,51 +30,55 @@
</div> </div>
</div> </div>
<div class="index-row"> <div class="index-row">
<div class="title">{{ $t("home.recommendArtist") }}</div> <div class="title">{{ $t('home.recommendArtist') }}</div>
<CoverRow <CoverRow
type="artist" type="artist"
:columnNumber="6" :column-number="6"
:items="recommendArtists.items" :items="recommendArtists.items"
/> />
</div> </div>
<div class="index-row"> <div class="index-row">
<div class="title"> <div class="title">
{{ $t("home.newAlbum") }} {{ $t('home.newAlbum') }}
<router-link to="/new-album">{{ $t("home.seeMore") }}</router-link> <router-link to="/new-album">{{ $t('home.seeMore') }}</router-link>
</div> </div>
<CoverRow type="album" :items="newReleasesAlbum.items" subText="artist" /> <CoverRow
type="album"
:items="newReleasesAlbum.items"
sub-text="artist"
/>
</div> </div>
<div class="index-row"> <div class="index-row">
<div class="title"> <div class="title">
{{ $t("home.charts") }} {{ $t('home.charts') }}
<router-link to="/explore?category=排行榜">{{ <router-link to="/explore?category=排行榜">{{
$t("home.seeMore") $t('home.seeMore')
}}</router-link> }}</router-link>
</div> </div>
<CoverRow <CoverRow
type="playlist" type="playlist"
:items="topList.items" :items="topList.items"
:subText="'updateFrequency'" sub-text="updateFrequency"
:imageSize="1024" :image-size="1024"
/> />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { toplists, recommendPlaylist } from "@/api/playlist"; import { toplists, recommendPlaylist } from '@/api/playlist';
import { toplistOfArtists } from "@/api/artist"; import { toplistOfArtists } from '@/api/artist';
import { byAppleMusic } from "@/utils/staticData"; import { byAppleMusic } from '@/utils/staticData';
import { countDBSize } from "@/utils/db"; import { countDBSize } from '@/utils/db';
import { newAlbums } from "@/api/album"; import { newAlbums } from '@/api/album';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import { mapState } from "vuex"; import { mapState } from 'vuex';
import CoverRow from "@/components/CoverRow.vue"; import CoverRow from '@/components/CoverRow.vue';
import FMCard from "@/components/FMCard.vue"; import FMCard from '@/components/FMCard.vue';
import DailyTracksCard from "@/components/DailyTracksCard.vue"; import DailyTracksCard from '@/components/DailyTracksCard.vue';
export default { export default {
name: "Home", name: 'Home',
components: { CoverRow, FMCard, DailyTracksCard }, components: { CoverRow, FMCard, DailyTracksCard },
data() { data() {
return { return {
@ -92,28 +96,31 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["settings"]), ...mapState(['settings']),
byAppleMusic() { byAppleMusic() {
return byAppleMusic; return byAppleMusic;
}, },
}, },
activated() {
this.loadData();
},
methods: { methods: {
loadData() { loadData() {
if (!this.show) NProgress.start(); if (!this.show) NProgress.start();
recommendPlaylist({ recommendPlaylist({
limit: 10, limit: 10,
}).then((data) => { }).then(data => {
this.recommendPlaylist.items = data.result; this.recommendPlaylist.items = data.result;
NProgress.done(); NProgress.done();
this.show = true; this.show = true;
}); });
newAlbums({ newAlbums({
area: "EA", area: 'EA',
limit: 10, limit: 10,
}).then((data) => { }).then(data => {
this.newReleasesAlbum.items = data.albums; this.newReleasesAlbum.items = data.albums;
}); });
toplistOfArtists(2).then((data) => { toplistOfArtists(2).then(data => {
let indexs = []; let indexs = [];
while (indexs.length < 6) { while (indexs.length < 6) {
let tmp = ~~(Math.random() * 100); let tmp = ~~(Math.random() * 100);
@ -124,17 +131,14 @@ export default {
indexs.includes(index) indexs.includes(index)
); );
}); });
toplists().then((data) => { toplists().then(data => {
this.topList.items = data.list.filter((l) => this.topList.items = data.list.filter(l =>
this.topList.ids.includes(l.id) this.topList.ids.includes(l.id)
); );
}); });
countDBSize(); countDBSize();
}, },
}, },
activated() {
this.loadData();
},
}; };
</script> </script>

@ -6,36 +6,36 @@
<img src="/img/logos/lastfm.png" /> <img src="/img/logos/lastfm.png" />
</div> </div>
<div class="message">{{ message }}</div> <div class="message">{{ message }}</div>
<button @click="close" v-show="done"> </button> <button v-show="done" @click="close"> </button>
</div> </div>
</template> </template>
<script> <script>
import { authGetSession } from "@/api/lastfm"; import { authGetSession } from '@/api/lastfm';
export default { export default {
name: "lastfmCallback", name: 'LastfmCallback',
data() { data() {
return { message: "请稍等...", done: false }; return { message: '请稍等...', done: false };
}, },
created() { created() {
const token = new URLSearchParams(window.location.search).get("token"); const token = new URLSearchParams(window.location.search).get('token');
if (!token) { if (!token) {
this.message = "连接失败请重试或联系开发者无Token"; this.message = '连接失败请重试或联系开发者无Token';
this.done = true; this.done = true;
return; return;
} }
console.log(token); console.log(token);
authGetSession(token).then((result) => { authGetSession(token).then(result => {
console.log(result); console.log(result);
if (!result.data.session) { if (!result.data.session) {
this.message = "连接失败请重试或联系开发者无Session"; this.message = '连接失败请重试或联系开发者无Session';
this.done = true; this.done = true;
return; return;
} }
localStorage.setItem("lastfm", JSON.stringify(result.data.session)); localStorage.setItem('lastfm', JSON.stringify(result.data.session));
this.$store.commit("updateLastfm", result.data.session); this.$store.commit('updateLastfm', result.data.session);
this.message = "已成功连接到 Last.fm"; this.message = '已成功连接到 Last.fm';
this.done = true; this.done = true;
}); });
}, },

@ -14,8 +14,8 @@
> >
<div class="container" :class="{ active: activeCard === 1 }"> <div class="container" :class="{ active: activeCard === 1 }">
<div class="title-info"> <div class="title-info">
<div class="title">{{ $t("login.loginText") }}</div> <div class="title">{{ $t('login.loginText') }}</div>
<div class="info">{{ $t("login.accessToAll") }}</div> <div class="info">{{ $t('login.accessToAll') }}</div>
</div> </div>
<svg-icon icon-class="arrow-right"></svg-icon> <svg-icon icon-class="arrow-right"></svg-icon>
</div> </div>
@ -28,8 +28,8 @@
> >
<div class="container" :class="{ active: activeCard === 2 }"> <div class="container" :class="{ active: activeCard === 2 }">
<div class="title-info"> <div class="title-info">
<div class="title">{{ $t("login.search") }}</div> <div class="title">{{ $t('login.search') }}</div>
<div class="info">{{ $t("login.readonly") }}</div> <div class="info">{{ $t('login.readonly') }}</div>
</div> </div>
<svg-icon icon-class="arrow-right"></svg-icon> <svg-icon icon-class="arrow-right"></svg-icon>
</div> </div>
@ -39,12 +39,12 @@
</template> </template>
<script> <script>
import NProgress from "nprogress"; import NProgress from 'nprogress';
import SvgIcon from "@/components/SvgIcon.vue"; import SvgIcon from '@/components/SvgIcon.vue';
export default { export default {
name: "Login", name: 'Login',
components: { components: {
SvgIcon, SvgIcon,
}, },
@ -58,7 +58,7 @@ export default {
}, },
methods: { methods: {
goTo(path) { goTo(path) {
this.$router.push({ path: "/login/" + path }); this.$router.push({ path: '/login/' + path });
}, },
}, },
}; };

@ -3,26 +3,26 @@
<div class="section-1"> <div class="section-1">
<img src="/img/logos/netease-music.png" /> <img src="/img/logos/netease-music.png" />
</div> </div>
<div class="title">{{ $t("login.loginText") }}</div> <div class="title">{{ $t('login.loginText') }}</div>
<div class="section-2"> <div class="section-2">
<div class="input-box" v-show="mode === 'phone'"> <div v-show="mode === 'phone'" class="input-box">
<div class="container" :class="{ active: inputFocus === 'phone' }"> <div class="container" :class="{ active: inputFocus === 'phone' }">
<svg-icon icon-class="mobile" /> <svg-icon icon-class="mobile" />
<div class="inputs"> <div class="inputs">
<input <input
id="countryCode" id="countryCode"
v-model="countryCode"
:placeholder=" :placeholder="
inputFocus === 'phone' ? '' : $t('login.countryCode') inputFocus === 'phone' ? '' : $t('login.countryCode')
" "
v-model="countryCode"
@focus="inputFocus = 'phone'" @focus="inputFocus = 'phone'"
@blur="inputFocus = ''" @blur="inputFocus = ''"
@keyup.enter="login" @keyup.enter="login"
/> />
<input <input
id="phoneNumber" id="phoneNumber"
:placeholder="inputFocus === 'phone' ? '' : $t('login.phone')"
v-model="phoneNumber" v-model="phoneNumber"
:placeholder="inputFocus === 'phone' ? '' : $t('login.phone')"
@focus="inputFocus = 'phone'" @focus="inputFocus = 'phone'"
@blur="inputFocus = ''" @blur="inputFocus = ''"
@keyup.enter="login" @keyup.enter="login"
@ -30,15 +30,15 @@
</div> </div>
</div> </div>
</div> </div>
<div class="input-box" v-show="mode === 'email'"> <div v-show="mode === 'email'" class="input-box">
<div class="container" :class="{ active: inputFocus === 'email' }"> <div class="container" :class="{ active: inputFocus === 'email' }">
<svg-icon icon-class="mail" /> <svg-icon icon-class="mail" />
<div class="inputs"> <div class="inputs">
<input <input
type="email"
id="email" id="email"
:placeholder="inputFocus === 'email' ? '' : $t('login.email')"
v-model="email" v-model="email"
type="email"
:placeholder="inputFocus === 'email' ? '' : $t('login.email')"
@focus="inputFocus = 'email'" @focus="inputFocus = 'email'"
@blur="inputFocus = ''" @blur="inputFocus = ''"
@keyup.enter="login" @keyup.enter="login"
@ -51,12 +51,12 @@
<svg-icon icon-class="lock" /> <svg-icon icon-class="lock" />
<div class="inputs"> <div class="inputs">
<input <input
type="password"
id="password" id="password"
v-model="password"
type="password"
:placeholder=" :placeholder="
inputFocus === 'password' ? '' : $t('login.password') inputFocus === 'password' ? '' : $t('login.password')
" "
v-model="password"
@focus="inputFocus = 'password'" @focus="inputFocus = 'password'"
@blur="inputFocus = ''" @blur="inputFocus = ''"
@keyup.enter="login" @keyup.enter="login"
@ -66,8 +66,8 @@
</div> </div>
</div> </div>
<div class="confirm"> <div class="confirm">
<button @click="login" v-show="!processing"> <button v-show="!processing" @click="login">
{{ $t("login.login") }} {{ $t('login.login') }}
</button> </button>
<button v-show="processing" class="loading" disabled> <button v-show="processing" class="loading" disabled>
<span></span> <span></span>
@ -77,10 +77,10 @@
</div> </div>
<div class="other-login"> <div class="other-login">
<a v-show="mode === 'phone'" @click="mode = 'email'">{{ <a v-show="mode === 'phone'" @click="mode = 'email'">{{
$t("login.loginWithEmail") $t('login.loginWithEmail')
}}</a> }}</a>
<a v-show="mode === 'email'" @click="mode = 'phone'">{{ <a v-show="mode === 'email'" @click="mode = 'phone'">{{
$t("login.loginWithPhone") $t('login.loginWithPhone')
}}</a> }}</a>
</div> </div>
<div <div
@ -91,25 +91,25 @@
</template> </template>
<script> <script>
import NProgress from "nprogress"; import NProgress from 'nprogress';
import { loginWithPhone, loginWithEmail } from "@/api/auth"; import { loginWithPhone, loginWithEmail } from '@/api/auth';
import { setCookies } from "@/utils/auth"; import { setCookies } from '@/utils/auth';
import md5 from "crypto-js/md5"; import md5 from 'crypto-js/md5';
import { mapMutations } from "vuex"; import { mapMutations } from 'vuex';
import nativeAlert from "@/utils/nativeAlert"; import nativeAlert from '@/utils/nativeAlert';
export default { export default {
name: "Login", name: 'Login',
data() { data() {
return { return {
processing: false, processing: false,
mode: "email", mode: 'email',
countryCode: "+86", countryCode: '+86',
phoneNumber: "", phoneNumber: '',
email: "", email: '',
password: "", password: '',
smsCode: "", smsCode: '',
inputFocus: "", inputFocus: '',
}; };
}, },
computed: { computed: {
@ -118,20 +118,20 @@ export default {
}, },
}, },
created() { created() {
if (this.$route.query.mode === "phone") { if (this.$route.query.mode === 'phone') {
this.mode = "phone"; this.mode = 'phone';
} }
NProgress.done(); NProgress.done();
}, },
methods: { methods: {
...mapMutations(["updateData"]), ...mapMutations(['updateData']),
validatePhone() { validatePhone() {
if ( if (
this.countryCode === "" || this.countryCode === '' ||
this.phone === "" || this.phone === '' ||
this.password === "" this.password === ''
) { ) {
nativeAlert("国家区号或手机号不正确"); nativeAlert('国家区号或手机号不正确');
this.processing = false; this.processing = false;
return false; return false;
} }
@ -140,27 +140,27 @@ export default {
validateEmail() { validateEmail() {
const emailReg = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; const emailReg = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if ( if (
this.email === "" || this.email === '' ||
this.password === "" || this.password === '' ||
!emailReg.test(this.email) !emailReg.test(this.email)
) { ) {
nativeAlert("邮箱不正确"); nativeAlert('邮箱不正确');
return false; return false;
} }
return true; return true;
}, },
login() { login() {
if (this.mode === "phone") { if (this.mode === 'phone') {
this.processing = this.validatePhone(); this.processing = this.validatePhone();
if (!this.processing) return; if (!this.processing) return;
loginWithPhone({ loginWithPhone({
countrycode: this.countryCode.replace("+", "").replace(/\s/g, ""), countrycode: this.countryCode.replace('+', '').replace(/\s/g, ''),
phone: this.phoneNumber.replace(/\s/g, ""), phone: this.phoneNumber.replace(/\s/g, ''),
password: "fakePassword", password: 'fakePassword',
md5_password: md5(this.password).toString(), md5_password: md5(this.password).toString(),
}) })
.then(this.handleLoginResponse) .then(this.handleLoginResponse)
.catch((error) => { .catch(error => {
this.processing = false; this.processing = false;
nativeAlert(`发生错误,请检查你的账号密码是否正确\n${error}`); nativeAlert(`发生错误,请检查你的账号密码是否正确\n${error}`);
}); });
@ -168,12 +168,12 @@ export default {
this.processing = this.validateEmail(); this.processing = this.validateEmail();
if (!this.processing) return; if (!this.processing) return;
loginWithEmail({ loginWithEmail({
email: this.email.replace(/\s/g, ""), email: this.email.replace(/\s/g, ''),
password: "fakePassword", password: 'fakePassword',
md5_password: md5(this.password).toString(), md5_password: md5(this.password).toString(),
}) })
.then(this.handleLoginResponse) .then(this.handleLoginResponse)
.catch((error) => { .catch(error => {
this.processing = false; this.processing = false;
nativeAlert(`发生错误,请检查你的账号密码是否正确\n${error}`); nativeAlert(`发生错误,请检查你的账号密码是否正确\n${error}`);
}); });
@ -186,13 +186,13 @@ export default {
} }
if (data.code === 200) { if (data.code === 200) {
setCookies(data.cookie); setCookies(data.cookie);
this.updateData({ key: "user", value: data.profile }); this.updateData({ key: 'user', value: data.profile });
this.updateData({ key: "loginMode", value: "account" }); this.updateData({ key: 'loginMode', value: 'account' });
this.$router.push({ path: "/library" }); this.$router.push({ path: '/library' });
} else { } else {
this.processing = false; this.processing = false;
console.log(data.msg); console.log(data.msg);
nativeAlert(data.msg ?? data.message ?? "账号或密码错误,请检查"); nativeAlert(data.msg ?? data.message ?? '账号或密码错误,请检查');
} }
}, },
}, },

@ -1,15 +1,15 @@
<template> <template>
<div class="login"> <div class="login">
<div> <div>
<div class="title">{{ $t("login.usernameLogin") }}</div> <div class="title">{{ $t('login.usernameLogin') }}</div>
<div class="sestion"> <div class="section">
<div class="search-box"> <div class="search-box">
<div class="container"> <div class="container">
<svg-icon icon-class="search" /> <svg-icon icon-class="search" />
<div class="input"> <div class="input">
<input <input
:placeholder="$t('login.searchHolder')"
v-model="keyword" v-model="keyword"
:placeholder="$t('login.searchHolder')"
@keydown.enter="throttleSearch" @keydown.enter="throttleSearch"
/> />
</div> </div>
@ -17,17 +17,17 @@
</div> </div>
</div> </div>
<div class="sestion"> <div class="sestion">
<div class="name" v-show="activeUser.nickname === undefined"> <div v-show="activeUser.nickname === undefined" class="name">
{{ $t("login.enterTip") }} {{ $t('login.enterTip') }}
</div> </div>
<div class="name" v-show="activeUser.nickname !== undefined"> <div v-show="activeUser.nickname !== undefined" class="name">
{{ $t("login.choose") }} {{ $t('login.choose') }}
</div> </div>
<div class="user-list"> <div class="user-list">
<div <div
class="user"
v-for="user in result" v-for="user in result"
:key="user.id" :key="user.id"
class="user"
:class="{ active: user.nickname === activeUser.nickname }" :class="{ active: user.nickname === activeUser.nickname }"
@click="activeUser = user" @click="activeUser = user"
> >
@ -39,32 +39,32 @@
</div> </div>
</div> </div>
<ButtonTwoTone <ButtonTwoTone
@click.native="confirm"
v-show="activeUser.nickname !== undefined" v-show="activeUser.nickname !== undefined"
@click.native="confirm"
> >
{{ $t("login.confirm") }} {{ $t('login.confirm') }}
</ButtonTwoTone> </ButtonTwoTone>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { mapMutations } from "vuex"; import { mapMutations } from 'vuex';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import { search } from "@/api/others"; import { search } from '@/api/others';
import { userPlaylist } from "@/api/user"; import { userPlaylist } from '@/api/user';
import { throttle } from "@/utils/common"; import { throttle } from '@/utils/common';
import ButtonTwoTone from "@/components/ButtonTwoTone.vue"; import ButtonTwoTone from '@/components/ButtonTwoTone.vue';
export default { export default {
name: "loginUsername", name: 'LoginUsername',
components: { components: {
ButtonTwoTone, ButtonTwoTone,
}, },
data() { data() {
return { return {
keyword: "", keyword: '',
result: [], result: [],
activeUser: {}, activeUser: {},
}; };
@ -73,26 +73,26 @@ export default {
NProgress.done(); NProgress.done();
}, },
methods: { methods: {
...mapMutations(["updateData"]), ...mapMutations(['updateData']),
search() { search() {
if (!this.keyword) return; if (!this.keyword) return;
search({ keywords: this.keyword, limit: 9, type: 1002 }).then((data) => { search({ keywords: this.keyword, limit: 9, type: 1002 }).then(data => {
this.result = data.result.userprofiles; this.result = data.result.userprofiles;
this.activeUser = this.result[0]; this.activeUser = this.result[0];
}); });
}, },
confirm() { confirm() {
this.updateData({ key: "user", value: this.activeUser }); this.updateData({ key: 'user', value: this.activeUser });
this.updateData({ key: "loginMode", value: "username" }); this.updateData({ key: 'loginMode', value: 'username' });
userPlaylist({ userPlaylist({
uid: this.activeUser.userId, uid: this.activeUser.userId,
limit: 1, limit: 1,
}).then((data) => { }).then(data => {
this.updateData({ this.updateData({
key: "likedSongPlaylistID", key: 'likedSongPlaylistID',
value: data.playlist[0].id, value: data.playlist[0].id,
}); });
this.$router.push({ path: "/library" }); this.$router.push({ path: '/library' });
}); });
}, },
throttleSearch: throttle(function () { throttleSearch: throttle(function () {

@ -215,7 +215,7 @@ export default {
return this.player.currentTrack?.al?.picUrl + '?param=1024y1024'; return this.player.currentTrack?.al?.picUrl + '?param=1024y1024';
}, },
bgImageUrl() { bgImageUrl() {
return this.player.currentTrack?.al?.picUrl + "?param=500y500"; return this.player.currentTrack?.al?.picUrl + '?param=500y500';
}, },
progress: { progress: {
get() { get() {

@ -13,8 +13,8 @@
{{ mv.data.name }} {{ mv.data.name }}
<div class="like-button"> <div class="like-button">
<button-icon @click.native="likeMV"> <button-icon @click.native="likeMV">
<svg-icon icon-class="heart-solid" v-if="mv.subed"></svg-icon> <svg-icon v-if="mv.subed" icon-class="heart-solid"></svg-icon>
<svg-icon icon-class="heart" v-else></svg-icon> <svg-icon v-else icon-class="heart"></svg-icon>
</button-icon> </button-icon>
</div> </div>
</div> </div>
@ -25,108 +25,108 @@
</div> </div>
</div> </div>
<div class="more-video"> <div class="more-video">
<div class="section-title">{{ $t("mv.moreVideo") }}</div> <div class="section-title">{{ $t('mv.moreVideo') }}</div>
<MvRow :mvs="simiMvs" /> <MvRow :mvs="simiMvs" />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { mvDetail, mvUrl, simiMv, likeAMV } from "@/api/mv"; import { mvDetail, mvUrl, simiMv, likeAMV } from '@/api/mv';
import { isAccountLoggedIn } from "@/utils/auth"; import { isAccountLoggedIn } from '@/utils/auth';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import "@/assets/css/plyr.css"; import '@/assets/css/plyr.css';
import Plyr from "plyr"; import Plyr from 'plyr';
import ButtonIcon from "@/components/ButtonIcon.vue"; import ButtonIcon from '@/components/ButtonIcon.vue';
import MvRow from "@/components/MvRow.vue"; import MvRow from '@/components/MvRow.vue';
import { mapActions } from "vuex"; import { mapActions } from 'vuex';
export default { export default {
name: "mv", name: 'mv',
components: { components: {
MvRow, MvRow,
ButtonIcon, ButtonIcon,
}, },
beforeRouteUpdate(to, from, next) {
this.getData(to.params.id);
next();
},
data() { data() {
return { return {
mv: { mv: {
url: "", url: '',
data: { data: {
name: "", name: '',
artistName: "", artistName: '',
playCount: "", playCount: '',
publishTime: "", publishTime: '',
}, },
}, },
player: null, player: null,
simiMvs: [], simiMvs: [],
}; };
}, },
mounted() {
let videoOptions = {
settings: ['quality'],
autoplay: false,
quality: {
default: 1080,
options: [1080, 720, 480, 240],
},
};
if (this.$route.query.autoplay === 'true') videoOptions.autoplay = true;
this.player = new Plyr(this.$refs.videoPlayer, videoOptions);
this.player.volume = this.$store.state.player.volume;
this.player.on('playing', () => {
this.$store.state.player.pause();
});
this.getData(this.$route.params.id);
console.log('网易云你这mv音频码率也太糊了吧🙄');
},
methods: { methods: {
...mapActions(["showToast"]), ...mapActions(['showToast']),
getData(id) { getData(id) {
mvDetail(id).then((data) => { mvDetail(id).then(data => {
this.mv = data; this.mv = data;
let requests = data.data.brs.map((br) => { let requests = data.data.brs.map(br => {
return mvUrl({ id, r: br.br }); return mvUrl({ id, r: br.br });
}); });
Promise.all(requests).then((results) => { Promise.all(requests).then(results => {
let sources = results.map((result) => { let sources = results.map(result => {
return { return {
src: result.data.url.replace(/^http:/, "https:"), src: result.data.url.replace(/^http:/, 'https:'),
type: "video/mp4", type: 'video/mp4',
size: result.data.r, size: result.data.r,
}; };
}); });
this.player.source = { this.player.source = {
type: "video", type: 'video',
title: this.mv.data.name, title: this.mv.data.name,
sources: sources, sources: sources,
poster: this.mv.data.cover.replace(/^http:/, "https:"), poster: this.mv.data.cover.replace(/^http:/, 'https:'),
}; };
NProgress.done(); NProgress.done();
}); });
}); });
simiMv(id).then((data) => { simiMv(id).then(data => {
this.simiMvs = data.mvs; this.simiMvs = data.mvs;
}); });
}, },
likeMV() { likeMV() {
if (!isAccountLoggedIn()) { if (!isAccountLoggedIn()) {
this.showToast("此操作需要登录网易云账号"); this.showToast('此操作需要登录网易云账号');
return; return;
} }
likeAMV({ likeAMV({
mvid: this.mv.data.id, mvid: this.mv.data.id,
t: this.mv.subed ? 0 : 1, t: this.mv.subed ? 0 : 1,
}).then((data) => { }).then(data => {
if (data.code === 200) this.mv.subed = !this.mv.subed; if (data.code === 200) this.mv.subed = !this.mv.subed;
}); });
}, },
}, },
mounted() {
let videoOptions = {
settings: ["quality"],
autoplay: false,
quality: {
default: 1080,
options: [1080, 720, 480, 240],
},
};
if (this.$route.query.autoplay === "true") videoOptions.autoplay = true;
this.player = new Plyr(this.$refs.videoPlayer, videoOptions);
this.player.volume = this.$store.state.player.volume;
this.player.on("playing", () => {
this.$store.state.player.pause();
});
this.getData(this.$route.params.id);
console.log("网易云你这mv音频码率也太糊了吧🙄");
},
beforeRouteUpdate(to, from, next) {
this.getData(to.params.id);
next();
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

@ -1,12 +1,12 @@
<template> <template>
<div class="newAlbum"> <div class="newAlbum">
<h1>{{ $t("home.newAlbum") }}</h1> <h1>{{ $t('home.newAlbum') }}</h1>
<div class="playlist-row"> <div class="playlist-row">
<div class="playlists"> <div class="playlists">
<CoverRow <CoverRow
type="album" type="album"
:items="albums" :items="albums"
subText="artist" sub-text="artist"
:show-play-button="true" :show-play-button="true"
/> />
</div> </div>
@ -15,25 +15,25 @@
</template> </template>
<script> <script>
import { newAlbums } from "@/api/album"; import { newAlbums } from '@/api/album';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import CoverRow from "@/components/CoverRow.vue"; import CoverRow from '@/components/CoverRow.vue';
export default { export default {
components: {
CoverRow,
},
data() { data() {
return { return {
albums: [], albums: [],
}; };
}, },
components: {
CoverRow,
},
created() { created() {
newAlbums({ newAlbums({
area: "EA", area: 'EA',
limit: 100, limit: 100,
}).then((data) => { }).then(data => {
this.albums = data.albums; this.albums = data.albums;
NProgress.done(); NProgress.done();
}); });

@ -1,37 +1,37 @@
<template> <template>
<div class="next-tracks"> <div class="next-tracks">
<h1>{{ $t("next.nowPlaying") }}</h1> <h1>{{ $t('next.nowPlaying') }}</h1>
<TrackList <TrackList
:tracks="[currentTrack]" :tracks="[currentTrack]"
type="playlist" type="playlist"
dbclickTrackFunc="none" dbclick-track-func="none"
/> />
<h1 v-show="playNextList.length > 0"></h1> <h1 v-show="playNextList.length > 0"></h1>
<TrackList <TrackList
v-show="playNextList.length > 0"
:tracks="playNextTracks" :tracks="playNextTracks"
type="playlist" type="playlist"
:highlightPlayingTrack="false" :highlight-playing-track="false"
dbclickTrackFunc="playTrackOnListByID" dbclick-track-func="playTrackOnListByID"
itemKey="id+index" item-key="id+index"
v-show="playNextList.length > 0"
/> />
<h1>{{ $t("next.nextUp") }}</h1> <h1>{{ $t('next.nextUp') }}</h1>
<TrackList <TrackList
:tracks="filteredTracks" :tracks="filteredTracks"
type="playlist" type="playlist"
:highlightPlayingTrack="false" :highlight-playing-track="false"
dbclickTrackFunc="playTrackOnListByID" dbclick-track-func="playTrackOnListByID"
/> />
</div> </div>
</template> </template>
<script> <script>
import { mapState, mapActions } from "vuex"; import { mapState, mapActions } from 'vuex';
import { getTrackDetail } from "@/api/track"; import { getTrackDetail } from '@/api/track';
import TrackList from "@/components/TrackList.vue"; import TrackList from '@/components/TrackList.vue';
export default { export default {
name: "Next", name: 'Next',
components: { components: {
TrackList, TrackList,
}, },
@ -41,7 +41,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["player"]), ...mapState(['player']),
currentTrack() { currentTrack() {
return this.player.currentTrack; return this.player.currentTrack;
}, },
@ -53,14 +53,14 @@ export default {
this.player.current + 1, this.player.current + 1,
this.player.current + 100 this.player.current + 100
); );
return this.tracks.filter((t) => trackIDs.includes(t.id)); return this.tracks.filter(t => trackIDs.includes(t.id));
}, },
playNextList() { playNextList() {
return this.player.playNextList; return this.player.playNextList;
}, },
playNextTracks() { playNextTracks() {
return this.playNextList.map((tid) => { return this.playNextList.map(tid => {
return this.tracks.find((t) => t.id === tid); return this.tracks.find(t => t.id === tid);
}); });
}, },
}, },
@ -75,8 +75,11 @@ export default {
this.loadTracks(); this.loadTracks();
}, },
}, },
activated() {
this.loadTracks();
},
methods: { methods: {
...mapActions(["playTrackOnListByID"]), ...mapActions(['playTrackOnListByID']),
loadTracks() { loadTracks() {
// 100 // 100
let trackIDs = this.player.list.slice( let trackIDs = this.player.list.slice(
@ -88,21 +91,18 @@ export default {
trackIDs.push(...this.playNextList); trackIDs.push(...this.playNextList);
// //
let loadedTrackIDs = this.tracks.map((t) => t.id); let loadedTrackIDs = this.tracks.map(t => t.id);
if (trackIDs.length > 0) { if (trackIDs.length > 0) {
getTrackDetail(trackIDs.join(",")).then((data) => { getTrackDetail(trackIDs.join(',')).then(data => {
let newTracks = data.songs.filter( let newTracks = data.songs.filter(
(t) => !loadedTrackIDs.includes(t.id) t => !loadedTrackIDs.includes(t.id)
); );
this.tracks.push(...newTracks); this.tracks.push(...newTracks);
}); });
} }
}, },
}, },
activated() {
this.loadTracks();
},
}; };
</script> </script>

@ -1,31 +1,30 @@
<template> <template>
<div v-show="show"> <div v-show="show">
<div <div
class="playlist-info"
v-if="specialPlaylistInfo === undefined && !isLikeSongsPage" v-if="specialPlaylistInfo === undefined && !isLikeSongsPage"
class="playlist-info"
> >
<Cover <Cover
:imageUrl="playlist.coverImgUrl | resizeImage(1024)"
:showPlayButton="true"
:alwaysShowShadow="true"
:clickCoverToPlay="true"
:fixedSize="288"
type="playlist"
:id="playlist.id" :id="playlist.id"
:coverHover="false" :image-url="playlist.coverImgUrl | resizeImage(1024)"
:playButtonSize="18" :show-play-button="true"
:always-show-shadow="true"
:click-cover-to-play="true"
:fixed-size="288"
type="playlist"
:cover-hover="false"
:play-button-size="18"
@click.right.native="openMenu" @click.right.native="openMenu"
/> />
<div class="info"> <div class="info">
<div class="title" @click.right="openMenu" <div class="title" @click.right="openMenu"
><span class="lock-icon" v-if="playlist.privacy === 10"> ><span v-if="playlist.privacy === 10" class="lock-icon">
<svg-icon icon-class="lock" /></span <svg-icon icon-class="lock" /></span
>{{ playlist.name }}</div >{{ playlist.name }}</div
> >
<div class="artist"> <div class="artist">
Playlist by Playlist by
<span <span
style="font-weight: 600"
v-if=" v-if="
[ [
5277771961, 5277771961,
@ -35,6 +34,7 @@
5278068783, 5278068783,
].includes(playlist.id) ].includes(playlist.id)
" "
style="font-weight: 600"
>Apple Music</span >Apple Music</span
> >
<a <a
@ -45,48 +45,48 @@
> >
</div> </div>
<div class="date-and-count"> <div class="date-and-count">
{{ $t("playlist.updatedAt") }} {{ $t('playlist.updatedAt') }}
{{ playlist.updateTime | formatDate }} · {{ playlist.trackCount }} {{ playlist.updateTime | formatDate }} · {{ playlist.trackCount }}
{{ $t("common.songs") }} {{ $t('common.songs') }}
</div> </div>
<div class="description" @click="showFullDescription = true"> <div class="description" @click="showFullDescription = true">
{{ playlist.description }} {{ playlist.description }}
</div> </div>
<div class="buttons"> <div class="buttons">
<ButtonTwoTone @click.native="playPlaylistByID()" :iconClass="`play`"> <ButtonTwoTone icon-class="play" @click.native="playPlaylistByID()">
{{ $t("common.play") }} {{ $t('common.play') }}
</ButtonTwoTone> </ButtonTwoTone>
<ButtonTwoTone <ButtonTwoTone
v-if="playlist.creator.userId !== data.user.userId" v-if="playlist.creator.userId !== data.user.userId"
:iconClass="playlist.subscribed ? 'heart-solid' : 'heart'" :icon-class="playlist.subscribed ? 'heart-solid' : 'heart'"
:iconButton="true" :icon-button="true"
:horizontalPadding="0" :horizontal-padding="0"
:color="playlist.subscribed ? 'blue' : 'grey'" :color="playlist.subscribed ? 'blue' : 'grey'"
:textColor="playlist.subscribed ? '#335eea' : ''" :text-color="playlist.subscribed ? '#335eea' : ''"
:backgroundColor=" :background-color="
playlist.subscribed ? 'var(--color-secondary-bg)' : '' playlist.subscribed ? 'var(--color-secondary-bg)' : ''
" "
@click.native="likePlaylist" @click.native="likePlaylist"
> >
</ButtonTwoTone> </ButtonTwoTone>
<ButtonTwoTone <ButtonTwoTone
iconClass="more" icon-class="more"
:iconButton="true" :icon-button="true"
:horizontalPadding="0" :horizontal-padding="0"
color="grey" color="grey"
@click.native="openMenu" @click.native="openMenu"
> >
</ButtonTwoTone> </ButtonTwoTone>
</div> </div>
</div> </div>
<div class="search-box" v-if="displaySearchInPlaylist"> <div v-if="displaySearchInPlaylist" class="search-box">
<div class="container" :class="{ active: inputFocus }"> <div class="container" :class="{ active: inputFocus }">
<svg-icon icon-class="search" /> <svg-icon icon-class="search" />
<div class="input"> <div class="input">
<input <input
:placeholder="inputFocus ? '' : $t('playlist.search')"
v-model.trim="inputSearchKeyWords" v-model.trim="inputSearchKeyWords"
v-focus="displaySearchInPlaylist" v-focus="displaySearchInPlaylist"
:placeholder="inputFocus ? '' : $t('playlist.search')"
@input="inputDebounce()" @input="inputDebounce()"
@focus="inputFocus = true" @focus="inputFocus = true"
@blur="inputFocus = false" @blur="inputFocus = false"
@ -95,7 +95,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="special-playlist" v-if="specialPlaylistInfo !== undefined"> <div v-if="specialPlaylistInfo !== undefined" class="special-playlist">
<div <div
class="title" class="title"
:class="specialPlaylistInfo.gradient" :class="specialPlaylistInfo.gradient"
@ -111,29 +111,29 @@
<div class="buttons"> <div class="buttons">
<ButtonTwoTone <ButtonTwoTone
class="play-button" class="play-button"
@click.native="playPlaylistByID()" icon-class="play"
iconClass="play"
color="grey" color="grey"
@click.native="playPlaylistByID()"
> >
{{ $t("common.play") }} {{ $t('common.play') }}
</ButtonTwoTone> </ButtonTwoTone>
<ButtonTwoTone <ButtonTwoTone
v-if="playlist.creator.userId !== data.user.userId" v-if="playlist.creator.userId !== data.user.userId"
:iconClass="playlist.subscribed ? 'heart-solid' : 'heart'" :icon-class="playlist.subscribed ? 'heart-solid' : 'heart'"
:iconButton="true" :icon-button="true"
:horizontalPadding="0" :horizontal-padding="0"
:color="playlist.subscribed ? 'blue' : 'grey'" :color="playlist.subscribed ? 'blue' : 'grey'"
:textColor="playlist.subscribed ? '#335eea' : ''" :text-color="playlist.subscribed ? '#335eea' : ''"
:backgroundColor=" :background-color="
playlist.subscribed ? 'var(--color-secondary-bg)' : '' playlist.subscribed ? 'var(--color-secondary-bg)' : ''
" "
@click.native="likePlaylist" @click.native="likePlaylist"
> >
</ButtonTwoTone> </ButtonTwoTone>
<ButtonTwoTone <ButtonTwoTone
iconClass="more" icon-class="more"
:iconButton="true" :icon-button="true"
:horizontalPadding="0" :horizontal-padding="0"
color="grey" color="grey"
@click.native="openMenu" @click.native="openMenu"
> >
@ -141,19 +141,19 @@
</div> </div>
</div> </div>
<div class="user-info" v-if="isLikeSongsPage"> <div v-if="isLikeSongsPage" class="user-info">
<h1> <h1>
<img class="avatar" :src="data.user.avatarUrl | resizeImage" />{{ <img class="avatar" :src="data.user.avatarUrl | resizeImage" />{{
data.user.nickname data.user.nickname
}}{{ $t("library.sLikedSongs") }} }}{{ $t('library.sLikedSongs') }}
</h1> </h1>
</div> </div>
<TrackList <TrackList
:tracks="filteredTracks"
:type="'playlist'"
:id="playlist.id" :id="playlist.id"
:extraContextMenuItem=" :tracks="filteredTracks"
type="playlist"
:extra-context-menu-item="
isUserOwnPlaylist ? ['removeTrackFromPlaylist'] : [] isUserOwnPlaylist ? ['removeTrackFromPlaylist'] : []
" "
/> />
@ -161,27 +161,27 @@
<Modal <Modal
:show="showFullDescription" :show="showFullDescription"
:close="() => (showFullDescription = false)" :close="() => (showFullDescription = false)"
:showFooter="false" :show-footer="false"
:clickOutsideHide="true" :click-outside-hide="true"
title="歌单介绍" title="歌单介绍"
>{{ playlist.description }}</Modal >{{ playlist.description }}</Modal
> >
<ContextMenu ref="playlistMenu"> <ContextMenu ref="playlistMenu">
<div class="item">{{ $t("contextMenu.playNext") }}</div> <div class="item">{{ $t('contextMenu.playNext') }}</div>
<div class="item" @click="likePlaylist(true)">{{ <div class="item" @click="likePlaylist(true)">{{
playlist.subscribed ? "从音乐库删除" : "保存到音乐库" playlist.subscribed ? '从音乐库删除' : '保存到音乐库'
}}</div> }}</div>
<div class="item" @click="searchInPlaylist()"></div> <div class="item" @click="searchInPlaylist()"></div>
<div <div
class="item"
v-if="playlist.creator.userId === data.user.userId" v-if="playlist.creator.userId === data.user.userId"
class="item"
@click="editPlaylist" @click="editPlaylist"
>编辑歌单信息</div >编辑歌单信息</div
> >
<div <div
class="item"
v-if="playlist.creator.userId === data.user.userId" v-if="playlist.creator.userId === data.user.userId"
class="item"
@click="deletePlaylist" @click="deletePlaylist"
>删除歌单</div >删除歌单</div
> >
@ -190,112 +190,112 @@
</template> </template>
<script> <script>
import { mapMutations, mapActions, mapState } from "vuex"; import { mapMutations, mapActions, mapState } from 'vuex';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import { import {
getPlaylistDetail, getPlaylistDetail,
subscribePlaylist, subscribePlaylist,
deletePlaylist, deletePlaylist,
} from "@/api/playlist"; } from '@/api/playlist';
import { getTrackDetail } from "@/api/track"; import { getTrackDetail } from '@/api/track';
import { isAccountLoggedIn } from "@/utils/auth"; import { isAccountLoggedIn } from '@/utils/auth';
import nativeAlert from "@/utils/nativeAlert"; import nativeAlert from '@/utils/nativeAlert';
import ButtonTwoTone from "@/components/ButtonTwoTone.vue"; import ButtonTwoTone from '@/components/ButtonTwoTone.vue';
import ContextMenu from "@/components/ContextMenu.vue"; import ContextMenu from '@/components/ContextMenu.vue';
import TrackList from "@/components/TrackList.vue"; import TrackList from '@/components/TrackList.vue';
import Cover from "@/components/Cover.vue"; import Cover from '@/components/Cover.vue';
import Modal from "@/components/Modal.vue"; import Modal from '@/components/Modal.vue';
const specialPlaylist = { const specialPlaylist = {
2829816518: { 2829816518: {
name: "欧美私人订制", name: '欧美私人订制',
gradient: "gradient-pink-purple-blue", gradient: 'gradient-pink-purple-blue',
}, },
2890490211: { 2890490211: {
name: "助眠鸟鸣声", name: '助眠鸟鸣声',
gradient: "gradient-green", gradient: 'gradient-green',
}, },
5089855855: { 5089855855: {
name: "夜的胡思乱想", name: '夜的胡思乱想',
gradient: "gradient-moonstone-blue", gradient: 'gradient-moonstone-blue',
}, },
2888212971: { 2888212971: {
name: "全球百大DJ", name: '全球百大DJ',
gradient: "gradient-orange-red", gradient: 'gradient-orange-red',
}, },
2829733864: { 2829733864: {
name: "睡眠伴侣", name: '睡眠伴侣',
gradient: "gradient-midnight-blue", gradient: 'gradient-midnight-blue',
}, },
2829844572: { 2829844572: {
name: "洗澡时听的歌", name: '洗澡时听的歌',
gradient: "gradient-yellow", gradient: 'gradient-yellow',
}, },
2920647537: { 2920647537: {
name: "还是会想你", name: '还是会想你',
gradient: "gradient-dark-blue-midnight-blue", gradient: 'gradient-dark-blue-midnight-blue',
}, },
2890501416: { 2890501416: {
name: "助眠白噪声", name: '助眠白噪声',
gradient: "gradient-sky-blue", gradient: 'gradient-sky-blue',
}, },
5217150082: { 5217150082: {
name: "摇滚唱片行", name: '摇滚唱片行',
gradient: "gradient-yellow-red", gradient: 'gradient-yellow-red',
}, },
2829961453: { 2829961453: {
name: "古风音乐大赏", name: '古风音乐大赏',
gradient: "gradient-fog", gradient: 'gradient-fog',
}, },
4923261701: { 4923261701: {
name: "Trance", name: 'Trance',
gradient: "gradient-light-red-light-blue ", gradient: 'gradient-light-red-light-blue ',
}, },
5212729721: { 5212729721: {
name: "欧美点唱机", name: '欧美点唱机',
gradient: "gradient-indigo-pink-yellow", gradient: 'gradient-indigo-pink-yellow',
}, },
3103434282: { 3103434282: {
name: "甜蜜少女心", name: '甜蜜少女心',
gradient: "gradient-pink", gradient: 'gradient-pink',
}, },
2829896389: { 2829896389: {
name: "日系私人订制", name: '日系私人订制',
gradient: "gradient-yellow-pink", gradient: 'gradient-yellow-pink',
}, },
2829779628: { 2829779628: {
name: "运动随身听", name: '运动随身听',
gradient: "gradient-orange-red", gradient: 'gradient-orange-red',
}, },
2860654884: { 2860654884: {
name: "独立女声精选", name: '独立女声精选',
gradient: "gradient-sharp-blue", gradient: 'gradient-sharp-blue',
}, },
898150: { 898150: {
name: "浪漫婚礼专用", name: '浪漫婚礼专用',
gradient: "gradient-pink", gradient: 'gradient-pink',
}, },
2638104052: { 2638104052: {
name: "牛奶泡泡浴", name: '牛奶泡泡浴',
gradient: "gradient-fog", gradient: 'gradient-fog',
}, },
5317236517: { 5317236517: {
name: "后朋克精选", name: '后朋克精选',
gradient: "gradient-pink-purple-blue", gradient: 'gradient-pink-purple-blue',
}, },
2821115454: { 2821115454: {
name: "一周原创发现", name: '一周原创发现',
gradient: "gradient-blue-purple", gradient: 'gradient-blue-purple',
}, },
3136952023: { 3136952023: {
name: "私人雷达", name: '私人雷达',
gradient: "gradient-radar", gradient: 'gradient-radar',
}, },
}; };
export default { export default {
name: "Playlist", name: 'Playlist',
components: { components: {
Cover, Cover,
ButtonTwoTone, ButtonTwoTone,
@ -303,14 +303,21 @@ export default {
Modal, Modal,
ContextMenu, ContextMenu,
}, },
directives: {
focus: {
inserted: function (el) {
el.focus();
},
},
},
data() { data() {
return { return {
show: false, show: false,
playlist: { playlist: {
id: 0, id: 0,
coverImgUrl: "", coverImgUrl: '',
creator: { creator: {
userId: "", userId: '',
}, },
trackIds: [], trackIds: [],
}, },
@ -319,26 +326,16 @@ export default {
loadingMore: false, loadingMore: false,
lastLoadedTrackIndex: 9, lastLoadedTrackIndex: 9,
displaySearchInPlaylist: false, displaySearchInPlaylist: false,
searchKeyWords: "", // 使 searchKeyWords: '', // 使
inputSearchKeyWords: "", // inputSearchKeyWords: '', //
inputFocus: false, inputFocus: false,
debounceTimeout: null, debounceTimeout: null,
}; };
}, },
created() {
if (this.$route.name === "likedSongs") {
this.loadData(this.data.likedSongPlaylistID);
} else {
this.loadData(this.$route.params.id);
}
},
destroyed() {
window.removeEventListener("scroll", this.handleScroll, true);
},
computed: { computed: {
...mapState(["player", "data"]), ...mapState(['player', 'data']),
isLikeSongsPage() { isLikeSongsPage() {
return this.$route.name === "likedSongs"; return this.$route.name === 'likedSongs';
}, },
specialPlaylistInfo() { specialPlaylistInfo() {
return specialPlaylist[this.playlist.id]; return specialPlaylist[this.playlist.id];
@ -351,7 +348,7 @@ export default {
}, },
filteredTracks() { filteredTracks() {
return this.tracks.filter( return this.tracks.filter(
(track) => track =>
(track.name && (track.name &&
track.name track.name
.toLowerCase() .toLowerCase()
@ -361,7 +358,7 @@ export default {
.toLowerCase() .toLowerCase()
.includes(this.searchKeyWords.toLowerCase())) || .includes(this.searchKeyWords.toLowerCase())) ||
track.ar.find( track.ar.find(
(artist) => artist =>
artist.name && artist.name &&
artist.name artist.name
.toLowerCase() .toLowerCase()
@ -370,35 +367,45 @@ export default {
); );
}, },
}, },
created() {
if (this.$route.name === 'likedSongs') {
this.loadData(this.data.likedSongPlaylistID);
} else {
this.loadData(this.$route.params.id);
}
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll, true);
},
methods: { methods: {
...mapMutations(["appendTrackToPlayerList"]), ...mapMutations(['appendTrackToPlayerList']),
...mapActions(["playFirstTrackOnList", "playTrackOnListByID", "showToast"]), ...mapActions(['playFirstTrackOnList', 'playTrackOnListByID', 'showToast']),
playPlaylistByID(trackID = "first") { playPlaylistByID(trackID = 'first') {
let trackIDs = this.playlist.trackIds.map((t) => t.id); let trackIDs = this.playlist.trackIds.map(t => t.id);
this.$store.state.player.replacePlaylist( this.$store.state.player.replacePlaylist(
trackIDs, trackIDs,
this.playlist.id, this.playlist.id,
"playlist", 'playlist',
trackID trackID
); );
}, },
likePlaylist(toast = false) { likePlaylist(toast = false) {
if (!isAccountLoggedIn()) { if (!isAccountLoggedIn()) {
this.showToast("此操作需要登录网易云账号"); this.showToast('此操作需要登录网易云账号');
return; return;
} }
subscribePlaylist({ subscribePlaylist({
id: this.playlist.id, id: this.playlist.id,
t: this.playlist.subscribed ? 2 : 1, t: this.playlist.subscribed ? 2 : 1,
}).then((data) => { }).then(data => {
if (data.code === 200) { if (data.code === 200) {
this.playlist.subscribed = !this.playlist.subscribed; this.playlist.subscribed = !this.playlist.subscribed;
if (toast === true) if (toast === true)
this.showToast( this.showToast(
this.playlist.subscribed ? "已保存到音乐库" : "已从音乐库删除" this.playlist.subscribed ? '已保存到音乐库' : '已从音乐库删除'
); );
} }
getPlaylistDetail(this.id, true).then((data) => { getPlaylistDetail(this.id, true).then(data => {
this.playlist = data.playlist; this.playlist = data.playlist;
}); });
}); });
@ -406,7 +413,7 @@ export default {
loadData(id, next = undefined) { loadData(id, next = undefined) {
this.id = id; this.id = id;
getPlaylistDetail(this.id, true) getPlaylistDetail(this.id, true)
.then((data) => { .then(data => {
this.playlist = data.playlist; this.playlist = data.playlist;
this.tracks = data.playlist.tracks; this.tracks = data.playlist.tracks;
NProgress.done(); NProgress.done();
@ -414,7 +421,7 @@ export default {
this.show = true; this.show = true;
this.lastLoadedTrackIndex = data.playlist.tracks.length - 1; this.lastLoadedTrackIndex = data.playlist.tracks.length - 1;
if (this.playlist.trackCount > this.tracks.length) { if (this.playlist.trackCount > this.tracks.length) {
window.addEventListener("scroll", this.handleScroll, true); window.addEventListener('scroll', this.handleScroll, true);
} }
return data; return data;
}) })
@ -433,15 +440,15 @@ export default {
) )
return t; return t;
}); });
trackIDs = trackIDs.map((t) => t.id); trackIDs = trackIDs.map(t => t.id);
getTrackDetail(trackIDs.join(",")).then((data) => { getTrackDetail(trackIDs.join(',')).then(data => {
this.tracks.push(...data.songs); this.tracks.push(...data.songs);
this.lastLoadedTrackIndex += trackIDs.length; this.lastLoadedTrackIndex += trackIDs.length;
this.loadingMore = false; this.loadingMore = false;
}); });
}, },
handleScroll(e) { handleScroll(e) {
let dom = document.querySelector("html"); let dom = document.querySelector('html');
let scrollHeight = Math.max(dom.scrollHeight, dom.scrollHeight); let scrollHeight = Math.max(dom.scrollHeight, dom.scrollHeight);
let scrollTop = e.target.scrollingElement.scrollTop; let scrollTop = e.target.scrollingElement.scrollTop;
let clientHeight = let clientHeight =
@ -461,39 +468,39 @@ export default {
}, },
deletePlaylist() { deletePlaylist() {
if (!isAccountLoggedIn()) { if (!isAccountLoggedIn()) {
this.showToast("此操作需要登录网易云账号"); this.showToast('此操作需要登录网易云账号');
return; return;
} }
let confirmation = confirm(`确定要删除歌单 ${this.playlist.name}`); let confirmation = confirm(`确定要删除歌单 ${this.playlist.name}`);
if (confirmation === true) { if (confirmation === true) {
deletePlaylist(this.playlist.id).then((data) => { deletePlaylist(this.playlist.id).then(data => {
if (data.code === 200) { if (data.code === 200) {
nativeAlert(`已删除歌单 ${this.playlist.name}`); nativeAlert(`已删除歌单 ${this.playlist.name}`);
this.$router.go(-1); this.$router.go(-1);
} else { } else {
nativeAlert("发生错误"); nativeAlert('发生错误');
} }
}); });
} }
}, },
editPlaylist() { editPlaylist() {
nativeAlert("此功能开发中"); nativeAlert('此功能开发中');
}, },
searchInPlaylist() { searchInPlaylist() {
this.displaySearchInPlaylist = !this.displaySearchInPlaylist; this.displaySearchInPlaylist = !this.displaySearchInPlaylist;
if (this.displaySearchInPlaylist == false) { if (this.displaySearchInPlaylist == false) {
this.searchKeyWords = ""; this.searchKeyWords = '';
this.inputSearchKeyWords = ""; this.inputSearchKeyWords = '';
} else { } else {
this.loadMore(500); this.loadMore(500);
} }
}, },
removeTrack(trackID) { removeTrack(trackID) {
if (!isAccountLoggedIn()) { if (!isAccountLoggedIn()) {
this.showToast("此操作需要登录网易云账号"); this.showToast('此操作需要登录网易云账号');
return; return;
} }
this.tracks = this.tracks.filter((t) => t.id !== trackID); this.tracks = this.tracks.filter(t => t.id !== trackID);
}, },
inputDebounce() { inputDebounce() {
if (this.debounceTimeout) clearTimeout(this.debounceTimeout); if (this.debounceTimeout) clearTimeout(this.debounceTimeout);
@ -502,13 +509,6 @@ export default {
}, 600); }, 600);
}, },
}, },
directives: {
focus: {
inserted: function (el) {
el.focus();
},
},
},
}; };
</script> </script>
@ -647,7 +647,7 @@ export default {
background-image: linear-gradient(to left, #92fe9d 0%, #00c9ff 100%); background-image: linear-gradient(to left, #92fe9d 0%, #00c9ff 100%);
} }
[data-theme="dark"] { [data-theme='dark'] {
.gradient-radar { .gradient-radar {
background-image: linear-gradient(to left, #92fe9d 0%, #00c9ff 100%); background-image: linear-gradient(to left, #92fe9d 0%, #00c9ff 100%);
} }
@ -824,7 +824,7 @@ export default {
} }
} }
[data-theme="dark"] { [data-theme='dark'] {
.search-box { .search-box {
.active { .active {
input, input,

@ -1,83 +1,83 @@
<template> <template>
<div class="search" v-show="show"> <div v-show="show" class="search">
<div class="row" v-show="artists.length > 0 || albums.length > 0"> <div v-show="artists.length > 0 || albums.length > 0" class="row">
<div class="artists" v-show="artists.length > 0"> <div v-show="artists.length > 0" class="artists">
<div class="section-title" v-show="artists.length > 0" <div v-show="artists.length > 0" class="section-title"
>{{ $t("search.artist") >{{ $t('search.artist')
}}<router-link :to="`/search/${keywords}/artists`">{{ }}<router-link :to="`/search/${keywords}/artists`">{{
$t("home.seeMore") $t('home.seeMore')
}}</router-link></div }}</router-link></div
> >
<CoverRow <CoverRow
type="artist" type="artist"
:columnNumber="3" :column-number="3"
:items="artists.slice(0, 3)" :items="artists.slice(0, 3)"
gap="34px 24px" gap="34px 24px"
/> />
</div> </div>
<div class="albums"> <div class="albums">
<div class="section-title" v-show="albums.length > 0" <div v-show="albums.length > 0" class="section-title"
>{{ $t("search.album") >{{ $t('search.album')
}}<router-link :to="`/search/${keywords}/albums`">{{ }}<router-link :to="`/search/${keywords}/albums`">{{
$t("home.seeMore") $t('home.seeMore')
}}</router-link></div }}</router-link></div
> >
<CoverRow <CoverRow
type="album" type="album"
:items="albums.slice(0, 3)" :items="albums.slice(0, 3)"
subText="artist" sub-text="artist"
:columnNumber="3" :column-number="3"
subTextFontSize="14px" sub-text-font-size="14px"
gap="34px 24px" gap="34px 24px"
:playButtonSize="26" :play-button-size="26"
/> />
</div> </div>
</div> </div>
<div class="tracks" v-show="tracks.length > 0"> <div v-show="tracks.length > 0" class="tracks">
<div class="section-title" <div class="section-title"
>{{ $t("search.song") >{{ $t('search.song')
}}<router-link :to="`/search/${keywords}/tracks`">{{ }}<router-link :to="`/search/${keywords}/tracks`">{{
$t("home.seeMore") $t('home.seeMore')
}}</router-link></div }}</router-link></div
> >
<TrackList :tracks="tracks" type="tracklist" /> <TrackList :tracks="tracks" type="tracklist" />
</div> </div>
<div class="music-videos" v-show="musicVideos.length > 0"> <div v-show="musicVideos.length > 0" class="music-videos">
<div class="section-title" <div class="section-title"
>{{ $t("search.mv") >{{ $t('search.mv')
}}<router-link :to="`/search/${keywords}/music-videos`">{{ }}<router-link :to="`/search/${keywords}/music-videos`">{{
$t("home.seeMore") $t('home.seeMore')
}}</router-link></div }}</router-link></div
> >
<MvRow :mvs="musicVideos.slice(0, 5)" /> <MvRow :mvs="musicVideos.slice(0, 5)" />
</div> </div>
<div class="playlists" v-show="playlists.length > 0"> <div v-show="playlists.length > 0" class="playlists">
<div class="section-title" <div class="section-title"
>{{ $t("search.playlist") >{{ $t('search.playlist')
}}<router-link :to="`/search/${keywords}/playlists`">{{ }}<router-link :to="`/search/${keywords}/playlists`">{{
$t("home.seeMore") $t('home.seeMore')
}}</router-link></div }}</router-link></div
> >
<CoverRow <CoverRow
type="playlist" type="playlist"
:items="playlists.slice(0, 12)" :items="playlists.slice(0, 12)"
subText="title" sub-text="title"
:columnNumber="6" :column-number="6"
subTextFontSize="14px" sub-text-font-size="14px"
gap="34px 24px" gap="34px 24px"
:playButtonSize="26" :play-button-size="26"
/> />
</div> </div>
<div class="no-results" v-show="!haveResult"> <div v-show="!haveResult" class="no-results">
<div <div
><svg-icon icon-class="search" /> ><svg-icon icon-class="search" />
{{ {{
keywords.length === 0 ? "输入关键字搜索" : $t("search.noResult") keywords.length === 0 ? '输入关键字搜索' : $t('search.noResult')
}}</div }}</div
> >
</div> </div>
@ -85,16 +85,16 @@
</template> </template>
<script> <script>
import { getTrackDetail } from "@/api/track"; import { getTrackDetail } from '@/api/track';
import { search } from "@/api/others"; import { search } from '@/api/others';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import TrackList from "@/components/TrackList.vue"; import TrackList from '@/components/TrackList.vue';
import MvRow from "@/components/MvRow.vue"; import MvRow from '@/components/MvRow.vue';
import CoverRow from "@/components/CoverRow.vue"; import CoverRow from '@/components/CoverRow.vue';
export default { export default {
name: "Search", name: 'Search',
components: { components: {
TrackList, TrackList,
MvRow, MvRow,
@ -112,7 +112,7 @@ export default {
}, },
computed: { computed: {
keywords() { keywords() {
return this.$route.params.keywords ?? ""; return this.$route.params.keywords ?? '';
}, },
haveResult() { haveResult() {
return ( return (
@ -125,12 +125,21 @@ export default {
); );
}, },
}, },
watch: {
keywords: function (newKeywords) {
if (newKeywords.length === 0) return;
this.getData();
},
},
created() {
this.getData();
},
methods: { methods: {
playTrackInSearchResult(id) { playTrackInSearchResult(id) {
let track = this.tracks.find((t) => t.id === id); let track = this.tracks.find(t => t.id === id);
this.$store.state.player.appendTrackToPlayerList(track, true); this.$store.state.player.appendTrackToPlayerList(track, true);
}, },
search(type = "all") { search(type = 'all') {
const typeTable = { const typeTable = {
all: 1018, all: 1018,
musicVideos: 1004, musicVideos: 1004,
@ -143,7 +152,7 @@ export default {
keywords: this.keywords, keywords: this.keywords,
type: typeTable[type], type: typeTable[type],
limit: 16, limit: 16,
}).then((result) => { }).then(result => {
return { result: result.result, type }; return { result: result.result, type };
}); });
}, },
@ -151,32 +160,32 @@ export default {
NProgress.start(); NProgress.start();
this.show = false; this.show = false;
const requestAll = (requests) => { const requestAll = requests => {
const keywords = this.keywords; const keywords = this.keywords;
Promise.all(requests).then((results) => { Promise.all(requests).then(results => {
if (keywords != this.keywords) return; if (keywords != this.keywords) return;
results.map((result) => { results.map(result => {
const searchType = result.type; const searchType = result.type;
if (result.result === undefined) return; if (result.result === undefined) return;
result = result.result; result = result.result;
switch (searchType) { switch (searchType) {
case "all": case 'all':
this.result = result; this.result = result;
break; break;
case "musicVideos": case 'musicVideos':
this.musicVideos = result.mvs ?? []; this.musicVideos = result.mvs ?? [];
break; break;
case "artists": case 'artists':
this.artists = result.artists ?? []; this.artists = result.artists ?? [];
break; break;
case "albums": case 'albums':
this.albums = result.albums ?? []; this.albums = result.albums ?? [];
break; break;
case "tracks": case 'tracks':
this.tracks = result.songs ?? []; this.tracks = result.songs ?? [];
this.getTracksDetail(); this.getTracksDetail();
break; break;
case "playlists": case 'playlists':
this.playlists = result.playlists ?? []; this.playlists = result.playlists ?? [];
break; break;
} }
@ -187,32 +196,23 @@ export default {
}; };
const requests = [ const requests = [
this.search("artists"), this.search('artists'),
this.search("albums"), this.search('albums'),
this.search("tracks"), this.search('tracks'),
]; ];
const requests2 = [this.search("musicVideos"), this.search("playlists")]; const requests2 = [this.search('musicVideos'), this.search('playlists')];
requestAll(requests); requestAll(requests);
requestAll(requests2); requestAll(requests2);
}, },
getTracksDetail() { getTracksDetail() {
const trackIDs = this.tracks.map((t) => t.id); const trackIDs = this.tracks.map(t => t.id);
if (trackIDs.length === 0) return; if (trackIDs.length === 0) return;
getTrackDetail(trackIDs.join(",")).then((result) => { getTrackDetail(trackIDs.join(',')).then(result => {
this.tracks = result.songs; this.tracks = result.songs;
}); });
}, },
}, },
created() {
this.getData();
},
watch: {
keywords: function (newKeywords) {
if (newKeywords.length === 0) return;
this.getData();
},
},
}; };
</script> </script>

@ -1,57 +1,57 @@
<template> <template>
<div class="search" v-show="show"> <div v-show="show" class="search">
<h1> <h1>
<span>{{ $t("search.searchFor") }}{{ typeNameTable[type] }}</span> "{{ <span>{{ $t('search.searchFor') }}{{ typeNameTable[type] }}</span> "{{
keywords keywords
}}" }}"
</h1> </h1>
<div v-if="type === 'artists'"> <div v-if="type === 'artists'">
<CoverRow type="artist" :items="result" :columnNumber="6" /> <CoverRow type="artist" :items="result" :column-number="6" />
</div> </div>
<div v-if="type === 'albums'"> <div v-if="type === 'albums'">
<CoverRow <CoverRow
type="album" type="album"
:items="result" :items="result"
subText="artist" sub-text="artist"
subTextFontSize="14px" sub-text-font-size="14px"
/> />
</div> </div>
<div v-if="type === 'tracks'"> <div v-if="type === 'tracks'">
<TrackList <TrackList
:tracks="result" :tracks="result"
type="playlist" type="playlist"
dbclickTrackFunc="playAList" dbclick-track-func="playAList"
/> />
</div> </div>
<div v-if="type === 'musicVideos'"> <div v-if="type === 'musicVideos'">
<MvRow :mvs="result" /> <MvRow :mvs="result" />
</div> </div>
<div v-if="type === 'playlists'"> <div v-if="type === 'playlists'">
<CoverRow type="playlist" :items="result" subText="title" /> <CoverRow type="playlist" :items="result" sub-text="title" />
</div> </div>
<div class="load-more"> <div class="load-more">
<ButtonTwoTone v-show="hasMore" @click.native="fetchData" color="grey">{{ <ButtonTwoTone v-show="hasMore" color="grey" @click.native="fetchData">{{
$t("explore.loadMore") $t('explore.loadMore')
}}</ButtonTwoTone> }}</ButtonTwoTone>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { getTrackDetail } from "@/api/track"; import { getTrackDetail } from '@/api/track';
import { search } from "@/api/others"; import { search } from '@/api/others';
import { camelCase } from "change-case"; import { camelCase } from 'change-case';
import NProgress from "nprogress"; import NProgress from 'nprogress';
import TrackList from "@/components/TrackList.vue"; import TrackList from '@/components/TrackList.vue';
import MvRow from "@/components/MvRow.vue"; import MvRow from '@/components/MvRow.vue';
import CoverRow from "@/components/CoverRow.vue"; import CoverRow from '@/components/CoverRow.vue';
import ButtonTwoTone from "@/components/ButtonTwoTone.vue"; import ButtonTwoTone from '@/components/ButtonTwoTone.vue';
export default { export default {
name: "Search", name: 'Search',
components: { components: {
TrackList, TrackList,
MvRow, MvRow,
@ -70,14 +70,17 @@ export default {
}, },
typeNameTable() { typeNameTable() {
return { return {
musicVideos: "MV", musicVideos: 'MV',
tracks: "歌曲", tracks: '歌曲',
albums: "专辑", albums: '专辑',
artists: "艺人", artists: '艺人',
playlists: "歌单", playlists: '歌单',
}; };
}, },
}, },
created() {
this.fetchData();
},
methods: { methods: {
fetchData() { fetchData() {
const typeTable = { const typeTable = {
@ -91,30 +94,30 @@ export default {
keywords: this.keywords, keywords: this.keywords,
type: typeTable[this.type], type: typeTable[this.type],
offset: this.result.length, offset: this.result.length,
}).then((result) => { }).then(result => {
result = result.result; result = result.result;
this.hasMore = result.hasMore ?? true; this.hasMore = result.hasMore ?? true;
switch (this.type) { switch (this.type) {
case "musicVideos": case 'musicVideos':
this.result.push(...result.mvs); this.result.push(...result.mvs);
if (result.mvCount <= this.result.length) { if (result.mvCount <= this.result.length) {
this.hasMore = false; this.hasMore = false;
} }
break; break;
case "artists": case 'artists':
this.result.push(...result.artists); this.result.push(...result.artists);
break; break;
case "albums": case 'albums':
this.result.push(...result.albums); this.result.push(...result.albums);
if (result.albumCount <= this.result.length) { if (result.albumCount <= this.result.length) {
this.hasMore = false; this.hasMore = false;
} }
break; break;
case "tracks": case 'tracks':
this.result.push(...result.songs); this.result.push(...result.songs);
this.getTracksDetail(); this.getTracksDetail();
break; break;
case "playlists": case 'playlists':
this.result.push(...result.playlists); this.result.push(...result.playlists);
break; break;
} }
@ -123,16 +126,13 @@ export default {
}); });
}, },
getTracksDetail() { getTracksDetail() {
const trackIDs = this.result.map((t) => t.id); const trackIDs = this.result.map(t => t.id);
if (trackIDs.length === 0) return; if (trackIDs.length === 0) return;
getTrackDetail(trackIDs.join(",")).then((result) => { getTrackDetail(trackIDs.join(',')).then(result => {
this.result = result.songs; this.result = result.songs;
}); });
}, },
}, },
created() {
this.fetchData();
},
}; };
</script> </script>

@ -21,14 +21,14 @@
<div class="right"> <div class="right">
<button @click="logout"> <button @click="logout">
<svg-icon icon-class="logout" /> <svg-icon icon-class="logout" />
{{ $t("settings.logout") }} {{ $t('settings.logout') }}
</button> </button>
</div> </div>
</div> </div>
<h2>{{ $t("settings.settings") }}</h2> <h2>{{ $t('settings.settings') }}</h2>
<div class="item"> <div class="item">
<div class="left"> <div class="left">
<div class="title"> {{ $t("settings.language") }} </div> <div class="title"> {{ $t('settings.language') }} </div>
</div> </div>
<div class="right"> <div class="right">
<select v-model="lang"> <select v-model="lang">
@ -40,44 +40,44 @@
</div> </div>
<div class="item"> <div class="item">
<div class="left"> <div class="left">
<div class="title"> {{ $t("settings.appearance.text") }} </div> <div class="title"> {{ $t('settings.appearance.text') }} </div>
</div> </div>
<div class="right"> <div class="right">
<select v-model="appearance"> <select v-model="appearance">
<option value="auto">{{ $t("settings.appearance.auto") }}</option> <option value="auto">{{ $t('settings.appearance.auto') }}</option>
<option value="light" <option value="light"
>🌞 {{ $t("settings.appearance.light") }}</option >🌞 {{ $t('settings.appearance.light') }}</option
> >
<option value="dark" <option value="dark"
>🌚 {{ $t("settings.appearance.dark") }}</option >🌚 {{ $t('settings.appearance.dark') }}</option
> >
</select> </select>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<div class="left"> <div class="left">
<div class="title"> {{ $t("settings.musicQuality.text") }} </div> <div class="title"> {{ $t('settings.musicQuality.text') }} </div>
</div> </div>
<div class="right"> <div class="right">
<select v-model="musicQuality"> <select v-model="musicQuality">
<option value="128000"> <option value="128000">
{{ $t("settings.musicQuality.low") }} - 128Kbps {{ $t('settings.musicQuality.low') }} - 128Kbps
</option> </option>
<option value="192000"> <option value="192000">
{{ $t("settings.musicQuality.medium") }} - 192Kbps {{ $t('settings.musicQuality.medium') }} - 192Kbps
</option> </option>
<option value="320000"> <option value="320000">
{{ $t("settings.musicQuality.high") }} - 320Kbps {{ $t('settings.musicQuality.high') }} - 320Kbps
</option> </option>
<option value="999000"> <option value="999000">
{{ $t("settings.musicQuality.lossless") }} - FLAC {{ $t('settings.musicQuality.lossless') }} - FLAC
</option> </option>
</select> </select>
</div> </div>
</div> </div>
<div v-if="isElectron" class="item"> <div v-if="isElectron" class="item">
<div class="left"> <div class="left">
<div class="title"> {{ $t("settings.deviceSelector") }} </div> <div class="title"> {{ $t('settings.deviceSelector') }} </div>
</div> </div>
<div class="right"> <div class="right">
<select v-model="outputDevice" :disabled="withoutAudioPrivilege"> <select v-model="outputDevice" :disabled="withoutAudioPrivilege">
@ -95,7 +95,7 @@
<div v-if="isElectron" class="item"> <div v-if="isElectron" class="item">
<div class="left"> <div class="left">
<div class="title"> <div class="title">
{{ $t("settings.automaticallyCacheSongs") }} {{ $t('settings.automaticallyCacheSongs') }}
</div> </div>
</div> </div>
<div class="right"> <div class="right">
@ -112,12 +112,12 @@
</div> </div>
<div v-if="isElectron" class="item"> <div v-if="isElectron" class="item">
<div class="left"> <div class="left">
<div class="title"> {{ $t("settings.cacheLimit.text") }} </div> <div class="title"> {{ $t('settings.cacheLimit.text') }} </div>
</div> </div>
<div class="right"> <div class="right">
<select v-model="cacheLimit"> <select v-model="cacheLimit">
<option :value="false"> <option :value="false">
{{ $t("settings.cacheLimit.none") }} {{ $t('settings.cacheLimit.none') }}
</option> </option>
<option :value="512"> 500MB </option> <option :value="512"> 500MB </option>
<option :value="1024"> 1GB </option> <option :value="1024"> 1GB </option>
@ -130,7 +130,7 @@
<div class="left"> <div class="left">
<div class="title"> <div class="title">
{{ {{
$t("settings.cacheCount", { $t('settings.cacheCount', {
song: tracksCache.length, song: tracksCache.length,
size: tracksCache.size, size: tracksCache.size,
}) })
@ -139,13 +139,13 @@
</div> </div>
<div class="right"> <div class="right">
<button @click="clearCache()"> <button @click="clearCache()">
{{ $t("settings.clearSongsCache") }} {{ $t('settings.clearSongsCache') }}
</button> </button>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<div class="left"> <div class="left">
<div class="title">{{ $t("settings.showLyricsTranslation") }}</div> <div class="title">{{ $t('settings.showLyricsTranslation') }}</div>
</div> </div>
<div class="right"> <div class="right">
<div class="toggle"> <div class="toggle">
@ -162,7 +162,7 @@
<div class="item"> <div class="item">
<div class="left"> <div class="left">
<div class="title">{{ <div class="title">{{
$t("settings.showLyricsDynamicBackground") $t('settings.showLyricsDynamicBackground')
}}</div> }}</div>
</div> </div>
<div class="right"> <div class="right">
@ -179,28 +179,28 @@
</div> </div>
<div class="item"> <div class="item">
<div class="left"> <div class="left">
<div class="title"> {{ $t("settings.lyricFontSize.text") }} </div> <div class="title"> {{ $t('settings.lyricFontSize.text') }} </div>
</div> </div>
<div class="right"> <div class="right">
<select v-model="lyricFontSize"> <select v-model="lyricFontSize">
<option value="16"> <option value="16">
{{ $t("settings.lyricFontSize.small") }} - 16px {{ $t('settings.lyricFontSize.small') }} - 16px
</option> </option>
<option value="22"> <option value="22">
{{ $t("settings.lyricFontSize.medium") }} - 22px {{ $t('settings.lyricFontSize.medium') }} - 22px
</option> </option>
<option value="28"> <option value="28">
{{ $t("settings.lyricFontSize.large") }} - 28px {{ $t('settings.lyricFontSize.large') }} - 28px
</option> </option>
<option value="36"> <option value="36">
{{ $t("settings.lyricFontSize.xlarge") }} - 36px {{ $t('settings.lyricFontSize.xlarge') }} - 36px
</option> </option>
</select> </select>
</div> </div>
</div> </div>
<div v-if="isElectron && !isMac" class="item"> <div v-if="isElectron && !isMac" class="item">
<div class="left"> <div class="left">
<div class="title">{{ $t("settings.minimizeToTray") }}</div> <div class="title">{{ $t('settings.minimizeToTray') }}</div>
</div> </div>
<div class="right"> <div class="right">
<div class="toggle"> <div class="toggle">
@ -221,7 +221,7 @@
{{ {{
isLastfmConnected isLastfmConnected
? `已连接到 Last.fm (${lastfm.name})` ? `已连接到 Last.fm (${lastfm.name})`
: "连接 Last.fm " : '连接 Last.fm '
}}</div }}</div
> >
</div> </div>
@ -235,7 +235,7 @@
<div class="item"> <div class="item">
<div class="left"> <div class="left">
<div class="title"> {{ $t("settings.showLibraryDefault") }}</div> <div class="title"> {{ $t('settings.showLibraryDefault') }}</div>
</div> </div>
<div class="right"> <div class="right">
<div class="toggle"> <div class="toggle">
@ -277,7 +277,7 @@
<div class="item"> <div class="item">
<div class="left"> <div class="left">
<div class="title"> <div class="title">
{{ $t("settings.showPlaylistsByAppleMusic") }}</div {{ $t('settings.showPlaylistsByAppleMusic') }}</div
> >
</div> </div>
<div class="right"> <div class="right">
@ -295,7 +295,7 @@
<div v-if="isElectron" class="item"> <div v-if="isElectron" class="item">
<div class="left"> <div class="left">
<div class="title"> <div class="title">
{{ $t("settings.enableDiscordRichPresence") }}</div {{ $t('settings.enableDiscordRichPresence') }}</div
> >
</div> </div>
<div class="right"> <div class="right">
@ -312,7 +312,7 @@
</div> </div>
<div v-if="isElectron" class="item"> <div v-if="isElectron" class="item">
<div class="left"> <div class="left">
<div class="title"> {{ $t("settings.enableGlobalShortcut") }}</div> <div class="title"> {{ $t('settings.enableGlobalShortcut') }}</div>
</div> </div>
<div class="right"> <div class="right">
<div class="toggle"> <div class="toggle">
@ -355,32 +355,32 @@
</template> </template>
<script> <script>
import { mapState } from "vuex"; import { mapState } from 'vuex';
import { isLooseLoggedIn, doLogout } from "@/utils/auth"; import { isLooseLoggedIn, doLogout } from '@/utils/auth';
import { auth as lastfmAuth } from "@/api/lastfm"; import { auth as lastfmAuth } from '@/api/lastfm';
import { changeAppearance, bytesToSize } from "@/utils/common"; import { changeAppearance, bytesToSize } from '@/utils/common';
import { countDBSize, clearDB } from "@/utils/db"; import { countDBSize, clearDB } from '@/utils/db';
import pkg from "../../package.json"; import pkg from '../../package.json';
export default { export default {
name: "Settings", name: 'Settings',
data() { data() {
return { return {
tracksCache: { tracksCache: {
size: "0KB", size: '0KB',
length: 0, length: 0,
}, },
allOutputDevices: [ allOutputDevices: [
{ {
deviceId: "default", deviceId: 'default',
label: "settings.permissionRequired", label: 'settings.permissionRequired',
}, },
], ],
withoutAudioPrivilege: true, withoutAudioPrivilege: true,
}; };
}, },
computed: { computed: {
...mapState(["player", "settings", "data", "lastfm"]), ...mapState(['player', 'settings', 'data', 'lastfm']),
isElectron() { isElectron() {
return process.env.IS_ELECTRON; return process.env.IS_ELECTRON;
}, },
@ -400,17 +400,17 @@ export default {
}, },
set(lang) { set(lang) {
this.$i18n.locale = lang; this.$i18n.locale = lang;
this.$store.commit("changeLang", lang); this.$store.commit('changeLang', lang);
}, },
}, },
appearance: { appearance: {
get() { get() {
if (this.settings.appearance === undefined) return "auto"; if (this.settings.appearance === undefined) return 'auto';
return this.settings.appearance; return this.settings.appearance;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "appearance", key: 'appearance',
value, value,
}); });
changeAppearance(value); changeAppearance(value);
@ -423,7 +423,7 @@ export default {
}, },
set(value) { set(value) {
if (value === this.settings.musicQuality) return; if (value === this.settings.musicQuality) return;
this.$store.commit("changeMusicQuality", value); this.$store.commit('changeMusicQuality', value);
this.clearCache(); this.clearCache();
}, },
}, },
@ -433,26 +433,26 @@ export default {
return this.settings.lyricFontSize; return this.settings.lyricFontSize;
}, },
set(value) { set(value) {
this.$store.commit("changeLyricFontSize", value); this.$store.commit('changeLyricFontSize', value);
}, },
}, },
outputDevice: { outputDevice: {
get() { get() {
if (this.withoutAudioPrivilege === true) this.getAllOutputDevices(); if (this.withoutAudioPrivilege === true) this.getAllOutputDevices();
const isValidDevice = this.allOutputDevices.find( const isValidDevice = this.allOutputDevices.find(
(device) => device.deviceId === this.settings.outputDevice device => device.deviceId === this.settings.outputDevice
); );
if ( if (
this.settings.outputDevice === undefined || this.settings.outputDevice === undefined ||
isValidDevice === undefined isValidDevice === undefined
) )
return "default"; // Default deviceId return 'default'; // Default deviceId
return this.settings.outputDevice; return this.settings.outputDevice;
}, },
set(deviceId) { set(deviceId) {
if (deviceId === this.settings.outputDevice || deviceId === undefined) if (deviceId === this.settings.outputDevice || deviceId === undefined)
return; return;
this.$store.commit("changeOutputDevice", deviceId); this.$store.commit('changeOutputDevice', deviceId);
this.player.setOutputDevice(); this.player.setOutputDevice();
}, },
}, },
@ -461,8 +461,8 @@ export default {
return this.settings.enableUnblockNeteaseMusic || true; return this.settings.enableUnblockNeteaseMusic || true;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "enableUnblockNeteaseMusic", key: 'enableUnblockNeteaseMusic',
value, value,
}); });
}, },
@ -473,8 +473,8 @@ export default {
return this.settings.showPlaylistsByAppleMusic; return this.settings.showPlaylistsByAppleMusic;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "showPlaylistsByAppleMusic", key: 'showPlaylistsByAppleMusic',
value, value,
}); });
}, },
@ -485,8 +485,8 @@ export default {
return this.settings.nyancatStyle; return this.settings.nyancatStyle;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "nyancatStyle", key: 'nyancatStyle',
value, value,
}); });
}, },
@ -497,8 +497,8 @@ export default {
return this.settings.automaticallyCacheSongs; return this.settings.automaticallyCacheSongs;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "automaticallyCacheSongs", key: 'automaticallyCacheSongs',
value, value,
}); });
if (value === false) { if (value === false) {
@ -511,8 +511,8 @@ export default {
return this.settings.showLyricsTranslation; return this.settings.showLyricsTranslation;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "showLyricsTranslation", key: 'showLyricsTranslation',
value, value,
}); });
}, },
@ -522,8 +522,8 @@ export default {
return this.settings.showLyricsDynamicBackground; return this.settings.showLyricsDynamicBackground;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "showLyricsDynamicBackground", key: 'showLyricsDynamicBackground',
value, value,
}); });
}, },
@ -533,8 +533,8 @@ export default {
return this.settings.minimizeToTray; return this.settings.minimizeToTray;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "minimizeToTray", key: 'minimizeToTray',
value, value,
}); });
}, },
@ -544,8 +544,8 @@ export default {
return this.settings.enableDiscordRichPresence; return this.settings.enableDiscordRichPresence;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "enableDiscordRichPresence", key: 'enableDiscordRichPresence',
value, value,
}); });
}, },
@ -555,8 +555,8 @@ export default {
return this.settings.enableGlobalShortcut; return this.settings.enableGlobalShortcut;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "enableGlobalShortcut", key: 'enableGlobalShortcut',
value, value,
}); });
}, },
@ -566,8 +566,8 @@ export default {
return this.settings.showLibraryDefault || false; return this.settings.showLibraryDefault || false;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "showLibraryDefault", key: 'showLibraryDefault',
value, value,
}); });
}, },
@ -577,8 +577,8 @@ export default {
return this.settings.cacheLimit || false; return this.settings.cacheLimit || false;
}, },
set(value) { set(value) {
this.$store.commit("updateSettings", { this.$store.commit('updateSettings', {
key: "cacheLimit", key: 'cacheLimit',
value, value,
}); });
}, },
@ -588,27 +588,27 @@ export default {
}, },
}, },
created() { created() {
this.countDBSize("tracks"); this.countDBSize('tracks');
}, },
activated() { activated() {
this.countDBSize("tracks"); this.countDBSize('tracks');
}, },
methods: { methods: {
getAllOutputDevices() { getAllOutputDevices() {
navigator.mediaDevices.enumerateDevices().then((devices) => { navigator.mediaDevices.enumerateDevices().then(devices => {
this.allOutputDevices = devices.filter((device) => { this.allOutputDevices = devices.filter(device => {
return device.kind == "audiooutput"; return device.kind == 'audiooutput';
}); });
if ( if (
this.allOutputDevices.length > 0 && this.allOutputDevices.length > 0 &&
this.allOutputDevices[0].label !== "" this.allOutputDevices[0].label !== ''
) { ) {
this.withoutAudioPriviledge = false; this.withoutAudioPriviledge = false;
} else { } else {
this.allOutputDevices = [ this.allOutputDevices = [
{ {
deviceId: "default", deviceId: 'default',
label: "settings.permissionRequired", label: 'settings.permissionRequired',
}, },
]; ];
} }
@ -616,13 +616,13 @@ export default {
}, },
logout() { logout() {
doLogout(); doLogout();
this.$router.push({ name: "home" }); this.$router.push({ name: 'home' });
}, },
countDBSize() { countDBSize() {
countDBSize().then((data) => { countDBSize().then(data => {
if (data === undefined) { if (data === undefined) {
this.tracksCache = { this.tracksCache = {
size: "0KB", size: '0KB',
length: 0, length: 0,
}; };
return; return;
@ -639,16 +639,16 @@ export default {
lastfmConnect() { lastfmConnect() {
lastfmAuth(); lastfmAuth();
let lastfmChecker = setInterval(() => { let lastfmChecker = setInterval(() => {
const session = localStorage.getItem("lastfm"); const session = localStorage.getItem('lastfm');
if (session) { if (session) {
this.$store.commit("updateLastfm", JSON.parse(session)); this.$store.commit('updateLastfm', JSON.parse(session));
clearInterval(lastfmChecker); clearInterval(lastfmChecker);
} }
}, 1000); }, 1000);
}, },
lastfmDisconnect() { lastfmDisconnect() {
localStorage.removeItem("lastfm"); localStorage.removeItem('lastfm');
this.$store.commit("updateLastfm", {}); this.$store.commit('updateLastfm', {});
}, },
}, },
}; };
@ -835,7 +835,7 @@ h2 {
border-radius: 8px; border-radius: 8px;
} }
.toggle input + label:before { .toggle input + label:before {
content: ""; content: '';
position: absolute; position: absolute;
display: block; display: block;
-webkit-transition: 0.2s cubic-bezier(0.24, 0, 0.5, 1); -webkit-transition: 0.2s cubic-bezier(0.24, 0, 0.5, 1);
@ -847,7 +847,7 @@ h2 {
border-radius: 8px; border-radius: 8px;
} }
.toggle input + label:after { .toggle input + label:after {
content: ""; content: '';
position: absolute; position: absolute;
display: block; display: block;
box-shadow: 0 0 0 1px hsla(0, 0%, 0%, 0.02), 0 4px 0px 0 hsla(0, 0%, 0%, 0.01), box-shadow: 0 0 0 1px hsla(0, 0%, 0%, 0.02), 0 4px 0px 0 hsla(0, 0%, 0%, 0.01),

Loading…
Cancel
Save