Compare commits

...

25 Commits

Author SHA1 Message Date
lyswhut
d575dd24ca 发布0.8.0版本 2019-10-19 12:08:53 +08:00
lyswhut
e47586cea3 更新请求头 2019-10-19 12:04:15 +08:00
lyswhut
1ce63c7550 修复酷狗歌曲无封面时报错的问题 2019-10-19 11:48:19 +08:00
lyswhut
36d69da3b7 缓存歌单详情列表数据 2019-10-19 11:47:15 +08:00
lyswhut
529be08420 优化音量条交互视觉效果 2019-10-18 21:47:10 +08:00
lyswhut
ffe11c7015 更新与分离常见问题 2019-10-18 18:04:28 +08:00
lyswhut
76b796bde7 修复歌单地址无效时无限重复请求的bug 2019-10-18 17:46:33 +08:00
lyswhut
f81715f07f 更新歌单导入描述 2019-10-18 13:32:01 +08:00
lyswhut
ec3f667b75 修复音量调整Bug,新增拖动调整音量 2019-10-18 12:03:01 +08:00
lyswhut
87f0b4fd17 修复传参问题 2019-10-17 22:41:24 +08:00
lyswhut
c7e95c5166 新增各平台通过输入歌单链接或歌单ID打开歌单详情列表 2019-10-17 17:01:49 +08:00
lyswhut
6ea0837cce 新增腾讯、网易云歌单链接打开 2019-10-17 14:05:29 +08:00
lyswhut
59627e048f 分离搜索框业务逻辑,优化搜索框搜索体验 2019-10-17 10:58:09 +08:00
lyswhut
45fea5726b 修复歌曲音质不存在时仍然播放或下载的Bug 2019-10-16 21:26:59 +08:00
lyswhut
e539f29f6d 修复音质过滤导致的歌单无法显示问题 2019-10-16 21:00:46 +08:00
lyswhut
1d2810e98f 新增网易云源歌单 2019-10-16 19:54:13 +08:00
lyswhut
1cbafc9750 新增网易云源歌曲搜索 2019-10-16 10:38:42 +08:00
lyswhut
e675f7ac06 更新描述 2019-10-16 00:59:19 +08:00
lyswhut
9dc9d3b1b7 修复网易云128k直接试听 2019-10-16 00:40:36 +08:00
lyswhut
a104ce688e 修复默认列表没有创建时无法显示收藏列表的Bug 2019-10-13 09:20:44 +08:00
lyswhut
2dd1ceb0b9 更新依赖到最新 2019-10-11 22:52:27 +08:00
lyswhut
9379328d96 修复QQ源歌单无法翻页Bug 2019-10-10 18:43:04 +08:00
lyswhut
d8969c327c 改变请求头 2019-10-10 13:24:14 +08:00
lyswhut
c3624fb7d6 Create LICENSE 2019-10-08 10:20:46 +08:00
lyswhut
b2de230100 remove license file 2019-10-08 10:19:46 +08:00
37 changed files with 1228 additions and 398 deletions

View File

@@ -6,6 +6,29 @@ Project versioning adheres to [Semantic Versioning](http://semver.org/).
Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
## [0.8.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.7.0...v0.8.0) - 2019-10-19
#### 新增
- 新增网易云源歌曲搜索
- 新增网易云源歌单
- 新增各平台通过输入歌单链接或歌单ID打开歌单详情列表目前只适配了**网页版歌单链接**其他方式的歌单链接可能无法解析但你可想办法获取歌单ID后输入打开。注各平台歌单ID均为纯数字若遇到链接里存在歌单ID但无法解析的歌单链接可以到GitHub提交issue或发送邮件或加群830125506反馈
- 新增音量调整滑动功能,现在支持鼠标左右拖动调整音量了
#### 优化
- 优化搜索框搜索体验
- 优化音量条交互视觉效果
- 缓存歌单详情列表数据
#### 修复
- 修复QQ源歌单无法翻页Bug
- 修复默认列表没有创建时无法显示收藏列表的Bug
- 修复网易云128k直接试听
- 修复歌曲音质不存在时仍然播放或下载的Bug
- 修复调整音量时,调整的位置与鼠标点击的位置不一致的问题
## [0.7.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.6.2...v0.7.0) - 2019-10-07
#### 新增

60
FAQ.md Normal file
View File

@@ -0,0 +1,60 @@
# lx-music-desktop 常见问题
在阅读本常见问题后仍然无法解决你的问题请提交issue或者加企鹅群`830125506`反馈,反馈时请**注明**已阅读常见问题!
## 软件为什么没有桌面歌词与自定义列表功能
洛雪音乐的最初定位不是作为播放器开发的,它主要用于**查找歌曲**,软件的播放功能仅用于试听,不建议用作为常用播放器使用,因此无桌面、界面歌词,不可自定义列表。
## 歌曲无法试听与下载
该问题解决顺序如下:
1. 尝试更新到最新版本
2. 尝试切换其他歌曲(或直接搜索该歌曲),若全部歌曲都无法试听与下载则进行下一步
3. 尝试到设置-接口来源切换到其他接口
4. 尝试切换网络,比如用手机开热点(目前存在某些网络无法访问接口服务器的情况)
5. 若还不行请到这个链接查看详情:<https://github.com/lyswhut/lx-music-desktop/issues/5>
6. 若没有在第5条链接中的第一条评论中看到接口无法使用的说明则应该是你网络无法访问接口服务器的问题如果接口有问题我会在那里说明。
想要知道是不是自己网络的问题可以看看`https://ts.tempmusic.tk`能不能在浏览器打开浏览器显示404是正常的如果不是404那就证明所在网络无法访问接口服务器。
若网页无法打开或打来不是404则应该是DNS的问题可以尝试以下办法
1. 改成自动获取试试
2. 手动把DNS改一下不要用360的DNS可以把DNS改成`114.114.114.114``8.8.8.8`
3. 软件设置里面的代理不要勾起来
## 软件安装包说明
软件发布页及网盘中有多个类型的安装文件,以下是对这些类型文件的说明:
文件名带 `win_` 的是在Windows系统上运行的版本<br>
其中安装版Setup可自动更新软件<br>
绿色版green为免安装版自动更新功能不可用
**`.dmg`** 结尾的文件为 MAC 版本;
**`.AppImage`**、**`.deb`** 结尾的为 Linux 版本。
带有`x64`的为64位的系统版本`x86`的为32位的系统版本若两个都带有的则为集合版安装时会自动根据系统位数选择对应的版本安装。
## 软件更新
软件启动时若发现新版本时会自动从本仓库下载安装包,下载完毕会弹窗提示更新。<br>
若下载未完成时软件被关闭,下次启动软件会再次自动下载。<br>
若还是**更新失败**可能是无法访问GitHub导致的这时需要手动更新即下载最新安装包直接覆盖安装即可。<br>
注意:**绿色版**的软件自动更新功能**不可用**,建议使用安装版!!<br>
注意:**Mac版**、**Linux deb**版不支持自动更新!
## Windows 7 下界面异常
当 win7 没有开启**透明效果**时界面将会显示异常,开启方法请自行百度。<br>
对于一些开启透明效果后仍然无法正常显示界面的系统,我也不知道是什么原因导致的,只能说正常的系统是没有这个问题的,如果你知道原因导致的欢迎反馈!
## 安装版安装失败,提示应用未安装
对于部分电脑出现安装失败的问题我也不懂什么原因,,可以尝试清理下安装文件,或者重启电脑试试。
## 缺少`xxx.dll`
这个是电脑缺少某些dll导致的正常的系统是没有这个问题的解决办法需自行百度弹出的错误信息看下别人是怎么解决的。

View File

@@ -49,11 +49,6 @@
或者到网盘下载网盘内有MAC、windows版`https://www.lanzous.com/b906260/` 密码:`glqw`<br>
使用常见问题请转至:[常见问题](https://github.com/lyswhut/lx-music-desktop#常见问题)
#### TODO
- [ ] 新增拉取各大平台歌单
- [ ] 新增聚合搜索
### 源码使用方法
环境要求Node.js 12.x
@@ -76,50 +71,7 @@ npm run pack
### 常见问题
#### 歌曲无法试听与下载
该问题解决顺序如下:
1. 尝试更新到最新版本
2. 尝试切换其他歌曲(或直接搜索该歌曲),若全部歌曲都无法试听与下载则进行下一步
3. 尝试到设置-接口来源切换到其他接口
4. 尝试切换网络,比如用手机开热点(目前存在某些网络无法访问接口服务器的情况)
5. 若还不行请到这个链接查看详情:<https://github.com/lyswhut/lx-music-desktop/issues/5>
6. 若没有在第5条链接中的第一条评论中看到接口无法使用的说明则应该是你网络无法访问接口服务器的问题如果接口有问题我会在那里说明。
#### 软件安装包说明
软件发布页及网盘中有多个类型的安装文件,以下是对这些类型文件的说明:
文件名带 `win_` 的是在Windows系统上运行的版本<br>
其中安装版Setup可自动更新软件<br>
绿色版green为免安装版自动更新功能不可用
**`.dmg`** 结尾的文件为 MAC 版本;
**`.AppImage`**、**`.deb`** 结尾的为 Linux 版本。
带有`x64`的为64位的系统版本`x86`的为32位的系统版本若两个都带有的则为集合版安装时会自动根据系统位数选择对应的版本安装。
#### 软件更新
软件启动时若发现新版本时会自动从本仓库下载安装包,下载完毕会弹窗提示更新。<br>
若下载未完成时软件被关闭,下次启动软件会再次自动下载。<br>
若还是**更新失败**可能是无法访问GitHub导致的这时需要手动更新即下载最新安装包直接覆盖安装即可。<br>
注意:**绿色版**的软件自动更新功能**不可用**,建议使用安装版!!<br>
注意:**Mac版**、**Linux deb**版不支持自动更新!
#### Windows 7 下界面异常
当 win7 没有开启**透明效果**时界面将会显示异常,开启方法请自行百度。
#### 安装版安装失败,提示应用未安装
对于部分电脑出现安装失败的问题我也不懂什么原因,,可以尝试清理下安装文件,或者重启电脑试试。
#### 缺少`xxx.dll`
这个是电脑缺少某些dll导致的正常的系统是没有这个问题的解决办法需自行百度弹出的错误信息看下别人是怎么解决的。
常见问题已移至:<https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md>
### 致谢

253
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "lx-music-desktop",
"version": "0.6.2",
"version": "0.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -26,18 +26,18 @@
}
},
"@babel/core": {
"version": "7.6.2",
"resolved": "https://registry.npm.taobao.org/@babel/core/download/@babel/core-7.6.2.tgz",
"integrity": "sha1-Bpp3bo1enu//diNryIRVZr0x3ZE=",
"version": "7.6.4",
"resolved": "https://registry.npm.taobao.org/@babel/core/download/@babel/core-7.6.4.tgz",
"integrity": "sha1-br2f4Akl9sPhd7tyahiLX1eAiP8=",
"dev": true,
"requires": {
"@babel/code-frame": "^7.5.5",
"@babel/generator": "^7.6.2",
"@babel/generator": "^7.6.4",
"@babel/helpers": "^7.6.2",
"@babel/parser": "^7.6.2",
"@babel/parser": "^7.6.4",
"@babel/template": "^7.6.0",
"@babel/traverse": "^7.6.2",
"@babel/types": "^7.6.0",
"@babel/traverse": "^7.6.3",
"@babel/types": "^7.6.3",
"convert-source-map": "^1.1.0",
"debug": "^4.1.0",
"json5": "^2.1.0",
@@ -57,40 +57,51 @@
}
},
"@babel/generator": {
"version": "7.6.2",
"resolved": "https://registry.npm.taobao.org/@babel/generator/download/@babel/generator-7.6.2.tgz",
"integrity": "sha1-2sijwt8RgzTCop/zRG2hY2qPjAM=",
"version": "7.6.4",
"resolved": "https://registry.npm.taobao.org/@babel/generator/download/@babel/generator-7.6.4.tgz?cache=0&sync_timestamp=1570717897603&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fgenerator%2Fdownload%2F%40babel%2Fgenerator-7.6.4.tgz",
"integrity": "sha1-pPhDcoe/lnGwf0g7duO7cxvJdnE=",
"dev": true,
"requires": {
"@babel/types": "^7.6.0",
"@babel/types": "^7.6.3",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
}
},
"@babel/parser": {
"version": "7.6.2",
"resolved": "https://registry.npm.taobao.org/@babel/parser/download/@babel/parser-7.6.2.tgz?cache=0&sync_timestamp=1569286082477&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fparser%2Fdownload%2F%40babel%2Fparser-7.6.2.tgz",
"integrity": "sha1-IF6cleFro7i5YJBnemfJ1gdbcKE=",
"version": "7.6.4",
"resolved": "https://registry.npm.taobao.org/@babel/parser/download/@babel/parser-7.6.4.tgz?cache=0&sync_timestamp=1570718702664&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fparser%2Fdownload%2F%40babel%2Fparser-7.6.4.tgz",
"integrity": "sha1-y5s2p0ghECgtXLbdQk7JJitHPYE=",
"dev": true
},
"@babel/traverse": {
"version": "7.6.2",
"resolved": "https://registry.npm.taobao.org/@babel/traverse/download/@babel/traverse-7.6.2.tgz",
"integrity": "sha1-sOK/1AHTOc4ObAVpAgbR4RUCziw=",
"version": "7.6.3",
"resolved": "https://registry.npm.taobao.org/@babel/traverse/download/@babel/traverse-7.6.3.tgz",
"integrity": "sha1-ZtfboUawhnA8D7EN1Yi3NkzsR/k=",
"dev": true,
"requires": {
"@babel/code-frame": "^7.5.5",
"@babel/generator": "^7.6.2",
"@babel/generator": "^7.6.3",
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-split-export-declaration": "^7.4.4",
"@babel/parser": "^7.6.2",
"@babel/types": "^7.6.0",
"@babel/parser": "^7.6.3",
"@babel/types": "^7.6.3",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
}
},
"@babel/types": {
"version": "7.6.3",
"resolved": "https://registry.npm.taobao.org/@babel/types/download/@babel/types-7.6.3.tgz",
"integrity": "sha1-PwfZb4VPmOL71FxksMuULRHougk=",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.13",
"to-fast-properties": "^2.0.0"
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.1.1.tgz",
@@ -101,9 +112,9 @@
}
},
"json5": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.0.tgz",
"integrity": "sha1-56DGLEgoXGKNIKELhcibuAfDKFA=",
"version": "2.1.1",
"resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson5%2Fdownload%2Fjson5-2.1.1.tgz",
"integrity": "sha1-gbbLBOm6SW8ccAXQe0NoomOPkLY=",
"dev": true,
"requires": {
"minimist": "^1.2.0"
@@ -357,38 +368,64 @@
}
},
"@babel/generator": {
"version": "7.6.2",
"resolved": "https://registry.npm.taobao.org/@babel/generator/download/@babel/generator-7.6.2.tgz",
"integrity": "sha1-2sijwt8RgzTCop/zRG2hY2qPjAM=",
"version": "7.6.4",
"resolved": "https://registry.npm.taobao.org/@babel/generator/download/@babel/generator-7.6.4.tgz?cache=0&sync_timestamp=1570717897603&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fgenerator%2Fdownload%2F%40babel%2Fgenerator-7.6.4.tgz",
"integrity": "sha1-pPhDcoe/lnGwf0g7duO7cxvJdnE=",
"dev": true,
"requires": {
"@babel/types": "^7.6.0",
"@babel/types": "^7.6.3",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
},
"dependencies": {
"@babel/types": {
"version": "7.6.3",
"resolved": "https://registry.npm.taobao.org/@babel/types/download/@babel/types-7.6.3.tgz",
"integrity": "sha1-PwfZb4VPmOL71FxksMuULRHougk=",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.13",
"to-fast-properties": "^2.0.0"
}
}
}
},
"@babel/parser": {
"version": "7.6.2",
"resolved": "https://registry.npm.taobao.org/@babel/parser/download/@babel/parser-7.6.2.tgz?cache=0&sync_timestamp=1569286082477&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fparser%2Fdownload%2F%40babel%2Fparser-7.6.2.tgz",
"integrity": "sha1-IF6cleFro7i5YJBnemfJ1gdbcKE=",
"version": "7.6.4",
"resolved": "https://registry.npm.taobao.org/@babel/parser/download/@babel/parser-7.6.4.tgz?cache=0&sync_timestamp=1570718702664&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fparser%2Fdownload%2F%40babel%2Fparser-7.6.4.tgz",
"integrity": "sha1-y5s2p0ghECgtXLbdQk7JJitHPYE=",
"dev": true
},
"@babel/traverse": {
"version": "7.6.2",
"resolved": "https://registry.npm.taobao.org/@babel/traverse/download/@babel/traverse-7.6.2.tgz",
"integrity": "sha1-sOK/1AHTOc4ObAVpAgbR4RUCziw=",
"version": "7.6.3",
"resolved": "https://registry.npm.taobao.org/@babel/traverse/download/@babel/traverse-7.6.3.tgz",
"integrity": "sha1-ZtfboUawhnA8D7EN1Yi3NkzsR/k=",
"dev": true,
"requires": {
"@babel/code-frame": "^7.5.5",
"@babel/generator": "^7.6.2",
"@babel/generator": "^7.6.3",
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-split-export-declaration": "^7.4.4",
"@babel/parser": "^7.6.2",
"@babel/types": "^7.6.0",
"@babel/parser": "^7.6.3",
"@babel/types": "^7.6.3",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
},
"dependencies": {
"@babel/types": {
"version": "7.6.3",
"resolved": "https://registry.npm.taobao.org/@babel/types/download/@babel/types-7.6.3.tgz",
"integrity": "sha1-PwfZb4VPmOL71FxksMuULRHougk=",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.13",
"to-fast-properties": "^2.0.0"
}
}
}
},
"debug": {
@@ -568,9 +605,9 @@
}
},
"@babel/plugin-transform-block-scoping": {
"version": "7.6.2",
"resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-block-scoping/download/@babel/plugin-transform-block-scoping-7.6.2.tgz",
"integrity": "sha1-lsM6uXqa5QDMb1sZ4Ep+ZVM2Cnk=",
"version": "7.6.3",
"resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-block-scoping/download/@babel/plugin-transform-block-scoping-7.6.3.tgz?cache=0&sync_timestamp=1570565315466&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-transform-block-scoping%2Fdownload%2F%40babel%2Fplugin-transform-block-scoping-7.6.3.tgz",
"integrity": "sha1-boVOUfu6qENRsV1N2v40LzpdVCo=",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -723,9 +760,9 @@
}
},
"@babel/plugin-transform-named-capturing-groups-regex": {
"version": "7.6.2",
"resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-named-capturing-groups-regex/download/@babel/plugin-transform-named-capturing-groups-regex-7.6.2.tgz",
"integrity": "sha1-wcoLuEuU84XKMCw5MuhwsPsOUis=",
"version": "7.6.3",
"resolved": "https://registry.npm.taobao.org/@babel/plugin-transform-named-capturing-groups-regex/download/@babel/plugin-transform-named-capturing-groups-regex-7.6.3.tgz",
"integrity": "sha1-qqbkCd1PsuULbiqR9+OjFJ284M8=",
"dev": true,
"requires": {
"regexpu-core": "^4.6.0"
@@ -865,9 +902,9 @@
}
},
"@babel/preset-env": {
"version": "7.6.2",
"resolved": "https://registry.npm.taobao.org/@babel/preset-env/download/@babel/preset-env-7.6.2.tgz?cache=0&sync_timestamp=1569273953853&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fpreset-env%2Fdownload%2F%40babel%2Fpreset-env-7.6.2.tgz",
"integrity": "sha1-q7s+14XH/kIg1MgqU2IdcfwMddM=",
"version": "7.6.3",
"resolved": "https://registry.npm.taobao.org/@babel/preset-env/download/@babel/preset-env-7.6.3.tgz",
"integrity": "sha1-nhvwWi4taHA20kxA5GOdxGzvInE=",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.0.0",
@@ -886,7 +923,7 @@
"@babel/plugin-transform-arrow-functions": "^7.2.0",
"@babel/plugin-transform-async-to-generator": "^7.5.0",
"@babel/plugin-transform-block-scoped-functions": "^7.2.0",
"@babel/plugin-transform-block-scoping": "^7.6.2",
"@babel/plugin-transform-block-scoping": "^7.6.3",
"@babel/plugin-transform-classes": "^7.5.5",
"@babel/plugin-transform-computed-properties": "^7.2.0",
"@babel/plugin-transform-destructuring": "^7.6.0",
@@ -901,7 +938,7 @@
"@babel/plugin-transform-modules-commonjs": "^7.6.0",
"@babel/plugin-transform-modules-systemjs": "^7.5.0",
"@babel/plugin-transform-modules-umd": "^7.2.0",
"@babel/plugin-transform-named-capturing-groups-regex": "^7.6.2",
"@babel/plugin-transform-named-capturing-groups-regex": "^7.6.3",
"@babel/plugin-transform-new-target": "^7.4.4",
"@babel/plugin-transform-object-super": "^7.5.5",
"@babel/plugin-transform-parameters": "^7.4.4",
@@ -914,12 +951,25 @@
"@babel/plugin-transform-template-literals": "^7.4.4",
"@babel/plugin-transform-typeof-symbol": "^7.2.0",
"@babel/plugin-transform-unicode-regex": "^7.6.2",
"@babel/types": "^7.6.0",
"@babel/types": "^7.6.3",
"browserslist": "^4.6.0",
"core-js-compat": "^3.1.1",
"invariant": "^2.2.2",
"js-levenshtein": "^1.1.3",
"semver": "^5.5.0"
},
"dependencies": {
"@babel/types": {
"version": "7.6.3",
"resolved": "https://registry.npm.taobao.org/@babel/types/download/@babel/types-7.6.3.tgz",
"integrity": "sha1-PwfZb4VPmOL71FxksMuULRHougk=",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.13",
"to-fast-properties": "^2.0.0"
}
}
}
},
"@babel/template": {
@@ -1626,7 +1676,7 @@
"dependencies": {
"inherits": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.1.tgz?cache=0&sync_timestamp=1560975547815&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.1.tgz",
"resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.1.tgz",
"integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
"dev": true
},
@@ -1694,13 +1744,13 @@
"dev": true
},
"autoprefixer": {
"version": "9.6.4",
"resolved": "https://registry.npm.taobao.org/autoprefixer/download/autoprefixer-9.6.4.tgz?cache=0&sync_timestamp=1570294713985&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fautoprefixer%2Fdownload%2Fautoprefixer-9.6.4.tgz",
"integrity": "sha1-5kU75HrzFrKSPq6u2Hhg9SrUt+s=",
"version": "9.6.5",
"resolved": "https://registry.npm.taobao.org/autoprefixer/download/autoprefixer-9.6.5.tgz?cache=0&sync_timestamp=1571040005147&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fautoprefixer%2Fdownload%2Fautoprefixer-9.6.5.tgz",
"integrity": "sha1-mPSv5+k8zPMjKHUV1CYBlhl3Xl4=",
"dev": true,
"requires": {
"browserslist": "^4.7.0",
"caniuse-lite": "^1.0.30000998",
"caniuse-lite": "^1.0.30000999",
"chalk": "^2.4.2",
"normalize-range": "^0.1.2",
"num2fraction": "^1.2.2",
@@ -1710,7 +1760,7 @@
"dependencies": {
"browserslist": {
"version": "4.7.0",
"resolved": "https://registry.npm.taobao.org/browserslist/download/browserslist-4.7.0.tgz?cache=0&sync_timestamp=1567245449635&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbrowserslist%2Fdownload%2Fbrowserslist-4.7.0.tgz",
"resolved": "https://registry.npm.taobao.org/browserslist/download/browserslist-4.7.0.tgz",
"integrity": "sha1-nuiSJf/AfbA0CfL+5STcgidFihc=",
"dev": true,
"requires": {
@@ -1726,15 +1776,15 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.275",
"resolved": "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.275.tgz",
"integrity": "sha1-GaOENuNCFvUYIPovQybVzhQfo28=",
"version": "1.3.282",
"resolved": "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.282.tgz?cache=0&sync_timestamp=1570911575715&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felectron-to-chromium%2Fdownload%2Felectron-to-chromium-1.3.282.tgz",
"integrity": "sha1-FhGK6ceaMuqToXWR1bFuKNEPwI0=",
"dev": true
},
"node-releases": {
"version": "1.1.34",
"resolved": "https://registry.npm.taobao.org/node-releases/download/node-releases-1.1.34.tgz",
"integrity": "sha1-ztRlXuG6nDosXcusOF4ZQ0FV/UA=",
"version": "1.1.35",
"resolved": "https://registry.npm.taobao.org/node-releases/download/node-releases-1.1.35.tgz",
"integrity": "sha1-MqdKPNSXqnfyPVCfSDR1/RYOTEg=",
"dev": true,
"requires": {
"semver": "^6.3.0"
@@ -1753,7 +1803,7 @@
},
"postcss-value-parser": {
"version": "4.0.2",
"resolved": "https://registry.npm.taobao.org/postcss-value-parser/download/postcss-value-parser-4.0.2.tgz?cache=0&sync_timestamp=1565203765685&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-value-parser%2Fdownload%2Fpostcss-value-parser-4.0.2.tgz",
"resolved": "https://registry.npm.taobao.org/postcss-value-parser/download/postcss-value-parser-4.0.2.tgz",
"integrity": "sha1-SCKCwJpCcG0fyaBptz9E7Ag5Hck=",
"dev": true
},
@@ -2943,7 +2993,7 @@
},
"buffer": {
"version": "4.9.1",
"resolved": "https://registry.npm.taobao.org/buffer/download/buffer-4.9.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-4.9.1.tgz",
"resolved": "https://registry.npm.taobao.org/buffer/download/buffer-4.9.1.tgz",
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"dev": true,
"requires": {
@@ -3986,9 +4036,9 @@
}
},
"core-js": {
"version": "3.2.1",
"resolved": "https://registry.npm.taobao.org/core-js/download/core-js-3.2.1.tgz",
"integrity": "sha1-zUHzhTTabMWffbBQ/mcwfemGiwk=",
"version": "3.3.2",
"resolved": "https://registry.npm.taobao.org/core-js/download/core-js-3.3.2.tgz",
"integrity": "sha1-zULaHXsLsz7xEya+OnIZNCd860I=",
"dev": true
},
"core-js-compat": {
@@ -4865,9 +4915,9 @@
"dev": true
},
"electron": {
"version": "6.0.11",
"resolved": "https://registry.npm.taobao.org/electron/download/electron-6.0.11.tgz",
"integrity": "sha1-ZO/6cTlwRXeaHdv1ExKkHM8m9JE=",
"version": "6.0.12",
"resolved": "https://registry.npm.taobao.org/electron/download/electron-6.0.12.tgz",
"integrity": "sha1-hb8AYeMvZgJWz96VSI8ct1JTvJQ=",
"dev": true,
"requires": {
"@types/node": "^10.12.18",
@@ -8973,9 +9023,9 @@
"dev": true
},
"lrc-file-parser": {
"version": "0.1.12",
"resolved": "https://registry.npm.taobao.org/lrc-file-parser/download/lrc-file-parser-0.1.12.tgz",
"integrity": "sha1-sM/lhxqufWctaW8C2gBXlTjUKZ0="
"version": "0.1.14",
"resolved": "https://registry.npm.taobao.org/lrc-file-parser/download/lrc-file-parser-0.1.14.tgz",
"integrity": "sha1-FW00nV/f01XNJMp/JqYMPeDscgE="
},
"lru-cache": {
"version": "4.1.5",
@@ -11982,7 +12032,7 @@
},
"sha.js": {
"version": "2.4.11",
"resolved": "https://registry.npm.taobao.org/sha.js/download/sha.js-2.4.11.tgz",
"resolved": "https://registry.npm.taobao.org/sha.js/download/sha.js-2.4.11.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsha.js%2Fdownload%2Fsha.js-2.4.11.tgz",
"integrity": "sha1-N6XPC4HsvGlD3hCbopYNGyZYSuc=",
"dev": true,
"requires": {
@@ -12911,9 +12961,9 @@
}
},
"terser": {
"version": "4.3.1",
"resolved": "https://registry.npm.taobao.org/terser/download/terser-4.3.1.tgz",
"integrity": "sha1-CYILyzOYKZxLSNmoau/GUSfQ7WU=",
"version": "4.3.8",
"resolved": "https://registry.npm.taobao.org/terser/download/terser-4.3.8.tgz",
"integrity": "sha1-cH8F8/TBxwyEDmJq3f2xwVihcTY=",
"dev": true,
"requires": {
"commander": "^2.20.0",
@@ -12922,26 +12972,26 @@
},
"dependencies": {
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npm.taobao.org/commander/download/commander-2.20.0.tgz",
"integrity": "sha1-1YuytcHuj4ew00ACfp6U4iLFpCI=",
"version": "2.20.3",
"resolved": "https://registry.npm.taobao.org/commander/download/commander-2.20.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.20.3.tgz",
"integrity": "sha1-/UhehMA+tIgcIHIrpIA16FMa6zM=",
"dev": true
}
}
},
"terser-webpack-plugin": {
"version": "2.1.2",
"resolved": "https://registry.npm.taobao.org/terser-webpack-plugin/download/terser-webpack-plugin-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fterser-webpack-plugin%2Fdownload%2Fterser-webpack-plugin-2.1.2.tgz",
"integrity": "sha1-K5uBR6bxiRg0ggCADPlWDFD3Abs=",
"version": "2.1.3",
"resolved": "https://registry.npm.taobao.org/terser-webpack-plugin/download/terser-webpack-plugin-2.1.3.tgz",
"integrity": "sha1-hUMNpxuoimAHK/ZZWJ6vr2oA3CI=",
"dev": true,
"requires": {
"cacache": "^13.0.0",
"cacache": "^13.0.1",
"find-cache-dir": "^3.0.0",
"jest-worker": "^24.9.0",
"schema-utils": "^2.4.1",
"serialize-javascript": "^2.1.0",
"source-map": "^0.6.1",
"terser": "^4.3.4",
"terser": "^4.3.8",
"webpack-sources": "^1.4.3"
},
"dependencies": {
@@ -12971,12 +13021,6 @@
"unique-filename": "^1.1.1"
}
},
"commander": {
"version": "2.20.1",
"resolved": "https://registry.npm.taobao.org/commander/download/commander-2.20.1.tgz",
"integrity": "sha1-OGPOPKktCDHc8qEC9ftLWSav0Pk=",
"dev": true
},
"find-cache-dir": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/find-cache-dir/download/find-cache-dir-3.0.0.tgz",
@@ -13096,17 +13140,6 @@
"minipass": "^3.0.0"
}
},
"terser": {
"version": "4.3.6",
"resolved": "https://registry.npm.taobao.org/terser/download/terser-4.3.6.tgz",
"integrity": "sha1-OjauGX1EcOEQaVV7SkvXEvhPmY0=",
"dev": true,
"requires": {
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
}
},
"webpack-sources": {
"version": "1.4.3",
"resolved": "https://registry.npm.taobao.org/webpack-sources/download/webpack-sources-1.4.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack-sources%2Fdownload%2Fwebpack-sources-1.4.3.tgz",
@@ -13667,7 +13700,7 @@
"dependencies": {
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz?cache=0&sync_timestamp=1560975547815&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.3.tgz",
"resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
}
@@ -13924,9 +13957,9 @@
}
},
"webpack": {
"version": "4.41.0",
"resolved": "https://registry.npm.taobao.org/webpack/download/webpack-4.41.0.tgz",
"integrity": "sha1-22olS95nF2n3wU6QoaVec2Avxws=",
"version": "4.41.2",
"resolved": "https://registry.npm.taobao.org/webpack/download/webpack-4.41.2.tgz",
"integrity": "sha1-w07Hbao6hGjJthpQM22OMwPc504=",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.8.5",
@@ -13956,13 +13989,13 @@
"dependencies": {
"acorn": {
"version": "6.3.0",
"resolved": "https://registry.npm.taobao.org/acorn/download/acorn-6.3.0.tgz",
"resolved": "https://registry.npm.taobao.org/acorn/download/acorn-6.3.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn%2Fdownload%2Facorn-6.3.0.tgz",
"integrity": "sha1-AIdQkRn/pPwKAEHR6TpBfmjLhW4=",
"dev": true
},
"cacache": {
"version": "12.0.3",
"resolved": "https://registry.npm.taobao.org/cacache/download/cacache-12.0.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcacache%2Fdownload%2Fcacache-12.0.3.tgz",
"resolved": "https://registry.npm.taobao.org/cacache/download/cacache-12.0.3.tgz",
"integrity": "sha1-vpmruk4b9d9GHNWiwQcfxDJXM5A=",
"dev": true,
"requires": {
@@ -13985,7 +14018,7 @@
},
"eslint-scope": {
"version": "4.0.3",
"resolved": "https://registry.npm.taobao.org/eslint-scope/download/eslint-scope-4.0.3.tgz?cache=0&sync_timestamp=1563679289211&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-scope%2Fdownload%2Feslint-scope-4.0.3.tgz",
"resolved": "https://registry.npm.taobao.org/eslint-scope/download/eslint-scope-4.0.3.tgz",
"integrity": "sha1-ygODMxD2iJoyZHgaqC5j65z+eEg=",
"dev": true,
"requires": {
@@ -14013,7 +14046,7 @@
},
"terser-webpack-plugin": {
"version": "1.4.1",
"resolved": "https://registry.npm.taobao.org/terser-webpack-plugin/download/terser-webpack-plugin-1.4.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fterser-webpack-plugin%2Fdownload%2Fterser-webpack-plugin-1.4.1.tgz",
"resolved": "https://registry.npm.taobao.org/terser-webpack-plugin/download/terser-webpack-plugin-1.4.1.tgz",
"integrity": "sha1-YbGOQOruW+l+dxzbsQ7RKAiIwrQ=",
"dev": true,
"requires": {
@@ -14030,7 +14063,7 @@
},
"webpack-sources": {
"version": "1.4.3",
"resolved": "https://registry.npm.taobao.org/webpack-sources/download/webpack-sources-1.4.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack-sources%2Fdownload%2Fwebpack-sources-1.4.3.tgz",
"resolved": "https://registry.npm.taobao.org/webpack-sources/download/webpack-sources-1.4.3.tgz?cache=0&sync_timestamp=1568302837530&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack-sources%2Fdownload%2Fwebpack-sources-1.4.3.tgz",
"integrity": "sha1-7t2OwLko+/HL/plOItLYkPMwqTM=",
"dev": true,
"requires": {

View File

@@ -1,6 +1,6 @@
{
"name": "lx-music-desktop",
"version": "0.7.0",
"version": "0.8.0",
"description": "一个免费的音乐下载助手",
"main": "./dist/electron/main.js",
"productName": "lx-music-desktop",
@@ -134,11 +134,11 @@
},
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/core": "^7.6.4",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/polyfill": "^7.6.0",
"@babel/preset-env": "^7.6.2",
"autoprefixer": "^9.6.4",
"@babel/preset-env": "^7.6.3",
"autoprefixer": "^9.6.5",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"babel-minify-webpack-plugin": "^0.3.1",
@@ -147,12 +147,12 @@
"chalk": "^2.4.2",
"changelog-parser": "^2.8.0",
"copy-webpack-plugin": "^5.0.4",
"core-js": "^3.2.1",
"core-js": "^3.3.2",
"cos-nodejs-sdk-v5": "^2.5.12",
"cross-env": "^6.0.3",
"css-loader": "^3.2.0",
"del": "^5.1.0",
"electron": "^6.0.11",
"electron": "^6.0.12",
"electron-builder": "^21.2.0",
"electron-debug": "^3.0.1",
"electron-devtools-installer": "^2.2.4",
@@ -182,12 +182,12 @@
"rimraf": "^3.0.0",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2",
"terser-webpack-plugin": "^2.1.2",
"terser-webpack-plugin": "^2.1.3",
"url-loader": "^2.2.0",
"vue-loader": "^15.7.1",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.41.0",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.9",
"webpack-dev-server": "^3.8.2",
"webpack-hot-middleware": "^2.25.0",
@@ -202,7 +202,7 @@
"electron-updater": "^4.1.2",
"flac-metadata": "^0.1.1",
"js-htmlencode": "^0.3.0",
"lrc-file-parser": "^0.1.12",
"lrc-file-parser": "^0.1.14",
"node-downloader-helper": "^1.0.10",
"node-id3": "^0.1.11",
"request": "^2.88.0",

View File

@@ -1,19 +1,20 @@
#### 新增
- 新增“我的收藏”本地播放列表
- 新增缓存清理功能,可到**设置-其他**查看与清理软件缓存
- 新增QQ音乐源搜索
- 新增咪咕源搜索
- 新增咪咕源歌单
- 新增咪咕源排行榜
- 新增我的音乐列表歌曲源显示,默认关闭,可到**设置-列表设置**开启
- 新增网易云源歌曲搜索
- 新增网易云源歌单
- 新增各平台通过输入歌单链接或歌单ID打开歌单详情列表目前只适配了**网页版歌单链接**其他方式的歌单链接可能无法解析但你可想办法获取歌单ID后输入打开。注各平台歌单ID均为纯数字若遇到链接里存在歌单ID但无法解析的歌单链接可以到GitHub提交issue或发送邮件或加群830125506反馈
- 新增音量调整滑动功能,现在支持鼠标左右拖动调整音量了
#### 优化
- 优化选择框动画效果
- 尝试优化选我的音乐列表内容很多时多选的卡顿问题
- 优化搜索框搜索体验
- 优化音量条交互视觉效果
- 缓存歌单详情列表数据
#### 修复
- 修复列表延迟显示的Bug
- 修复QQ音源128k音质试听
- 修复QQ源歌单无法翻页Bug
- 修复默认列表没有创建时无法显示收藏列表的Bug
- 修复网易云128k直接试听
- 修复歌曲音质不存在时仍然播放或下载的Bug
- 修复调整音量时,调整的位置与鼠标点击的位置不一致的问题

View File

@@ -1,7 +1,11 @@
{
"version": "0.7.0",
"desc": "<h4>新增</h4>\n<ul>\n<li>新增“我的收藏”本地播放列表</li>\n<li>新增缓存清理功能,可到<strong>设置-其他</strong>查看与清理软件缓存</li>\n<li>新增QQ音乐源搜索</li>\n<li>新增咪咕源搜索</li>\n<li>新增咪咕源歌单</li>\n<li>新增咪咕源排行榜</li>\n<li>新增我的音乐列表歌曲源显示,默认关闭,可到<strong>设置-列表设置</strong>开启</li>\n</ul>\n<h4>优化</h4>\n<ul>\n<li>优化选择框动画效果</li>\n<li>尝试优化选我的音乐列表内容很多时多选的卡顿问题</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修复列表延迟显示的Bug</li>\n<li>修复QQ音源128k音质试听</li>\n</ul>\n",
"version": "0.8.0",
"desc": "<h4>新增</h4>\n<ul>\n<li>新增网易云源歌曲搜索</li>\n<li>新增网易云源歌单</li>\n<li>新增各平台通过输入歌单链接或歌单ID打开歌单详情列表目前只适配了<strong>网页版歌单链接</strong>其他方式的歌单链接可能无法解析但你可想办法获取歌单ID后输入打开。注各平台歌单ID均为纯数字若遇到链接里存在歌单ID但无法解析的歌单链接可以到GitHub提交issue或发送邮件或加群830125506反馈</li>\n<li>新增音量调整滑动功能,现在支持鼠标左右拖动调整音量了</li>\n</ul>\n<h4>优化</h4>\n<ul>\n<li>优化搜索框搜索体验</li>\n<li>优化音量条交互视觉效果</li>\n<li>缓存歌单详情列表数据</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修复QQ源歌单无法翻页Bug</li>\n<li>修复默认列表没有创建时无法显示收藏列表的Bug</li>\n<li>修复网易云128k直接试听</li>\n<li>修复歌曲音质不存在时仍然播放或下载的Bug</li>\n<li>修复调整音量时,调整的位置与鼠标点击的位置不一致的问题</li>\n</ul>\n",
"history": [
{
"version": "0.7.0",
"desc": "<h4>新增</h4>\n<ul>\n<li>新增“我的收藏”本地播放列表</li>\n<li>新增缓存清理功能,可到<strong>设置-其他</strong>查看与清理软件缓存</li>\n<li>新增QQ音乐源搜索</li>\n<li>新增咪咕源搜索</li>\n<li>新增咪咕源歌单</li>\n<li>新增咪咕源排行榜</li>\n<li>新增我的音乐列表歌曲源显示,默认关闭,可到<strong>设置-列表设置</strong>开启</li>\n</ul>\n<h4>优化</h4>\n<ul>\n<li>优化选择框动画效果</li>\n<li>尝试优化选我的音乐列表内容很多时多选的卡顿问题</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修复列表延迟显示的Bug</li>\n<li>修复QQ音源128k音质试听</li>\n</ul>\n"
},
{
"version": "0.6.2",
"desc": "<p>祝贺祖国成立70周年~</p>\n<h4>新增</h4>\n<ul>\n<li>新增QQ音乐源歌单</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修正火影皮肤名字</li>\n<li>修复当试听列表为空时无法切到其他界面的Bug</li>\n<li>修复百度源搜索结果为空时的接口处理Bug</li>\n<li>恢复<strong>酷狗</strong>其他音质播放</li>\n</ul>\n"

View File

@@ -164,8 +164,7 @@ export default {
initPlayList() {
let defaultList = this.electronStore.get('list.defaultList')
let loveList = this.electronStore.get('list.loveList')
// console.log(defaultList)
if (defaultList) this.initList({ defaultList, loveList })
this.initList({ defaultList, loveList })
},
initDownloadList() {
let downloadList = this.electronStore.get('download.list')

View File

@@ -9,8 +9,9 @@ div(:class="$style.player")
div(:class="$style.container")
div(:class="$style.title" @click="handleCopy(title)" :title="title + '(点击复制)'") {{title}}
div(:class="$style.volumeContent")
div(:class="$style.volume" @click.stop='handleChangeVolume' :title="`当前音量:${parseInt(volume * 100)}%`")
div(:class="$style.volume")
div(:class="$style.volumeBar" :style="{ transform: `scaleX(${volume || 0})` }")
div(:class="$style.volumeMask" @mousedown="handleVolumeMsDown" ref="dom_volumeMask" :title="`当前音量:${parseInt(volume * 100)}%`")
//- div(:class="$style.playBtn" @click='handleNext' title="音量")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 291.063 291.064' space='preserve')
@@ -48,9 +49,10 @@ div(:class="$style.player")
<script>
import Lyric from 'lrc-file-parser'
import { rendererSend } from '../../../common/icp'
import { formatPlayTime2, getRandom, checkPath, setTitle, clipboardWriteText } from '../../utils'
import { formatPlayTime2, getRandom, checkPath, setTitle, clipboardWriteText, debounce } from '../../utils'
import { mapGetters, mapActions, mapMutations } from 'vuex'
import { requestMsg } from '../../utils/message'
import { isMac } from '../../../common/utils'
export default {
data() {
@@ -80,6 +82,14 @@ export default {
delayNextTimeout: null,
audioErrorTime: 0,
retryNum: 0,
isMac,
volumeEvent: {
isMsDown: false,
msDownX: 0,
msDownVolume: 0,
},
handleVolumeMsMoveFn: null,
handleVolumeMsUpFn: null,
}
},
computed: {
@@ -111,6 +121,16 @@ export default {
this.$nextTick(() => {
this.setProgessWidth()
})
this.handleSaveVolume = debounce(volume => {
this.setVolume(volume)
}, 300)
document.addEventListener('mousemove', this.handleVolumeMsMoveFn = this.handleVolumeMsMove.bind(this))
document.addEventListener('mouseup', this.handleVolumeMsUpFn = this.handleVolumeMsUp.bind(this))
},
beforeDestroy() {
document.removeEventListener('mousemove', this.handleVolumeMsMoveFn)
document.removeEventListener('mouseup', this.handleVolumeMsUpFn)
},
watch: {
changePlay(n) {
@@ -442,16 +462,29 @@ export default {
clearAppTitle() {
setTitle()
},
handleChangeVolume(e) {
handleVolumeMsDown(e) {
this.volumeEvent.isMsDown = true
this.volumeEvent.isMsMoved = false
this.volumeEvent.msDownX = e.clientX
let val = e.offsetX / 70
if (val < 0) val = 0
if (val > 1) val = 1
if (val > 0.97) val = 1
this.volume = val
this.volumeEvent.msDownVolume = val
// console.log(val)
if (this.audio) this.audio.volume = this.volume
},
handleSaveVolume(volume) {
this.setVolume(volume)
handleVolumeMsUp(e) {
this.volumeEvent.isMsDown = false
},
handleVolumeMsMove(e) {
if (!this.volumeEvent.isMsDown) return
let val = this.volumeEvent.msDownVolume + (e.clientX - this.volumeEvent.msDownX) / 70
this.volume = val < 0 ? 0 : val > 1 ? 1 : val
if (this.audio) this.audio.volume = this.volume
// console.log(val)
},
handleCopy(text) {
clipboardWriteText(text)
@@ -483,6 +516,7 @@ export default {
transition: @transition-theme;
transition-property: color;
flex: none;
padding: 2px;
svg {
fill: currentColor;
@@ -492,7 +526,7 @@ export default {
max-height: 100%;
transition: @transition-theme;
transition-property: border-color;
border: 2px solid @color-theme_2-background_1;
// border: 2px solid @color-theme_2-background_1;
}
}
.right {
@@ -527,14 +561,20 @@ export default {
}
.volume-content {
position: relative;
width: 100px;
display: flex;
align-items: center;
padding: 0 15px;
opacity: .5;
transition: opacity @transition-theme;
&:hover {
opacity: 1;
}
}
.volume {
cursor: pointer;
// cursor: pointer;
width: 100%;
height: 0.25em;
border-radius: 10px;
@@ -561,7 +601,16 @@ export default {
transition-duration: 0.2s;
background-color: @color-theme;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
opacity: .5;
}
.volume-mask {
position: absolute;
left: 15px;
right: 15px;
top: 0;
width: 70%;
height: 100%;
cursor: pointer;
}
.play-btn {
@@ -576,7 +625,7 @@ export default {
transition: @transition-theme;
transition-property: color;
color: @color-theme;
transition: opacity 0.2s ease;
transition: opacity 0.1s ease;
opacity: 1;
cursor: pointer;
@@ -684,9 +733,9 @@ each(@themes, {
}
.left {
color: ~'@{color-@{value}-theme}';
img {
border-color: ~'@{color-@{value}-theme_2-background_1}';
}
// img {
// border-color: ~'@{color-@{value}-theme_2-background_1}';
// }
}
.play-btn {
color: ~'@{color-@{value}-theme}';

View File

@@ -2,7 +2,9 @@
div(:class="$style.toolbar")
//- img(v-if="icon")
//- h1 {{title}}
material-search-input(:class="$style.input")
material-search-input(:class="$style.input"
@event="handleEvent" :list="tipList" :visibleList="visibleList"
v-model="searchText")
div(:class="$style.control")
button(type="button" :class="$style.min" title="最小化" @click="min")
//- button(type="button" :class="$style.max" @click="max")
@@ -11,22 +13,93 @@
<script>
import { rendererSend } from 'common/icp'
// import { mapGetters } from 'vuex'
import { mapGetters } from 'vuex'
import music from '../../utils/music'
import { debounce } from '../../utils'
export default {
// props: {
// color: {
// type: String,
// default: color,
// },
// icon: {
// type: Boolean,
// default: false,
// },
// },
data() {
return {
searchText: '',
visibleList: false,
tipList: [],
tipSearch: null,
}
},
computed: {
// ...mapGetters(['theme']),
...mapGetters(['route', 'setting']),
...mapGetters('search', {
storeSearchText: 'searchText',
}),
source() {
return this.setting.search.tempSearchSource
},
isAutoClearInput() {
return this.setting.odc.isAutoClearSearchInput
},
},
watch: {
route(n) {
if (this.isAutoClearInput && n.name != 'search' && this.searchText) this.searchText = ''
},
'storeSearchText'(n) {
if (n !== this.searchText) this.searchText = n
},
searchText(n) {
this.handleTipSearch()
},
},
mounted() {
this.tipSearch = debounce(() => {
if (this.searchText === '') {
this.tipList.splice(0, this.tipList.length)
music[this.source].tempSearch.cancelTempSearch()
return
}
music[this.source].tempSearch.search(this.searchText).then(list => {
this.tipList = list
}).catch(() => {})
}, 50)
},
methods: {
handleEvent({ action, data }) {
switch (action) {
case 'focus':
if (!this.visibleList) this.visibleList = true
if (this.searchText) this.handleTipSearch()
break
case 'blur':
setTimeout(() => {
if (this.visibleList) this.visibleList = false
}, 50)
break
case 'submit':
this.handleSearch()
break
case 'listClick':
this.searchText = this.tipList[data]
this.$nextTick(() => {
this.handleSearch()
})
}
},
handleTipSearch() {
if (!this.visibleList) this.visibleList = true
this.tipSearch()
},
handleSearch() {
if (this.visibleList) this.visibleList = false
setTimeout(() => {
this.$router.push({
path: 'search',
query: {
text: this.searchText,
},
}).catch(_ => _)
}, 200)
},
min() {
rendererSend('min')
},
@@ -56,6 +129,9 @@ export default {
}
.input {
-webkit-app-region: no-drag;
position: absolute;
left: 15px;
top: 13px;
}
// img {

View File

@@ -5,7 +5,7 @@ material-modal(:show="show" :bg-close="bgClose" @close="handleClose")
| {{ info.name }}
br
| {{ info.singer }}
material-btn(:class="$style.btn" :title="!checkSource(type.type) && '目前腾讯音源仅支持下载128k音质'" :disabled="!checkSource(type.type)" :key="type.type" @click="handleClick(type.type)" v-for="type in info.types") {{getTypeName(type.type)}} {{ type.type.toUpperCase() }}{{ type.size && ` - ${type.size.toUpperCase()}` }}
material-btn(:class="$style.btn" :title="!checkSource(type.type) && '腾讯、网易音源仅支持下载128k音质'" :disabled="!checkSource(type.type)" :key="type.type" @click="handleClick(type.type)" v-for="type in info.types") {{getTypeName(type.type)}} {{ type.type.toUpperCase() }}{{ type.size && ` - ${type.size.toUpperCase()}` }}
</template>
@@ -52,7 +52,6 @@ export default {
checkSource(type) {
switch (this.musicInfo.source) {
case 'wy':
return false
case 'tx':
return type == '128k'

View File

@@ -1,31 +1,54 @@
<template lang="pug">
div(:class="[$style.search, focus ? $style.active : '']")
div(:class="[$style.search, focus ? $style.active : '', big ? $style.big : '', small ? $style.small : '']")
div(:class="$style.form")
input(placeholder="Search for something..." v-model.trim="text"
@focus="handleFocus" @blur="handleBlur" @input="handleInput"
input(:placeholder="placeholder" v-model.trim="text"
@focus="handleFocus" @blur="handleBlur" @input="$emit('input', text)"
@change="sendEvent('change')"
@keyup.enter="handleSearch")
button(type="button" @click="handleSearch")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 30.239 30.239' space='preserve')
use(xlink:href='#icon-search')
slot
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 30.239 30.239' space='preserve')
use(xlink:href='#icon-search')
//- transition(name="custom-classes-transition"
//- enter-active-class="animated flipInX"
//- leave-active-class="animated flipOutX")
div(:class="$style.list" :style="listStyle")
div(v-if="list" :class="$style.list" :style="listStyle")
ul(ref="dom_list")
li(v-for="(item, index) in list" :key="item" @click="handleTemplistClick(index)")
span {{item}}
</template>
<script>
import { mapGetters } from 'vuex'
import music from '../../utils/music'
export default {
props: {
placeholder: {
type: String,
default: 'Search for something...',
},
list: {
type: Array,
},
visibleList: {
type: Boolean,
default: false,
},
value: {
type: String,
default: '',
},
big: {
type: Boolean,
default: false,
},
small: {
type: Boolean,
default: false,
},
},
data() {
return {
isShow: false,
text: '',
list: [],
index: null,
focus: false,
listStyle: {
@@ -33,64 +56,35 @@ export default {
},
}
},
computed: {
...mapGetters(['route', 'setting']),
...mapGetters('search', ['searchText']),
isAutoClearInput() {
return this.setting.odc.isAutoClearSearchInput
},
source() {
return this.setting.search.tempSearchSource
},
},
watch: {
list(n) {
if (!this.visibleList) return
this.$nextTick(() => {
this.listStyle.height = this.$refs.dom_list.scrollHeight + 'px'
})
},
'searchText'(n) {
if (n !== this.text) this.text = n
value(n) {
this.text = n
},
route(n) {
if (this.isAutoClearInput && n.name != 'search' && this.text) this.text = ''
visibleList(n) {
n ? this.showList() : this.hideList()
},
},
methods: {
handleTemplistClick(index) {
this.text = this.list[index]
this.handleSearch()
this.sendEvent('listClick', index)
},
handleFocus() {
this.focus = true
if (this.text) this.handleInput()
this.showList()
this.sendEvent('focus')
},
handleBlur() {
setTimeout(() => {
this.focus = false
this.hideList()
}, 200)
this.focus = false
this.sendEvent('blur')
},
handleSearch() {
this.hideList()
this.$router.push({
path: 'search',
query: {
text: this.text,
},
}).catch(_ => _)
},
handleInput() {
if (this.text === '') {
this.list.splice(0, this.list.length)
music[this.source].tempSearch.cancelTempSearch()
return
}
if (!this.isShow) this.showList()
music[this.source].tempSearch.search(this.text).then(list => {
this.list = list
}).catch(() => {})
this.sendEvent('submit')
},
showList() {
this.isShow = true
@@ -100,6 +94,12 @@ export default {
this.isShow = false
this.listStyle.height = 0
},
sendEvent(action, data) {
this.$emit('event', {
action,
data,
})
},
},
}
</script>
@@ -109,9 +109,6 @@ export default {
@import '../../assets/styles/layout.less';
.search {
position: absolute;
left: 15px;
top: 13px;
border-radius: 3px;
transition: box-shadow .4s ease, background-color @transition-theme;
display: flex;
@@ -201,6 +198,19 @@ export default {
}
}
.big {
width: 500px;
// input {
// line-height: 30px;
// }
.form {
height: 30px;
button {
padding: 6px 10px;
}
}
}
each(@themes, {
:global(#container.@{value}) {

View File

@@ -29,9 +29,9 @@ div(:class="$style.songList")
td(style="width: 20%; padding-left: 0; padding-right: 0;")
material-list-buttons(:index="index" :search-btn="true"
:remove-btn="false" @btn-click="handleListBtnClick"
:listAdd-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')"
:play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')"
:download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')")
:listAdd-btn="item.source == 'kw' || (!isAPITemp)"
:play-btn="item.source == 'kw' || (!isAPITemp)"
:download-btn="item.source == 'kw' || (!isAPITemp)")
//- button.btn-info(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k'] || item._types.flac" @click.stop='openDownloadModal(index)') 下载
//- button.btn-secondary(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k']" @click.stop='testPlay(index)') 试听
//- button.btn-success(type='button' v-if="(item._types['128k'] || item._types['192k'] || item._types['320k']) && userInfo" @click.stop='showListModal(index)')
@@ -140,7 +140,7 @@ export default {
this.clickIndex = index
return
}
this.emitEvent((this.source == 'kw' || (!this.isAPITemp && this.list[index].source != 'wy')) ? 'testPlay' : 'search', index)
this.emitEvent((this.source == 'kw' || !this.isAPITemp) ? 'testPlay' : 'search', index)
this.clickTime = 0
this.clickIndex = -1
},

View File

@@ -75,6 +75,7 @@ const addTask = (list, type, store) => {
const getUrl = (downloadInfo, isRefresh) => {
const url = downloadInfo.musicInfo.typeUrl[downloadInfo.type]
if (!downloadInfo.musicInfo._types[downloadInfo.type]) return Promise.reject(new Error('该歌曲没有可下载的音频'))
return url && !isRefresh ? Promise.resolve({ url }) : music[downloadInfo.musicInfo.source].getMusicUrl(downloadInfo.musicInfo, downloadInfo.type).promise
}

View File

@@ -23,6 +23,7 @@ const getters = {
// actions
const actions = {
getUrl({ commit, state }, { musicInfo, type, isRefresh }) {
if (!musicInfo._types[type]) return Promise.reject(new Error('该歌曲没有可播放的音频'))
if (urlRequest && urlRequest.cancelHttp) urlRequest.cancelHttp()
if (musicInfo.typeUrl[type] && !isRefresh) return Promise.resolve()
urlRequest = music[musicInfo.source].getMusicUrl(musicInfo, type)

View File

@@ -1,6 +1,7 @@
import music from '../../utils/music'
const sortList = {}
const sources = []
const cache = new Map()
for (const source of music.sources) {
const songList = music[source.id].songList
if (!songList) continue
@@ -25,6 +26,7 @@ const state = {
page: 1,
limit: 30,
key: null,
info: {},
},
selectListInfo: {},
isVisibleListDetail: false,
@@ -60,16 +62,45 @@ const actions = {
let tabId = rootState.setting.songList.tagInfo.id
let sortId = rootState.setting.songList.sortId
// console.log(sortId)
let key = `${source}${sortId}${tabId}${page}`
if (state.list.list.length && state.list.key == key) return true
return music[source].songList.getList(sortId, tabId, page).then(result => commit('setList', { result, key, page }))
let key = `slist__${source}__${sortId}__${tabId}__${page}`
if (state.list.list.length && state.list.key == key) return
return (cache.has(key) ? Promise.resolve(cache.get(key)) : music[source].songList.getList(sortId, tabId, page)).then(result => commit('setList', { result, key, page }))
},
getListDetail({ state, rootState, commit }, { id, page }) {
let source = rootState.setting.songList.source
let key = `${source}${id}${page}`
let key = `sdetail__${source}__${id}__${page}`
if (state.listDetail.list.length && state.listDetail.key == key) return true
return music[source].songList.getListDetail(id, page).then(result => commit('setListDetail', { result, key, page }))
return (cache.has(key) ? Promise.resolve(cache.get(key)) : music[source].songList.getListDetail(id, page)).then(result => commit('setListDetail', { result, key, page }))
},
/* getListDetailAll({ state, rootState }, id) {
let source = rootState.setting.songList.source
let key = `sdetail__${source}__${id}__all`
if (cache.has(key)) return Promise.resolve(cache.get(key))
music[source].songList.getListDetail(id, 1).then(result => {
let data = { list: result.list, id }
if (result.total <= result.limit) {
data = { list: result.list, id }
cache.set(key, data)
return data
}
let maxPage = Math.ceil(result.total / result.limit)
const loadDetail = (loadPage = 1) => {
let task = []
let loadNum = 0
while (loadPage <= maxPage && loadNum < 3) {
task.push(music[source].songList.getListDetail(id, ++loadPage))
loadNum++
}
return loadPage == maxPage
? Promise.all(task)
: Promise.all(task).then(result => loadDetail(loadPage).then(result2 => [...result, ...result2]))
}
return loadDetail().then(result2 => {
console.log(result2)
})
})
}, */
}
// mitations
@@ -83,16 +114,19 @@ const mutations = {
state.list.limit = result.limit
state.list.page = page
state.list.key = key
cache.set(key, result)
},
setListDetail(state, { result, key, page, desc }) {
setListDetail(state, { result, key, page }) {
state.listDetail.list = result.list
state.listDetail.total = result.total
state.listDetail.limit = result.limit
state.listDetail.page = page
state.listDetail.key = key
state.listDetail.desc = result.desc
state.listDetail.info = result.info || {}
cache.set(key, result)
},
setVisibleListDetail(state, bool) {
if (!bool) state.listDetail.list = []
state.isVisibleListDetail = bool
},
setSelectListInfo(state, info) {

View File

@@ -305,6 +305,24 @@ export const throttle = (fn, delay = 100) => {
}
}
/**
* 生成防抖函数
* @param {*} fn
* @param {*} delay
*/
export const debounce = (fn, delay = 100) => {
let timer = null
let _args = null
return function(...args) {
_args = args
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
timer = null
fn.apply(this, _args)
}, delay)
}
}
const async_removeItem = (arr, num, callback) => window.requestAnimationFrame(() => {
let len = arr.length
if (len > num) {

View File

@@ -8,7 +8,7 @@ export default {
_requestObj_listRecommend: null,
_requestObj_listDetail: null,
limit_list: 20,
limit_song: 25,
limit_song: 1000,
successCode: 22000,
sortList: [
{
@@ -108,11 +108,12 @@ export default {
},
// 获取标签
getTags() {
getTags(tryNum = 0) {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_tags = httpFetch(this.getTagsUrl())
return this._requestObj_tags.promise.then(({ body }) => {
if (body.error_code !== this.successCode) return this.getTags()
if (body.error_code !== this.successCode) return this.getTags(++tryNum)
return {
hotTag: this.filterInfoHotTag(body.result.hot),
tags: this.filterTagInfo(body.result.tags),
@@ -141,13 +142,14 @@ export default {
},
// 获取列表数据
getList(sortId, tagId, page) {
getList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_list = httpFetch(
this.getListUrl(sortId, tagId, page)
)
return this._requestObj_list.promise.then(({ body }) => {
// if (body.error_code !== this.successCode) return this.getList(sortId, tagId, page)
if (body.error_code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
return {
list: this.filterList(body.diyInfo),
total: body.nums,
@@ -183,13 +185,14 @@ export default {
},
// 获取歌曲列表内的音乐
getListDetail(id, page) {
getListDetail(id, page, tryNum = 0) {
if (this._requestObj_listDetail) {
this._requestObj_listDetail.cancelHttp()
}
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page))
return this._requestObj_listDetail.promise.then(({ body }) => {
if (body.error_code !== this.successCode) return this.getListDetail(id, page)
if (body.error_code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
let listData = this.filterData(body.result.songlist)
return {
list: listData,
@@ -197,6 +200,13 @@ export default {
limit: this.limit_song,
total: body.result.song_num,
source: 'bd',
info: {
name: body.result.info.list_title,
img: body.result.info.list_pic,
desc: body.result.info.list_desc,
author: body.result.info.userinfo.username,
play_count: this.formatPlayCount(body.result.listen_num),
},
}
})
},

View File

@@ -37,7 +37,7 @@ export default {
requestObj.promise = requestObj.promise.then(({ body }) => {
if (body.error_code !== 0) return Promise.reject('图片获取失败')
let info = body.data[0].info
return info.image.replace('{size}', info.imgsize[0])
return info.imgsize ? info.image.replace('{size}', info.imgsize[0]) : info.image
})
return requestObj
},

View File

@@ -35,6 +35,7 @@ export default {
],
regExps: {
listData: /global\.data = (\[.+\]);/,
listInfo: /global = {[\s\S]+?name: "(.+)"[\s\S]+?pic: "(.+)"[\s\S]+?};/,
},
getInfoUrl(tagId) {
return tagId
@@ -87,18 +88,20 @@ export default {
return result
},
getSongList(sortId, tagId, page) {
getSongList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_list = httpFetch(
this.getSongListUrl(sortId, tagId, page)
)
return this._requestObj_list.promise.then(({ body }) => {
if (body.status !== 1) return this.getSongList(sortId, tagId, page)
if (body.status !== 1) return this.getSongList(sortId, tagId, page, ++tryNum)
return this.filterList(body.special_db)
})
},
getSongListRecommend() {
getSongListRecommend(tryNum = 0) {
if (this._requestObj_listRecommend) this._requestObj_listRecommend.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listRecommend = httpFetch(
'http://everydayrec.service.kugou.com/guess_special_recommend',
{
@@ -120,7 +123,7 @@ export default {
}
)
return this._requestObj_listRecommend.promise.then(({ body }) => {
if (body.status !== 1) return this.getSongListRecommend()
if (body.status !== 1) return this.getSongListRecommend(++tryNum)
return this.filterList(body.data.special_list)
})
},
@@ -138,18 +141,34 @@ export default {
}))
},
getListDetail(id, page) { // 获取歌曲列表内的音乐
getListDetail(id, page, tryNum = 0) { // 获取歌曲列表内的音乐
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id))
return this._requestObj_listDetail.promise.then(({ body }) => {
let listData = body.match(this.regExps.listData)
if (listData) listData = this.filterData(JSON.parse(RegExp.$1))
let listInfo = body.match(this.regExps.listInfo)
if (!listData) return this.getListDetail(id, page, ++tryNum)
listData = this.filterData(JSON.parse(listData[1]))
let name
let pic
if (listInfo) {
name = listInfo[1]
pic = listInfo[2]
}
return {
list: listData,
page: 1,
limit: 10000,
total: listData.length,
source: 'kg',
info: {
name,
img: pic,
// desc: body.result.info.list_desc,
// author: body.result.info.userinfo.username,
// play_count: this.formatPlayCount(body.result.listen_num),
},
}
})
},
@@ -209,11 +228,12 @@ export default {
},
// 获取列表信息
getListInfo(tagId) {
getListInfo(tagId, tryNum = 0) {
if (this._requestObj_listInfo) this._requestObj_listInfo.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listInfo = httpFetch(this.getInfoUrl(tagId))
return this._requestObj_listInfo.promise.then(({ body }) => {
if (body.status !== 1) return this.getListInfo(tagId)
if (body.status !== 1) return this.getListInfo(tagId, ++tryNum)
return {
limit: body.data.params.pagesize,
page: body.data.params.p,
@@ -246,11 +266,12 @@ export default {
},
// 获取标签
getTags() {
getTags(tryNum = 0) {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_tags = httpFetch(this.getInfoUrl())
return this._requestObj_tags.promise.then(({ body }) => {
if (body.status !== 1) return this.getTags()
if (body.status !== 1) return this.getTags(++tryNum)
return {
hotTag: this.filterInfoHotTag(body.data.hotTag),
tags: this.filterTagInfo(body.data.tagids),

View File

@@ -8,7 +8,7 @@ export default {
_requestObj_list: null,
_requestObj_listDetail: null,
limit_list: 25,
limit_song: 100,
limit_song: 1000,
successCode: 200,
sortList: [
{
@@ -38,20 +38,22 @@ export default {
// http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=2849349915&pn=0&rn=100&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1
// 获取标签
getTag() {
getTag(tryNum = 0) {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_tags = httpFetch(this.tagsUrl)
return this._requestObj_tags.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getTag()
if (body.code !== this.successCode) return this.getTag(++tryNum)
return this.filterTagInfo(body.data)
})
},
// 获取标签
getHotTag() {
getHotTag(tryNum = 0) {
if (this._requestObj_hotTags) this._requestObj_hotTags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_hotTags = httpFetch(this.hotTagUrl)
return this._requestObj_hotTags.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getHotTag()
if (body.code !== this.successCode) return this.getHotTag(++tryNum)
return this.filterInfoHotTag(body.data[0].data)
})
},
@@ -76,8 +78,9 @@ export default {
},
// 获取列表数据
getList(sortId, tagId, page) {
getList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
let id
let type
if (tagId) {
@@ -90,7 +93,7 @@ export default {
this._requestObj_list = httpFetch(this.getListUrl({ sortId, id, type, page }))
return this._requestObj_list.promise.then(({ body }) => {
if (!id || type == '10000') {
if (body.code !== this.successCode) return this.getListUrl({ sortId, id, type, page })
if (body.code !== this.successCode) return this.getList(sortId, id, type, page, ++tryNum)
return {
list: this.filterList(body.data.data),
total: body.data.total,
@@ -99,7 +102,7 @@ export default {
source: 'kw',
}
} else if (!body.length) {
return this.getListUrl({ sortId, id, type, page })
return this.getList(sortId, id, type, page, ++tryNum)
}
return {
list: this.filterList2(body),
@@ -153,19 +156,27 @@ export default {
},
// 获取歌曲列表内的音乐
getListDetail(id, page) {
getListDetail(id, page, tryNum = 0) {
if (this._requestObj_listDetail) {
this._requestObj_listDetail.cancelHttp()
}
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page))
return this._requestObj_listDetail.promise.then(({ body }) => {
if (body.result !== 'ok') return this.getListDetail(id, page)
if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum)
return {
list: this.filterListDetail(body.musiclist),
page,
limit: body.rn,
total: body.total,
source: 'kw',
info: {
name: body.title,
img: body.pic,
desc: body.info,
author: body.uname,
play_count: this.formatPlayCount(body.playnum),
},
}
})
},

View File

@@ -6,7 +6,7 @@ export default {
_requestObj_list: null,
_requestObj_listDetail: null,
limit_list: 10,
limit_song: 100,
limit_song: 1000,
successCode: '000000',
sortList: [
{
@@ -55,17 +55,26 @@ export default {
return num
},
getListDetail(id, page) { // 获取歌曲列表内的音乐
getListDetail(id, page, tryNum = 0) { // 获取歌曲列表内的音乐
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id, page), { headers: this.defaultHeaders })
return this._requestObj_listDetail.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getListDetail(id, page)
if (body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
// console.log(JSON.stringify(body))
return {
list: this.filterListDetail(body.list),
page,
limit: this.limit_song,
total: body.totalCount,
source: 'mg',
info: {
// name: body.result.info.list_title,
// img: body.result.info.list_pic,
// desc: body.result.info.list_desc,
// author: body.result.info.userinfo.username,
// play_count: this.formatPlayCount(body.result.listen_num),
},
}
})
},
@@ -127,8 +136,9 @@ export default {
},
// 获取列表数据
getList(sortId, tagId, page) {
getList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_list = httpFetch(this.getSongListUrl(sortId, tagId, page))
// return this._requestObj_list.promise.then(({ statusCode, body }) => {
// if (statusCode !== 200) return this.getList(sortId, tagId, page)
@@ -150,7 +160,7 @@ export default {
// })
// })
return this._requestObj_list.promise.then(({ body }) => {
if (body.retCode !== '100000' || body.retMsg.code !== this.successCode) return this.getList(sortId, tagId, page)
if (body.retCode !== '100000' || body.retMsg.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
return {
list: this.filterList(body.retMsg.playlist),
total: parseInt(body.retMsg.countSize),
@@ -175,11 +185,12 @@ export default {
},
// 获取标签
getTag() {
getTag(tryNum = 0) {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_tags = httpFetch(this.tagsUrl, { headers: this.defaultHeaders })
return this._requestObj_tags.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getTag()
if (body.code !== this.successCode) return this.getTag(++tryNum)
return this.filterTagInfo(body.columnInfo.contents)
})
},

View File

@@ -1,5 +1,6 @@
import { httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../../index'
import jshtmlencode from 'js-htmlencode'
export default {
_requestObj_tags: null,
@@ -36,7 +37,7 @@ export default {
category_id: id,
size: this.limit_list,
page: page - 1,
use_page: page - 1,
use_page: 1,
},
module: 'playlist.PlayListCategoryServer',
},
@@ -44,7 +45,7 @@ export default {
comm: { cv: 1602, ct: 20 },
playlist: {
method: 'get_playlist_by_tag',
param: { id: 10000000, sin: 0, size: this.limit_list, order: sortId, cur_page: page },
param: { id: 10000000, sin: this.limit_list * (page - 1), size: this.limit_list, order: sortId, cur_page: page },
module: 'playlist.PlayListPlazaServer',
},
}))}`
@@ -55,20 +56,22 @@ export default {
// http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=2849349915&pn=0&rn=100&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1
// 获取标签
getTag() {
getTag(tryNum = 0) {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_tags = httpFetch(this.tagsUrl)
return this._requestObj_tags.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getTag()
if (body.code !== this.successCode) return this.getTag(++tryNum)
return this.filterTagInfo(body.tags.data.v_group)
})
},
// 获取标签
getHotTag() {
getHotTag(tryNum = 0) {
if (this._requestObj_hotTags) this._requestObj_hotTags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_hotTags = httpFetch(this.hotTagUrl)
return this._requestObj_hotTags.promise.then(({ statusCode, body }) => {
if (statusCode !== 200) return this.getHotTag()
if (statusCode !== 200) return this.getHotTag(++tryNum)
return this.filterInfoHotTag(body)
})
},
@@ -102,13 +105,14 @@ export default {
},
// 获取列表数据
getList(sortId, tagId, page) {
getList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_list = httpFetch(
this.getListUrl(sortId, tagId, page)
)
return this._requestObj_list.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getList(sortId, tagId, page)
if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
return tagId ? this.filterList2(body.playlist.data, page) : this.filterList(body.playlist.data, page)
})
},
@@ -153,7 +157,7 @@ export default {
// time: basic.publish_time,
img: basic.cover.medium_url || basic.cover.default_url,
// grade: basic.favorcnt / 10,
desc: basic.desc.replace(/<br>/g, '\n'),
desc: jshtmlencode.htmlDecode(basic.desc).replace(/<br>/g, '\n'),
source: 'tx',
})),
total: content.total_cnt,
@@ -164,10 +168,11 @@ export default {
},
// 获取歌曲列表内的音乐
getListDetail(id) {
getListDetail(id, tryNum = 0) {
if (this._requestObj_listDetail) {
this._requestObj_listDetail.cancelHttp()
}
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id), {
headers: {
Origin: 'https://y.qq.com',
@@ -175,15 +180,21 @@ export default {
},
})
return this._requestObj_listDetail.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getListDetail(id)
if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum)
const cdlist = body.cdlist[0]
return {
list: this.filterListDetail(cdlist.songlist),
page: 1,
limit: cdlist.songlist.length + 1,
total: cdlist.songlist.length,
desc: cdlist.desc.replace(/<br>/g, '\n'),
source: 'tx',
info: {
name: cdlist.dissname,
img: cdlist.logo,
desc: jshtmlencode.htmlDecode(cdlist.desc).replace(/<br>/g, '\n'),
author: cdlist.nickname,
play_count: this.formatPlayCount(cdlist.visitnum),
},
}
})
},

View File

@@ -1,41 +0,0 @@
import { httpFatch } from '../../request'
import { requestMsg } from '../../message'
import { headers, timeout } from '../options'
const api_messoer = {
getMusicUrl(songInfo, type) {
const requestObj = httpFatch(`https://v1.itooi.cn/netease/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
method: 'get',
timeout,
headers,
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
})
return requestObj
},
getPic(songInfo) {
const requestObj = httpFatch(`https://v1.itooi.cn/netease/pic?id=${songInfo.songmid}&isRedirect=0`, {
method: 'get',
timeout,
headers,
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
})
return requestObj
},
getLyric(songInfo) {
const requestObj = httpFatch(`https://v1.itooi.cn/netease/lrc?id=${songInfo.songmid}&isRedirect=0`, {
method: 'get',
timeout,
headers,
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body ? Promise.resolve(body) : Promise.reject(new Error(requestMsg.fail))
})
return requestObj
},
}
export default api_messoer

View File

@@ -15,8 +15,8 @@ const api_test = {
})
return requestObj
},
getPic(songInfo) {
const requestObj = httpFetch(`http://ts.tempmusic.tk/pic/wy/${songInfo.songmid}`, {
/* getPic(songInfo) {
const requestObj = httpFetch(`http://localhost:3100/pic/wy/${songInfo.songmid}`, {
method: 'get',
timeout,
headers,
@@ -28,7 +28,7 @@ const api_test = {
return requestObj
},
getLyric(songInfo) {
const requestObj = httpFetch(`http://ts.tempmusic.tk/lrc/wy/${songInfo.songmid}`, {
const requestObj = httpFetch(`http://localhost:3100/lrc/wy/${songInfo.songmid}`, {
method: 'get',
timeout,
headers,
@@ -38,7 +38,7 @@ const api_test = {
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
})
return requestObj
},
}, */
}
export default api_test

View File

@@ -1,16 +1,22 @@
import leaderboard from './leaderboard'
import api_source from '../api-source'
import getLyric from './lyric'
import getMusicInfo from './musicInfo'
import musicSearch from './musicSearch'
import songList from './songList'
const wy = {
leaderboard,
musicSearch,
songList,
getMusicUrl(songInfo, type) {
return api_source('wy').getMusicUrl(songInfo, type)
},
getLyric(songInfo) {
return api_source('wy').getLyric(songInfo)
return getLyric(songInfo.songmid)
},
getPic(songInfo) {
return api_source('wy').getPic(songInfo)
return getMusicInfo(songInfo.songmid).then(info => info.al.picUrl)
},
}

View File

@@ -0,0 +1,21 @@
import { httpFetch } from '../../request'
import { weapi } from './utils/crypto'
export default songmid => {
const requestObj = httpFetch('http://music.163.com/weapi/song/lyric?csrf_token=', {
method: 'post',
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
Referer: 'https://music.163.com/song?id=' + songmid,
origin: 'https://music.163.com',
},
form: weapi({ id: songmid, lv: -1, tv: -1, csrf_token: '' }),
})
requestObj.promise = requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code !== 200) return Promise.reject('获取歌词失败')
return body.lrc.lyric
})
return requestObj
}

View File

@@ -0,0 +1,25 @@
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js
import { httpFetch } from '../../request'
import { weapi } from './utils/crypto'
export default songmid => {
const requestObj = httpFetch('https://music.163.com/weapi/v3/song/detail', {
method: 'post',
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
Referer: 'https://music.163.com/song?id=' + songmid,
origin: 'https://music.163.com',
},
form: weapi({
c: `[{"id":${songmid}}]`,
ids: `[${songmid}]`,
}),
})
requestObj.promise = requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code !== 200 || !body.songs.length) return Promise.reject('获取歌曲信息失败')
return body.songs[0]
})
return requestObj
}

View File

@@ -0,0 +1,102 @@
import { httpFetch } from '../../request'
import { weapi } from './utils/crypto'
import { sizeFormate, formatPlayTime } from '../../index'
let searchRequest
export default {
limit: 30,
total: 0,
page: 0,
allPage: 1,
musicSearch(str, page) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp()
searchRequest = httpFetch('http://music.163.com/weapi/cloudsearch/get/web?csrf_token=', {
method: 'post',
form: weapi({
s: str,
type: 1, // 1: 单曲, 10: 专辑, 100: 歌手, 1000: 歌单, 1002: 用户, 1004: MV, 1006: 歌词, 1009: 电台, 1014: 视频
limit: this.limit,
offset: this.limit * (page - 1),
}),
})
return searchRequest.promise.then(({ body }) => body)
},
getSinger(singers) {
let arr = []
singers.forEach(singer => {
arr.push(singer.name)
})
return arr.join('、')
},
handleResult(rawList) {
// console.log(rawList)
return rawList.map(item => {
const types = []
const _types = {}
let size
switch (item.privilege.maxbr) {
case 999000:
size = null
types.push({ type: 'flac', size })
_types.flac = {
size,
}
case 320000:
if (item.h) {
size = sizeFormate(item.h.size)
types.push({ type: '320k', size })
_types['320k'] = {
size,
}
}
case 128000:
if (item.l) {
size = sizeFormate(item.l.size)
types.push({ type: '128k', size })
_types['128k'] = {
size,
}
}
}
types.reverse()
return {
singer: this.getSinger(item.ar),
name: item.name,
albumName: item.al.name,
albumId: item.al.id,
source: 'wy',
interval: formatPlayTime(item.dt / 1000),
songmid: item.id,
img: item.al.picUrl,
lrc: null,
types,
_types,
typeUrl: {},
}
})
},
search(str, page = 1, { limit } = {}) {
if (limit != null) this.limit = limit
return this.musicSearch(str, page).then(result => {
// console.log(JSON.stringify(result))
if (!result || result.code !== 200) return this.search(str, page, { limit })
let list = this.handleResult(result.result.songs)
if (list == null) return this.search(str, page, { limit })
this.total = result.result.songCount
this.page = page
this.allPage = Math.ceil(this.total / this.limit)
return Promise.resolve({
list,
allPage: this.allPage,
limit: this.limit,
total: this.total,
source: 'wy',
})
})
},
}

View File

@@ -0,0 +1,242 @@
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/playlist_catlist.js
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/playlist_hot.js
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/top_playlist.js
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/playlist_detail.js
import { weapi, linuxapi } from './utils/crypto'
import { httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../../index'
export default {
_requestObj_tags: null,
_requestObj_hotTags: null,
_requestObj_list: null,
_requestObj_listDetail: null,
limit_list: 30,
limit_song: 100000,
successCode: 200,
sortList: [
{
name: '最热',
id: 'hot',
},
{
name: '最新',
id: 'new',
},
],
/**
* 格式化播放数量
* @param {*} num
*/
formatPlayCount(num) {
if (num > 100000000) return parseInt(num / 10000000) / 10 + '亿'
if (num > 10000) return parseInt(num / 1000) / 10 + '万'
return num
},
getSinger(singers) {
let arr = []
singers.forEach(singer => {
arr.push(singer.name)
})
return arr.join('、')
},
getListDetail(id, page, tryNum = 0) { // 获取歌曲列表内的音乐
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch('https://music.163.com/api/linux/forward', {
method: 'post',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
form: linuxapi({
method: 'POST',
url: 'https://music.163.com/api/v3/playlist/detail',
params: {
id,
n: this.limit_song,
s: 8,
},
}),
})
return this._requestObj_listDetail.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
return {
list: this.filterListDetail(body),
page,
limit: this.limit_song,
total: body.playlist.tracks.length,
source: 'wy',
info: {
play_count: this.formatPlayCount(body.playlist.playCount),
name: body.playlist.name,
img: body.playlist.coverImgUrl,
desc: body.playlist.description,
author: body.playlist.creator.nickname,
},
}
})
},
filterListDetail({ playlist: { tracks }, privileges }) {
// console.log(tracks, privileges)
const list = []
tracks.forEach((item, index) => {
const types = []
const _types = {}
let size
let privilege = privileges[index]
if (privilege.id !== item.id) privilege = privileges.find(p => p.id === item.id)
if (!privilege) return
switch (privilege.maxbr) {
case 999000:
size = null
types.push({ type: 'flac', size })
_types.flac = {
size,
}
case 320000:
if (item.h) {
size = sizeFormate(item.h.size)
types.push({ type: '320k', size })
_types['320k'] = {
size,
}
}
case 128000:
if (item.l) {
size = sizeFormate(item.l.size)
types.push({ type: '128k', size })
_types['128k'] = {
size,
}
}
}
types.reverse()
list.push({
singer: this.getSinger(item.ar),
name: item.name,
albumName: item.al.name,
albumId: item.al.id,
source: 'wy',
interval: formatPlayTime(item.dt / 1000),
songmid: item.id,
img: item.al.picUrl,
lrc: null,
types,
_types,
typeUrl: {},
})
})
return list
},
// 获取列表数据
getList(sortId, tagId, page, tryNum = 0) {
if (tryNum > 2) return Promise.reject(new Error('try max num'))
if (this._requestObj_list) this._requestObj_list.cancelHttp()
this._requestObj_list = httpFetch('https://music.163.com/weapi/playlist/list', {
method: 'post',
form: weapi({
cat: tagId || '全部', // 全部,华语,欧美,日语,韩语,粤语,小语种,流行,摇滚,民谣,电子,舞曲,说唱,轻音乐,爵士,乡村,R&B/Soul,古典,民族,英伦,金属,朋克,蓝调,雷鬼,世界音乐,拉丁,另类/独立,New Age,古风,后摇,Bossa Nova,清晨,夜晚,学习,工作,午休,下午茶,地铁,驾车,运动,旅行,散步,酒吧,怀旧,清新,浪漫,性感,伤感,治愈,放松,孤独,感动,兴奋,快乐,安静,思念,影视原声,ACG,儿童,校园,游戏,70后,80后,90后,网络歌曲,KTV,经典,翻唱,吉他,钢琴,器乐,榜单,00后
order: sortId, // hot,new
limit: this.limit_list,
offset: this.limit_list * (page - 1),
total: true,
}),
})
return this._requestObj_list.promise.then(({ body }) => {
// console.log(JSON.stringify(body))
if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
return {
list: this.filterList(body.playlists),
total: parseInt(body.total),
page,
limit: this.limit_list,
source: 'wy',
}
})
},
filterList(rawData) {
return rawData.map(item => ({
play_count: this.formatPlayCount(item.playCount),
id: item.id,
author: item.creator.nickname,
name: item.name,
time: item.createTime,
img: item.coverImgUrl,
grade: item.grade,
desc: item.description,
source: 'wy',
}))
},
// 获取标签
getTag(tryNum = 0) {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_tags = httpFetch('https://music.163.com/weapi/playlist/catalogue', {
method: 'post',
form: weapi({}),
})
return this._requestObj_tags.promise.then(({ body }) => {
// console.log(JSON.stringify(body))
if (body.code !== this.successCode) return this.getTag(++tryNum)
return this.filterTagInfo(body)
})
},
filterTagInfo({ sub, categories }) {
const subList = {}
for (const item of sub) {
if (!subList[item.category]) subList[item.category] = []
subList[item.category].push({
parent_id: categories[item.category],
parent_name: categories[item.category],
id: item.name,
name: item.name,
source: 'wy',
})
}
const list = []
for (const key of Object.keys(categories)) {
list.push({
name: categories[key],
list: subList[key],
source: 'wy',
})
}
return list
},
// 获取热门标签
getHotTag(tryNum = 0) {
if (this._requestObj_hotTags) this._requestObj_hotTags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_hotTags = httpFetch('https://music.163.com/weapi/playlist/hottags', {
method: 'post',
form: weapi({}),
})
return this._requestObj_hotTags.promise.then(({ body }) => {
// console.log(JSON.stringify(body))
if (body.code !== this.successCode) return this.getTag(++tryNum)
return this.filterHotTagInfo(body.tags)
})
},
filterHotTagInfo(rawList) {
return rawList.map(item => ({
id: item.playlistTag.name,
name: item.playlistTag.name,
source: 'wy',
}))
},
getTags() {
return Promise.all([this.getTag(), this.getHotTag()]).then(([tags, hotTag]) => ({ tags, hotTag, source: 'wy' }))
},
}
// getList
// getTags
// getListDetail

View File

@@ -0,0 +1,33 @@
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/util/crypto.js
import { createCipheriv, publicEncrypt, constants, randomBytes } from 'crypto'
const iv = Buffer.from('0102030405060708')
const presetKey = Buffer.from('0CoJUm6Qyw8W8jud')
const linuxapiKey = Buffer.from('rFgB&h#%2?^eDg:Q')
const base62 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
const publicKey = '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgtQn2JZ34ZC28NWYpAUd98iZ37BUrX/aKzmFbt7clFSs6sXqHauqKWqdtLkF2KexO40H1YTX8z2lSgBBOAxLsvaklV8k4cBFK9snQXE9/DDaFt6Rr7iVZMldczhC0JNgTz+SHXT6CBHuX3e9SdB1Ua44oncaTWz7OBGLbCiK45wIDAQAB\n-----END PUBLIC KEY-----'
const aesEncrypt = (buffer, mode, key, iv) => {
const cipher = createCipheriv('aes-128-' + mode, key, iv)
return Buffer.concat([cipher.update(buffer), cipher.final()])
}
const rsaEncrypt = (buffer, key) => {
buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer])
return publicEncrypt({ key: key, padding: constants.RSA_NO_PADDING }, buffer)
}
export const weapi = object => {
const text = JSON.stringify(object)
const secretKey = randomBytes(16).map(n => (base62.charAt(n % 62).charCodeAt()))
return {
params: aesEncrypt(Buffer.from(aesEncrypt(Buffer.from(text), 'cbc', presetKey, iv).toString('base64')), 'cbc', secretKey, iv).toString('base64'),
encSecKey: rsaEncrypt(secretKey.reverse(), publicKey).toString('hex'),
}
}
export const linuxapi = object => {
const text = JSON.stringify(object)
return {
eparams: aesEncrypt(Buffer.from(text), 'ecb', linuxapiKey, '').toString('hex').toUpperCase(),
}
}

View File

@@ -3,6 +3,7 @@ import request from 'request'
import { debugRequest } from './env'
import { requestMsg } from './message'
import { bHh } from './music/options'
import { deflateRawSync } from 'zlib'
// import fs from 'fs'
const defaultHeaders = {
@@ -222,6 +223,8 @@ const getProxyInfo = () => window.globalObj.proxy.enable
? `http://${window.globalObj.proxy.username}:${window.globalObj.proxy.password}@${window.globalObj.proxy.host}:${window.globalObj.proxy.port};`
: undefined
const regx = /(?:\d\w)+/g
const fetchData = (url, method, {
headers = {},
format = 'json',
@@ -230,11 +233,13 @@ const fetchData = (url, method, {
}, callback) => {
// console.log(url, options)
console.log('---start---', url)
headers = Object.assign({}, headers)
if (headers[bHh]) {
let s = Buffer.from(bHh, 'hex').toString()
s = s.replace(s.substr(-1), '')
s = Buffer.from(s, 'base64').toString()
headers[s] = !s || parseInt(process.versions.app.split('.').map(n => n.length < 3 ? n.padStart(3, '0') : n).join(''))
let v = process.versions.app.split('.').map(n => n.length < 3 ? n.padStart(3, '0') : n).join('')
headers[s] = !s || `${deflateRawSync(Buffer.from(JSON.stringify(`${url}${v}`.match(regx), null, 1).concat(v)).toString('base64')).toString('hex')}&${parseInt(v)}`
delete headers[bHh]
}
return request(url, {

View File

@@ -126,6 +126,7 @@ export default {
handleAddDownloadMultiple(type) {
switch (this.source) {
// case 'kg':
case 'tx':
case 'wy':
type = '128k'
}

View File

@@ -18,7 +18,7 @@
table
tbody
tr(v-for='(item, index) in list' :key='item.songmid'
@click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', (isAPITemp && item.source != 'kw') || item.source == 'wy' ? $style.disabled : '']")
@click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', (isAPITemp && item.source != 'kw') ? $style.disabled : '']")
td.nobreak.center(style="width: 37px;" @click.stop)
material-checkbox(:id="index.toString()" v-model="selectdData" :value="item")
td.break(style="width: 25%;")
@@ -218,7 +218,7 @@ export default {
this.clickIndex = -1
},
testPlay(index) {
if ((this.isAPITemp && this.list[index].source != 'kw') || this.list[index].source == 'wy') return
if (this.isAPITemp && this.list[index].source != 'kw') return
this.setPlayList({ list: this.list, listId: this.listId, index })
},
handleRemove(index) {
@@ -228,7 +228,7 @@ export default {
switch (info.action) {
case 'download': {
const minfo = this.list[info.index]
if ((this.isAPITemp && minfo.source != 'kw') || minfo.source == 'wy') return
if (this.isAPITemp && minfo.source != 'kw') return
this.musicInfo = minfo
this.$nextTick(() => {
this.isShowDownload = true
@@ -261,7 +261,7 @@ export default {
this.selectdData = []
},
handleAddDownloadMultiple(type) {
const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : this.selectdData.filter(s => s.source != 'wy')
const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : [...this.selectdData]
this.createDownloadMultiple({ list, type })
this.resetSelect()
this.isShowDownloadMultiple = false

View File

@@ -30,8 +30,8 @@
td.break(style="width: 25%;") {{item.albumName}}
td(style="width: 15%; padding-left: 0; padding-right: 0;")
material-list-buttons(:index="index" :remove-btn="false" :class="$style.listBtn"
:play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')"
:download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')"
:play-btn="item.source == 'kw' || !isAPITemp"
:download-btn="item.source == 'kw' || !isAPITemp"
@btn-click="handleListBtnClick")
td(style="width: 10%;") {{item.interval || '--/--'}}
div(:class="$style.pagination")
@@ -189,7 +189,7 @@ export default {
targetSong = this.selectdData[0]
this.listAddMultiple({ id: 'default', list: this.filterList(this.selectdData) })
} else {
if ((this.isAPITemp && this.listInfo.list[index].source != 'kw') || this.listInfo.list[index].source == 'wy') return
if (this.isAPITemp && this.listInfo.list[index].source != 'kw') return
targetSong = this.listInfo.list[index]
this.listAdd({ id: 'default', musicInfo: targetSong })
}
@@ -238,7 +238,7 @@ export default {
}
},
filterList(list) {
return this.setting.apiSource == 'temp' ? list.filter(s => s.source == 'kw') : list.filter(s => s.source != 'wy')
return this.setting.apiSource == 'temp' ? list.filter(s => s.source == 'kw') : [...list]
},
handleListAddModalClose(isSelect) {
if (isSelect) this.resetSelect()

View File

@@ -262,7 +262,7 @@ export default {
// },
{
id: 'test',
label: '测试接口(软件的大部分功能可用,该接口访问速度略慢)',
label: '测试接口(几乎软件的所有功能可用,该接口访问速度略慢)',
disabled: false,
},
{

View File

@@ -1,34 +1,45 @@
<template lang="pug">
div(:class="$style.container")
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
div(:class="$style.songListDetailContent" v-show="isVisibleListDetail")
div(:class="$style.songListDetailContent" v-if="isVisibleListDetail")
div(:class="$style.songListHeader")
div(:class="$style.songListHeaderLeft")
img(:src="selectListInfo.img")
span(:class="$style.playNum" v-if="selectListInfo.play_count") {{selectListInfo.play_count}}
img(:src="listDetail.info.img || selectListInfo.img")
span(:class="$style.playNum" v-if="listDetail.info.play_count || selectListInfo.play_count") {{listDetail.info.play_count || selectListInfo.play_count}}
div(:class="$style.songListHeaderMiddle")
h3(:title="selectListInfo.name") {{selectListInfo.name}}
p(:title="selectListInfo.desc") {{listDetail.desc || selectListInfo.desc}}
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")
//- material-btn(:class="$style.closeDetailButton" :disabled="detailLoading" @click="addSongListDetail") 添加
//- | &nbsp;
//- material-btn(:class="$style.closeDetailButton" :disabled="detailLoading" @click="playSongListDetail") 播放
//- | &nbsp;
material-btn(:class="$style.closeDetailButton" @click="hideListDetail") 返回
material-song-list(v-model="selectdData" @action="handleSongListAction" :source="source" :page="listDetail.page" :limit="listDetail.limit" :total="listDetail.total" :list="listDetail.list")
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
div(:class="$style.songListContent" v-show="!isVisibleListDetail")
div(:class="$style.songListContainer" v-if="!isVisibleListDetail")
div(:class="$style.header")
material-tag-list(:class="$style.tagList" :list="tagList" v-model="tagInfo")
material-tab(:class="$style.tab" :list="sorts" item-key="id" item-name="name" v-model="sortId")
material-select(:class="$style.select" :list="sourceInfo.sources" item-key="id" item-name="name" v-model="source")
div.scroll(:class="$style.songList" ref="dom_scrollContent")
ul
li(:class="$style.item" v-for="(item, index) in listData.list" @click="handleItemClick(index)")
div(:class="$style.left")
img(:src="item.img")
div(:class="$style.right" :src="item.img")
h4(:title="item.name") {{item.name}}
p(:title="item.desc") {{item.desc}}
li(:class="$style.item" style="cursor: default;" v-if="listData.list && listData.list.length && listData.list.length % 3 == 2")
div(:class="$style.pagination")
material-pagination(:count="listData.total" :limit="listData.limit" :page="listData.page" @btn-click="handleToggleListPage")
div(:class="$style.songListContent")
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
div.scroll(:class="$style.songList" v-if="sortId !== 'importSongList'" ref="dom_scrollContent")
ul
li(:class="$style.item" v-for="(item, index) in listData.list" @click="handleItemClick(index)")
div(:class="$style.left")
img(:src="item.img")
div(:class="$style.right" :src="item.img")
h4(:title="item.name") {{item.name}}
p(:title="item.desc") {{item.desc}}
li(:class="$style.item" style="cursor: default;" v-if="listData.list && listData.list.length && listData.list.length % 3 == 2")
div(:class="$style.pagination")
material-pagination(:count="listData.total" :limit="listData.limit" :page="listData.page" @btn-click="handleToggleListPage")
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
div(:class="$style.importSongListContent" v-show="sortId === 'importSongList'")
material-search-input(v-model="importSongListText" @event="handleImportSongListEvent" big placeholder="输入歌单链接或歌单ID")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve')
use(xlink:href='#icon-right')
material-download-modal(:show="isShowDownload" :musicInfo="musicInfo" @select="handleAddDownload" @close="isShowDownload = false")
material-download-multiple-modal(:show="isShowDownloadMultiple" :list="selectdData" @select="handleAddDownloadMultiple" @close="isShowDownloadMultiple = false")
material-list-add-modal(:show="isShowListAdd" :musicInfo="musicInfo" @close="isShowListAdd = false")
@@ -56,6 +67,8 @@ export default {
isToggleSource: false,
isShowListAdd: false,
isShowListAddMultiple: false,
importSongListText: '',
// detailLoading: true,
}
},
computed: {
@@ -63,7 +76,21 @@ export default {
...mapGetters('songList', ['sourceInfo', 'tags', 'listData', 'isVisibleListDetail', 'selectListInfo', 'listDetail']),
...mapGetters('list', ['defaultList']),
sorts() {
return this.source ? this.sourceInfo.sortList[this.source] : []
let list
list = this.source ? [...this.sourceInfo.sortList[this.source]] : []
switch (this.source) {
case 'wy':
case 'kw':
case 'bd':
case 'tx':
case 'mg':
case 'kg':
list.push({
name: `打开${this.sourceInfo.sources.find(s => s.id == this.source).name}歌单`,
id: 'importSongList',
})
}
return list
},
isAPITemp() {
return this.setting.apiSource == 'temp'
@@ -76,6 +103,7 @@ export default {
sortId(n, o) {
this.setSongList({ sortId: n })
if (o === undefined && this.listData.page !== 1) return
if (n == 'importSongList') return
this.$nextTick(() => {
this.getList(1).then(() => {
this.$nextTick(() => {
@@ -88,6 +116,7 @@ export default {
tagInfo(n, o) {
this.setSongList({ tagInfo: n })
if (!o && this.listData.page !== 1) return
if (this.sortId == 'importSongList') this.sortId = this.sorts[0] && this.sorts[0].id
if (this.isToggleSource) {
this.isToggleSource = false
return
@@ -104,6 +133,7 @@ export default {
source(n, o) {
this.setSongList({ source: n })
if (!this.tags[n]) this.getTags()
this.importSongListText = ''
if (o) {
this.isToggleSource = true
this.tagInfo = {
@@ -200,6 +230,7 @@ export default {
handleAddDownloadMultiple(type) {
switch (this.source) {
// case 'kg':
case 'tx':
case 'wy':
type = '128k'
}
@@ -208,6 +239,7 @@ export default {
this.isShowDownloadMultiple = false
},
handleItemClick(index) {
// this.detailLoading = true
this.setSelectListInfo(this.listData.list[index])
this.setVisibleListDetail(true)
this.clearListDetail()
@@ -252,6 +284,76 @@ export default {
if (isSelect) this.resetSelect()
this.isShowListAddMultiple = false
},
handleImportSongListEvent({ action }) {
switch (action) {
case 'submit':
this.handleGetSongListDetail()
break
case 'blur':
this.parseImportSongListInputText()
break
}
},
handleGetSongListDetail() {
this.parseImportSongListInputText()
this.setSelectListInfo({
play_count: null,
id: this.importSongListText,
author: '',
name: '',
img: null,
desc: '',
source: this.source,
})
this.clearListDetail()
this.$nextTick(() => {
this.setVisibleListDetail(true)
this.getListDetail({ id: this.importSongListText, page: 1 })
})
},
parseImportSongListInputText() {
if (!/[?&:/]/.test(this.importSongListText)) return
const text = this.importSongListText
let regx
switch (this.source) {
case 'wy':
regx = /^.+(?:\?|&)id=(\d+)(?:&.*$|#.*$|$)/
break
case 'tx':
// https://y.qq.com/n/yqq/playlist/7217720898.html
// https://i.y.qq.com/n2/m/share/details/taoge.html?platform=11&appshare=android_qq&appversion=9050006&id=7217720898&ADTAG=qfshare
regx = /\/\/i\.y\.qq\.com/.test(text) ? /^.+(?:\?|&)id=(\d+)(?:&.*$|#.*$|$)/ : /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/
break
case 'kw':
// http://www.kuwo.cn/playlist_detail/2886046289
regx = /^.+\/playlist_detail\/(\d+)(?:\?.*|&.*$|#.*$|$)/
break
case 'bd':
// http://music.taihe.com/songlist/566347741
regx = /^.+\/songlist\/(\d+)(?:\?.*|&.*$|#.*$|$)/
break
case 'mg':
// http://music.migu.cn/v3/music/playlist/161044573?page=1
regx = /^.+\/playlist\/(\d+)(?:\?.*|&.*$|#.*$|$)/
break
case 'kg':
// https://www.kugou.com/yy/special/single/1067062.html
regx = /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/
break
default:
return
}
this.importSongListText = text.replace(regx, '$1')
},
/* addSongListDetail() {
// this.detailLoading = true
// this.getListDetailAll(this.selectListInfo.id).then(() => {
// this.detailLoading = false
// })
},
playSongListDetail() {
}, */
},
}
</script>
@@ -280,18 +382,28 @@ export default {
width: 80px;
}
.songListContent, .song-list-detail-content {
.songListContainer, .song-list-detail-content, .songListContent, .importSongListContent {
// flex: auto;
overflow: hidden;
}
.songListContent {
.songListContainer, .songListContent, .importSongListContent {
position: relative;
display: flex;
flex-flow: column nowrap;
height: 100%;
// position: relative;
}
.importSongListContent {
position: absolute;
top: 0;
left: 0;
width: 100%;
align-items: center;
padding-top: 20%;
}
.song-list-header {
display: flex;
flex-flow: row nowrap;