Merge branch 'dev'
commit
53d4f08ee7
|
@ -2,7 +2,6 @@ module.exports = {
|
|||
upgrade: true,
|
||||
reject: [
|
||||
'electron',
|
||||
'electron-builder',
|
||||
'chalk',
|
||||
],
|
||||
// target: 'newest',
|
||||
|
|
32
CHANGELOG.md
32
CHANGELOG.md
|
@ -6,6 +6,38 @@ 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.18.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.17.1...v1.18.0) - 2022-02-26
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增“双击列表里的歌曲时自动切换到当前列表播放”设置,此功能仅对歌单、排行榜有效,默认关闭
|
||||
- 新增打开收藏的在线列表的对应平台详情页功能,可以在我的列表-列表右键菜单中使用
|
||||
- 新增定时暂停播放功能,由于此功能大多数人可能不常用,所以将其放在设置-基本设置中
|
||||
- 新增任务栏缩略图工具栏控制按钮(此功能仅在Windows平台可用),按钮分别为收藏/取消收藏(将歌曲添加到“我的收藏”列表)、上一曲、播放/暂停、下一曲
|
||||
- 新增设置-基本设置-软件字体设置,此设置可用于设置主界面的字体(已知的问题:Windows 7 下可能会出现字体列表为空的情况,这是当前系统的 Powershell 版本小于5.1导致的,请自行尝试看常见解决)
|
||||
- 新增Scheme URL对音乐搜索的调用支持,详情看常见问题-Scheme URL支持
|
||||
- 新增Scheme URL以url传参的方式调用,详情看常见问题-Scheme URL支持
|
||||
- 自定义源新增更新弹窗方法,同时自定义源管理新增是否允许源显示更新弹窗设置(出于防止滥用考虑),当源作者想要通知用户源已更新时,可以调用此方法弹窗告诉用户,调用说明看常见问题-自定义源部分
|
||||
|
||||
### 优化
|
||||
|
||||
- 过滤tx源某些不支持播放的歌曲,解决播放此类内容会导致意外的问题
|
||||
- 把歌曲的热门评论与最新评论拆分成两个列表显示
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复排行榜名字右击菜单的播放功能在播放非激活的列表时的列表获取问题
|
||||
- 修复修改列表名时无法使用`Ctrl`键的问题
|
||||
- 修复wy源某些歌曲获取歌词翻译的问题处理
|
||||
- 修复下载功能的歌词换源时会进入死循环的问题
|
||||
- 修复某些歌曲无法下载的问题
|
||||
- 修复windows平台下软件目录存在`portable`文件夹时,仍会创建`C:\Users\<user>\AppData\Roaming\lx-music-desktop\Dictionaries\en-US-9-0.bdic`文件的问题,现在不会再创建文件,但仍会创建空目录(Electron的问题,目前暂无解决方法)
|
||||
- 修复播放器的停止逻辑问题
|
||||
|
||||
### 其他
|
||||
|
||||
- 更新electron到v13.6.9
|
||||
|
||||
## [1.17.1](https://github.com/lyswhut/lx-music-desktop/compare/v1.17.0...v1.17.1) - 2022-01-28
|
||||
|
||||
### 优化
|
||||
|
|
33
FAQ.md
33
FAQ.md
|
@ -6,6 +6,15 @@
|
|||
|
||||
洛雪音乐的最初定位不是作为播放器开发的,它主要用于**查找歌曲**,软件的播放功能仅用于试听,不建议用作为常用播放器使用。
|
||||
|
||||
## LX Music中的音乐播放列表机制
|
||||
|
||||
1. 默认情况下,播放搜索列表、歌单列表、排行榜列表的歌曲时会自动将该歌曲添加到“我的列表”的试听列表后再播放,手动将歌曲添加到试听列表,再去试听列表找到这首歌点播放是等价的
|
||||
2. 如果你想要播放多首歌曲,需要使用多选功能(若不知道如何多选请看常见问题)多选后,将歌曲这些歌曲添加到“我的列表”播放,或使用稍后播放功能播放
|
||||
3. 第2条适用于搜索列表、歌单列表、排行榜列表、我的列表中的歌曲
|
||||
4. 对于歌单详情列表,除了可以使用第2条的方式播放外,你可以点击详情页上面的播放按钮临时播放当前歌单,或点击收藏将当前歌单收藏到“我的列表”后再去播放
|
||||
5. 对于排行榜详情列表,除了可以使用第2条的方式播放外,你可以在右击排行榜名字后弹出的菜单中,播放或收藏整个排行榜,这与第四条的歌单中的播放、与收藏按钮功能一致
|
||||
6. v1.18.0及之后新增了“双击列表里的歌曲时自动切换到当前列表播放”设置,默认关闭,此功能仅对歌单、排行榜有效
|
||||
|
||||
## 歌曲无法试听与下载
|
||||
|
||||
### 所有歌曲都提示 `请求异常😮,可以多试几次,若还是不行就换一首吧。。。`
|
||||
|
@ -101,6 +110,9 @@
|
|||
|
||||
需要注意的是:这将会覆盖本地的目标列表,歌曲将被替换成最新的在线列表。
|
||||
|
||||
## 调整我的列表的列表顺序
|
||||
|
||||
按住Ctrl键(Mac上对应Command键)的时候将进入“拖动模式”,此时可以拖动列表的位置来调整顺序。
|
||||
|
||||
## 同步功能的使用(实验性,首次使用前建议先备份一次列表)
|
||||
|
||||
|
@ -305,16 +317,34 @@ Windows 7 未开启 Aero 效果时桌面歌词会有问题,详情看上面的
|
|||
|
||||
- URL统一以`lxmusic://`开头
|
||||
- 此技术目前只支持 Windows、Mac系统
|
||||
- URL传参以经过URL编码的JSON数据传参,例:`lxmusic://music/play?data=xxxx`,其中`xxxx`为经过URL编码后的JSON数据
|
||||
- 若无特别说明,源的可用值为:`kw/kg/tx/wy/mg`
|
||||
- 若无特别说明,音质的可用值为:`128k/320k/flac/flac32bit`
|
||||
|
||||
目前支持两种传参方式:
|
||||
|
||||
- 通过`data`传参,以经过URL编码的JSON数据传参,例:`lxmusic://music/play?data=xxxx`,其中`xxxx`为经过URL编码后的JSON数据,支持复杂的参数调用
|
||||
- 通过`URL`传参,适用于简单传参的调用,不需要转成JSON格式,例:`lxmusic://music/search/xxxx`,但仍然需要对数据进行URL编码,只适应于简单参数调用(v1.18.0新增)
|
||||
|
||||
### `data`方式传参
|
||||
|
||||
以经过URL编码的JSON数据传参,例:`lxmusic://music/play?data=xxxx`,其中`xxxx`为经过URL编码后的JSON数据,JSON数据内容取决于下表的参数部分
|
||||
|
||||
| 描述 | URL | 参数
|
||||
| --- | --- | ---
|
||||
| 打开歌单 | `songlist/open` | `source<String>`(源,必须)<br>`id<String/Number>`(歌单ID,可选)<br>`url<String>`(歌单URL,可选)其中ID与URL必需传一个
|
||||
| 播放歌单 | `songlist/play` | `source<String>`(源,必须)<br>`id<String/Number>`(歌单ID,可选)<br>`url<String>`(歌单URL,可选)其中`id`与`url`必需传一个<br>`index<Number>`(播放第几首歌,可选,从0开始)
|
||||
| 搜索歌曲 | `music/search` | `keywords<String/Number>`(要搜索的内容,必须)<br>`source<String>`(源,可选)
|
||||
| 播放歌曲 | `music/play` | `name<String>`(歌曲名,必传)<br>`singer<String>`(艺术家名,必传)<br>`source<String>`(源,必传)<br>`songmid<String/Number>`(歌曲ID,必传)<br>`img<String>`(歌曲图片链接,选传)<br>`albumId<String/Number>`(歌曲专辑ID,选传)<br>`interval<String>`(格式化后的歌曲时长,选传,例:`03:55`)<br>`albumName<String>`(歌曲专辑名称,选传)<br>`types<Object>`(歌曲可用音质数组,必传,<br>数组格式:`[{"type": "<音质>", size: "<格式化后的文件大小,选传>", hash: "<kg源必传>"}]`,<br>例:`[{"type": "128k", size: "3.56M"}, {"type": "320k", size: null}]`)<br><br>以下为平台特定参数:<br>`hash<String>`(歌曲hash,kg源必传)<br>`strMediaMid<String>`(歌曲strMediaMid,tx源必传)<br>`albumMid<String>`(歌曲albumMid,tx源专用,选传)<br>`copyrightId<String>`(歌曲copyrightId,mg源必传)<br>`lrcUrl<String>`(歌曲lrcUrl,mg源专用,选传)
|
||||
|
||||
### `URL`方式传参
|
||||
|
||||
由于URL传参只适用于简单传参场景,所以目前只支持以下功能的调用:
|
||||
|
||||
| 描述 | URL | 参数
|
||||
| --- | --- | ---
|
||||
| 搜索歌曲 | `music/search/{source}/{keywords}` | `source`(源,可选)<br>`keywords`(要搜索的内容,必须)<br>例:`music/search/kw/xxx`、`music/search/xxx`
|
||||
| 打开歌单 | `songlist/open/{source}/{id/url}` | `source`(源,必须)<br>`id/url`(歌单ID或歌单URL,必须)<br>例:`songlist/open/kw/123456`
|
||||
|
||||
## 自定义源脚本编写说明
|
||||
|
||||
文件请使用UTF-8编码格式编写,脚本所用编程语言为JavaScript,可以使用ES6+语法,脚本与应用的交互是使用类似事件收发的方式进行,这是一个基本的脚本例子:
|
||||
|
@ -427,6 +457,7 @@ send(EVENT_NAMES.inited, {
|
|||
| --- | ---
|
||||
| `inited` | 脚本初始化完成后发送给应用的事件名,发送该事件时需要传入以下信息:`{status, sources, openDevTools}`<br>`status`:初始化结果(`true`成功,`false`失败)<br>`openDevTools`:是否打开DevTools,此选项可用于开发脚本时的调试<br>`sources`:支持的源信息对象,<br>`sources[kw/kg/tx/wy/mg].name`:源的名字(目前非必须)<br>`sources[kw/kg/tx/wy/mg].type`:源类型,目前固定值需为`music`<br>`sources[kw/kg/tx/wy/mg].actions`:支持的actions,由于目前只支持`musicUrl`,所以固定传`['musicUrl']`即可<br>`sources[kw/kg/tx/wy/mg].qualitys`:该源支持的音质列表,有效的值为`['128k', '320k', 'flac']`,该字段用于控制应用可用的音质类型
|
||||
| `request` | 应用API请求事件名,回调入参:`handler({ source, action, info})`,回调必须返回`Promise`对象<br>`source`:音乐源,可能的值取决于初始化时传入的`sources`对象的源key值<br>`info`:请求附加信息,内容根据`action`变化<br>`action`:请求操作类型,目前只有`musicUrl`,即获取音乐URL链接,需要在 Promise 返回歌曲 url,`info`的结构:`{type, musicInfo}`,`info.type`:音乐质量,可能的值有`128k` / `320k` / `flac`(取决于初始化时对应源传入的`qualitys`值中的一个),`info.musicInfo`:音乐信息对象,里面有音乐ID、名字等信息
|
||||
| `updateAlert` | 显示源更新弹窗,发送该事件时的参数:`{log, updateUrl}`<br>`log`:更新日志,必传,字符串类型,内容可以使用`\n`换行,最大长度1024,超过此长度后将被截取超出的部分<br>`updateUrl`:更新地址,用于引导用户去该地址更新源,选传,需为http协议的url地址,最大长度1024<br>此事件每次运行脚本只能调用一次(源版本v1.2.0新增)<br>例子:`lx.send(lx.EVENT_NAMES.updateAlert, { log: 'hello world', updateUrl: 'https://xxx.com' })`
|
||||
|
||||
|
||||
#### `window.lx.on`
|
||||
|
|
|
@ -48,7 +48,8 @@
|
|||
软件变化请查看:[更新日志](https://github.com/lyswhut/lx-music-desktop/blob/master/CHANGELOG.md)<br>
|
||||
软件下载请转到:[发布页面](https://github.com/lyswhut/lx-music-desktop/releases)<br>
|
||||
或者到网盘下载(网盘内有MAC、windows版):`https://www.lanzoui.com/b0bf2cfa/` 密码:`glqw`(若链接无法打开请百度:蓝奏云链接打不开)<br>
|
||||
使用常见问题请转至:[常见问题](https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md)
|
||||
使用常见问题请转至:[常见问题](https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md)<br>
|
||||
移动版项目地址:<https://github.com/lyswhut/lx-music-mobile>
|
||||
|
||||
#### Scheme URL支持
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
42
package.json
42
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "lx-music-desktop",
|
||||
"version": "1.17.1",
|
||||
"version": "1.18.0",
|
||||
"description": "一个免费的音乐查找助手",
|
||||
"main": "./dist/electron/main.js",
|
||||
"productName": "lx-music-desktop",
|
||||
|
@ -173,32 +173,32 @@
|
|||
},
|
||||
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.16.12",
|
||||
"@babel/eslint-parser": "^7.16.5",
|
||||
"@babel/core": "^7.17.5",
|
||||
"@babel/eslint-parser": "^7.17.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-transform-modules-umd": "^7.16.7",
|
||||
"@babel/plugin-transform-runtime": "^7.16.10",
|
||||
"@babel/plugin-transform-runtime": "^7.17.0",
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"babel-loader": "^8.2.3",
|
||||
"babel-preset-minify": "^0.5.1",
|
||||
"browserslist": "^4.19.1",
|
||||
"browserslist": "^4.19.3",
|
||||
"cfonts": "^2.10.0",
|
||||
"chalk": "^4.1.2",
|
||||
"changelog-parser": "^2.8.0",
|
||||
"copy-webpack-plugin": "^10.2.1",
|
||||
"core-js": "^3.20.3",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"core-js": "^3.21.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^6.5.1",
|
||||
"css-loader": "^6.6.0",
|
||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||
"del": "^6.0.0",
|
||||
"electron": "^13.6.8",
|
||||
"electron-builder": "^22.11.7",
|
||||
"electron": "^13.6.9",
|
||||
"electron-builder": "^22.14.13",
|
||||
"electron-debug": "^3.2.0",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-to-chromium": "^1.4.56",
|
||||
"eslint": "^8.7.0",
|
||||
"electron-to-chromium": "^1.4.73",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
"eslint-formatter-friendly": "^7.0.0",
|
||||
"eslint-plugin-html": "^6.2.0",
|
||||
|
@ -206,7 +206,7 @@
|
|||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-standard": "^4.1.0",
|
||||
"eslint-plugin-vue": "^8.4.0",
|
||||
"eslint-plugin-vue": "^8.5.0",
|
||||
"eslint-webpack-plugin": "^3.1.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
|
@ -215,7 +215,7 @@
|
|||
"markdown-it": "^12.3.2",
|
||||
"mini-css-extract-plugin": "^2.5.3",
|
||||
"node-loader": "^2.0.0",
|
||||
"postcss": "^8.4.5",
|
||||
"postcss": "^8.4.7",
|
||||
"postcss-loader": "^6.2.1",
|
||||
"postcss-pxtorem": "^6.0.0",
|
||||
"pug": "^3.0.2",
|
||||
|
@ -227,22 +227,22 @@
|
|||
"svg-sprite-loader": "^6.0.11",
|
||||
"svg-transform-loader": "^2.0.13",
|
||||
"svgo-loader": "^3.0.0",
|
||||
"terser-webpack-plugin": "^5.3.0",
|
||||
"terser-webpack-plugin": "^5.3.1",
|
||||
"url-loader": "^4.1.1",
|
||||
"vue-loader": "^17.0.0",
|
||||
"vue-template-compiler": "^2.6.14",
|
||||
"webpack": "^5.67.0",
|
||||
"webpack": "^5.69.1",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.7.3",
|
||||
"webpack-dev-server": "^4.7.4",
|
||||
"webpack-hot-middleware": "github:lyswhut/webpack-hot-middleware#329c4375134b89d39da23a56a94db651247c74a1",
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bufferutil": "^4.0.6",
|
||||
"crypto-js": "^4.1.1",
|
||||
"electron-log": "^4.4.5",
|
||||
"electron-log": "^4.4.6",
|
||||
"electron-store": "^8.0.1",
|
||||
"electron-updater": "^4.6.1",
|
||||
"electron-updater": "^4.6.5",
|
||||
"font-list": "github:lyswhut/node-font-list#4edbb1933b49a9bac1eedd63a31da16b487fe57d",
|
||||
"http-terminator": "^3.0.4",
|
||||
"iconv-lite": "^0.6.3",
|
||||
|
@ -256,8 +256,8 @@
|
|||
"socket.io": "^4.4.1",
|
||||
"sortablejs": "^1.14.0",
|
||||
"utf-8-validate": "^5.0.8",
|
||||
"vue": "^3.2.29",
|
||||
"vue-i18n": "^9.2.0-beta.29",
|
||||
"vue": "^3.2.31",
|
||||
"vue-i18n": "^9.2.0-beta.30",
|
||||
"vue-router": "^4.0.12",
|
||||
"vuex": "^4.0.2"
|
||||
}
|
||||
|
|
|
@ -1,12 +1,29 @@
|
|||
### 新增
|
||||
|
||||
- 新增“双击列表里的歌曲时自动切换到当前列表播放”设置,此功能仅对歌单、排行榜有效,默认关闭
|
||||
- 新增打开收藏的在线列表的对应平台详情页功能,可以在我的列表-列表右键菜单中使用
|
||||
- 新增定时暂停播放功能,由于此功能大多数人可能不常用,所以将其放在设置-基本设置中
|
||||
- 新增任务栏缩略图工具栏控制按钮(此功能仅在Windows平台可用),按钮分别为收藏/取消收藏(将歌曲添加到“我的收藏”列表)、上一曲、播放/暂停、下一曲
|
||||
- 新增设置-基本设置-软件字体设置,此设置可用于设置主界面的字体(已知的问题:Windows 7 下可能会出现字体列表为空的情况,这是当前系统的 Powershell 版本小于5.1导致的,请自行尝试看常见解决)
|
||||
- 新增Scheme URL对音乐搜索的调用支持,详情看常见问题-Scheme URL支持
|
||||
- 新增Scheme URL以url传参的方式调用,详情看常见问题-Scheme URL支持
|
||||
- 自定义源新增更新弹窗方法,同时自定义源管理新增是否允许源显示更新弹窗设置(出于防止滥用考虑),当源作者想要通知用户源已更新时,可以调用此方法弹窗告诉用户,调用说明看常见问题-自定义源部分
|
||||
|
||||
### 优化
|
||||
|
||||
- 优化kw源英文与翻译歌词的匹配
|
||||
- 过滤tx源某些不支持播放的歌曲,解决播放此类内容会导致意外的问题
|
||||
- 把歌曲的热门评论与最新评论拆分成两个列表显示
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复快捷键与默认按键行为冲突的问题,现在若将某些有默认行为的按键(如在列表中上、下箭头、Home、End等键可以使列表滚动)设置为快捷键时,将禁用其默认行为
|
||||
- 修复列表的聚焦问题,现在在列表中使用上、下箭头、空格等键滚动列表时不会导致滚动到一定距离后丢失焦点的问题
|
||||
- 修复排行榜名字右击菜单的播放功能在播放非激活的列表时的列表获取问题
|
||||
- 修复修改列表名时无法使用`Ctrl`键的问题
|
||||
- 修复wy源某些歌曲获取歌词翻译的问题处理
|
||||
- 修复下载功能的歌词换源时会进入死循环的问题
|
||||
- 修复某些歌曲无法下载的问题
|
||||
- 修复windows平台下软件目录存在`portable`文件夹时,仍会创建`C:\Users\<user>\AppData\Roaming\lx-music-desktop\Dictionaries\en-US-9-0.bdic`文件的问题,现在不会再创建文件,但仍会创建空目录(Electron的问题,目前暂无解决方法)
|
||||
- 修复播放器的停止逻辑问题
|
||||
|
||||
### 其他
|
||||
|
||||
- 更新electron到v13.6.8
|
||||
- 更新electron到v13.6.9
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,7 +2,7 @@ const path = require('path')
|
|||
const os = require('os')
|
||||
|
||||
const defaultSetting = {
|
||||
version: '1.0.48',
|
||||
version: '1.0.51',
|
||||
player: {
|
||||
togglePlayMethod: 'listLoop',
|
||||
highQuality: false,
|
||||
|
@ -16,6 +16,8 @@ const defaultSetting = {
|
|||
isPlayLxlrc: true,
|
||||
isSavePlayTime: false,
|
||||
audioVisualization: false,
|
||||
waitPlayEndStop: true,
|
||||
waitPlayEndStopTime: '',
|
||||
},
|
||||
desktopLyric: {
|
||||
enable: false,
|
||||
|
@ -36,6 +38,7 @@ const defaultSetting = {
|
|||
},
|
||||
},
|
||||
list: {
|
||||
isClickPlayList: false,
|
||||
isShowAlbumName: true,
|
||||
isShowSource: true,
|
||||
isSaveScrollLocation: true,
|
||||
|
@ -99,6 +102,7 @@ const defaultSetting = {
|
|||
sourceId: 'kw',
|
||||
apiSource: 'temp',
|
||||
sourceNameType: 'alias',
|
||||
font: '',
|
||||
isShowAnimation: true,
|
||||
randomAnimate: true,
|
||||
ignoreVersion: null,
|
||||
|
|
|
@ -11,6 +11,9 @@ const names = {
|
|||
clear_env_params_deeplink: 'clear_env_params_deeplink',
|
||||
wait: 'wait',
|
||||
wait_cancel: 'wait_cancel',
|
||||
interval: 'interval',
|
||||
interval_callback: 'interval_callback',
|
||||
interval_cancel: 'interval_cancel',
|
||||
open_dev_tools: 'open_dev_tools',
|
||||
|
||||
set_music_meta: 'set_music_meta',
|
||||
|
@ -66,6 +69,8 @@ const names = {
|
|||
request_user_api_cancel: 'request_user_api_cancel',
|
||||
get_user_api_status: 'get_user_api_status',
|
||||
user_api_status: 'user_api_status',
|
||||
user_api_show_update_alert: 'user_api_show_update_alert',
|
||||
user_api_set_allow_update_alert: 'user_api_set_allow_update_alert',
|
||||
|
||||
get_lyric: 'get_lyric',
|
||||
save_lyric: 'save_lyric',
|
||||
|
@ -81,6 +86,9 @@ const names = {
|
|||
sync_action_list: 'sync_action_list',
|
||||
sync_list: 'sync_list',
|
||||
|
||||
taskbar_set_thumbar_buttons: 'taskbar_set_thumbar_buttons',
|
||||
taskbar_set_thumbnail_clip: 'taskbar_set_thumbnail_clip',
|
||||
taskbar_on_thumbar_button_click: 'taskbar_on_thumbar_button_click',
|
||||
},
|
||||
winLyric: {
|
||||
close: 'close',
|
||||
|
|
|
@ -118,6 +118,7 @@
|
|||
"lists__remove_tip_button": "Yes, that's right",
|
||||
"lists__rename": "Rename",
|
||||
"lists__sort_list": "Sort songs",
|
||||
"lists__source_detail": "Playlist Page",
|
||||
"lists__sync": "Update",
|
||||
"load_list_file_error_detail": "We have helped you back up the old list file to {path}\nIt is stored in JSON format, you can try to repair and restore it manually\n\nError details: {detail}",
|
||||
"load_list_file_error_title": "Error loading playlist data",
|
||||
|
@ -139,9 +140,18 @@
|
|||
"my_list": "Your Library",
|
||||
"no_item": "Nothing's here...",
|
||||
"not_agree": "Not accept",
|
||||
"ok": "OK",
|
||||
"pagination__next": "Next page",
|
||||
"pagination__page": "Page {num}",
|
||||
"pagination__prev": "Previous page",
|
||||
"play_timeout": "Timed pause",
|
||||
"play_timeout_close": "Close",
|
||||
"play_timeout_confirm": "Confirm",
|
||||
"play_timeout_end": "Wait for the song to finish before pausing",
|
||||
"play_timeout_stop": "Cancel timer",
|
||||
"play_timeout_tip": "Pause after {time}",
|
||||
"play_timeout_unit": "minute",
|
||||
"play_timeout_update": "Update timing",
|
||||
"player__add_music_to": "Add the current song to...",
|
||||
"player__buffering": "Buffering...",
|
||||
"player__desktop_lyric_lock": "Right click to lock lyrics",
|
||||
|
@ -200,6 +210,7 @@
|
|||
"setting__basic_control_btn_position": "Control Button Position",
|
||||
"setting__basic_control_btn_position_left": "Left",
|
||||
"setting__basic_control_btn_position_right": "Right",
|
||||
"setting__basic_font": "Font",
|
||||
"setting__basic_lang": "Language",
|
||||
"setting__basic_lang_title": "The language displayed in the software",
|
||||
"setting__basic_show_animation": "Show switching animation",
|
||||
|
@ -284,6 +295,7 @@
|
|||
"setting__list_add_music_location_type": "Position when adding a song to the list",
|
||||
"setting__list_add_music_location_type_bottom": "Bottom",
|
||||
"setting__list_add_music_location_type_top": "Top",
|
||||
"setting__list_click_action": "Automatically switch to the current list when double-clicking a song in the list (only valid for playlists and rankings)",
|
||||
"setting__list_scroll": "Remember the position of the scroll bar of the playlist (only valid for my music classification)",
|
||||
"setting__list_source": "Show song source (only valid for my music category)",
|
||||
"setting__network": "Network",
|
||||
|
@ -320,6 +332,7 @@
|
|||
"setting__play_quality": "Play 320K quality songs first (if supported)",
|
||||
"setting__play_save_play_time": "Remember playback progress",
|
||||
"setting__play_task_bar": "Show playing progress on the taskbar",
|
||||
"setting__play_timeout": "Timed pause",
|
||||
"setting__search": "Search",
|
||||
"setting__search_focus_search_box": "Automatically focus the search box on startup",
|
||||
"setting__search_history": "Search history",
|
||||
|
@ -398,12 +411,16 @@
|
|||
"theme_purple": "Purple",
|
||||
"theme_red": "Red",
|
||||
"theme_yellow": "Yellow",
|
||||
"user_api__allow_show_update_alert": "Allow update popup to show",
|
||||
"user_api__btn_export": "Export",
|
||||
"user_api__btn_import": "Import",
|
||||
"user_api__btn_remove": "Remove",
|
||||
"user_api__import_file": "Select music API script file",
|
||||
"user_api__max_tip": "There can only be a maximum of 20 sources at the same time🤪\nIf you want to continue importing, please remove some old sources to make room",
|
||||
"user_api__noitem": "There is nothing here...😲",
|
||||
"user_api__note": "Tip: Although we have isolated the script's running environment as much as possible, importing scripts containing malicious behaviors may still affect your system. Please import them carefully.",
|
||||
"user_api__readme": "Source writing instructions: ",
|
||||
"user_api__title": "Custom Source Management"
|
||||
"user_api__title": "Custom Source Management",
|
||||
"user_api__update_alert": "Custom source [{name}] found new version:",
|
||||
"user_api__update_alert_open_url": "Open update address"
|
||||
}
|
||||
|
|
|
@ -118,6 +118,7 @@
|
|||
"lists__remove_tip_button": "是的 没错",
|
||||
"lists__rename": "重命名",
|
||||
"lists__sort_list": "排序歌曲",
|
||||
"lists__source_detail": "歌单详情页",
|
||||
"lists__sync": "更新",
|
||||
"load_list_file_error_detail": "我们已经帮你把旧的列表文件备份到{path}\n它以 JSON 格式存储,你可以尝试手动修复并恢复它\n\n错误详情:{detail}",
|
||||
"load_list_file_error_title": "播放列表数据加载错误(建议到GitHub或加群反馈)",
|
||||
|
@ -139,9 +140,18 @@
|
|||
"my_list": "我的列表",
|
||||
"no_item": "列表竟然是空的...",
|
||||
"not_agree": "不接受",
|
||||
"ok": "我知道了",
|
||||
"pagination__next": "下一页",
|
||||
"pagination__page": "第 {num} 页",
|
||||
"pagination__prev": "上一页",
|
||||
"play_timeout": "定时暂停",
|
||||
"play_timeout_close": "关闭",
|
||||
"play_timeout_confirm": "确认",
|
||||
"play_timeout_end": "等待歌曲播放完毕再暂停",
|
||||
"play_timeout_stop": "取消定时",
|
||||
"play_timeout_tip": "{time} 后暂停播放",
|
||||
"play_timeout_unit": "分钟",
|
||||
"play_timeout_update": "更新定时",
|
||||
"player__add_music_to": "添加当前歌曲到...",
|
||||
"player__buffering": "缓冲中...",
|
||||
"player__desktop_lyric_lock": "右击锁定歌词",
|
||||
|
@ -200,6 +210,7 @@
|
|||
"setting__basic_control_btn_position": "控制按钮位置",
|
||||
"setting__basic_control_btn_position_left": "左边",
|
||||
"setting__basic_control_btn_position_right": "右边",
|
||||
"setting__basic_font": "字体",
|
||||
"setting__basic_lang": "语言",
|
||||
"setting__basic_lang_title": "软件显示的语言",
|
||||
"setting__basic_show_animation": "显示切换动画",
|
||||
|
@ -284,6 +295,7 @@
|
|||
"setting__list_add_music_location_type": "添加歌曲到列表时的位置",
|
||||
"setting__list_add_music_location_type_bottom": "底部",
|
||||
"setting__list_add_music_location_type_top": "顶部",
|
||||
"setting__list_click_action": "双击列表里的歌曲时自动切换到当前列表播放(仅对歌单、排行榜有效)",
|
||||
"setting__list_scroll": "记住播放列表滚动条位置(仅对我的音乐分类有效)",
|
||||
"setting__list_source": "显示歌曲源(仅对我的音乐分类有效)",
|
||||
"setting__network": "网络设置",
|
||||
|
@ -320,6 +332,7 @@
|
|||
"setting__play_quality": "优先播放320K品质的歌曲(如果支持)",
|
||||
"setting__play_save_play_time": "记住播放进度",
|
||||
"setting__play_task_bar": "在任务栏上显示当前歌曲播放进度",
|
||||
"setting__play_timeout": "定时暂停",
|
||||
"setting__search": "搜索设置",
|
||||
"setting__search_focus_search_box": "启动时自动聚焦搜索框",
|
||||
"setting__search_history": "显示历史搜索记录",
|
||||
|
@ -398,12 +411,16 @@
|
|||
"theme_purple": "重斤球紫",
|
||||
"theme_red": "热情似火",
|
||||
"theme_yellow": "信口雌黄",
|
||||
"user_api__allow_show_update_alert": "允许显示更新弹窗",
|
||||
"user_api__btn_export": "导出",
|
||||
"user_api__btn_import": "导入",
|
||||
"user_api__btn_remove": "移除",
|
||||
"user_api__import_file": "选择音乐API脚本文件",
|
||||
"user_api__max_tip": "最多只能同时存在20个源哦🤪\n想要继续导入的话,请先移除一些旧的源腾出位置吧",
|
||||
"user_api__noitem": "这里竟然是空的 😲",
|
||||
"user_api__note": "提示:虽然我们已经尽可能地隔离了脚本的运行环境,但导入包含恶意行为的脚本仍可能会影响你的系统,请谨慎导入。",
|
||||
"user_api__readme": "源编写说明:",
|
||||
"user_api__title": "自定义源管理"
|
||||
"user_api__title": "自定义源管理",
|
||||
"user_api__update_alert": "自定义源 [{name}] 发现新版本:",
|
||||
"user_api__update_alert_open_url": "打开更新地址"
|
||||
}
|
||||
|
|
|
@ -118,6 +118,7 @@
|
|||
"lists__remove_tip_button": "是的 沒錯",
|
||||
"lists__rename": "重命名",
|
||||
"lists__sort_list": "排序歌曲",
|
||||
"lists__source_detail": "歌單詳情頁",
|
||||
"lists__sync": "更新",
|
||||
"load_list_file_error_detail": "我們已經幫你把舊的列表文件備份到{path}\n它以 JSON 格式存儲,你可以嘗試手動修復並恢復它\n\n錯誤詳情:{detail}",
|
||||
"load_list_file_error_title": "播放列表數據加載錯誤",
|
||||
|
@ -139,9 +140,18 @@
|
|||
"my_list": "我的列表",
|
||||
"no_item": "列表竟然是空的...",
|
||||
"not_agree": "不接受",
|
||||
"ok": "我知道了",
|
||||
"pagination__next": "下一頁",
|
||||
"pagination__page": "第 {num} 頁",
|
||||
"pagination__prev": "上一頁",
|
||||
"play_timeout": "定時暫停",
|
||||
"play_timeout_close": "關閉",
|
||||
"play_timeout_confirm": "確認",
|
||||
"play_timeout_end": "等待歌曲播放完畢再暫停",
|
||||
"play_timeout_stop": "取消定時",
|
||||
"play_timeout_tip": "{time} 後暫停播放",
|
||||
"play_timeout_unit": "分鐘",
|
||||
"play_timeout_update": "更新定時",
|
||||
"player__add_music_to": "添加當前歌曲到...",
|
||||
"player__album": "專輯名:",
|
||||
"player__buffering": "緩衝中...",
|
||||
|
@ -200,6 +210,7 @@
|
|||
"setting__basic_control_btn_position": "控制按鈕位置",
|
||||
"setting__basic_control_btn_position_left": "左邊",
|
||||
"setting__basic_control_btn_position_right": "右邊",
|
||||
"setting__basic_font": "字體",
|
||||
"setting__basic_lang": "語言",
|
||||
"setting__basic_lang_title": "軟件顯示的語言",
|
||||
"setting__basic_show_animation": "顯示切換動畫",
|
||||
|
@ -284,6 +295,7 @@
|
|||
"setting__list_add_music_location_type": "添加歌曲到列表時的位置",
|
||||
"setting__list_add_music_location_type_bottom": "底部",
|
||||
"setting__list_add_music_location_type_top": "頂部",
|
||||
"setting__list_click_action": "雙擊列表裡的歌曲時自動切換到當前列表播放(僅對歌單、排行榜有效)",
|
||||
"setting__list_scroll": "記住播放列表滾動條位置(僅對我的音樂分類有效)",
|
||||
"setting__list_source": "顯示歌曲源(僅對我的音樂分類有效)",
|
||||
"setting__network": "網絡設置",
|
||||
|
@ -320,6 +332,7 @@
|
|||
"setting__play_quality": "優先播放320K品質的歌曲(如果支持)",
|
||||
"setting__play_save_play_time": "記住播放進度",
|
||||
"setting__play_task_bar": "在任務欄上顯示當前歌曲播放進度",
|
||||
"setting__play_timeout": "定時暫停",
|
||||
"setting__search": "搜索設置",
|
||||
"setting__search_focus_search_box": "啟動時自動聚焦搜索框",
|
||||
"setting__search_history": "顯示歷史搜索記錄",
|
||||
|
@ -398,12 +411,16 @@
|
|||
"theme_purple": "重斤球紫",
|
||||
"theme_red": "熱情似火",
|
||||
"theme_yellow": "信口雌黃",
|
||||
"user_api__allow_show_update_alert": "允許顯示更新彈窗",
|
||||
"user_api__btn_export": "導出",
|
||||
"user_api__btn_import": "導入",
|
||||
"user_api__btn_remove": "移除",
|
||||
"user_api__import_file": "選擇音樂API腳本文件",
|
||||
"user_api__max_tip": "最多只能同時存在20個源哦🤪\n想要繼續導入的話,請先移除一些舊的源騰出位置吧",
|
||||
"user_api__noitem": "這裡竟然是空的 😲",
|
||||
"user_api__note": "提示:雖然我們已經盡可能地隔離了腳本的運行環境,但導入包含惡意行為的腳本仍可能會影響你的系統,請謹慎導入。",
|
||||
"user_api__readme": "源編寫說明:",
|
||||
"user_api__title": "自定義源管理"
|
||||
"user_api__title": "自定義源管理",
|
||||
"user_api__update_alert": "自定義源 [{name}] 發現新版本:",
|
||||
"user_api__update_alert_open_url": "打開更新地址"
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ const Tray = require('./Tray')
|
|||
const WinLyric = require('./WinLyric')
|
||||
const HotKey = require('./HotKey')
|
||||
|
||||
const { Event: TaskBar } = require('../modules/taskbar')
|
||||
const { Event: UserApi } = require('../modules/userApi')
|
||||
const { Event: Sync } = require('../modules/sync')
|
||||
|
||||
|
@ -15,5 +16,6 @@ if (!global.lx_event.tray) global.lx_event.tray = new Tray()
|
|||
if (!global.lx_event.winLyric) global.lx_event.winLyric = new WinLyric()
|
||||
if (!global.lx_event.hotKey) global.lx_event.hotKey = new HotKey()
|
||||
|
||||
if (!global.lx_event.taskbar) global.lx_event.taskbar = new TaskBar()
|
||||
if (!global.lx_event.userApi) global.lx_event.userApi = new UserApi()
|
||||
if (!global.lx_event.sync) global.lx_event.sync = new Sync()
|
||||
|
|
|
@ -129,6 +129,10 @@ app.on('web-contents-created', (event, contents) => {
|
|||
event.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
// disable create dictionary
|
||||
// https://github.com/lyswhut/lx-music-desktop/issues/773
|
||||
contents.session.setSpellCheckerDictionaryDownloadURL('http://0.0.0.0')
|
||||
})
|
||||
|
||||
|
||||
|
|
|
@ -2,4 +2,3 @@ require('./appMenu')
|
|||
require('./winLyric')
|
||||
require('./tray')
|
||||
require('./hotKey')
|
||||
require('./userApi')
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
const { EventEmitter } = require('events')
|
||||
const TASKBAR_EVENT_NAME = require('./name')
|
||||
|
||||
class TaskBar extends EventEmitter {
|
||||
thumbarButtonClick(type) {
|
||||
this.emit(TASKBAR_EVENT_NAME.thumbarButtonClick, type)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TaskBar
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
thumbarButtonClick: 'thumbarButtonClick',
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
const path = require('path')
|
||||
const Event = require('./event/event')
|
||||
const eventNames = require('./event/name')
|
||||
|
||||
exports.Event = Event
|
||||
exports.eventNames = eventNames
|
||||
|
||||
exports.setThumbnailClip = (region = { x: 0, y: 0, width: 0, height: 0 }) => {
|
||||
if (!global.modules.mainWindow) return
|
||||
return global.modules.mainWindow.setThumbnailClip(region)
|
||||
}
|
||||
|
||||
const getIconPath = name => {
|
||||
return path.join(global.__static, 'images/taskbar', name + '.png')
|
||||
}
|
||||
|
||||
const buttonsFlags = {
|
||||
empty: true,
|
||||
collect: false,
|
||||
play: false,
|
||||
next: true,
|
||||
prev: true,
|
||||
}
|
||||
const createButtons = ({ empty = false, collect = false, play = false, next = true, prev = true }) => {
|
||||
const buttons = [
|
||||
collect
|
||||
? {
|
||||
icon: getIconPath('collected'),
|
||||
click() {
|
||||
global.lx_event.taskbar.thumbarButtonClick('unCollect')
|
||||
},
|
||||
tooltip: '取消收藏',
|
||||
flags: ['nobackground'],
|
||||
}
|
||||
: {
|
||||
icon: getIconPath('collect'),
|
||||
click() {
|
||||
global.lx_event.taskbar.thumbarButtonClick('collect')
|
||||
},
|
||||
tooltip: '收藏',
|
||||
flags: ['nobackground'],
|
||||
},
|
||||
{
|
||||
icon: getIconPath('prev'),
|
||||
click() {
|
||||
global.lx_event.taskbar.thumbarButtonClick('prev')
|
||||
},
|
||||
tooltip: '上一曲',
|
||||
flags: prev ? ['nobackground'] : ['nobackground', 'disabled'],
|
||||
},
|
||||
play
|
||||
? {
|
||||
icon: getIconPath('pause'),
|
||||
click() {
|
||||
global.lx_event.taskbar.thumbarButtonClick('pause')
|
||||
},
|
||||
tooltip: '暂停',
|
||||
flags: ['nobackground'],
|
||||
}
|
||||
: {
|
||||
icon: getIconPath('play'),
|
||||
click() {
|
||||
global.lx_event.taskbar.thumbarButtonClick('play')
|
||||
},
|
||||
tooltip: '播放',
|
||||
flags: ['nobackground'],
|
||||
},
|
||||
{
|
||||
icon: getIconPath('next'),
|
||||
click() {
|
||||
global.lx_event.taskbar.thumbarButtonClick('next')
|
||||
},
|
||||
tooltip: '下一曲',
|
||||
flags: next ? ['nobackground'] : ['nobackground', 'disabled'],
|
||||
},
|
||||
]
|
||||
if (empty) {
|
||||
for (const button of buttons) {
|
||||
button.flags = ['nobackground', 'disabled']
|
||||
}
|
||||
}
|
||||
return buttons
|
||||
}
|
||||
exports.setThumbarButtons = ({ empty, collect, play, next, prev } = buttonsFlags) => {
|
||||
if (!global.modules.mainWindow) return
|
||||
buttonsFlags.empty = empty
|
||||
buttonsFlags.collect = collect
|
||||
buttonsFlags.play = play
|
||||
buttonsFlags.next = next
|
||||
buttonsFlags.prev = prev
|
||||
global.modules.mainWindow.setThumbarButtons(createButtons(buttonsFlags))
|
||||
}
|
|
@ -5,6 +5,10 @@ class UserApi extends EventEmitter {
|
|||
status(info) {
|
||||
this.emit(USER_API_EVENT_NAME.status, info)
|
||||
}
|
||||
|
||||
showUpdateAlert(info) {
|
||||
this.emit(USER_API_EVENT_NAME.showUpdateAlert, info)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserApi
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
module.exports = {
|
||||
status: 'status',
|
||||
showUpdateAlert: 'showUpdateAlert',
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const Event = require('./event/event')
|
||||
const eventNames = require('./event/name')
|
||||
const { closeWindow } = require('./main')
|
||||
const { getUserApis, importApi, removeApi } = require('./utils')
|
||||
const { request, cancelRequest, getStatus, loadApi } = require('./rendererEvent/rendererEvent')
|
||||
const { getUserApis, importApi, removeApi, setAllowShowUpdateAlert: saveAllowShowUpdateAlert } = require('./utils')
|
||||
const { request, cancelRequest, getStatus, loadApi, setAllowShowUpdateAlert } = require('./rendererEvent/rendererEvent')
|
||||
|
||||
// const { getApiList, importApi, removeApi, setApi, getStatus, request, eventNames }
|
||||
let userApiId
|
||||
|
@ -41,4 +41,7 @@ exports.setApi = async id => {
|
|||
await loadApi(id)
|
||||
}
|
||||
|
||||
|
||||
exports.setAllowShowUpdateAlert = (id, enable) => {
|
||||
saveAllowShowUpdateAlert(id, enable)
|
||||
setAllowShowUpdateAlert(id, enable)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ fs.readFile(path.join(dir, 'renderer/user-api.html'), 'utf8', (err, data) => {
|
|||
})
|
||||
|
||||
const denyEvents = [
|
||||
'new-window',
|
||||
'will-navigate',
|
||||
'will-redirect',
|
||||
'will-attach-webview',
|
||||
|
@ -49,11 +48,13 @@ exports.createWindow = async userApi => {
|
|||
contextIsolation: true,
|
||||
// worldSafeExecuteJavaScript: true,
|
||||
nodeIntegration: false,
|
||||
nodeIntegrationInWorker: false,
|
||||
|
||||
spellcheck: false,
|
||||
autoplayPolicy: 'document-user-activation-required',
|
||||
enableWebSQL: false,
|
||||
disableDialogs: true,
|
||||
nativeWindowOpen: false,
|
||||
webgl: false,
|
||||
images: false,
|
||||
|
||||
|
@ -70,6 +71,9 @@ exports.createWindow = async userApi => {
|
|||
// eslint-disable-next-line node/no-callback-literal
|
||||
callback(false)
|
||||
})
|
||||
global.modules.userApiWindow.webContents.setWindowOpenHandler(() => {
|
||||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
winEvent(global.modules.userApiWindow)
|
||||
|
||||
|
|
|
@ -3,14 +3,16 @@ const needle = require('needle')
|
|||
const { createCipheriv, publicEncrypt, constants, randomBytes, createHash } = require('crypto')
|
||||
const USER_API_RENDERER_EVENT_NAME = require('../rendererEvent/name')
|
||||
|
||||
const sendMessage = (action, status, data, message) => {
|
||||
ipcRenderer.send(action, { status, data, message })
|
||||
const sendMessage = (action, data, status, message) => {
|
||||
ipcRenderer.send(action, { data, status, message })
|
||||
}
|
||||
|
||||
let isInitedApi = false
|
||||
let isShowedUpdateAlert = false
|
||||
const EVENT_NAMES = {
|
||||
request: 'request',
|
||||
inited: 'inited',
|
||||
updateAlert: 'updateAlert',
|
||||
}
|
||||
const eventNames = Object.values(EVENT_NAMES)
|
||||
const events = {
|
||||
|
@ -35,7 +37,7 @@ const supportActions = {
|
|||
|
||||
const handleRequest = (context, { requestKey, data }) => {
|
||||
// console.log(data)
|
||||
if (!events.request) return sendMessage(USER_API_RENDERER_EVENT_NAME.response, false, { requestKey }, 'Request event is not defined')
|
||||
if (!events.request) return sendMessage(USER_API_RENDERER_EVENT_NAME.response, { requestKey }, false, 'Request event is not defined')
|
||||
try {
|
||||
events.request.call(context, { source: data.source, action: data.action, info: data.info }).then(response => {
|
||||
let sendData = {
|
||||
|
@ -53,12 +55,12 @@ const handleRequest = (context, { requestKey, data }) => {
|
|||
}
|
||||
break
|
||||
}
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.response, true, sendData)
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.response, sendData, true)
|
||||
}).catch(err => {
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.response, false, { requestKey }, err.message)
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.response, { requestKey }, false, err.message)
|
||||
})
|
||||
} catch (err) {
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.response, false, { requestKey }, err.message)
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.response, { requestKey }, false, err.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +82,7 @@ const handleRequest = (context, { requestKey, data }) => {
|
|||
*/
|
||||
const handleInit = (context, info) => {
|
||||
if (!info) {
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, 'Init failed')
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, null, false, 'Init failed')
|
||||
// sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, typeof info.message === 'string' ? info.message.substring(0, 100) : '')
|
||||
return
|
||||
}
|
||||
|
@ -88,7 +90,7 @@ const handleInit = (context, info) => {
|
|||
sendMessage(USER_API_RENDERER_EVENT_NAME.openDevTools)
|
||||
}
|
||||
if (!info.status) {
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, 'Init failed')
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, null, false, 'Init failed')
|
||||
// sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, typeof info.message === 'string' ? info.message.substring(0, 100) : '')
|
||||
return
|
||||
}
|
||||
|
@ -109,16 +111,28 @@ const handleInit = (context, info) => {
|
|||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, false, null, error.message)
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, null, false, error.message)
|
||||
return
|
||||
}
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, true, sourceInfo)
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.init, sourceInfo, true)
|
||||
|
||||
ipcRenderer.on(USER_API_RENDERER_EVENT_NAME.request, (event, data) => {
|
||||
handleRequest(context, data)
|
||||
})
|
||||
}
|
||||
|
||||
const handleShowUpdateAlert = (data, resolve, reject) => {
|
||||
if (!data || typeof data != 'object') return reject(new Error('parameter format error.'))
|
||||
if (!data.log || typeof data.log != 'string') return reject(new Error('log is required.'))
|
||||
if (data.updateUrl && !/^https?:\/\/[^\s$.?#].[^\s]*$/.test(data.updateUrl) && data.updateUrl.length > 1024) delete data.updateUrl
|
||||
if (data.log.length > 1024) data.log = data.log.substring(0, 1024) + '...'
|
||||
sendMessage(USER_API_RENDERER_EVENT_NAME.showUpdateAlert, {
|
||||
log: data.log,
|
||||
updateUrl: data.updateUrl,
|
||||
})
|
||||
resolve()
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('lx', {
|
||||
EVENT_NAMES,
|
||||
request(url, { method = 'get', timeout, headers, body, form, formData }, callback) {
|
||||
|
@ -165,12 +179,18 @@ contextBridge.exposeInMainWorld('lx', {
|
|||
if (!eventNames.includes(eventName)) return reject(new Error('The event is not supported: ' + eventName))
|
||||
switch (eventName) {
|
||||
case EVENT_NAMES.inited:
|
||||
if (isInitedApi) return
|
||||
if (isInitedApi) return reject(new Error('Script is inited'))
|
||||
isInitedApi = true
|
||||
handleInit(this, data)
|
||||
resolve()
|
||||
break
|
||||
case EVENT_NAMES.updateAlert:
|
||||
if (isShowedUpdateAlert) return reject(new Error('The update alert can only be called once.'))
|
||||
isShowedUpdateAlert = true
|
||||
handleShowUpdateAlert(data, resolve, reject)
|
||||
break
|
||||
default:
|
||||
resolve(new Error('Unknown event name: ' + eventName))
|
||||
reject(new Error('Unknown event name: ' + eventName))
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -180,7 +200,9 @@ contextBridge.exposeInMainWorld('lx', {
|
|||
case EVENT_NAMES.request:
|
||||
events.request = handler
|
||||
break
|
||||
default: return Promise.reject(new Error('The event is not supported: ' + eventName))
|
||||
}
|
||||
return Promise.resolve()
|
||||
},
|
||||
utils: {
|
||||
crypto: {
|
||||
|
@ -208,7 +230,7 @@ contextBridge.exposeInMainWorld('lx', {
|
|||
},
|
||||
},
|
||||
},
|
||||
version: '1.1.0',
|
||||
version: '1.2.0',
|
||||
// removeEvent(eventName, handler) {
|
||||
// if (!eventNames.includes(eventName)) return Promise.reject(new Error('The event is not supported: ' + eventName))
|
||||
// let handlers
|
||||
|
|
|
@ -3,6 +3,7 @@ const names = {
|
|||
request: '',
|
||||
response: '',
|
||||
openDevTools: '',
|
||||
showUpdateAlert: '',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ const { mainOn, mainSend } = require('@common/ipc')
|
|||
const USER_API_RENDERER_EVENT_NAME = require('../rendererEvent/name')
|
||||
const { createWindow } = require('../main')
|
||||
const { getUserApis } = require('../utils')
|
||||
const { openDevTools } = require('@main/utils')
|
||||
|
||||
let userApi
|
||||
let status = { status: true }
|
||||
|
@ -32,14 +33,22 @@ const handleResponse = (event, { status, data: { requestKey, result }, message }
|
|||
}
|
||||
const handleOpenDevTools = () => {
|
||||
if (global.modules.userApiWindow) {
|
||||
global.modules.userApiWindow.webContents.openDevTools({
|
||||
mode: 'undocked',
|
||||
})
|
||||
openDevTools(global.modules.userApiWindow.webContents)
|
||||
}
|
||||
}
|
||||
const handleShowUpdateAlert = (event, { data }) => {
|
||||
if (!userApi.allowShowUpdateAlert) return
|
||||
global.lx_event.userApi.showUpdateAlert({
|
||||
name: userApi.name,
|
||||
description: userApi.description,
|
||||
log: data.log,
|
||||
updateUrl: data.updateUrl,
|
||||
})
|
||||
}
|
||||
mainOn(USER_API_RENDERER_EVENT_NAME.init, handleInit)
|
||||
mainOn(USER_API_RENDERER_EVENT_NAME.response, handleResponse)
|
||||
mainOn(USER_API_RENDERER_EVENT_NAME.openDevTools, handleOpenDevTools)
|
||||
mainOn(USER_API_RENDERER_EVENT_NAME.showUpdateAlert, handleShowUpdateAlert)
|
||||
|
||||
exports.loadApi = async apiId => {
|
||||
if (!apiId) return global.lx_event.userApi.status(status = { status: false, message: 'api id is null' })
|
||||
|
@ -82,3 +91,8 @@ exports.request = ({ requestKey, data }) => new Promise((resolve, reject) => {
|
|||
})
|
||||
|
||||
exports.getStatus = () => status
|
||||
|
||||
exports.setAllowShowUpdateAlert = (id, enable) => {
|
||||
if (!userApi || userApi.id != id) return
|
||||
userApi.allowShowUpdateAlert = enable
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@ exports.getUserApis = () => {
|
|||
userApis = defaultUserApis
|
||||
electronStore_userApi.set('userApis', userApis)
|
||||
}
|
||||
for (const api of userApis) {
|
||||
if (api.allowShowUpdateAlert == null) api.allowShowUpdateAlert = false
|
||||
}
|
||||
return userApis
|
||||
}
|
||||
|
||||
|
@ -27,6 +30,7 @@ exports.importApi = script => {
|
|||
name,
|
||||
description,
|
||||
script,
|
||||
allowShowUpdateAlert: true,
|
||||
}
|
||||
userApis.push(apiInfo)
|
||||
getStore('userApi').set('userApis', userApis)
|
||||
|
@ -42,3 +46,10 @@ exports.removeApi = ids => {
|
|||
}
|
||||
getStore('userApi').set('userApis', userApis)
|
||||
}
|
||||
|
||||
exports.setAllowShowUpdateAlert = (id, enable) => {
|
||||
const targetApi = userApis.find(api => api.id == id)
|
||||
if (!targetApi) return
|
||||
targetApi.allowShowUpdateAlert = enable
|
||||
getStore('userApi').set('userApis', userApis)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
const { isWin } = require('@common/utils')
|
||||
// require('./request')
|
||||
// require('./appName')
|
||||
require('./progressBar')
|
||||
|
@ -23,6 +23,8 @@ require('./systemFonts')
|
|||
require('./wait')
|
||||
require('./openDevtools')
|
||||
|
||||
if (isWin) require('./taskbar')
|
||||
|
||||
// require('./kw_decodeLyric')
|
||||
|
||||
require('./userApi')
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
const { mainOn, NAMES: { mainWindow: ipcMainWindowNames } } = require('../../common/ipc')
|
||||
const { openDevTools } = require('@main/utils')
|
||||
|
||||
mainOn(ipcMainWindowNames.open_dev_tools, event => {
|
||||
if (global.modules.mainWindow) {
|
||||
if (global.modules.mainWindow.isDevToolsOpened()) {
|
||||
global.modules.mainWindow.webContents.closeDevTools()
|
||||
} else {
|
||||
global.modules.mainWindow.webContents.openDevTools({
|
||||
mode: 'undocked',
|
||||
})
|
||||
openDevTools(global.modules.mainWindow.webContents)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
const { mainSend, mainOn, NAMES: { mainWindow: ipcMainWindowNames }, mainHandle } = require('@common/ipc')
|
||||
const { setThumbnailClip, setThumbarButtons, eventNames } = require('../modules/taskbar')
|
||||
const { mainWindow: MAIN_WINDOW_EVENT_NAME } = require('@main/events/_name')
|
||||
|
||||
const handleThumbarButtonClick = action => {
|
||||
mainSend(global.modules.mainWindow, ipcMainWindowNames.taskbar_on_thumbar_button_click, action)
|
||||
}
|
||||
|
||||
global.lx_event.taskbar.on(eventNames.thumbarButtonClick, handleThumbarButtonClick)
|
||||
global.lx_event.mainWindow.on(MAIN_WINDOW_EVENT_NAME.show, setThumbarButtons)
|
||||
|
||||
mainHandle(ipcMainWindowNames.taskbar_set_thumbnail_clip, (event, clip) => {
|
||||
return setThumbnailClip(clip)
|
||||
})
|
||||
|
||||
mainOn(ipcMainWindowNames.taskbar_set_thumbar_buttons, (event, buttons) => {
|
||||
setThumbarButtons(buttons)
|
||||
})
|
|
@ -1,11 +1,15 @@
|
|||
const { mainSend, mainHandle, NAMES: { mainWindow: ipcMainWindowNames } } = require('@common/ipc')
|
||||
const { getApiList, importApi, removeApi, setApi, getStatus, request, cancelRequest, eventNames } = require('../modules/userApi')
|
||||
const { getApiList, importApi, removeApi, setApi, getStatus, request, cancelRequest, eventNames, setAllowShowUpdateAlert } = require('../modules/userApi')
|
||||
|
||||
const handleStatusChange = status => {
|
||||
mainSend(global.modules.mainWindow, ipcMainWindowNames.user_api_status, status)
|
||||
}
|
||||
const handleShowUpdateAlert = info => {
|
||||
mainSend(global.modules.mainWindow, ipcMainWindowNames.user_api_show_update_alert, info)
|
||||
}
|
||||
|
||||
global.lx_event.userApi.on(eventNames.status, handleStatusChange)
|
||||
global.lx_event.userApi.on(eventNames.showUpdateAlert, handleShowUpdateAlert)
|
||||
|
||||
mainHandle(ipcMainWindowNames.import_user_api, async(event, script) => {
|
||||
return importApi(script)
|
||||
|
@ -27,6 +31,10 @@ mainHandle(ipcMainWindowNames.get_user_api_status, event => {
|
|||
return getStatus()
|
||||
})
|
||||
|
||||
mainHandle(ipcMainWindowNames.user_api_set_allow_update_alert, (event, { id, enable }) => {
|
||||
return setAllowShowUpdateAlert(id, enable)
|
||||
})
|
||||
|
||||
mainHandle(ipcMainWindowNames.request_user_api, (event, musicInfo) => {
|
||||
return request(musicInfo)
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { mainOn, mainHandle, NAMES: { mainWindow: ipcMainWindowNames } } = require('@common/ipc')
|
||||
const { mainOn, mainHandle, mainSend, NAMES: { mainWindow: ipcMainWindowNames } } = require('@common/ipc')
|
||||
|
||||
const timeoutMap = new Map()
|
||||
|
||||
|
@ -23,3 +23,24 @@ mainOn(ipcMainWindowNames.wait_cancel, (event, id) => {
|
|||
clearTimeout(timeout.timeout)
|
||||
timeout.reject('cancelled')
|
||||
})
|
||||
|
||||
mainOn(ipcMainWindowNames.interval, (event, { time, id }) => {
|
||||
if (timeoutMap.has(id)) return
|
||||
const timeout = setInterval(() => {
|
||||
if (global.modules.mainWindow) mainSend(global.modules.mainWindow, ipcMainWindowNames.interval_callback, id)
|
||||
}, time)
|
||||
|
||||
timeoutMap.set(id, {
|
||||
timeout,
|
||||
type: 'interval',
|
||||
time,
|
||||
})
|
||||
})
|
||||
|
||||
mainOn(ipcMainWindowNames.interval_cancel, (event, id) => {
|
||||
if (!timeoutMap.has(id)) return
|
||||
const timeout = timeoutMap.get(id)
|
||||
timeoutMap.delete(id)
|
||||
if (timeout.type != 'interval') return
|
||||
clearInterval(timeout.timeout)
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const { isWin } = require('../../common/utils')
|
||||
const { openDevTools } = require('@main/utils')
|
||||
const { mainSend, NAMES: { mainWindow: ipcMainWindowNames } } = require('../../common/ipc')
|
||||
global.isQuitting = false
|
||||
global.isTrafficLightClose = false // 是否点击软件上的关闭按钮关闭
|
||||
|
@ -30,6 +31,7 @@ module.exports = mainWindow => {
|
|||
mainWindow.once('ready-to-show', () => {
|
||||
mainWindow.show()
|
||||
global.lx_event.mainWindow.readyToShow()
|
||||
if (global.envParams.cmdParams.debug) openDevTools(global.modules.mainWindow.webContents)
|
||||
})
|
||||
|
||||
mainWindow.on('show', () => {
|
||||
|
|
|
@ -41,3 +41,9 @@ exports.initSetting = (isShowErrorAlert = true) => {
|
|||
global.appSetting = info.setting
|
||||
global.appSettingVersion = info.version
|
||||
}
|
||||
|
||||
exports.openDevTools = webContents => {
|
||||
webContents.openDevTools({
|
||||
mode: 'undocked',
|
||||
})
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ article, aside, details, figcaption, figure,
|
|||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
html {
|
||||
font-family: "Microsoft YaHei", "Microsoft Jhenghei", "PingFang SC", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans GB", "Source Han Sans", "Noto Sans CJK Sc", sans-serif;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
|
@ -41,3 +44,6 @@ table {
|
|||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
button {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
|
|
@ -20,13 +20,20 @@ import useApp from '@renderer/core/useApp'
|
|||
export default {
|
||||
setup() {
|
||||
const theme = useRefGetter('theme')
|
||||
const font = useRefGetter('font')
|
||||
|
||||
const dom_root = document.getElementById('root')
|
||||
|
||||
watch(theme, (val) => {
|
||||
dom_root.className = val
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
watch(font, (val) => {
|
||||
document.documentElement.style.fontFamily = val
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
dom_root.className = theme.value
|
||||
|
||||
useApp()
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@ article, aside, details, figcaption, figure,
|
|||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
html {
|
||||
font-family: "Microsoft YaHei", "Microsoft Jhenghei", "PingFang SC", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans GB", "Source Han Sans", "Noto Sans CJK Sc", sans-serif;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
|
@ -41,3 +44,6 @@ table {
|
|||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
button {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
@color-player-pic-c2: darken(@color-theme_2, 30%);
|
||||
@color-player-progress: darken(@color-theme_2, 6%);
|
||||
@color-player-progress-bar1: darken(@color-theme_2, 12%);
|
||||
@color-player-progress-bar2: lighten(@color-theme, 12%);
|
||||
@color-player-progress-bar2: fadeout(lighten(@color-theme, 12%), 20%);
|
||||
@color-player-status-text: lighten(@color-theme_2-font, 32%);
|
||||
@color-player-detail-lyric: fadeout(@color-theme_2-font, 40%);
|
||||
@color-player-detail-lyric-active: darken(@color-theme, 2%);
|
||||
|
@ -114,7 +114,7 @@
|
|||
@color-green-player-pic-c2: darken(@color-green-theme_2, 30%);
|
||||
@color-green-player-progress: darken(@color-green-theme_2, 6%);
|
||||
@color-green-player-progress-bar1: darken(@color-green-theme_2, 12%);
|
||||
@color-green-player-progress-bar2: lighten(@color-green-theme, 12%);
|
||||
@color-green-player-progress-bar2: fadeout(lighten(@color-green-theme, 12%), 20%);
|
||||
@color-green-player-status-text: lighten(@color-green-theme_2-font, 32%);
|
||||
@color-green-player-detail-lyric: fadeout(@color-green-theme_2-font, 40%);
|
||||
@color-green-player-detail-lyric-active: darken(@color-green-theme, 2%);
|
||||
|
@ -174,7 +174,7 @@
|
|||
@color-yellow-player-pic-c2: darken(@color-yellow-theme_2, 30%);
|
||||
@color-yellow-player-progress: darken(@color-yellow-theme_2, 6%);
|
||||
@color-yellow-player-progress-bar1: darken(@color-yellow-theme_2, 12%);
|
||||
@color-yellow-player-progress-bar2: lighten(@color-yellow-theme, 2%);
|
||||
@color-yellow-player-progress-bar2: fadeout(lighten(@color-yellow-theme, 2%), 20%);
|
||||
@color-yellow-player-status-text: lighten(@color-yellow-theme_2-font, 32%);
|
||||
@color-yellow-player-detail-lyric: fadeout(@color-yellow-theme_2-font, 40%);
|
||||
@color-yellow-player-detail-lyric-active: darken(@color-yellow-theme, 7%);
|
||||
|
@ -233,7 +233,7 @@
|
|||
@color-orange-player-pic-c2: darken(@color-orange-theme_2, 30%);
|
||||
@color-orange-player-progress: darken(@color-orange-theme_2, 6%);
|
||||
@color-orange-player-progress-bar1: darken(@color-orange-theme_2, 12%);
|
||||
@color-orange-player-progress-bar2: lighten(@color-orange-theme, 12%);
|
||||
@color-orange-player-progress-bar2: fadeout(lighten(@color-orange-theme, 12%), 20%);
|
||||
@color-orange-player-status-text: lighten(@color-orange-theme_2-font, 32%);
|
||||
@color-orange-player-detail-lyric: fadeout(@color-orange-theme_2-font, 40%);
|
||||
@color-orange-player-detail-lyric-active: darken(@color-orange-theme, 7%);
|
||||
|
@ -292,7 +292,7 @@
|
|||
@color-blue-player-pic-c2: darken(@color-blue-theme_2, 30%);
|
||||
@color-blue-player-progress: darken(@color-blue-theme_2, 6%);
|
||||
@color-blue-player-progress-bar1: darken(@color-blue-theme_2, 12%);
|
||||
@color-blue-player-progress-bar2: lighten(@color-blue-theme, 12%);
|
||||
@color-blue-player-progress-bar2: fadeout(lighten(@color-blue-theme, 12%), 20%);
|
||||
@color-blue-player-status-text: lighten(@color-blue-theme_2-font, 32%);
|
||||
@color-blue-player-detail-lyric: fadeout(@color-blue-theme_2-font, 40%);
|
||||
@color-blue-player-detail-lyric-active: darken(@color-blue-theme, 2%);
|
||||
|
@ -351,7 +351,7 @@
|
|||
@color-red-player-pic-c2: darken(@color-red-theme_2, 30%);
|
||||
@color-red-player-progress: darken(@color-red-theme_2, 6%);
|
||||
@color-red-player-progress-bar1: darken(@color-red-theme_2, 12%);
|
||||
@color-red-player-progress-bar2: lighten(@color-red-theme, 12%);
|
||||
@color-red-player-progress-bar2: fadeout(lighten(@color-red-theme, 12%), 20%);
|
||||
@color-red-player-status-text: lighten(@color-red-theme_2-font, 32%);
|
||||
@color-red-player-detail-lyric: fadeout(@color-red-theme_2-font, 40%);
|
||||
@color-red-player-detail-lyric-active: lighten(@color-red-theme, 2%);
|
||||
|
@ -412,7 +412,7 @@
|
|||
@color-pink-player-pic-c2: darken(@color-pink-theme_2, 30%);
|
||||
@color-pink-player-progress: darken(@color-pink-theme_2, 6%);
|
||||
@color-pink-player-progress-bar1: darken(@color-pink-theme_2, 12%);
|
||||
@color-pink-player-progress-bar2: lighten(@color-pink-theme, 2%);
|
||||
@color-pink-player-progress-bar2: fadeout(lighten(@color-pink-theme, 2%), 20%);
|
||||
@color-pink-player-status-text: lighten(@color-pink-theme_2-font, 32%);
|
||||
@color-pink-player-detail-lyric: fadeout(@color-pink-theme_2-font, 40%);
|
||||
@color-pink-player-detail-lyric-active: darken(@color-pink-theme, 2%);
|
||||
|
@ -471,7 +471,7 @@
|
|||
@color-purple-player-pic-c2: darken(@color-purple-theme_2, 30%);
|
||||
@color-purple-player-progress: darken(@color-purple-theme_2, 6%);
|
||||
@color-purple-player-progress-bar1: darken(@color-purple-theme_2, 12%);
|
||||
@color-purple-player-progress-bar2: lighten(@color-purple-theme, 12%);
|
||||
@color-purple-player-progress-bar2: fadeout(lighten(@color-purple-theme, 12%), 20%);
|
||||
@color-purple-player-status-text: lighten(@color-purple-theme_2-font, 32%);
|
||||
@color-purple-player-detail-lyric: fadeout(@color-purple-theme_2-font, 40%);
|
||||
@color-purple-player-detail-lyric-active: darken(@color-purple-theme, 2%);
|
||||
|
@ -530,7 +530,7 @@
|
|||
@color-grey-player-pic-c2: darken(@color-grey-theme_2, 30%);
|
||||
@color-grey-player-progress: darken(@color-grey-theme_2, 6%);
|
||||
@color-grey-player-progress-bar1: darken(@color-grey-theme_2, 12%);
|
||||
@color-grey-player-progress-bar2: lighten(@color-grey-theme, 12%);
|
||||
@color-grey-player-progress-bar2: fadeout(lighten(@color-grey-theme, 12%), 20%);
|
||||
@color-grey-player-status-text: lighten(@color-grey-theme_2-font, 32%);
|
||||
@color-grey-player-detail-lyric: fadeout(@color-grey-theme_2-font, 55%);
|
||||
@color-grey-player-detail-lyric-active: darken(@color-grey-theme, 3%);
|
||||
|
@ -590,7 +590,7 @@
|
|||
@color-ming-player-pic-c2: darken(@color-ming-theme_2, 30%);
|
||||
@color-ming-player-progress: darken(@color-ming-theme_2, 6%);
|
||||
@color-ming-player-progress-bar1: darken(@color-ming-theme_2, 12%);
|
||||
@color-ming-player-progress-bar2: lighten(@color-ming-theme, 12%);
|
||||
@color-ming-player-progress-bar2: fadeout(lighten(@color-ming-theme, 12%), 20%);
|
||||
@color-ming-player-status-text: lighten(@color-ming-theme_2-font, 32%);
|
||||
@color-ming-player-detail-lyric: fadeout(@color-ming-theme_2-font, 50%);
|
||||
@color-ming-player-detail-lyric-active: lighten(@color-ming-theme, 2%);
|
||||
|
@ -652,7 +652,7 @@
|
|||
@color-blue2-player-pic-c2: darken(@color-blue2-theme_2, 30%);
|
||||
@color-blue2-player-progress: darken(@color-blue2-theme_2, 6%);
|
||||
@color-blue2-player-progress-bar1: darken(@color-blue2-theme_2, 12%);
|
||||
@color-blue2-player-progress-bar2: lighten(@color-blue2-theme, 12%);
|
||||
@color-blue2-player-progress-bar2: fadeout(lighten(@color-blue2-theme, 12%), 20%);
|
||||
@color-blue2-player-status-text: lighten(@color-blue2-theme_2-font, 32%);
|
||||
@color-blue2-player-detail-lyric: fadeout(@color-blue2-theme_2-font, 50%);
|
||||
@color-blue2-player-detail-lyric-active: darken(@color-blue2-theme, 2%);
|
||||
|
@ -712,7 +712,7 @@
|
|||
@color-black-player-pic-c2: lighten(@color-black-theme_2, 30%);
|
||||
@color-black-player-progress: lighten(@color-black-theme_2, 6%);
|
||||
@color-black-player-progress-bar1: lighten(@color-black-theme_2, 12%);
|
||||
@color-black-player-progress-bar2: lighten(@color-black-theme, 12%);
|
||||
@color-black-player-progress-bar2: fadeout(lighten(@color-black-theme, 12%), 20%);
|
||||
@color-black-player-status-text: darken(@color-black-theme_2-font, 20%);
|
||||
@color-black-player-detail-lyric: fadeout(darken(@color-black-theme_2-font, 30%), 10%);
|
||||
@color-black-player-detail-lyric-active: lighten(@color-black-theme, 50%);
|
||||
|
@ -772,7 +772,7 @@
|
|||
@color-mid_autumn-player-pic-c2: darken(@color-mid_autumn-theme_2, 30%);
|
||||
@color-mid_autumn-player-progress: darken(@color-mid_autumn-theme_2, 10%);
|
||||
@color-mid_autumn-player-progress-bar1: darken(@color-mid_autumn-theme_2, 12%);
|
||||
@color-mid_autumn-player-progress-bar2: lighten(@color-mid_autumn-theme, 12%);
|
||||
@color-mid_autumn-player-progress-bar2: fadeout(lighten(@color-mid_autumn-theme, 12%), 20%);
|
||||
@color-mid_autumn-player-status-text: lighten(@color-mid_autumn-theme_2-font, 32%);
|
||||
@color-mid_autumn-player-detail-lyric: fadeout(@color-mid_autumn-theme_2-font, 40%);
|
||||
@color-mid_autumn-player-detail-lyric-active: lighten(@color-mid_autumn-theme, 7%);
|
||||
|
@ -832,7 +832,7 @@
|
|||
@color-mid_autumn-player-pic-c2: darken(@color-mid_autumn-theme_2, 30%);
|
||||
@color-mid_autumn-player-progress: darken(@color-mid_autumn-theme_2, 10%);
|
||||
@color-mid_autumn-player-progress-bar1: darken(@color-mid_autumn-theme_2, 12%);
|
||||
@color-mid_autumn-player-progress-bar2: lighten(@color-mid_autumn-theme, 12%);
|
||||
@color-mid_autumn-player-progress-bar2: fadeout(lighten(@color-mid_autumn-theme, 12%), 20%);
|
||||
@color-mid_autumn-player-status-text: lighten(@color-mid_autumn-theme_2-font, 32%);
|
||||
@color-mid_autumn-player-detail-lyric: lighten(@color-mid_autumn-theme, 7%);
|
||||
@color-mid_autumn-player-detail-lyric-active: lighten(@color-mid_autumn-theme, 7%);
|
||||
|
@ -891,7 +891,7 @@
|
|||
@color-naruto-player-pic-c2: darken(@color-naruto-theme_2, 30%);
|
||||
@color-naruto-player-progress: darken(@color-naruto-theme_2, 10%);
|
||||
@color-naruto-player-progress-bar1: darken(@color-naruto-theme_2, 12%);
|
||||
@color-naruto-player-progress-bar2: lighten(@color-naruto-theme, 12%);
|
||||
@color-naruto-player-progress-bar2: fadeout(lighten(@color-naruto-theme, 12%), 20%);
|
||||
@color-naruto-player-status-text: lighten(@color-naruto-theme_2-font, 32%);
|
||||
@color-naruto-player-detail-lyric: fadeout(@color-naruto-theme_2-font, 36%);
|
||||
@color-naruto-player-detail-lyric-active: darken(@color-naruto-theme, 7%);
|
||||
|
@ -950,7 +950,7 @@
|
|||
@color-happy_new_year-player-pic-c2: darken(@color-happy_new_year-theme_2, 30%);
|
||||
@color-happy_new_year-player-progress: darken(@color-happy_new_year-theme_2, 10%);
|
||||
@color-happy_new_year-player-progress-bar1: darken(@color-happy_new_year-theme_2, 5%);
|
||||
@color-happy_new_year-player-progress-bar2: lighten(@color-happy_new_year-theme, 5%);
|
||||
@color-happy_new_year-player-progress-bar2: fadeout(lighten(@color-happy_new_year-theme, 5%), 20%);
|
||||
@color-happy_new_year-player-status-text: lighten(@color-happy_new_year-theme_2-font, 32%);
|
||||
@color-happy_new_year-player-detail-lyric: fadeout(@color-happy_new_year-theme_2-font, 36%);
|
||||
@color-happy_new_year-player-detail-lyric-active: darken(@color-happy_new_year-theme, 7%);
|
||||
|
|
|
@ -41,6 +41,7 @@ export default {
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'change'],
|
||||
data() {
|
||||
return {
|
||||
bool: false,
|
||||
|
|
|
@ -24,7 +24,7 @@ export default {
|
|||
default: false,
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
|
|
|
@ -125,14 +125,13 @@ export default {
|
|||
|
||||
.progress-bar2 {
|
||||
background-color: @color-player-progress-bar2;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||
opacity: 0.8;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.progress-bar3 {
|
||||
background-color: @color-player-progress-bar2;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||
opacity: 0.3;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.bar-transition {
|
||||
|
|
|
@ -10,12 +10,13 @@ div(:class="$style.player")
|
|||
div(:class="$style.title" @click="handleCopy(title)" :tips="title + $t('copy_tip')") {{title}}
|
||||
control-btns
|
||||
div(:class="$style.column2")
|
||||
common-progress-bar(:progress="progress" :handleTransitionEnd="handleTransitionEnd" :isActiveTransition="isActiveTransition")
|
||||
common-progress-bar(:progress="progress" :handleTransitionEnd="handleTransitionEnd" :isActiveTransition="isActiveTransition" v-if="!isShowPlayerDetail")
|
||||
div(:class="$style.column3")
|
||||
span(:class="$style.statusText") {{statusText}}
|
||||
span {{nowPlayTimeStr}}
|
||||
span(style="margin: 0 5px;") /
|
||||
span {{maxPlayTimeStr}}
|
||||
template(v-if="!isShowPlayerDetail")
|
||||
span(:class="$style.statusText") {{statusText}}
|
||||
span {{nowPlayTimeStr}}
|
||||
span(style="margin: 0 5px;") /
|
||||
span {{maxPlayTimeStr}}
|
||||
div(:class="$style.right")
|
||||
div(:class="$style.playBtn" @click='playPrev' :tips="$t('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')
|
||||
|
@ -48,7 +49,17 @@ import { player as eventPlayerNames } from '@renderer/event/names'
|
|||
import ControlBtns from './ControlBtns'
|
||||
import usePlayProgress from '@renderer/utils/compositions/usePlayProgress'
|
||||
// import { lyric } from '@renderer/core/share/lyric'
|
||||
import { statusText, musicInfo, setMusicInfo, setShowPlayerDetail, isPlay, musicInfoItem, playInfo, playMusicInfo } from '@renderer/core/share/player'
|
||||
import {
|
||||
statusText,
|
||||
musicInfo,
|
||||
setMusicInfo,
|
||||
isShowPlayerDetail,
|
||||
setShowPlayerDetail,
|
||||
isPlay,
|
||||
musicInfoItem,
|
||||
playInfo,
|
||||
playMusicInfo,
|
||||
} from '@renderer/core/share/player'
|
||||
|
||||
export default {
|
||||
name: 'CorePlayBar',
|
||||
|
@ -138,6 +149,7 @@ export default {
|
|||
playNext,
|
||||
playPrev,
|
||||
handleToMusicLocation,
|
||||
isShowPlayerDetail,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
<div :class="['right', $style.right]">
|
||||
<div :class="['lyric', $style.lyric, { [$style.draging]: isMsDown }]" @wheel="handleWheel" @mousedown="handleLyricMouseDown" ref="dom_lyric">
|
||||
<div :class="$style.lyricSpace"></div>
|
||||
<div :class="[$style.lyricText]" ref="dom_lyric_text"></div>
|
||||
<div ref="dom_lyric_text"></div>
|
||||
<div :class="$style.lyricSpace"></div>
|
||||
</div>
|
||||
<transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut">
|
||||
<div :class="[$style.lyricSelectContent, 'select', 'scroll']" v-if="isShowLrcSelectContent" @contextmenu="handleCopySelectText">
|
||||
<div :class="[$style.lyricSelectContent, 'select', 'scroll', 'lyricSelectContent']" v-if="isShowLrcSelectContent" @contextmenu="handleCopySelectText">
|
||||
<div v-for="(info, index) in lyric.lines" :key="index" :class="[$style.lyricSelectline, { [$style.lrcActive]: lyric.line == index }]">
|
||||
<span>{{info.text}}</span>
|
||||
<br v-if="info.translation"/>
|
||||
|
|
|
@ -6,11 +6,10 @@ div(:class="$style.footer")
|
|||
div(:class="$style.progressContent")
|
||||
common-progress-bar(:class-name="$style.progress" :progress="progress" :handleTransitionEnd="handleTransitionEnd" :isActiveTransition="isActiveTransition")
|
||||
div(:class="$style.timeLabel")
|
||||
span(style="margin-left: 15px") {{status}}
|
||||
div
|
||||
span {{nowPlayTimeStr}}
|
||||
span(style="margin: 0 5px;") /
|
||||
span {{maxPlayTimeStr}}
|
||||
span(:class="$style.status" style="margin-left: 15px") {{status}}
|
||||
span {{nowPlayTimeStr}}
|
||||
span(style="margin: 0 5px;") /
|
||||
span {{maxPlayTimeStr}}
|
||||
div(:class="$style.playControl")
|
||||
div(:class="$style.playBtn" @click="playPrev" style="transform: rotate(180deg);" :tips="$t('player__prev')")
|
||||
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')
|
||||
|
@ -91,7 +90,7 @@ export default {
|
|||
|
||||
.progress-content {
|
||||
position: relative;
|
||||
height: 15px;
|
||||
height: 16px;
|
||||
padding: 5px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -108,11 +107,13 @@ export default {
|
|||
width: 100%;
|
||||
height: 18px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
span {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
.status {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.play-control {
|
||||
flex: none;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template lang="pug">
|
||||
div.comment(:class="$style.comment")
|
||||
div.comment(:class="$style.comment" ref="dom_container")
|
||||
div(:class="$style.commentHeader")
|
||||
h3 {{$t('comment__title', { name: title })}}
|
||||
div(:class="$style.commentHeaderBtns")
|
||||
|
@ -10,21 +10,27 @@ div.comment(:class="$style.comment")
|
|||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-close')
|
||||
|
||||
div.scroll(:class="$style.commentMain" ref="dom_comment")
|
||||
div(v-if="page == 1")
|
||||
h2(:class="$style.commentType") {{$t('comment__hot_title')}}
|
||||
p(:class="$style.commentLabel" style="cursor: pointer;" v-if="isHotLoadError" @click="handleGetHotComment(currentMusicInfo)") {{$t('comment__hot_load_error')}}
|
||||
p(:class="$style.commentLabel" v-else-if="isHotLoading && !hotComments.length") {{$t('comment__hot_loading')}}
|
||||
comment-floor(v-if="!isHotLoadError && hotComments.length" :class="[$style.commentFloor, isHotLoading ? $style.loading : null]" :comments="hotComments")
|
||||
p(:class="$style.commentLabel" v-else-if="!isHotLoadError && !isHotLoading") {{$t('comment__no_content')}}
|
||||
div
|
||||
h2(:class="$style.commentType") {{$t('comment__new_title')}} ({{total}})
|
||||
p(:class="$style.commentLabel" style="cursor: pointer;" v-if="isNewLoadError" @click="handleGetNewComment(currentMusicInfo, nextPage, limit)") {{$t('comment__new_load_error')}}
|
||||
p(:class="$style.commentLabel" v-else-if="isNewLoading && !newComments.length") {{$t('comment__new_loading')}}
|
||||
comment-floor(v-if="!isNewLoadError && newComments.length" :class="[$style.commentFloor, isNewLoading ? $style.loading : null]" :comments="newComments")
|
||||
p(:class="$style.commentLabel" v-else-if="!isNewLoadError && !isNewLoading") {{$t('comment__no_content')}}
|
||||
div(:class="$style.pagination")
|
||||
material-pagination(:count="total" :btnLength="5" :limit="limit" :page="page" @btn-click="handleToggleCommentPage")
|
||||
div(:class="$style.commentMain")
|
||||
header(:class="$style.tab_header")
|
||||
button(type="button" @click="handleToggleTab('hot')" :class="[$style.commentType, { [$style.active]: tabActiveId == 'hot' }]") {{$t('comment__hot_title')}} ({{hotComment.total}})
|
||||
button(type="button" @click="handleToggleTab('new')" :class="[$style.commentType, { [$style.active]: tabActiveId == 'new' }]") {{$t('comment__new_title')}} ({{newComment.total}})
|
||||
main(:class="$style.tab_main" ref="dom_tabMain")
|
||||
div(:class="$style.tab_content")
|
||||
div.scroll(:class="$style.tab_content_scroll" ref="dom_commentHot")
|
||||
p(:class="$style.commentLabel" style="cursor: pointer;" v-if="hotComment.isLoadError" @click="handleGetHotComment(currentMusicInfo, hotComment.nextPage, hotComment.limit)") {{$t('comment__hot_load_error')}}
|
||||
p(:class="$style.commentLabel" v-else-if="hotComment.isLoading && !hotComment.list.length") {{$t('comment__hot_loading')}}
|
||||
comment-floor(v-if="!hotComment.isLoadError && hotComment.list.length" :class="[$style.commentFloor, hotComment.isLoading ? $style.loading : null]" :comments="hotComment.list")
|
||||
p(:class="$style.commentLabel" v-else-if="!hotComment.isLoadError && !hotComment.isLoading") {{$t('comment__no_content')}}
|
||||
div(:class="$style.pagination")
|
||||
material-pagination(:count="hotComment.total" :btnLength="5" :limit="hotComment.limit" :page="hotComment.page" @btn-click="handleToggleHotCommentPage")
|
||||
div(:class="$style.tab_content")
|
||||
div.scroll(:class="$style.tab_content_scroll" ref="dom_commentNew")
|
||||
p(:class="$style.commentLabel" style="cursor: pointer;" v-if="newComment.isLoadError" @click="handleGetNewComment(currentMusicInfo, newComment.nextPage, newComment.limit)") {{$t('comment__new_load_error')}}
|
||||
p(:class="$style.commentLabel" v-else-if="newComment.isLoading && !newComment.list.length") {{$t('comment__new_loading')}}
|
||||
comment-floor(v-if="!newComment.isLoadError && newComment.list.length" :class="[$style.commentFloor, newComment.isLoading ? $style.loading : null]" :comments="newComment.list")
|
||||
p(:class="$style.commentLabel" v-else-if="!newComment.isLoadError && !newComment.isLoading") {{$t('comment__no_content')}}
|
||||
div(:class="$style.pagination")
|
||||
material-pagination(:count="newComment.total" :btnLength="5" :limit="newComment.limit" :page="newComment.page" @btn-click="handleToggleCommentPage")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -52,16 +58,16 @@ export default {
|
|||
name: '',
|
||||
singer: '',
|
||||
},
|
||||
page: 1,
|
||||
total: 0,
|
||||
maxPage: 1,
|
||||
limit: 20,
|
||||
isHotLoading: true,
|
||||
isNewLoading: false,
|
||||
isHotLoadError: true,
|
||||
isNewLoadError: false,
|
||||
nextPage: 1,
|
||||
newComments: [
|
||||
tabActiveId: 'hot',
|
||||
newComment: {
|
||||
isLoading: false,
|
||||
isLoadError: false,
|
||||
page: 1,
|
||||
total: 0,
|
||||
maxPage: 1,
|
||||
nextPage: 1,
|
||||
limit: 20,
|
||||
list: [
|
||||
// {
|
||||
// text: ['123123hhh'],
|
||||
// userName: 'dsads',
|
||||
|
@ -71,8 +77,17 @@ export default {
|
|||
// likedCount: 100,
|
||||
// reply: [],
|
||||
// },
|
||||
],
|
||||
hotComments: [
|
||||
],
|
||||
},
|
||||
hotComment: {
|
||||
isLoading: true,
|
||||
isLoadError: true,
|
||||
page: 1,
|
||||
total: 0,
|
||||
maxPage: 1,
|
||||
nextPage: 1,
|
||||
limit: 20,
|
||||
list: [
|
||||
// {
|
||||
// text: ['123123hhh'],
|
||||
// userName: 'dsads',
|
||||
|
@ -91,7 +106,8 @@ export default {
|
|||
// },
|
||||
// ],
|
||||
// },
|
||||
],
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -102,12 +118,22 @@ export default {
|
|||
: '^-^'
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setWidth()
|
||||
window.addEventListener('resize', this.setWidth)
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('resize', this.setWidth)
|
||||
},
|
||||
watch: {
|
||||
show(n) {
|
||||
if (n) this.handleShowComment()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setWidth() {
|
||||
this.$refs.dom_container.style.width = this.$refs.dom_container.clientWidth + 'px'
|
||||
},
|
||||
async getComment(musicInfo, page, limit, retryNum = 0) {
|
||||
let resp
|
||||
try {
|
||||
|
@ -118,65 +144,92 @@ export default {
|
|||
}
|
||||
return resp
|
||||
},
|
||||
async getHotComment(musicInfo, retryNum = 0) {
|
||||
async getHotComment(musicInfo, page, limit, retryNum = 0) {
|
||||
let resp
|
||||
try {
|
||||
resp = await music[musicInfo.source].comment.getHotComment(musicInfo)
|
||||
resp = await music[musicInfo.source].comment.getHotComment(musicInfo, page, limit)
|
||||
} catch (error) {
|
||||
if (error.message == '取消请求' || ++retryNum > 2) throw error
|
||||
resp = await this.getHotComment(musicInfo, retryNum)
|
||||
resp = await this.getHotComment(musicInfo, page, limit, retryNum)
|
||||
}
|
||||
return resp
|
||||
},
|
||||
handleGetNewComment(musicInfo, page, limit) {
|
||||
this.isNewLoadError = false
|
||||
this.isNewLoading = true
|
||||
this.newComment.isLoadError = false
|
||||
this.newComment.isLoading = true
|
||||
this.getComment(musicInfo, page, limit).then(comment => {
|
||||
this.isNewLoading = false
|
||||
this.total = comment.total
|
||||
this.maxPage = comment.maxPage
|
||||
this.page = page
|
||||
this.newComments = comment.comments
|
||||
this.newComment.isLoading = false
|
||||
this.newComment.total = comment.total
|
||||
this.newComment.maxPage = comment.maxPage
|
||||
this.newComment.page = page
|
||||
this.newComment.list = comment.comments
|
||||
this.$nextTick(() => {
|
||||
scrollTo(this.$refs.dom_comment, 0, 300)
|
||||
scrollTo(this.$refs.dom_commentNew, 0, 300)
|
||||
})
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
if (err.message == '取消请求') return
|
||||
this.isNewLoadError = true
|
||||
this.isNewLoading = false
|
||||
this.newComment.isLoadError = true
|
||||
this.newComment.isLoading = false
|
||||
})
|
||||
},
|
||||
handleGetHotComment(musicInfo) {
|
||||
this.isHotLoadError = false
|
||||
this.isHotLoading = true
|
||||
this.getHotComment(musicInfo).then(hotComment => {
|
||||
this.isHotLoading = false
|
||||
this.hotComments = hotComment.comments
|
||||
handleGetHotComment(musicInfo, page, limit) {
|
||||
this.hotComment.isLoadError = false
|
||||
this.hotComment.isLoading = true
|
||||
this.getHotComment(musicInfo, page, limit).then(hotComment => {
|
||||
this.hotComment.isLoading = false
|
||||
this.hotComment.total = hotComment.total
|
||||
this.hotComment.maxPage = hotComment.maxPage
|
||||
this.hotComment.page = page
|
||||
this.hotComment.list = hotComment.comments
|
||||
this.$nextTick(() => {
|
||||
scrollTo(this.$refs.dom_commentHot, 0, 300)
|
||||
})
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
if (err.message == '取消请求') return
|
||||
this.isHotLoadError = true
|
||||
this.isHotLoading = false
|
||||
this.hotComment.isLoadError = true
|
||||
this.hotComment.isLoading = false
|
||||
})
|
||||
},
|
||||
handleShowComment() {
|
||||
if (!this.musicInfo.songmid || !music[this.musicInfo.source].comment) return
|
||||
// if (this.musicInfo.songmid != this.currentMusicInfo.songmid) {
|
||||
this.page = 1
|
||||
this.total = 0
|
||||
this.maxPage = 1
|
||||
this.nextPage = 1
|
||||
this.hotComment.page = 1
|
||||
this.hotComment.total = 0
|
||||
this.hotComment.maxPage = 1
|
||||
this.hotComment.nextPage = 1
|
||||
|
||||
this.newComment.page = 1
|
||||
this.newComment.total = 0
|
||||
this.newComment.maxPage = 1
|
||||
this.newComment.nextPage = 1
|
||||
// }
|
||||
this.isShowComment = true
|
||||
this.currentMusicInfo = this.musicInfo
|
||||
|
||||
if (this.page == 1) this.handleGetHotComment(this.currentMusicInfo)
|
||||
this.handleGetNewComment(this.currentMusicInfo, this.page, this.limit)
|
||||
this.handleGetHotComment(this.currentMusicInfo, this.hotComment.page, this.hotComment.limit)
|
||||
this.handleGetNewComment(this.currentMusicInfo, this.newComment.page, this.newComment.limit)
|
||||
},
|
||||
handleToggleHotCommentPage(page) {
|
||||
this.hotComment.nextPage = page
|
||||
this.handleGetHotComment(this.currentMusicInfo, page, this.hotComment.limit)
|
||||
},
|
||||
handleToggleCommentPage(page) {
|
||||
this.nextPage = page
|
||||
this.handleGetNewComment(this.currentMusicInfo, page, this.limit)
|
||||
this.newComment.nextPage = page
|
||||
this.handleGetNewComment(this.currentMusicInfo, page, this.newComment.limit)
|
||||
},
|
||||
handleToggleTab(id) {
|
||||
if (this.tabActiveId == id) return
|
||||
switch (id) {
|
||||
case 'hot':
|
||||
this.$refs.dom_tabMain.scrollLeft = 0
|
||||
break
|
||||
case 'new':
|
||||
this.$refs.dom_tabMain.scrollLeft = this.$refs.dom_tabMain.clientWidth
|
||||
break
|
||||
}
|
||||
this.tabActiveId = id
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -229,10 +282,39 @@ export default {
|
|||
}
|
||||
.commentMain {
|
||||
flex: auto;
|
||||
padding-left: 15px;
|
||||
padding-right: 10px;
|
||||
background-color: @color-reply-floor;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.tab_header {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
gap: 15px;
|
||||
padding-left: 15px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.tab_main {
|
||||
flex: auto;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
overflow: hidden;
|
||||
scroll-snap-type: x mandatory;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
.tab_content {
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.tab_content_scroll {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-left: 15px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.commentLabel {
|
||||
padding: 15px;
|
||||
|
@ -240,9 +322,20 @@ export default {
|
|||
font-size: 14px;
|
||||
}
|
||||
.commentType {
|
||||
padding: 10px 0;
|
||||
padding: 5px;
|
||||
margin: 5px 0;
|
||||
font-size: 13px;
|
||||
color: @color-theme;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: @transition-theme;
|
||||
transition-property: opacity, color;
|
||||
&:hover {
|
||||
opacity: .7;
|
||||
}
|
||||
&.active {
|
||||
color: @color-theme;
|
||||
}
|
||||
}
|
||||
.commentFloor {
|
||||
opacity: 1;
|
||||
|
@ -268,7 +361,9 @@ each(@themes, {
|
|||
color: ~'@{color-@{value}-theme_2-font-label}';
|
||||
}
|
||||
.commentType {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
&.active {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -162,25 +162,25 @@ export default {
|
|||
}
|
||||
|
||||
}
|
||||
.bg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-size: 110% 110%;
|
||||
filter: blur(60px);
|
||||
z-index: -1;
|
||||
}
|
||||
.bg2 {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
background-color: rgba(255, 255, 255, .8);
|
||||
}
|
||||
// .bg {
|
||||
// position: absolute;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// background-size: 110% 110%;
|
||||
// filter: blur(60px);
|
||||
// z-index: -1;
|
||||
// }
|
||||
// .bg2 {
|
||||
// position: absolute;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// z-index: -1;
|
||||
// background-color: rgba(255, 255, 255, .8);
|
||||
// }
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
|
@ -299,6 +299,9 @@ export default {
|
|||
.lyric {
|
||||
font-size: 13px;
|
||||
}
|
||||
.lyricSelectContent {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.comment {
|
||||
opacity: 1;
|
||||
|
@ -346,131 +349,6 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 0 0 60%;
|
||||
// padding: 0 30px;
|
||||
position: relative;
|
||||
transition: flex-basis @transition-theme;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: ' ';
|
||||
height: 100px;
|
||||
width: 100%;
|
||||
background-image: linear-gradient(0deg,rgba(255,255,255,0) 0%,@color-theme_2-background_1 95%);
|
||||
pointer-events: none;
|
||||
}
|
||||
&:after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
content: ' ';
|
||||
height: 100px;
|
||||
width: 100%;
|
||||
background-image: linear-gradient(-180deg,rgba(255,255,255,0) 0%,@color-theme_2-background_1 95%);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
.lyric {
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-size: 16px;
|
||||
cursor: grab;
|
||||
&.draging {
|
||||
cursor: grabbing;
|
||||
}
|
||||
:global {
|
||||
.lrc-content {
|
||||
line-height: 1.2;
|
||||
margin: 16px 0;
|
||||
overflow-wrap: break-word;
|
||||
color: @color-player-detail-lyric;
|
||||
|
||||
.translation {
|
||||
transition: @transition-theme !important;
|
||||
transition-property: font-size, color;
|
||||
font-size: .9em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.line {
|
||||
transition-property: font-size, color !important;
|
||||
background: none !important;
|
||||
-webkit-text-fill-color: unset;
|
||||
// -webkit-text-fill-color: none !important;
|
||||
}
|
||||
&.active {
|
||||
.line {
|
||||
color: @color-theme;
|
||||
}
|
||||
.translation {
|
||||
font-size: 1em;
|
||||
color: @color-theme;
|
||||
}
|
||||
span {
|
||||
// color: @color-theme;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
transition: @transition-theme !important;
|
||||
transition-property: font-size !important;
|
||||
font-size: 1em;
|
||||
background-repeat: no-repeat;
|
||||
background-color: rgba(77, 77, 77, 0.9);
|
||||
background-image: -webkit-linear-gradient(top, @color-theme, @color-theme);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
background-size: 0 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
// p {
|
||||
// padding: 8px 0;
|
||||
// line-height: 1.2;
|
||||
// overflow-wrap: break-word;
|
||||
// transition: @transition-theme !important;
|
||||
// transition-property: color, font-size;
|
||||
// }
|
||||
// .lrc-active {
|
||||
// color: @color-theme;
|
||||
// font-size: 1.2em;
|
||||
// }
|
||||
}
|
||||
.lyricSelectContent {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
// text-align: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
background-color: @color-theme_2-background_1;
|
||||
z-index: 10;
|
||||
color: @color-player-detail-lyric;
|
||||
|
||||
.lyricSelectline {
|
||||
padding: 8px 0;
|
||||
overflow-wrap: break-word;
|
||||
transition: @transition-theme !important;
|
||||
transition-property: color, font-size;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.lyricSelectlineTransition {
|
||||
font-size: 14px;
|
||||
}
|
||||
.lrc-active {
|
||||
color: @color-theme;
|
||||
}
|
||||
}
|
||||
|
||||
.lyric-space {
|
||||
height: 70%;
|
||||
}
|
||||
|
||||
.comment {
|
||||
position: absolute;
|
||||
|
@ -491,14 +369,6 @@ each(@themes, {
|
|||
background-color: ~'@{color-@{value}-theme_2-background_1}';
|
||||
// color: ~'@{color-@{value}-theme_2-font}';
|
||||
}
|
||||
.right {
|
||||
&:before {
|
||||
background-image: linear-gradient(0deg,rgba(255,255,255,0) 0%,~'@{color-@{value}-theme_2-background_1}' 95%);
|
||||
}
|
||||
&:after {
|
||||
background-image: linear-gradient(-180deg,rgba(255,255,255,0) 0%,~'@{color-@{value}-theme_2-background_1}' 95%);
|
||||
}
|
||||
}
|
||||
.header {
|
||||
&.controlBtnLeft {
|
||||
.controBtn {
|
||||
|
@ -542,56 +412,6 @@ each(@themes, {
|
|||
box-shadow: 0 0 4px ~'@{color-@{value}-theme-hover}';
|
||||
// border-color: ~'@{color-@{value}-theme-hover}';
|
||||
}
|
||||
.lyric {
|
||||
:global {
|
||||
.lrc-content {
|
||||
color: ~'@{color-@{value}-player-detail-lyric}';
|
||||
|
||||
&.active {
|
||||
.translation {
|
||||
color: ~'@{color-@{value}-player-detail-lyric-active}';
|
||||
}
|
||||
.line {
|
||||
color: ~'@{color-@{value}-player-detail-lyric-active}';
|
||||
}
|
||||
}
|
||||
span {
|
||||
background-color: ~'@{color-@{value}-player-detail-lyric}';
|
||||
background-image: -webkit-linear-gradient(top, ~'@{color-@{value}-player-detail-lyric-active}', ~'@{color-@{value}-player-detail-lyric-active}');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// .lrc-active {
|
||||
// color: ~'@{color-@{value}-theme}';
|
||||
// }
|
||||
.lyricSelectContent {
|
||||
background-color: ~'@{color-@{value}-theme_2-background_1}';
|
||||
color: ~'@{color-@{value}-player-detail-lyric}';
|
||||
.lrc-active {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
}
|
||||
.footerLeftControlBtns {
|
||||
color: ~'@{color-@{value}-theme_2-font}';
|
||||
}
|
||||
.footerLeftControlBtn {
|
||||
&.active {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
}
|
||||
.progress {
|
||||
background-color: ~'@{color-@{value}-player-progress}';
|
||||
}
|
||||
.progress-bar1 {
|
||||
background-color: ~'@{color-@{value}-player-progress-bar1}';
|
||||
}
|
||||
.progress-bar2 {
|
||||
background-color: ~'@{color-@{value}-player-progress-bar2}';
|
||||
}
|
||||
.play-btn {
|
||||
color: ~'@{color-@{value}-player-detail-play-btn}';
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ div(:class="$style.songList")
|
|||
|
||||
<script>
|
||||
import { clipboardWriteText, assertApiSupport } from '@renderer/utils'
|
||||
import { ref, useCssModule } from '@renderer/utils/vueTools'
|
||||
import { ref, useCssModule, useRefGetter } from '@renderer/utils/vueTools'
|
||||
import useList from './useList'
|
||||
import useMenu from './useMenu'
|
||||
import usePlay from './usePlay'
|
||||
|
@ -95,7 +95,7 @@ export default {
|
|||
type: String,
|
||||
},
|
||||
},
|
||||
emits: ['show-menu', 'togglePage'],
|
||||
emits: ['show-menu', 'play-list', 'togglePage'],
|
||||
setup(props, { emit }) {
|
||||
const rightClickSelectedIndex = ref(-1)
|
||||
const dom_listContent = ref(null)
|
||||
|
@ -103,18 +103,20 @@ export default {
|
|||
|
||||
const styles = useCssModule()
|
||||
|
||||
const setting = useRefGetter('setting')
|
||||
|
||||
const {
|
||||
selectedList,
|
||||
listItemHeight,
|
||||
handleSelectData,
|
||||
removeAllSelect,
|
||||
} = useList({ props, emit })
|
||||
} = useList({ props })
|
||||
|
||||
const {
|
||||
handlePlayMusic,
|
||||
handlePlayMusicLater,
|
||||
doubleClickPlay,
|
||||
} = usePlay({ selectedList, props, removeAllSelect })
|
||||
} = usePlay({ selectedList, props, removeAllSelect, setting, emit })
|
||||
|
||||
const {
|
||||
isShowListAdd,
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useCommit } from '@renderer/utils/vueTools'
|
|||
import { defaultList } from '@renderer/core/share/list'
|
||||
import { getList } from '@renderer/core/share/utils'
|
||||
|
||||
export default ({ selectedList, props, removeAllSelect }) => {
|
||||
export default ({ selectedList, props, removeAllSelect, setting, emit }) => {
|
||||
let clickTime = 0
|
||||
let clickIndex = -1
|
||||
|
||||
|
@ -47,7 +47,11 @@ export default ({ selectedList, props, removeAllSelect }) => {
|
|||
clickIndex = index
|
||||
return
|
||||
}
|
||||
handlePlayMusic(index, true)
|
||||
if (setting.value.list.isClickPlayList) {
|
||||
emit('play-list', index)
|
||||
} else {
|
||||
handlePlayMusic(index, true)
|
||||
}
|
||||
clickTime = 0
|
||||
clickIndex = -1
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ export const getPlayIndex = (listId, musicInfo, isTempPlay) => {
|
|||
}
|
||||
|
||||
const list = getList(listId)
|
||||
if (list?.length) {
|
||||
if (list?.length && musicInfo) {
|
||||
if (musicInfo.key) { // 已下载的歌曲
|
||||
const currentKey = musicInfo.key
|
||||
playIndex = list.findIndex(m => m.key == currentKey)
|
||||
|
|
|
@ -40,7 +40,7 @@ export default () => {
|
|||
isProd,
|
||||
isLinux,
|
||||
})
|
||||
usePlayer({ setting })
|
||||
const initPlayer = usePlayer({ setting })
|
||||
const handleEnvParams = useHandleEnvParams()
|
||||
const initData = useDataInit({
|
||||
setting,
|
||||
|
@ -60,6 +60,7 @@ export default () => {
|
|||
|
||||
// 初始化我的列表、下载列表等数据
|
||||
initData().then(() => {
|
||||
initPlayer()
|
||||
handleEnvParams(envParams) // 处理传入的启动参数
|
||||
initDeepLink(envParams)
|
||||
})
|
||||
|
|
|
@ -1,268 +0,0 @@
|
|||
import { useCommit, useAction, onBeforeUnmount, useRouter, useI18n, markRaw } from '@renderer/utils/vueTools'
|
||||
import { base as eventBaseName } from '@renderer/event/names'
|
||||
import { getEnvParams, clearEnvParamsDeeplink } from '@renderer/utils/tools'
|
||||
import { decodeName } from '@renderer/utils'
|
||||
// import { allList, defaultList, loveList, userLists } from '@renderer/core/share/list'
|
||||
import { isShowPlayerDetail, setShowPlayerDetail, playMusicInfo } from '@renderer/core/share/player'
|
||||
import usePlaySonglist from './compositions/usePlaySonglist'
|
||||
import { dialog } from '@renderer/plugins/Dialog'
|
||||
|
||||
const sources = ['kw', 'kg', 'tx', 'wy', 'mg']
|
||||
const sourceVerify = source => {
|
||||
if (!sources.includes(source)) throw new Error('Source no match')
|
||||
}
|
||||
|
||||
const qualitys = ['128k', '320k', 'flac', 'flac32bit']
|
||||
const qualityFilter = (source, types) => {
|
||||
types = types.filter(({ type }) => qualitys.includes(type)).map(({ type, size, hash }) => {
|
||||
if (size != null && typeof size != 'string') throw new Error(type + ' size type no match')
|
||||
if (source == 'kg' && typeof hash != 'string') throw new Error(type + ' hash type no match')
|
||||
return hash == null ? { type, size } : { type, size, hash }
|
||||
})
|
||||
if (!types.length) throw new Error('quality no match')
|
||||
return types
|
||||
}
|
||||
|
||||
const dataVerify = (rules, data) => {
|
||||
const newData = {}
|
||||
for (const rule of rules) {
|
||||
const val = data[rule.key]
|
||||
if (rule.required && val == null) throw new Error(rule.key + ' missing')
|
||||
if (val != null) {
|
||||
if (rule.types && !rule.types.includes(typeof val)) throw new Error(rule.key + ' type no match')
|
||||
if (rule.max && String(val).length > rule.max) throw new Error(rule.key + ' max length no match')
|
||||
if (rule.min && String(val).length > rule.min) throw new Error(rule.key + ' min length no match')
|
||||
}
|
||||
newData[rule.key] = val
|
||||
}
|
||||
return newData
|
||||
}
|
||||
|
||||
export default () => {
|
||||
// const setList = useCommit('list', 'setList')
|
||||
// const listAdd = useCommit('list', 'listAdd')
|
||||
// const listMove = useCommit('list', 'listMove')
|
||||
// const listAddMultiple = useCommit('list', 'listAddMultiple')
|
||||
// const listMoveMultiple = useCommit('list', 'listMoveMultiple')
|
||||
// const listRemove = useCommit('list', 'listRemove')
|
||||
// const listRemoveMultiple = useCommit('list', 'listRemoveMultiple')
|
||||
// const listClear = useCommit('list', 'listClear')
|
||||
// const updateMusicInfo = useCommit('list', 'updateMusicInfo')
|
||||
// const createUserList = useCommit('list', 'createUserList')
|
||||
// const removeUserList = useCommit('list', 'removeUserList')
|
||||
// const setUserListName = useCommit('list', 'setUserListName')
|
||||
// const setMusicPosition = useCommit('list', 'setMusicPosition')
|
||||
// // const setSyncListData = useCommit('list', 'setSyncListData')
|
||||
// const setUserListPosition = useCommit('list', 'setUserListPosition')
|
||||
const router = useRouter()
|
||||
const setTempPlayList = useCommit('player', 'setTempPlayList')
|
||||
const playNext = useAction('player', 'playNext')
|
||||
const playSongListDetail = usePlaySonglist()
|
||||
const { t } = useI18n()
|
||||
let isInited = false
|
||||
|
||||
const handleOpenSonglist = params => {
|
||||
if (params.id) {
|
||||
router.replace({
|
||||
path: '/songList',
|
||||
query: {
|
||||
source: params.source,
|
||||
id: params.id,
|
||||
},
|
||||
})
|
||||
} else if (params.url) {
|
||||
router.replace({
|
||||
path: '/songList',
|
||||
query: {
|
||||
source: params.source,
|
||||
url: params.url,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handlePlayMusic = _musicInfo => {
|
||||
const musicInfo = {
|
||||
..._musicInfo,
|
||||
singer: decodeName(_musicInfo.singer),
|
||||
name: decodeName(_musicInfo.name),
|
||||
albumName: decodeName(_musicInfo.albumName),
|
||||
otherSource: null,
|
||||
_types: {},
|
||||
typeUrl: {},
|
||||
}
|
||||
for (const type of musicInfo.types) {
|
||||
musicInfo._types[type.type] = { size: type.size }
|
||||
}
|
||||
markRaw(musicInfo)
|
||||
const isPlaying = !!playMusicInfo.musicInfo
|
||||
setTempPlayList([{ listId: '__temp__', musicInfo, isTop: true }])
|
||||
if (isPlaying) playNext()
|
||||
}
|
||||
|
||||
const handleSonglist = (action, songlistInfo) => {
|
||||
sourceVerify(songlistInfo.source)
|
||||
switch (action) {
|
||||
case 'open':
|
||||
songlistInfo = dataVerify([
|
||||
{ key: 'source', types: ['string'] },
|
||||
{ key: 'id', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'url', types: ['string'], max: 500 },
|
||||
], songlistInfo)
|
||||
if (isShowPlayerDetail.value) setShowPlayerDetail(false)
|
||||
handleOpenSonglist(songlistInfo)
|
||||
break
|
||||
case 'play':
|
||||
songlistInfo = dataVerify([
|
||||
{ key: 'source', types: ['string'] },
|
||||
{ key: 'id', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'url', types: ['string'], max: 500 },
|
||||
{ key: 'index', types: ['number'], max: 1000000 },
|
||||
], songlistInfo)
|
||||
playSongListDetail(songlistInfo.source, songlistInfo.id ?? songlistInfo.url, songlistInfo.index ?? 0)
|
||||
break
|
||||
default: throw new Error('Unknown action: ' + action)
|
||||
}
|
||||
}
|
||||
|
||||
const handleMusic = (action, musicInfo) => {
|
||||
switch (musicInfo.source) {
|
||||
case 'kw':
|
||||
musicInfo = dataVerify([
|
||||
{ key: 'name', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'singer', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'source', types: ['string'], required: true },
|
||||
{ key: 'songmid', types: ['string', 'number'], max: 64, required: true },
|
||||
{ key: 'img', types: ['string'], max: 1024 },
|
||||
{ key: 'albumId', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'interval', types: ['string'], max: 64 },
|
||||
{ key: 'albumName', types: ['string'], max: 64 },
|
||||
{ key: 'types', types: ['object'], required: true },
|
||||
], musicInfo)
|
||||
break
|
||||
case 'kg':
|
||||
musicInfo = dataVerify([
|
||||
{ key: 'name', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'singer', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'source', types: ['string'], required: true },
|
||||
{ key: 'songmid', types: ['string', 'number'], max: 64, required: true },
|
||||
{ key: 'img', types: ['string'], max: 1024 },
|
||||
{ key: 'albumId', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'interval', types: ['string'], max: 64 },
|
||||
{ key: '_interval', types: ['number'], max: 64 },
|
||||
{ key: 'albumName', types: ['string'], max: 64 },
|
||||
{ key: 'types', types: ['object'], required: true },
|
||||
|
||||
{ key: 'hash', types: ['string'], required: true, max: 64 },
|
||||
], musicInfo)
|
||||
break
|
||||
case 'tx':
|
||||
musicInfo = dataVerify([
|
||||
{ key: 'name', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'singer', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'source', types: ['string'], required: true },
|
||||
{ key: 'songmid', types: ['string', 'number'], max: 64, required: true },
|
||||
{ key: 'img', types: ['string'], max: 1024 },
|
||||
{ key: 'albumId', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'interval', types: ['string'], max: 64 },
|
||||
{ key: 'albumName', types: ['string'], max: 64 },
|
||||
{ key: 'types', types: ['object'], required: true },
|
||||
|
||||
{ key: 'strMediaMid', types: ['string'], required: true, max: 64 },
|
||||
{ key: 'albumMid', types: ['string'], max: 64 },
|
||||
], musicInfo)
|
||||
break
|
||||
case 'wy':
|
||||
musicInfo = dataVerify([
|
||||
{ key: 'name', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'singer', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'source', types: ['string'], required: true },
|
||||
{ key: 'songmid', types: ['string', 'number'], max: 64, required: true },
|
||||
{ key: 'img', types: ['string'], max: 1024 },
|
||||
{ key: 'albumId', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'interval', types: ['string'], max: 64 },
|
||||
{ key: 'albumName', types: ['string'], max: 64 },
|
||||
{ key: 'types', types: ['object'], required: true },
|
||||
], musicInfo)
|
||||
break
|
||||
case 'mg':
|
||||
musicInfo = dataVerify([
|
||||
{ key: 'name', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'singer', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'source', types: ['string'], required: true },
|
||||
{ key: 'songmid', types: ['string', 'number'], max: 64, required: true },
|
||||
{ key: 'img', types: ['string'], max: 1024 },
|
||||
{ key: 'albumId', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'interval', types: ['string'], max: 64 },
|
||||
{ key: 'albumName', types: ['string'], max: 64 },
|
||||
{ key: 'types', types: ['object'], required: true },
|
||||
|
||||
{ key: 'copyrightId', types: ['string', 'number'], required: true, max: 64 },
|
||||
{ key: 'lrcUrl', types: ['string'], max: 1024 },
|
||||
], musicInfo)
|
||||
break
|
||||
default: throw new Error('Unknown action: ' + action)
|
||||
}
|
||||
musicInfo.types = qualityFilter(musicInfo.source, musicInfo.types)
|
||||
switch (action) {
|
||||
case 'play':
|
||||
handlePlayMusic(musicInfo)
|
||||
break
|
||||
default: throw new Error('Unknown action: ' + action)
|
||||
}
|
||||
}
|
||||
|
||||
const handleLinkAction = link => {
|
||||
// console.log(link)
|
||||
const [url, search] = link.split('?')
|
||||
const [type, action] = url.replace('lxmusic://', '').split('/')
|
||||
const params = {}
|
||||
for (const param of search.split('&')) {
|
||||
const [key, value] = param.split('=')
|
||||
params[key] = value
|
||||
}
|
||||
if (params.data) params.data = JSON.parse(decodeURIComponent(params.data))
|
||||
console.log(params.data)
|
||||
switch (type) {
|
||||
case 'music':
|
||||
handleMusic(action, params.data)
|
||||
break
|
||||
case 'songlist':
|
||||
handleSonglist(action, params.data)
|
||||
break
|
||||
default: throw new Error('Unknown type: ' + type)
|
||||
}
|
||||
}
|
||||
|
||||
const handleFocus = () => {
|
||||
if (!isInited) return
|
||||
|
||||
getEnvParams().then(envParams => {
|
||||
if (!envParams.deeplink) return
|
||||
clearEnvParamsDeeplink()
|
||||
try {
|
||||
handleLinkAction(envParams.deeplink)
|
||||
} catch (err) {
|
||||
dialog(`${t('deep_link__handle_error_tip', { message: err.message })}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
window.eventHub.on(eventBaseName.focus, handleFocus)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.eventHub.off(eventBaseName.focus, handleFocus)
|
||||
})
|
||||
|
||||
return envParams => {
|
||||
if (envParams.deeplink) {
|
||||
clearEnvParamsDeeplink()
|
||||
try {
|
||||
handleLinkAction(envParams.deeplink)
|
||||
} catch (err) {
|
||||
dialog(`${t('deep_link__handle_error_tip', { message: err.message })}`)
|
||||
}
|
||||
}
|
||||
isInited = true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
import { onBeforeUnmount } from '@renderer/utils/vueTools'
|
||||
import { base as eventBaseName } from '@renderer/event/names'
|
||||
import { getEnvParams, clearEnvParamsDeeplink } from '@renderer/utils/tools'
|
||||
|
||||
import { useDialog } from './utils'
|
||||
import useMusicAction from './useMusicAction'
|
||||
import useSonglistAction from './useSonglistAction'
|
||||
|
||||
export default () => {
|
||||
let isInited = false
|
||||
|
||||
const showErrorDialog = useDialog()
|
||||
|
||||
const handleMusicAction = useMusicAction()
|
||||
const handleSonglistAction = useSonglistAction()
|
||||
|
||||
|
||||
const handleLinkAction = link => {
|
||||
// console.log(link)
|
||||
const [url, search] = link.split('?')
|
||||
const [type, action, ...paths] = url.replace('lxmusic://', '').split('/')
|
||||
const params = {}
|
||||
if (search) {
|
||||
for (const param of search.split('&')) {
|
||||
const [key, value] = param.split('=')
|
||||
params[key] = value
|
||||
}
|
||||
if (params.data) params.data = JSON.parse(decodeURIComponent(params.data))
|
||||
}
|
||||
params.paths = paths.map(p => decodeURIComponent(p))
|
||||
console.log(params)
|
||||
switch (type) {
|
||||
case 'music':
|
||||
handleMusicAction(action, params)
|
||||
break
|
||||
case 'songlist':
|
||||
handleSonglistAction(action, params)
|
||||
break
|
||||
default: throw new Error('Unknown type: ' + type)
|
||||
}
|
||||
}
|
||||
|
||||
const handleFocus = () => {
|
||||
if (!isInited) return
|
||||
|
||||
getEnvParams().then(envParams => {
|
||||
if (!envParams.deeplink) return
|
||||
clearEnvParamsDeeplink()
|
||||
try {
|
||||
handleLinkAction(envParams.deeplink)
|
||||
} catch (err) {
|
||||
showErrorDialog(err.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
window.eventHub.on(eventBaseName.focus, handleFocus)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.eventHub.off(eventBaseName.focus, handleFocus)
|
||||
})
|
||||
|
||||
return envParams => {
|
||||
if (envParams.deeplink) {
|
||||
clearEnvParamsDeeplink()
|
||||
try {
|
||||
handleLinkAction(envParams.deeplink)
|
||||
} catch (err) {
|
||||
showErrorDialog(err.message)
|
||||
}
|
||||
}
|
||||
isInited = true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
import { useCommit, useAction, useRouter, markRaw } from '@renderer/utils/vueTools'
|
||||
import { decodeName } from '@renderer/utils'
|
||||
// import { allList, defaultList, loveList, userLists } from '@renderer/core/share/list'
|
||||
import { playMusicInfo } from '@renderer/core/share/player'
|
||||
|
||||
import { dataVerify, qualityFilter, sources } from './utils'
|
||||
|
||||
const useSearchMusic = () => {
|
||||
const router = useRouter()
|
||||
|
||||
return ({ paths, data: params }) => {
|
||||
let text
|
||||
let source
|
||||
if (params) {
|
||||
text = dataVerify([
|
||||
{ key: 'keywords', types: ['string', 'number'], max: 128, required: true },
|
||||
], params).keywords
|
||||
source = params.source
|
||||
} else {
|
||||
if (!paths.length) throw new Error('Keyword missing')
|
||||
|
||||
if (paths.length > 1) {
|
||||
text = paths[1]
|
||||
source = paths[0]
|
||||
} else {
|
||||
text = paths[0]
|
||||
}
|
||||
|
||||
if (text.length > 128) text = text.substring(0, 128)
|
||||
}
|
||||
|
||||
const sourceList = [...sources, 'all']
|
||||
source = sourceList.includes(source) ? source : null
|
||||
router.replace({
|
||||
path: '/search',
|
||||
query: {
|
||||
text,
|
||||
source,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const usePlayMusic = () => {
|
||||
const setTempPlayList = useCommit('player', 'setTempPlayList')
|
||||
const playNext = useAction('player', 'playNext')
|
||||
|
||||
const filterInfoByPlayMusic = musicInfo => {
|
||||
switch (musicInfo.source) {
|
||||
case 'kw':
|
||||
musicInfo = dataVerify([
|
||||
{ key: 'name', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'singer', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'source', types: ['string'], required: true },
|
||||
{ key: 'songmid', types: ['string', 'number'], max: 64, required: true },
|
||||
{ key: 'img', types: ['string'], max: 1024 },
|
||||
{ key: 'albumId', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'interval', types: ['string'], max: 64 },
|
||||
{ key: 'albumName', types: ['string'], max: 64 },
|
||||
{ key: 'types', types: ['object'], required: true },
|
||||
], musicInfo)
|
||||
break
|
||||
case 'kg':
|
||||
musicInfo = dataVerify([
|
||||
{ key: 'name', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'singer', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'source', types: ['string'], required: true },
|
||||
{ key: 'songmid', types: ['string', 'number'], max: 64, required: true },
|
||||
{ key: 'img', types: ['string'], max: 1024 },
|
||||
{ key: 'albumId', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'interval', types: ['string'], max: 64 },
|
||||
{ key: '_interval', types: ['number'], max: 64 },
|
||||
{ key: 'albumName', types: ['string'], max: 64 },
|
||||
{ key: 'types', types: ['object'], required: true },
|
||||
|
||||
{ key: 'hash', types: ['string'], required: true, max: 64 },
|
||||
], musicInfo)
|
||||
break
|
||||
case 'tx':
|
||||
musicInfo = dataVerify([
|
||||
{ key: 'name', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'singer', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'source', types: ['string'], required: true },
|
||||
{ key: 'songmid', types: ['string', 'number'], max: 64, required: true },
|
||||
{ key: 'img', types: ['string'], max: 1024 },
|
||||
{ key: 'albumId', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'interval', types: ['string'], max: 64 },
|
||||
{ key: 'albumName', types: ['string'], max: 64 },
|
||||
{ key: 'types', types: ['object'], required: true },
|
||||
|
||||
{ key: 'strMediaMid', types: ['string'], required: true, max: 64 },
|
||||
{ key: 'albumMid', types: ['string'], max: 64 },
|
||||
], musicInfo)
|
||||
break
|
||||
case 'wy':
|
||||
musicInfo = dataVerify([
|
||||
{ key: 'name', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'singer', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'source', types: ['string'], required: true },
|
||||
{ key: 'songmid', types: ['string', 'number'], max: 64, required: true },
|
||||
{ key: 'img', types: ['string'], max: 1024 },
|
||||
{ key: 'albumId', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'interval', types: ['string'], max: 64 },
|
||||
{ key: 'albumName', types: ['string'], max: 64 },
|
||||
{ key: 'types', types: ['object'], required: true },
|
||||
], musicInfo)
|
||||
break
|
||||
case 'mg':
|
||||
musicInfo = dataVerify([
|
||||
{ key: 'name', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'singer', types: ['string'], required: true, max: 200 },
|
||||
{ key: 'source', types: ['string'], required: true },
|
||||
{ key: 'songmid', types: ['string', 'number'], max: 64, required: true },
|
||||
{ key: 'img', types: ['string'], max: 1024 },
|
||||
{ key: 'albumId', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'interval', types: ['string'], max: 64 },
|
||||
{ key: 'albumName', types: ['string'], max: 64 },
|
||||
{ key: 'types', types: ['object'], required: true },
|
||||
|
||||
{ key: 'copyrightId', types: ['string', 'number'], required: true, max: 64 },
|
||||
{ key: 'lrcUrl', types: ['string'], max: 1024 },
|
||||
], musicInfo)
|
||||
break
|
||||
default: throw new Error('Unknown source: ' + musicInfo.source)
|
||||
}
|
||||
musicInfo.types = qualityFilter(musicInfo.source, musicInfo.types)
|
||||
return musicInfo
|
||||
}
|
||||
|
||||
return ({ data: _musicInfo }) => {
|
||||
_musicInfo = filterInfoByPlayMusic(_musicInfo)
|
||||
|
||||
const musicInfo = {
|
||||
..._musicInfo,
|
||||
singer: decodeName(_musicInfo.singer),
|
||||
name: decodeName(_musicInfo.name),
|
||||
albumName: decodeName(_musicInfo.albumName),
|
||||
otherSource: null,
|
||||
_types: {},
|
||||
typeUrl: {},
|
||||
}
|
||||
for (const type of musicInfo.types) {
|
||||
musicInfo._types[type.type] = { size: type.size }
|
||||
}
|
||||
markRaw(musicInfo)
|
||||
const isPlaying = !!playMusicInfo.musicInfo
|
||||
setTempPlayList([{ listId: '__temp__', musicInfo, isTop: true }])
|
||||
if (isPlaying) playNext()
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const handleSearchMusic = useSearchMusic()
|
||||
const handlePlayMusic = usePlayMusic()
|
||||
|
||||
return (action, info) => {
|
||||
switch (action) {
|
||||
case 'search':
|
||||
handleSearchMusic(info)
|
||||
break
|
||||
case 'play':
|
||||
handlePlayMusic(info)
|
||||
break
|
||||
default: throw new Error('Unknown action: ' + action)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
import { useRouter } from '@renderer/utils/vueTools'
|
||||
import { isShowPlayerDetail, setShowPlayerDetail } from '@renderer/core/share/player'
|
||||
import usePlaySonglist from '../compositions/usePlaySonglist'
|
||||
|
||||
import { dataVerify, sourceVerify } from './utils'
|
||||
|
||||
const useOpenSonglist = () => {
|
||||
const router = useRouter()
|
||||
|
||||
const handleOpenSonglist = params => {
|
||||
if (params.id) {
|
||||
router.replace({
|
||||
path: '/songList',
|
||||
query: {
|
||||
source: params.source,
|
||||
id: params.id,
|
||||
},
|
||||
})
|
||||
} else if (params.url) {
|
||||
router.replace({
|
||||
path: '/songList',
|
||||
query: {
|
||||
source: params.source,
|
||||
url: params.url,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return ({ paths, data }) => {
|
||||
let songlistInfo = {
|
||||
source: null,
|
||||
id: null,
|
||||
url: null,
|
||||
}
|
||||
if (data) {
|
||||
songlistInfo = data
|
||||
} else {
|
||||
songlistInfo.source = paths[0]
|
||||
songlistInfo.url = paths[1]
|
||||
}
|
||||
|
||||
sourceVerify(songlistInfo.source)
|
||||
|
||||
songlistInfo = dataVerify([
|
||||
{ key: 'source', types: ['string'] },
|
||||
{ key: 'id', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'url', types: ['string'], max: 500 },
|
||||
], songlistInfo)
|
||||
|
||||
if (!songlistInfo.id && !songlistInfo.url) throw new Error('id or url missing')
|
||||
if (isShowPlayerDetail.value) setShowPlayerDetail(false)
|
||||
handleOpenSonglist(songlistInfo)
|
||||
}
|
||||
}
|
||||
const usePlaySonglistDetail = () => {
|
||||
const playSongListDetail = usePlaySonglist()
|
||||
|
||||
return ({ paths, data }) => {
|
||||
let songlistInfo = {
|
||||
source: null,
|
||||
id: null,
|
||||
url: null,
|
||||
index: null,
|
||||
}
|
||||
if (data) {
|
||||
songlistInfo = data
|
||||
} else {
|
||||
songlistInfo.source = paths[0]
|
||||
songlistInfo.url = paths[1]
|
||||
songlistInfo.index = paths[2]
|
||||
if (songlistInfo.index != null) songlistInfo.index = parseInt(songlistInfo.index)
|
||||
}
|
||||
|
||||
sourceVerify(songlistInfo.source)
|
||||
|
||||
songlistInfo = dataVerify([
|
||||
{ key: 'source', types: ['string'] },
|
||||
{ key: 'id', types: ['string', 'number'], max: 64 },
|
||||
{ key: 'url', types: ['string'], max: 500 },
|
||||
{ key: 'index', types: ['number'], max: 1000000 },
|
||||
], songlistInfo)
|
||||
|
||||
if (!songlistInfo.id && !songlistInfo.url) throw new Error('id or url missing')
|
||||
|
||||
playSongListDetail(songlistInfo.source, songlistInfo.id ?? songlistInfo.url, songlistInfo.index ?? 0)
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const handleOpenSonglist = useOpenSonglist()
|
||||
const handlePlaySonglist = usePlaySonglistDetail()
|
||||
|
||||
|
||||
return (action, info) => {
|
||||
switch (action) {
|
||||
case 'open':
|
||||
handleOpenSonglist(info)
|
||||
break
|
||||
case 'play':
|
||||
handlePlaySonglist(info)
|
||||
break
|
||||
default: throw new Error('Unknown action: ' + action)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import { useI18n } from '@renderer/utils/vueTools'
|
||||
import { dialog } from '@renderer/plugins/Dialog'
|
||||
|
||||
export const useDialog = () => {
|
||||
const { t } = useI18n()
|
||||
const errorDialog = message => {
|
||||
dialog({
|
||||
message: `${t('deep_link__handle_error_tip', { message })}`,
|
||||
confirmButtonText: t('ok'),
|
||||
})
|
||||
}
|
||||
|
||||
return errorDialog
|
||||
}
|
||||
|
||||
export const sources = ['kw', 'kg', 'tx', 'wy', 'mg']
|
||||
export const sourceVerify = source => {
|
||||
if (!sources.includes(source)) throw new Error('Source no match')
|
||||
}
|
||||
|
||||
export const qualitys = ['128k', '320k', 'flac', 'flac32bit']
|
||||
export const qualityFilter = (source, types) => {
|
||||
types = types.filter(({ type }) => qualitys.includes(type)).map(({ type, size, hash }) => {
|
||||
if (size != null && typeof size != 'string') throw new Error(type + ' size type no match')
|
||||
if (source == 'kg' && typeof hash != 'string') throw new Error(type + ' hash type no match')
|
||||
return hash == null ? { type, size } : { type, size, hash }
|
||||
})
|
||||
if (!types.length) throw new Error('quality no match')
|
||||
return types
|
||||
}
|
||||
|
||||
export const dataVerify = (rules, data) => {
|
||||
const newData = {}
|
||||
for (const rule of rules) {
|
||||
const val = data[rule.key]
|
||||
if (rule.required && val == null) throw new Error(rule.key + ' missing')
|
||||
if (val != null) {
|
||||
if (rule.types && !rule.types.includes(typeof val)) throw new Error(rule.key + ' type no match')
|
||||
if (rule.max && String(val).length > rule.max) throw new Error(rule.key + ' max length no match')
|
||||
if (rule.min && String(val).length > rule.min) throw new Error(rule.key + ' min length no match')
|
||||
}
|
||||
newData[rule.key] = val
|
||||
}
|
||||
return newData
|
||||
}
|
|
@ -1,18 +1,22 @@
|
|||
import { onBeforeUnmount } from '@renderer/utils/vueTools'
|
||||
import { onUserApiStatus, setUserApi, getUserApiList, userApiRequest, userApiRequestCancel } from '@renderer/utils/tools'
|
||||
import { onBeforeUnmount, useI18n } from '@renderer/utils/vueTools'
|
||||
import { onUserApiStatus, setUserApi, getUserApiList, userApiRequest, userApiRequestCancel, onShowUserApiUpdateAlert } from '@renderer/utils/tools'
|
||||
import { openUrl } from '@renderer/utils'
|
||||
import apiSourceInfo from '@renderer/utils/music/api-source-info'
|
||||
import music from '@renderer/utils/music'
|
||||
import { apiSource, qualityList, userApi } from '@renderer/core/share'
|
||||
import { dialog } from '@renderer/plugins/Dialog'
|
||||
|
||||
|
||||
export default ({ setting }) => {
|
||||
const { t } = useI18n()
|
||||
|
||||
if (/^user_api/.test(setting.value.apiSource)) {
|
||||
setUserApi(setting.value.apiSource)
|
||||
} else {
|
||||
qualityList.value = music.supportQuality[setting.value.apiSource]
|
||||
}
|
||||
|
||||
const rUserApiStatus = onUserApiStatus(({ status, message, apiInfo }) => {
|
||||
const rUserApiStatus = onUserApiStatus((event, { status, message, apiInfo }) => {
|
||||
userApi.status = status
|
||||
userApi.message = message
|
||||
|
||||
|
@ -66,8 +70,32 @@ export default ({ setting }) => {
|
|||
}
|
||||
})
|
||||
|
||||
const rUserApiShowUpdateAlert = onShowUserApiUpdateAlert((event, { name, log, updateUrl }) => {
|
||||
if (updateUrl) {
|
||||
dialog({
|
||||
message: `${t('user_api__update_alert', { name })}\n${log}`,
|
||||
selection: true,
|
||||
showCancel: true,
|
||||
confirmButtonText: t('user_api__update_alert_open_url'),
|
||||
cancelButtonText: t('close'),
|
||||
}).then(confirm => {
|
||||
if (!confirm) return
|
||||
setTimeout(() => {
|
||||
openUrl(updateUrl)
|
||||
}, 300)
|
||||
})
|
||||
} else {
|
||||
dialog({
|
||||
message: `${t('user_api__update_alert', { name })}\n${log}`,
|
||||
selection: true,
|
||||
confirmButtonText: t('ok'),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
rUserApiStatus()
|
||||
rUserApiShowUpdateAlert()
|
||||
})
|
||||
|
||||
return () => {
|
||||
|
|
|
@ -4,6 +4,8 @@ import {
|
|||
import useMediaDevice from './useMediaDevice'
|
||||
import usePlayerEvent from './usePlayerEvent'
|
||||
import usePlayer from './usePlayer'
|
||||
import useTaskbar from './useTaskbar'
|
||||
import { init as initPlayTimeoutStop } from '@renderer/utils/timeoutStop'
|
||||
|
||||
export default ({ setting }) => {
|
||||
createAudio()
|
||||
|
@ -11,5 +13,12 @@ export default ({ setting }) => {
|
|||
usePlayerEvent()
|
||||
useMediaDevice({ setting }) // 初始化音频驱动输出设置
|
||||
usePlayer({ setting })
|
||||
const initTaskbar = useTaskbar()
|
||||
|
||||
initPlayTimeoutStop()
|
||||
|
||||
return () => {
|
||||
initTaskbar()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ export default ({
|
|||
}
|
||||
|
||||
const handleLoadstart = () => {
|
||||
if (global.isPlayedStop) return
|
||||
startLoadingTimeout()
|
||||
setAllStatus(t('player__loading'))
|
||||
}
|
||||
|
@ -77,6 +78,7 @@ export default ({
|
|||
const handleError = errCode => {
|
||||
if (!musicInfo.songmid) return
|
||||
clearLoadingTimeout()
|
||||
if (global.isPlayedStop) return
|
||||
if (playMusicInfo.listId != 'download' && errCode !== 1 && retryNum < 2) { // 若音频URL无效则尝试刷新2次URL
|
||||
// console.log(this.retryNum)
|
||||
retryNum++
|
||||
|
@ -96,6 +98,11 @@ export default ({
|
|||
clearLoadingTimeout()
|
||||
}
|
||||
|
||||
const handlePlayedStop = () => {
|
||||
clearDelayNextTimeout()
|
||||
clearLoadingTimeout()
|
||||
}
|
||||
|
||||
|
||||
window.eventHub.on(eventPlayerNames.player_loadstart, handleLoadstart)
|
||||
window.eventHub.on(eventPlayerNames.player_loadeddata, handleLoadeddata)
|
||||
|
@ -105,6 +112,7 @@ export default ({
|
|||
window.eventHub.on(eventPlayerNames.player_emptied, handleEmpied)
|
||||
window.eventHub.on(eventPlayerNames.error, handleError)
|
||||
window.eventHub.on(eventPlayerNames.setPlayInfo, handleSetPlayInfo)
|
||||
window.eventHub.on(eventPlayerNames.playedStop, handlePlayedStop)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.eventHub.off(eventPlayerNames.player_loadstart, handleLoadstart)
|
||||
|
@ -115,5 +123,6 @@ export default ({
|
|||
window.eventHub.off(eventPlayerNames.player_emptied, handleEmpied)
|
||||
window.eventHub.off(eventPlayerNames.error, handleError)
|
||||
window.eventHub.off(eventPlayerNames.setPlayInfo, handleSetPlayInfo)
|
||||
window.eventHub.off(eventPlayerNames.playedStop, handlePlayedStop)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import {
|
|||
setMusicInfo,
|
||||
musicInfoItem,
|
||||
playMusicInfo,
|
||||
playInfo,
|
||||
setPlayList,
|
||||
setPlayMusicInfo,
|
||||
playedList,
|
||||
|
@ -103,11 +102,13 @@ export default ({ setting }) => {
|
|||
setAllStatus('Try toggle source...')
|
||||
},
|
||||
}).then(url => {
|
||||
if (global.isPlayedStop) return
|
||||
if (targetSong !== musicInfoItem.value || isPlay.value || type != getPlayType(setting.value.player.highQuality, musicInfoItem.value)) return
|
||||
setMusicInfo({ url })
|
||||
setResource(url)
|
||||
}).catch(err => {
|
||||
// console.log('err', err.message)
|
||||
if (global.isPlayedStop) return
|
||||
if (targetSong !== musicInfoItem.value || isPlay.value) return
|
||||
if (err.message == requestMsg.cancelRequest) return
|
||||
if (!isRetryed) return setUrl(targetSong, isRefresh, true)
|
||||
|
@ -193,11 +194,10 @@ export default ({ setting }) => {
|
|||
|
||||
const setPlayStatus = () => {
|
||||
setPlay(true)
|
||||
setTitle(`${musicInfo.name} - ${musicInfo.singer}`)
|
||||
}
|
||||
const setPauseStatus = () => {
|
||||
setPlay(false)
|
||||
setTitle()
|
||||
if (global.isPlayedStop) handlePause()
|
||||
}
|
||||
const setStopStatus = () => {
|
||||
setPlay(false)
|
||||
|
@ -226,7 +226,7 @@ export default ({ setting }) => {
|
|||
return
|
||||
}
|
||||
|
||||
if (playInfo.musicInfo) {
|
||||
if (playMusicInfo.musicInfo) {
|
||||
setPlayerStop()
|
||||
window.eventHub.emit(eventPlayerNames.pause)
|
||||
setStopStatus()
|
||||
|
@ -279,20 +279,27 @@ export default ({ setting }) => {
|
|||
name: musicInfo.name,
|
||||
album: musicInfo.albumName,
|
||||
})
|
||||
setTitle(`${musicInfo.name} - ${musicInfo.singer}`)
|
||||
}
|
||||
|
||||
const handelStop = () => {
|
||||
setPlayerStop()
|
||||
setPlayMusicInfo(playMusicInfo.listId, null)
|
||||
window.eventHub.emit(eventPlayerNames.stop)
|
||||
}
|
||||
|
||||
const handleEnded = () => {
|
||||
setAllStatus(t('player__end'))
|
||||
|
||||
if (global.isPlayedStop) return
|
||||
playNext()
|
||||
}
|
||||
|
||||
// 播放、暂停播放切换
|
||||
const handleTogglePlay = async() => {
|
||||
const handlePause = () => {
|
||||
setPlayerPause()
|
||||
}
|
||||
|
||||
const handlePlay = async() => {
|
||||
if (playMusicInfo.musicInfo == null) return
|
||||
if (isPlayerEmpty()) {
|
||||
if (playMusicInfo.listId == 'download') {
|
||||
|
@ -313,13 +320,24 @@ export default ({ setting }) => {
|
|||
}
|
||||
return
|
||||
}
|
||||
setPlayerPlay()
|
||||
}
|
||||
|
||||
// 播放、暂停播放切换
|
||||
const handleTogglePlay = () => {
|
||||
if (global.isPlayedStop) global.isPlayedStop = false
|
||||
if (isPlay.value) {
|
||||
setPlayerPause()
|
||||
handlePause()
|
||||
} else {
|
||||
setPlayerPlay()
|
||||
handlePlay()
|
||||
}
|
||||
}
|
||||
|
||||
const handlePlayedStop = () => {
|
||||
clearDelayNextTimeout()
|
||||
clearLoadTimeout()
|
||||
}
|
||||
|
||||
watch(() => setting.value.player.togglePlayMethod, newValue => {
|
||||
setLoopPlay(newValue === 'singleLoop')
|
||||
if (playedList.length) clearPlayedList()
|
||||
|
@ -339,13 +357,16 @@ export default ({ setting }) => {
|
|||
window.eventHub.on(eventPlayerNames.stop, setStopStatus)
|
||||
|
||||
window.eventHub.on(eventPlayerNames.playMusic, playMusic)
|
||||
window.eventHub.on(eventPlayerNames.setPlay, handlePlay)
|
||||
window.eventHub.on(eventPlayerNames.setPause, handlePause)
|
||||
window.eventHub.on(eventPlayerNames.setStop, handelStop)
|
||||
window.eventHub.on(eventPlayerNames.setTogglePlay, handleTogglePlay)
|
||||
window.eventHub.on(eventPlayerNames.setPlayPrev, playPrev)
|
||||
window.eventHub.on(eventPlayerNames.setPlayNext, playNext)
|
||||
window.eventHub.on(eventPlayerNames.setPlayInfo, handleSetPlayInfo)
|
||||
window.eventHub.on(eventPlayerNames.setStop, handelStop)
|
||||
|
||||
window.eventHub.on(eventPlayerNames.player_ended, handleEnded)
|
||||
window.eventHub.on(eventPlayerNames.playedStop, handlePlayedStop)
|
||||
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
|
@ -360,11 +381,14 @@ export default ({ setting }) => {
|
|||
|
||||
window.eventHub.off(eventPlayerNames.playMusic, playMusic)
|
||||
window.eventHub.off(eventPlayerNames.setTogglePlay, handleTogglePlay)
|
||||
window.eventHub.off(eventPlayerNames.setPlay, handlePlay)
|
||||
window.eventHub.off(eventPlayerNames.setPause, handlePause)
|
||||
window.eventHub.off(eventPlayerNames.setStop, handelStop)
|
||||
window.eventHub.off(eventPlayerNames.setPlayPrev, playPrev)
|
||||
window.eventHub.off(eventPlayerNames.setPlayNext, playNext)
|
||||
window.eventHub.off(eventPlayerNames.setPlayInfo, handleSetPlayInfo)
|
||||
window.eventHub.off(eventPlayerNames.setStop, handelStop)
|
||||
|
||||
window.eventHub.off(eventPlayerNames.player_ended, handleEnded)
|
||||
window.eventHub.off(eventPlayerNames.playedStop, handlePlayedStop)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
import { onBeforeUnmount, useCommit } from '@renderer/utils/vueTools'
|
||||
import { player as eventPlayerNames, taskbar as eventTaskbarNames } from '@renderer/event/names'
|
||||
import { onTaskbarThumbarClick, setTaskbarThumbnailClip, setTaskbarThumbarButtons } from '@renderer/utils/tools'
|
||||
// import store from '@renderer/store'
|
||||
|
||||
import { loveList, getList } from '@renderer/core/share/list'
|
||||
import { playMusicInfo } from '@renderer/core/share/player'
|
||||
|
||||
export default () => {
|
||||
const listAdd = useCommit('list', 'listAdd')
|
||||
const listRemove = useCommit('list', 'listRemove')
|
||||
// const setVisibleDesktopLyric = useCommit('setVisibleDesktopLyric')
|
||||
// const setLockDesktopLyric = useCommit('setLockDesktopLyric')
|
||||
|
||||
const buttons = {
|
||||
empty: true,
|
||||
collect: false,
|
||||
play: false,
|
||||
prev: true,
|
||||
next: true,
|
||||
lrc: false,
|
||||
lockLrc: false,
|
||||
}
|
||||
const setButtons = () => {
|
||||
setTaskbarThumbarButtons(buttons)
|
||||
}
|
||||
const updateCollectStatus = () => {
|
||||
let status = !!playMusicInfo.musicInfo && getList(loveList.id).some(musicInfo => playMusicInfo.musicInfo.songmid == musicInfo.songmid)
|
||||
if (buttons.collect == status) return false
|
||||
buttons.collect = status
|
||||
return true
|
||||
}
|
||||
|
||||
const handlePlay = () => {
|
||||
if (buttons.empty) buttons.empty = false
|
||||
buttons.play = true
|
||||
setButtons()
|
||||
}
|
||||
const handlePause = () => {
|
||||
if (buttons.empty) buttons.empty = false
|
||||
buttons.play = false
|
||||
setButtons()
|
||||
}
|
||||
const handleStop = async() => {
|
||||
if (playMusicInfo.musicInfo != null) return
|
||||
if (buttons.collect) buttons.collect = false
|
||||
buttons.empty = true
|
||||
setButtons()
|
||||
}
|
||||
const handleSetPlayInfo = () => {
|
||||
if (!updateCollectStatus()) return
|
||||
setButtons()
|
||||
}
|
||||
const handleSetTaskbarThumbnailClip = (clip) => {
|
||||
setTaskbarThumbnailClip(clip)
|
||||
}
|
||||
// const updateSetting = () => {
|
||||
// const setting = store.getters.setting
|
||||
// buttons.lrc = setting.desktopLyric.enable
|
||||
// buttons.lockLrc = setting.desktopLyric.isLock
|
||||
// setButtons()
|
||||
// }
|
||||
const rTaskbarThumbarClick = onTaskbarThumbarClick((event, action) => {
|
||||
switch (action) {
|
||||
case 'play':
|
||||
window.eventHub.emit(eventPlayerNames.setPlay)
|
||||
break
|
||||
case 'pause':
|
||||
window.eventHub.emit(eventPlayerNames.setPause)
|
||||
break
|
||||
case 'prev':
|
||||
window.eventHub.emit(eventPlayerNames.setPlayPrev)
|
||||
break
|
||||
case 'next':
|
||||
window.eventHub.emit(eventPlayerNames.setPlayNext)
|
||||
break
|
||||
case 'collect':
|
||||
if (!playMusicInfo.musicInfo) return
|
||||
listAdd({ id: loveList.id, musicInfo: playMusicInfo.musicInfo })
|
||||
if (updateCollectStatus()) setButtons()
|
||||
break
|
||||
case 'unCollect':
|
||||
if (!playMusicInfo.musicInfo) return
|
||||
listRemove({ listId: loveList.id, id: playMusicInfo.musicInfo.songmid })
|
||||
if (updateCollectStatus()) setButtons()
|
||||
break
|
||||
// case 'lrc':
|
||||
// setVisibleDesktopLyric(true)
|
||||
// updateSetting()
|
||||
// break
|
||||
// case 'unLrc':
|
||||
// setVisibleDesktopLyric(false)
|
||||
// updateSetting()
|
||||
// break
|
||||
// case 'lockLrc':
|
||||
// setLockDesktopLyric(true)
|
||||
// updateSetting()
|
||||
// break
|
||||
// case 'unlockLrc':
|
||||
// setLockDesktopLyric(false)
|
||||
// updateSetting()
|
||||
// break
|
||||
}
|
||||
})
|
||||
window.eventHub.on(eventPlayerNames.play, handlePlay)
|
||||
window.eventHub.on(eventPlayerNames.pause, handlePause)
|
||||
window.eventHub.on(eventPlayerNames.stop, handleStop)
|
||||
window.eventHub.on(eventPlayerNames.setPlayInfo, handleSetPlayInfo)
|
||||
window.eventHub.on(eventTaskbarNames.setTaskbarThumbnailClip, handleSetTaskbarThumbnailClip)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
rTaskbarThumbarClick()
|
||||
window.eventHub.off(eventPlayerNames.play, handlePlay)
|
||||
window.eventHub.off(eventPlayerNames.pause, handlePause)
|
||||
window.eventHub.off(eventPlayerNames.stop, handleStop)
|
||||
window.eventHub.off(eventPlayerNames.setPlayInfo, handleSetPlayInfo)
|
||||
window.eventHub.off(eventTaskbarNames.setTaskbarThumbnailClip, handleSetTaskbarThumbnailClip)
|
||||
})
|
||||
|
||||
return () => {
|
||||
// const setting = store.getters.setting
|
||||
// buttons.lrc = setting.desktopLyric.enable
|
||||
// buttons.lockLrc = setting.desktopLyric.isLock
|
||||
updateCollectStatus()
|
||||
if (playMusicInfo.musicInfo != null) buttons.empty = false
|
||||
setButtons()
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ export default ({ playNext }) => {
|
|||
|
||||
const { playIndex } = updatePlayIndex()
|
||||
if (playIndex < 0 && !playMusicInfo.isTempPlay) { // 歌曲被移除
|
||||
if (getList(playMusicInfo.listId).length) {
|
||||
if (getList(playMusicInfo.listId).length && !global.isPlayedStop) {
|
||||
playNext()
|
||||
} else {
|
||||
window.eventHub.emit(eventPlayerNames.setStop)
|
||||
|
|
|
@ -15,6 +15,7 @@ const names = {
|
|||
},
|
||||
player: {
|
||||
setTogglePlay: 'setTogglePlay', // 播放/暂停切换
|
||||
setPlay: 'setPlay', // 播放
|
||||
setPause: 'setPause', // 暂停
|
||||
setStop: 'setStop', // 停止
|
||||
setPlayPrev: 'setPlayPrev', // 上一曲
|
||||
|
@ -26,11 +27,12 @@ const names = {
|
|||
|
||||
playMusic: 'playMusic',
|
||||
|
||||
setPlayInfo: 'setPlayInfo',
|
||||
updatePic: 'updatePic',
|
||||
updateLyric: 'updateLyric',
|
||||
setPlayInfo: 'setPlayInfo', // 设置播放信息
|
||||
updatePic: 'updatePic', // 更新图片事件
|
||||
updateLyric: 'updateLyric', // 更新歌词事件
|
||||
|
||||
activeTransition: 'activeTransition', // 激活进度条动画事件
|
||||
playedStop: 'playedStop', // 定时停止事件
|
||||
|
||||
// 播放器事件
|
||||
play: 'play',
|
||||
|
@ -64,6 +66,9 @@ const names = {
|
|||
send_sync_list: 'send_sync_list',
|
||||
handle_sync_list: 'handle_sync_list',
|
||||
},
|
||||
taskbar: {
|
||||
setTaskbarThumbnailClip: 'setTaskbarThumbnailClip',
|
||||
},
|
||||
}
|
||||
|
||||
for (const item of Object.keys(names)) {
|
||||
|
@ -78,3 +83,4 @@ export const player = names.player
|
|||
export const list = names.list
|
||||
export const download = names.download
|
||||
export const sync = names.sync
|
||||
export const taskbar = names.taskbar
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<Modal :show="visible" @close="handleCancel" @after-leave="afterLeave" :closeBtn="false" :teleport="teleport">
|
||||
<main :class="$style.main">{{message}}</main>
|
||||
<main class="scroll" :class="[$style.main, { 'select': selection }]">{{message}}</main>
|
||||
<footer :class="$style.footer">
|
||||
<Btn :class="$style.btn" v-if="showCancel" @click="handleCancel">{{cancelBtnText}}</Btn>
|
||||
<Btn :class="$style.btn" @click="handleComfirm">{{confirmBtnText}}</Btn>
|
||||
|
@ -30,6 +30,7 @@ export default {
|
|||
cancelButtonText: '',
|
||||
confirmButtonText: '',
|
||||
teleport: '#root',
|
||||
selection: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -58,9 +59,9 @@ export default {
|
|||
.main {
|
||||
flex: auto;
|
||||
min-height: 40px;
|
||||
padding: 15px;
|
||||
padding: 15px 15px 0;
|
||||
font-size: 14px;
|
||||
max-width: 320px;
|
||||
// max-width: 320px;
|
||||
min-width: 220px;
|
||||
line-height: 1.5;
|
||||
white-space: pre-line;
|
||||
|
@ -68,7 +69,7 @@ export default {
|
|||
|
||||
.footer {
|
||||
flex: none;
|
||||
padding: 0 15px 15px;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-end;
|
||||
|
|
|
@ -10,10 +10,11 @@ const defaultOptions = {
|
|||
showCancel: false,
|
||||
cancelButtonText: '',
|
||||
confirmButtonText: '',
|
||||
selection: false,
|
||||
}
|
||||
|
||||
export const dialog = function(options) {
|
||||
const { message, showCancel, cancelButtonText, confirmButtonText, teleport } =
|
||||
const { message, showCancel, cancelButtonText, confirmButtonText, teleport, selection } =
|
||||
Object.assign({}, defaultOptions, typeof options == 'string' ? { message: options } : options || {})
|
||||
return new Promise((resolve, reject) => {
|
||||
let app = createApp(Dialog, {
|
||||
|
@ -32,6 +33,7 @@ export const dialog = function(options) {
|
|||
instance.cancelButtonText = cancelButtonText
|
||||
instance.confirmButtonText = confirmButtonText
|
||||
instance.teleport = teleport
|
||||
instance.selection = selection
|
||||
|
||||
// 挂载
|
||||
document.getElementById('container').appendChild(instance.$el)
|
||||
|
|
|
@ -6,6 +6,9 @@ export default {
|
|||
let theme = themes.find(theme => theme.id == state.setting.themeId)
|
||||
return (theme && theme.className) || ''
|
||||
},
|
||||
font(state) {
|
||||
return state.setting.font
|
||||
},
|
||||
themes(state) {
|
||||
return {
|
||||
active: state.setting.themeId,
|
||||
|
|
|
@ -162,6 +162,7 @@ const handleGetLyric = function(musicInfo, retryedSource = [], originMusic) {
|
|||
reqPromise = Promise.reject(err)
|
||||
}
|
||||
return reqPromise.catch(err => {
|
||||
// console.log(err)
|
||||
if (!retryedSource.includes(musicInfo.source)) retryedSource.push(musicInfo.source)
|
||||
return this.dispatch('list/getOtherSource', originMusic).then(otherSource => {
|
||||
console.log('find otherSource', otherSource)
|
||||
|
@ -169,7 +170,7 @@ const handleGetLyric = function(musicInfo, retryedSource = [], originMusic) {
|
|||
for (const item of otherSource) {
|
||||
if (retryedSource.includes(item.source)) continue
|
||||
console.log('try toggle to: ', item.source, item.name, item.singer, item.interval)
|
||||
return getLyric.call(this, item, retryedSource, originMusic)
|
||||
return handleGetLyric.call(this, item, retryedSource, originMusic)
|
||||
}
|
||||
}
|
||||
return Promise.reject(err)
|
||||
|
@ -438,6 +439,14 @@ const actions = {
|
|||
dispatch('startTask')
|
||||
return
|
||||
}
|
||||
if (err.message?.startsWith('Resume failed')) {
|
||||
fs.unlink(downloadInfo.metadata.filePath, err => {
|
||||
if (err) return commit('onError', { downloadInfo, errorMsg: '删除不匹配的文件失败:' + err.message })
|
||||
dls[downloadInfo.key].start()
|
||||
commit('setStatusText', { downloadInfo, text: '正在重试' })
|
||||
})
|
||||
return
|
||||
}
|
||||
if (err.code == 'ENOTFOUND') {
|
||||
commit('onError', { downloadInfo, errorMsg: '链接失效' })
|
||||
refreshUrl.call(_this, commit, downloadInfo, rootState.setting.download.isUseOtherSource)
|
||||
|
@ -561,6 +570,7 @@ const actions = {
|
|||
filePath: path.join(rootState.setting.download.savePath, downloadInfo.metadata.fileName),
|
||||
})
|
||||
dl.updateSaveInfo(rootState.setting.download.savePath, downloadInfo.metadata.fileName)
|
||||
if (tryNum[downloadInfo.key]) tryNum[downloadInfo.key] = 0
|
||||
try {
|
||||
await dl.start()
|
||||
} catch (error) {
|
||||
|
|
|
@ -32,6 +32,7 @@ const state = {
|
|||
}
|
||||
|
||||
const playMusic = () => {
|
||||
if (global.isPlayedStop) global.isPlayedStop = false
|
||||
window.eventHub.emit(eventPlayerNames.playMusic)
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ const getters = {
|
|||
const actions = {
|
||||
getTags({ state, rootState, commit }) {
|
||||
let source = rootState.setting.songList.source
|
||||
return music[source].songList.getTags().then(result => commit('setTags', { tags: result, source }))
|
||||
return music[source]?.songList.getTags().then(result => commit('setTags', { tags: result, source }))
|
||||
},
|
||||
getList({ state, rootState, commit }, page) {
|
||||
let source = rootState.setting.songList.source
|
||||
|
@ -79,7 +79,7 @@ const actions = {
|
|||
if (state.list.list.length && state.list.key == key) return
|
||||
if (cache.has(key)) return Promise.resolve(cache.get(key)).then(result => commit('setList', { result, key, page }))
|
||||
commit('clearList')
|
||||
return music[source].songList.getList(sortId, tabId, page).then(result => commit('setList', { result, key, page }))
|
||||
return music[source]?.songList.getList(sortId, tabId, page).then(result => commit('setList', { result, key, page }))
|
||||
},
|
||||
getListDetail({ state, commit }, { id, source, page, isRefresh = false }) {
|
||||
let key = `sdetail__${source}__${id}__${page}`
|
||||
|
@ -89,7 +89,7 @@ const actions = {
|
|||
return (
|
||||
cache.has(key)
|
||||
? Promise.resolve(cache.get(key))
|
||||
: music[source].songList.getListDetail(id, page).then(result => ({ ...result, list: filterList(result.list) }))
|
||||
: music[source]?.songList.getListDetail(id, page).then(result => ({ ...result, list: filterList(result.list) }))
|
||||
).then(result => {
|
||||
commit('setListDetail', { result, key, source, id, page })
|
||||
return result.list
|
||||
|
@ -102,7 +102,7 @@ const actions = {
|
|||
if (isRefresh && cache.has(key)) cache.delete(key)
|
||||
return cache.has(key)
|
||||
? Promise.resolve(cache.get(key))
|
||||
: music[source].songList.getListDetail(id, page).then(result => {
|
||||
: music[source]?.songList.getListDetail(id, page).then(result => {
|
||||
cache.set(key, result)
|
||||
return result
|
||||
})
|
||||
|
|
|
@ -3,6 +3,7 @@ export default {
|
|||
state.setting.themeId = val
|
||||
},
|
||||
setSearchSource(state, { searchSource, tempSearchSource }) {
|
||||
console.log(searchSource, tempSearchSource)
|
||||
if (searchSource != null) state.setting.search.searchSource = searchSource
|
||||
if (tempSearchSource != null) state.setting.search.tempSearchSource = tempSearchSource
|
||||
},
|
||||
|
|
|
@ -153,7 +153,7 @@ class Task extends EventEmitter {
|
|||
__initDownload(response) {
|
||||
this.progress.total = parseInt(response.headers['content-length'] || 0)
|
||||
let options = {}
|
||||
let isResumable = this.options.forceResume || response.headers['accept-ranges'] !== 'none'
|
||||
let isResumable = this.options.forceResume || response.headers['accept-ranges'] !== 'none' || (typeof response.headers['accept-ranges'] == 'string' && parseInt(response.headers['accept-ranges'].replace(/^bytes=(\d+)/, '$1')) > 0)
|
||||
if (isResumable) {
|
||||
options.flags = 'a'
|
||||
if (this.progress.downloaded) this.progress.total -= 10
|
||||
|
@ -227,8 +227,9 @@ class Task extends EventEmitter {
|
|||
if (this.resumeLastChunk) {
|
||||
chunk = this.__handleDiffChunk(chunk)
|
||||
if (!chunk) {
|
||||
this.__handleError(new Error('Resume failed, response chunk does not match.'))
|
||||
this.stop()
|
||||
this.__handleStop().finally(() => {
|
||||
this.__handleError(new Error('Resume failed, response chunk does not match.'))
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ const easeInOutQuad = (t, b, c, d) => {
|
|||
t--
|
||||
return (-c / 2) * (t * (t - 2) - 1) + b
|
||||
}
|
||||
const handleScroll = (element, to, duration = 300, fn = () => {}) => {
|
||||
const handleScrollY = (element, to, duration = 300, fn = () => {}) => {
|
||||
if (!element) return fn()
|
||||
const start = element.scrollTop || element.scrollY || 0
|
||||
let cancel = false
|
||||
|
@ -148,10 +148,72 @@ export const scrollTo = (element, to, duration = 300, fn = () => {}, delay = 0)
|
|||
}
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null
|
||||
scrollCancelFn = handleScroll(element, to, duration, fn, delay)
|
||||
scrollCancelFn = handleScrollY(element, to, duration, fn, delay)
|
||||
}, delay)
|
||||
} else {
|
||||
cancelFn = handleScroll(element, to, duration, fn, delay)
|
||||
cancelFn = handleScrollY(element, to, duration, fn, delay)
|
||||
}
|
||||
return cancelFn
|
||||
}
|
||||
const handleScrollX = (element, to, duration = 300, fn = () => {}) => {
|
||||
if (!element) return fn()
|
||||
const start = element.scrollLeft || element.scrollX || 0
|
||||
let cancel = false
|
||||
if (to > start) {
|
||||
let maxScrollLeft = element.scrollWidth - element.clientWidth
|
||||
if (to > maxScrollLeft) to = maxScrollLeft
|
||||
} else if (to < start) {
|
||||
if (to < 0) to = 0
|
||||
} else return fn()
|
||||
const change = to - start
|
||||
const increment = 10
|
||||
if (!change) return fn()
|
||||
|
||||
let currentTime = 0
|
||||
let val
|
||||
|
||||
const animateScroll = () => {
|
||||
currentTime += increment
|
||||
val = parseInt(easeInOutQuad(currentTime, start, change, duration))
|
||||
if (element.scrollTo) {
|
||||
element.scrollTo(val, 0)
|
||||
} else {
|
||||
element.scrollLeft = val
|
||||
}
|
||||
if (currentTime < duration) {
|
||||
if (cancel) return fn()
|
||||
setTimeout(animateScroll, increment)
|
||||
} else {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
animateScroll()
|
||||
return () => {
|
||||
cancel = true
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置滚动条位置
|
||||
* @param {*} element 要设置滚动的容器 dom
|
||||
* @param {*} to 滚动的目标位置
|
||||
* @param {*} duration 滚动完成时间 ms
|
||||
* @param {*} fn 滚动完成后的回调
|
||||
* @param {*} delay 延迟执行时间
|
||||
*/
|
||||
export const scrollXTo = (element, to, duration = 300, fn = () => {}, delay = 0) => {
|
||||
let cancelFn
|
||||
let timeout
|
||||
if (delay) {
|
||||
let scrollCancelFn
|
||||
cancelFn = () => {
|
||||
timeout == null ? scrollCancelFn && scrollCancelFn() : clearTimeout(timeout)
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null
|
||||
scrollCancelFn = handleScrollX(element, to, duration, fn, delay)
|
||||
}, delay)
|
||||
} else {
|
||||
cancelFn = handleScrollX(element, to, duration, fn, delay)
|
||||
}
|
||||
return cancelFn
|
||||
}
|
||||
|
|
|
@ -193,4 +193,8 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
getDetailPageUrl(id) {
|
||||
if (typeof id == 'string') id = id.replace('kg__', '')
|
||||
return `https://www.kugou.com/yy/rank/home/1-${id}.html`
|
||||
},
|
||||
}
|
||||
|
|
|
@ -718,6 +718,11 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
|
||||
getDetailPageUrl(id) {
|
||||
if (typeof id == 'string') id = id.replace('id_', '')
|
||||
return `https://www.kugou.com/yy/special/single/${id}.html`
|
||||
},
|
||||
}
|
||||
|
||||
// getList
|
||||
|
|
|
@ -191,4 +191,8 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
|
||||
// getDetailPageUrl(id) {
|
||||
// return `http://www.kuwo.cn/rankList/${id}`
|
||||
// },
|
||||
}
|
||||
|
|
|
@ -307,6 +307,13 @@ export default {
|
|||
getTags() {
|
||||
return Promise.all([this.getTag(), this.getHotTag()]).then(([tags, hotTag]) => ({ tags, hotTag, source: 'kw' }))
|
||||
},
|
||||
getDetailPageUrl(id) {
|
||||
if (/^digest-/.test(id)) {
|
||||
let result = id.split('__')
|
||||
id = result[1]
|
||||
}
|
||||
return `http://www.kuwo.cn/playlist_detail/${id}`
|
||||
},
|
||||
}
|
||||
|
||||
// getList
|
||||
|
|
|
@ -4,8 +4,7 @@ import { sizeFormate } from '../../index'
|
|||
|
||||
// const boardList = [{ id: 'mg__27553319', name: '咪咕尖叫新歌榜', bangid: '27553319' }, { id: 'mg__27186466', name: '咪咕尖叫热歌榜', bangid: '27186466' }, { id: 'mg__27553408', name: '咪咕尖叫原创榜', bangid: '27553408' }, { id: 'mg__23189800', name: '咪咕港台榜', bangid: '23189800' }, { id: 'mg__23189399', name: '咪咕内地榜', bangid: '23189399' }, { id: 'mg__19190036', name: '咪咕欧美榜', bangid: '19190036' }, { id: 'mg__23189813', name: '咪咕日韩榜', bangid: '23189813' }, { id: 'mg__23190126', name: '咪咕彩铃榜', bangid: '23190126' }, { id: 'mg__15140045', name: '咪咕KTV榜', bangid: '15140045' }, { id: 'mg__15140034', name: '咪咕网络榜', bangid: '15140034' }, { id: 'mg__23217754', name: 'MV榜', bangid: '23217754' }, { id: 'mg__23218151', name: '新专辑榜', bangid: '23218151' }, { id: 'mg__21958042', name: 'iTunes榜', bangid: '21958042' }, { id: 'mg__21975570', name: 'billboard榜', bangid: '21975570' }, { id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815' }, { id: 'mg__22272904', name: '中国TOP排行榜', bangid: '22272904' }, { id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943' }, { id: 'mg__22273437', name: '英国UK榜', bangid: '22273437' }]
|
||||
|
||||
const boardList = [{ id: 'mg__27553319', name: '尖叫新歌榜', bangid: '27553319' }, { id: 'mg__27186466', name: '尖叫热歌榜', bangid: '27186466' }, { id: 'mg__27553408', name: '尖叫原创榜', bangid: '27553408' }, { id: 'mg__23189800', name: '港台榜', bangid: '23189800' }, { id: 'mg__23189399', name: '内地榜', bangid: '23189399' }, { id: 'mg__19190036', name: '欧美榜', bangid: '19190036' }, { id: 'mg__23189813', name: '日韩榜', bangid: '23189813' }, { id: 'mg__23190126', name: '彩铃榜', bangid: '23190126' }, { id: 'mg__15140045', name: 'KTV榜', bangid: '15140045' }, { id: 'mg__15140034', name: '网络榜', bangid: '15140034' }, { id: 'mg__21958042', name: '美国iTunes榜', bangid: '21958042' }, { id: 'mg__21975570', name: '美国billboard榜', bangid: '21975570' }, { id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815' }, { id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943' }, { id: 'mg__22273437', name: '英国UK榜', bangid: '22273437' }]
|
||||
|
||||
const boardList = [{ id: 'mg__27553319', name: '尖叫新歌榜', bangid: '27553319', webId: 'jianjiao_newsong' }, { id: 'mg__27186466', name: '尖叫热歌榜', bangid: '27186466', webId: 'jianjiao_hotsong' }, { id: 'mg__27553408', name: '尖叫原创榜', bangid: '27553408', webId: 'jianjiao_original' }, { id: 'mg__23189800', name: '港台榜', bangid: '23189800', webId: 'hktw' }, { id: 'mg__23189399', name: '内地榜', bangid: '23189399', webId: 'mainland' }, { id: 'mg__19190036', name: '欧美榜', bangid: '19190036', webId: 'eur_usa' }, { id: 'mg__23189813', name: '日韩榜', bangid: '23189813', webId: 'jpn_kor' }, { id: 'mg__23190126', name: '彩铃榜', bangid: '23190126', webId: 'coloring' }, { id: 'mg__15140045', name: 'KTV榜', bangid: '15140045', webId: 'ktv' }, { id: 'mg__15140034', name: '网络榜', bangid: '15140034', webId: 'network' }, { id: 'mg__21958042', name: '美国iTunes榜', bangid: '21958042', webId: 'itunes' }, { id: 'mg__21975570', name: '美国billboard榜', bangid: '21975570', webId: 'billboard' }, { id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815', webId: 'hito' }, { id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943', webId: 'mnet' }, { id: 'mg__22273437', name: '英国UK榜', bangid: '22273437', webId: 'uk' }]
|
||||
export default {
|
||||
limit: 200,
|
||||
list: [
|
||||
|
@ -216,4 +215,14 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
|
||||
getDetailPageUrl(id) {
|
||||
if (typeof id == 'string') id = id.replace('mg__', '')
|
||||
for (const item of boardList) {
|
||||
if (item.bangid == id) {
|
||||
return `https://music.migu.cn/v3/music/top/${item.webId}`
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
}
|
||||
|
|
|
@ -355,6 +355,10 @@ export default {
|
|||
getTags() {
|
||||
return this.getTag()
|
||||
},
|
||||
|
||||
getDetailPageUrl(id) {
|
||||
return `https://music.migu.cn/v3/music/playlist/${id}`
|
||||
},
|
||||
}
|
||||
|
||||
// getList
|
||||
|
|
|
@ -237,4 +237,9 @@ export default {
|
|||
})
|
||||
})
|
||||
},
|
||||
|
||||
getDetailPageUrl(id) {
|
||||
if (typeof id == 'string') id = id.replace('tx__', '')
|
||||
return `https://y.qq.com/n/ryqq/toplist/${id}`
|
||||
},
|
||||
}
|
||||
|
|
|
@ -32,7 +32,10 @@ export default {
|
|||
},
|
||||
handleResult(rawList) {
|
||||
// console.log(rawList)
|
||||
return rawList.map(item => {
|
||||
const list = []
|
||||
rawList.forEach(item => {
|
||||
if (!item.strMediaMid) return
|
||||
|
||||
let types = []
|
||||
let _types = {}
|
||||
if (item.size128 !== 0) {
|
||||
|
@ -64,7 +67,7 @@ export default {
|
|||
}
|
||||
}
|
||||
// types.reverse()
|
||||
return {
|
||||
list.push({
|
||||
singer: this.getSinger(item.singer),
|
||||
name: item.songname,
|
||||
albumName: item.albumname,
|
||||
|
@ -83,8 +86,9 @@ export default {
|
|||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
}
|
||||
})
|
||||
})
|
||||
return list
|
||||
},
|
||||
search(str, page = 1, { limit } = {}) {
|
||||
if (limit == null) limit = this.limit
|
||||
|
|
|
@ -292,6 +292,10 @@ export default {
|
|||
getTags() {
|
||||
return Promise.all([this.getTag(), this.getHotTag()]).then(([tags, hotTag]) => ({ tags, hotTag, source: 'tx' }))
|
||||
},
|
||||
|
||||
getDetailPageUrl(id) {
|
||||
return `https://y.qq.com/n/ryqq/playlist/${id}`
|
||||
},
|
||||
}
|
||||
|
||||
// getList
|
||||
|
|
|
@ -199,4 +199,9 @@ export default {
|
|||
source: 'wy',
|
||||
}
|
||||
},
|
||||
|
||||
getDetailPageUrl(id) {
|
||||
if (typeof id == 'string') id = id.replace('wy__', '')
|
||||
return `https://music.163.com/#/discover/toplist?id=${id}`
|
||||
},
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ export default songmid => {
|
|||
if (body.code !== 200 || !body?.lrc?.lyric) return Promise.reject(new Error('Get lyric failed'))
|
||||
return {
|
||||
lyric: body.lrc.lyric,
|
||||
tlyric: body.tlyric.lyric,
|
||||
tlyric: body.tlyric?.lyric ?? '',
|
||||
// lxlyric: parseLyric(body.klyric.lyric),
|
||||
}
|
||||
})
|
||||
|
|
|
@ -289,6 +289,10 @@ export default {
|
|||
getTags() {
|
||||
return Promise.all([this.getTag(), this.getHotTag()]).then(([tags, hotTag]) => ({ tags, hotTag, source: 'wy' }))
|
||||
},
|
||||
|
||||
getDetailPageUrl(id) {
|
||||
return `https://music.163.com/#/playlist?id=${id}`
|
||||
},
|
||||
}
|
||||
|
||||
// getList
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
import { ref, computed } from '@renderer/utils/vueTools'
|
||||
import { rendererSend, rendererOn, NAMES } from '@common/ipc'
|
||||
import { isPlay } from '@renderer/core/share/player'
|
||||
import store from '@renderer/store'
|
||||
import { player as eventPlayerNames } from '@renderer/event/names'
|
||||
|
||||
global.isPlayedStop = false
|
||||
|
||||
const time = ref(-1)
|
||||
|
||||
|
||||
const timeoutTools = {
|
||||
inited: false,
|
||||
isRunning: false,
|
||||
timeout: null,
|
||||
time: -1,
|
||||
id: 'play__stop__timeout',
|
||||
exit() {
|
||||
const setting = store.getters.setting
|
||||
global.isPlayedStop = true
|
||||
if (!setting.player.waitPlayEndStop && isPlay.value) {
|
||||
window.eventHub.emit(eventPlayerNames.setPause)
|
||||
}
|
||||
},
|
||||
clearTimeout() {
|
||||
rendererSend(NAMES.mainWindow.interval_cancel, this.id)
|
||||
if (!this.isRunning) return
|
||||
this.time = -1
|
||||
time.value = -1
|
||||
this.isRunning = false
|
||||
},
|
||||
start(_time) {
|
||||
this.clearTimeout()
|
||||
this.time = _time
|
||||
time.value = _time
|
||||
this.isRunning = true
|
||||
rendererSend(NAMES.mainWindow.interval, {
|
||||
time: 1000,
|
||||
id: this.id,
|
||||
})
|
||||
},
|
||||
init() {
|
||||
if (this.inited) return
|
||||
this.clearTimeout()
|
||||
rendererOn(NAMES.mainWindow.interval_callback, (event, id) => {
|
||||
if (id !== this.id) return
|
||||
|
||||
if (this.time > 0) {
|
||||
this.time--
|
||||
time.value--
|
||||
} else {
|
||||
this.clearTimeout()
|
||||
this.exit()
|
||||
}
|
||||
})
|
||||
this.inited = true
|
||||
},
|
||||
}
|
||||
|
||||
export const init = () => {
|
||||
timeoutTools.init()
|
||||
}
|
||||
|
||||
export const startTimeoutStop = time => {
|
||||
if (global.isPlayedStop) global.isPlayedStop = false
|
||||
timeoutTools.start(time)
|
||||
}
|
||||
export const stopTimeoutStop = () => {
|
||||
if (global.isPlayedStop) global.isPlayedStop = false
|
||||
timeoutTools.clearTimeout()
|
||||
}
|
||||
|
||||
const formatTime = time => {
|
||||
// let d = parseInt(time / 86400)
|
||||
// d = d ? d.toString() + ':' : ''
|
||||
// time = time % 86400
|
||||
let h = parseInt(time / 3600)
|
||||
h = h ? h.toString() + ':' : ''
|
||||
time = time % 3600
|
||||
const m = parseInt(time / 60).toString().padStart(2, '0')
|
||||
const s = parseInt(time % 60).toString().padStart(2, '0')
|
||||
return `${h}${m}:${s}`
|
||||
}
|
||||
export const useTimeout = () => {
|
||||
const timeLabel = computed(() => {
|
||||
return time.value > 0 ? formatTime(time.value) : ''
|
||||
})
|
||||
|
||||
return {
|
||||
time,
|
||||
timeLabel,
|
||||
}
|
||||
}
|
|
@ -13,6 +13,17 @@ export const setUserApi = source => {
|
|||
})
|
||||
}
|
||||
|
||||
export const onShowUserApiUpdateAlert = callback => {
|
||||
rendererOn(NAMES.mainWindow.user_api_show_update_alert, callback)
|
||||
return () => {
|
||||
rendererOff(callback)
|
||||
}
|
||||
}
|
||||
|
||||
export const setAllowShowUserApiUpdateAlert = (id, enable) => {
|
||||
return rendererInvoke(NAMES.mainWindow.user_api_set_allow_update_alert, { id, enable })
|
||||
}
|
||||
|
||||
export const saveMyList = data => {
|
||||
rendererSend(NAMES.mainWindow.save_playlist, {
|
||||
type: 'myList',
|
||||
|
@ -105,7 +116,7 @@ export const getSearchHistoryList = () => {
|
|||
}
|
||||
|
||||
export const onUserApiStatus = callback => {
|
||||
rendererOn(NAMES.mainWindow.user_api_status, (event, { status, message, apiInfo }) => callback({ status, message, apiInfo }))
|
||||
rendererOn(NAMES.mainWindow.user_api_status, callback)
|
||||
return () => {
|
||||
rendererOff(callback)
|
||||
}
|
||||
|
@ -133,7 +144,7 @@ export const setDesktopLyricInfo = (type, data, info) => {
|
|||
})
|
||||
}
|
||||
export const onGetDesktopLyricInfo = callback => {
|
||||
rendererOn(NAMES.mainWindow.get_lyric_info, (event, info) => callback(event, info))
|
||||
rendererOn(NAMES.mainWindow.get_lyric_info, callback)
|
||||
return () => {
|
||||
rendererOff(callback)
|
||||
}
|
||||
|
@ -299,3 +310,16 @@ export const hotKeySetConfig = (config) => {
|
|||
export const hotKeyGetStatus = () => {
|
||||
return rendererInvoke(NAMES.hotKey.status)
|
||||
}
|
||||
|
||||
export const onTaskbarThumbarClick = callback => {
|
||||
rendererOn(NAMES.mainWindow.taskbar_on_thumbar_button_click, callback)
|
||||
return () => {
|
||||
rendererOff(callback)
|
||||
}
|
||||
}
|
||||
export const setTaskbarThumbnailClip = (clip) => {
|
||||
return rendererInvoke(NAMES.mainWindow.taskbar_set_thumbnail_clip, clip)
|
||||
}
|
||||
export const setTaskbarThumbarButtons = (buttons) => {
|
||||
rendererSend(NAMES.mainWindow.taskbar_set_thumbar_buttons, buttons)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<material-online-list
|
||||
ref="songList"
|
||||
@show-menu="hideListsMenu"
|
||||
@play-list="handlePlayList"
|
||||
@toggle-page="handleGetList"
|
||||
:rowWidth="{r1: '5%', r2: 'auto', r3: '22%', r4: '22%', r5: '9%', r6: '15%'}"
|
||||
:page="page"
|
||||
|
@ -193,7 +194,6 @@ export default {
|
|||
case 'play':
|
||||
this.playSongListDetail({
|
||||
boardId: board.id,
|
||||
list: [...this.list],
|
||||
id,
|
||||
})
|
||||
break
|
||||
|
@ -220,12 +220,13 @@ export default {
|
|||
sourceListId: `board__${boardId}`,
|
||||
})
|
||||
},
|
||||
async playSongListDetail({ boardId, id, list }) {
|
||||
async playSongListDetail({ boardId, id, index = 0 }) {
|
||||
let isPlayingList = false
|
||||
const list = this.tabId == boardId ? [...this.list] : null
|
||||
if (list?.length) {
|
||||
this.setTempList({
|
||||
list,
|
||||
index: 0,
|
||||
index,
|
||||
id,
|
||||
})
|
||||
isPlayingList = true
|
||||
|
@ -242,11 +243,18 @@ export default {
|
|||
} else {
|
||||
this.setTempList({
|
||||
list: fullList,
|
||||
index: 0,
|
||||
index,
|
||||
id,
|
||||
})
|
||||
}
|
||||
},
|
||||
handlePlayList(index) {
|
||||
this.playSongListDetail({
|
||||
boardId: this.tabId,
|
||||
id: `board__${this.source}__${this.tabId}`,
|
||||
index,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
div(:class="$style.search")
|
||||
//- transition
|
||||
div(:class="$style.header")
|
||||
base-tab(:class="$style.tab" :list="sources" align="left" item-key="id" item-name="name" v-model="searchSourceId")
|
||||
base-tab(:class="$style.tab" :list="sources" align="left" item-key="id" item-name="name" @change="handleSourceChange" v-model="searchSourceId")
|
||||
div(:class="$style.main")
|
||||
div(:class="$style.list" v-show="isLoading || listInfo.list.length")
|
||||
div.thead(:class="$style.thead")
|
||||
|
@ -109,12 +109,28 @@ export default {
|
|||
isLoading: false,
|
||||
}
|
||||
},
|
||||
beforeRouteUpdate(to, from, next) {
|
||||
if (to.query.text === undefined) return
|
||||
this.text = to.query.text
|
||||
this.page = 1
|
||||
this.handleSearch(this.text, this.page)
|
||||
next()
|
||||
beforeRouteUpdate(to, from) {
|
||||
if (to.query.source && (this.sourceList[to.query.source] || to.query.source == 'all')) {
|
||||
if (this.setting.search.searchSource != to.query.source) {
|
||||
this.setSearchSource({
|
||||
searchSource: to.query.source,
|
||||
})
|
||||
}
|
||||
if (this.searchSourceId != to.query.source) {
|
||||
this.searchSourceId = to.query.source
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.handleGetHotSearch()
|
||||
})
|
||||
}
|
||||
if (to.query.text != null && this.text != to.query.text) {
|
||||
this.text = to.query.text
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.page = 1
|
||||
this.handleSearch(this.text, this.page)
|
||||
})
|
||||
},
|
||||
created() {
|
||||
this.listenEvent()
|
||||
|
@ -124,18 +140,20 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
// console.log('mounted')
|
||||
|
||||
// 处理搜索源不存在时页面报错的问题
|
||||
if (!this.sourceList[this.setting.search.searchSource] && this.setting.search.searchSource != 'all') {
|
||||
if (this.$route.query.source && (this.sourceList[this.$route.query.source] || this.$route.query.source == 'all')) {
|
||||
this.setSearchSource({
|
||||
searchSource: this.$route.query.source,
|
||||
})
|
||||
} else if (!this.sourceList[this.setting.search.searchSource] && this.setting.search.searchSource != 'all') { // 处理搜索源不存在时页面报错的问题
|
||||
this.setSearchSource({
|
||||
searchSource: 'kw',
|
||||
})
|
||||
}
|
||||
this.searchSourceId = this.setting.search.searchSource
|
||||
if (this.$route.query.text === undefined) {
|
||||
if (this.$route.query.text == null) {
|
||||
this.text = this.$store.getters['search/searchText']
|
||||
this.page = this.listInfo.page
|
||||
} else if (this.$route.query.text === '') {
|
||||
} else if (this.$route.query.text == '') {
|
||||
this.clearList()
|
||||
} else {
|
||||
this.text = this.$route.query.text
|
||||
|
@ -156,18 +174,6 @@ export default {
|
|||
'listInfo.list'() {
|
||||
this.removeAllSelect()
|
||||
},
|
||||
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)
|
||||
this.handleGetHotSearch()
|
||||
})
|
||||
this.setSearchSource({
|
||||
searchSource: n,
|
||||
})
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'setting']),
|
||||
|
@ -417,7 +423,7 @@ export default {
|
|||
this.getHotSearch(this.setting.search.searchSource)
|
||||
},
|
||||
handleNoitemSearch(text) {
|
||||
this.$router.push({
|
||||
this.$router.replace({
|
||||
path: 'search',
|
||||
query: {
|
||||
text,
|
||||
|
@ -512,6 +518,15 @@ export default {
|
|||
break
|
||||
}
|
||||
},
|
||||
handleSourceChange(source) {
|
||||
this.$router.replace({
|
||||
path: 'search',
|
||||
query: {
|
||||
text: this.text,
|
||||
source,
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<span :class="$style.listsLabel">{{loveList.name}}</span>
|
||||
</li>
|
||||
<li class="user-list"
|
||||
:class="[$style.listsItem, {[$style.active]:item.id == listId}, {[$style.clicked]: listsData.rightClickItemIndex == index}, {[$style.fetching]: fetchingListStatus[item.id]}]" :data-index="index"
|
||||
:class="[$style.listsItem, {[$style.active]: item.id == listId}, {[$style.clicked]: listsData.rightClickItemIndex == index}, {[$style.fetching]: fetchingListStatus[item.id]}]" :data-index="index"
|
||||
@contextmenu="handleListsItemRigthClick($event, index)" :tips="item.name" v-for="(item, index) in userLists" :key="item.id"
|
||||
>
|
||||
<span :class="$style.listsLabel" @click="handleListToggle(item.id, index + 2)">{{item.name}}</span>
|
||||
|
@ -41,7 +41,7 @@
|
|||
|
||||
<script>
|
||||
import { mapMutations, mapActions } from 'vuex'
|
||||
import { openSaveDir, saveLxConfigFile, selectDir, readLxConfigFile, filterFileName } from '@renderer/utils'
|
||||
import { openSaveDir, saveLxConfigFile, selectDir, readLxConfigFile, filterFileName, openUrl } from '@renderer/utils'
|
||||
import musicSdk from '@renderer/utils/music'
|
||||
import DuplicateMusicModal from './DuplicateMusicModal'
|
||||
import ListSortModal from './ListSortModal'
|
||||
|
@ -97,6 +97,7 @@ export default {
|
|||
rename: true,
|
||||
duplicate: true,
|
||||
sort: true,
|
||||
sourceDetail: true,
|
||||
import: true,
|
||||
export: true,
|
||||
sync: false,
|
||||
|
@ -141,6 +142,11 @@ export default {
|
|||
action: 'duplicate',
|
||||
disabled: !this.listsData.itemMenuControl.duplicate,
|
||||
},
|
||||
{
|
||||
name: this.$t('lists__source_detail'),
|
||||
action: 'sourceDetail',
|
||||
disabled: !this.listsData.itemMenuControl.sourceDetail,
|
||||
},
|
||||
{
|
||||
name: this.$t('lists__import'),
|
||||
action: 'import',
|
||||
|
@ -197,8 +203,17 @@ export default {
|
|||
...mapActions('leaderboard', {
|
||||
getBoardListAll: 'getListAll',
|
||||
}),
|
||||
handle_key_mod_down() {
|
||||
handle_key_mod_down(event) {
|
||||
if (!this.keyEvent.isModDown) {
|
||||
// console.log(event)
|
||||
switch (event.event.target.tagName) {
|
||||
case 'INPUT':
|
||||
case 'SELECT':
|
||||
case 'TEXTAREA':
|
||||
return
|
||||
default: if (event.event.target.isContentEditable) return
|
||||
}
|
||||
|
||||
this.keyEvent.isModDown = true
|
||||
this.setDisabledSort(false)
|
||||
const dom_target = this.dom_lists_list.querySelector('.' + this.$style.editing)
|
||||
|
@ -279,6 +294,7 @@ export default {
|
|||
break
|
||||
}
|
||||
this.listsData.itemMenuControl.sort = !!getList(this.getTargetListInfo(index)?.id).length
|
||||
this.listsData.itemMenuControl.sourceDetail = this.assertSupportDetail(source, index)
|
||||
this.listsData.rightClickItemIndex = index
|
||||
this.listsData.menuLocation.x = event.currentTarget.offsetLeft + event.offsetX
|
||||
this.listsData.menuLocation.y = event.currentTarget.offsetTop + event.offsetY - this.dom_lists_list.scrollTop
|
||||
|
@ -313,6 +329,9 @@ export default {
|
|||
this.selectedSortListInfo = this.getTargetListInfo(index)
|
||||
this.isShowListSortModal = true
|
||||
break
|
||||
case 'sourceDetail':
|
||||
this.openSourceDetailPage(index)
|
||||
break
|
||||
case 'import':
|
||||
this.handleImportList(index)
|
||||
break
|
||||
|
@ -373,6 +392,33 @@ export default {
|
|||
}
|
||||
return list
|
||||
},
|
||||
assertSupportDetail(source, index) {
|
||||
if (source) {
|
||||
const { sourceListId } = this.userLists[index]
|
||||
if (sourceListId) {
|
||||
if (/board__/.test(sourceListId)) {
|
||||
// const id = sourceListId.replace(/board__/, '')
|
||||
return !!musicSdk[source]?.leaderboard?.getDetailPageUrl
|
||||
} else {
|
||||
return !!musicSdk[source]?.songList?.getDetailPageUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
openSourceDetailPage(index) {
|
||||
const { source, sourceListId } = this.userLists[index]
|
||||
if (!sourceListId) return
|
||||
let url
|
||||
if (/board__/.test(sourceListId)) {
|
||||
const id = sourceListId.replace(/board__/, '')
|
||||
url = musicSdk[source].leaderboard.getDetailPageUrl(id)
|
||||
} else {
|
||||
url = musicSdk[source].songList.getDetailPageUrl(sourceListId)
|
||||
}
|
||||
if (!url) return
|
||||
openUrl(url)
|
||||
},
|
||||
handleExportList(index) {
|
||||
const list = this.getTargetListInfo(index)
|
||||
if (!list) return
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
<template lang="pug">
|
||||
material-modal(:show="modelValue" bg-close @close="handleCloseModal" @after-enter="$refs.dom_input.focus()" teleport="#view")
|
||||
main(:class="$style.main")
|
||||
h2 {{$t('play_timeout')}}
|
||||
div(:class="$style.content")
|
||||
div(:class="[$style.row, $style.inputGroup]")
|
||||
base-input(:class="$style.input" ref="dom_input" v-model="time" type="number")
|
||||
p(:class="$style.inputLabel") {{$t('play_timeout_unit')}}
|
||||
div(:class="$style.row")
|
||||
base-checkbox(id="play_timeout_end" v-model="currentStting.player.waitPlayEndStop" :label="$t('play_timeout_end')")
|
||||
div(:class="[$style.row, $style.tip, { [$style.show]: !!timeLabel }]")
|
||||
p {{$t('play_timeout_tip', { time: timeLabel })}}
|
||||
div(:class="$style.footer")
|
||||
base-btn(:class="$style.footerBtn" @click="handleCancel") {{$t(timeLabel ? 'play_timeout_stop' : 'play_timeout_close')}}
|
||||
base-btn(:class="$style.footerBtn" @click="handleConfirm") {{$t(timeLabel ? 'play_timeout_update' : 'play_timeout_confirm')}}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { currentStting } from '../setting'
|
||||
import { useTimeout, startTimeoutStop, stopTimeoutStop } from '@renderer/utils/timeoutStop'
|
||||
import { ref } from '@renderer/utils/vueTools'
|
||||
|
||||
const MAX_MIN = 1440
|
||||
|
||||
const rxp = /([1-9]\d*)/
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const { timeLabel } = useTimeout()
|
||||
const time = ref(currentStting.value.player.waitPlayEndStopTime)
|
||||
|
||||
const handleCloseModal = () => {
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
const handleCancel = () => {
|
||||
if (timeLabel.value) {
|
||||
stopTimeoutStop()
|
||||
}
|
||||
handleCloseModal()
|
||||
}
|
||||
const verify = () => {
|
||||
const orgText = time.value
|
||||
let text = time.value
|
||||
|
||||
if (rxp.test(text)) {
|
||||
text = RegExp.$1
|
||||
if (parseInt(text) > MAX_MIN) {
|
||||
text = text.substring(0, text.length - 1)
|
||||
}
|
||||
} else {
|
||||
text = ''
|
||||
}
|
||||
time.value = text
|
||||
return text && orgText == text ? parseInt(text) : ''
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
let time = verify()
|
||||
if (time == '') return
|
||||
currentStting.value.player.waitPlayEndStopTime = time
|
||||
startTimeoutStop(time * 60)
|
||||
handleCloseModal()
|
||||
}
|
||||
return {
|
||||
currentStting,
|
||||
timeLabel,
|
||||
time,
|
||||
handleCloseModal,
|
||||
handleCancel,
|
||||
handleConfirm,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.main {
|
||||
padding: 15px;
|
||||
max-width: 530px;
|
||||
min-width: 280px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
min-height: 0;
|
||||
// max-height: 100%;
|
||||
// overflow: hidden;
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
color: @color-theme_2-font;
|
||||
line-height: 1.3;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
padding-top: 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.row {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.inputGroup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.input {
|
||||
flex: auto;
|
||||
}
|
||||
.inputLabel {
|
||||
flex: none;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.tip {
|
||||
visibility: hidden;
|
||||
|
||||
&.show {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
.footerBtn {
|
||||
flex: auto;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
padding: 0 10px !important;
|
||||
width: 150px;
|
||||
.mixin-ellipsis-1;
|
||||
+ .footerBtn {
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
.ruleLink {
|
||||
.mixin-ellipsis-1;
|
||||
}
|
||||
|
||||
|
||||
each(@themes, {
|
||||
:global(#root.@{value}) {
|
||||
.main {
|
||||
h2 {
|
||||
color: ~'@{color-@{value}-theme_2-font}';
|
||||
}
|
||||
}
|
||||
.listItem {
|
||||
&:hover {
|
||||
background-color: ~'@{color-@{value}-theme_2-hover}';
|
||||
}
|
||||
&.active {
|
||||
background-color: ~'@{color-@{value}-theme_2-active}';
|
||||
}
|
||||
h3 {
|
||||
color: ~'@{color-@{value}-theme_2-font}';
|
||||
}
|
||||
p {
|
||||
color: ~'@{color-@{value}-theme_2-font-label}';
|
||||
}
|
||||
}
|
||||
.noitem {
|
||||
color: ~'@{color-@{value}-theme_2-font-label}';
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
|
@ -9,12 +9,15 @@ dd
|
|||
label {{$t('theme_' + theme.className)}}
|
||||
|
||||
dd
|
||||
.gap-top.top
|
||||
base-checkbox(id="setting_show_animate" v-model="currentStting.isShowAnimation" :label="$t('setting__basic_show_animation')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_animate" v-model="currentStting.randomAnimate" :label="$t('setting__basic_animation')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_to_tray" v-model="currentStting.tray.isShow" :label="$t('setting__basic_to_tray')")
|
||||
div
|
||||
.gap-top.top
|
||||
base-checkbox(id="setting_show_animate" v-model="currentStting.isShowAnimation" :label="$t('setting__basic_show_animation')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_animate" v-model="currentStting.randomAnimate" :label="$t('setting__basic_animation')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_to_tray" v-model="currentStting.tray.isShow" :label="$t('setting__basic_to_tray')")
|
||||
p.gap-top
|
||||
base-btn.btn(min @click="isShowPlayTimeoutModal = true") {{$t('setting__play_timeout')}} {{ timeLabel ? ` (${timeLabel})` : '' }}
|
||||
|
||||
dd(:tips="$t('setting__basic_source_title')")
|
||||
h3#basic_source {{$t('setting__basic_source')}}
|
||||
|
@ -42,12 +45,17 @@ dd(:tips="$t('setting__basic_sourcename_title')")
|
|||
div
|
||||
base-checkbox.gap-left(v-for="item in sourceNameTypes" :key="item.id" :id="`setting_abasic_sourcename_${item.id}`"
|
||||
name="setting_basic_sourcename" need v-model="currentStting.sourceNameType" :value="item.id" :label="item.label")
|
||||
|
||||
dd
|
||||
h3#basic_control_btn_position {{$t('setting__basic_control_btn_position')}}
|
||||
div
|
||||
base-checkbox.gap-left(v-for="item in controlBtnPositionList" :key="item.id" :id="`setting_basic_control_btn_position_${item.id}`"
|
||||
name="setting_basic_control_btn_position" need v-model="currentStting.controlBtnPosition" :value="item.id" :label="item.name")
|
||||
dd
|
||||
h3#basic_font {{$t('setting__basic_font')}}
|
||||
div
|
||||
base-selection.gap-teft(:list="fontList" v-model="currentStting.font" item-key="id" item-name="label")
|
||||
|
||||
play-timeout-modal(v-model="isShowPlayTimeoutModal")
|
||||
user-api-modal(v-model="isShowUserApiModal")
|
||||
</template>
|
||||
|
||||
|
@ -58,12 +66,17 @@ import { langList } from '@/lang'
|
|||
import { currentStting } from '../setting'
|
||||
import { setWindowSize } from '@renderer/utils'
|
||||
import apiSourceInfo from '@renderer/utils/music/api-source-info'
|
||||
import { useTimeout } from '@renderer/utils/timeoutStop'
|
||||
import { getSystemFonts } from '@renderer/utils/tools'
|
||||
|
||||
import PlayTimeoutModal from './PlayTimeoutModal'
|
||||
import UserApiModal from './UserApiModal'
|
||||
|
||||
|
||||
export default {
|
||||
name: 'SettingBasic',
|
||||
components: {
|
||||
PlayTimeoutModal,
|
||||
UserApiModal,
|
||||
},
|
||||
setup() {
|
||||
|
@ -81,6 +94,9 @@ export default {
|
|||
apiSource.value = visible
|
||||
})
|
||||
|
||||
const isShowPlayTimeoutModal = ref(false)
|
||||
const { timeLabel } = useTimeout()
|
||||
|
||||
const isShowUserApiModal = ref(false)
|
||||
const getApiStatus = () => {
|
||||
let status
|
||||
|
@ -134,16 +150,26 @@ export default {
|
|||
]
|
||||
})
|
||||
|
||||
const systemFontList = ref([])
|
||||
const fontList = computed(() => {
|
||||
return [{ id: '', label: t('setting__desktop_lyric_font_default') }, ...systemFontList.value]
|
||||
})
|
||||
getSystemFonts().then(fonts => {
|
||||
systemFontList.value = fonts.map(f => ({ id: f, label: f.replace(/(^"|"$)/g, '') }))
|
||||
})
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
themes,
|
||||
isShowPlayTimeoutModal,
|
||||
timeLabel,
|
||||
apiSources,
|
||||
isShowUserApiModal,
|
||||
windowSizeList,
|
||||
langList,
|
||||
sourceNameTypes,
|
||||
controlBtnPositionList,
|
||||
fontList,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ dd
|
|||
base-checkbox(id="setting_list_showSource_enable" v-model="currentStting.list.isShowSource" :label="$t('setting__list_source')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_list_scroll_enable" v-model="currentStting.list.isSaveScrollLocation" :label="$t('setting__list_scroll')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_list_clickAction_enable" v-model="currentStting.list.isClickPlayList" :label="$t('setting__list_click_action')")
|
||||
dd(:tips="$t('setting__basic_sourcename_title')")
|
||||
h3#list_addMusicLocationType {{$t('setting__list_add_music_location_type')}}
|
||||
div
|
||||
|
|
|
@ -7,6 +7,8 @@ material-modal(:show="modelValue" bg-close @close="handleClose" teleport="#view"
|
|||
div(:class="$style.listLeft")
|
||||
h3 {{api.name}}
|
||||
p {{api.description}}
|
||||
div
|
||||
base-checkbox(:class="$style.checkbox" :id="`user_api_${api.id}`" v-model="api.allowShowUpdateAlert" @change="handleChangeAllowUpdateAlert(api, $event)" :label="$t('user_api__allow_show_update_alert')")
|
||||
base-btn(:class="$style.listBtn" outline :tips="$t('user_api__btn_remove')" @click.stop="handleRemove(index)")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 212.982 212.982' space='preserve' v-once)
|
||||
use(xlink:href='#icon-delete')
|
||||
|
@ -29,6 +31,7 @@ import { promises as fsPromises } from 'fs'
|
|||
import { selectDir, openUrl } from '@renderer/utils'
|
||||
import apiSourceInfo from '@renderer/utils/music/api-source-info'
|
||||
import { userApi, apiSource } from '@renderer/core/share'
|
||||
import { setAllowShowUserApiUpdateAlert } from '@renderer/utils/tools'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
@ -50,6 +53,13 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
handleImport() {
|
||||
if (this.userApi.list.length > 20) {
|
||||
this.$dialog({
|
||||
message: this.$t('user_api__max_tip'),
|
||||
confirmButtonText: this.$t('ok'),
|
||||
})
|
||||
return
|
||||
}
|
||||
selectDir({
|
||||
title: this.$t('user_api__import_file'),
|
||||
properties: ['openFile'],
|
||||
|
@ -84,6 +94,9 @@ export default {
|
|||
handleOpenUrl(url) {
|
||||
openUrl(url)
|
||||
},
|
||||
handleChangeAllowUpdateAlert(api, enable) {
|
||||
setAllowShowUserApiUpdateAlert(api.id, enable)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -114,6 +127,12 @@ export default {
|
|||
color: @color-theme;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin-top: 3px;
|
||||
font-size: 14px;
|
||||
opacity: .86;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: auto;
|
||||
min-height: 100px;
|
||||
|
|
|
@ -8,6 +8,8 @@ export const currentStting = ref({
|
|||
volume: 1,
|
||||
mediaDeviceId: 'default',
|
||||
isMediaDeviceRemovedStopPlay: false,
|
||||
waitPlayEndStop: true,
|
||||
waitPlayEndStopTime: 0,
|
||||
},
|
||||
desktopLyric: {
|
||||
enable: false,
|
||||
|
@ -73,6 +75,7 @@ export const currentStting = ref({
|
|||
langId: 'cns',
|
||||
themeId: 0,
|
||||
sourceId: 0,
|
||||
font: '',
|
||||
isShowAnimation: true,
|
||||
randomAnimate: true,
|
||||
isAgreePact: false,
|
||||
|
|
|
@ -11,10 +11,10 @@ div(:class="$style.container")
|
|||
h3(:title="listDetail.info.name || selectListInfo.name") {{listDetail.info.name || selectListInfo.name}}
|
||||
p(:title="listDetail.info.desc || selectListInfo.desc") {{listDetail.info.desc || selectListInfo.desc}}
|
||||
div(:class="$style.songListHeaderRight")
|
||||
base-btn(:class="$style.headerRightBtn" :disabled="detailLoading" @click="playSongListDetail") {{$t('list__play')}}
|
||||
base-btn(:class="$style.headerRightBtn" :disabled="detailLoading" @click="playSongListDetail()") {{$t('list__play')}}
|
||||
base-btn(:class="$style.headerRightBtn" :disabled="detailLoading" @click="addSongListDetail") {{$t('list__collect')}}
|
||||
base-btn(:class="$style.headerRightBtn" @click="hideListDetail") {{$t('back')}}
|
||||
material-online-list(ref="songList" @toggle-page="handleToggleListDetailPage" :page="listDetail.page" :limit="listDetail.limit" :total="listDetail.total"
|
||||
material-online-list(ref="songList" @play-list="playSongListDetail" @toggle-page="handleToggleListDetailPage" :page="listDetail.page" :limit="listDetail.limit" :total="listDetail.total"
|
||||
:list="listDetail.list" :noItem="isGetDetailFailed ? $t('list__load_failed') : $t('list__loading')")
|
||||
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
|
||||
div(:class="$style.songListContainer" v-show="!isVisibleListDetail")
|
||||
|
@ -315,14 +315,14 @@ export default {
|
|||
sourceListId: this.listDetail.id,
|
||||
})
|
||||
},
|
||||
async playSongListDetail() {
|
||||
async playSongListDetail(index = 0) {
|
||||
if (!this.listDetail.info.name) return
|
||||
const id = `${this.listDetail.source}__${this.listDetail.id}`
|
||||
let isPlayingList = false
|
||||
if (this.listDetail.list?.length) {
|
||||
this.setTempList({
|
||||
list: [...this.listDetail.list],
|
||||
index: 0,
|
||||
index,
|
||||
id,
|
||||
})
|
||||
isPlayingList = true
|
||||
|
@ -339,7 +339,7 @@ export default {
|
|||
} else {
|
||||
this.setTempList({
|
||||
list,
|
||||
index: 0,
|
||||
index,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 781 B |
Binary file not shown.
After Width: | Height: | Size: 943 B |
Binary file not shown.
After Width: | Height: | Size: 731 B |
Binary file not shown.
After Width: | Height: | Size: 729 B |
Binary file not shown.
After Width: | Height: | Size: 491 B |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue