Merge branch 'qier222:master' into revincx-pr

This commit is contained in:
Lvc Revincx 2023-08-25 23:28:35 +08:00 committed by GitHub
commit 845bc8a921
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 246 additions and 75 deletions

View file

@ -1,6 +1,6 @@
VUE_APP_NETEASE_API_URL=/api VUE_APP_NETEASE_API_URL=/api
VUE_APP_ELECTRON_API_URL=/api VUE_APP_ELECTRON_API_URL=/api
VUE_APP_ELECTRON_API_URL_DEV=http://127.0.0.1:35216 VUE_APP_ELECTRON_API_URL_DEV=http://127.0.0.1:10754
VUE_APP_LASTFM_API_KEY=09c55292403d961aa517ff7f5e8a3d9c VUE_APP_LASTFM_API_KEY=09c55292403d961aa517ff7f5e8a3d9c
VUE_APP_LASTFM_API_SHARED_SECRET=307c9fda32b3904e53654baff215cb67 VUE_APP_LASTFM_API_SHARED_SECRET=307c9fda32b3904e53654baff215cb67
DEV_SERVER_PORT=20201 DEV_SERVER_PORT=20201

View file

@ -17,7 +17,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [macos-latest, windows-latest, ubuntu-18.04] os: [macos-latest, windows-latest, ubuntu-22.04]
steps: steps:
- name: Check out Git repository - name: Check out Git repository
@ -36,7 +36,7 @@ jobs:
run: | run: |
sudo apt-get update && sudo apt-get update &&
sudo apt-get install --no-install-recommends -y rpm && sudo apt-get install --no-install-recommends -y rpm &&
sudo apt-get install --no-install-recommends -y bsdtar && sudo apt-get install --no-install-recommends -y libarchive-tools &&
sudo apt-get install --no-install-recommends -y libopenjp2-tools sudo apt-get install --no-install-recommends -y libopenjp2-tools
- name: Install Snapcraft (on Ubuntu) - name: Install Snapcraft (on Ubuntu)
@ -82,7 +82,7 @@ jobs:
uses: samuelmeuli/action-electron-builder@v1.6.0 uses: samuelmeuli/action-electron-builder@v1.6.0
env: env:
VUE_APP_ELECTRON_API_URL: /api VUE_APP_ELECTRON_API_URL: /api
VUE_APP_ELECTRON_API_URL_DEV: http://127.0.0.1:35216 VUE_APP_ELECTRON_API_URL_DEV: http://127.0.0.1:10754
VUE_APP_LASTFM_API_KEY: 09c55292403d961aa517ff7f5e8a3d9c VUE_APP_LASTFM_API_KEY: 09c55292403d961aa517ff7f5e8a3d9c
VUE_APP_LASTFM_API_SHARED_SECRET: 307c9fda32b3904e53654baff215cb67 VUE_APP_LASTFM_API_SHARED_SECRET: 307c9fda32b3904e53654baff215cb67
with: with:

View file

@ -8,41 +8,15 @@ COPY . .
RUN yarn build RUN yarn build
FROM nginx:1.20.2-alpine as app FROM nginx:1.20.2-alpine as app
RUN echo $'server { \n\
gzip on;\n\
listen 80; \n\
listen [::]:80; \n\
server_name localhost; \n\
\n\
location / { \n\
root /usr/share/nginx/html; \n\
index index.html; \n\
try_files $uri $uri/ /index.html; \n\
} \n\
\n\
location @rewrites { \n\
rewrite ^(.*)$ /index.html last; \n\
} \n\
\n\
location /api/ { \n\
proxy_buffer_size 128k; \n\
proxy_buffers 16 32k; \n\
proxy_busy_buffers_size 128k; \n\
proxy_set_header Host $host; \n\
proxy_set_header X-Real-IP $remote_addr; \n\
proxy_set_header X-Forwarded-For $remote_addr; \n\
proxy_set_header X-Forwarded-Host $remote_addr; \n\
proxy_set_header X-NginX-Proxy true; \n\
proxy_pass http://localhost:3000/; \n\
} \n\
}' > /etc/nginx/conf.d/default.conf
COPY --from=build /app/package.json /usr/local/lib/ COPY --from=build /app/package.json /usr/local/lib/
RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main libuv jq \ RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main libuv \
&& apk add --no-cache --update-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main nodejs npm \ && apk add --no-cache --update-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main nodejs npm \
&& npm i -g NeteaseCloudMusicApi@"$(jq -r '.dependencies.NeteaseCloudMusicApi' /usr/local/lib/package.json)" && npm i -g $(awk -F \" '{if($2=="NeteaseCloudMusicApi") print $2"@"$4}' /usr/local/lib/package.json) \
&& rm -f /usr/local/lib/package.json
COPY --from=build /app/docker/nginx.conf.example /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html COPY --from=build /app/dist /usr/share/nginx/html
CMD nginx ; exec npx NeteaseCloudMusicApi CMD nginx ; exec npx NeteaseCloudMusicApi

View file

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2020-2022 qier222 Copyright (c) 2020-2023 qier222
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -18,6 +18,11 @@
[![Library][library-screenshot]](https://music.qier222.com) [![Library][library-screenshot]](https://music.qier222.com)
## 全新版本
全新2.0 Alpha测试版已发布欢迎前往 [Releases](https://github.com/qier222/YesPlayMusic/releases) 页面下载。
当前版本将会进入维护模式除重大bug修复外不会再更新新功能。
## ✨ 特性 ## ✨ 特性
- ✅ 使用 Vue.js 全家桶开发 - ✅ 使用 Vue.js 全家桶开发

View file

@ -4,6 +4,9 @@ services:
context: . context: .
image: yesplaymusic image: yesplaymusic
container_name: YesPlayMusic container_name: YesPlayMusic
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
ports: ports:
- 80:80 - 80:80
restart: always restart: always

28
docker/nginx.conf.example Normal file
View file

@ -0,0 +1,28 @@
server {
gzip on;
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location @rewrites {
rewrite ^(.*)$ /index.html last;
}
location /api/ {
proxy_buffers 16 32k;
proxy_buffer_size 128k;
proxy_busy_buffers_size 128k;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $remote_addr;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://localhost:3000/;
}
}

View file

@ -1,6 +1,6 @@
{ {
"name": "yesplaymusic", "name": "yesplaymusic",
"version": "0.4.5", "version": "0.4.7",
"private": true, "private": true,
"description": "A third party music player for Netease Music", "description": "A third party music player for Netease Music",
"author": "qier222<qier222@outlook.com>", "author": "qier222<qier222@outlook.com>",
@ -22,9 +22,12 @@
"netease_api:run": "npx NeteaseCloudMusicApi" "netease_api:run": "npx NeteaseCloudMusicApi"
}, },
"main": "background.js", "main": "background.js",
"engines": {
"node": "14 || 16"
},
"dependencies": { "dependencies": {
"@unblockneteasemusic/rust-napi": "^0.3.0-pre.1", "@unblockneteasemusic/rust-napi": "^0.4.0",
"NeteaseCloudMusicApi": "^4.5.2", "NeteaseCloudMusicApi": "^4.8.7",
"axios": "^0.26.1", "axios": "^0.26.1",
"change-case": "^4.1.2", "change-case": "^4.1.2",
"cli-color": "^2.0.0", "cli-color": "^2.0.0",
@ -59,7 +62,6 @@
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pac-proxy-agent": "^4.1.0", "pac-proxy-agent": "^4.1.0",
"plyr": "^3.6.2", "plyr": "^3.6.2",
"prettier": "2.5.1",
"qrcode": "^1.4.4", "qrcode": "^1.4.4",
"register-service-worker": "^1.7.1", "register-service-worker": "^1.7.1",
"svg-sprite-loader": "^6.0.11", "svg-sprite-loader": "^6.0.11",
@ -83,6 +85,7 @@
"@vue/cli-service": "~4.5.0", "@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"eslint": "^6.7.2", "eslint": "^6.7.2",
"prettier": "2.5.1",
"eslint-config-prettier": "^8.1.0", "eslint-config-prettier": "^8.1.0",
"eslint-plugin-prettier": "^3.3.1", "eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^7.9.0", "eslint-plugin-vue": "^7.9.0",

View file

@ -67,7 +67,7 @@ const closeOnLinux = (e, win, store) => {
win.hide(); //调用 最小化实例方法 win.hide(); //调用 最小化实例方法
} else if (result.response === 1) { } else if (result.response === 1) {
win = null; win = null;
app.exit(); // exit()直接关闭客户端不会执行quit(); app.exit(); //exit()直接关闭客户端不会执行quit();
} }
}) })
.catch(err => { .catch(err => {
@ -154,7 +154,7 @@ class Background {
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:35216')); expressApp.use('/api', expressProxy('http://127.0.0.1:10754'));
expressApp.use('/player', (req, res) => { expressApp.use('/player', (req, res) => {
this.window.webContents this.window.webContents
.executeJavaScript('window.yesplaymusic.player') .executeJavaScript('window.yesplaymusic.player')
@ -167,7 +167,7 @@ class Background {
}); });
}); });
}); });
this.expressApp = expressApp.listen(41342, '127.0.0.1'); this.expressApp = expressApp.listen(27232, '127.0.0.1');
} }
createWindow() { createWindow() {
@ -258,8 +258,8 @@ class Background {
createProtocol('app'); createProtocol('app');
this.window.loadURL( this.window.loadURL(
showLibraryDefault showLibraryDefault
? 'http://localhost:41342/#/library' ? 'http://localhost:27232/#/library'
: 'http://localhost:41342' : 'http://localhost:27232'
); );
} }
} }

View file

@ -194,7 +194,7 @@ nav {
@media (max-width: 1336px) { @media (max-width: 1336px) {
nav { nav {
padding: 0 5vw; padding: 0 max(5vw, 90px);
} }
} }

View file

@ -187,6 +187,7 @@ import '@/assets/css/slider.css';
import ButtonIcon from '@/components/ButtonIcon.vue'; import ButtonIcon from '@/components/ButtonIcon.vue';
import VueSlider from 'vue-slider-component'; import VueSlider from 'vue-slider-component';
import { goToListSource, hasListSource } from '@/utils/playList'; import { goToListSource, hasListSource } from '@/utils/playList';
import { formatTrackTime } from '@/utils/common';
export default { export default {
name: 'Player', name: 'Player',
@ -239,10 +240,7 @@ export default {
: this.$router.push({ name: 'next' }); : this.$router.push({ name: 'next' });
}, },
formatTrackTime(value) { formatTrackTime(value) {
if (!value) return ''; return formatTrackTime(value);
let min = ~~((value / 60) % 60);
let sec = (~~(value % 60)).toString().padStart(2, '0');
return `${min}:${sec}`;
}, },
hasList() { hasList() {
return hasListSource(); return hasListSource();

View file

@ -65,6 +65,7 @@
v-for="(track, index) in tracks" v-for="(track, index) in tracks"
:key="itemKey === 'id' ? track.id : `${track.id}${index}`" :key="itemKey === 'id' ? track.id : `${track.id}${index}`"
:track-prop="track" :track-prop="track"
:track-no="index + 1"
:highlight-playing-track="highlightPlayingTrack" :highlight-playing-track="highlightPlayingTrack"
@dblclick.native="playThisList(track.id || track.songId)" @dblclick.native="playThisList(track.id || track.songId)"
@click.right.native="openMenu($event, track, index)" @click.right.native="openMenu($event, track, index)"

View file

@ -21,7 +21,7 @@
style="height: 14px; width: 14px" style="height: 14px; width: 14px"
></svg-icon> ></svg-icon>
</button> </button>
<span v-show="(!focus || !playable) && !isPlaying">{{ track.no }}</span> <span v-show="(!focus || !playable) && !isPlaying">{{ trackNo }}</span>
<button v-show="isPlaying"> <button v-show="isPlaying">
<svg-icon <svg-icon
icon-class="volume" icon-class="volume"
@ -96,6 +96,7 @@ export default {
props: { props: {
trackProp: Object, trackProp: Object,
trackNo: Number,
highlightPlayingTrack: { highlightPlayingTrack: {
type: Boolean, type: Boolean,
default: true, default: true,

View file

@ -37,6 +37,7 @@ export function createMpris(window) {
'xesam:title': metadata.title, 'xesam:title': metadata.title,
'xesam:album': metadata.album, 'xesam:album': metadata.album,
'xesam:artist': metadata.artist.split(','), 'xesam:artist': metadata.artist.split(','),
'xesam:url': metadata.url,
}; };
}); });

View file

@ -7,7 +7,7 @@ export async function startNeteaseMusicApi() {
// Load the NCM API. // Load the NCM API.
await server.serveNcmApi({ await server.serveNcmApi({
port: 35216, port: 10754,
moduleDefs: require('../ncmModDef'), moduleDefs: require('../ncmModDef'),
}); });
} }

View file

@ -113,6 +113,8 @@ export default {
pause: 'Pause', pause: 'Pause',
mute: 'Mute', mute: 'Mute',
nextUp: 'Next Up', nextUp: 'Next Up',
translationLyric: 'lyric (trans)',
PronunciationLyric: 'lyric (pronounce)',
}, },
modal: { modal: {
close: 'Close', close: 'Close',
@ -130,6 +132,17 @@ export default {
settings: 'Settings', settings: 'Settings',
logout: 'LOGOUT', logout: 'LOGOUT',
language: 'Languages', language: 'Languages',
lyric: 'Lyric',
others: 'Others',
customization: 'Customization',
MusicGenrePreference: {
text: 'Music Language Preference',
none: 'No preferences',
mandarin: 'Mandarin',
western: 'Europe & America',
korean: 'Korean',
japanese: 'Japanese',
},
musicQuality: { musicQuality: {
text: 'Music Quality', text: 'Music Quality',
low: 'Low', low: 'Low',

View file

@ -108,6 +108,8 @@ export default {
pause: 'Durdur', pause: 'Durdur',
mute: 'Sesi kapat', mute: 'Sesi kapat',
nextUp: 'Sıradaki', nextUp: 'Sıradaki',
translationLyric: 'şarkı sözleri (çeviri)',
PronunciationLyric: 'şarkı sözleri (çeviri)',
}, },
modal: { modal: {
close: 'Kapat', close: 'Kapat',
@ -125,6 +127,17 @@ export default {
settings: 'Ayarlar', settings: 'Ayarlar',
logout: 'ÇIKIŞ YAP', logout: 'ÇIKIŞ YAP',
language: 'Diller', language: 'Diller',
lyric: 'Şarkı Sözleri',
others: 'Diğerleri',
customization: 'Özelleştirme',
MusicGenrePreference: {
text: 'Müzik Dili Tercihi',
none: 'Tercih yok',
mandarin: 'Çince dili',
western: 'Avrupa ve Amerika',
korean: 'Korece',
japanese: 'Japonca',
},
musicQuality: { musicQuality: {
text: 'Müzik Kalitesi', text: 'Müzik Kalitesi',
low: 'Düşük', low: 'Düşük',

View file

@ -114,6 +114,8 @@ export default {
pause: '暂停', pause: '暂停',
mute: '静音', mute: '静音',
nextUp: '播放列表', nextUp: '播放列表',
translationLyric: '歌词(译)',
PronunciationLyric: '歌词(音)',
}, },
modal: { modal: {
close: '关闭', close: '关闭',
@ -131,6 +133,17 @@ export default {
settings: '设置', settings: '设置',
logout: '登出', logout: '登出',
language: '语言', language: '语言',
lyric: '歌词',
others: '其他',
customization: '自定义',
MusicGenrePreference: {
text: '音乐语种偏好',
none: '无偏好',
mandarin: '华语',
western: '欧美',
korean: '韩语',
japanese: '日语',
},
musicQuality: { musicQuality: {
text: '音质选择', text: '音质选择',
low: '普通', low: '普通',

View file

@ -110,6 +110,8 @@ export default {
pause: '暫停', pause: '暫停',
mute: '靜音', mute: '靜音',
nextUp: '播放清單', nextUp: '播放清單',
translationLyric: '歌詞(譯)',
PronunciationLyric: '歌詞(音)',
}, },
modal: { modal: {
close: '關閉', close: '關閉',
@ -127,6 +129,17 @@ export default {
settings: '設定', settings: '設定',
logout: '登出', logout: '登出',
language: '語言', language: '語言',
lyric: '歌詞',
others: '其他',
customization: '自訂',
MusicGenrePreference: {
text: '音樂語種偏好',
none: '無偏好',
mandarin: '華語',
western: '歐美',
korean: '韓語',
japanese: '日語',
},
musicQuality: { musicQuality: {
text: '音質選擇', text: '音質選擇',
low: '普通', low: '普通',

View file

@ -146,7 +146,7 @@ export default class {
} }
set volume(volume) { set volume(volume) {
this._volume = volume; this._volume = volume;
Howler.volume(volume); this._howler?.volume(volume);
} }
get list() { get list() {
return this.shuffle ? this._shuffledList : this._list; return this.shuffle ? this._shuffledList : this._list;
@ -207,7 +207,7 @@ export default class {
_init() { _init() {
this._loadSelfFromLocalStorage(); this._loadSelfFromLocalStorage();
Howler.volume(this.volume); this._howler?.volume(this.volume);
if (this._enabled) { if (this._enabled) {
// 恢复当前播放歌曲 // 恢复当前播放歌曲
@ -338,6 +338,10 @@ export default class {
// code 3: MEDIA_ERR_DECODE // code 3: MEDIA_ERR_DECODE
if (errCode === 3) { if (errCode === 3) {
this._playNextTrack(this._isPersonalFM); this._playNextTrack(this._isPersonalFM);
} else if (errCode === 4) {
// code 4: MEDIA_ERR_SRC_NOT_SUPPORTED
store.dispatch('showToast', `无法播放: 不支持的音频格式`);
this._playNextTrack(this._isPersonalFM);
} else { } else {
const t = this.progress; const t = this.progress;
this._replaceCurrentTrackAudio(this.currentTrack, false, false).then( this._replaceCurrentTrackAudio(this.currentTrack, false, false).then(
@ -432,12 +436,11 @@ export default class {
} }
}; };
/** @type {import("@unblockneteasemusic/rust-napi").RetrievedSongInfo | null} */
const retrieveSongInfo = await ipcRenderer.invoke( const retrieveSongInfo = await ipcRenderer.invoke(
'unblock-music', 'unblock-music',
store.state.settings.unmSource, store.state.settings.unmSource,
track, track,
/** @type {import("@unblockneteasemusic/rust-napi").Context} */ ({ {
enableFlac: store.state.settings.unmEnableFlac || null, enableFlac: store.state.settings.unmEnableFlac || null,
proxyUri: store.state.settings.unmProxyUri || null, proxyUri: store.state.settings.unmProxyUri || null,
searchMode: determineSearchMode(store.state.settings.unmSearchMode), searchMode: determineSearchMode(store.state.settings.unmSearchMode),
@ -446,7 +449,7 @@ export default class {
'qq:cookie': store.state.settings.unmQQCookie || null, 'qq:cookie': store.state.settings.unmQQCookie || null,
'ytdl:exe': store.state.settings.unmYtDlExe || null, 'ytdl:exe': store.state.settings.unmYtDlExe || null,
}, },
}) }
); );
if (store.state.settings.automaticallyCacheSongs && retrieveSongInfo?.url) { if (store.state.settings.automaticallyCacheSongs && retrieveSongInfo?.url) {
@ -612,6 +615,7 @@ export default class {
], ],
length: this.currentTrackDuration, length: this.currentTrackDuration,
trackId: this.current, trackId: this.current,
url: '/trackid/' + track.id,
}; };
navigator.mediaSession.metadata = new window.MediaMetadata(metadata); navigator.mediaSession.metadata = new window.MediaMetadata(metadata);

View file

@ -221,7 +221,7 @@ export function bytesToSize(bytes) {
export function formatTrackTime(value) { export function formatTrackTime(value) {
if (!value) return ''; if (!value) return '';
let min = ~~((value / 60) % 60); let min = ~~(value / 60);
let sec = (~~(value % 60)).toString().padStart(2, '0'); let sec = (~~(value % 60)).toString().padStart(2, '0');
return `${min}:${sec}`; return `${min}:${sec}`;
} }

View file

@ -2,6 +2,7 @@ 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 || ''),
romalyric: parseLyric(lrc?.romalrc?.lyric || ''),
lyricuser: lrc.lyricUser, lyricuser: lrc.lyricUser,
transuser: lrc.transUser, transuser: lrc.transUser,
}; };

View file

@ -23,7 +23,11 @@ const service = axios.create({
service.interceptors.request.use(function (config) { service.interceptors.request.use(function (config) {
if (!config.params) config.params = {}; if (!config.params) config.params = {};
if (baseURL.length) { if (baseURL.length) {
if (baseURL[0] !== '/' && !process.env.IS_ELECTRON) { if (
baseURL[0] !== '/' &&
!process.env.IS_ELECTRON &&
getCookie('MUSIC_U') !== null
) {
config.params.cookie = `MUSIC_U=${getCookie('MUSIC_U')};`; config.params.cookie = `MUSIC_U=${getCookie('MUSIC_U')};`;
} }
} else { } else {

View file

@ -96,9 +96,7 @@
{{ $t('album.released') }} {{ $t('album.released') }}
{{ album.publishTime | formatDate('MMMM D, YYYY') }} {{ album.publishTime | formatDate('MMMM D, YYYY') }}
</div> </div>
<div v-if="album.company !== null" class="copyright"> <div v-if="album.company" class="copyright"> © {{ album.company }} </div>
© {{ album.company }}
</div>
</div> </div>
<div v-if="filteredMoreAlbums.length !== 0" class="more-by"> <div v-if="filteredMoreAlbums.length !== 0" class="more-by">
<div class="section-title"> <div class="section-title">

View file

@ -241,7 +241,9 @@ export default {
computed: { computed: {
...mapState(['player']), ...mapState(['player']),
albums() { albums() {
return this.albumsData.filter(a => a.type === '专辑'); return this.albumsData.filter(
a => a.type === '专辑' || a.type === '精选集'
);
}, },
eps() { eps() {
return this.albumsData.filter(a => return this.albumsData.filter(a =>

View file

@ -261,6 +261,8 @@ export default {
}); });
}, },
checkQrCodeLogin() { checkQrCodeLogin() {
//
clearInterval(this.qrCodeCheckInterval);
this.qrCodeCheckInterval = setInterval(() => { this.qrCodeCheckInterval = setInterval(() => {
if (this.qrCodeKey === '') return; if (this.qrCodeKey === '') return;
loginQrCodeCheck(this.qrCodeKey).then(result => { loginQrCodeCheck(this.qrCodeKey).then(result => {
@ -275,7 +277,7 @@ export default {
clearInterval(this.qrCodeCheckInterval); clearInterval(this.qrCodeCheckInterval);
this.qrCodeInformation = '登录成功,请稍等...'; this.qrCodeInformation = '登录成功,请稍等...';
result.code = 200; result.code = 200;
result.cookie = result.cookie.replace('HTTPOnly', ''); result.cookie = result.cookie.replaceAll(' HTTPOnly', '');
this.handleLoginResponse(result); this.handleLoginResponse(result);
} }
}); });

View file

@ -201,6 +201,28 @@
> >
<svg-icon icon-class="shuffle" /> <svg-icon icon-class="shuffle" />
</button-icon> </button-icon>
<button-icon
v-show="
isShowLyricTypeSwitch &&
$store.state.settings.showLyricsTranslation &&
lyricType === 'translation'
"
:title="$t('player.translationLyric')"
@click.native="switchLyricType"
>
<span class="lyric-switch-icon"></span>
</button-icon>
<button-icon
v-show="
isShowLyricTypeSwitch &&
$store.state.settings.showLyricsTranslation &&
lyricType === 'romaPronunciation'
"
:title="$t('player.PronunciationLyric')"
@click.native="switchLyricType"
>
<span class="lyric-switch-icon"></span>
</button-icon>
</div> </div>
</div> </div>
</div> </div>
@ -215,7 +237,7 @@
> >
<div id="line-1" class="line"></div> <div id="line-1" class="line"></div>
<div <div
v-for="(line, index) in lyricWithTranslation" v-for="(line, index) in lyricToShow"
:id="`line${index}`" :id="`line${index}`"
:key="index" :key="index"
class="line" class="line"
@ -277,6 +299,8 @@ export default {
lyricsInterval: null, lyricsInterval: null,
lyric: [], lyric: [],
tlyric: [], tlyric: [],
romalyric: [],
lyricType: 'translation', // or 'romaPronunciation'
highlightLyricIndex: -1, highlightLyricIndex: -1,
minimize: true, minimize: true,
background: '', background: '',
@ -302,6 +326,14 @@ export default {
bgImageUrl() { bgImageUrl() {
return this.player.currentTrack?.al?.picUrl + '?param=512y512'; return this.player.currentTrack?.al?.picUrl + '?param=512y512';
}, },
isShowLyricTypeSwitch() {
return this.romalyric.length > 0 && this.tlyric.length > 0;
},
lyricToShow() {
return this.lyricType === 'translation'
? this.lyricWithTranslation
: this.lyricWithRomaPronunciation;
},
lyricWithTranslation() { lyricWithTranslation() {
let ret = []; let ret = [];
// //
@ -333,6 +365,37 @@ export default {
} }
return ret; return ret;
}, },
lyricWithRomaPronunciation() {
let ret = [];
//
const lyricFiltered = this.lyric.filter(({ content }) =>
Boolean(content)
);
// content
if (lyricFiltered.length) {
lyricFiltered.forEach(l => {
const { rawTime, time, content } = l;
const lyricItem = { time, content, contents: [content] };
const sameTimeRomaLyric = this.romalyric.find(
({ rawTime: tLyricRawTime }) => tLyricRawTime === rawTime
);
if (sameTimeRomaLyric) {
const { content: romaLyricContent } = sameTimeRomaLyric;
if (content) {
lyricItem.contents.push(romaLyricContent);
}
}
ret.push(lyricItem);
});
} else {
ret = lyricFiltered.map(({ time, content }) => ({
time,
content,
contents: [content],
}));
}
return ret;
},
lyricFontSize() { lyricFontSize() {
return { return {
fontSize: `${this.$store.state.settings.lyricFontSize || 28}px`, fontSize: `${this.$store.state.settings.lyricFontSize || 28}px`,
@ -439,9 +502,10 @@ export default {
if (!data?.lrc?.lyric) { if (!data?.lrc?.lyric) {
this.lyric = []; this.lyric = [];
this.tlyric = []; this.tlyric = [];
this.romalyric = [];
return false; return false;
} else { } else {
let { lyric, tlyric } = lyricParser(data); let { lyric, tlyric, romalyric } = lyricParser(data);
lyric = lyric.filter( lyric = lyric.filter(
l => !/^作(词|曲)\s*(:|)\s*无$/.exec(l.content) l => !/^作(词|曲)\s*(:|)\s*无$/.exec(l.content)
); );
@ -461,15 +525,27 @@ export default {
if (lyric.length === 1 && includeAM) { if (lyric.length === 1 && includeAM) {
this.lyric = []; this.lyric = [];
this.tlyric = []; this.tlyric = [];
this.romalyric = [];
return false; return false;
} else { } else {
this.lyric = lyric; this.lyric = lyric;
this.tlyric = tlyric; this.tlyric = tlyric;
this.romalyric = romalyric;
if (tlyric.length * romalyric.length > 0) {
this.lyricType = 'translation';
} else {
this.lyricType =
lyric.length > 0 ? 'translation' : 'romaPronunciation';
}
return true; return true;
} }
} }
}); });
}, },
switchLyricType() {
this.lyricType =
this.lyricType === 'translation' ? 'romaPronunciation' : 'translation';
},
formatTrackTime(value) { formatTrackTime(value) {
return formatTrackTime(value); return formatTrackTime(value);
}, },
@ -758,6 +834,12 @@ export default {
width: 22px; width: 22px;
} }
} }
.lyric-switch-icon {
color: var(--color-text);
font-size: 14px;
line-height: 14px;
opacity: 0.88;
}
} }
} }
} }

View file

@ -58,20 +58,32 @@
</div> </div>
<div class="item"> <div class="item">
<div class="left"> <div class="left">
<div class="title"> 音乐语种偏好 </div> <div class="title">
{{ $t('settings.MusicGenrePreference.text') }}
</div>
</div> </div>
<div class="right"> <div class="right">
<select v-model="musicLanguage"> <select v-model="musicLanguage">
<option value="all">无偏好</option> <option value="all">{{
<option value="zh">华语</option> $t('settings.MusicGenrePreference.none')
<option value="ea">欧美</option> }}</option>
<option value="jp">日语</option> <option value="zh">{{
<option value="kr">韩语</option> $t('settings.MusicGenrePreference.mandarin')
}}</option>
<option value="ea">{{
$t('settings.MusicGenrePreference.western')
}}</option>
<option value="jp">{{
$t('settings.MusicGenrePreference.japanese')
}}</option>
<option value="kr">{{
$t('settings.MusicGenrePreference.korean')
}}</option>
</select> </select>
</div> </div>
</div> </div>
<h3>音质</h3> <!-- <h3>音质</h3> -->
<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>
@ -166,7 +178,7 @@
</div> </div>
</div> </div>
<h3>歌词</h3> <h3>{{ $t('settings.lyric') }}</h3>
<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>
@ -433,7 +445,7 @@
</div> </div>
</section> </section>
<h3>第三方</h3> <h3>{{ $t('settings.customization') }}</h3>
<div class="item"> <div class="item">
<div class="left"> <div class="left">
<div class="title"> <div class="title">
@ -470,7 +482,7 @@
</div> </div>
</div> </div>
<h3>其他</h3> <h3>{{ $t('settings.others') }}</h3>
<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.closeAppOption.text') }} </div> <div class="title"> {{ $t('settings.closeAppOption.text') }} </div>