Merge branch 'dev'

pull/392/head v1.5.0
lyswhut 2020-12-13 11:48:17 +08:00
commit 0709aa56a4
29 changed files with 771 additions and 459 deletions

View File

@ -13,10 +13,13 @@ assignees: ''
- [ ] 我已搜索issue列表(<https://github.com/lyswhut/lx-music-desktop/issues?utf8=✓&q=>) - [ ] 我已搜索issue列表(<https://github.com/lyswhut/lx-music-desktop/issues?utf8=✓&q=>)
**描述您想要的解决方案** **描述您想要的解决方案**
简洁明了地描述您要发生的事情。 <!-- 简洁明了地描述您要发生的事情。 -->
**描述您考虑过的替代方案** **描述您考虑过的替代方案**
对您考虑过的所有替代解决方案或功能的简洁明了的描述。 <!-- 对您考虑过的所有替代解决方案或功能的简洁明了的描述。 -->
**其他内容** **其他内容**
在此处添加有关功能请求的任何其他上下文或屏幕截图(直接把图片拖到编辑框即可添加图片)。 <!-- 在此处添加有关功能请求的任何其他上下文或屏幕截图(直接把图片拖到编辑框即可添加图片)。 -->

View File

@ -13,7 +13,8 @@ assignees: ''
- [ ] 我已搜索issue列表(<https://github.com/lyswhut/lx-music-desktop/issues?utf8=✓&q=>) - [ ] 我已搜索issue列表(<https://github.com/lyswhut/lx-music-desktop/issues?utf8=✓&q=>)
**描述错误** **描述错误**
清楚简洁地说明错误是什么。 <!-- 清楚简洁地说明错误是什么。 -->
**重现** **重现**
重现行为的步骤: 重现行为的步骤:
@ -22,15 +23,20 @@ assignees: ''
3.向下滚动到“ ....” 3.向下滚动到“ ....”
4.看到错误 4.看到错误
**预期行为** **预期行为**
对您期望发生的事情的简洁明了的描述。 <!-- 对您期望发生的事情的简洁明了的描述。 -->
**截图** **截图**
如果适用,请添加屏幕截图以帮助解释您的问题(直接把图片拖到编辑框即可添加图片)。 <!-- 如果适用,请添加屏幕截图以帮助解释您的问题(直接把图片拖到编辑框即可添加图片)。 -->
**环境:** **环境:**
  -操作系统及版本:[例如Windows 10 64位 18362.156]   -操作系统及版本:[例如Windows 10 64位 18362.156]
  -软件安装包及版本:[例如Windows 64位绿色版 1.0.0]   -软件安装包及版本:[例如Windows 64位绿色版 1.0.0]
**其他内容** **其他内容**
在此处添加有关该问题的任何其他上下文。 <!-- 在此处添加有关该问题的任何其他上下文。 -->

View File

@ -6,6 +6,23 @@ Project versioning adheres to [Semantic Versioning](http://semver.org/).
Commit convention is based on [Conventional Commits](http://conventionalcommits.org). Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
Change log format is based on [Keep a Changelog](http://keepachangelog.com/). Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
## [1.5.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.4.1...v1.5.0) - 2020-12-13
### 新增
- 直接从歌单详情收藏的列表新增同步功能。注意:这将会覆盖本地的目标列表,歌曲将被替换成最新的在线列表
### 优化
- 优化软件启动时恢复上一次播放的歌曲进度功能
### 修复
- 修复MAC平台上下载歌曲封面嵌入无法显示的问题
- 修复MAC平台首次运行软件最小化、关闭控制按钮默认在右边的问题
- 修复酷狗源的某些歌曲没有专辑字段导致的列表加载失败问题
- 修复某些酷狗源歌单链接无法打开的问题
## [1.4.1](https://github.com/lyswhut/lx-music-desktop/compare/v1.4.0...v1.4.1) - 2020-11-25 ## [1.4.1](https://github.com/lyswhut/lx-music-desktop/compare/v1.4.0...v1.4.1) - 2020-11-25

View File

@ -9,9 +9,10 @@ module.exports = {
}, },
resolve: { resolve: {
alias: { alias: {
'@': path.join(__dirname, '../../src/main'), '@main': path.join(__dirname, '../../src/main'),
events: path.join(__dirname, '../../src/main/events'), '@renderer': path.join(__dirname, '../../src/renderer'),
common: path.join(__dirname, '../../src/common'), '@lyric': path.join(__dirname, '../../src/renderer-lyric'),
'@common': path.join(__dirname, '../../src/common'),
}, },
extensions: ['*', '.js', '.json', '.node'], extensions: ['*', '.js', '.json', '.node'],
}, },

View File

@ -21,8 +21,11 @@ module.exports = {
}, },
resolve: { resolve: {
alias: { alias: {
'@': path.join(__dirname, '../../src/renderer'), '@main': path.join(__dirname, '../../src/main'),
common: path.join(__dirname, '../../src/common'), '@renderer': path.join(__dirname, '../../src/renderer'),
'@lyric': path.join(__dirname, '../../src/renderer-lyric'),
'@static': path.join(__dirname, '../../src/static'),
'@common': path.join(__dirname, '../../src/common'),
}, },
extensions: ['*', '.js', '.json', '.vue', '.node'], extensions: ['*', '.js', '.json', '.vue', '.node'],
}, },

View File

@ -21,8 +21,11 @@ module.exports = {
}, },
resolve: { resolve: {
alias: { alias: {
'@': path.join(__dirname, '../../src/renderer'), '@main': path.join(__dirname, '../../src/main'),
common: path.join(__dirname, '../../src/common'), '@renderer': path.join(__dirname, '../../src/renderer'),
'@lyric': path.join(__dirname, '../../src/renderer-lyric'),
'@static': path.join(__dirname, '../../src/static'),
'@common': path.join(__dirname, '../../src/common'),
}, },
extensions: ['*', '.js', '.json', '.vue', '.node'], extensions: ['*', '.js', '.json', '.vue', '.node'],
}, },

19
jsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
// This is the line you want to add
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"paths": {
"@main": ["src/main"],
"@renderer": ["src/renderer"],
"@lyric": ["src/renderer-lyric"],
"@static": ["src/static"],
"@common": ["src/common"],
}
},
"include": ["src/**/*"],
"exclude": ["node_modules/**/*"]
}

906
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "lx-music-desktop", "name": "lx-music-desktop",
"version": "1.4.1", "version": "1.5.0",
"description": "一个免费的音乐下载助手", "description": "一个免费的音乐下载助手",
"main": "./dist/electron/main.js", "main": "./dist/electron/main.js",
"productName": "lx-music-desktop", "productName": "lx-music-desktop",
@ -34,16 +34,18 @@
"publish:gh:linux": "node build-config/pack.js && npm run publish:linux", "publish:gh:linux": "node build-config/pack.js && npm run publish:linux",
"publish:linux": "npm run publish:linux:deb && npm run publish:linux:appImage && npm run publish:linux:rpm && npm run publish:linux:pacman", "publish:linux": "npm run publish:linux:deb && npm run publish:linux:appImage && npm run publish:linux:rpm && npm run publish:linux:pacman",
"publish:linux:appImage": "cross-env ARCH=x64 electron-builder -l=AppImage -p onTagOrDraft", "publish:linux:appImage": "cross-env ARCH=x64 electron-builder -l=AppImage -p onTagOrDraft",
"publish:linux:deb": "npm run publish:linux:deb:x64 && npm run publish:linux:deb:x86", "publish:linux:deb": "npm run publish:linux:deb:x64 && npm run publish:linux:deb:x86 && npm run publish:linux:deb:arm64",
"publish:linux:deb:x64": "cross-env ARCH=x64 electron-builder -l=deb --x64 -p onTagOrDraft", "publish:linux:deb:x64": "cross-env ARCH=x64 electron-builder -l=deb --x64 -p onTagOrDraft",
"publish:linux:deb:x86": "cross-env ARCH=x86 electron-builder -l=deb --ia32 -p onTagOrDraft", "publish:linux:deb:x86": "cross-env ARCH=x86 electron-builder -l=deb --ia32 -p onTagOrDraft",
"publish:linux:deb:arm64": "cross-env ARCH=arm64 electron-builder -l=deb --arm64 -p onTagOrDraft",
"publish:linux:rpm": "cross-env ARCH=x64 electron-builder -l=rpm --x64 -p onTagOrDraft", "publish:linux:rpm": "cross-env ARCH=x64 electron-builder -l=rpm --x64 -p onTagOrDraft",
"publish:linux:pacman": "cross-env ARCH=x64 electron-builder -l=pacman --x64 -p onTagOrDraft", "publish:linux:pacman": "cross-env ARCH=x64 electron-builder -l=pacman --x64 -p onTagOrDraft",
"pack:linux": "node build-config/pack.js && npm run pack:linux:deb && npm run pack:linux:appImage && npm run pack:linux:rpm && npm run pack:linux:pacman", "pack:linux": "node build-config/pack.js && npm run pack:linux:deb && npm run pack:linux:appImage && npm run pack:linux:rpm && npm run pack:linux:pacman",
"pack:linux:appImage": "cross-env ARCH=x64 electron-builder -l=AppImage", "pack:linux:appImage": "cross-env ARCH=x64 electron-builder -l=AppImage",
"pack:linux:deb": "npm run pack:linux:deb:x64 && npm run pack:linux:deb:x86", "pack:linux:deb": "npm run pack:linux:deb:x64 && npm run pack:linux:deb:x86 && npm run pack:linux:deb:arm64",
"pack:linux:deb:x64": "cross-env ARCH=x64 electron-builder -l=deb --x64", "pack:linux:deb:x64": "cross-env ARCH=x64 electron-builder -l=deb --x64",
"pack:linux:deb:x86": "cross-env ARCH=x86 electron-builder -l=deb --ia32", "pack:linux:deb:x86": "cross-env ARCH=x86 electron-builder -l=deb --ia32",
"pack:linux:deb:arm64": "cross-env ARCH=arm64 electron-builder -l=deb --arm64",
"pack:linux:rpm": "cross-env ARCH=x64 electron-builder -l=rpm --x64", "pack:linux:rpm": "cross-env ARCH=x64 electron-builder -l=rpm --x64",
"pack:linux:pacman": "cross-env ARCH=x64 electron-builder -l=pacman --x64", "pack:linux:pacman": "cross-env ARCH=x64 electron-builder -l=pacman --x64",
"pack:mac": "node build-config/pack.js && electron-builder -m=dmg", "pack:mac": "node build-config/pack.js && electron-builder -m=dmg",
@ -158,29 +160,29 @@
}, },
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme", "homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.3", "@babel/core": "^7.12.10",
"@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-modules-umd": "^7.12.1", "@babel/plugin-transform-modules-umd": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.12.1", "@babel/plugin-transform-runtime": "^7.12.10",
"@babel/polyfill": "^7.12.1", "@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.12.1", "@babel/preset-env": "^7.12.10",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-loader": "^8.2.1", "babel-loader": "^8.2.2",
"babel-minify-webpack-plugin": "^0.3.1", "babel-minify-webpack-plugin": "^0.3.1",
"babel-preset-minify": "^0.5.1", "babel-preset-minify": "^0.5.1",
"cfonts": "^2.8.6", "cfonts": "^2.9.0",
"chalk": "^4.1.0", "chalk": "^4.1.0",
"changelog-parser": "^2.8.0", "changelog-parser": "^2.8.0",
"copy-webpack-plugin": "^6.3.1", "copy-webpack-plugin": "^6.4.0",
"core-js": "^3.7.0", "core-js": "^3.8.1",
"cross-env": "^7.0.2", "cross-env": "^7.0.3",
"css-loader": "^4.3.0", "css-loader": "^4.3.0",
"del": "^6.0.0", "del": "^6.0.0",
"electron": "^9.3.3", "electron": "^9.3.3",
"electron-builder": "^22.9.1", "electron-builder": "^22.9.1",
"electron-debug": "^3.1.0", "electron-debug": "^3.1.0",
"electron-devtools-installer": "^3.1.1", "electron-devtools-installer": "^3.1.1",
"eslint": "^7.13.0", "eslint": "^7.15.0",
"eslint-config-standard": "^14.1.1", "eslint-config-standard": "^14.1.1",
"eslint-formatter-friendly": "^7.0.0", "eslint-formatter-friendly": "^7.0.0",
"eslint-loader": "^4.0.2", "eslint-loader": "^4.0.2",
@ -194,19 +196,19 @@
"html-webpack-plugin": "^4.5.0", "html-webpack-plugin": "^4.5.0",
"less": "^3.12.2", "less": "^3.12.2",
"less-loader": "^7.1.0", "less-loader": "^7.1.0",
"markdown-it": "^12.0.2", "markdown-it": "^12.0.3",
"mini-css-extract-plugin": "^0.12.0", "mini-css-extract-plugin": "^0.12.0",
"optimize-css-assets-webpack-plugin": "^5.0.4", "optimize-css-assets-webpack-plugin": "^5.0.4",
"postcss-loader": "^4.0.4", "postcss-loader": "^4.1.0",
"postcss-pxtorem": "^5.1.1", "postcss-pxtorem": "^5.1.1",
"pug": "^3.0.0", "pug": "^3.0.0",
"pug-loader": "^2.4.0", "pug-loader": "^2.4.0",
"pug-plain-loader": "^1.0.0", "pug-plain-loader": "^1.1.0",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"spinnies": "^0.5.1", "spinnies": "^0.5.1",
"stylus": "^0.54.8", "stylus": "^0.54.8",
"stylus-loader": "^4.3.0", "stylus-loader": "^4.3.1",
"terser-webpack-plugin": "^4.2.3", "terser-webpack-plugin": "^4.2.3",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"vue-loader": "^15.9.5", "vue-loader": "^15.9.5",
@ -215,7 +217,7 @@
"webpack-cli": "^3.3.12", "webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0", "webpack-dev-server": "^3.11.0",
"webpack-hot-middleware": "^2.25.0", "webpack-hot-middleware": "^2.25.0",
"webpack-merge": "^5.4.0" "webpack-merge": "^5.6.1"
}, },
"dependencies": { "dependencies": {
"crypto-js": "^4.0.0", "crypto-js": "^4.0.0",
@ -228,12 +230,12 @@
"js-htmlencode": "^0.3.0", "js-htmlencode": "^0.3.0",
"lrc-file-parser": "^1.0.5", "lrc-file-parser": "^1.0.5",
"needle": "^2.5.2", "needle": "^2.5.2",
"node-id3": "^0.1.21", "node-id3": "^0.1.19",
"request": "^2.88.2", "request": "^2.88.2",
"vue": "^2.6.12", "vue": "^2.6.12",
"vue-i18n": "^8.22.1", "vue-i18n": "^8.22.2",
"vue-router": "^3.4.9", "vue-router": "^3.4.9",
"vuex": "^3.5.1", "vuex": "^3.6.0",
"vuex-router-sync": "^5.0.0" "vuex-router-sync": "^5.0.0"
} }
} }

View File

@ -1,6 +1,14 @@
### 新增
- 直接从歌单详情收藏的列表新增同步功能。注意:这将会覆盖本地的目标列表,歌曲将被替换成最新的在线列表
### 优化
- 优化软件启动时恢复上一次播放的歌曲进度功能
### 修复 ### 修复
- 修复有歌词翻译与无歌词的音乐间切换会导致歌词翻译残留显示的问题 - 修复MAC平台上下载歌曲封面嵌入无法显示的问题
- 修复歌曲URL过期时等待刷新URL的自动切换歌曲时间间隔太短的问题 - 修复MAC平台首次运行软件最小化、关闭控制按钮默认在右边的问题
- 修复某些电脑上的某些歌曲没有声音的问题升级Electron9.3.4导致的现降级到9.3.3 - 修复酷狗源的某些歌曲没有专辑字段导致的列表加载失败问题
- 修复某些酷狗源歌单链接无法打开的问题

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,5 @@
const path = require('path') const path = require('path')
const os = require('os') const os = require('os')
const { isMac } = require('./utils')
const defaultSetting = { const defaultSetting = {
version: '1.0.38', version: '1.0.38',
@ -92,7 +91,7 @@ const defaultSetting = {
randomAnimate: true, randomAnimate: true,
ignoreVersion: null, ignoreVersion: null,
isAgreePact: false, isAgreePact: false,
controlBtnPosition: isMac ? 'left' : 'right', controlBtnPosition: process.platform === 'darwin' ? 'left' : 'right',
} }
const overwriteSetting = { const overwriteSetting = {

View File

@ -1,4 +1,4 @@
const names = require('../main/events/_name') const names = require('@main/events/_name')
const hotKey = { const hotKey = {
common: { common: {
min: { min: {

View File

@ -273,7 +273,21 @@ export default {
getPlayList().then(({ defaultList, loveList, userList, downloadList }) => { getPlayList().then(({ defaultList, loveList, userList, downloadList }) => {
if (!defaultList) defaultList = this.defaultList if (!defaultList) defaultList = this.defaultList
if (!loveList) loveList = this.loveList if (!loveList) loveList = this.loveList
if (!userList) userList = this.userList if (userList) {
let needSave = false
const getListId = id => id.includes('.') ? getListId(id.substring(0, id.lastIndexOf('_'))) : id
userList.forEach(l => {
if (!l.id.includes('__') || l.source) return
let [source, id] = l.id.split('__')
id = getListId(id)
l.source = source
l.sourceListId = id
if (!needSave) needSave = true
})
if (needSave) this.saveUserList(userList)
} else {
userList = this.userList
}
if (!defaultList.list) defaultList.list = [] if (!defaultList.list) defaultList.list = []
if (!loveList.list) loveList.list = [] if (!loveList.list) loveList.list = []
@ -306,7 +320,9 @@ export default {
initPlayInfo() { initPlayInfo() {
rendererInvoke(NAMES.mainWindow.get_data, 'playInfo').then(info => { rendererInvoke(NAMES.mainWindow.get_data, 'playInfo').then(info => {
// console.log(info, window.allList) // console.log(info, window.allList)
window.restorePlayInfo = null
if (!info) return if (!info) return
if (info.index < 0) return
if (info.listId) { if (info.listId) {
const list = window.allList[info.listId] const list = window.allList[info.listId]
// console.log(list) // console.log(list)

View File

@ -244,9 +244,31 @@ export default {
watch: { watch: {
changePlay(n) { changePlay(n) {
if (!n) return 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')
})
window.restorePlayInfo = null
return
}
// console.log('changePlay') // console.log('changePlay')
this.handleRemoveMusic() this.handleRemoveMusic()
this.resetChangePlay()
if (this.playIndex < 0) return if (this.playIndex < 0) return
this.stopPlay() this.stopPlay()
this.play() this.play()
@ -295,6 +317,7 @@ export default {
if (Math.abs(n - o) > 2) this.isActiveTransition = true if (Math.abs(n - o) > 2) this.isActiveTransition = true
this.savePlayInfo({ this.savePlayInfo({
time: n, time: n,
maxTime: this.maxPlayTime,
listId: this.listId, listId: this.listId,
list: this.listId == null ? this.list : null, list: this.listId == null ? this.list : null,
index: this.playIndex, index: this.playIndex,
@ -377,12 +400,7 @@ export default {
this.clearLoadingTimeout() this.clearLoadingTimeout()
this.status = this.statusText = this.$t('core.player.loading') this.status = this.statusText = this.$t('core.player.loading')
this.maxPlayTime = audio.duration this.maxPlayTime = audio.duration
if (window.restorePlayInfo) { if (this.restorePlayTime) {
audio.currentTime = window.restorePlayInfo.time
window.restorePlayInfo = null
audio.pause()
this.stopPlay()
} else if (this.restorePlayTime) {
audio.currentTime = this.restorePlayTime audio.currentTime = this.restorePlayTime
this.restorePlayTime = 0 this.restorePlayTime = 0
} }
@ -651,7 +669,13 @@ export default {
) )
}, },
togglePlay() { togglePlay() {
if (!audio.src) return if (!audio.src) {
if (this.restorePlayTime != null) {
if (!this.assertApiSupport(this.targetSong.source)) return this.handleNext()
this.setUrl(this.targetSong)
}
return
}
if (this.isPlay) { if (this.isPlay) {
audio.pause() audio.pause()
this.clearBufferTimeout() this.clearBufferTimeout()
@ -801,7 +825,7 @@ export default {
// console.log('start load timeout') // console.log('start load timeout')
this.loadingTimeout = setTimeout(() => { this.loadingTimeout = setTimeout(() => {
this.handleNext() this.handleNext()
}, 10000) }, 20000)
}, },
clearLoadingTimeout() { clearLoadingTimeout() {
if (!this.loadingTimeout) return if (!this.loadingTimeout) return
@ -810,7 +834,7 @@ export default {
this.loadingTimeout = null this.loadingTimeout = null
}, },
startBuffering() { startBuffering() {
console.error('start t') console.log('start t')
if (this.mediaBuffer.timeout) return if (this.mediaBuffer.timeout) return
this.mediaBuffer.timeout = setTimeout(() => { this.mediaBuffer.timeout = setTimeout(() => {
this.mediaBuffer.timeout = null this.mediaBuffer.timeout = null

View File

@ -4,6 +4,7 @@
"lists_rename": "Rename", "lists_rename": "Rename",
"lists_moveup": "Move Up", "lists_moveup": "Move Up",
"lists_movedown": "Move Down", "lists_movedown": "Move Down",
"lists_sync": "Sync",
"lists_remove": "Remove", "lists_remove": "Remove",
"list_play": "Play", "list_play": "Play",
"list_copy_name": "Copy name", "list_copy_name": "Copy name",

View File

@ -1,7 +1,7 @@
// http://kazupon.github.io/vue-i18n/en/messages.html // http://kazupon.github.io/vue-i18n/en/messages.html
const requireLang = require.context( const requireLang = require.context(
'@/lang', '@renderer/lang',
true, true,
/\.json$/, /\.json$/,
) )

View File

@ -4,6 +4,7 @@
"lists_rename": "重命名", "lists_rename": "重命名",
"lists_moveup": "上移", "lists_moveup": "上移",
"lists_movedown": "下移", "lists_movedown": "下移",
"lists_sync": "同步",
"lists_remove": "删除", "lists_remove": "删除",
"list_play": "播放", "list_play": "播放",
"list_copy_name": "复制歌曲名", "list_copy_name": "复制歌曲名",

View File

@ -4,6 +4,7 @@
"lists_rename": "重命名", "lists_rename": "重命名",
"lists_moveup": "上移", "lists_moveup": "上移",
"lists_movedown": "下移", "lists_movedown": "下移",
"lists_sync": "同步",
"lists_remove": "刪除", "lists_remove": "刪除",
"list_play": "播放", "list_play": "播放",
"list_copy_name": "複製歌曲名", "list_copy_name": "複製歌曲名",

View File

@ -17,7 +17,7 @@ import store from './store'
import '../common/error' import '../common/error'
import { getSetting } from './utils' import { getSetting } from './utils'
import languageList from '@/lang/languages.json' import languageList from '@renderer/lang/languages.json'
import { rendererSend, NAMES } from '../common/ipc' import { rendererSend, NAMES } from '../common/ipc'
sync(store, router) sync(store, router)

View File

@ -9,7 +9,7 @@
// Lib imports // Lib imports
import Vue from 'vue' import Vue from 'vue'
import VueI18n from 'vue-i18n' import VueI18n from 'vue-i18n'
import messages from '@/lang' import messages from '@renderer/lang'
Vue.use(VueI18n) Vue.use(VueI18n)

View File

@ -60,7 +60,7 @@ const mutations = {
allListInit(state.defaultList, state.loveList, state.userList) allListInit(state.defaultList, state.loveList, state.userList)
state.isInitedList = true state.isInitedList = true
}, },
setList(state, { id, list, name, location }) { setList(state, { id, list, name, location, source, sourceListId }) {
const targetList = allList[id] const targetList = allList[id]
if (targetList) { if (targetList) {
if (name && targetList.name === name) { if (name && targetList.name === name) {
@ -76,6 +76,8 @@ const mutations = {
id, id,
list, list,
location, location,
source,
sourceListId,
} }
state.userList.push(newList) state.userList.push(newList)
allListUpdate(newList) allListUpdate(newList)
@ -145,7 +147,7 @@ const mutations = {
if (!targetList) return if (!targetList) return
Object.assign(targetList.list[index], data) Object.assign(targetList.list[index], data)
}, },
createUserList(state, { name, id = `userlist_${Date.now()}`, list = [] }) { createUserList(state, { name, id = `userlist_${Date.now()}`, list = [], source, sourceListId }) {
let newList = state.userList.find(item => item.id === id) let newList = state.userList.find(item => item.id === id)
if (!newList) { if (!newList) {
newList = { newList = {
@ -153,6 +155,8 @@ const mutations = {
id, id,
list: [], list: [],
location: 0, location: 0,
source,
sourceListId,
} }
state.userList.push(newList) state.userList.push(newList)
allListUpdate(newList) allListUpdate(newList)

View File

@ -9,6 +9,15 @@ for (const source of music.sources) {
sources.push(source) sources.push(source)
} }
const filterList = list => {
const keys = new Set()
return list.filter(item => {
if (keys.has(item.songmid)) return false
keys.add(item.songmid)
return true
})
}
// state // state
const state = { const state = {
tags: {}, tags: {},
@ -78,11 +87,11 @@ const actions = {
return ( return (
cache.has(key) cache.has(key)
? Promise.resolve(cache.get(key)) ? Promise.resolve(cache.get(key))
: music[source].songList.getListDetail(id, page) : music[source].songList.getListDetail(id, page).then(result => ({ ...result, list: filterList(result.list) }))
).then(result => commit('setListDetail', { result, key, source, id, page })) ).then(result => commit('setListDetail', { result, key, source, id, page }))
}, },
getListDetailAll({ state, rootState }, id) { getListDetailAll({ state, rootState }, { source, id }) {
let source = rootState.setting.songList.source // console.log(source, id)
const loadData = (id, page) => { const loadData = (id, page) => {
let key = `sdetail__${source}__${id}__${page}` let key = `sdetail__${source}__${id}__${page}`
return cache.has(key) return cache.has(key)
@ -93,7 +102,7 @@ const actions = {
}) })
} }
return loadData(id, 1).then(result => { return loadData(id, 1).then(result => {
if (result.total <= result.limit) return result.list if (result.total <= result.limit) return filterList(result.list)
let maxPage = Math.ceil(result.total / result.limit) let maxPage = Math.ceil(result.total / result.limit)
const loadDetail = (loadPage = 1) => { const loadDetail = (loadPage = 1) => {
@ -101,7 +110,7 @@ const actions = {
? loadData(id, ++loadPage).then(result => result.list) ? loadData(id, ++loadPage).then(result => result.list)
: loadData(id, ++loadPage).then(result1 => loadDetail(loadPage).then(result2 => [...result1.list, ...result2])) : loadData(id, ++loadPage).then(result1 => loadDetail(loadPage).then(result2 => [...result1.list, ...result2]))
} }
return loadDetail().then(result2 => [...result.list, ...result2]) return loadDetail().then(result2 => [...result.list, ...result2]).then(list => filterList(list))
}) })
}, },
} }

View File

@ -80,7 +80,7 @@ const encodeNames = {
'&apos;': "'", '&apos;': "'",
'&#039;': "'", '&#039;': "'",
} }
export const decodeName = str => str.replace(/(?:&amp;|&lt;|&gt;|&quot;|&apos;|&#039;)/gm, s => encodeNames[s]) export const decodeName = (str = '') => str.replace(/(?:&amp;|&lt;|&gt;|&quot;|&apos;|&#039;)/gm, s => encodeNames[s])
const easeInOutQuad = (t, b, c, d) => { const easeInOutQuad = (t, b, c, d) => {
t /= d / 2 t /= d / 2

View File

@ -378,7 +378,7 @@ export default {
id = id.toString() id = id.toString()
if (id.includes('special/single/')) { if (id.includes('special/single/')) {
id = id.replace(this.regExps.listDetailLink, '$1') id = id.replace(this.regExps.listDetailLink, '$1')
} else if (/http(?:s):/.test(id)) { } else if (/https?:/.test(id)) {
return this.getUserListDetail(id.replace(/^.*http/, 'http'), page) return this.getUserListDetail(id.replace(/^.*http/, 'http'), page)
} else if (/^\d+$/.test(id)) { } else if (/^\d+$/.test(id)) {
return this.getUserListDetailByCode(id) return this.getUserListDetailByCode(id)

View File

@ -31,7 +31,7 @@ export default {
}, },
}) })
const { body, statusCode } = await _requestObj.promise const { body, statusCode } = await _requestObj.promise
console.log(body) // console.log(body)
if (statusCode != 200 || body.returnCode !== '000000') throw new Error('获取评论失败') if (statusCode != 200 || body.returnCode !== '000000') throw new Error('获取评论失败')
return { source: 'mg', comments: this.filterComment(body.data.items), total: body.data.itemTotal, page, limit, maxPage: Math.ceil(body.data.itemTotal / limit) || 1 } return { source: 'mg', comments: this.filterComment(body.data.items), total: body.data.itemTotal, page, limit, maxPage: Math.ceil(body.data.itemTotal / limit) || 1 }
}, },

View File

@ -11,7 +11,10 @@
span(:class="$style.listsLabel") {{defaultList.name}} span(:class="$style.listsLabel") {{defaultList.name}}
li(:class="[$style.listsItem, loveList.id == listId ? $style.active : null]" :tips="loveList.name" @click="handleListToggle(loveList.id)") li(:class="[$style.listsItem, loveList.id == listId ? $style.active : null]" :tips="loveList.name" @click="handleListToggle(loveList.id)")
span(:class="$style.listsLabel") {{loveList.name}} span(:class="$style.listsLabel") {{loveList.name}}
li.user-list(:class="[$style.listsItem, item.id == listId ? $style.active : null, listsData.rightClickItemIndex == index ? $style.clicked : null]" @contextmenu="handleListsItemRigthClick($event, index)" :tips="item.name" v-for="(item, index) in userList" :key="item.id") li.user-list(
:class="[$style.listsItem, item.id == listId ? $style.active : null, listsData.rightClickItemIndex == index ? $style.clicked : null, fetchingListStatus[item.id] ? $style.fetching : null]"
@contextmenu="handleListsItemRigthClick($event, index)"
:tips="item.name" v-for="(item, index) in userList" :key="item.id")
span(:class="$style.listsLabel" @click="handleListToggle(item.id, index + 2)") {{item.name}} span(:class="$style.listsLabel" @click="handleListToggle(item.id, index + 2)") {{item.name}}
input.key-bind(:class="$style.listsInput" @contextmenu.stop type="text" @keyup.enter="handleListsSave(index, $event)" @blur="handleListsSave(index, $event)" :value="item.name" :placeholder="item.name") input.key-bind(:class="$style.listsInput" @contextmenu.stop type="text" @keyup.enter="handleListsSave(index, $event)" @blur="handleListsSave(index, $event)" :value="item.name" :placeholder="item.name")
transition(enter-active-class="animated-fast slideInLeft" leave-active-class="animated-fast fadeOut" @after-leave="handleListsNewAfterLeave") transition(enter-active-class="animated-fast slideInLeft" leave-active-class="animated-fast fadeOut" @after-leave="handleListsNewAfterLeave")
@ -100,6 +103,7 @@ export default {
isShowItemMenu: false, isShowItemMenu: false,
itemMenuControl: { itemMenuControl: {
rename: true, rename: true,
sync: false,
moveup: true, moveup: true,
movedown: true, movedown: true,
remove: true, remove: true,
@ -131,6 +135,7 @@ export default {
isMove: false, isMove: false,
isMoveMultiple: false, isMoveMultiple: false,
isVisibleMusicSearch: false, isVisibleMusicSearch: false,
fetchingListStatus: {},
} }
}, },
computed: { computed: {
@ -180,6 +185,11 @@ export default {
action: 'rename', action: 'rename',
disabled: !this.listsData.itemMenuControl.rename, disabled: !this.listsData.itemMenuControl.rename,
}, },
{
name: this.$t('view.list.lists_sync'),
action: 'sync',
disabled: !this.listsData.itemMenuControl.sync,
},
{ {
name: this.$t('view.list.lists_moveup'), name: this.$t('view.list.lists_moveup'),
action: 'moveup', action: 'moveup',
@ -309,7 +319,18 @@ export default {
}, },
methods: { methods: {
...mapMutations(['setPrevSelectListId']), ...mapMutations(['setPrevSelectListId']),
...mapMutations('list', ['listRemove', 'listRemoveMultiple', 'setUserListName', 'createUserList', 'moveupUserList', 'movedownUserList', 'removeUserList', 'setListScroll']), ...mapMutations('list', [
'listRemove',
'listRemoveMultiple',
'setUserListName',
'createUserList',
'moveupUserList',
'movedownUserList',
'removeUserList',
'setListScroll',
'setList',
]),
...mapActions('songList', ['getListDetailAll']),
...mapActions('download', ['createDownload', 'createDownloadMultiple']), ...mapActions('download', ['createDownload', 'createDownloadMultiple']),
...mapMutations('player', { ...mapMutations('player', {
setPlayList: 'setList', setPlayList: 'setList',
@ -668,6 +689,8 @@ export default {
}).catch(_ => _) }).catch(_ => _)
}, },
handleListsItemRigthClick(event, index) { handleListsItemRigthClick(event, index) {
const source = this.userList[index].source
this.listsData.itemMenuControl.sync = !!source && !!musicSdk[source].songList
this.listsData.itemMenuControl.moveup = index > 0 this.listsData.itemMenuControl.moveup = index > 0
this.listsData.itemMenuControl.movedown = index < this.userList.length - 1 this.listsData.itemMenuControl.movedown = index < this.userList.length - 1
this.listsData.rightClickItemIndex = index this.listsData.rightClickItemIndex = index
@ -714,6 +737,9 @@ export default {
dom.querySelector('input').focus() dom.querySelector('input').focus()
}) })
break break
case 'sync':
this.handleSyncSourceList(index)
break
case 'moveup': case 'moveup':
this.moveupUserList(index) this.moveupUserList(index)
break break
@ -814,6 +840,27 @@ export default {
break break
} }
}, },
fetchList(id, source, sourceListId) {
if (this.fetchingListStatus[id] == null) {
this.$set(this.fetchingListStatus, id, true)
} else {
this.fetchingListStatus[id] = true
}
return this.getListDetailAll({ source, id: sourceListId }).catch(err => {
return Promise.reject(err)
}).finally(() => {
this.fetchingListStatus[id] = false
})
},
async handleSyncSourceList(index) {
const targetList = this.userList[index]
const list = await this.fetchList(targetList.id, targetList.source, targetList.sourceListId)
// console.log(targetList.list.length, list.length)
this.setList({
...targetList,
list,
})
},
}, },
} }
</script> </script>
@ -880,7 +927,7 @@ export default {
.listsItem { .listsItem {
position: relative; position: relative;
transition: .3s ease; transition: .3s ease;
transition-property: color, background-color; transition-property: color, background-color, opacity;
background-color: transparent; background-color: transparent;
&:hover:not(.active) { &:hover:not(.active) {
background-color: @color-theme_2-hover; background-color: @color-theme_2-hover;
@ -896,6 +943,9 @@ export default {
&.clicked { &.clicked {
background-color: @color-theme_2-hover; background-color: @color-theme_2-hover;
} }
&.fetching {
opacity: .5;
}
&.editing { &.editing {
padding: 0 10px; padding: 0 10px;
background-color: @color-theme_2-hover; background-color: @color-theme_2-hover;

View File

@ -296,11 +296,11 @@ import {
sizeFormate, sizeFormate,
setWindowSize, setWindowSize,
} from '../utils' } from '../utils'
import { rendererSend, rendererInvoke, NAMES } from '../../common/ipc' import { rendererSend, rendererInvoke, NAMES } from '@common/ipc'
import { mergeSetting, isMac } from '../../common/utils' import { mergeSetting, isMac } from '../../common/utils'
import apiSourceInfo from '../utils/music/api-source-info' import apiSourceInfo from '../utils/music/api-source-info'
import fs from 'fs' import fs from 'fs'
import languageList from '@/lang/languages.json' import languageList from '@renderer/lang/languages.json'
import { base as eventBaseName } from '../event/names' import { base as eventBaseName } from '../event/names'
import * as hotKeys from '../../common/hotKey' import * as hotKeys from '../../common/hotKey'
import { mainWindow as eventsNameMainWindow, winLyric as eventsNameWinLyric } from '../../main/events/_name' import { mainWindow as eventsNameMainWindow, winLyric as eventsNameWinLyric } from '../../main/events/_name'

View File

@ -373,6 +373,7 @@ export default {
} }
}, },
handleGetSongListDetail() { handleGetSongListDetail() {
if (!this.importSongListText.length) return
this.setSelectListInfo({ this.setSelectListInfo({
play_count: null, play_count: null,
id: this.importSongListText, id: this.importSongListText,
@ -404,14 +405,22 @@ export default {
}, },
async fetchList() { async fetchList() {
this.detailLoading = true this.detailLoading = true
const list = await this.getListDetailAll(this.selectListInfo.id) return this.getListDetailAll({ source: this.source, id: this.selectListInfo.id }).catch(err => {
return Promise.reject(err)
}).finally(() => {
this.detailLoading = false this.detailLoading = false
return list })
}, },
async addSongListDetail() { async addSongListDetail() {
if (!this.listDetail.info.name) return if (!this.listDetail.info.name) return
const list = await this.fetchList() const list = await this.fetchList()
this.createUserList({ name: this.listDetail.info.name, id: `${this.listDetail.source}__${this.listDetail.id}`, list }) this.createUserList({
name: this.listDetail.info.name,
id: `${this.listDetail.source}__${this.listDetail.id}`,
list,
source: this.listDetail.source,
sourceListId: this.listDetail.id,
})
}, },
async playSongListDetail() { async playSongListDetail() {
if (!this.listDetail.info.name) return if (!this.listDetail.info.name) return