Merge branch 'dev'

pull/453/head v1.7.0
lyswhut 2021-01-30 13:17:47 +08:00
commit bb411a365c
48 changed files with 1607 additions and 1724 deletions

View File

@ -6,6 +6,29 @@ Project versioning adheres to [Semantic Versioning](http://semver.org/).
Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
## [1.7.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.6.1...v1.7.0) - 2021-01-30
### 新增
- 搜索界面新增搜索状态的提示
- 新增“稍后播放”功能,可在歌曲列表右键菜单使用
- 新增“记住播放进度”功能的控制,该功能默认不再开启,可到播放设置-记住播放进度开启
### 优化
- 优化播放歌曲换源匹配
- 优化设置界面设置项的展示
### 修复
- 修复快速切换歌曲时, 会出现播放的歌曲和界面展示的歌曲不一致的问题
- 修复了一个由版本更新日志显示导致的潜在远程代码执行攻击漏洞该漏洞影响v1.6.1及之前的所有版本,请务必更新到最新版本
- 修复xm搜索源验证问题
### 其他
- 更新electron到9.4.2
## [1.6.1](https://github.com/lyswhut/lx-music-desktop/compare/v1.6.0...v1.6.1) - 2021-01-13
### 优化

View File

@ -62,7 +62,7 @@ npm run dev
npm run pack:dir
# 构建安装包Windows版
npm run pack
npm run pack:win
# 构建安装包Mac版
npm run pack:mac

1931
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
{
"name": "lx-music-desktop",
"version": "1.6.1",
"description": "一个免费的音乐下载助手",
"version": "1.7.0",
"description": "一个免费的音乐查找助手",
"main": "./dist/electron/main.js",
"productName": "lx-music-desktop",
"scripts": {
"pack": "node build-config/pack.js && npm run pack:win",
"pack:win": "npm run pack:win:setup:x86_64 && npm run pack:win:7z",
"pack": "node build-config/pack.js && npm run pack:win:setup:x64",
"pack:win": "node build-config/pack.js && npm run pack:win:setup:x86_64 && npm run pack:win:7z",
"pack:win:setup:x86_64": "cross-env TARGET=win_安装版 ARCH=x86_64 electron-builder -w=nsis --x64 --ia32",
"pack:win:setup:x64": "cross-env TARGET=win_安装版 ARCH=x64 electron-builder -w=nsis --x64",
"pack:win:setup:x86": "cross-env TARGET=win_安装版 ARCH=x86 electron-builder -w=nsis --ia32",
@ -173,16 +173,16 @@
"cfonts": "^2.9.1",
"chalk": "^4.1.0",
"changelog-parser": "^2.8.0",
"copy-webpack-plugin": "^6.4.0",
"core-js": "^3.8.2",
"copy-webpack-plugin": "^6.4.1",
"core-js": "^3.8.3",
"cross-env": "^7.0.3",
"css-loader": "^4.3.0",
"del": "^6.0.0",
"electron": "^9.3.3",
"electron": "^9.4.2",
"electron-builder": "^22.9.1",
"electron-debug": "^3.2.0",
"electron-devtools-installer": "^3.1.1",
"eslint": "^7.17.0",
"eslint": "^7.18.0",
"eslint-config-standard": "^14.1.1",
"eslint-formatter-friendly": "^7.0.0",
"eslint-loader": "^4.0.2",
@ -194,12 +194,12 @@
"file-loader": "^6.2.0",
"friendly-errors-webpack-plugin": "^1.7.0",
"html-webpack-plugin": "^4.5.1",
"less": "^3.12.2",
"less-loader": "^7.2.1",
"less": "^3.13.1",
"less-loader": "^7.3.0",
"markdown-it": "^12.0.4",
"mini-css-extract-plugin": "^0.12.0",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"postcss-loader": "^4.1.0",
"postcss-loader": "^4.2.0",
"postcss-pxtorem": "^5.1.1",
"pug": "^3.0.0",
"pug-loader": "^2.4.0",
@ -208,14 +208,14 @@
"rimraf": "^3.0.2",
"spinnies": "^0.5.1",
"stylus": "^0.54.8",
"stylus-loader": "^4.3.1",
"stylus-loader": "^4.3.3",
"terser-webpack-plugin": "^4.2.3",
"url-loader": "^4.1.1",
"vue-loader": "^15.9.6",
"vue-template-compiler": "^2.6.12",
"webpack": "^4.44.2",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.1",
"webpack-dev-server": "^3.11.2",
"webpack-hot-middleware": "^2.25.0",
"webpack-merge": "^5.7.3"
},
@ -233,9 +233,9 @@
"node-id3": "^0.2.2",
"request": "^2.88.2",
"vue": "^2.6.12",
"vue-i18n": "^8.22.3",
"vue-router": "^3.4.9",
"vuex": "^3.6.0",
"vue-i18n": "^8.22.4",
"vue-router": "^3.5.1",
"vuex": "^3.6.2",
"vuex-router-sync": "^5.0.0"
}
}

View File

@ -1,9 +1,20 @@
### 新增
- 搜索界面新增搜索状态的提示
- 新增“稍后播放”功能,可在歌曲列表右键菜单使用
- 新增“记住播放进度”功能的控制,该功能默认不再开启,可到播放设置-记住播放进度开启
### 优化
- 改进自动换源时的歌曲匹配
- 优化播放歌曲换源匹配
- 优化设置界面设置项的展示
### 修复
- 修复某些情况下自动换源的时间过长时会终止换源自动切歌的问题
- 修复自动换源导致的搜索列表每页变成10条数据的问题
- 降级electron到9.3.3修复部分系统没有声音的问题
- 修复快速切换歌曲时, 会出现播放的歌曲和界面展示的歌曲不一致的问题
- 修复了一个由版本更新日志显示导致的潜在远程代码执行攻击漏洞该漏洞影响v1.6.1及之前的所有版本,请务必更新到最新版本
- 修复xm搜索源验证问题
### 其他
- 更新electron到9.4.2

View File

@ -45,6 +45,7 @@ module.exports = async newVerNum => {
})
version.version = newVerNum
version.desc = newChangeLog
// version.desc = newMDChangeLog.replace(/(?:^|(\n))#{1,6} (.+)\n/g, '$1$2').trim()
pkg.version = newVerNum
console.log(chalk.blue('new version: ') + chalk.green(newVerNum))

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@ const path = require('path')
const os = require('os')
const defaultSetting = {
version: '1.0.38',
version: '1.0.39',
player: {
togglePlayMethod: 'listLoop',
highQuality: false,
@ -12,6 +12,7 @@ const defaultSetting = {
mediaDeviceId: 'default',
isMediaDeviceRemovedStopPlay: false,
isShowLyricTransition: true,
isSavePlayTime: false,
},
desktopLyric: {
enable: false,

View File

@ -54,6 +54,7 @@ app.on('web-contents-created', (event, contents) => {
event.preventDefault()
if (/^devtools/.test(navigationUrl)) return
console.log(navigationUrl)
if (!/^https?:\/\//.test(navigationUrl)) return
await shell.openExternal(navigationUrl)
})
contents.on('will-attach-webview', (event, webPreferences, params) => {

View File

@ -32,22 +32,35 @@ mainHandle(ipcMainWindowNames.handle_xm_verify_open, (event, url) => new Promise
disableHtmlFullscreenWindowResize: true,
},
})
view.webContents.on('did-finish-load', () => {
if (/punish\?/.test(view.webContents.getURL())) return
let ses = view.webContents.session
ses.cookies.get({ name: 'x5sec' })
.then(async([x5sec]) => {
// view.webContents.on('did-finish-load', () => {
// if (/punish\?/.test(view.webContents.getURL())) return
// let ses = view.webContents.session
// ses.cookies.get({ name: 'x5sec' })
// .then(async([x5sec]) => {
// isActioned = true
// await closeView()
// if (!x5sec) return reject(new Error('get x5sec failed'))
// resolve(x5sec.value)
// }).catch(async err => {
// isActioned = true
// await closeView()
// reject(err)
// })
// })
view.webContents.session.webRequest.onCompleted({ urls: ['*://www.xiami.com/*'] }, details => {
if (/\/_____tmd_____\/slide\?/.test(details.url)) {
for (const item of details.responseHeaders['set-cookie']) {
if (!/^x5sec=/.test(item)) continue
const x5sec = /x5sec=(\w+);.+$/.exec(item)
isActioned = true
await closeView()
if (!x5sec) return reject(new Error('get x5sec failed'))
resolve(x5sec.value)
}).catch(async err => {
isActioned = true
await closeView()
reject(err)
})
closeView().finally(() => {
if (!x5sec) return reject(new Error('get x5sec failed'))
resolve(x5sec[1])
})
}
}
})
// console.log(url)
global.modules.mainWindow.setBrowserView(view)
const windowSizeInfo = getWindowSizeInfo(global.appSetting)
view.setBounds({ x: (windowSizeInfo.width - 380) / 2, y: ((windowSizeInfo.height - 320 + 52) / 2), width: 380, height: 320 })

View File

@ -871,6 +871,41 @@
transform: scale(1);
}
}
@keyframes hinge {
0% {
-webkit-transform-origin: top left;
transform-origin: top left;
-webkit-animation-timing-function: ease-in-out;
animation-timing-function: ease-in-out;
}
20%,
60% {
-webkit-transform: rotate3d(0, 0, 1, 80deg);
transform: rotate3d(0, 0, 1, 80deg);
-webkit-transform-origin: top left;
transform-origin: top left;
-webkit-animation-timing-function: ease-in-out;
animation-timing-function: ease-in-out;
}
40%,
80% {
-webkit-transform: rotate3d(0, 0, 1, 60deg);
transform: rotate3d(0, 0, 1, 60deg);
-webkit-transform-origin: top left;
transform-origin: top left;
-webkit-animation-timing-function: ease-in-out;
animation-timing-function: ease-in-out;
opacity: 1;
}
to {
-webkit-transform: translate3d(0, 700px, 0);
transform: translate3d(0, 700px, 0);
opacity: 0;
}
}
.flipInX {
backface-visibility: visible !important;

View File

@ -34,7 +34,7 @@ div(:class="$style.player")
div(:class="$style.titleBtn" @click='addMusicTo' :tips="$t('core.player.add_music_to')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='80%' viewBox='0 0 512 512' space='preserve')
use(xlink:href='#icon-add-2')
//- div(:class="$style.playBtn" @click='handleNext' tips="")
//- div(:class="$style.playBtn" @click='playNext' tips="")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 291.063 291.064' space='preserve')
use(xlink:href='#icon-sound')
@ -49,7 +49,7 @@ div(:class="$style.player")
span(style="margin: 0 5px;") /
span {{maxPlayTimeStr}}
div(:class="$style.right")
div(:class="$style.playBtn" @click='handlePrev' :tips="$t('core.player.prev')" style="transform: rotate(180deg);")
div(:class="$style.playBtn" @click='playPrev' :tips="$t('core.player.prev')" style="transform: rotate(180deg);")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve')
use(xlink:href='#icon-nextMusic')
div(:class="$style.playBtn" :tips="isPlay ? $t('core.player.pause') : $t('core.player.play')" @click='togglePlay')
@ -57,7 +57,7 @@ div(:class="$style.player")
use(xlink:href='#icon-pause')
svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve')
use(xlink:href='#icon-play')
div(:class="$style.playBtn" @click='handleNext' :tips="$t('core.player.next')")
div(:class="$style.playBtn" @click='playNext' :tips="$t('core.player.next')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve')
use(xlink:href='#icon-nextMusic')
//- transition(enter-active-class="animated lightSpeedIn"
@ -90,7 +90,6 @@ import { rendererSend, rendererOn, NAMES } from '../../../common/ipc'
import { formatPlayTime2, getRandom, checkPath, setTitle, clipboardWriteText, debounce, throttle, assertApiSupport } from '../../utils'
import { mapGetters, mapActions, mapMutations } from 'vuex'
import { requestMsg } from '../../utils/message'
import { isMac } from '../../../common/utils'
import { player as eventPlayerNames } from '../../../common/hotKey'
import musicSdk from '@renderer/utils/music'
import path from 'path'
@ -107,7 +106,6 @@ const playNextModes = [
export default {
data() {
return {
show: true,
volume: 0,
nowPlayTime: 0,
maxPlayTime: 0,
@ -123,7 +121,6 @@ export default {
singer: '',
album: '',
},
targetSong: null,
pregessWidth: 0,
lyric: {
lines: [],
@ -133,7 +130,6 @@ export default {
delayNextTimeout: null,
restorePlayTime: 0,
retryNum: 0,
isMac,
volumeEvent: {
isMsDown: false,
msDownX: 0,
@ -149,10 +145,19 @@ export default {
},
computed: {
...mapGetters(['setting']),
...mapGetters('player', ['list', 'playIndex', 'changePlay', 'listId', 'isShowPlayerDetail', 'playedList']),
...mapGetters('player', ['list', 'changePlay', 'playMusicInfo', 'isShowPlayerDetail', 'playInfo', 'playedList']),
// pic() {
// return this.musicInfo.img ? this.musicInfo.img : ''
// },
listId() { // ID
return this.playInfo.listId
},
playIndex() { //
return this.playInfo.playIndex
},
targetSong() {
return this.playInfo.musicInfo
},
title() {
return this.musicInfo.name
? this.setting.download.fileName.replace('歌名', this.musicInfo.name).replace('歌手', this.musicInfo.singer)
@ -247,38 +252,19 @@ export default {
if (!n) return
this.resetChangePlay()
if (window.restorePlayInfo) {
let musicInfo = this.targetSong = this.list[window.restorePlayInfo.index]
this.musicInfo.songmid = musicInfo.songmid
this.musicInfo.singer = musicInfo.singer
this.musicInfo.name = musicInfo.name
this.musicInfo.album = musicInfo.albumName
this.setImg(musicInfo)
this.setLrc(musicInfo)
this.nowPlayTime = this.restorePlayTime = window.restorePlayInfo.time
this.maxPlayTime = window.restorePlayInfo.maxTime || 0
this.handleUpdateWinLyricInfo('music_info', {
songmid: this.musicInfo.songmid,
singer: this.musicInfo.singer,
name: this.musicInfo.name,
album: this.musicInfo.album,
})
this.$nextTick(() => {
this.sendProgressEvent(this.progress, 'paused')
})
if (this.setting.player.togglePlayMethod == 'random') this.setPlayedList(musicInfo)
this.handleRestorePlay(window.restorePlayInfo)
window.restorePlayInfo = null
return
}
// console.log('changePlay')
this.handleRemoveMusic()
if (this.playIndex < 0) return
this.stopPlay()
if (!this.playInfo.musicInfo) return
this.play()
},
'setting.player.togglePlayMethod'(n) {
audio.loop = n === 'singleLoop'
if (this.playedList.length) this.clearPlayedList()
if (n == 'random' && this.playIndex > -1) this.setPlayedList(this.list[this.playIndex])
if (n == 'random') this.setPlayedList(this.playMusicInfo)
},
'setting.player.isMute'(n) {
audio.muted = n
@ -297,13 +283,13 @@ export default {
if (index < 0) {
// console.log(this.playIndex)
if (n.length) {
this.fixPlayIndex(this.playIndex - 1)
this.handleNext()
this.setPlayIndex(this.playInfo.listPlayIndex - 1)
this.playNext()
} else {
this.setPlayIndex(-1)
this.setPlayMusicInfo(null)
}
} else {
this.fixPlayIndex(index)
this.setPlayIndex(index)
}
// console.log(this.playIndex)
}
@ -317,26 +303,37 @@ export default {
},
nowPlayTime(n, o) {
if (Math.abs(n - o) > 2) this.isActiveTransition = true
this.savePlayInfo({
time: n,
maxTime: this.maxPlayTime,
listId: this.listId,
list: this.listId == null ? this.list : null,
index: this.playIndex,
})
if (this.setting.player.isSavePlayTime && !this.playInfo.isTempPlay) {
this.savePlayInfo({
time: n,
maxTime: this.maxPlayTime,
listId: this.listId,
list: this.listId == null ? this.list : null,
index: this.playIndex,
})
}
},
maxPlayTime(maxPlayTime) {
if (!this.playInfo.isTempPlay) {
this.savePlayInfo({
time: this.nowPlayTime,
maxTime: maxPlayTime,
listId: this.listId,
list: this.listId == null ? this.list : null,
index: this.playIndex,
})
}
},
},
methods: {
...mapActions('player', ['getUrl', 'getPic', 'getLrc']),
...mapActions('player', ['getUrl', 'getPic', 'getLrc', 'playPrev', 'playNext']),
...mapMutations('player', [
'setPlayMusicInfo',
'setPlayIndex',
'fixPlayIndex',
'resetChangePlay',
'visiblePlayerDetail',
'clearPlayedList',
'setPlayedList',
'removePlayedList',
'setList',
]),
...mapMutations(['setVolume', 'setPlayNextMode', 'setVisibleDesktopLyric', 'setLockDesktopLyric']),
...mapMutations('list', ['updateMusicInfo']),
@ -345,8 +342,8 @@ export default {
let eventHub = window.eventHub
let name = action == 'on' ? '$on' : '$off'
eventHub[name](eventPlayerNames.toggle_play.action, this.togglePlay)
eventHub[name](eventPlayerNames.next.action, this.handleNext)
eventHub[name](eventPlayerNames.prev.action, this.handlePrev)
eventHub[name](eventPlayerNames.next.action, this.playNext)
eventHub[name](eventPlayerNames.prev.action, this.playPrev)
eventHub[name](eventPlayerNames.volume_up.action, this.handleSetVolumeUp)
eventHub[name](eventPlayerNames.volume_down.action, this.handleSetVolumeDown)
eventHub[name](eventPlayerNames.volume_mute.action, this.handleSetVolumeMute)
@ -377,7 +374,7 @@ export default {
console.log('播放完毕')
this.stopPlay()
this.status = this.statusText = this.$t('core.player.end')
this.handleNext()
this.playNext()
})
audio.addEventListener('error', () => {
// console.log('code', audio.error)
@ -389,7 +386,7 @@ export default {
// console.log(this.retryNum)
if (!this.restorePlayTime) this.restorePlayTime = audio.currentTime //
this.retryNum++
this.setUrl(this.list[this.playIndex], true)
this.setUrl(this.targetSong, true)
this.status = this.statusText = this.$t('core.player.refresh_url')
return
}
@ -407,7 +404,9 @@ export default {
audio.currentTime = this.restorePlayTime
this.restorePlayTime = 0
}
if (!this.targetSong.interval && this.listId != 'download') this.updateMusicInfo({ id: this.listId, index: this.playIndex, data: { interval: formatPlayTime2(this.maxPlayTime) } })
if (!this.targetSong.interval && this.listId != 'download') {
this.updateMusicInfo({ id: this.listId, index: this.playIndex, data: { interval: formatPlayTime2(this.maxPlayTime) }, musicInfo: this.targetSong })
}
})
audio.addEventListener('loadstart', () => {
console.log('loadstart')
@ -470,10 +469,11 @@ export default {
this.handleRegisterEvent('on')
},
async play() {
console.log('play', this.playIndex)
this.clearDelayNextTimeout()
let targetSong = this.targetSong = this.list[this.playIndex]
if (this.setting.player.togglePlayMethod == 'random') this.setPlayedList(targetSong)
const targetSong = this.targetSong
if (this.setting.player.togglePlayMethod == 'random') this.setPlayedList(this.playMusicInfo)
this.retryNum = 0
this.restorePlayTime = 0
@ -481,7 +481,7 @@ export default {
const filePath = path.join(this.setting.download.savePath, targetSong.fileName)
// console.log(filePath)
if (!await checkPath(filePath) || !targetSong.isComplate || /\.ape$/.test(filePath)) {
return this.list.length == 1 ? null : this.handleNext()
return this.list.length == 1 ? null : this.playNext()
}
this.musicInfo.songmid = targetSong.musicInfo.songmid
this.musicInfo.singer = targetSong.musicInfo.singer
@ -492,7 +492,7 @@ export default {
this.setImg(targetSong.musicInfo)
this.setLrc(targetSong.musicInfo)
} else {
if (!this.assertApiSupport(targetSong.source)) return this.handleNext()
if (!this.assertApiSupport(targetSong.source)) return this.playNext()
this.musicInfo.songmid = targetSong.songmid
this.musicInfo.singer = targetSong.singer
this.musicInfo.name = targetSong.name
@ -507,6 +507,15 @@ export default {
name: this.musicInfo.name,
album: this.musicInfo.album,
})
if (!this.playInfo.isTempPlay) {
this.savePlayInfo({
time: this.nowPlayTime,
maxTime: this.maxPlayTime,
listId: this.listId,
list: this.listId == null ? this.list : null,
index: this.playIndex,
})
}
},
clearDelayNextTimeout() {
// console.log(this.delayNextTimeout)
@ -519,120 +528,10 @@ export default {
this.clearDelayNextTimeout()
this.delayNextTimeout = setTimeout(() => {
this.delayNextTimeout = null
this.handleNext()
this.playNext()
}, 5000)
},
async filterList() {
// if (this.list.listName === null) return
let list
let playedList = [...this.playedList]
if (this.listId == 'download') {
list = []
for (const item of this.list) {
const filePath = path.join(this.setting.download.savePath, item.fileName)
if (!await checkPath(filePath) || !item.isComplate || /\.ape$/.test(filePath)) continue
let index = playedList.indexOf(item)
if (index > -1) {
playedList.splice(index, 1)
continue
}
list.push(item)
}
} else {
list = this.list.filter(s => {
let index = playedList.indexOf(s)
if (index > -1) {
playedList.splice(index, 1)
return false
}
return this.assertApiSupport(s.source)
})
}
if (!list.length && this.playedList.length) {
this.clearPlayedList()
return this.filterList()
}
return list
},
async handlePrev() {
// console.log(playIndex)
if (this.setting.player.togglePlayMethod == 'random' && this.playedList.length) {
let index = this.playedList.indexOf(this.targetSong)
index -= 1
while (true) {
if (index > -1) {
let listIndex = this.list.indexOf(this.playedList[index])
if (listIndex < 0) {
this.removePlayedList(index)
continue
}
this.setPlayIndex(listIndex)
return
}
break
}
}
let list = await this.filterList()
if (!list.length) return this.setPlayIndex(-1)
let playIndex = list.indexOf(this.list[this.playIndex])
let index
switch (this.setting.player.togglePlayMethod) {
case 'random':
index = this.hanldeListRandom(list, playIndex)
break
case 'listLoop':
case 'list':
index = playIndex === 0 ? list.length - 1 : playIndex - 1
break
default:
return
}
if (index < 0) return
index = this.list.indexOf(list[index])
this.setPlayIndex(index)
},
async handleNext() {
// if (this.list.listName === null) return
// eslint-disable-next-line no-debugger
if (this.setting.player.togglePlayMethod == 'random' && this.playedList.length) {
let index = this.playedList.indexOf(this.targetSong)
index += 1
while (true) {
if (index < this.playedList.length) {
let listIndex = this.list.indexOf(this.playedList[index])
if (listIndex < 0) {
this.removePlayedList(index)
continue
}
this.setPlayIndex(listIndex)
return
}
break
}
}
let list = await this.filterList()
if (!list.length) return this.setPlayIndex(-1)
let playIndex = list.indexOf(this.list[this.playIndex])
// console.log(playIndex)
let index
switch (this.setting.player.togglePlayMethod) {
case 'listLoop':
index = playIndex === list.length - 1 ? 0 : playIndex + 1
break
case 'random':
index = this.hanldeListRandom(list, playIndex)
break
case 'list':
index = playIndex === list.length - 1 ? -1 : playIndex + 1
break
default:
return
}
if (index < 0) return
index = this.list.indexOf(list[index])
this.setPlayIndex(index)
},
hanldeListRandom(list, index) {
return getRandom(0, list.length)
},
@ -674,7 +573,7 @@ export default {
togglePlay() {
if (!audio.src) {
if (this.restorePlayTime != null) {
if (!this.assertApiSupport(this.targetSong.source)) return this.handleNext()
if (!this.assertApiSupport(this.targetSong.source)) return this.playNext()
this.setUrl(this.targetSong)
}
return
@ -703,8 +602,9 @@ export default {
this.musicInfo.url = targetSong.typeUrl[type]
this.status = this.statusText = this.$t('core.player.geting_url')
return this.getUrl({ musicInfo: targetSong, originMusic, type, isRefresh }).then(() => {
audio.src = this.musicInfo.url = targetSong.typeUrl[type]
return this.getUrl({ musicInfo: targetSong, originMusic, type, isRefresh }).then(url => {
if ((targetSong !== this.targetSong && originMusic !== this.targetSong) || this.isPlay) return
audio.src = this.musicInfo.url = url
}).catch(err => {
// console.log('err', err.message)
if (err.message == requestMsg.cancelRequest) return
@ -714,13 +614,13 @@ export default {
this.status = this.statusText = 'Try toggle source...'
return (originMusic.otherSource && originMusic.otherSource.length ? Promise.resolve(originMusic.otherSource) : musicSdk.findMusic(originMusic)).then(res => {
this.updateMusicInfo({ id: this.listId, index: this.playIndex, data: { otherSource: res } })
this.updateMusicInfo({ id: this.listId, index: this.playIndex, data: { otherSource: res }, musicInfo: originMusic })
return res
}).then(otherSource => {
console.log('find otherSource', otherSource)
if (otherSource.length) {
for (const item of otherSource) {
if (retryedSource.includes(item.source)) continue
if (retryedSource.includes(item.source) || !this.assertApiSupport(item.source)) continue
console.log('try toggle to: ', item.source, item.name, item.singer, item.interval)
return this.setUrl(item, isRefresh, false, retryedSource, originMusic)
}
@ -825,13 +725,13 @@ export default {
this.setProgressWidth()
},
handleToMusicLocation() {
if (!this.listId || this.listId == 'download') return
if (!this.listId || this.listId == '__temp__' || this.listId == 'download') return
if (this.playIndex == -1) return
this.$router.push({
path: 'list',
query: {
id: this.listId,
scrollIndex: this.playIndex,
scrollIndex: this.playInfo.playIndex,
},
})
},
@ -846,7 +746,7 @@ export default {
startLoadingTimeout() {
// console.log('start load timeout')
this.loadingTimeout = setTimeout(() => {
this.handleNext()
this.playNext()
}, 20000)
},
clearLoadingTimeout() {
@ -865,7 +765,7 @@ export default {
if (skipTime > this.maxPlayTime) skipTime = (this.maxPlayTime - audio.currentTime) / 2
if (skipTime - this.mediaBuffer.playTime < 1 || this.maxPlayTime - skipTime < 1) {
this.mediaBuffer.playTime = 0
this.handleNext()
this.playNext()
return
}
this.startBuffering()
@ -925,13 +825,13 @@ export default {
handlePlayDetailAction({ type, data }) {
switch (type) {
case 'prev':
this.handlePrev()
this.playPrev()
break
case 'togglePlay':
this.togglePlay()
break
case 'next':
this.handleNext()
this.playNext()
break
case 'progress':
this.setProgress(data)
@ -972,6 +872,28 @@ export default {
if (!this.musicInfo.songmid) return
this.isShowAddMusicTo = true
},
handleRestorePlay(restorePlayInfo) {
let musicInfo = this.list[restorePlayInfo.index]
this.musicInfo.songmid = musicInfo.songmid
this.musicInfo.singer = musicInfo.singer
this.musicInfo.name = musicInfo.name
this.musicInfo.album = musicInfo.albumName
this.setImg(musicInfo)
this.setLrc(musicInfo)
this.nowPlayTime = this.restorePlayTime = restorePlayInfo.time
this.maxPlayTime = restorePlayInfo.maxTime || 0
this.handleUpdateWinLyricInfo('music_info', {
songmid: this.musicInfo.songmid,
singer: this.musicInfo.singer,
name: this.musicInfo.name,
album: this.musicInfo.album,
})
this.$nextTick(() => {
this.sendProgressEvent(this.progress, 'paused')
})
if (this.setting.player.togglePlayMethod == 'random') this.setPlayedList(this.playMusicInfo)
},
},
}
</script>

View File

@ -138,7 +138,7 @@ export default {
// will-change: transform;
li {
cursor: pointer;
min-width: 90px;
min-width: 96px;
line-height: 34px;
// color: @color-btn;
padding: 0 10px;

View File

@ -74,9 +74,9 @@ export default {
'slideOutLeft',
'slideOutRight',
'slideOutUp',
'hinge',
// 'hinge',
],
inClass: 'animated flipInX',
inClass: 'animated jackInTheBox',
outClass: 'animated flipOutX',
unwatchFn: null,
}

View File

@ -114,6 +114,11 @@ export default {
action: 'download',
disabled: !this.listMenu.itemMenuControl.download,
},
{
name: this.$t('material.song_list.list_play_later'),
action: 'playLater',
disabled: !this.listMenu.itemMenuControl.playLater,
},
{
name: this.$t('material.song_list.list_search'),
action: 'search',
@ -173,6 +178,7 @@ export default {
itemMenuControl: {
play: true,
addTo: true,
playLater: true,
download: true,
search: true,
sourceDetail: true,
@ -335,6 +341,7 @@ export default {
handleListItemRigthClick(event, index) {
this.listMenu.itemMenuControl.sourceDetail = !!musicSdk[this.list[index].source].getMusicDetailPageUrl
this.listMenu.itemMenuControl.play =
this.listMenu.itemMenuControl.playLater =
this.listMenu.itemMenuControl.download =
this.assertApiSupport(this.list[index].source)
let dom_selected = this.$refs.dom_tbody.querySelector('tr.selected')

View File

@ -3,17 +3,17 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV
main(:class="$style.main" v-if="version.isDownloaded")
h2 🚀程序更新🚀
div.scroll(:class="$style.info")
div.scroll.select(:class="$style.info")
div(:class="$style.current")
h3 最新版本{{version.newVersion.version}}
h3 当前版本{{version.version}}
h3 版本变化
p(:class="$style.desc" v-html="version.newVersion.desc")
pre(:class="$style.desc" v-text="version.newVersion.desc")
div(:class="[$style.history, $style.desc]" v-if="history.length")
h3 历史版本
div(:class="$style.item" v-for="ver in history")
h4 v{{ver.version}}
p(v-html="ver.desc")
pre(v-text="ver.desc")
div(:class="$style.footer")
div(:class="$style.desc")
p 新版本已下载完毕
@ -27,17 +27,17 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV
main(:class="$style.main" v-else-if="version.isError && !version.isUnknow && version.newVersion.version != version.version")
h2 版本更新出错
div.scroll(:class="$style.info")
div.scroll.select(:class="$style.info")
div(:class="$style.current")
h3 最新版本{{version.newVersion.version}}
h3 当前版本{{version.version}}
h3 版本变化
p(:class="$style.desc" v-html="version.newVersion.desc")
pre(:class="$style.desc" v-text="version.newVersion.desc")
div(:class="[$style.history, $style.desc]" v-if="history.length")
h3 历史版本
div(:class="$style.item" v-for="ver in history")
h4 v{{ver.version}}
p(v-html="ver.desc")
pre(v-text="ver.desc")
div(:class="$style.footer")
div(:class="$style.desc")
@ -58,7 +58,7 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV
main(:class="$style.main" v-else-if="version.isDownloading && version.isTimeOut && !version.isUnknow")
h2 新版本下载超时
div(:class="$style.desc")
p 你当前所在网络访问GitHub较慢导致新版本下载超时已经下了半个钟了😳建议手动更新版本
p 你当前所在网络访问GitHub较慢导致新版本下载超时已经下了半个钟了😳你仍可选择继续等但墙裂建议手动更新版本
p
| 你可以去
material-btn(min @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" tips="点击打开") 软件发布页
@ -75,7 +75,7 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV
main(:class="$style.main" v-else-if="version.isUnknow")
h2 获取最新版本信息失败
div.scroll(:class="$style.info")
div.scroll.select(:class="$style.info")
div(:class="$style.current")
h3 当前版本{{version.version}}
div(:class="$style.desc")
@ -94,17 +94,17 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV
main(:class="$style.main" v-else)
h2 🌟发现新版本🌟
div.scroll(:class="$style.info")
div.scroll.select(:class="$style.info")
div(:class="$style.current")
h3 最新版本{{version.newVersion.version}}
h3 当前版本{{version.version}}
h3 版本变化
p(:class="$style.desc" v-html="version.newVersion.desc")
pre(:class="$style.desc" v-text="version.newVersion.desc")
div(:class="[$style.history, $style.desc]" v-if="history.length")
h3 历史版本
div(:class="$style.item" v-for="ver in history")
h4 v{{ver.version}}
p(v-html="ver.desc")
pre(v-text="ver.desc")
div(:class="$style.footer")
div(:class="$style.desc")
@ -146,7 +146,7 @@ export default {
progress() {
return this.version.downloadProgress
? `${this.version.downloadProgress.percent.toFixed(2)}% - ${sizeFormate(this.version.downloadProgress.transferred)}/${sizeFormate(this.version.downloadProgress.total)} - ${sizeFormate(this.version.downloadProgress.bytesPerSecond)}/s`
: '初始化中...'
: '处理更新中...'
},
isIgnored() {
return this.setting.ignoreVersion == this.version.newVersion.version
@ -207,6 +207,11 @@ export default {
font-size: 14px;
line-height: 1.3;
}
pre {
white-space: pre-wrap;
text-align: justify;
margin-top: 10px;
}
}
.info {

View File

@ -1,5 +1,6 @@
{
"list_play": "Play",
"list_play_later": "Play later",
"list_add_to": "Add to ...",
"list_download": "Download",
"list_search": "Search",

View File

@ -1,5 +1,6 @@
{
"menu_play": "Play",
"menu_play_later": "Play later",
"menu_start": "Start task",
"menu_pause": "Pause Task",
"menu_file": "Locate File",

View File

@ -7,6 +7,7 @@
"lists_sync": "Sync",
"lists_remove": "Remove",
"list_play": "Play",
"list_play_later": "Play later",
"list_copy_name": "Copy name",
"list_add_to": "Add to ...",
"list_move_to": "Move to ...",

View File

@ -1,5 +1,6 @@
{
"list_play": "Play",
"list_play_later": "Play later",
"list_add_to": "Add to ...",
"list_download": "Download",
"list_source_detail": "Song Page",
@ -10,6 +11,7 @@
"time": "Length",
"lossless": "SQ",
"high_quality": "HQ",
"loding_list": "Loading...",
"no_item": "Search what I want to 😉",
"hot_search": "Top Searches",
"history_search": "History Searches",

View File

@ -2,7 +2,6 @@
"basic": "General",
"basic_theme": "Theme",
"basic_show_animation": "Show switching animation",
"basic_animation_title": "Animation effect of the pop-up layer",
"basic_animation": "Random pop-up animation",
"basic_source_title": "Choose a music source",
"basic_source_test": "Test API (Available for most software features)",
@ -21,8 +20,7 @@
"basic_window_size_larger": "Larger",
"basic_window_size_oversized": "Oversized",
"basic_window_size_huge": "Huge",
"basic_to_tray_title": "Minimize it to the system tray without closing the software when closing",
"basic_to_tray": "Minimize to system tray when closing",
"basic_to_tray": "Do not exit the software when closing the software and minimize it to the system tray",
"basic_lang_title": "The language displayed in the software",
"basic_lang": "Language",
"basic_control_btn_position": "Control Button Position",
@ -30,21 +28,13 @@
"basic_control_btn_position_right": "Right",
"play": "Play",
"play_toggle_title": "If none selected, it stopped when the music playing is done.",
"play_toggle": "Playback mode",
"play_toggle_list_loop": "Playlist repeat",
"play_toggle_random": "Playlist shuffle",
"play_toggle_list": "Play in order",
"play_toggle_single_loop": "Single repeat",
"play_save_play_time": "Remember playback progress",
"play_lyric_transition": "Show lyrics translation",
"play_quality_title": "The 320k quality is preferred for playing",
"play_quality": "Prefer High Quality 320k",
"play_task_bar_title": "Show playing progress on the taskbar",
"play_task_bar": "Taskbar play progress bar",
"play_quality": "Play 320K quality songs first (if supported)",
"play_task_bar": "Show playing progress on the taskbar",
"play_mediaDevice_title": "Select a media device for audio output",
"play_mediaDevice": "Audio output",
"play_mediaDevice_remove_stop_play": "Whether to pause playback when the audio output device is changed",
"play_mediaDevice_remove_stop_play_title": "Whether to pause the song when the current sound output device is changed",
"play_mediaDevice_remove_stop_play": "Pause the song when the current sound output device is changed",
"desktop_lyric": "Desktop Lyric Settings",
"desktop_lyric_enable": "Display lyrics",
@ -53,18 +43,13 @@
"desktop_lyric_lock_screen": "It is not allowed to drag the lyrics window out of the main screen",
"search": "Search",
"search_hot_title": "Select whether to show popular searches",
"search_hot": "Top Searches",
"search_history_title": "Select whether to show search history",
"search_history": "Search history",
"search_focus_search_box_title": "Whether the search box is automatically focused on startup",
"search_focus_search_box": "Whether the search box is focused on startup",
"search_focus_search_box": "Automatically focus the search box on startup",
"list": "List",
"list_source_title": "Select whether to show music source",
"list_source": "Select whether to show music source (for Your Library only)",
"list_scroll_title": "Select whether to remember the playlist scrollbar position",
"list_scroll": "Remember playlist scrolling position (for Your library only)",
"list_source": "Show song source (only valid for my music category)",
"list_scroll": "Remember the position of the scroll bar of the playlist (only valid for my music classification)",
"download": "Download",
"download_enable": "Whether to enable download function",
@ -150,7 +135,7 @@
"update_latest": "The software is up-to-date, enjoy yourself!🥂",
"update_open_version_modal_btn": "Open the update window🚀",
"update_checking": "Checking for updates...",
"update_init": "Initializing update...",
"update_init": "Processing update...",
"about": "About lx-music-desktop",

View File

@ -8,5 +8,6 @@
"tip_2": "If you encounter a link to a playlist that cannot be opened, welcome feedback",
"tip_3": "Kugou source does not support opening with playlist ID, but supports Kugou code opening",
"play_all": "Play",
"play_later": "Play later",
"add_all": "Collect"
}

View File

@ -1,5 +1,6 @@
{
"list_play": "播放",
"list_play_later": "稍后播放",
"list_add_to": "添加到...",
"list_download": "下载",
"list_source_detail": "歌曲详情页",

View File

@ -1,5 +1,6 @@
{
"menu_play": "播放",
"menu_play_later": "稍后播放",
"menu_start": "开始任务",
"menu_pause": "暂停任务",
"menu_file": "定位文件",

View File

@ -7,6 +7,7 @@
"lists_sync": "同步",
"lists_remove": "删除",
"list_play": "播放",
"list_play_later": "稍后播放",
"list_copy_name": "复制歌曲名",
"list_source_detail": "歌曲详情页",
"list_add_to": "添加到...",

View File

@ -1,5 +1,6 @@
{
"list_play": "播放",
"list_play_later": "稍后播放",
"list_add_to": "添加到...",
"list_download": "下载",
"list_source_detail": "歌曲详情页",
@ -10,6 +11,7 @@
"time": "时长",
"lossless": "无损",
"high_quality": "高品质",
"loding_list": "加载中...",
"no_item": "搜我所想~~😉",
"hot_search": "热门搜索",
"history_search": "历史搜索",

View File

@ -1,7 +1,6 @@
{
"basic": "基本设置",
"basic_theme": "主题颜色",
"basic_animation_title": "弹出层的动画效果",
"basic_animation": "弹出层随机动画",
"basic_show_animation": "显示切换动画",
"basic_source_title": "选择音乐来源",
@ -21,8 +20,7 @@
"basic_window_size_larger": "较大",
"basic_window_size_oversized": "超大",
"basic_window_size_huge": "巨大",
"basic_to_tray_title": "关闭时不退出软件将其最小化到系统托盘",
"basic_to_tray": "关闭时最小化到系统托盘",
"basic_to_tray": "关闭软件时不退出软件将其最小化到系统托盘",
"basic_lang_title": "软件显示的语言",
"basic_lang": "语言",
"basic_control_btn_position": "控制按钮位置",
@ -30,21 +28,13 @@
"basic_control_btn_position_right": "右边",
"play": "播放设置",
"play_toggle_title": "都不选时播放完当前歌曲就停止播放",
"play_toggle": "歌曲切换方式",
"play_toggle_list_loop": "列表循环",
"play_toggle_random": "列表随机",
"play_toggle_list": "顺序播放",
"play_toggle_single_loop": "单曲循环",
"play_save_play_time": "记住播放进度",
"play_lyric_transition": "显示歌词翻译",
"play_quality_title": "启用时将优先播放320K品质的歌曲",
"play_quality": "优先播放高品质音乐",
"play_task_bar_title": "在任务栏上显示当前歌曲播放进度",
"play_task_bar": "任务栏播放进度条",
"play_quality": "优先播放320K品质的歌曲如果支持",
"play_task_bar": "在任务栏上显示当前歌曲播放进度",
"play_mediaDevice_title": "选择声音输出的媒体设备",
"play_mediaDevice": "音频输出",
"play_mediaDevice_remove_stop_play": "音频输出设备被改变时是否暂停播放",
"play_mediaDevice_remove_stop_play_title": "当前的声音输出设备被改变时是否暂停播放歌曲",
"play_mediaDevice_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲",
"desktop_lyric": "桌面歌词设置",
"desktop_lyric_enable": "显示歌词",
@ -53,18 +43,13 @@
"desktop_lyric_lock_screen": "不允许歌词窗口拖出主屏幕之外",
"search": "搜索设置",
"search_hot_title": "是否显示热门搜索",
"search_hot": "热门搜索",
"search_history_title": "是否显示历史搜索记录",
"search_history": "搜索历史",
"search_focus_search_box_title": "启动时是否自动聚焦搜索框",
"search_focus_search_box": "启动时是否聚焦搜索框",
"search_hot": "显示热门搜索",
"search_history": "显示历史搜索记录",
"search_focus_search_box": "启动时自动聚焦搜索框",
"list": "列表设置",
"list_source_title": "是否显示歌曲源",
"list_source": "是否显示歌曲源(仅对我的音乐分类有效)",
"list_scroll_title": "是否记住播放列表滚动条位置",
"list_scroll": "记住列表滚动位置(仅对我的音乐分类有效)",
"list_source": "显示歌曲源(仅对我的音乐分类有效)",
"list_scroll": "记住播放列表滚动条位置(仅对我的音乐分类有效)",
"download": "下载设置",
"download_enable": "是否启用下载功能",
@ -150,7 +135,7 @@
"update_latest": "软件已是最新,尽情地体验吧~🥂",
"update_open_version_modal_btn": "打开更新窗口 🚀",
"update_checking": "检查更新中...",
"update_init": "更新初始化中...",
"update_init": "处理更新中...",
"about": "关于洛雪音乐",

View File

@ -8,5 +8,6 @@
"tip_2": "若遇到无法打开的歌单链接,欢迎反馈",
"tip_3": "酷狗源不支持用歌单ID打开但支持酷狗码打开",
"play_all": "播放",
"play_later": "稍后播放",
"add_all": "收藏"
}

View File

@ -1,5 +1,6 @@
{
"list_play": "播放",
"list_play_later": "稍後播放",
"list_add_to": "添加到...",
"list_download": "下載",
"list_search": "搜索",

View File

@ -1,5 +1,6 @@
{
"menu_play": "播放",
"menu_play_later": "稍後播放",
"menu_start": "開始任務",
"menu_pause": "暫停任務",
"menu_file": "定位文件",

View File

@ -7,6 +7,7 @@
"lists_sync": "同步",
"lists_remove": "刪除",
"list_play": "播放",
"list_play_later": "稍後播放",
"list_copy_name": "複製歌曲名",
"list_add_to": "添加到...",
"list_move_to": "移動到...",

View File

@ -1,5 +1,6 @@
{
"list_play": "播放",
"list_play_later": "稍後播放",
"list_add_to": "添加到...",
"list_download": "下載",
"list_source_detail": "歌曲詳情頁",
@ -10,6 +11,7 @@
"time": "時長",
"lossless": "無損",
"high_quality": "高品質",
"loding_list": "加載中...",
"no_item": "搜我所想~~😉",
"hot_search": "熱門搜索",
"history_search": "歷史搜索",

View File

@ -1,7 +1,6 @@
{
"basic": "基本設置",
"basic_theme": "主題顏色",
"basic_animation_title": "彈出層的動畫效果",
"basic_animation": "彈出層隨機動畫",
"basic_show_animation": "顯示切換動畫",
"basic_source_title": "選擇音樂來源",
@ -21,46 +20,37 @@
"basic_window_size_larger": "較大",
"basic_window_size_oversized": "超大",
"basic_window_size_huge": "巨大",
"basic_to_tray_title": "關閉時不退出軟件將其最小化到系統托盤",
"basic_to_tray": "關閉時最小化到系統托盤",
"basic_to_tray": "關閉軟件時不退出軟件將其最小化到系統托盤",
"basic_lang_title": "軟件顯示的語言",
"basic_lang": "語言",
"basic_control_btn_position": "控制按鈕位置",
"basic_control_btn_position_left": "左邊",
"basic_control_btn_position_right": "右邊",
"play": "播放設置",
"play_toggle_title": "都不選時播放完當前歌曲就停止播放",
"play_toggle": "歌曲切換方式",
"play_toggle_list_loop": "列表循環",
"play_toggle_random": "列表隨機",
"play_toggle_list": "順序播放",
"play_toggle_single_loop": "單曲循環",
"play_save_play_time": "記住播放進度",
"play_lyric_transition": "顯示歌詞翻譯",
"play_quality_title": "啟用時將優先播放320K品質的歌曲",
"play_quality": "優先播放高品質音樂",
"play_task_bar_title": "在任務欄上顯示當前歌曲播放進度",
"play_task_bar": "任務欄播放進度條",
"play_quality": "優先播放320K品質的歌曲如果支持",
"play_task_bar": "在任務欄上顯示當前歌曲播放進度",
"play_mediaDevice_title": "選擇聲音輸出的媒體設備",
"play_mediaDevice": "音頻輸出",
"play_mediaDevice_remove_stop_play": "輸出設備被改變時是否暫停播放",
"play_mediaDevice_remove_stop_play_title": "當前的聲音輸出設備被改變時是否暫停播放歌曲",
"play_mediaDevice_remove_stop_play": "當前的聲音輸出設備被改變時暫停播放歌曲",
"desktop_lyric": "桌面歌詞設置",
"desktop_lyric_enable": "顯示歌詞",
"desktop_lyric_lock": "鎖定歌詞",
"desktop_lyric_always_on_top": "使歌詞總是在其他窗口之上",
"desktop_lyric_lock_screen": "不允許歌詞窗口拖出主屏幕之外",
"search": "搜索設置",
"search_hot_title": "是否顯示熱門搜索",
"search_hot": "熱門搜索",
"search_history_title": "是否顯示歷史搜索記錄",
"search_history": "搜索歷史",
"search_focus_search_box_title": "啟動時是否自動聚焦搜索框",
"search_focus_search_box": "啟動時是否聚焦搜索框",
"search_hot": "顯示熱門搜索",
"search_history": "顯示歷史搜索記錄",
"search_focus_search_box": "啟動時自動聚焦搜索框",
"list": "列表設置",
"list_source_title": "是否顯示歌曲源",
"list_source": "是否顯示歌曲源(僅對我的音樂分類有效)",
"list_scroll_title": "是否記住播放列表滾動條位置",
"list_scroll": "記住列表滾動位置(僅對我的音樂分類有效)",
"list_source": "顯示歌曲源(僅對我的音樂分類有效)",
"list_scroll": "記住播放列表滾動條位置(僅對我的音樂分類有效)",
"download": "下載設置",
"download_enable": "是否啟用下載功能",
"download_path_title": "下載歌曲保存的路徑",
@ -79,6 +69,7 @@
"download_name2": "歌手 - 歌名",
"download_name3": "歌名",
"download_select_save_path": "選擇歌曲保存路徑",
"hot_key": "快捷鍵設置",
"hot_key_local_title": "軟件內快捷鍵",
"hot_key_global_title": "全局快捷鍵",
@ -98,15 +89,18 @@
"hot_key_desktop_lyric_toggle_visible": "開/關桌面歌詞",
"hot_key_desktop_lyric_toggle_lock": "桌面歌詞鎖定切換",
"hot_key_desktop_lyric_toggle_always_top": "桌面歌詞置頂切換",
"network": "網絡設置",
"network_proxy_title": "HTTP代理設置亂設置軟件將無法聯網",
"network_proxy_host": "主機",
"network_proxy_port": "端口",
"network_proxy_username": "用戶名",
"network_proxy_password": "密碼",
"odc": "強迫症設置",
"odc_clear_search_input": "離開搜索界面時清空搜索框",
"odc_clear_search_list": "離開搜索界面時清空搜索列表",
"backup": "備份與恢復",
"backup_part": "部分數據(列表數據包括試聽列表、收藏列表、用戶自定義列表,設置數據不包括快捷鍵設置)",
"backup_part_import_list": "導入列表",
@ -121,7 +115,8 @@
"backup_part_import_setting_desc": "選擇配置文件",
"backup_part_export_setting_desc": "選擇設置保存位置",
"backup_part_import_list_desc": "選擇列表文件",
"backup_part_export_list_desc": "選擇列表保存位置",
"backup_part_export_list_desc": "選擇歌單保存位置",
"other": "其他",
"other_tray_theme": "托盤圖標樣式",
"other_tray_theme_native": "純色",
@ -130,6 +125,7 @@
"other_cache_label": "軟件已使用緩存大小:",
"other_cache_label_title": "當前已用緩存",
"other_cache_clear_btn": "清理緩存",
"update": "軟件更新",
"update_latest_label": "最新版本:",
"update_unknown": "未知",
@ -139,8 +135,11 @@
"update_latest": "軟件已是最新,盡情地體驗吧~🥂",
"update_open_version_modal_btn": "打開更新窗口 🚀",
"update_checking": "檢查更新中...",
"update_init": "更新初始化中...",
"update_init": "處理更新中...",
"about": "關於洛雪音樂",
"is_enable": "是否啟用",
"is_show": "是否顯示",
"click_open": "點擊打開",

View File

@ -8,5 +8,6 @@
"tip_2": "若遇到無法打開的歌單鏈接,歡迎反饋",
"tip_3": "酷狗源不支持用歌單ID打開但支持酷狗碼打開",
"play_all": "播放",
"play_later": "稍後播放",
"add_all": "收藏"
}

View File

@ -19,16 +19,31 @@ export default {
},
getVersionInfo2(state, retryNum = 0) {
return new Promise((resolve, reject) => {
httpGet('https://cdn.stsky.cn/lx-music/desktop/version.json', {
httpGet('https://gitee.com/lyswhut/lx-music-desktop-versions/raw/master/version.json', {
timeout: 20000,
}, (err, resp, body) => {
if (!err && !body.version) err = new Error(JSON.stringify(body))
if (err) {
return ++retryNum > 3
? reject(err)
? this.dispatch('getVersionInfo3').then(resolve).catch(reject)
: this.dispatch('getVersionInfo2', retryNum).then(resolve).catch(reject)
}
resolve(body)
})
})
},
getVersionInfo3(state, retryNum = 0) {
return new Promise((resolve, reject) => {
httpGet('https://cdn.stsky.cn/lx-music/desktop/version.json', {
timeout: 20000,
}, (err, resp, body) => {
if (err) {
return ++retryNum > 3
? reject(err)
: this.dispatch('getVersionInfo3', retryNum).then(resolve).catch(reject)
}
resolve(body)
})
})
},
}

View File

@ -57,6 +57,20 @@ const mutations = {
if (defaultList != null) Object.assign(state.defaultList, { list: defaultList.list, location: defaultList.location })
if (loveList != null) Object.assign(state.loveList, { list: loveList.list, location: loveList.location })
if (userList != null) state.userList = userList
if (window.localStorage.getItem('isResetOtherSource') != '1') {
for (const item of defaultList.list) {
if (item.otherSource) item.otherSource = null
}
for (const item of loveList.list) {
if (item.otherSource) item.otherSource = null
}
for (const list of userList) {
for (const item of list.list) {
if (item.otherSource) item.otherSource = null
}
}
window.localStorage.setItem('isResetOtherSource', '1')
}
allListInit(state.defaultList, state.loveList, state.userList)
state.isInitedList = true
},
@ -142,9 +156,9 @@ const mutations = {
if (!targetList) return
targetList.list.splice(0, targetList.list.length)
},
updateMusicInfo(state, { id, index, data }) {
updateMusicInfo(state, { id, index, data, musicInfo = {} }) {
let targetList = allList[id]
if (!targetList) return
if (!targetList) return Object.assign(musicInfo, data)
Object.assign(targetList.list[index], data)
},
createUserList(state, { name, id = `userlist_${Date.now()}`, list = [], source, sourceListId }) {

View File

@ -1,4 +1,6 @@
import path from 'path'
import music from '../../utils/music'
import { getRandom, checkPath, assertApiSupport } from '../../utils'
// state
const state = {
@ -10,20 +12,101 @@ const state = {
changePlay: false,
isShowPlayerDetail: false,
playedList: [],
playMusicInfo: null,
tempPlayList: [],
}
let urlRequest
let picRequest
let lrcRequest
const filterList = async({ playedList, listInfo, savePath, commit }) => {
// if (this.list.listName === null) return
let list
let canPlayList = []
const filteredPlayedList = playedList.filter(({ listId, isTempPlay }) => listInfo.id === listId && !isTempPlay).map(({ musicInfo }) => musicInfo)
if (listInfo.id == 'download') {
list = []
for (const item of listInfo.list) {
const filePath = path.join(savePath, item.fileName)
if (!await checkPath(filePath) || !item.isComplate || /\.ape$/.test(filePath)) continue
canPlayList.push(item)
// 排除已播放音乐
let index = filteredPlayedList.indexOf(item)
if (index > -1) {
filteredPlayedList.splice(index, 1)
continue
}
list.push(item)
}
} else {
list = listInfo.list.filter(s => {
if (!assertApiSupport(s.source)) return false
canPlayList.push(s)
let index = filteredPlayedList.indexOf(s)
if (index > -1) {
filteredPlayedList.splice(index, 1)
return false
}
return true
})
}
if (!list.length && playedList.length) {
commit('clearPlayedList')
return canPlayList
}
return list
}
// getters
const getters = {
list: state => state.listInfo.list,
listId: state => state.listInfo.id,
changePlay: satte => satte.changePlay,
playIndex: state => state.playIndex,
playInfo(state) {
if (state.playMusicInfo == null) return { listId: null, playIndex: -1, playListId: null, listPlayIndex: -1, isPlayList: false, musicInfo: null }
const playListId = state.listInfo.id
let listId = state.playMusicInfo.listId
const isTempPlay = !!state.playMusicInfo.isTempPlay
const isPlayList = listId === playListId
let playIndex = -1
let listPlayIndex = state.playIndex
if (listId != '__temp__') {
if (isPlayList) {
playIndex = state.listInfo.list.indexOf(state.playMusicInfo.musicInfo)
if (!isTempPlay) listPlayIndex = playIndex
} else {
let list = window.allList[listId]
if (list) playIndex = list.list.indexOf(state.playMusicInfo.musicInfo)
}
}
// console.log({
// listId,
// playIndex,
// playListId,
// listPlayIndex,
// isPlayList,
// isTempPlay,
// musicInfo: state.playMusicInfo.musicInfo,
// })
return {
listId,
playIndex,
playListId,
listPlayIndex,
isPlayList,
isTempPlay,
musicInfo: state.playMusicInfo.musicInfo,
}
},
isShowPlayerDetail: state => state.isShowPlayerDetail,
playMusicInfo: state => state.playMusicInfo,
playedList: state => state.playedList,
tempPlayList: state => state.tempPlayList,
}
// actions
@ -36,12 +119,13 @@ const actions = {
// return Promise.reject(new Error('该歌曲没有可播放的音频'))
}
if (urlRequest && urlRequest.cancelHttp) urlRequest.cancelHttp()
if (musicInfo.typeUrl[type] && !isRefresh) return Promise.resolve()
if (musicInfo.typeUrl[type] && !isRefresh) return Promise.resolve(musicInfo.typeUrl[type])
urlRequest = music[musicInfo.source].getMusicUrl(musicInfo, type)
return urlRequest.promise.then(result => {
if (originMusic) commit('setUrl', { musicInfo: originMusic, url: result.url, type })
commit('setUrl', { musicInfo, url: result.url, type })
return urlRequest.promise.then(({ url }) => {
if (originMusic) commit('setUrl', { musicInfo: originMusic, url, type })
commit('setUrl', { musicInfo, url, type })
urlRequest = null
return url
}).catch(err => {
urlRequest = null
return Promise.reject(err)
@ -78,6 +162,122 @@ const actions = {
return Promise.reject(err)
})
},
async playPrev({ state, rootState, commit, getters }) {
const currentListId = state.listInfo.id
const currentList = state.listInfo.list
if (state.playedList.length) {
// 从已播放列表移除播放列表已删除的歌曲
let index
for (index = state.playedList.indexOf(state.playMusicInfo) - 1; index > -1; index--) {
const playMusicInfo = state.playedList[index]
if (playMusicInfo.listId == currentListId && !currentList.includes(playMusicInfo.musicInfo)) {
commit('removePlayedList', index)
continue
}
break
}
if (index > -1) {
commit('setPlayMusicInfo', state.playedList[index])
return
}
}
let filteredList = await filterList({
listInfo: state.listInfo,
playedList: state.playedList,
savePath: rootState.setting.download.savePath,
commit,
})
if (!filteredList.length) return commit('setPlayMusicInfo', null)
const playInfo = getters.playInfo
let currentIndex = filteredList.indexOf(currentList[playInfo.listPlayIndex])
if (currentIndex == -1) currentIndex = 0
let nextIndex = currentIndex
if (!playInfo.isTempPlay) {
switch (rootState.setting.player.togglePlayMethod) {
case 'random':
nextIndex = getRandom(0, filteredList.length)
break
case 'listLoop':
case 'list':
nextIndex = currentIndex === 0 ? filteredList.length - 1 : currentIndex - 1
break
case 'singleLoop':
break
default:
nextIndex = -1
return
}
if (nextIndex < 0) return
}
commit('setPlayMusicInfo', {
musicInfo: filteredList[nextIndex],
listId: currentListId,
})
},
async playNext({ state, rootState, commit, getters }) {
if (state.tempPlayList.length) {
const playMusicInfo = state.tempPlayList[0]
commit('removeTempPlayList', 0)
commit('setPlayMusicInfo', playMusicInfo)
return
}
const currentListId = state.listInfo.id
const currentList = state.listInfo.list
if (state.playedList.length) {
// 从已播放列表移除播放列表已删除的歌曲
let index
for (index = state.playedList.indexOf(state.playMusicInfo) + 1; index < state.playedList.length; index++) {
const playMusicInfo = state.playedList[index]
if (playMusicInfo.listId == currentListId && !currentList.includes(playMusicInfo.musicInfo)) {
commit('removePlayedList', index)
continue
}
break
}
if (index < state.playedList.length) {
commit('setPlayMusicInfo', state.playedList[index])
return
}
}
let filteredList = await filterList({
listInfo: state.listInfo,
playedList: state.playedList,
savePath: rootState.setting.download.savePath,
commit,
})
if (!filteredList.length) return commit('setPlayMusicInfo', null)
const playInfo = getters.playInfo
const currentIndex = filteredList.indexOf(currentList[playInfo.listPlayIndex])
let nextIndex = currentIndex
switch (rootState.setting.player.togglePlayMethod) {
case 'listLoop':
nextIndex = currentIndex === filteredList.length - 1 ? 0 : currentIndex + 1
break
case 'random':
nextIndex = getRandom(0, filteredList.length)
break
case 'list':
nextIndex = currentIndex === filteredList.length - 1 ? -1 : currentIndex + 1
break
case 'singleLoop':
break
default:
nextIndex = -1
return
}
if (nextIndex < 0) return
commit('setPlayMusicInfo', {
musicInfo: filteredList[nextIndex],
listId: currentListId,
})
},
}
@ -94,23 +294,28 @@ const mutations = {
datas.musicInfo.tlrc = datas.tlyric
},
setList(state, { list, index }) {
state.playMusicInfo = {
musicInfo: list.list[index],
listId: list.id,
}
state.listInfo = list
state.playIndex = index
state.changePlay = true
// console.log(state.playMusicInfo)
if (state.playedList.length) this.commit('player/clearPlayedList')
if (state.tempPlayList.length) this.commit('player/clearTempPlayeList')
},
setPlayIndex(state, index) {
state.playIndex = index
state.changePlay = true
// console.log(state.changePlay)
},
fixPlayIndex(state, index) {
state.playIndex = index
setChangePlay(state) {
state.changePlay = true
},
resetChangePlay(state) {
state.changePlay = false
},
setPlayedList(state, item) {
// console.log(item)
if (state.playedList.includes(item)) return
state.playedList.push(item)
},
@ -118,11 +323,35 @@ const mutations = {
state.playedList.splice(index, 1)
},
clearPlayedList(state) {
state.playedList = []
state.playedList.splice(0, state.playedList.length)
},
visiblePlayerDetail(state, visible) {
state.isShowPlayerDetail = visible
},
setTempPlayList(state, list) {
state.tempPlayList.push(...list.map(({ musicInfo, listId }) => ({ musicInfo, listId, isTempPlay: true })))
if (!state.playMusicInfo) this.commit('player/playNext')
},
removeTempPlayList(state, index) {
state.tempPlayList.splice(index, 1)
},
clearTempPlayeList(state) {
state.tempPlayList.splice(0, state.tempPlayList.length)
},
setPlayMusicInfo(state, playMusicInfo) {
let playIndex = state.playIndex
if (playMusicInfo == null) {
playIndex = -1
} else {
let listId = playMusicInfo.listId
if (listId != '__temp__' && listId === state.listInfo.id) playIndex = state.listInfo.list.indexOf(state.playMusicInfo.musicInfo)
}
state.playMusicInfo = playMusicInfo
state.playIndex = playIndex
state.changePlay = true
},
}
export default {

View File

@ -127,7 +127,7 @@ const actions = {
}
}))
}
Promise.all(task).then(results => commit('setLists', { results, page }))
return Promise.all(task).then(results => commit('setLists', { results, page }))
} else {
return music[rootState.setting.search.searchSource].musicSearch.search(text, page, limit).catch(error => {
console.log(error)

View File

@ -232,6 +232,7 @@ export const objectDeepMerge = (target, source, mergedObj) => {
* @param {*} url
*/
export const openUrl = url => {
if (!/^https?:\/\//.test(url)) return
shell.openExternal(url)
}

View File

@ -59,25 +59,42 @@ export default {
async findMusic(musicInfo) {
const tasks = []
const sortSingle = singer => singer.includes('、') ? singer.split('、').sort((a, b) => a.charCodeAt(0) - b.charCodeAt(0)).join('、') : singer
const sortMusic = (arr, callback) => {
const tempResult = []
for (let i = arr.length - 1; i > -1; i--) {
const item = arr[i]
if (callback(item)) {
delete item.sortedSinger
tempResult.push(item)
arr.splice(i, 1)
}
}
tempResult.reverse()
return tempResult
}
const trimStr = str => typeof str == 'string' ? str.trim() : str
const sortedSinger = sortSingle(musicInfo.singer)
const musicName = trimStr(musicInfo.name)
for (const source of sources.sources) {
if (!sources[source.id].musicSearch || source.id === musicInfo.source || source.id === 'xm') continue
const sortedSinger = musicInfo.singer.includes('、') ? musicInfo.singer.split('、').sort((a, b) => a.charCodeAt(0) - b.charCodeAt(0)).join('、') : null
tasks.push(sources[source.id].musicSearch.search(`${musicInfo.name} ${musicInfo.singer || ''}`.trim(), 1, { limit: 10 }).then(res => {
tasks.push(sources[source.id].musicSearch.search(`${musicName} ${musicInfo.singer || ''}`.trim(), 1, { limit: 10 }).then(res => {
for (const item of res.list) {
item.sortedSinger = sortSingle(item.singer)
item.name = trimStr(item.name)
if (
(
item.singer === musicInfo.singer &&
(item.name === musicInfo.name || item.interval === musicInfo.interval)
item.sortedSinger === sortedSinger &&
(item.name === musicName || item.interval === musicInfo.interval)
) ||
(
item.interval === musicInfo.interval && item.name === musicInfo.name &&
(item.singer.includes(musicInfo.singer) || musicInfo.singer.includes(item.singer))
item.interval === musicInfo.interval && item.name === musicName &&
(item.sortedSinger.includes(sortedSinger) || sortedSinger.includes(item.sortedSinger))
) ||
(
sortedSinger &&
item.singer.includes('、') &&
item.singer.split('、').sort((a, b) => a.charCodeAt(0) - b.charCodeAt(0)).join('、') === sortedSinger
item.name === musicName && item.albumName === musicInfo.albumName &&
item.interval === musicInfo.interval
)
) {
return item
@ -89,35 +106,13 @@ export default {
const result = (await Promise.all(tasks)).filter(s => s)
const newResult = []
if (result.length) {
for (let i = result.length - 1; i > -1; i--) {
const item = result[i]
if (item.singer === musicInfo.singer && item.name === musicInfo.name && item.interval === musicInfo.interval) {
newResult.push(item)
result.splice(i, 1)
}
newResult.push(...sortMusic(result, item => item.sortedSinger === sortedSinger && item.name === musicName && item.interval === musicInfo.interval))
newResult.push(...sortMusic(result, item => item.sortedSinger === sortedSinger && item.interval === musicInfo.interval))
newResult.push(...sortMusic(result, item => item.name === musicName && item.sortedSinger === sortedSinger && item.albumName === musicInfo.albumName))
newResult.push(...sortMusic(result, item => item.sortedSinger === sortedSinger && item.name === musicName))
for (const item of result) {
delete item.sortedSinger
}
for (let i = result.length - 1; i > -1; i--) {
const item = result[i]
if (item.singer === musicInfo.singer && item.interval === musicInfo.interval) {
newResult.push(item)
result.splice(i, 1)
}
}
for (let i = result.length - 1; i > -1; i--) {
const item = result[i]
if (item.name === musicInfo.name && item.singer === musicInfo.singer && item.albumName === musicInfo.albumName) {
newResult.push(item)
result.splice(i, 1)
}
}
for (let i = result.length - 1; i > -1; i--) {
const item = result[i]
if (item.singer === musicInfo.singer && item.name === musicInfo.name) {
newResult.push(item)
result.splice(i, 1)
}
}
newResult.reverse()
newResult.push(...result)
}
// console.log(newResult)

View File

@ -1,17 +1,18 @@
import { xmRequest } from './util'
// import { xmRequest } from './util'
export default {
_requestObj: null,
async getList(retryNum = 0) {
if (this._requestObj) this._requestObj.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('try max num'))
// if (this._requestObj) this._requestObj.cancelHttp()
// if (retryNum > 2) return Promise.reject(new Error('try max num'))
const _requestObj = xmRequest('/api/search/getHotSearchWords')
const { body, statusCode } = await _requestObj.promise
// console.log(body)
if (statusCode != 200 || body.code !== 'SUCCESS') return this.getList(++retryNum)
// const _requestObj = xmRequest('/api/search/getHotSearchWords')
// const { body, statusCode } = await _requestObj.promise
// // console.log(body)
// if (statusCode != 200 || body.code !== 'SUCCESS') return this.getList(++retryNum)
// // console.log(body, statusCode)
return { source: 'xm', list: this.filterList(body.result.data.hotWords) }
// return { source: 'xm', list: this.filterList(body.result.data.hotWords) }
return { source: 'xm', list: [] }
},
filterList(rawList) {
return rawList.map(item => item.word)

View File

@ -96,7 +96,7 @@ export const xmRequest = (path, params = '') => {
if (resp.body.code !== 'SUCCESS' && resp.body.rgv587_flag == 'sm') {
window.globalObj.xm.isShowVerify = true
return wait(300).then(() => {
return rendererInvoke(NAMES.mainWindow.handle_xm_verify_open, 'https:' + resp.body.url).then(x5sec => {
return rendererInvoke(NAMES.mainWindow.handle_xm_verify_open, /^https:/.test(resp.body.url) ? resp.body.url : 'https:' + resp.body.url).then(x5sec => {
handleSaveToken({ cookies: { x5sec } })
// console.log(x5sec)
window.globalObj.xm.isShowVerify = false

View File

@ -45,7 +45,7 @@ export default {
return {
clickTime: window.performance.now(),
clickIndex: -1,
selectdData: [],
selectedData: [],
isShowDownloadMultiple: false,
tabId: 'all',
keyEvent: {
@ -59,6 +59,7 @@ export default {
play: true,
start: true,
pause: true,
playLater: true,
file: true,
search: true,
remove: true,
@ -74,13 +75,13 @@ export default {
computed: {
...mapGetters(['setting']),
...mapGetters('download', ['list', 'downloadStatus']),
...mapGetters('player', ['listId', 'playIndex']),
...mapGetters('player', ['playInfo']),
isPlayList() {
return this.listId == 'download'
return this.playInfo.listId == 'download'
},
playListIndex() {
if (this.listId != 'download' || !this.list.length) return
let info = this.list[this.playIndex]
if (this.playInfo.listId != 'download' || !this.list.length) return
let info = this.list[this.playInfo.playIndex]
if (!info) return -1
let key = info.key
return this.showList.findIndex(i => i.key == key)
@ -140,6 +141,11 @@ export default {
action: 'pause',
hide: !this.listMenu.itemMenuControl.pause,
},
{
name: this.$t('view.download.menu_play_later'),
action: 'playLater',
hide: !this.listMenu.itemMenuControl.playLater,
},
{
name: this.$t('view.download.menu_file'),
action: 'file',
@ -229,7 +235,7 @@ export default {
},
handleSelectData(event, clickIndex) {
if (this.keyEvent.isShiftDown) {
if (this.selectdData.length) {
if (this.selectedData.length) {
let lastSelectIndex = this.lastSelectIndex
this.removeAllSelect()
if (lastSelectIndex != clickIndex) {
@ -240,8 +246,8 @@ export default {
clickIndex = temp
isNeedReverse = true
}
this.selectdData = this.showList.slice(lastSelectIndex, clickIndex + 1)
if (isNeedReverse) this.selectdData.reverse()
this.selectedData = this.showList.slice(lastSelectIndex, clickIndex + 1)
if (isNeedReverse) this.selectedData.reverse()
let nodes = this.$refs.dom_tbody.childNodes
do {
nodes[lastSelectIndex].classList.add('active')
@ -250,24 +256,24 @@ export default {
}
} else {
event.currentTarget.classList.add('active')
this.selectdData.push(this.showList[clickIndex])
this.selectedData.push(this.showList[clickIndex])
this.lastSelectIndex = clickIndex
}
} else if (this.keyEvent.isModDown) {
this.lastSelectIndex = clickIndex
let item = this.showList[clickIndex]
let index = this.selectdData.indexOf(item)
let index = this.selectedData.indexOf(item)
if (index < 0) {
this.selectdData.push(item)
this.selectedData.push(item)
event.currentTarget.classList.add('active')
} else {
this.selectdData.splice(index, 1)
this.selectedData.splice(index, 1)
event.currentTarget.classList.remove('active')
}
} else if (this.selectdData.length) this.removeAllSelect()
} else if (this.selectedData.length) this.removeAllSelect()
},
removeAllSelect() {
this.selectdData = []
this.selectedData = []
let dom_tbody = this.$refs.dom_tbody
if (!dom_tbody) return
let nodes = dom_tbody.querySelectorAll('.active')
@ -306,6 +312,14 @@ export default {
case 'remove':
this.removeTask(item)
break
case 'playLater':
if (this.selectedData.length) {
this.setTempPlayList(this.selectedData.map(s => ({ listId: 'download', musicInfo: s })))
this.removeAllSelect()
} else {
this.setTempPlayList([{ listId: 'download', musicInfo: item }])
}
break
case 'file':
this.handleOpenFolder(item.filePath)
break
@ -316,7 +330,7 @@ export default {
},
handleSelectAllData() {
this.removeAllSelect()
this.selectdData = [...this.showList]
this.selectedData = [...this.showList]
let nodes = this.$refs.dom_tbody.childNodes
for (const node of nodes) {
@ -324,19 +338,19 @@ export default {
}
},
// async handleFlowBtnClick(action) {
// let selectdData = [...this.selectdData]
// let selectedData = [...this.selectedData]
// this.removeAllSelect()
// await this.$nextTick()
// switch (action) {
// case 'start':
// this.startTasks(selectdData)
// this.startTasks(selectedData)
// break
// case 'pause':
// this.pauseTasks(selectdData)
// this.pauseTasks(selectedData)
// break
// case 'remove':
// this.removeTasks(selectdData)
// this.removeTasks(selectedData)
// break
// }
// },
@ -353,7 +367,7 @@ export default {
})
},
handleTabChange() {
this.selectdData = []
this.selectedData = []
},
handleListItemRigthClick(event, index) {
this.listMenu.itemMenuControl.sourceDetail = !!musicSdk[this.showList[index].musicInfo.source].getMusicDetailPageUrl
@ -368,16 +382,19 @@ export default {
let item = this.showList[index]
if (item.isComplate) {
this.listMenu.itemMenuControl.play =
this.listMenu.itemMenuControl.playLater =
this.listMenu.itemMenuControl.file = true
this.listMenu.itemMenuControl.start =
this.listMenu.itemMenuControl.pause = false
} else if (item.status === this.downloadStatus.ERROR || item.status === this.downloadStatus.PAUSE) {
this.listMenu.itemMenuControl.play =
this.listMenu.itemMenuControl.pause =
this.listMenu.itemMenuControl.file = false
this.listMenu.itemMenuControl.start = true
// } else if (item.status === this.downloadStatus.ERROR || item.status === this.downloadStatus.PAUSE) {
// this.listMenu.itemMenuControl.play =
// this.listMenu.itemMenuControl.playLater =
// this.listMenu.itemMenuControl.pause =
// this.listMenu.itemMenuControl.file = false
// this.listMenu.itemMenuControl.start = true
} else {
this.listMenu.itemMenuControl.play =
this.listMenu.itemMenuControl.playLater =
this.listMenu.itemMenuControl.start =
this.listMenu.itemMenuControl.file = false
this.listMenu.itemMenuControl.pause = true
@ -407,10 +424,10 @@ export default {
if (item) this.handlePlay(item)
break
case 'start':
if (this.selectdData.length) {
let selectdData = [...this.selectdData]
if (this.selectedData.length) {
let selectedData = [...this.selectedData]
this.removeAllSelect()
this.startTasks(selectdData)
this.startTasks(selectedData)
} else {
item = this.showList[index]
if (item) this.startTask(item)
@ -420,10 +437,10 @@ export default {
}
break
case 'pause':
if (this.selectdData.length) {
let selectdData = [...this.selectdData]
if (this.selectedData.length) {
let selectedData = [...this.selectedData]
this.removeAllSelect()
this.pauseTasks(selectdData)
this.pauseTasks(selectedData)
} else {
item = this.showList[index]
if (item) this.pauseTask(item)
@ -443,10 +460,10 @@ export default {
if (item) this.handleSearch(item.musicInfo)
break
case 'remove':
if (this.selectdData.length) {
let selectdData = [...this.selectdData]
if (this.selectedData.length) {
let selectedData = [...this.selectedData]
this.removeAllSelect()
this.removeTasks(selectdData)
this.removeTasks(selectedData)
} else {
item = this.showList[index]
if (item) this.removeTask(item)

View File

@ -77,7 +77,7 @@ export default {
...mapActions('leaderboard', ['getBoardsList', 'getList']),
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
...mapMutations('list', ['listAdd', 'listAddMultiple']),
...mapMutations('player', ['setList']),
...mapMutations('player', ['setList', 'setTempPlayList']),
handleListBtnClick(info) {
switch (info.action) {
case 'download':
@ -121,6 +121,14 @@ export default {
}
this.testPlay(info.index)
break
case 'playLater':
if (this.selectedData.length) {
this.setTempPlayList(this.selectedData.map(s => ({ listId: '__temp__', musicInfo: s })))
this.resetSelect()
} else {
this.setTempPlayList([{ listId: '__temp__', musicInfo: this.list[info.index] }])
}
break
case 'search':
this.handleSearch(info.index)
break

View File

@ -37,7 +37,7 @@
table
tbody(@contextmenu.capture="handleContextMenu" ref="dom_tbody")
tr(v-for='(item, index) in list' :key='item.songmid' :id="'mid_' + item.songmid" @contextmenu="handleListItemRigthClick($event, index)"
@click="handleDoubleClick($event, index)" :class="[isPlayList && playIndex === index ? $style.active : '', assertApiSupport(item.source) ? null : $style.disabled]")
@click="handleDoubleClick($event, index)" :class="[isPlayList && playInfo.playIndex === index ? $style.active : '', assertApiSupport(item.source) ? null : $style.disabled]")
td.nobreak.center(style="width: 5%; padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}}
td.break
span.select {{item.name}}
@ -122,6 +122,7 @@ export default {
isShowItemMenu: false,
itemMenuControl: {
play: true,
playLater: true,
copyName: true,
addTo: true,
moveTo: true,
@ -145,12 +146,12 @@ export default {
computed: {
...mapGetters(['userInfo', 'setting']),
...mapGetters('list', ['isInitedList', 'defaultList', 'loveList', 'userList']),
...mapGetters('player', {
playerListId: 'listId',
playIndex: 'playIndex',
}),
...mapGetters('player', ['playInfo']),
playerListId() {
return this.playInfo.listId
},
isPlayList() {
return this.playerListId == this.listId
return this.playInfo.listId == this.listId
},
list() {
return this.listData.list
@ -223,6 +224,11 @@ export default {
action: 'download',
disabled: !this.listMenu.itemMenuControl.download,
},
{
name: this.$t('view.list.list_play_later'),
action: 'playLater',
disabled: !this.listMenu.itemMenuControl.playLater,
},
{
name: this.$t('view.list.list_add_to'),
action: 'addTo',
@ -355,6 +361,7 @@ export default {
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
...mapMutations('player', {
setPlayList: 'setList',
setTempPlayList: 'setTempPlayList',
}),
listenEvent() {
window.eventHub.$on('key_shift_down', this.handle_key_shift_down)
@ -726,6 +733,7 @@ export default {
handleListItemRigthClick(event, index) {
this.listMenu.itemMenuControl.sourceDetail = !!musicSdk[this.list[index].source].getMusicDetailPageUrl
this.listMenu.itemMenuControl.play =
this.listMenu.itemMenuControl.playLater =
this.listMenu.itemMenuControl.download =
this.assertApiSupport(this.list[index].source)
let dom_selected = this.$refs.dom_tbody.querySelector('tr.selected')
@ -789,6 +797,14 @@ export default {
case 'play':
this.testPlay(index)
break
case 'playLater':
if (this.selectdListDetailData.length) {
this.setTempPlayList(this.selectdListDetailData.map(s => ({ listId: this.listId, musicInfo: s })))
this.removeAllSelectListDetail()
} else {
this.setTempPlayList([{ listId: this.listId, musicInfo: this.list[index] }])
}
break
case 'copyName':
minfo = this.list[index]
clipboardWriteText(this.setting.download.fileName.replace('歌名', minfo.name).replace('歌手', minfo.singer))

View File

@ -3,60 +3,65 @@
//- transition
div(:class="$style.header")
material-tab(:class="$style.tab" :list="sources" align="left" item-key="id" item-name="name" v-model="searchSourceId")
div(v-if="listInfo.list.length" :class="$style.list")
div(:class="$style.thead")
table
thead
tr
th.nobreak.center(style="width: 5%;") #
th.nobreak {{$t('view.search.name')}}
th.nobreak(style="width: 22%;") {{$t('view.search.singer')}}
th.nobreak(style="width: 22%;") {{$t('view.search.album')}}
th.nobreak(style="width: 8%;") {{$t('view.search.time')}}
th.nobreak(style="width: 13%;") {{$t('view.search.action')}}
div.scroll(:class="$style.tbody" ref="dom_scrollContent")
table
tbody(@contextmenu.capture="handleContextMenu" ref="dom_tbody")
tr(v-for='(item, index) in listInfo.list' :key='item.songmid' @contextmenu="handleListItemRigthClick($event, index)" @click="handleDoubleClick($event, index)")
td.nobreak.center(style="width: 5%; padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}}
td.break
span.select {{item.name}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-if="item._types.ape || item._types.flac || item._types.wav") {{$t('material.song_list.lossless')}}
span.badge.badge-theme-info(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types['320k']") {{$t('material.song_list.high_quality')}}
span(:class="[$style.labelSource, $style.noSelect]" v-if="searchSourceId == 'all'") {{item.source}}
td.break(style="width: 22%;")
span.select {{item.singer}}
td.break(style="width: 22%;")
span.select {{item.albumName}}
td(style="width: 8%;")
span(:class="[$style.time, $style.noSelect]") {{item.interval || '--/--'}}
td(style="width: 13%; padding-left: 0; padding-right: 0;")
material-list-buttons(:index="index" :remove-btn="false" :class="$style.listBtn"
:play-btn="assertApiSupport(item.source)"
:download-btn="assertApiSupport(item.source)"
@btn-click="handleListBtnClick")
div(:class="$style.pagination")
material-pagination(:max-page="listInfo.allPage" :limit="listInfo.limit" :page="page" @btn-click="handleTogglePage")
div(v-else :class="$style.noitem")
div.scroll(:class="$style.noitemListContainer" v-if="setting.search.isShowHotSearch || setting.search.isShowHistorySearch")
dl(:class="[$style.noitemList, $style.noitemHotSearchList]" v-if="setting.search.isShowHotSearch")
dt(:class="$style.noitemListTitle") {{$t('view.search.hot_search')}}
dd(:class="$style.noitemListItem" @click="handleNoitemSearch(item)" v-for="item in hotSearchList") {{item}}
dl(:class="$style.noitemList" v-if="setting.search.isShowHistorySearch && historyList.length")
dt(:class="$style.noitemListTitle")
span {{$t('view.search.history_search')}}
span(:class="$style.historyClearBtn" @click="clearHistory" :tips="$t('view.search.history_clear')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 512 512' space='preserve')
use(xlink:href='#icon-eraser')
dd(:class="$style.noitemListItem" v-for="(item, index) in historyList" @contextmenu="removeHistory(index)" :key="index + item" @click="handleNoitemSearch(item)" :tips="$t('view.search.history_remove')") {{item}}
div(v-else :class="$style.noitem_list")
p {{$t('view.search.no_item')}}
div(:class="$style.main")
div(:class="$style.list" v-show="isLoading || listInfo.list.length")
div(:class="$style.thead")
table
thead
tr
th.nobreak.center(style="width: 5%;") #
th.nobreak {{$t('view.search.name')}}
th.nobreak(style="width: 22%;") {{$t('view.search.singer')}}
th.nobreak(style="width: 22%;") {{$t('view.search.album')}}
th.nobreak(style="width: 8%;") {{$t('view.search.time')}}
th.nobreak(style="width: 13%;") {{$t('view.search.action')}}
div.scroll(:class="$style.tbody" ref="dom_scrollContent")
table
tbody(@contextmenu.capture="handleContextMenu" ref="dom_tbody")
tr(v-for='(item, index) in listInfo.list' :key='item.songmid' @contextmenu="handleListItemRigthClick($event, index)" @click="handleDoubleClick($event, index)")
td.nobreak.center(style="width: 5%; padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}}
td.break
span.select {{item.name}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-if="item._types.ape || item._types.flac || item._types.wav") {{$t('material.song_list.lossless')}}
span.badge.badge-theme-info(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types['320k']") {{$t('material.song_list.high_quality')}}
span(:class="[$style.labelSource, $style.noSelect]" v-if="searchSourceId == 'all'") {{item.source}}
td.break(style="width: 22%;")
span.select {{item.singer}}
td.break(style="width: 22%;")
span.select {{item.albumName}}
td(style="width: 8%;")
span(:class="[$style.time, $style.noSelect]") {{item.interval || '--/--'}}
td(style="width: 13%; padding-left: 0; padding-right: 0;")
material-list-buttons(:index="index" :remove-btn="false" :class="$style.listBtn"
:play-btn="assertApiSupport(item.source)"
:download-btn="assertApiSupport(item.source)"
@btn-click="handleListBtnClick")
div(:class="$style.pagination")
material-pagination(:max-page="listInfo.allPage" :limit="listInfo.limit" :page="page" @btn-click="handleTogglePage")
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated fadeOut")
div(v-show="isLoading" :class="$style.loading")
p {{$t('view.search.loding_list')}}
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
div(v-show="!isLoading && !listInfo.list.length" :class="$style.noitem")
div.scroll(:class="$style.noitemListContainer" v-if="setting.search.isShowHotSearch || setting.search.isShowHistorySearch")
dl(:class="[$style.noitemList, $style.noitemHotSearchList]" v-if="setting.search.isShowHotSearch")
dt(:class="$style.noitemListTitle") {{$t('view.search.hot_search')}}
dd(:class="$style.noitemListItem" @click="handleNoitemSearch(item)" v-for="item in hotSearchList") {{item}}
dl(:class="$style.noitemList" v-if="setting.search.isShowHistorySearch && historyList.length")
dt(:class="$style.noitemListTitle")
span {{$t('view.search.history_search')}}
span(:class="$style.historyClearBtn" @click="clearHistory" :tips="$t('view.search.history_clear')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 512 512' space='preserve')
use(xlink:href='#icon-eraser')
dd(:class="$style.noitemListItem" v-for="(item, index) in historyList" @contextmenu="removeHistory(index)" :key="index + item" @click="handleNoitemSearch(item)" :tips="$t('view.search.history_remove')") {{item}}
div(v-else :class="$style.noitem_list")
p {{$t('view.search.no_item')}}
material-menu(:menus="listItemMenu" :location="listMenu.menuLocation" item-name="name" :isShow="listMenu.isShowItemMenu" @menu-click="handleListItemMenuClick")
material-download-modal(:show="isShowDownload" :musicInfo="musicInfo" @select="handleAddDownload" @close="isShowDownload = false")
material-download-multiple-modal(:show="isShowDownloadMultiple" :list="selectedData" @select="handleAddDownloadMultiple" @close="isShowDownloadMultiple = false")
//- material-flow-btn(:show="isShowEditBtn && (searchSourceId == 'all' || assertApiSupport(searchSourceId))" :remove-btn="false" @btn-click="handleFlowBtnClick")
material-list-add-modal(:show="isShowListAdd" :musicInfo="musicInfo" @close="handleListAddModalClose")
material-list-add-multiple-modal(:show="isShowListAddMultiple" :musicList="selectedData" @close="handleListAddMultipleModalClose")
material-menu(:menus="listItemMenu" :location="listMenu.menuLocation" item-name="name" :isShow="listMenu.isShowItemMenu" @menu-click="handleListItemMenuClick")
</template>
<script>
@ -88,6 +93,7 @@ export default {
isShowItemMenu: false,
itemMenuControl: {
play: true,
playLater: true,
addTo: true,
download: true,
sourceDetail: true,
@ -97,6 +103,7 @@ export default {
y: 0,
},
},
isLoading: false,
}
},
beforeRouteUpdate(to, from, next) {
@ -148,6 +155,7 @@ export default {
},
searchSourceId(n) {
if (n === this.setting.search.searchSource) return
if (this.text !== '') this.isLoading = true
this.$nextTick(() => {
this.page = 1
this.handleSearch(this.text, this.page)
@ -174,6 +182,11 @@ export default {
action: 'download',
disabled: !this.listMenu.itemMenuControl.download,
},
{
name: this.$t('view.search.list_play_later'),
action: 'playLater',
disabled: !this.listMenu.itemMenuControl.playLater,
},
{
name: this.$t('view.search.list_add_to'),
action: 'addTo',
@ -199,7 +212,7 @@ export default {
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
...mapMutations('search', ['clearList', 'setPage', 'removeHistory', 'clearHistory']),
...mapMutations('list', ['listAdd', 'listAddMultiple']),
...mapMutations('player', ['setList']),
...mapMutations('player', ['setList', 'setTempPlayList']),
...mapActions('hotSearch', {
getHotSearch: 'getList',
}),
@ -238,12 +251,14 @@ export default {
},
handleSearch(text, page) {
if (text === '') return this.clearList()
this.isLoading = true
this.search({ text, page, limit: this.listInfo.limit }).then(data => {
this.page = page
this.$nextTick(() => {
scrollTo(this.$refs.dom_scrollContent, 0)
})
}).finally(() => {
this.isLoading = false
})
},
handleDoubleClick(event, index) {
@ -274,6 +289,14 @@ export default {
case 'play':
this.testPlay(info.index)
break
case 'playLater':
if (this.selectedData.length) {
this.setTempPlayList(this.selectedData.map(s => ({ listId: '__temp__', musicInfo: s })))
this.resetSelect()
} else {
this.setTempPlayList([{ listId: '__temp__', musicInfo: this.list[info.index] }])
}
break
case 'listAdd':
this.musicInfo = this.listInfo.list[info.index]
this.$nextTick(() => {
@ -423,6 +446,7 @@ export default {
handleListItemRigthClick(event, index) {
this.listMenu.itemMenuControl.sourceDetail = !!musicSdk[this.listInfo.list[index].source].getMusicDetailPageUrl
this.listMenu.itemMenuControl.play =
this.listMenu.itemMenuControl.playLater =
this.listMenu.itemMenuControl.download =
this.assertApiSupport(this.listInfo.list[index].source)
let dom_selected = this.$refs.dom_tbody.querySelector('tr.selected')
@ -504,6 +528,12 @@ export default {
.header {
flex: none;
}
.main {
flex: auto;
min-height: 0;
position: relative;
display: flex;
}
.list {
// position: relative;
height: 100%;
@ -560,10 +590,30 @@ export default {
// left: 50%;
// transform: translateX(-50%);
}
.loading {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
background-color: @color-theme_2;
p {
font-size: 24px;
color: @color-theme_2-font-label;
}
}
.noitem {
flex: auto;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
overflow: hidden;
position: relative;
display: flex;
flex-flow: column nowrap;
justify-content: center;
@ -635,6 +685,9 @@ each(@themes, {
.labelSource {
color: ~'@{color-@{value}-theme}';
}
.loading {
background-color: ~'@{color-@{value}-theme_2}';
}
.noitem {
p {
color: ~'@{color-@{value}-theme_2-font-label}';

View File

@ -21,14 +21,13 @@ div.scroll(:class="$style.setting" ref="dom_setting")
label {{$t('store.state.theme_' + theme.class)}}
dd
h3#basic_show_animation {{$t('view.setting.basic_show_animation')}}
div
material-checkbox(id="setting_show_animate" v-model="current_setting.isShowAnimation" :label="$t('view.setting.is_show')")
div(:class="[$style.gapTop, $style.top]")
material-checkbox(id="setting_show_animate" v-model="current_setting.isShowAnimation" :label="$t('view.setting.basic_show_animation')")
div(:class="$style.gapTop")
material-checkbox(id="setting_animate" v-model="current_setting.randomAnimate" :label="$t('view.setting.basic_animation')")
div(:class="$style.gapTop")
material-checkbox(id="setting_to_tray" v-model="current_setting.tray.isShow" @change="handleTrayShowChange" :label="$t('view.setting.basic_to_tray')")
dd(:tips="$t('view.setting.basic_animation_title')")
h3#basic_animation {{$t('view.setting.basic_animation')}}
div
material-checkbox(id="setting_animate" v-model="current_setting.randomAnimate" :label="$t('view.setting.is_enable')")
dd(:tips="$t('view.setting.basic_source_title')")
h3#basic_source {{$t('view.setting.basic_source')}}
@ -37,11 +36,6 @@ div.scroll(:class="$style.setting" ref="dom_setting")
material-checkbox(:id="`setting_api_source_${item.id}`" name="setting_api_source" @change="handleAPISourceChange(item.id)"
need v-model="current_setting.apiSource" :disabled="item.disabled" :value="item.id" :label="item.label")
dd(:tips="$t('view.setting.basic_to_tray_title')")
h3#basic_to_tray {{$t('view.setting.basic_to_tray')}}
div
material-checkbox(id="setting_to_tray" v-model="current_setting.tray.isShow" @change="handleTrayShowChange" :label="$t('view.setting.is_enable')")
dd(:tips="$t('view.setting.basic_window_size_title')")
h3#basic_window_size {{$t('view.setting.basic_window_size')}}
div
@ -68,27 +62,17 @@ div.scroll(:class="$style.setting" ref="dom_setting")
name="setting_basic_control_btn_position" need v-model="current_setting.controlBtnPosition" :value="item.id" :label="item.name")
dt#play {{$t('view.setting.play')}}
dd(:tips="$t('view.setting.play_toggle_title')")
h3#play_toggle {{$t('view.setting.play_toggle')}}
div
material-checkbox(:id="`setting_player_togglePlay_${item.value}`" :class="$style.gapLeft" :value="item.value" :key="item.value"
v-model="current_setting.player.togglePlayMethod" v-for="item in togglePlayMethods" :label="item.name")
dd
h3#play_lyric_transition {{$t('view.setting.play_lyric_transition')}}
div
material-checkbox(id="setting_player_lyric_transition" v-model="current_setting.player.isShowLyricTransition" :label="$t('view.setting.is_show')")
dd(:tips="$t('view.setting.play_quality_title')")
h3#play_quality {{$t('view.setting.play_quality')}}
div
material-checkbox(id="setting_player_highQuality" v-model="current_setting.player.highQuality" :label="$t('view.setting.is_enable')")
dd(:tips="$t('view.setting.play_task_bar_title')")
h3#play_task_bar {{$t('view.setting.play_task_bar')}}
div
material-checkbox(id="setting_player_showTaskProgess" v-model="current_setting.player.isShowTaskProgess" :label="$t('view.setting.is_enable')")
dd(:tips="$t('view.setting.play_mediaDevice_remove_stop_play_title')")
h3#play_mediaDevice_remove_stop_play {{$t('view.setting.play_mediaDevice_remove_stop_play')}}
div
material-checkbox(id="setting_player_isMediaDeviceRemovedStopPlay" v-model="current_setting.player.isMediaDeviceRemovedStopPlay" :label="$t('view.setting.is_enable')")
div(:class="$style.gapTop")
material-checkbox(id="setting_player_save_play_time" v-model="current_setting.player.isSavePlayTime" :label="$t('view.setting.play_save_play_time')")
div(:class="$style.gapTop")
material-checkbox(id="setting_player_lyric_transition" v-model="current_setting.player.isShowLyricTransition" :label="$t('view.setting.play_lyric_transition')")
div(:class="$style.gapTop")
material-checkbox(id="setting_player_highQuality" v-model="current_setting.player.highQuality" :label="$t('view.setting.play_quality')")
div(:class="$style.gapTop")
material-checkbox(id="setting_player_showTaskProgess" v-model="current_setting.player.isShowTaskProgess" :label="$t('view.setting.play_task_bar')")
div(:class="$style.gapTop")
material-checkbox(id="setting_player_isMediaDeviceRemovedStopPlay" v-model="current_setting.player.isMediaDeviceRemovedStopPlay" :label="$t('view.setting.play_mediaDevice_remove_stop_play')")
dd(:tips="$t('view.setting.play_mediaDevice_title')")
h3#play_mediaDevice {{$t('view.setting.play_mediaDevice')}}
div
@ -103,29 +87,22 @@ div.scroll(:class="$style.setting" ref="dom_setting")
material-checkbox(id="setting_desktop_lyric_alwaysOnTop" v-model="current_setting.desktopLyric.isAlwaysOnTop" :label="$t('view.setting.desktop_lyric_always_on_top')")
div(:class="$style.gapTop")
material-checkbox(id="setting_desktop_lyric_lockScreen" v-model="current_setting.desktopLyric.isLockScreen" :label="$t('view.setting.desktop_lyric_lock_screen')")
dt#search {{$t('view.setting.search')}}
dd(:tips="$t('view.setting.search_hot_title')")
h3#search_hot {{$t('view.setting.search_hot')}}
div
material-checkbox(id="setting_search_showHot_enable" v-model="current_setting.search.isShowHotSearch" :label="$t('view.setting.is_show')")
dd(:tips="$t('view.setting.search_history_title')")
h3#search_history {{$t('view.setting.search_history')}}
div
material-checkbox(id="setting_search_showHistory_enable" v-model="current_setting.search.isShowHistorySearch" :label="$t('view.setting.is_show')")
dd(:tips="$t('view.setting.search_focus_search_box_title')")
h3#search_focus_search_box {{$t('view.setting.search_focus_search_box')}}
div
material-checkbox(id="setting_search_focusSearchBox_enable" v-model="current_setting.search.isFocusSearchBox" :label="$t('view.setting.is_enable')")
dd
div(:class="$style.gapTop")
material-checkbox(id="setting_search_showHot_enable" v-model="current_setting.search.isShowHotSearch" :label="$t('view.setting.search_hot')")
div(:class="$style.gapTop")
material-checkbox(id="setting_search_showHistory_enable" v-model="current_setting.search.isShowHistorySearch" :label="$t('view.setting.search_history')")
div(:class="$style.gapTop")
material-checkbox(id="setting_search_focusSearchBox_enable" v-model="current_setting.search.isFocusSearchBox" :label="$t('view.setting.search_focus_search_box')")
dt#list {{$t('view.setting.list')}}
dd(:tips="$t('view.setting.list_source_title')")
h3#list_source {{$t('view.setting.list_source')}}
div
material-checkbox(id="setting_list_showSource_enable" v-model="current_setting.list.isShowSource" :label="$t('view.setting.is_show')")
dd(:tips="$t('view.setting.list_scroll_title')")
h3#list_scroll {{$t('view.setting.list_scroll')}}
div
material-checkbox(id="setting_list_scroll_enable" v-model="current_setting.list.isSaveScrollLocation" :label="$t('view.setting.is_enable')")
dd
div(:class="$style.gapTop")
material-checkbox(id="setting_list_showSource_enable" v-model="current_setting.list.isShowSource" :label="$t('view.setting.list_source')")
div(:class="$style.gapTop")
material-checkbox(id="setting_list_scroll_enable" v-model="current_setting.list.isSaveScrollLocation" :label="$t('view.setting.list_scroll')")
//- dd(:tips="")
h3 专辑栏
div
@ -195,13 +172,10 @@ div.scroll(:class="$style.setting" ref="dom_setting")
material-input(:class="$style.gapLeft" v-model="current_setting.network.proxy.password" @change="handleProxyChange('password')" type="password" :placeholder="$t('view.setting.network_proxy_password')")
dt#odc {{$t('view.setting.odc')}}
dd
h3#odc_clear_search_input {{$t('view.setting.odc_clear_search_input')}}
div
material-checkbox(id="setting_odc_isAutoClearSearchInput" v-model="current_setting.odc.isAutoClearSearchInput" :label="$t('view.setting.is_enable')")
dd
h3#odc_clear_search_list {{$t('view.setting.odc_clear_search_list')}}
div
material-checkbox(id="setting_odc_isAutoClearSearchList" v-model="current_setting.odc.isAutoClearSearchList" :label="$t('view.setting.is_enable')")
div(:class="$style.gapTop")
material-checkbox(id="setting_odc_isAutoClearSearchInput" v-model="current_setting.odc.isAutoClearSearchInput" :label="$t('view.setting.odc_clear_search_input')")
div(:class="$style.gapTop")
material-checkbox(id="setting_odc_isAutoClearSearchList" v-model="current_setting.odc.isAutoClearSearchList" :label="$t('view.setting.odc_clear_search_list')")
dt#backup {{$t('view.setting.backup')}}
dd
h3#backup_part {{$t('view.setting.backup_part')}}
@ -745,13 +719,18 @@ export default {
}
},
exportPlayList(path) {
const data = {
const data = JSON.parse(JSON.stringify({
type: 'playList',
data: [
this.defaultList,
this.loveList,
...this.userList,
],
}))
for (const list of data.data) {
for (const item of list.list) {
if (item.otherSource) delete item.otherSource
}
}
this.handleSaveFile(path, JSON.stringify(data))
},
@ -776,7 +755,7 @@ export default {
}
},
async exportAllData(path) {
let allData = {
let allData = JSON.parse(JSON.stringify({
type: 'allData',
setting: Object.assign({ version: this.settingVersion }, this.setting),
playList: [
@ -784,6 +763,11 @@ export default {
this.loveList,
...this.userList,
],
}))
for (const list of allData.playList) {
for (const item of list.list) {
if (item.otherSource) delete item.otherSource
}
}
this.handleSaveFile(path, JSON.stringify(allData))
},
@ -1202,6 +1186,10 @@ export default {
}
}
.gap-top {
&.top {
margin-top: 25px;
}
+ .gap-top {
margin-top: 10px;
}

View File

@ -183,6 +183,7 @@ export default {
...mapMutations('list', ['listAdd', 'listAddMultiple', 'createUserList']),
...mapMutations('player', {
setPlayList: 'setList',
setTempPlayList: 'setTempPlayList',
}),
listenEvent() {
window.eventHub.$on('key_backspace_down', this.handle_key_backspace_down)
@ -243,6 +244,14 @@ export default {
}
this.testPlay(info.index)
break
case 'playLater':
if (this.selectedData.length) {
this.setTempPlayList(this.selectedData.map(s => ({ listId: '__temp__', musicInfo: s })))
this.resetSelect()
} else {
this.setTempPlayList([{ listId: '__temp__', musicInfo: this.listDetail.list[info.index] }])
}
break
case 'search':
this.handleSearch(info.index)
break