Merge branch 'dev' into 黑色主题

pull/495/head
lyswhut 2020-12-24 17:22:30 +08:00
commit 3b00efdeae
50 changed files with 1184 additions and 513 deletions

View File

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

View File

@ -6,6 +6,50 @@ 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.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
### 修复
- 修复有歌词翻译与无歌词的音乐间切换会导致歌词翻译残留显示的问题
- 修复歌曲URL过期时等待刷新URL的自动切换歌曲时间间隔太短的问题
- 修复某些电脑上的某些歌曲没有声音的问题升级Electron9.3.4导致的现降级到9.3.3
## [1.4.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.3.0...v1.4.0) - 2020-11-21
### 新增
- 托盘菜单新增显示、隐藏主界面选项为Linux、MAC版添加托盘菜单
- 新增播放进度信息保存
### 优化
- 移除kg源的歌词文件开头的空白字符串
### 修复
- 修复专辑图片无法嵌入的问题
- 修复播放状态栏切换“上一首”歌曲按钮提示错误的问题
- 修复移动单首歌曲时,如果目标列表存在该歌曲,会导致将源列表与目标列表里的目标歌曲移除
- 修复kg源歌曲信息带有单引号等特殊字符被转义的问题
## [1.3.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.2.2...v1.3.0) - 2020-11-01
### 新增

36
FAQ.md
View File

@ -33,10 +33,18 @@
用`Shift`或`Ctrl`选择时,鼠标点击未选中的内容会将其选中,点击已选择的内容会将其取消选择,若想全部取消选择,在不按`Shift`或`Alt`键的情况下,随意点击列表里的一项内容即可全部取消选择。(P.S`Ctrl`键对应Mac OS上的`Command`键)
注:选完后可用鼠标右击弹出右键菜单操作已选的内容
## 播放整个歌单或排行榜
播放在线列表内的歌曲需要将它们都添加到我的列表才能播放,你可以全选列表内的歌曲然后添加到现有列表或者新创建的列表,然后去播放该列表内的歌曲。
## 无法打开外部歌单
不支持垮源打开歌单,请**确认**你需要打开的歌单平台是否与软件标签所写的**歌单源**对应(不一样的话请通过右上角切换歌单源);<br>
对于分享出来的歌单若打开失败可尝试先在浏览器中打开后再从浏览器地址栏复制URL地址到软件打开<br>
或者如果你知道歌单 id 也可以直接输入歌单 id 打开。<br>
## Windows 7 下界面异常(界面显示不完整)
由于软件默认使用了透明窗口根据Electron官方文档的[说明](https://electronjs.org/docs/api/frameless-window#%E5%B1%80%E9%99%90%E6%80%A7)
@ -68,12 +76,6 @@
- 清理安装路径下的残留文件
- 清理注册表(建议用清理工具清理)
## 无法打开外部歌单
不支持垮源打开歌单,请**确认**你需要打开的歌单平台是否与软件标签所写的**歌单源**对应(不一样的话请通过右上角切换歌单源);<br>
对于分享出来的歌单若打开失败可尝试先在浏览器中打开后再从浏览器地址栏复制URL地址到软件打开<br>
或者如果你知道歌单 id 也可以直接输入歌单 id 打开。<br>
## 软件无法联网
软件的排行榜、歌单、搜索列表**都**无法加载:
@ -96,10 +98,24 @@ Windows 7 未开启 Aero 效果时桌面歌词会有问题,详情看下面的
### Linux 系统下桌面歌词窗口异常
目前在 Ubuntu 18.10 下第一次开启桌面歌词时歌词窗口会变白,需要关闭后再开启,
`v1.2.1`以前的版本在 Ubuntu 18.10 下第一次开启桌面歌词时歌词窗口会变白,需要关闭后再开启,
`v1.2.1`及之后的版本已修复该问题。
其他 Linux 系统未测试,如有异常也是意料之中,目前不打算去处理 Linux 平台的桌面歌词问题。
## 歌曲下载失败
### 提示 `ENOENT: no such file or directory, mkdir`
更换下载歌曲目录即可解决(一般是设置的歌曲下载目录没有读写入权限导致的)。
### 提示 `请求异常``Fail`
尝试更换网络,如切换到移动网络。
### 其他错误
按照前面的 "歌曲无法试听与下载" 方案解决。
## 软件安装包说明
@ -123,6 +139,12 @@ Windows 7 未开启 Aero 效果时桌面歌词会有问题,详情看下面的
注意:**绿色版**的软件自动更新功能**不可用**,建议使用安装版!!<br>
注意:**Mac版**、**Linux deb**版不支持自动更新!
## 更新已收藏的在线歌单
该功能仅对直接从歌单详情页点“收藏”按钮收藏的歌单有效,可右击已收藏的列表名从弹出的菜单中选择“同步”使用该功能,
需要注意的是:这将会覆盖本地的目标列表,歌曲将被替换成最新的在线列表。
## 缺少`xxx.dll`
这个是电脑缺少某些dll导致的正常的系统是没有这个问题的可以尝试如下几个解决办法

View File

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

View File

@ -21,8 +21,11 @@ module.exports = {
},
resolve: {
alias: {
'@': path.join(__dirname, '../../src/renderer'),
common: path.join(__dirname, '../../src/common'),
'@main': path.join(__dirname, '../../src/main'),
'@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'],
},

View File

@ -21,8 +21,11 @@ module.exports = {
},
resolve: {
alias: {
'@': path.join(__dirname, '../../src/renderer'),
common: path.join(__dirname, '../../src/common'),
'@main': path.join(__dirname, '../../src/main'),
'@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'],
},

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/**/*"]
}

914
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,8 +1,8 @@
### 优化
### 新增
- 移除kg源的歌词文件开头的空白字符串
- 我的列表右键菜单新增列表排序功能,可调整单曲、多选后的歌曲的顺序。注意:多选排序还将会按照选中歌曲时的顺序排序
### 修复
- 修复专辑图片无法嵌入的问题
- 修复播放状态栏切换“上一首”歌曲按钮提示错误的问题
- 修复恢复上次播放的歌曲时在随机播放模式下不把恢复播放的歌曲放入已播放队列的问题(该问题会导致随机模式下会导致未播放完整个列表前就会再次随机到该歌曲,以及无法通过上一曲切回该歌曲)
- 修复音乐嵌入的封面在 Mac 系统无法显示的问题

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@ -17,6 +17,18 @@ class MainWindow extends EventEmitter {
toggleHide() {
this.emit(MAIN_WINDOW_EVENT_NAME.toggle_hide)
}
readyToShow() {
this.emit(MAIN_WINDOW_EVENT_NAME.ready_to_show)
}
show() {
this.emit(MAIN_WINDOW_EVENT_NAME.show)
}
hide() {
this.emit(MAIN_WINDOW_EVENT_NAME.hide)
}
}
module.exports = MainWindow

View File

@ -10,6 +10,9 @@ exports.mainWindow = {
quit: 'quit',
toggle_minimize: 'toggle_minimize',
toggle_hide: 'toggle_hide',
ready_to_show: 'ready_to_show',
show: 'show',
hide: 'hide',
}
exports.tray = {

View File

@ -1,6 +1,6 @@
const { app, Tray, Menu } = require('electron')
const { isWin } = require('../../common/utils')
const { tray: TRAY_EVENT_NAME, common: COMMON_EVENT_NAME } = require('../events/_name')
const { tray: TRAY_EVENT_NAME, common: COMMON_EVENT_NAME, mainWindow: MAIN_WINDOW_NAME } = require('../events/_name')
const path = require('path')
let isEnableTray = null
let themeId = null
@ -29,6 +29,16 @@ global.lx_event.common.on(COMMON_EVENT_NAME.config, sourceName => {
createMenu(global.modules.tray)
})
global.lx_event.mainWindow.on(MAIN_WINDOW_NAME.ready_to_show, () => {
createMenu(global.modules.tray)
})
global.lx_event.mainWindow.on(MAIN_WINDOW_NAME.show, () => {
createMenu(global.modules.tray)
})
global.lx_event.mainWindow.on(MAIN_WINDOW_NAME.hide, () => {
createMenu(global.modules.tray)
})
const createTray = () => {
if ((global.modules.tray && !global.modules.tray.isDestroyed()) || !global.appSetting.tray || !global.appSetting.tray.isShow) return
@ -58,8 +68,24 @@ const destroyTray = () => {
}
const createMenu = tray => {
if (!global.modules.tray || !isWin) return
if (!global.modules.tray) return
let menu = []
global.modules.mainWindow && menu.push(global.modules.mainWindow.isVisible() ? {
label: '隐藏主界面',
click() {
global.modules.mainWindow.hide()
},
} : {
label: '显示主界面',
click() {
if (!global.modules.mainWindow) return
if (!global.modules.mainWindow.isVisible()) {
global.modules.mainWindow.show()
}
global.modules.mainWindow.restore()
global.modules.mainWindow.focus()
},
})
menu.push(global.appSetting.desktopLyric.enable ? {
label: '关闭桌面歌词',
click() {

View File

@ -29,5 +29,13 @@ module.exports = mainWindow => {
mainWindow.once('ready-to-show', () => {
mainWindow.show()
global.lx_event.mainWindow.readyToShow()
})
mainWindow.on('show', () => {
global.lx_event.mainWindow.show()
})
mainWindow.on('hide', () => {
global.lx_event.mainWindow.hide()
})
}

View File

@ -279,7 +279,7 @@ export default {
rendererSend(NAMES.winLyric.close)
},
setLyric() {
window.lrc.setLyric((this.isShowLyricTransition && this.lyrics.tlyric ? this.lyrics.tlyric + '\n' : '') + this.lyrics.lyric)
window.lrc.setLyric((this.isShowLyricTransition && this.lyrics.tlyric ? (this.lyrics.tlyric + '\n') : '') + (this.lyrics.lyric || ''))
},
},
}

View File

@ -174,6 +174,9 @@ export default {
...mapMutations('search', {
setSearchHistoryList: 'setHistory',
}),
...mapMutations('player', {
setPlayList: 'setList',
}),
init() {
document.documentElement.style.fontSize = this.windowSizeActive.fontSize
@ -270,12 +273,27 @@ export default {
getPlayList().then(({ defaultList, loveList, userList, downloadList }) => {
if (!defaultList) defaultList = this.defaultList
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 (!loveList.list) loveList.list = []
this.initList({ defaultList, loveList, userList })
this.initDownloadList(downloadList) //
this.initPlayInfo()
})
},
initDownloadList(downloadList) {
@ -299,6 +317,29 @@ export default {
}
})
},
initPlayInfo() {
rendererInvoke(NAMES.mainWindow.get_data, 'playInfo').then(info => {
// console.log(info, window.allList)
window.restorePlayInfo = null
if (!info) return
if (info.index < 0) return
if (info.listId) {
const list = window.allList[info.listId]
// console.log(list)
if (!list) return
info.list = list.list
}
window.restorePlayInfo = info
this.setPlayList({
list: {
list: info.list,
id: info.listId,
},
index: info.index,
})
})
},
showUpdateModal() {
(this.version.newVersion && this.version.newVersion.history
? Promise.resolve(this.version.newVersion)

View File

@ -87,7 +87,7 @@ div(:class="$style.player")
<script>
import Lyric from 'lrc-file-parser'
import { rendererSend, rendererOn, NAMES } from '../../../common/ipc'
import { formatPlayTime2, getRandom, checkPath, setTitle, clipboardWriteText, debounce, assertApiSupport } from '../../utils'
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'
@ -130,7 +130,7 @@ export default {
line: 0,
},
delayNextTimeout: null,
audioErrorTime: 0,
restorePlayTime: 0,
retryNum: 0,
isMac,
volumeEvent: {
@ -195,6 +195,12 @@ export default {
this.handleSaveVolume = debounce(volume => {
this.setVolume(volume)
}, 300)
this.savePlayInfo = throttle(n => {
rendererSend(NAMES.mainWindow.save_data, {
path: 'playInfo',
data: n,
})
}, 2000)
rendererOn(NAMES.mainWindow.get_lyric_info, (event, info) => {
switch (info.action) {
@ -238,9 +244,32 @@ export default {
watch: {
changePlay(n) {
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)
window.restorePlayInfo = null
return
}
// console.log('changePlay')
this.handleRemoveMusic()
this.resetChangePlay()
if (this.playIndex < 0) return
this.stopPlay()
this.play()
@ -287,6 +316,13 @@ 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,
})
},
},
methods: {
@ -299,6 +335,7 @@ export default {
'clearPlayedList',
'setPlayedList',
'removePlayedList',
'setList',
]),
...mapMutations(['setVolume', 'setPlayNextMode', 'setVisibleDesktopLyric', 'setLockDesktopLyric']),
...mapMutations('list', ['updateMusicInfo']),
@ -348,7 +385,7 @@ export default {
this.stopPlay()
if (this.listId != 'download' && audio.error.code !== 1 && this.retryNum < 2) { // URL2URL
// console.log(this.retryNum)
if (!this.audioErrorTime) this.audioErrorTime = audio.currentTime //
if (!this.restorePlayTime) this.restorePlayTime = audio.currentTime //
this.retryNum++
this.setUrl(this.list[this.playIndex], true)
this.status = this.statusText = this.$t('core.player.refresh_url')
@ -360,15 +397,19 @@ export default {
this.addDelayNextTimeout()
})
audio.addEventListener('loadeddata', () => {
console.log('loadeddata')
this.clearLoadingTimeout()
this.status = this.statusText = this.$t('core.player.loading')
this.maxPlayTime = audio.duration
if (this.audioErrorTime) {
audio.currentTime = this.audioErrorTime
this.audioErrorTime = 0
if (this.restorePlayTime) {
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) } })
this.status = this.statusText = this.$t('core.player.loading')
})
audio.addEventListener('loadstart', () => {
console.log('loadstart')
this.startLoadingTimeout()
this.status = this.statusText = this.$t('core.player.loading')
})
audio.addEventListener('canplay', () => {
@ -378,11 +419,10 @@ export default {
this.mediaBuffer.playTime = 0
audio.currentTime = playTime
}
if (this.mediaBuffer.timeout) {
this.clearBufferTimeout()
}
// if (this.musicInfo.lrc) window.lrc.play(audio.currentTime * 1000)
this.status = this.statusText = this.$t('core.player.loading')
this.status = this.statusText = ''
})
// audio.addEventListener('canplaythrough', () => {
// console.log('')
@ -391,6 +431,7 @@ export default {
// })
audio.addEventListener('emptied', () => {
this.mediaBuffer.playTime = 0
this.clearLoadingTimeout()
this.clearBufferTimeout()
// console.log(' or ')
@ -432,7 +473,7 @@ export default {
let targetSong = this.targetSong = this.list[this.playIndex]
if (this.setting.player.togglePlayMethod == 'random') this.setPlayedList(targetSong)
this.retryNum = 0
this.audioErrorTime = 0
this.restorePlayTime = 0
if (this.listId == 'download') {
const filePath = path.join(this.setting.download.savePath, targetSong.fileName)
@ -613,7 +654,7 @@ export default {
setProgress(pregress) {
if (!audio.src) return
const time = pregress * this.maxPlayTime
if (this.audioErrorTime) this.audioErrorTime = time
if (this.restorePlayTime) this.restorePlayTime = time
if (this.mediaBuffer.playTime) {
this.clearBufferTimeout()
this.mediaBuffer.playTime = time
@ -629,7 +670,13 @@ export default {
)
},
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) {
audio.pause()
this.clearBufferTimeout()
@ -692,6 +739,7 @@ export default {
this.status = this.musicInfo.name = this.musicInfo.singer = ''
this.musicInfo.songmid = null
this.musicInfo.lrc = null
this.musicInfo.tlrc = null
this.musicInfo.url = null
this.nowPlayTime = 0
this.maxPlayTime = 0
@ -774,6 +822,18 @@ export default {
// console.log(e)
this.isActiveTransition = false
},
startLoadingTimeout() {
// console.log('start load timeout')
this.loadingTimeout = setTimeout(() => {
this.handleNext()
}, 20000)
},
clearLoadingTimeout() {
if (!this.loadingTimeout) return
// console.log('clear load timeout')
clearTimeout(this.loadingTimeout)
this.loadingTimeout = null
},
startBuffering() {
console.log('start t')
if (this.mediaBuffer.timeout) return
@ -870,7 +930,7 @@ export default {
})
},
setLyric() {
window.lrc.setLyric((this.setting.player.isShowLyricTransition && this.musicInfo.tlrc ? this.musicInfo.tlrc + '\n' : '') + this.musicInfo.lrc)
window.lrc.setLyric((this.setting.player.isShowLyricTransition && this.musicInfo.tlrc ? (this.musicInfo.tlrc + '\n') : '') + (this.musicInfo.lrc || ''))
if (this.isPlay && (this.musicInfo.url || this.listId == 'download')) {
window.lrc.play(audio.currentTime * 1000)
this.handleUpdateWinLyricInfo('play', audio.currentTime * 1000)

View File

@ -1,6 +1,6 @@
<template lang="pug">
input(:class="$style.input" :type="type" :placeholder="placeholder" :value="value" :disabled="disabled"
@focus="$emit('focus', $event)" @blur="$emit('blur', $event)" @input="$emit('input', $event.target.value.trim())" @change="$emit('change', $event.target.value.trim())"
input(:class="$style.input" ref="dom_input" :type="type" :placeholder="placeholder" :value="value" :disabled="disabled"
@focus="$emit('focus', $event)" @blur="$emit('blur', $event)" @input="handleInput" @change="$emit('change', $event.target.value.trim())"
@keyup.enter="$emit('submit', $event.target.value.trim())")
</template>
@ -24,6 +24,16 @@ export default {
default: 'text',
},
},
methods: {
handleInput(event) {
let value = event.target.value.trim()
event.target.value = value
this.$emit('input', value)
},
focus() {
this.$refs.dom_input.focus()
},
},
}
</script>
@ -41,6 +51,13 @@ export default {
transition: background-color 0.2s ease;
background-color: @color-btn-background;
font-size: 13.3px;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
&[disabled] {
opacity: .4;
}

View File

@ -0,0 +1,121 @@
<template lang="pug">
material-modal(:show="show" @close="handleClose")
main(:class="$style.main")
h2 {{selectedNum > 0 ? $t('material.list_sort_modal.title_multiple', { num: selectedNum }) : $t('material.list_sort_modal.title', { name: musicInfo ? musicInfo.name : '' })}}
material-input(:class="$style.input" type="number" v-model="sortNum" ref="input" @blur.native="verify" :placeholder="$t('material.list_sort_modal.input_tip')" @keydown.native.enter="handleSubmit")
div(:class="$style.footer")
material-btn(:class="$style.btn" @click="handleSubmit") {{$t('material.list_sort_modal.btn_confirm')}}
</template>
<script>
export default {
props: {
show: {
type: Boolean,
default: false,
},
musicInfo: {
type: Object,
default() {
return {}
},
},
selectedNum: {
type: Number,
default: 0,
},
},
data() {
return {
sortNum: '',
}
},
watch: {
show(n) {
if (n) {
this.sortNum = ''
this.$nextTick(() => {
this.$refs.input.focus()
})
}
},
},
computed: {
},
methods: {
handleClose() {
this.$emit('close')
},
verify() {
let num = /^[1-9]\d*/.exec(this.sortNum)
num = num ? parseInt(num[0]) : ''
this.sortNum = num.toString()
return num
},
handleSubmit() {
let num = this.verify()
if (this.sortNum == '') return
this.$emit('confirm', num)
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.main {
padding: 0 15px;
max-width: 530px;
min-width: 280px;
display: flex;
flex-flow: column nowrap;
min-height: 0;
// max-height: 100%;
// overflow: hidden;
h2 {
font-size: 13px;
color: @color-theme_2-font;
line-height: 1.3;
word-break: break-all;
// text-align: center;
padding: 15px 0 8px;
}
}
.input {
// width: 100%;
// height: 26px;
padding: 8px 8px;
}
.footer {
margin: 20px 0 15px auto;
}
.btn {
// box-sizing: border-box;
// margin-left: 15px;
// margin-bottom: 15px;
// height: 36px;
// line-height: 36px;
// padding: 0 10px !important;
min-width: 70px;
// .mixin-ellipsis-1;
+.btn {
margin-left: 10px;
}
}
each(@themes, {
:global(#container.@{value}) {
.main {
h2 {
color: ~'@{color-@{value}-theme_2-font}';
}
}
}
})
</style>

View File

@ -138,10 +138,10 @@ export default {
// will-change: transform;
li {
cursor: pointer;
min-width: 80px;
min-width: 90px;
line-height: 34px;
// color: @color-btn;
padding: 0 5px;
padding: 0 10px;
text-align: center;
outline: none;
transition: @transition-theme;

View File

@ -0,0 +1,6 @@
{
"title": "Adjust the position of {name} to:",
"title_multiple": "Adjust the position of the selected {num} songs to:",
"input_tip": "Please input which position you want to adjust to",
"btn_confirm": "Confirm"
}

View File

@ -4,11 +4,13 @@
"lists_rename": "Rename",
"lists_moveup": "Move Up",
"lists_movedown": "Move Down",
"lists_sync": "Sync",
"lists_remove": "Remove",
"list_play": "Play",
"list_copy_name": "Copy name",
"list_add_to": "Add to ...",
"list_move_to": "Move to ...",
"list_sort": "Adjust position",
"list_download": "Download",
"list_remove": "Remove",
"list_source_detail": "Song Page",

View File

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

View File

@ -0,0 +1,6 @@
{
"title": "将 {name} 的位置调整到:",
"title_multiple": "将已选的 {num} 首歌曲的位置调整到:",
"input_tip": "请输入要调整到第几个位置",
"btn_confirm": "确定"
}

View File

@ -4,12 +4,14 @@
"lists_rename": "重命名",
"lists_moveup": "上移",
"lists_movedown": "下移",
"lists_sync": "同步",
"lists_remove": "删除",
"list_play": "播放",
"list_copy_name": "复制歌曲名",
"list_source_detail": "歌曲详情页",
"list_add_to": "添加到...",
"list_move_to": "移动到...",
"list_sort": "调整位置",
"list_download": "下载",
"list_remove": "删除",
"default_list": "试听列表",

View File

@ -0,0 +1,6 @@
{
"title": "將 {name} 的位置調整到:",
"title_multiple": "將已選的 {num} 首歌曲的位置調整到:",
"input_tip": "請輸入要調整到第幾個位置",
"btn_confirm": "確定"
}

View File

@ -4,11 +4,13 @@
"lists_rename": "重命名",
"lists_moveup": "上移",
"lists_movedown": "下移",
"lists_sync": "同步",
"lists_remove": "刪除",
"list_play": "播放",
"list_copy_name": "複製歌曲名",
"list_add_to": "添加到...",
"list_move_to": "移動到...",
"list_sort": "調整位置",
"list_download": "下載",
"list_remove": "刪除",
"list_source_detail": "歌曲詳情頁",

View File

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

View File

@ -4,11 +4,14 @@ import { debounce } from '../../utils'
let instance
let prevTips
const getTips = el => el.getAttribute('tips')
const getTips = el =>
el
? el.getAttribute('tips')
? el.getAttribute('tips')
: el.parentNode === document.documentElement
? null
: getTips(el.parentNode)
: null
const showTips = debounce(event => {
let msg = getTips(event.target)

View File

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

View File

@ -60,7 +60,7 @@ const mutations = {
allListInit(state.defaultList, state.loveList, state.userList)
state.isInitedList = true
},
setList(state, { id, list, name, location }) {
setList(state, { id, list, name, location, source, sourceListId }) {
const targetList = allList[id]
if (targetList) {
if (name && targetList.name === name) {
@ -76,6 +76,8 @@ const mutations = {
id,
list,
location,
source,
sourceListId,
}
state.userList.push(newList)
allListUpdate(newList)
@ -92,8 +94,7 @@ const mutations = {
if (!fromList || !toList) return
fromList.list.splice(fromList.list.indexOf(musicInfo), 1)
let index = toList.list.findIndex(s => s.songmid === musicInfo.songmid)
if (index > -1) return toList.list.splice(index, 1)
toList.list.push(musicInfo)
if (index < 0) toList.list.push(musicInfo)
},
listAddMultiple(state, { id, list }) {
let targetList = allList[id]
@ -110,7 +111,7 @@ const mutations = {
},
// { fromId, toId, list }
listMoveMultiple(state, { fromId, toId, list }) {
console.log(state.commit)
// console.log(state.commit)
this.commit('list/listRemoveMultiple', { id: fromId, list })
this.commit('list/listAddMultiple', { id: toId, list })
},
@ -146,7 +147,7 @@ const mutations = {
if (!targetList) return
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)
if (!newList) {
newList = {
@ -154,6 +155,8 @@ const mutations = {
id,
list: [],
location: 0,
source,
sourceListId,
}
state.userList.push(newList)
allListUpdate(newList)
@ -182,6 +185,12 @@ const mutations = {
setListScroll(state, { id, location }) {
if (allList[id]) allList[id].location = location
},
sortList(state, { id, sortNum, musicInfos }) {
let targetList = allList[id]
this.commit('list/listRemoveMultiple', { id, list: musicInfos })
targetList.list.splice(sortNum - 1, 0, ...musicInfos)
},
}
export default {

View File

@ -9,6 +9,15 @@ for (const source of music.sources) {
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
const state = {
tags: {},
@ -78,11 +87,11 @@ const actions = {
return (
cache.has(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 }))
},
getListDetailAll({ state, rootState }, id) {
let source = rootState.setting.songList.source
getListDetailAll({ state, rootState }, { source, id }) {
// console.log(source, id)
const loadData = (id, page) => {
let key = `sdetail__${source}__${id}__${page}`
return cache.has(key)
@ -93,7 +102,7 @@ const actions = {
})
}
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)
const loadDetail = (loadPage = 1) => {
@ -101,7 +110,7 @@ const actions = {
? loadData(id, ++loadPage).then(result => result.list)
: 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

@ -78,8 +78,9 @@ const encodeNames = {
'&gt;': '>',
'&quot;': '"',
'&apos;': "'",
'&#039;': "'",
}
export const decodeName = str => str.replace(/(?:&amp;|&lt;|&gt;|&quot;|&apos;)/g, 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) => {
t /= d / 2

View File

@ -1,5 +1,5 @@
import { httpFetch } from '../../request'
import { dateFormat2 } from '../../'
import { decodeName, dateFormat2 } from '../../'
export default {
_requestObj: null,
@ -48,7 +48,7 @@ export default {
return rawList.map(item => {
let data = {
id: item.id,
text: item.content.split('\n'),
text: decodeName(item.content || '').split('\n'),
time: item.addtime,
timeStr: dateFormat2(new Date(item.addtime).getTime()),
userName: item.user_name,
@ -61,7 +61,7 @@ export default {
return item.pcontent ? {
id: item.id,
text: item.pcontent.split('\n'),
text: decodeName(item.pcontent).split('\n'),
time: null,
userName: item.puser,
avatar: null,

View File

@ -1,4 +1,5 @@
import { httpFetch } from '../../request'
import { decodeName } from '../..'
export default {
_requestObj: null,
@ -25,7 +26,7 @@ export default {
filterList(rawList) {
const list = []
rawList.forEach(item => {
item.keywords.map(k => list.push(k.keyword))
item.keywords.map(k => list.push(decodeName(k.keyword)))
})
return list
},

View File

@ -1,5 +1,5 @@
import { httpGet, cancelHttp, httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../../index'
import { decodeName, formatPlayTime, sizeFormate } from '../../index'
let boardList = [{ id: 'kg__8888', name: '酷狗TOP500', bangid: '8888' }, { id: 'kg__6666', name: '酷狗飙升榜', bangid: '6666' }, { id: 'kg__37361', name: '酷狗雷达榜', bangid: '37361' }, { id: 'kg__23784', name: '网络红歌榜', bangid: '23784' }, { id: 'kg__24971', name: 'DJ热歌榜', bangid: '24971' }, { id: 'kg__35811', name: '会员专享热歌榜', bangid: '35811' }, { id: 'kg__31308', name: '华语新歌榜', bangid: '31308' }, { id: 'kg__31310', name: '欧美新歌榜', bangid: '31310' }, { id: 'kg__31311', name: '韩国新歌榜', bangid: '31311' }, { id: 'kg__31312', name: '日本新歌榜', bangid: '31312' }, { id: 'kg__31313', name: '粤语新歌榜', bangid: '31313' }, { id: 'kg__33162', name: 'ACG新歌榜', bangid: '33162' }, { id: 'kg__21101', name: '酷狗分享榜', bangid: '21101' }, { id: 'kg__30972', name: '腾讯音乐人原创榜', bangid: '30972' }, { id: 'kg__22603', name: '5sing音乐榜', bangid: '22603' }, { id: 'kg__33160', name: '电音热歌榜', bangid: '33160' }, { id: 'kg__21335', name: '繁星音乐榜', bangid: '21335' }, { id: 'kg__33161', name: '古风新歌榜', bangid: '33161' }, { id: 'kg__33163', name: '影视金曲榜', bangid: '33163' }, { id: 'kg__33166', name: '欧美金曲榜', bangid: '33166' }, { id: 'kg__33165', name: '粤语金曲榜', bangid: '33165' }, { id: 'kg__36107', name: '小语种热歌榜', bangid: '36107' }, { id: 'kg__4681', name: '美国BillBoard榜', bangid: '4681' }, { id: 'kg__4680', name: '英国单曲榜', bangid: '4680' }, { id: 'kg__4673', name: '日本公信榜', bangid: '4673' }, { id: 'kg__38623', name: '韩国Melon音乐榜', bangid: '38623' }, { id: 'kg__42807', name: 'joox本地热歌榜', bangid: '42807' }, { id: 'kg__42808', name: '台湾KKBOX风云榜', bangid: '42808' }]
@ -134,9 +134,9 @@ export default {
}
}
return {
singer: item.singername,
name: item.songname,
albumName: item.album_name,
singer: decodeName(item.singername),
name: decodeName(item.songname),
albumName: decodeName(item.album_name),
albumId: item.album_id,
songmid: item.audio_id,
source: 'kg',

View File

@ -1,5 +1,6 @@
import { httpFetch } from '../../request'
import { decodeLyric } from './util'
import { decodeName } from '../..'
const parseLyric = str => {
str = str.replace(/(?:<\d+,\d+,\d+>|\r)/g, '')
@ -30,6 +31,8 @@ const parseLyric = str => {
return str.replace(result[1], time)
})
tlyric = tlyric ? tlyric.join('\n') : ''
lyric = decodeName(lyric)
tlyric = decodeName(tlyric)
return {
lyric,
tlyric,

View File

@ -1,7 +1,7 @@
// import '../../polyfill/array.find'
// import jshtmlencode from 'js-htmlencode'
import { httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../../index'
import { decodeName, formatPlayTime, sizeFormate } from '../../index'
// import { debug } from '../../utils/env'
// import { formatSinger } from './util'
@ -50,9 +50,9 @@ export default {
}
}
list.push({
singer: item.singername,
name: item.songname,
albumName: item.album_name,
singer: decodeName(item.singername),
name: decodeName(item.songname),
albumName: decodeName(item.album_name),
albumId: item.album_id,
songmid: item.audio_id,
source: 'kg',

View File

@ -1,5 +1,5 @@
import { httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../../index'
import { decodeName, formatPlayTime, sizeFormate } from '../../index'
import { toMD5 } from '../utils'
export default {
@ -378,7 +378,7 @@ export default {
id = id.toString()
if (id.includes('special/single/')) {
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)
} else if (/^\d+$/.test(id)) {
return this.getUserListDetailByCode(id)
@ -454,9 +454,9 @@ export default {
}
}
return {
singer: item.singername,
name: item.songname,
albumName: item.album_name,
singer: decodeName(item.singername),
name: decodeName(item.songname),
albumName: decodeName(item.album_name),
albumId: item.album_id,
songmid: item.audio_id,
source: 'kg',
@ -507,9 +507,9 @@ export default {
}
}
list.push({
singer: item.author_name,
name: item.ori_audio_name,
albumName: item.album_info.album_name,
singer: decodeName(item.author_name),
name: decodeName(item.ori_audio_name),
albumName: decodeName(item.album_info.album_name),
albumId: item.album_info.album_id,
songmid: item.audio_info.audio_id,
source: 'kg',

View File

@ -31,7 +31,7 @@ export default {
},
})
const { body, statusCode } = await _requestObj.promise
console.log(body)
// console.log(body)
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 }
},

View File

@ -11,7 +11,10 @@
span(:class="$style.listsLabel") {{defaultList.name}}
li(:class="[$style.listsItem, loveList.id == listId ? $style.active : null]" :tips="loveList.name" @click="handleListToggle(loveList.id)")
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}}
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")
@ -66,6 +69,7 @@
material-menu(:menus="listsItemMenu" :location="listsData.menuLocation" item-name="name" :isShow="listsData.isShowItemMenu" @menu-click="handleListsItemMenuClick")
material-menu(:menus="listItemMenu" :location="listMenu.menuLocation" item-name="name" :isShow="listMenu.isShowItemMenu" @menu-click="handleListItemMenuClick")
material-search-list(:list="list" @action="handleMusicSearchAction" :visible="isVisibleMusicSearch")
material-list-sort-modal(:show="isShowListSortModal" :music-info="musicInfo" :selected-num="selectdListDetailData.length" @close="isShowListSortModal = false" @confirm="handleSortMusicInfo")
</template>
<script>
@ -88,6 +92,7 @@ export default {
delayShow: false,
isShowListAdd: false,
isShowListAddMultiple: false,
isShowListSortModal: false,
delayTimeout: null,
isToggleList: true,
focusTarget: 'listDetail',
@ -100,6 +105,7 @@ export default {
isShowItemMenu: false,
itemMenuControl: {
rename: true,
sync: false,
moveup: true,
movedown: true,
remove: true,
@ -119,6 +125,7 @@ export default {
copyName: true,
addTo: true,
moveTo: true,
sort: true,
download: true,
remove: true,
sourceDetail: true,
@ -131,6 +138,7 @@ export default {
isMove: false,
isMoveMultiple: false,
isVisibleMusicSearch: false,
fetchingListStatus: {},
}
},
computed: {
@ -180,6 +188,11 @@ export default {
action: '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'),
action: 'moveup',
@ -219,6 +232,11 @@ export default {
action: 'sourceDetail',
disabled: !this.listMenu.itemMenuControl.sourceDetail,
},
{
name: this.$t('view.list.list_sort'),
action: 'sort',
disabled: !this.listMenu.itemMenuControl.sort,
},
{
name: this.$t('view.list.list_add_to'),
action: 'addTo',
@ -270,6 +288,12 @@ export default {
this.handleDelayShow()
})
})
this.isShowDownload = false
this.isShowDownloadMultiple = false
this.isShowListAdd = false
this.isShowListAddMultiple = false
this.isShowListSortModal = false
this.listMenu.isShowItemMenu = false
next()
},
// mounted() {
@ -309,7 +333,19 @@ export default {
},
methods: {
...mapMutations(['setPrevSelectListId']),
...mapMutations('list', ['listRemove', 'listRemoveMultiple', 'setUserListName', 'createUserList', 'moveupUserList', 'movedownUserList', 'removeUserList', 'setListScroll']),
...mapMutations('list', [
'listRemove',
'listRemoveMultiple',
'setUserListName',
'createUserList',
'moveupUserList',
'movedownUserList',
'removeUserList',
'setListScroll',
'setList',
'sortList',
]),
...mapActions('songList', ['getListDetailAll']),
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
...mapMutations('player', {
setPlayList: 'setList',
@ -668,6 +704,8 @@ export default {
}).catch(_ => _)
},
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.movedown = index < this.userList.length - 1
this.listsData.rightClickItemIndex = index
@ -714,6 +752,9 @@ export default {
dom.querySelector('input').focus()
})
break
case 'sync':
this.handleSyncSourceList(index)
break
case 'moveup':
this.moveupUserList(index)
break
@ -783,6 +824,20 @@ export default {
})
}
break
case 'sort':
this.isShowListSortModal = true
this.musicInfo = this.list[index]
// if (this.selectdListDetailData.length) {
// this.isShowDownloadMultiple = true
// } else {
// minfo = this.list[index]
// if (!this.assertApiSupport(minfo.source)) return
// this.musicInfo = minfo
// this.$nextTick(() => {
// this.isShowDownload = true
// })
// }
break
case 'remove':
if (this.selectdListDetailData.length) {
this.listRemoveMultiple({ id: this.listId, list: this.selectdListDetailData })
@ -814,6 +869,36 @@ export default {
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 }).finally(() => {
this.fetchingListStatus[id] = false
})
},
async handleSyncSourceList(index) {
const targetListInfo = this.userList[index]
const list = await this.fetchList(targetListInfo.id, targetListInfo.source, targetListInfo.sourceListId)
// console.log(targetListInfo.list.length, list.length)
this.removeAllSelectListDetail()
this.setList({
...targetListInfo,
list,
})
},
handleSortMusicInfo(num) {
num = Math.min(num, this.list.length)
this.sortList({
id: this.listId,
sortNum: num,
musicInfos: this.selectdListDetailData.length ? [...this.selectdListDetailData] : [this.musicInfo],
})
this.removeAllSelectListDetail()
this.isShowListSortModal = false
},
},
}
</script>
@ -880,7 +965,7 @@ export default {
.listsItem {
position: relative;
transition: .3s ease;
transition-property: color, background-color;
transition-property: color, background-color, opacity;
background-color: transparent;
&:hover:not(.active) {
background-color: @color-theme_2-hover;
@ -896,6 +981,9 @@ export default {
&.clicked {
background-color: @color-theme_2-hover;
}
&.fetching {
opacity: .5;
}
&.editing {
padding: 0 10px;
background-color: @color-theme_2-hover;

View File

@ -296,11 +296,11 @@ import {
sizeFormate,
setWindowSize,
} from '../utils'
import { rendererSend, rendererInvoke, NAMES } from '../../common/ipc'
import { rendererSend, rendererInvoke, NAMES } from '@common/ipc'
import { mergeSetting, isMac } from '../../common/utils'
import apiSourceInfo from '../utils/music/api-source-info'
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 * as hotKeys from '../../common/hotKey'
import { mainWindow as eventsNameMainWindow, winLyric as eventsNameWinLyric } from '../../main/events/_name'

View File

@ -373,6 +373,7 @@ export default {
}
},
handleGetSongListDetail() {
if (!this.importSongListText.length) return
this.setSelectListInfo({
play_count: null,
id: this.importSongListText,
@ -404,14 +405,20 @@ export default {
},
async fetchList() {
this.detailLoading = true
const list = await this.getListDetailAll(this.selectListInfo.id)
return this.getListDetailAll({ source: this.source, id: this.selectListInfo.id }).finally(() => {
this.detailLoading = false
return list
})
},
async addSongListDetail() {
if (!this.listDetail.info.name) return
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() {
if (!this.listDetail.info.name) return