You've already forked lx-music-desktop
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e93052f0cb | ||
|
|
9539083f7e | ||
|
|
8f26eb1c69 | ||
|
|
a126e5b4c8 | ||
|
|
033c3cd8e9 | ||
|
|
50d029c671 | ||
|
|
6a3f7e6150 | ||
|
|
aadfbefbe9 | ||
|
|
edbcaf0f90 | ||
|
|
207427e993 | ||
|
|
e109dd876a | ||
|
|
64ac69c48b | ||
|
|
652b06c116 | ||
|
|
654035a469 | ||
|
|
cd50e0086b | ||
|
|
66a119e9dc | ||
|
|
14ec7d48f6 | ||
|
|
5ad5b211e9 | ||
|
|
aac4b6ccfe | ||
|
|
cfc6967fdc | ||
|
|
8a0b960985 | ||
|
|
bd260c3dc5 | ||
|
|
203dff8d98 | ||
|
|
ca56089a3e | ||
|
|
a012d1b927 | ||
|
|
0af322a3a8 | ||
|
|
8d743803c8 | ||
|
|
63fed77c5b | ||
|
|
f2077dca04 | ||
|
|
cccfb6a08a | ||
|
|
fa6dead83d | ||
|
|
605418d750 | ||
|
|
8aa869b2e4 | ||
|
|
558b6e0ccf | ||
|
|
6a695368da | ||
|
|
ce89f29aeb | ||
|
|
6d935d8589 | ||
|
|
967093328f | ||
|
|
48e1da128d | ||
|
|
369de684a6 | ||
|
|
d5f65eea06 | ||
|
|
8e5deb5bf4 | ||
|
|
df653258bb | ||
|
|
ed28a59fcb | ||
|
|
adc46411b2 | ||
|
|
fcc4d666ec | ||
|
|
9d89f5d48d | ||
|
|
88c62e01ad | ||
|
|
1823598a8b | ||
|
|
3fd22988c1 | ||
|
|
a3ade4cde2 | ||
|
|
55c2286eca | ||
|
|
1afa050698 | ||
|
|
dfa15c6952 | ||
|
|
a2af321c5e | ||
|
|
fddfaf23ce | ||
|
|
ae0ee2048a | ||
|
|
6659d1fe43 | ||
|
|
f601e61f8d | ||
|
|
f8ed7a112a | ||
|
|
b0d6181bd7 | ||
|
|
82843b14b4 | ||
|
|
0df3ca3c5f | ||
|
|
c881f8e886 | ||
|
|
f1bf274de1 | ||
|
|
91e4e9c2d5 | ||
|
|
63a5f94a67 | ||
|
|
1bd8694262 | ||
|
|
501231adff |
2
.github/ISSUE_TEMPLATE/----.md
vendored
2
.github/ISSUE_TEMPLATE/----.md
vendored
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: 功能请求
|
||||
name: 功能请求(请先查看常见问题及搜索issue列表中有无你要提的问题)
|
||||
about: 为这个项目提出一个想法
|
||||
title: 例如:添加xxx功能、优化xxx功能
|
||||
labels: ''
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/--bug.md
vendored
2
.github/ISSUE_TEMPLATE/--bug.md
vendored
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: 报告Bug
|
||||
name: 报告Bug(请先查看常见问题及搜索issue列表中有无你要提的问题)
|
||||
about: 创建报告以帮助我们改进
|
||||
title: 例如:音乐无法播放
|
||||
labels: ''
|
||||
|
||||
12
.travis.yml
12
.travis.yml
@@ -1,19 +1,17 @@
|
||||
sudo: true
|
||||
language: node_js
|
||||
node_js: 12
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
osx_image: xcode10.2
|
||||
language: node_js
|
||||
node_js: "12"
|
||||
env:
|
||||
- ELECTRON_CACHE=$HOME/.cache/electron
|
||||
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
|
||||
|
||||
- os: linux
|
||||
language: node_js
|
||||
node_js: "12"
|
||||
dist: trusty
|
||||
services: docker
|
||||
language: generic
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@@ -26,6 +24,8 @@ notifications:
|
||||
email: false
|
||||
|
||||
script:
|
||||
- node --version
|
||||
- npm --version
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||
npm install && npm run publish:gh:linux
|
||||
|
||||
96
CHANGELOG.md
96
CHANGELOG.md
@@ -6,6 +6,102 @@ 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.13.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.13.0...v0.13.1) - 2019-12-16
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复全局更新弹窗无法遮盖搜索框的问题
|
||||
|
||||
### 其他
|
||||
|
||||
- 由于electron 7.1.3 - 7.1.5 的自动更新功能存在Bug,现降级到7.1.2
|
||||
|
||||
## [0.13.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.12.1...v0.13.0) - 2019-12-15
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增搜索框搜索建议键盘上下方向键选择功能
|
||||
- 聚合搜索新增音源显示
|
||||
- 新增“离开搜索界面时清空搜索列表”设置选项,默认关闭,可到设置-强迫症设置开启
|
||||
|
||||
### 优化
|
||||
|
||||
- 优化“信口雌黄”皮肤配色
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复存在弹出层时,搜索建议列表被弹出层覆盖的问题
|
||||
- 修复搜索、排行榜、歌单列表多选框从不定状态到选中的Bug
|
||||
|
||||
### 移除
|
||||
|
||||
- 因Q音接口失效,移除Q音源的试听与下载
|
||||
|
||||
### 其他
|
||||
|
||||
- 更新electron到7.1.5
|
||||
- 更新vue到2.6.11
|
||||
|
||||
## [0.12.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.12.0...v0.12.1) - 2019-12-01
|
||||
|
||||
### 优化
|
||||
|
||||
- 优化定位歌曲时的列表滚动机制
|
||||
- 优化链接点击效果
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复使用酷我源下载歌曲时,当歌曲无封面时下载报错的问题
|
||||
- 修复酷我源排行榜、歌单详情列表里的歌曲音质匹配问题(原来无论歌曲有无高品、无损都会显示有)
|
||||
- 禁止外部链接在软件内打开,将所有外部链接从默认浏览器打开
|
||||
|
||||
### 其他
|
||||
|
||||
- 更新electron到7.1.2
|
||||
|
||||
## [0.12.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.11.0...v0.12.0) - 2019-11-17
|
||||
|
||||
由于新下载库仍然没有完成,但下载功能已经可用,so 移除之前使用的第三方下载库,暂时把新下载库的下载模块直接加入本程序,若出现下载问题欢迎反馈!
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增下载功能对代理设置的支持,现在若在软件设置了代理服务器,下载功能也将会走代理网络了
|
||||
|
||||
### 优化
|
||||
|
||||
- 新下载模块将对恢复下载的任务进行字节校验,用于解决下载进度超过100%后仍然下载的问题
|
||||
- 注意:目前仍然无法暂停处于**链接获取**状态中的任务
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复Linux deb版本`.desktop`桌面文件缺少图标的问题,新增中文名称显示、软件分类,感谢@lowy的反馈!
|
||||
- 修复下载列表歌曲状态分类列表操作Bug
|
||||
- 修复歌曲封面下载失败时仍然执行嵌入封面操作导致报错的问题
|
||||
- 跳过重复添加**相同歌曲名与扩展名的歌曲**,例如你之前下载了A歌曲的128k音质,现在想要下载它的320k音质,但由于两者都是MP3格式,会因为重名导致之前的128k音质被覆盖但列表中仍然显示两种音质的问题(但实际上都是指向后面的320k音质)
|
||||
|
||||
## [0.11.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.10.0...v0.11.0) - 2019-11-10
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增歌曲缓冲定时器,尝试用于解决网络正常但是歌曲缓冲过久的问题
|
||||
- 新增下载管理的任务状态分类
|
||||
- 添加**杀毒软件提示有病毒或恶意行为**的说明,可到**常见问题**拉到最后查看(常见问题可在开源地址找到)
|
||||
|
||||
### 优化
|
||||
|
||||
- 优化更新弹窗机制及其内容描述,对于可以自动更新的版本,现在可以看到软件的下载进度了
|
||||
|
||||
## [0.10.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.9.1...v0.10.0) - 2019-11-02
|
||||
|
||||
#### 优化
|
||||
|
||||
- 大幅减少程序**播放时**对CPU与GPU的使用,经测试CPU使用减少60%以上,GPU使用减少90%以上,这应该能解决MAC系统上的温度上涨的问题
|
||||
|
||||
#### 修复
|
||||
|
||||
- 修复酷我源**搜索提示**、**排行榜**无法获取的问题
|
||||
- 修复咪咕源无法播放的问题
|
||||
|
||||
## [0.9.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.9.0...v0.9.1) - 2019-10-27
|
||||
|
||||
#### 修复
|
||||
|
||||
23
FAQ.md
23
FAQ.md
@@ -4,7 +4,7 @@
|
||||
|
||||
## 软件为什么没有桌面歌词与自定义列表功能
|
||||
|
||||
洛雪音乐的最初定位不是作为播放器开发的,它主要用于**查找歌曲**,软件的播放功能仅用于试听,不建议用作为常用播放器使用,因此无桌面、界面歌词,不可自定义列表。
|
||||
洛雪音乐的最初定位不是作为播放器开发的,它主要用于**查找歌曲**,软件的播放功能仅用于试听,不建议用作为常用播放器使用,因此无桌面、界面歌词,不可自定义列表等功能。
|
||||
|
||||
## 歌曲无法试听与下载
|
||||
|
||||
@@ -49,9 +49,13 @@
|
||||
## Windows 7 下界面异常
|
||||
|
||||
当 win7 没有开启**透明效果**时界面将会显示异常,开启方法请自行百度。<br>
|
||||
对于一些开启透明效果后仍然无法正常显示界面的系统,我也不知道是什么原因导致的,只能说正常的系统是没有这个问题的,如果你知道原因导致的欢迎反馈!
|
||||
对于一些完全无法正常显示界面的情况,请阅读下面的 **软件启动后,界面无法显示**
|
||||
|
||||
## 安装版安装失败,提示应用未安装
|
||||
## 软件启动后,界面无法显示
|
||||
|
||||
软件启动后,可以在任务栏看到软件,但软件界面在桌面上无任何显示,这一般是显卡驱动异常导致的,由于软件默认使用GUP渲染界面,当驱动异常时可能出现该情况,可以用驱动管理软件检测下驱动,尝试更新显卡驱动试试。
|
||||
|
||||
## 安装版安装失败,提示安装程序并未成功地运行完成
|
||||
|
||||
对于部分电脑出现安装失败的问题,可以做出以下尝试:
|
||||
|
||||
@@ -62,4 +66,15 @@
|
||||
|
||||
## 缺少`xxx.dll`
|
||||
|
||||
这个是电脑缺少某些dll导致的,正常的系统是没有这个问题的,解决办法需自行百度弹出的错误信息看下别人是怎么解决的。
|
||||
这个是电脑缺少某些dll导致的,正常的系统是没有这个问题的,可以尝试如下几个解决办法:
|
||||
|
||||
- 以管理员权限打开`cmd`,输入`sfc /scannow`回车等待检查完成重启电脑
|
||||
- 若上面的方法**修复、重启**电脑后仍然不行,就自行百度弹出的**错误信息**看下别人是怎么解决的
|
||||
|
||||
## 杀毒软件提示有病毒或恶意行为
|
||||
|
||||
本人只能保证我写的代码不包含任何**恶意代码**、**搜集用户信息**的行为,并且软件代码已开源,请自行查阅,软件安装包也是由CI拉取源代码构建,构建日志:[windows包](https://ci.appveyor.com/project/lyswhut/lx-music-desktop)、[Mac/Linux包](https://travis-ci.org/lyswhut/lx-music-desktop)<br>
|
||||
尽管如此,但这不意味着软件是100%安全的,由于软件使用了第三方依赖,当这些依赖存在恶意行为时,软件也将会受到牵连,所以我只能尽量选择使用较多人用、信任度较高的依赖。<br>
|
||||
当然,以上说明建立的前提是在你所用的安装包是从**官方渠道**下载的,或者有相关能力者还可以下载源代码自己构建安装包。
|
||||
|
||||
最后,若出现杀毒软件报毒,请自行判断选择是否继续使用本软件!
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<a href="https://github.com/lyswhut/lx-music-desktop/releases"><img src="https://img.shields.io/github/release/lyswhut/lx-music-desktop" alt="Release version"></a>
|
||||
<a href="https://ci.appveyor.com/project/lyswhut/lx-music-desktop"><img src="https://ci.appveyor.com/api/projects/status/flrsqd5ymp8fnte5?svg=true" alt="Build status"></a>
|
||||
<a href="https://travis-ci.org/lyswhut/lx-music-desktop"><img src="https://travis-ci.org/lyswhut/lx-music-desktop.svg?branch=master" alt="Build status"></a>
|
||||
<a href="https://electronjs.org/releases/stable"><img src="https://img.shields.io/github/package-json/dependency-version/lyswhut/lx-music-desktop/dev/electron/master" alt="Electron version"></a>
|
||||
<!-- <a href="https://github.com/lyswhut/lx-music-desktop/releases"><img src="https://img.shields.io/github/downloads/lyswhut/lx-music-desktop/latest/total" alt="Downloads"></a> -->
|
||||
<a href="https://github.com/lyswhut/lx-music-desktop/tree/dev"><img src="https://img.shields.io/github/package-json/v/lyswhut/lx-music-desktop/dev" alt="Dev branch version"></a>
|
||||
<!-- <a href="https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE"><img src="https://img.shields.io/github/license/lyswhut/lx-music-desktop" alt="License"></a> -->
|
||||
@@ -35,8 +36,8 @@
|
||||
|
||||
所用技术栈:
|
||||
|
||||
- Electron 6.x
|
||||
- Vue 2.x
|
||||
- Electron 7
|
||||
- Vue 2
|
||||
|
||||
已支持的平台:
|
||||
|
||||
@@ -47,7 +48,7 @@
|
||||
软件变化请查看:[更新日志](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.lanzous.com/b906260/` 密码:`glqw`<br>
|
||||
使用常见问题请转至:[常见问题](https://github.com/lyswhut/lx-music-desktop#常见问题)
|
||||
使用常见问题请转至:[常见问题](https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md)
|
||||
|
||||
### 源码使用方法
|
||||
|
||||
@@ -80,7 +81,7 @@ npm run pack
|
||||
### 免责声明
|
||||
|
||||
本项目**不开发或者破解直接获取音频数据**的功能,所有音频数据均来自**第三方接口**!<br>
|
||||
本软件仅用于**测试 `electron 6.x` 在各种系统上的兼容性**及用于**对比各大音乐平台歌单、排行榜等数据列表的差异性**,使用本软件产生的**任何涉及版权相关的数据**请于**24小时内删除**。<br>
|
||||
本软件仅用于**测试 `electron 7` 在各种系统上的兼容性**及用于**对比各大音乐平台歌单、排行榜等数据列表的差异性**,使用本软件产生的**任何涉及版权相关的数据**请于**24小时内删除**。<br>
|
||||
本软件仅用于学习交流使用,禁止用于商业用途,使用本软件所造成的的后果由使用者承担!<br>
|
||||
若对此有疑问请 mail to: lyswhut@qq.com
|
||||
|
||||
|
||||
4141
package-lock.json
generated
4141
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
74
package.json
74
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lx-music-desktop",
|
||||
"version": "0.9.1",
|
||||
"version": "0.13.1",
|
||||
"description": "一个免费的音乐下载助手",
|
||||
"main": "./dist/electron/main.js",
|
||||
"productName": "lx-music-desktop",
|
||||
@@ -49,7 +49,7 @@
|
||||
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-formatter-friendly --fix src"
|
||||
},
|
||||
"browserslist": [
|
||||
"Chrome >= 76"
|
||||
"Electron >= 7.1.2"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
@@ -71,12 +71,17 @@
|
||||
"artifactName": "${productName} v${version} ${env.ARCH} ${env.TARGET}.${ext}"
|
||||
},
|
||||
"mac": {
|
||||
"icon": "./resources/icons/512x512.png",
|
||||
"icon": "./resources/icons/512x512.icns",
|
||||
"category": "public.app-category.music"
|
||||
},
|
||||
"linux": {
|
||||
"maintainer": "lyswhut <lyswuhut@qq.com>",
|
||||
"artifactName": "${productName} v${version} ${env.ARCH}.${ext}"
|
||||
"artifactName": "${productName} v${version} ${env.ARCH}.${ext}",
|
||||
"icon": "./resources/icons",
|
||||
"category": "Utility;AudioVideo;Audio;Player;Music;",
|
||||
"desktop": {
|
||||
"Name[zh_CN]": "洛雪音乐助手"
|
||||
}
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
@@ -105,7 +110,7 @@
|
||||
},
|
||||
"appImage": {
|
||||
"license": "./licenses/license_zh.txt",
|
||||
"category": "Audio"
|
||||
"category": "Utility;AudioVideo;Audio;Player;Music;"
|
||||
},
|
||||
"publish": [
|
||||
{
|
||||
@@ -134,38 +139,38 @@
|
||||
},
|
||||
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.6.4",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/polyfill": "^7.6.0",
|
||||
"@babel/preset-env": "^7.6.3",
|
||||
"autoprefixer": "^9.6.5",
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.7.4",
|
||||
"@babel/polyfill": "^7.7.0",
|
||||
"@babel/preset-env": "^7.7.6",
|
||||
"autoprefixer": "^9.7.3",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-minify-webpack-plugin": "^0.3.1",
|
||||
"babel-preset-minify": "^0.5.1",
|
||||
"cfonts": "^2.4.5",
|
||||
"chalk": "^2.4.2",
|
||||
"chalk": "^3.0.0",
|
||||
"changelog-parser": "^2.8.0",
|
||||
"copy-webpack-plugin": "^5.0.4",
|
||||
"core-js": "^3.3.2",
|
||||
"cos-nodejs-sdk-v5": "^2.5.12",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"core-js": "^3.5.0",
|
||||
"cos-nodejs-sdk-v5": "^2.5.14",
|
||||
"cross-env": "^6.0.3",
|
||||
"css-loader": "^3.2.0",
|
||||
"css-loader": "^3.3.2",
|
||||
"del": "^5.1.0",
|
||||
"electron": "^6.0.12",
|
||||
"electron": "^7.1.2",
|
||||
"electron-builder": "^21.2.0",
|
||||
"electron-debug": "^3.0.1",
|
||||
"electron-devtools-installer": "^2.2.4",
|
||||
"eslint": "^6.5.1",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-standard": "^14.1.0",
|
||||
"eslint-formatter-friendly": "^7.0.0",
|
||||
"eslint-loader": "^3.0.2",
|
||||
"eslint-loader": "^3.0.3",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-import": "^2.19.1",
|
||||
"eslint-plugin-node": "^10.0.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"file-loader": "^4.2.0",
|
||||
"file-loader": "^5.0.2",
|
||||
"friendly-errors-webpack-plugin": "^1.7.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"less": "^3.10.3",
|
||||
@@ -178,18 +183,18 @@
|
||||
"pug": "^2.0.4",
|
||||
"pug-loader": "^2.4.0",
|
||||
"pug-plain-loader": "^1.0.0",
|
||||
"raw-loader": "^3.1.0",
|
||||
"raw-loader": "^4.0.0",
|
||||
"rimraf": "^3.0.0",
|
||||
"stylus": "^0.54.7",
|
||||
"stylus-loader": "^3.0.2",
|
||||
"terser-webpack-plugin": "^2.1.3",
|
||||
"url-loader": "^2.2.0",
|
||||
"vue-loader": "^15.7.1",
|
||||
"terser-webpack-plugin": "^2.3.0",
|
||||
"url-loader": "^3.0.0",
|
||||
"vue-loader": "^15.7.2",
|
||||
"vue-style-loader": "^4.1.2",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack": "^4.41.2",
|
||||
"webpack-cli": "^3.3.9",
|
||||
"webpack-dev-server": "^3.8.2",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-dev-server": "^3.9.0",
|
||||
"webpack-hot-middleware": "^2.25.0",
|
||||
"webpack-merge": "^4.2.2"
|
||||
},
|
||||
@@ -197,19 +202,18 @@
|
||||
"axios": "^0.19.0",
|
||||
"crypto-js": "^3.1.9-1",
|
||||
"dnscache": "^1.0.2",
|
||||
"electron-log": "^3.0.8",
|
||||
"electron-store": "^5.0.0",
|
||||
"electron-updater": "^4.1.2",
|
||||
"electron-log": "^4.0.0",
|
||||
"electron-store": "^5.1.0",
|
||||
"electron-updater": "^4.2.0",
|
||||
"flac-metadata": "^0.1.1",
|
||||
"js-htmlencode": "^0.3.0",
|
||||
"lrc-file-parser": "^0.1.14",
|
||||
"node-downloader-helper": "^1.0.10",
|
||||
"node-id3": "^0.1.11",
|
||||
"lrc-file-parser": "^0.1.15",
|
||||
"node-id3": "^0.1.13",
|
||||
"request": "^2.88.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue": "^2.6.11",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-router": "^3.1.3",
|
||||
"vuex": "^3.1.1",
|
||||
"vuex": "^3.1.2",
|
||||
"vuex-electron": "^1.0.3",
|
||||
"vuex-router-sync": "^5.0.0"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#### 修复
|
||||
### 修复
|
||||
|
||||
- 修复没有配置文件时程序启动出错的问题
|
||||
- 修复全局更新弹窗无法遮盖搜索框的问题
|
||||
|
||||
### 其他
|
||||
|
||||
- 由于electron 7.1.3 - 7.1.5 的自动更新功能存在Bug,现降级到7.1.2
|
||||
|
||||
@@ -1,7 +1,31 @@
|
||||
{
|
||||
"version": "0.9.1",
|
||||
"desc": "<h4>修复</h4>\n<ul>\n<li>修复没有配置文件时程序启动出错的问题</li>\n</ul>\n",
|
||||
"version": "0.13.1",
|
||||
"desc": "<h3>修复</h3>\n<ul>\n<li>修复全局更新弹窗无法遮盖搜索框的问题</li>\n</ul>\n<h3>其他</h3>\n<ul>\n<li>由于electron 7.1.3 - 7.1.5 的自动更新功能存在Bug,现降级到7.1.2</li>\n</ul>\n",
|
||||
"history": [
|
||||
{
|
||||
"version": "0.13.0",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增搜索框搜索建议键盘上下方向键选择功能</li>\n<li>聚合搜索新增音源显示</li>\n<li>新增“离开搜索界面时清空搜索列表”设置选项,默认关闭,可到设置-强迫症设置开启</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>优化“信口雌黄”皮肤配色</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复存在弹出层时,搜索建议列表被弹出层覆盖的问题</li>\n<li>修复搜索、排行榜、歌单列表多选框从不定状态到选中的Bug</li>\n</ul>\n<h3>移除</h3>\n<ul>\n<li>因Q音接口失效,移除Q音源的试听与下载</li>\n</ul>\n<h3>其他</h3>\n<ul>\n<li>更新electron到7.1.5</li>\n<li>更新vue到2.6.11</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.12.1",
|
||||
"desc": "<h3>优化</h3>\n<ul>\n<li>优化定位歌曲时的列表滚动机制</li>\n<li>优化链接点击效果</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复使用酷我源下载歌曲时,当歌曲无封面时下载报错的问题</li>\n<li>修复酷我源排行榜、歌单详情列表里的歌曲音质匹配问题(原来无论歌曲有无高品、无损都会显示有)</li>\n<li>禁止外部链接在软件内打开,将所有外部链接从默认浏览器打开</li>\n</ul>\n<h3>其他</h3>\n<ul>\n<li>更新electron到7.1.2</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.12.0",
|
||||
"desc": "<p>由于新下载库仍然没有完成,但下载功能已经可用,so 移除之前使用的第三方下载库,暂时把新下载库的下载模块直接加入本程序,若出现下载问题欢迎反馈!</p>\n<h3>新增</h3>\n<ul>\n<li>新增下载功能对代理设置的支持,现在若在软件设置了代理服务器,下载功能也将会走代理网络了</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>新下载模块将对恢复下载的任务进行字节校验,用于解决下载进度超过100%后仍然下载的问题</li>\n<li>注意:目前仍然无法暂停处于<strong>链接获取</strong>状态中的任务</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复Linux deb版本<code>.desktop</code>桌面文件缺少图标的问题,新增中文名称显示、软件分类,感谢@lowy的反馈!</li>\n<li>修复下载列表歌曲状态分类列表操作Bug</li>\n<li>修复歌曲封面下载失败时仍然执行嵌入封面操作导致报错的问题</li>\n<li>跳过重复添加<strong>相同歌曲名与扩展名的歌曲</strong>,例如你之前下载了A歌曲的128k音质,现在想要下载它的320k音质,但由于两者都是MP3格式,会因为重名导致之前的128k音质被覆盖但列表中仍然显示两种音质的问题(但实际上都是指向后面的320k音质)</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.11.0",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增歌曲缓冲定时器,尝试用于解决网络正常但是歌曲缓冲过久的问题</li>\n<li>新增下载管理的任务状态分类</li>\n<li>添加<strong>杀毒软件提示有病毒或恶意行为</strong>的说明,可到<strong>常见问题</strong>拉到最后查看(常见问题可在开源地址找到)</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>优化更新弹窗机制及其内容描述,对于可以自动更新的版本,现在可以看到软件的下载进度了</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.10.0",
|
||||
"desc": "<h4>优化</h4>\n<ul>\n<li>大幅减少程序<strong>播放时</strong>对CPU与GPU的使用,经测试CPU使用减少60%以上,GPU使用减少90%以上,这应该能解决MAC系统上的温度上涨的问题</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修复酷我源<strong>搜索提示</strong>、<strong>排行榜</strong>无法获取的问题</li>\n<li>修复咪咕源无法播放的问题</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.9.1",
|
||||
"desc": "<h4>修复</h4>\n<ul>\n<li>修复没有配置文件时程序启动出错的问题</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.9.0",
|
||||
"desc": "<h4>新增</h4>\n<ul>\n<li>新增窗口大小设置,若觉得软件窗口小可以到设置页调大点</li>\n<li>新增定位当前播放歌曲,点击播放栏左侧的<strong>歌曲图片</strong>可在播放列表定位当前播放的歌曲(该功能对播放下载列表的歌曲无效)</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修复搜索提示失效的问题</li>\n<li>修复从歌单或列表点击搜索按钮搜索目标歌曲时,搜索框未聚焦仍然弹出候选搜索列表的问题</li>\n</ul>\n"
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
const { ipcMain, ipcRenderer } = require('electron')
|
||||
|
||||
|
||||
export const mainSend = (name, params) => {
|
||||
ipcMain.send(name, params)
|
||||
}
|
||||
|
||||
export const mainOn = (name, callback) => {
|
||||
ipcMain.on(name, callback)
|
||||
}
|
||||
|
||||
|
||||
export const rendererSend = (name, params) => {
|
||||
ipcRenderer.send(name, params)
|
||||
}
|
||||
|
||||
export const rendererOn = (name, callback) => {
|
||||
ipcRenderer.on(name, callback)
|
||||
}
|
||||
31
src/common/ipc.js
Normal file
31
src/common/ipc.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const { ipcMain, ipcRenderer } = require('electron')
|
||||
|
||||
|
||||
exports.mainOn = (event, callback) => {
|
||||
ipcMain.on(event, callback)
|
||||
}
|
||||
exports.mainOnce = (event, callback) => {
|
||||
ipcMain.once(event, callback)
|
||||
}
|
||||
|
||||
exports.mainHandle = (name, callback) => {
|
||||
ipcMain.handle(name, callback)
|
||||
}
|
||||
exports.mainHandleOnce = (name, callback) => {
|
||||
ipcMain.handleOnce(name, callback)
|
||||
}
|
||||
|
||||
|
||||
exports.rendererSend = (name, params) => {
|
||||
ipcRenderer.send(name, params)
|
||||
}
|
||||
exports.rendererSendSync = (name, params) => ipcRenderer.sendSync(name, params)
|
||||
|
||||
exports.rendererInvoke = (name, params) => ipcRenderer.invoke(name, params)
|
||||
|
||||
exports.rendererOn = (name, callback) => {
|
||||
ipcRenderer.on(name, callback)
|
||||
}
|
||||
exports.rendererOnce = (name, callback) => {
|
||||
ipcRenderer.once(name, callback)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
const { mainOn } = require('../../common/icp')
|
||||
const { mainOn } = require('../../common/ipc')
|
||||
const { app } = require('electron')
|
||||
const { name: defaultName } = require('../../../package.json')
|
||||
|
||||
|
||||
8
src/main/events/clearCache.js
Normal file
8
src/main/events/clearCache.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const { mainHandle } = require('../../common/ipc')
|
||||
|
||||
mainHandle('clearCache', async(event, options) => {
|
||||
if (!global.mainWindow) throw new Error('mainwindow is undefined')
|
||||
return global.mainWindow.webContents.session.clearCache()
|
||||
})
|
||||
|
||||
|
||||
7
src/main/events/getCacheSize.js
Normal file
7
src/main/events/getCacheSize.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const { mainHandle } = require('../../common/ipc')
|
||||
|
||||
mainHandle('getCacheSize', async(event, options) => {
|
||||
if (!global.mainWindow) throw new Error('mainwindow is undefined')
|
||||
return global.mainWindow.webContents.session.getCacheSize()
|
||||
})
|
||||
|
||||
@@ -4,3 +4,9 @@ require('./request')
|
||||
require('./progressBar')
|
||||
require('./trafficLight')
|
||||
require('./musicMeta')
|
||||
require('./selectDir')
|
||||
require('./setWindowSize')
|
||||
require('./showSaveDialog')
|
||||
require('./clearCache')
|
||||
require('./getCacheSize')
|
||||
require('./setIgnoreMouseEvent')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { mainOn } = require('../../common/icp')
|
||||
const { mainOn } = require('../../common/ipc')
|
||||
const { setMeta } = require('../utils/musicMeta')
|
||||
|
||||
mainOn('setMusicMeta', (event, { filePath, meta }) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { mainOn } = require('../../common/icp')
|
||||
const { mainOn } = require('../../common/ipc')
|
||||
|
||||
|
||||
mainOn('progress', (event, params) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const request = require('request')
|
||||
|
||||
const { mainOn } = require('../../common/icp')
|
||||
const { mainOn } = require('../../common/ipc')
|
||||
|
||||
const tasks = []
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { mainOn } = require('../../common/icp')
|
||||
const { mainOn } = require('../../common/ipc')
|
||||
|
||||
|
||||
mainOn('restartWindow', (event, name) => {
|
||||
|
||||
8
src/main/events/selectDir.js
Normal file
8
src/main/events/selectDir.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const { mainHandle } = require('../../common/ipc')
|
||||
const { dialog } = require('electron')
|
||||
|
||||
mainHandle('selectDir', async(event, options) => {
|
||||
if (!global.mainWindow) throw new Error('mainwindow is undefined')
|
||||
return dialog.showOpenDialog(global.mainWindow, options)
|
||||
})
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
const { mainOn } = require('../../common/icp')
|
||||
const { dialog } = require('electron')
|
||||
|
||||
module.exports = win => {
|
||||
mainOn('selectPath', (event, params) => {
|
||||
let path = dialog.showOpenDialog(win, params.options)
|
||||
if (path === undefined) return
|
||||
event.sender.send(params.eventName, path)
|
||||
})
|
||||
}
|
||||
|
||||
8
src/main/events/setIgnoreMouseEvent.js
Normal file
8
src/main/events/setIgnoreMouseEvent.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const { mainOn } = require('../../common/ipc')
|
||||
|
||||
mainOn('setIgnoreMouseEvents', (event, isIgnored) => {
|
||||
if (!global.mainWindow) return
|
||||
isIgnored
|
||||
? global.mainWindow.setIgnoreMouseEvents(true, { forward: true })
|
||||
: global.mainWindow.setIgnoreMouseEvents(false)
|
||||
})
|
||||
7
src/main/events/setWindowSize.js
Normal file
7
src/main/events/setWindowSize.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const { mainOn } = require('../../common/ipc')
|
||||
|
||||
mainOn('setWindowSize', (event, options) => {
|
||||
if (!global.mainWindow) return
|
||||
global.mainWindow.setBounds(options)
|
||||
})
|
||||
|
||||
8
src/main/events/showSaveDialog.js
Normal file
8
src/main/events/showSaveDialog.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const { mainHandle } = require('../../common/ipc')
|
||||
const { dialog } = require('electron')
|
||||
|
||||
mainHandle('showSaveDialog', async(event, options) => {
|
||||
if (!global.mainWindow) throw new Error('mainwindow is undefined')
|
||||
return dialog.showSaveDialog(global.mainWindow, options)
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// const { app } = require('electron')
|
||||
const { mainOn } = require('../../common/icp')
|
||||
const { mainOn } = require('../../common/ipc')
|
||||
|
||||
|
||||
mainOn('min', event => {
|
||||
@@ -7,11 +7,11 @@ mainOn('min', event => {
|
||||
global.mainWindow.minimize()
|
||||
}
|
||||
})
|
||||
// mainOn('max', event => {
|
||||
// if (global.mainWindow) {
|
||||
// global.mainWindow.maximize()
|
||||
// }
|
||||
// })
|
||||
mainOn('max', event => {
|
||||
if (global.mainWindow) {
|
||||
global.mainWindow.maximize()
|
||||
}
|
||||
})
|
||||
mainOn('close', event => {
|
||||
if (global.mainWindow) {
|
||||
// global.mainWindowdow.destroy()
|
||||
|
||||
@@ -50,6 +50,7 @@ function createWindow() {
|
||||
width: windowSizeInfo.width,
|
||||
frame: false,
|
||||
transparent: !isLinux,
|
||||
enableRemoteModule: false,
|
||||
// icon: path.join(global.__static, isWin ? 'icons/256x256.ico' : 'icons/512x512.png'),
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { log } = require('../../common/utils')
|
||||
const { autoUpdater } = require('electron-updater')
|
||||
const { mainOn } = require('../../common/icp')
|
||||
const { mainOn } = require('../../common/ipc')
|
||||
|
||||
autoUpdater.logger = log
|
||||
// autoUpdater.autoDownload = false
|
||||
@@ -21,7 +21,7 @@ log.info('App starting...')
|
||||
|
||||
function sendStatusToWindow(text) {
|
||||
log.info(text)
|
||||
// global.mainWindow.webContents.send('message', text)
|
||||
// ipcMain.send('message', text)
|
||||
}
|
||||
|
||||
|
||||
@@ -90,16 +90,20 @@ module.exports = isFirstCheckedUpdate => {
|
||||
sendStatusToWindow('Update not available.')
|
||||
handleSendEvent({ type: 'update-not-available' })
|
||||
})
|
||||
autoUpdater.on('error', () => {
|
||||
autoUpdater.on('error', err => {
|
||||
sendStatusToWindow('Error in auto-updater.')
|
||||
handleSendEvent({ type: 'update-error' })
|
||||
handleSendEvent({ type: 'update-error', info: err.message })
|
||||
})
|
||||
autoUpdater.on('download-progress', progressObj => {
|
||||
sendStatusToWindow('Download progress...')
|
||||
let log_message = 'Download speed: ' + progressObj.bytesPerSecond
|
||||
log_message = log_message + ' - Downloaded ' + progressObj.percent + '%'
|
||||
log_message = log_message + ' (' + progressObj.transferred + '/' + progressObj.total + ')'
|
||||
sendStatusToWindow(log_message)
|
||||
handleSendEvent({ type: 'update-progress', info: progressObj })
|
||||
})
|
||||
autoUpdater.on('update-downloaded', info => {
|
||||
sendStatusToWindow('Update downloaded.')
|
||||
handleSendEvent({ type: 'update-downloaded' })
|
||||
handleSendEvent({ type: 'update-downloaded', info })
|
||||
})
|
||||
mainOn('quit-update', () => {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -6,12 +6,39 @@ const extReg = /^(\.(?:jpe?g|png)).*$/
|
||||
|
||||
module.exports = (filePath, meta) => {
|
||||
if (!meta.APIC) return NodeID3.write(meta, filePath)
|
||||
if (!/^http/.test(meta.APIC)) {
|
||||
delete meta.APIC
|
||||
return NodeID3.write(meta, filePath)
|
||||
}
|
||||
let picPath = filePath.replace(/\.mp3$/, '') + path.extname(meta.APIC).replace(extReg, '$1')
|
||||
request(meta.APIC).pipe(fs.createWriteStream(picPath)).on('finish', () => {
|
||||
meta.APIC = picPath
|
||||
NodeID3.write(meta, filePath)
|
||||
fs.unlink(picPath, err => {
|
||||
if (err) console.log(err.message)
|
||||
request(meta.APIC)
|
||||
.on('response', respones => {
|
||||
if (respones.statusCode !== 200 && respones.statusCode != 206) {
|
||||
delete meta.APIC
|
||||
NodeID3.write(meta, filePath)
|
||||
return
|
||||
}
|
||||
respones
|
||||
.pipe(fs.createWriteStream(picPath))
|
||||
.on('finish', () => {
|
||||
if (respones.complete) {
|
||||
meta.APIC = picPath
|
||||
NodeID3.write(meta, filePath)
|
||||
} else {
|
||||
delete meta.APIC
|
||||
}
|
||||
fs.unlink(picPath, err => {
|
||||
if (err) console.log(err.message)
|
||||
})
|
||||
}).on('error', err => {
|
||||
if (err) console.log(err.message)
|
||||
delete meta.APIC
|
||||
NodeID3.write(meta, filePath)
|
||||
})
|
||||
})
|
||||
.on('error', err => {
|
||||
if (err) console.log(err.message)
|
||||
delete meta.APIC
|
||||
NodeID3.write(meta, filePath)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,20 +20,16 @@
|
||||
<script>
|
||||
import dnscache from 'dnscache'
|
||||
import { mapMutations, mapGetters, mapActions } from 'vuex'
|
||||
import { rendererOn } from '../common/icp'
|
||||
import { rendererOn, rendererSend } from '../common/ipc'
|
||||
import { isLinux } from '../common/utils'
|
||||
import music from './utils/music'
|
||||
import { throttle, openUrl } from './utils'
|
||||
window.ELECTRON_DISABLE_SECURITY_WARNINGS = process.env.ELECTRON_DISABLE_SECURITY_WARNINGS
|
||||
dnscache({
|
||||
enable: true,
|
||||
ttl: 21600,
|
||||
cachesize: 1000,
|
||||
})
|
||||
let win
|
||||
let body
|
||||
if (!isLinux) {
|
||||
win = require('electron').remote.getCurrentWindow()
|
||||
body = document.body
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -55,6 +51,20 @@ export default {
|
||||
downloadStatus: 'downloadStatus',
|
||||
}),
|
||||
},
|
||||
created() {
|
||||
this.saveSetting = throttle(n => {
|
||||
this.electronStore.set('setting', n)
|
||||
})
|
||||
this.saveDefaultList = throttle(n => {
|
||||
this.electronStore.set('list.defaultList', n)
|
||||
}, 500)
|
||||
this.saveLoveList = throttle(n => {
|
||||
this.electronStore.set('list.loveList', n)
|
||||
}, 500)
|
||||
this.saveDownloadList = throttle(n => {
|
||||
this.electronStore.set('download.list', n)
|
||||
}, 1000)
|
||||
},
|
||||
mounted() {
|
||||
document.body.classList.add(this.isLinux ? 'noTransparent' : 'transparent')
|
||||
this.init()
|
||||
@@ -62,27 +72,25 @@ export default {
|
||||
watch: {
|
||||
setting: {
|
||||
handler(n) {
|
||||
this.electronStore.set('setting', n)
|
||||
this.saveSetting(n)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
defaultList: {
|
||||
handler(n) {
|
||||
// console.log(n)
|
||||
this.electronStore.set('list.defaultList', n)
|
||||
this.saveDefaultList(n)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
loveList: {
|
||||
handler(n) {
|
||||
// console.log(n)
|
||||
this.electronStore.set('list.loveList', n)
|
||||
this.saveLoveList(n)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
downloadList: {
|
||||
handler(n) {
|
||||
this.electronStore.set('download.list', n)
|
||||
this.saveDownloadList(n)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
@@ -96,37 +104,52 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['getVersionInfo']),
|
||||
...mapMutations(['setNewVersion', 'setVersionModalVisible']),
|
||||
...mapMutations(['setNewVersion', 'setVersionModalVisible', 'setDownloadProgress']),
|
||||
...mapMutations('list', ['initList']),
|
||||
...mapMutations('download', ['updateDownloadList']),
|
||||
...mapMutations(['setSetting']),
|
||||
init() {
|
||||
document.body.addEventListener('click', this.handleBodyClick, true)
|
||||
if (this.isProd && !isLinux) {
|
||||
body.addEventListener('mouseenter', this.dieableIgnoreMouseEvents)
|
||||
body.addEventListener('mouseleave', this.enableIgnoreMouseEvents)
|
||||
document.body.addEventListener('mouseenter', this.dieableIgnoreMouseEvents)
|
||||
document.body.addEventListener('mouseleave', this.enableIgnoreMouseEvents)
|
||||
}
|
||||
rendererOn('update-available', (e, info) => {
|
||||
// this.showUpdateModal(true)
|
||||
this.setNewVersion({
|
||||
// console.log(info)
|
||||
this.setVersionModalVisible({ isDownloading: true })
|
||||
this.getVersionInfo().catch(() => ({
|
||||
version: info.version,
|
||||
desc: info.releaseNotes,
|
||||
})).then(body => {
|
||||
// console.log(body)
|
||||
this.setNewVersion(body)
|
||||
this.$nextTick(() => {
|
||||
this.setVersionModalVisible({ isShow: true })
|
||||
})
|
||||
})
|
||||
})
|
||||
rendererOn('update-error', () => {
|
||||
if (!this.updateTimeout) return
|
||||
this.setVersionModalVisible({ isError: true })
|
||||
rendererOn('update-error', (event, err) => {
|
||||
// console.log(err)
|
||||
this.clearUpdateTimeout()
|
||||
this.setVersionModalVisible({ isError: true })
|
||||
this.$nextTick(() => {
|
||||
this.showUpdateModal()
|
||||
})
|
||||
})
|
||||
rendererOn('update-downloaded', () => {
|
||||
rendererOn('update-progress', (event, progress) => {
|
||||
// console.log(progress)
|
||||
this.setDownloadProgress(progress)
|
||||
})
|
||||
rendererOn('update-downloaded', info => {
|
||||
// console.log(info)
|
||||
this.clearUpdateTimeout()
|
||||
this.setVersionModalVisible({ isError: false })
|
||||
this.showUpdateModal()
|
||||
this.setVersionModalVisible({ isDownloaded: true })
|
||||
this.$nextTick(() => {
|
||||
this.showUpdateModal()
|
||||
})
|
||||
})
|
||||
rendererOn('update-not-available', () => {
|
||||
if (!this.updateTimeout) return
|
||||
if (this.setting.ignoreVersion) this.setSetting(Object.assign({}, this.setting, { ignoreVersion: null }))
|
||||
this.clearUpdateTimeout()
|
||||
this.setNewVersion({
|
||||
version: this.version.version,
|
||||
@@ -135,26 +158,29 @@ export default {
|
||||
// 更新超时定时器
|
||||
this.updateTimeout = setTimeout(() => {
|
||||
this.updateTimeout = null
|
||||
this.setVersionModalVisible({ isError: true })
|
||||
this.setVersionModalVisible({ isTimeOut: true })
|
||||
this.$nextTick(() => {
|
||||
this.showUpdateModal()
|
||||
})
|
||||
}, 180000)
|
||||
}, 60 * 30 * 1000)
|
||||
|
||||
this.initData()
|
||||
this.globalObj.apiSource = this.setting.apiSource
|
||||
this.globalObj.proxy = Object.assign({}, this.setting.network.proxy)
|
||||
window.globalObj = this.globalObj
|
||||
|
||||
// 初始化音乐sdk
|
||||
music.init()
|
||||
},
|
||||
enableIgnoreMouseEvents() {
|
||||
if (isLinux) return
|
||||
win.setIgnoreMouseEvents(false)
|
||||
rendererSend('setIgnoreMouseEvents', false)
|
||||
// console.log('content enable')
|
||||
},
|
||||
dieableIgnoreMouseEvents() {
|
||||
if (isLinux) return
|
||||
// console.log('content disable')
|
||||
win.setIgnoreMouseEvents(true, { forward: true })
|
||||
rendererSend('setIgnoreMouseEvents', true)
|
||||
},
|
||||
|
||||
initData() { // 初始化数据
|
||||
@@ -181,12 +207,19 @@ export default {
|
||||
showUpdateModal() {
|
||||
(this.version.newVersion && this.version.newVersion.history ? Promise.resolve(this.version.newVersion) : this.getVersionInfo().then(body => {
|
||||
this.setNewVersion(body)
|
||||
if (body.version !== this.setting.ignoreVersion) this.setSetting(Object.assign({}, this.setting, { ignoreVersion: null }))
|
||||
return body
|
||||
})).then(body => {
|
||||
if (body.version === this.version.version) return
|
||||
if (this.version.isError && body.version === this.setting.ignoreVersion) return
|
||||
|
||||
})).catch(() => {
|
||||
if (this.version.newVersion) return this.version.newVersion
|
||||
this.setVersionModalVisible({ isUnknow: true })
|
||||
let result = {
|
||||
version: '0.0.0',
|
||||
desc: null,
|
||||
}
|
||||
this.setNewVersion(result)
|
||||
return result
|
||||
}).then(result => {
|
||||
if (result.version === this.version.version) return
|
||||
// console.log(this.version)
|
||||
this.$nextTick(() => {
|
||||
this.setVersionModalVisible({ isShow: true })
|
||||
})
|
||||
@@ -197,13 +230,20 @@ export default {
|
||||
clearTimeout(this.updateTimeout)
|
||||
this.updateTimeout = null
|
||||
},
|
||||
handleBodyClick(event) {
|
||||
if (event.target.tagName != 'A') return
|
||||
if (event.target.host == window.location.host) return
|
||||
event.preventDefault()
|
||||
if (/^https?:\/\//.test(event.target.href)) openUrl(event.target.href)
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.clearUpdateTimeout()
|
||||
if (this.isProd) {
|
||||
body.removeEventListener('mouseenter', this.dieableIgnoreMouseEvents)
|
||||
body.removeEventListener('mouseleave', this.enableIgnoreMouseEvents)
|
||||
document.body.removeEventListener('mouseenter', this.dieableIgnoreMouseEvents)
|
||||
document.body.removeEventListener('mouseleave', this.enableIgnoreMouseEvents)
|
||||
}
|
||||
document.body.removeEventListener('click', this.handleBodyClick)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -57,6 +57,10 @@ table {
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.25em 0.4em;
|
||||
@@ -112,7 +116,7 @@ svg {
|
||||
transition-property: fill;
|
||||
}
|
||||
|
||||
.hover {
|
||||
.hover, a {
|
||||
cursor: pointer;
|
||||
transition: color .2s ease;
|
||||
&:hover {
|
||||
@@ -150,7 +154,7 @@ svg {
|
||||
|
||||
each(@themes, {
|
||||
#container.@{value} {
|
||||
.hover {
|
||||
.hover, a {
|
||||
&:hover {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// @color-theme: #03a678;
|
||||
@color-theme: #4daf7c;
|
||||
@color-theme-bgimg: none;
|
||||
@color-theme-bgposition: center center;
|
||||
@color-theme-bgposition: center;
|
||||
@color-theme-bgsize: auto auto;
|
||||
@color-theme-hover: fadeout(lighten(@color-theme, 10%), 30%);
|
||||
@color-theme-active: fadeout(darken(@color-theme, 20%), 60%);
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
@color-green-theme: #4daf7c;
|
||||
@color-green-theme-bgimg: none;
|
||||
@color-green-theme-bgposition: center center;
|
||||
@color-green-theme-bgposition: center;
|
||||
@color-green-theme-bgsize: auto auto;
|
||||
@color-green-theme-hover: fadeout(lighten(@color-green-theme, 10%), 30%);
|
||||
@color-green-theme-active: fadeout(darken(@color-green-theme, 20%), 60%);
|
||||
@@ -97,9 +97,9 @@
|
||||
@color-green-tab-border-bottom: lighten(@color-green-theme, 5%);
|
||||
|
||||
|
||||
@color-yellow-theme: #f2d35b;
|
||||
@color-yellow-theme: #e9d460;
|
||||
@color-yellow-theme-bgimg: none;
|
||||
@color-yellow-theme-bgposition: center center;
|
||||
@color-yellow-theme-bgposition: center;
|
||||
@color-yellow-theme-bgsize: auto auto;
|
||||
@color-yellow-theme-hover: fadeout(lighten(@color-yellow-theme, 10%), 30%);
|
||||
@color-yellow-theme-active: fadeout(darken(@color-yellow-theme, 20%), 60%);
|
||||
@@ -108,37 +108,37 @@
|
||||
@color-yellow-theme_2: #fff;
|
||||
@color-yellow-theme_2-background_1: #fff;
|
||||
@color-yellow-theme_2-background_2: fadeout(@color-yellow-theme_2-background_1, 2%);
|
||||
@color-yellow-theme_2-hover: fadeout(lighten(@color-yellow-theme, 10%), 70%);
|
||||
@color-yellow-theme_2-active: fadeout(darken(@color-yellow-theme, 5%), 70%);
|
||||
@color-yellow-theme_2-hover: fadeout(lighten(@color-yellow-theme, 10%), 60%);
|
||||
@color-yellow-theme_2-active: fadeout(darken(@color-yellow-theme, 5%), 60%);
|
||||
@color-yellow-theme_2-font: darken(@color-yellow-theme_2, 70%);
|
||||
@color-yellow-theme_2-font-label: fadeout(@color-yellow-theme_2-font, 50%);
|
||||
@color-yellow-theme_2-line: lighten(@color-yellow-theme, 35%);
|
||||
@color-yellow-theme_2-font-label: fadeout(@color-yellow-theme_2-font, 40%);
|
||||
@color-yellow-theme_2-line: lighten(@color-yellow-theme, 25%);
|
||||
@color-yellow-theme-sidebar: @color-yellow-theme;
|
||||
@color-yellow-btn: fadeout(darken(@color-yellow-theme, 5%), 15%);
|
||||
@color-yellow-btn-background: fadeout(lighten(@color-yellow-theme, 25%), 70%);
|
||||
@color-yellow-btn: fadeout(darken(@color-yellow-theme, 5%), 5%);
|
||||
@color-yellow-btn-background: fadeout(lighten(@color-yellow-theme, 25%), 60%);
|
||||
@color-yellow-pagination-background: fadeout(lighten(@color-yellow-theme, 30%), 30%);
|
||||
@color-yellow-pagination-hover: fadeout(lighten(@color-yellow-theme, 5%), 70%);
|
||||
@color-yellow-pagination-active: fadeout(darken(@color-yellow-theme, 5%), 70%);
|
||||
@color-yellow-pagination-select: fadeout(lighten(@color-yellow-theme, 5%), 50%);
|
||||
@color-yellow-search-form-background: fadeout(lighten(@color-yellow-theme, 35%), 10%);
|
||||
@color-yellow-search-list-hover: fadeout(darken(@color-yellow-theme, 10%), 70%);
|
||||
@color-yellow-scrollbar-track: fadeout(@color-yellow-theme, 80%);
|
||||
@color-yellow-scrollbar-thumb: fadeout(@color-yellow-theme, 60%);
|
||||
@color-yellow-scrollbar-thumb-hover: fadeout(@color-yellow-theme, 40%);
|
||||
@color-yellow-search-form-background: fadeout(lighten(@color-yellow-theme, 25%), 10%);
|
||||
@color-yellow-search-list-hover: fadeout(darken(@color-yellow-theme, 10%), 60%);
|
||||
@color-yellow-scrollbar-track: fadeout(@color-yellow-theme, 60%);
|
||||
@color-yellow-scrollbar-thumb: fadeout(@color-yellow-theme, 45%);
|
||||
@color-yellow-scrollbar-thumb-hover: fadeout(@color-yellow-theme, 30%);
|
||||
@color-yellow-player-pic-c1: fadeout(@color-yellow-theme_2, 50%);
|
||||
@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, 12%);
|
||||
@color-yellow-player-progress-bar2: lighten(@color-yellow-theme, 2%);
|
||||
@color-yellow-player-status-text: lighten(@color-yellow-theme_2-font, 10%);
|
||||
@color-yellow-tab-btn-background: fadeout(lighten(@color-yellow-theme, 10%), 80%);
|
||||
@color-yellow-tab-btn-background: fadeout(lighten(@color-yellow-theme, 10%), 70%);
|
||||
@color-yellow-tab-btn-background-hover: @color-yellow-theme_2-hover;
|
||||
@color-yellow-tab-border-top: fadeout(lighten(@color-yellow-theme, 5%), 50%);
|
||||
@color-yellow-tab-border-bottom: lighten(@color-yellow-theme, 5%);
|
||||
@color-yellow-tab-border-top: fadeout(lighten(@color-yellow-theme, 5%), 40%);
|
||||
@color-yellow-tab-border-bottom: @color-yellow-theme;
|
||||
|
||||
@color-orange-theme: #f5ab35;
|
||||
@color-orange-theme-bgimg: none;
|
||||
@color-orange-theme-bgposition: center center;
|
||||
@color-orange-theme-bgposition: center;
|
||||
@color-orange-theme-bgsize: auto auto;
|
||||
@color-orange-theme-hover: fadeout(lighten(@color-orange-theme, 10%), 30%);
|
||||
@color-orange-theme-active: fadeout(darken(@color-orange-theme, 20%), 60%);
|
||||
@@ -177,7 +177,7 @@
|
||||
|
||||
@color-blue-theme: #3498db;
|
||||
@color-blue-theme-bgimg: none;
|
||||
@color-blue-theme-bgposition: center center;
|
||||
@color-blue-theme-bgposition: center;
|
||||
@color-blue-theme-bgsize: auto auto;
|
||||
@color-blue-theme-hover: fadeout(lighten(@color-blue-theme, 10%), 30%);
|
||||
@color-blue-theme-active: fadeout(darken(@color-blue-theme, 20%), 60%);
|
||||
@@ -216,7 +216,7 @@
|
||||
|
||||
@color-red-theme: #d64541;
|
||||
@color-red-theme-bgimg: none;
|
||||
@color-red-theme-bgposition: center center;
|
||||
@color-red-theme-bgposition: center;
|
||||
@color-red-theme-bgsize: auto auto;
|
||||
@color-red-theme-hover: fadeout(lighten(@color-red-theme, 10%), 30%);
|
||||
@color-red-theme-active: fadeout(darken(@color-red-theme, 20%), 60%);
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
@color-purple-theme: #9b59b6;
|
||||
@color-purple-theme-bgimg: none;
|
||||
@color-purple-theme-bgposition: center center;
|
||||
@color-purple-theme-bgposition: center;
|
||||
@color-purple-theme-bgsize: auto auto;
|
||||
@color-purple-theme-hover: fadeout(lighten(@color-purple-theme, 10%), 30%);
|
||||
@color-purple-theme-active: fadeout(darken(@color-purple-theme, 20%), 60%);
|
||||
@@ -296,7 +296,7 @@
|
||||
|
||||
@color-grey-theme: #6c7a89;
|
||||
@color-grey-theme-bgimg: none;
|
||||
@color-grey-theme-bgposition: center center;
|
||||
@color-grey-theme-bgposition: center;
|
||||
@color-grey-theme-bgsize: auto auto;
|
||||
@color-grey-theme-hover: fadeout(lighten(@color-grey-theme, 10%), 30%);
|
||||
@color-grey-theme-active: fadeout(darken(@color-grey-theme, 20%), 60%);
|
||||
@@ -335,7 +335,7 @@
|
||||
|
||||
@color-midAutumn-theme: rgba(74, 55, 82, 1);
|
||||
@color-midAutumn-theme-bgimg: url(../images/jqbg.jpg);
|
||||
@color-midAutumn-theme-bgposition: center center;
|
||||
@color-midAutumn-theme-bgposition: center;
|
||||
@color-midAutumn-theme-bgsize: auto 100%;
|
||||
@color-midAutumn-theme-hover: fadeout(lighten(@color-midAutumn-theme, 10%), 30%);
|
||||
@color-midAutumn-theme-active: fadeout(lighten(@color-midAutumn-theme, 15%), 60%);
|
||||
@@ -374,7 +374,7 @@
|
||||
|
||||
@color-dhHyrz-theme: rgb(87, 144, 167);
|
||||
@color-dhHyrz-theme-bgimg: url(../images/hzwbg.jpeg);
|
||||
@color-dhHyrz-theme-bgposition: center center;
|
||||
@color-dhHyrz-theme-bgposition: center;
|
||||
@color-dhHyrz-theme-bgsize: auto 100%;
|
||||
@color-dhHyrz-theme-hover: fadeout(lighten(@color-dhHyrz-theme, 10%), 45%);
|
||||
@color-dhHyrz-theme-active: fadeout(lighten(@color-dhHyrz-theme, 15%), 60%);
|
||||
|
||||
@@ -115,6 +115,9 @@ export default {
|
||||
&:hover:not(.active) {
|
||||
background-color: @color-theme-hover;
|
||||
}
|
||||
&:hover:not(.active) {
|
||||
background-color: @color-theme-active;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,6 +144,9 @@ each(@themes, {
|
||||
&:hover:not(.active) {
|
||||
background-color: ~'@{color-@{value}-theme-hover}';
|
||||
}
|
||||
&:active:not(.active) {
|
||||
background-color: ~'@{color-@{value}-theme-active}';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ div(:class="$style.player")
|
||||
div(:class="$style.column2")
|
||||
div(:class="$style.progress")
|
||||
//- div(:class="[$style.progressBar, $style.progressBar1]" :style="{ transform: `scaleX(${progress || 0})` }")
|
||||
div(:class="[$style.progressBar, $style.progressBar2]" :style="{ transform: `scaleX(${progress || 0})` }")
|
||||
div(:class="[$style.progressBar, $style.progressBar2, isActiveTransition ? $style.barTransition : '']" @transitionend="handleTransitionEnd" :style="{ transform: `scaleX(${progress || 0})` }")
|
||||
div(:class="$style.progressMask" @click='setProgess' ref="dom_progress")
|
||||
div(:class="$style.column3")
|
||||
span {{nowPlayTimeStr}}
|
||||
@@ -48,7 +48,7 @@ div(:class="$style.player")
|
||||
|
||||
<script>
|
||||
import Lyric from 'lrc-file-parser'
|
||||
import { rendererSend } from '../../../common/icp'
|
||||
import { rendererSend } from '../../../common/ipc'
|
||||
import { formatPlayTime2, getRandom, checkPath, setTitle, clipboardWriteText, debounce } from '../../utils'
|
||||
import { mapGetters, mapActions, mapMutations } from 'vuex'
|
||||
import { requestMsg } from '../../utils/message'
|
||||
@@ -88,6 +88,11 @@ export default {
|
||||
msDownX: 0,
|
||||
msDownVolume: 0,
|
||||
},
|
||||
isActiveTransition: false,
|
||||
mediaBuffer: {
|
||||
timeout: null,
|
||||
playTime: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -213,7 +218,7 @@ export default {
|
||||
this.stopPlay()
|
||||
if (this.listId != 'download' && this.audio.error.code !== 1 && this.retryNum < 2) { // 若音频URL无效则尝试刷新2次URL
|
||||
// console.log(this.retryNum)
|
||||
this.audioErrorTime = this.audio.currentTime // 记录出错的播放时间
|
||||
if (!this.audioErrorTime) this.audioErrorTime = this.audio.currentTime // 记录出错的播放时间
|
||||
this.retryNum++
|
||||
this.setUrl(this.list[this.playIndex], true)
|
||||
this.status = 'URL过期,正在刷新URL...'
|
||||
@@ -238,6 +243,14 @@ export default {
|
||||
})
|
||||
this.audio.addEventListener('canplay', () => {
|
||||
console.log('加载完成开始播放')
|
||||
if (this.mediaBuffer.playTime) {
|
||||
let playTime = this.mediaBuffer.playTime
|
||||
this.mediaBuffer.playTime = 0
|
||||
this.audio.currentTime = playTime
|
||||
}
|
||||
if (this.mediaBuffer.timeout) {
|
||||
this.clearBufferTimeout()
|
||||
}
|
||||
// if (this.musicInfo.lrc) this.lyric.lrc.play(this.audio.currentTime * 1000)
|
||||
this.status = '音乐加载中...'
|
||||
})
|
||||
@@ -246,10 +259,13 @@ export default {
|
||||
// // if (this.musicInfo.lyric.orgLrc) this.musicInfo.lyric.lrc.play(this.audio.currentTime * 1000)
|
||||
// this.status = '播放中...'
|
||||
// })
|
||||
// this.audio.addEventListener('emptied', () => {
|
||||
// console.log('媒介资源元素突然为空,网络错误 or 切换歌曲?')
|
||||
// this.status = '媒介资源元素突然为空,网络错误?'
|
||||
// })
|
||||
this.audio.addEventListener('emptied', () => {
|
||||
this.mediaBuffer.playTime = 0
|
||||
this.clearBufferTimeout()
|
||||
|
||||
// console.log('媒介资源元素突然为空,网络错误 or 切换歌曲?')
|
||||
// this.status = '媒介资源元素突然为空,网络错误?'
|
||||
})
|
||||
|
||||
this.audio.addEventListener('timeupdate', () => {
|
||||
this.nowPlayTime = this.audio.currentTime
|
||||
@@ -257,7 +273,9 @@ export default {
|
||||
|
||||
this.audio.addEventListener('waiting', () => {
|
||||
// this.musicInfo.lyric.lrc.pause()
|
||||
// console.log('缓冲中...')
|
||||
this.stopPlay()
|
||||
this.startBuffering()
|
||||
this.status = '缓冲中...'
|
||||
})
|
||||
|
||||
@@ -321,7 +339,7 @@ export default {
|
||||
} else if (this.isAPITemp) {
|
||||
list = this.list.filter(s => s.source == 'kw')
|
||||
} else {
|
||||
list = this.list
|
||||
list = this.list.filter(s => s.source != 'tx')
|
||||
}
|
||||
if (!list.length) return this.setPlayIndex(-1)
|
||||
let playIndex = this.list === list ? this.playIndex : list.indexOf(this.list[this.playIndex])
|
||||
@@ -367,18 +385,33 @@ export default {
|
||||
},
|
||||
setProgess(e) {
|
||||
if (!this.audio.src) return
|
||||
this.audio.currentTime =
|
||||
(e.offsetX / this.pregessWidth) * this.maxPlayTime
|
||||
if (!this.isPlay) this.audio.play()
|
||||
this.isActiveTransition = true
|
||||
this.$nextTick(() => {
|
||||
const time = (e.offsetX / this.pregessWidth) * this.maxPlayTime
|
||||
if (this.audioErrorTime) this.audioErrorTime = time
|
||||
if (this.mediaBuffer.playTime) {
|
||||
this.clearBufferTimeout()
|
||||
this.mediaBuffer.playTime = time
|
||||
this.startBuffering()
|
||||
}
|
||||
this.audio.currentTime = time
|
||||
|
||||
if (!this.isPlay) this.audio.play()
|
||||
})
|
||||
},
|
||||
setProgessWidth() {
|
||||
this.pregessWidth = parseInt(
|
||||
window.getComputedStyle(this.$refs.dom_progress, null).width
|
||||
window.getComputedStyle(this.$refs.dom_progress, null).width,
|
||||
)
|
||||
},
|
||||
togglePlay() {
|
||||
if (!this.audio.src) return
|
||||
this.isPlay ? this.audio.pause() : this.audio.play()
|
||||
if (this.isPlay) {
|
||||
this.audio.pause()
|
||||
this.clearBufferTimeout()
|
||||
} else {
|
||||
this.audio.play()
|
||||
}
|
||||
},
|
||||
imgError(e) {
|
||||
// e.target.src = 'https://y.gtimg.cn/music/photo_new/T002R500x500M000002BMEC42fM8S3.jpg'
|
||||
@@ -503,6 +536,36 @@ export default {
|
||||
},
|
||||
})
|
||||
},
|
||||
handleTransitionEnd(e) {
|
||||
// console.log(e)
|
||||
this.isActiveTransition = false
|
||||
},
|
||||
startBuffering() {
|
||||
console.log('start t')
|
||||
if (this.mediaBuffer.timeout) return
|
||||
this.mediaBuffer.timeout = setTimeout(() => {
|
||||
this.mediaBuffer.timeout = null
|
||||
if (!this.mediaBuffer.playTime) this.mediaBuffer.playTime = this.audio.currentTime
|
||||
let skipTime = this.audio.currentTime + getRandom(3, 6)
|
||||
if (skipTime > this.maxPlayTime) skipTime = (this.maxPlayTime - this.audio.currentTime) / 2
|
||||
if (skipTime - this.mediaBuffer.playTime < 1 || this.maxPlayTime - skipTime < 1) {
|
||||
this.mediaBuffer.playTime = 0
|
||||
this.handleNext()
|
||||
return
|
||||
}
|
||||
this.startBuffering()
|
||||
this.audio.currentTime = skipTime
|
||||
console.log(this.mediaBuffer.playTime)
|
||||
console.log(this.audio.currentTime)
|
||||
}, 3000)
|
||||
},
|
||||
clearBufferTimeout() {
|
||||
console.log('clear t')
|
||||
if (!this.mediaBuffer.timeout) return
|
||||
clearTimeout(this.mediaBuffer.timeout)
|
||||
this.mediaBuffer.timeout = null
|
||||
this.mediaBuffer.playTime = 0
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -693,21 +756,23 @@ export default {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform-origin: 0;
|
||||
transition-property: transform;
|
||||
transition-timing-function: ease-out;
|
||||
border-radius: @radius-progress-border;
|
||||
}
|
||||
.progress-bar1 {
|
||||
transition-duration: 0.6s;
|
||||
background-color: @color-player-progress-bar1;
|
||||
}
|
||||
|
||||
.progress-bar2 {
|
||||
transition-duration: 0.2s;
|
||||
background-color: @color-player-progress-bar2;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.bar-transition {
|
||||
transition-property: transform;
|
||||
transition-timing-function: ease-out;
|
||||
transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
.column3 {
|
||||
transition: @transition-theme;
|
||||
transition-property: color;
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { rendererSend } from 'common/icp'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { rendererSend } from 'common/ipc'
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import music from '../../utils/music'
|
||||
import { debounce } from '../../utils'
|
||||
export default {
|
||||
@@ -34,15 +34,21 @@ export default {
|
||||
source() {
|
||||
return this.setting.search.tempSearchSource
|
||||
},
|
||||
isAutoClearInput() {
|
||||
isAutoClearSearchInput() {
|
||||
return this.setting.odc.isAutoClearSearchInput
|
||||
},
|
||||
isAutoClearSearchList() {
|
||||
return this.setting.odc.isAutoClearSearchList
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
route(n) {
|
||||
if (this.isAutoClearInput && n.name != 'search' && this.searchText) this.searchText = ''
|
||||
if (n.name != 'search') {
|
||||
if (this.isAutoClearSearchInput && this.searchText) this.searchText = ''
|
||||
if (this.isAutoClearSearchList) this.clearSearchList()
|
||||
}
|
||||
},
|
||||
'storeSearchText'(n) {
|
||||
storeSearchText(n) {
|
||||
if (n !== this.searchText) this.searchText = n
|
||||
},
|
||||
searchText(n) {
|
||||
@@ -62,6 +68,9 @@ export default {
|
||||
}, 50)
|
||||
},
|
||||
methods: {
|
||||
...mapMutations('search', {
|
||||
clearSearchList: 'clearList',
|
||||
}),
|
||||
handleEvent({ action, data }) {
|
||||
switch (action) {
|
||||
case 'focus':
|
||||
@@ -127,7 +136,7 @@ export default {
|
||||
align-items: center;
|
||||
padding-left: 15px;
|
||||
-webkit-app-region: drag;
|
||||
z-index: 1;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
}
|
||||
.input {
|
||||
|
||||
@@ -13,6 +13,7 @@ div(:class="$style.view")
|
||||
|
||||
.view {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
> * {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
@@ -2,16 +2,12 @@ import Vue from 'vue'
|
||||
import upperFirst from 'lodash/upperFirst'
|
||||
import camelCase from 'lodash/camelCase'
|
||||
|
||||
const requireComponent = require.context(
|
||||
'./', true, /\.vue$/
|
||||
)
|
||||
const requireComponent = require.context('./', true, /\.vue$/)
|
||||
|
||||
requireComponent.keys().forEach(fileName => {
|
||||
const componentConfig = requireComponent(fileName)
|
||||
|
||||
const componentName = upperFirst(
|
||||
camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, ''))
|
||||
)
|
||||
const componentName = upperFirst(camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')))
|
||||
|
||||
Vue.component(componentName, componentConfig.default || componentConfig)
|
||||
})
|
||||
|
||||
@@ -71,9 +71,9 @@ export default {
|
||||
let bool = this.bool
|
||||
if (this.indeterminate) {
|
||||
bool = true
|
||||
// this.$nextTick(() => {
|
||||
this.bool = true
|
||||
// })
|
||||
this.$nextTick(() => {
|
||||
this.bool = bool
|
||||
})
|
||||
}
|
||||
checked = bool
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,9 @@ div(:class="[$style.search, focus ? $style.active : '', big ? $style.big : '', s
|
||||
input(:placeholder="placeholder" v-model.trim="text"
|
||||
@focus="handleFocus" @blur="handleBlur" @input="$emit('input', text)"
|
||||
@change="sendEvent('change')"
|
||||
@keyup.enter="handleSearch")
|
||||
@keyup.enter="handleSearch"
|
||||
@keyup.40.prevent="handleKeyDown"
|
||||
@keyup.38.prevent="handleKeyUp")
|
||||
button(type="button" @click="handleSearch")
|
||||
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')
|
||||
@@ -14,7 +16,7 @@ div(:class="[$style.search, focus ? $style.active : '', big ? $style.big : '', s
|
||||
//- leave-active-class="animated flipOutX")
|
||||
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)")
|
||||
li(v-for="(item, index) in list" :key="item" :class="selectIndex === index ? $style.select : null" @mouseenter="selectIndex = index" @click="handleTemplistClick(index)")
|
||||
span {{item}}
|
||||
</template>
|
||||
|
||||
@@ -49,7 +51,7 @@ export default {
|
||||
return {
|
||||
isShow: false,
|
||||
text: '',
|
||||
index: null,
|
||||
selectIndex: -1,
|
||||
focus: false,
|
||||
listStyle: {
|
||||
height: 0,
|
||||
@@ -59,6 +61,7 @@ export default {
|
||||
watch: {
|
||||
list(n) {
|
||||
if (!this.visibleList) return
|
||||
if (this.selectIndex > -1) this.selectIndex = -1
|
||||
this.$nextTick(() => {
|
||||
this.listStyle.height = this.$refs.dom_list.scrollHeight + 'px'
|
||||
})
|
||||
@@ -84,7 +87,8 @@ export default {
|
||||
},
|
||||
handleSearch() {
|
||||
this.hideList()
|
||||
this.sendEvent('submit')
|
||||
if (this.selectIndex < 0) return this.sendEvent('submit')
|
||||
this.sendEvent('listClick', this.selectIndex)
|
||||
},
|
||||
showList() {
|
||||
this.isShow = true
|
||||
@@ -93,6 +97,9 @@ export default {
|
||||
hideList() {
|
||||
this.isShow = false
|
||||
this.listStyle.height = 0
|
||||
this.$nextTick(() => {
|
||||
this.selectIndex = -1
|
||||
})
|
||||
},
|
||||
sendEvent(action, data) {
|
||||
this.$emit('event', {
|
||||
@@ -100,6 +107,12 @@ export default {
|
||||
data,
|
||||
})
|
||||
},
|
||||
handleKeyDown() {
|
||||
this.selectIndex = this.selectIndex + 1 < this.list.length ? this.selectIndex + 1 : 0
|
||||
},
|
||||
handleKeyUp() {
|
||||
this.selectIndex = this.selectIndex - 1 < -1 ? this.list.length - 1 : this.selectIndex - 1
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -187,7 +200,7 @@ export default {
|
||||
.mixin-ellipsis-2;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&.select {
|
||||
background-color: @color-search-list-hover;
|
||||
}
|
||||
&:last-child {
|
||||
@@ -239,7 +252,7 @@ each(@themes, {
|
||||
}
|
||||
.list {
|
||||
li {
|
||||
&:hover {
|
||||
&.select {
|
||||
background-color: ~'@{color-@{value}-search-list-hover}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)"
|
||||
:play-btn="item.source == 'kw' || (!isAPITemp)"
|
||||
:download-btn="item.source == 'kw' || (!isAPITemp)")
|
||||
:listAdd-btn="item.source == 'kw' || !isAPITemp"
|
||||
:play-btn="item.source != 'tx' && (item.source == 'kw' || !isAPITemp)"
|
||||
:download-btn="item.source != 'tx' && (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)') +
|
||||
@@ -40,7 +40,7 @@ div(:class="$style.songList")
|
||||
material-pagination(:count="total" :limit="limit" :page="page" @btn-click="handleTogglePage")
|
||||
div(v-else :class="$style.noitem")
|
||||
p(v-html="noItem")
|
||||
material-flow-btn(:show="isShowEditBtn && (source == 'kw' || !isAPITemp)" :remove-btn="false" @btn-click="handleFlowBtnClick")
|
||||
material-flow-btn(:show="isShowEditBtn && source != 'tx' && (source == 'kw' || !isAPITemp)" :remove-btn="false" @btn-click="handleFlowBtnClick")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -140,7 +140,7 @@ export default {
|
||||
this.clickIndex = index
|
||||
return
|
||||
}
|
||||
this.emitEvent((this.source == 'kw' || !this.isAPITemp) ? 'testPlay' : 'search', index)
|
||||
this.emitEvent((this.source != 'tx' && (this.source == 'kw' || !this.isAPITemp)) ? 'testPlay' : 'search', index)
|
||||
this.clickTime = 0
|
||||
this.clickIndex = -1
|
||||
},
|
||||
|
||||
@@ -110,13 +110,13 @@ export default {
|
||||
display: inline-block;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 5px 10px 7px;
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
// color: @color-btn;
|
||||
outline: none;
|
||||
transition: background-color @transition-theme;
|
||||
background-color: @color-tab-btn-background;
|
||||
|
||||
line-height: 28px;
|
||||
}
|
||||
&:hover {
|
||||
// border-left-color: @color-theme_2-hover;
|
||||
|
||||
@@ -1,7 +1,31 @@
|
||||
<template lang="pug">
|
||||
material-modal(:show="version.showModal" @close="handleClose")
|
||||
main(:class="$style.main" v-if="version.newVersion")
|
||||
h2 {{ version.isError ? (isUnknow ? '❓ 版本信息获取失败 ❓' : '🌟发现新版本🌟') : '🚀程序更新🚀'}}
|
||||
material-modal(:show="version.showModal" @close="handleClose" v-if="version.newVersion")
|
||||
main(:class="$style.main" v-if="version.isDownloaded")
|
||||
h2 🚀程序更新🚀
|
||||
|
||||
div.scroll(:class="$style.info")
|
||||
div(:class="$style.current")
|
||||
h3 最新版本:{{version.newVersion.version}}
|
||||
h3 当前版本:{{version.version}}
|
||||
h3 版本变化:
|
||||
p(:class="$style.desc" v-html="version.newVersion.desc")
|
||||
div(:class="[$style.history, $style.desc]" v-if="history.length")
|
||||
h3 历史版本:
|
||||
div(:class="$style.item" v-for="ver in history")
|
||||
h4 v{{ver.version}}
|
||||
p(v-html="ver.desc")
|
||||
div(:class="$style.footer")
|
||||
div(:class="$style.desc")
|
||||
p 新版本已下载完毕,
|
||||
p
|
||||
| 你可以选择
|
||||
strong 立即重启更新
|
||||
| 或稍后
|
||||
strong 关闭程序时
|
||||
| 自动更新~
|
||||
material-btn(:class="$style.btn" @click.onec="handleRestartClick") 立即重启更新
|
||||
main(:class="$style.main" v-else-if="version.isError && !version.isUnknow && version.newVersion.version != version.version")
|
||||
h2 ❌ 版本更新出错 ❌
|
||||
|
||||
div.scroll(:class="$style.info")
|
||||
div(:class="$style.current")
|
||||
@@ -15,44 +39,97 @@ material-modal(:show="version.showModal" @close="handleClose")
|
||||
h4 v{{ver.version}}
|
||||
p(v-html="ver.desc")
|
||||
|
||||
div(:class="$style.footer" v-if="version.isError")
|
||||
div(:class="$style.desc" v-if="!isUnknow")
|
||||
div(:class="$style.footer")
|
||||
div(:class="$style.desc")
|
||||
p 发现有新版本啦,但是自动更新功能出问题了,
|
||||
p
|
||||
| 你现在可以选择继续使用当前版本或
|
||||
strong 去发布页下载新版本
|
||||
| ,
|
||||
| 你可以去
|
||||
strong.hover.underline(@click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" title="点击打开") 软件发布页
|
||||
| 或
|
||||
strong.hover.underline(@click="handleOpenUrl('https://www.lanzous.com/b906260/')" title="点击打开") 网盘
|
||||
| (密码:
|
||||
strong.hover(@click="handleCopy('glqw')" title="点击复制") glqw
|
||||
| ) 下载新版本,
|
||||
p
|
||||
| 国内Windows/MAC用户推荐到
|
||||
strong.hover.underline(@click="handleOpenUrl('https://www.lanzous.com/b906260/')") 网盘(点击打开)
|
||||
| 下载,密码:
|
||||
strong.hover(@click="handleCopy('glqw')" title="点击复制") glqw
|
||||
div(:class="$style.btns")
|
||||
material-btn(:class="$style.btn" @click.onec="handleIgnoreClick") 忽略该版本
|
||||
material-btn(:class="$style.btn" @click.onec="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop#readme')") 去软件发布页
|
||||
div(:class="$style.footer" v-else)
|
||||
div(:class="$style.desc")
|
||||
p 新版本已下载完毕,
|
||||
p
|
||||
| 你可以选择
|
||||
strong 立即重启更新
|
||||
| 或稍后
|
||||
strong 关闭程序时
|
||||
| 自动更新~
|
||||
material-btn(:class="$style.btn" @click.onec="handleRestartClick") 立即重启更新
|
||||
strong 网盘
|
||||
| 下载。
|
||||
main(:class="$style.main" v-else-if="version.isDownloading && version.isTimeOut && !version.isUnknow")
|
||||
h2 ❗️ 新版本下载超时 ❗️
|
||||
div(:class="$style.desc")
|
||||
p 你当前所在网络访问GitHub较慢,导致新版本下载超时(已经下了半个钟了😳),建议手动更新版本!
|
||||
p
|
||||
| 你可以去
|
||||
material-btn(min @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" title="点击打开") 软件发布页
|
||||
| 或
|
||||
material-btn(min @click="handleOpenUrl('https://www.lanzous.com/b906260/')" title="点击打开") 网盘
|
||||
| (密码:
|
||||
strong.hover(@click="handleCopy('glqw')" title="点击复制") glqw
|
||||
| )下载新版本,
|
||||
p
|
||||
| 国内Windows/MAC用户推荐到
|
||||
strong 网盘
|
||||
| 下载。
|
||||
p 当前下载进度:{{progress}}
|
||||
main(:class="$style.main" v-else-if="version.isUnknow")
|
||||
h2 ❓ 获取最新版本信息失败 ❓
|
||||
|
||||
div.scroll(:class="$style.info")
|
||||
div(:class="$style.current")
|
||||
h3 当前版本:{{version.version}}
|
||||
div(:class="$style.desc")
|
||||
p 更新信息获取失败,可能是无法访问Github导致的,请手动检查更新!
|
||||
p
|
||||
| 检查方法:打开
|
||||
material-btn(min @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" title="点击打开") 软件发布页
|
||||
| 或
|
||||
material-btn(min @click="handleOpenUrl('https://www.lanzous.com/b906260/')" title="点击打开") 网盘
|
||||
| (密码:
|
||||
strong.hover(@click="handleCopy('glqw')" title="点击复制") glqw
|
||||
| )查看它们的
|
||||
strong 版本号
|
||||
| 与当前版本({{version.version}})对比是否一样,
|
||||
p 若一样则不必理会该弹窗,直接关闭即可,否则请手动下载新版本更新。
|
||||
main(:class="$style.main" v-else)
|
||||
h2 🌟发现新版本🌟
|
||||
|
||||
div.scroll(:class="$style.info")
|
||||
div(:class="$style.current")
|
||||
h3 最新版本:{{version.newVersion.version}}
|
||||
h3 当前版本:{{version.version}}
|
||||
h3 版本变化:
|
||||
p(:class="$style.desc" v-html="version.newVersion.desc")
|
||||
div(:class="[$style.history, $style.desc]" v-if="history.length")
|
||||
h3 历史版本:
|
||||
div(:class="$style.item" v-for="ver in history")
|
||||
h4 v{{ver.version}}
|
||||
p(v-html="ver.desc")
|
||||
|
||||
div(:class="$style.footer")
|
||||
div(:class="$style.desc")
|
||||
p 发现有新版本啦,正在努力更新中,若下载太慢可以手动更新哦~
|
||||
p
|
||||
| 手动更新可以去
|
||||
strong.hover.underline(@click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" title="点击打开") 软件发布页
|
||||
| 或
|
||||
strong.hover.underline(@click="handleOpenUrl('https://www.lanzous.com/b906260/')" title="点击打开") 网盘
|
||||
| (密码:
|
||||
strong.hover(@click="handleCopy('glqw')" title="点击复制") glqw
|
||||
| ) 下载,
|
||||
p 国内Windows/MAC用户推荐到网盘下载。
|
||||
p 当前下载进度:{{progress}}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import { rendererSend } from '../../../common/icp'
|
||||
import { checkVersion, openUrl, clipboardWriteText } from '../../utils'
|
||||
import { rendererSend } from '../../../common/ipc'
|
||||
import { checkVersion, openUrl, clipboardWriteText, sizeFormate } from '../../utils'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['version', 'setting']),
|
||||
history() {
|
||||
if (!this.version.newVersion) return []
|
||||
if (!this.version.newVersion || !this.version.newVersion.history) return []
|
||||
let arr = []
|
||||
let currentVer = this.version.version
|
||||
this.version.newVersion.history.forEach(ver => {
|
||||
@@ -61,8 +138,10 @@ export default {
|
||||
|
||||
return arr
|
||||
},
|
||||
isUnknow() {
|
||||
return this.version.newVersion.version == '0.0.0'
|
||||
progress() {
|
||||
return this.version.downloadProgress
|
||||
? `${this.version.downloadProgress.percent.toFixed(2)}% - ${sizeFormate(this.version.downloadProgress.transferred)}/${sizeFormate(this.version.downloadProgress.total)} - ${sizeFormate(this.version.downloadProgress.bytesPerSecond)}/s`
|
||||
: '初始化中...'
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -72,11 +151,6 @@ export default {
|
||||
isShow: false,
|
||||
})
|
||||
},
|
||||
handleIgnoreClick(event) {
|
||||
this.handleClose()
|
||||
// event.target.disabled = true
|
||||
this.setSetting(Object.assign({}, this.setting, { ignoreVersion: this.version.newVersion.version }))
|
||||
},
|
||||
handleOpenUrl(url) {
|
||||
openUrl(url)
|
||||
},
|
||||
@@ -148,6 +222,10 @@ export default {
|
||||
list-style: initial;
|
||||
padding-inline-start: 30px;
|
||||
}
|
||||
p {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.history {
|
||||
@@ -175,22 +253,23 @@ export default {
|
||||
.footer {
|
||||
flex: 0 0 none;
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
padding-top: 10px;
|
||||
font-size: 12px;
|
||||
color: @color-theme;
|
||||
line-height: 1.2;
|
||||
line-height: 1.25;
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: @color-theme;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
.btns {
|
||||
display: grid;
|
||||
padding-top: 10px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 0 10px;
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
|
||||
@@ -10,9 +10,7 @@ function route(path, view, name, meta, props) {
|
||||
path,
|
||||
meta,
|
||||
props,
|
||||
component: (resovle) => import(
|
||||
`../views/${view}.vue`
|
||||
).then(resovle),
|
||||
component: (resovle) => import(`../views/${view}.vue`).then(resovle),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,7 @@ export default {
|
||||
timeout: 20000,
|
||||
}, (err, resp, body) => {
|
||||
if (err) {
|
||||
return ++retryNum > 3 ? resolve({
|
||||
version: '0.0.0',
|
||||
desc: '<h3>版本信息获取失败</h3><ul><li>更新信息获取失败,可能是无法访问Github导致的,请手动检查更新!</li><li>检查方法:去设置-关于洛雪音乐打开<strong>开源地址</strong>或<strong>网盘地址</strong>查看<strong>版本号</strong>与当前版本对比是否最新</li></ul>',
|
||||
history: [],
|
||||
}) : this.dispatch('getVersionInfo', retryNum).then(ver => resolve(ver))
|
||||
return ++retryNum > 3 ? reject() : this.dispatch('getVersionInfo', retryNum).then(ver => resolve(ver)).catch(err => reject(err))
|
||||
}
|
||||
resolve(body)
|
||||
})
|
||||
|
||||
@@ -53,7 +53,7 @@ const getExt = type => {
|
||||
}
|
||||
}
|
||||
|
||||
const checkList = (list, musicInfo, type) => list.some(s => s.musicInfo.songmid === musicInfo.songmid && s.type === type)
|
||||
const checkList = (list, musicInfo, type, ext) => list.some(s => s.musicInfo.songmid === musicInfo.songmid && (s.type === type || s.ext === ext))
|
||||
|
||||
const getStartTask = (list, downloadStatus, maxDownloadNum) => {
|
||||
let downloadCount = 0
|
||||
@@ -88,7 +88,7 @@ const getUrl = (downloadInfo, isRefresh) => {
|
||||
* 设置歌曲meta信息
|
||||
* @param {*} downloadInfo
|
||||
* @param {*} filePath
|
||||
* @param {*} isEmbedPic
|
||||
* @param {*} isEmbedPic // 是否嵌入图片
|
||||
*/
|
||||
const saveMeta = (downloadInfo, filePath, isEmbedPic) => {
|
||||
if (downloadInfo.type === 'ape' || downloadInfo.type === 'flac') return
|
||||
@@ -121,11 +121,24 @@ const downloadLyric = (downloadInfo, filePath) => {
|
||||
})
|
||||
}
|
||||
|
||||
const refreshUrl = function(commit, downloadInfo) {
|
||||
commit('setStatusText', { downloadInfo, text: '链接失效,正在刷新链接' })
|
||||
getUrl(downloadInfo, true).then(result => {
|
||||
commit('updateUrl', { downloadInfo, url: result.url })
|
||||
commit('setStatusText', { downloadInfo, text: '链接刷新成功' })
|
||||
dls[downloadInfo.key].refreshUrl(result.url)
|
||||
dls[downloadInfo.key].start()
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
this.dispatch('download/startTask')
|
||||
})
|
||||
}
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
createDownload({ state, rootState, commit }, { musicInfo, type }) {
|
||||
if (checkList(state.list, musicInfo, type)) return
|
||||
let ext = getExt(type)
|
||||
if (checkList(state.list, musicInfo, type, ext)) return
|
||||
const downloadInfo = {
|
||||
isComplate: false,
|
||||
status: state.downloadStatus.WAITING,
|
||||
@@ -146,6 +159,11 @@ const actions = {
|
||||
}
|
||||
downloadInfo.filePath = path.join(rootState.setting.download.savePath, downloadInfo.fileName)
|
||||
commit('addTask', downloadInfo)
|
||||
try { // 删除同路径下的同名文件
|
||||
fs.unlinkSync(downloadInfo.filePath)
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') return commit('setStatusText', { downloadInfo, text: '文件删除失败' })
|
||||
}
|
||||
if (dls[downloadInfo.key]) {
|
||||
dls[downloadInfo.key].stop().finally(() => {
|
||||
delete dls[downloadInfo.key]
|
||||
@@ -167,7 +185,7 @@ const actions = {
|
||||
if (!downloadInfo) downloadInfo = result
|
||||
|
||||
// 开始任务
|
||||
commit('onDownload', downloadInfo)
|
||||
commit('onStart', downloadInfo)
|
||||
commit('setStatusText', { downloadInfo, text: '任务初始化中' })
|
||||
let msg = checkPath(rootState.setting.download.savePath)
|
||||
if (msg) return commit('setStatusText', '检查下载目录出错: ' + msg)
|
||||
@@ -178,12 +196,12 @@ const actions = {
|
||||
fileName: downloadInfo.fileName,
|
||||
method: 'get',
|
||||
override: true,
|
||||
onEnd() {
|
||||
if (downloadInfo.progress.progress != '100.00') {
|
||||
delete dls[downloadInfo.key]
|
||||
return this.dispatch('download/startTask', downloadInfo)
|
||||
}
|
||||
commit('onEnd', downloadInfo)
|
||||
onCompleted() {
|
||||
// if (downloadInfo.progress.progress != '100.00') {
|
||||
// delete dls[downloadInfo.key]
|
||||
// return this.dispatch('download/startTask', downloadInfo)
|
||||
// }
|
||||
commit('onCompleted', downloadInfo)
|
||||
_this.dispatch('download/startTask')
|
||||
const filePath = path.join(options.path, options.fileName)
|
||||
|
||||
@@ -199,54 +217,40 @@ const actions = {
|
||||
_this.dispatch('download/startTask')
|
||||
return
|
||||
}
|
||||
let code
|
||||
if (err.message.includes('Response status was')) {
|
||||
code = err.message.replace(/Response status was (\d+)$/, '$1')
|
||||
} else if (err.code === 'ETIMEDOUT' || err.code == 'ENOTFOUND') {
|
||||
code = err.code
|
||||
if (err.code == 'ENOTFOUND') {
|
||||
refreshUrl.call(_this, commit, downloadInfo)
|
||||
} else {
|
||||
console.log('Download failed, Attempting Retry')
|
||||
dls[downloadInfo.key].resume()
|
||||
dls[downloadInfo.key].start()
|
||||
commit('setStatusText', { downloadInfo, text: '正在重试' })
|
||||
return
|
||||
}
|
||||
switch (code) {
|
||||
case '401':
|
||||
case '403':
|
||||
case '410':
|
||||
case 'ETIMEDOUT':
|
||||
case 'ENOTFOUND':
|
||||
commit('setStatusText', { downloadInfo, text: '链接失效,正在刷新链接' })
|
||||
getUrl(downloadInfo, true).then(result => {
|
||||
commit('updateUrl', { downloadInfo, url: result.url })
|
||||
commit('setStatusText', { downloadInfo, text: '链接刷新成功' })
|
||||
dls[downloadInfo.key].url = dls[downloadInfo.key].requestURL = result.url
|
||||
dls[downloadInfo.key].__initProtocol(result.url)
|
||||
dls[downloadInfo.key].resume()
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
_this.dispatch('download/startTask')
|
||||
})
|
||||
}
|
||||
},
|
||||
// onStateChanged(state) {
|
||||
// console.log(state)
|
||||
// },
|
||||
onDownload() {
|
||||
commit('onDownload', downloadInfo)
|
||||
console.log('on download')
|
||||
onFail(response) {
|
||||
commit('onError', downloadInfo)
|
||||
|
||||
if (++tryNum[downloadInfo.key] > 2) {
|
||||
_this.dispatch('download/startTask')
|
||||
return
|
||||
}
|
||||
switch (response.statusCode) {
|
||||
case 401:
|
||||
case 403:
|
||||
case 410:
|
||||
refreshUrl.call(_this, commit, downloadInfo)
|
||||
}
|
||||
},
|
||||
onStart() {
|
||||
commit('onStart', downloadInfo)
|
||||
console.log('on start')
|
||||
},
|
||||
onProgress(status) {
|
||||
commit('onProgress', { downloadInfo, status })
|
||||
console.log(status)
|
||||
},
|
||||
onPause() {
|
||||
onStop() {
|
||||
commit('pauseTask', downloadInfo)
|
||||
_this.dispatch('download/startTask')
|
||||
},
|
||||
onResume() {
|
||||
commit('resumeTask', downloadInfo)
|
||||
},
|
||||
}
|
||||
commit('setStatusText', { downloadInfo, text: '获取URL中...' })
|
||||
let p = options.url ? Promise.resolve() : getUrl(downloadInfo).then(result => {
|
||||
@@ -315,9 +319,6 @@ const mutations = {
|
||||
downloadInfo.status = state.downloadStatus.PAUSE
|
||||
downloadInfo.statusText = '暂停下载'
|
||||
},
|
||||
resumeTask(state, downloadInfo) {
|
||||
downloadInfo.statusText = '开始下载'
|
||||
},
|
||||
setStatusText(state, { downloadInfo, index, text }) { // 设置状态文本
|
||||
if (downloadInfo) {
|
||||
downloadInfo.statusText = text
|
||||
@@ -352,7 +353,7 @@ const mutations = {
|
||||
state.list[index].status = status
|
||||
}
|
||||
},
|
||||
onEnd(state, downloadInfo) {
|
||||
onCompleted(state, downloadInfo) {
|
||||
downloadInfo.isComplate = true
|
||||
downloadInfo.status = state.downloadStatus.COMPLETED
|
||||
downloadInfo.statusText = '下载完成'
|
||||
@@ -361,7 +362,7 @@ const mutations = {
|
||||
downloadInfo.status = state.downloadStatus.ERROR
|
||||
downloadInfo.statusText = '任务出错'
|
||||
},
|
||||
onDownload(state, downloadInfo) {
|
||||
onStart(state, downloadInfo) {
|
||||
downloadInfo.status = state.downloadStatus.RUN
|
||||
downloadInfo.statusText = '正在下载'
|
||||
},
|
||||
|
||||
@@ -22,15 +22,18 @@ export default {
|
||||
state.setting.list.scroll.locations[id] = location
|
||||
},
|
||||
setNewVersion(state, val) {
|
||||
// val.history.forEach(ver => {
|
||||
// ver.desc = ver.desc.replace(/\n/g, '<br>')
|
||||
// })
|
||||
// val.desc = val.desc.replace(/\n/g, '<br>')
|
||||
state.version.newVersion = val
|
||||
},
|
||||
setVersionModalVisible(state, { isShow, isError }) {
|
||||
setDownloadProgress(state, info) {
|
||||
state.version.downloadProgress = info
|
||||
},
|
||||
setVersionModalVisible(state, { isShow, isError, isDownloaded, isTimeOut, isDownloading, isUnknow }) {
|
||||
if (isShow !== undefined) state.version.showModal = isShow
|
||||
if (isError !== undefined) state.version.isError = isError
|
||||
if (isTimeOut !== undefined) state.version.isTimeOut = isTimeOut
|
||||
if (isDownloading !== undefined) state.version.isDownloading = isDownloading
|
||||
if (isDownloaded !== undefined) state.version.isDownloaded = isDownloaded
|
||||
if (isUnknow !== undefined) state.version.isUnknow = isUnknow
|
||||
},
|
||||
setVolume(state, val) {
|
||||
state.setting.player.volume = val
|
||||
|
||||
@@ -62,6 +62,11 @@ export default {
|
||||
newVersion: null,
|
||||
showModal: false,
|
||||
isError: false,
|
||||
isTimeOut: false,
|
||||
isUnknow: false,
|
||||
isDownloaded: false,
|
||||
isDownloading: false,
|
||||
downloadProgress: null,
|
||||
},
|
||||
userInfo: null,
|
||||
setting,
|
||||
|
||||
312
src/renderer/utils/download/Downloader.js
Normal file
312
src/renderer/utils/download/Downloader.js
Normal file
@@ -0,0 +1,312 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import request from 'request'
|
||||
import { EventEmitter } from 'events'
|
||||
import { performance } from 'perf_hooks'
|
||||
import { STATUS } from './util'
|
||||
|
||||
|
||||
const defaultChunkInfo = {
|
||||
path: null,
|
||||
startByte: 0,
|
||||
endByte: '',
|
||||
}
|
||||
|
||||
const defaultRequestOptions = {
|
||||
method: 'GET',
|
||||
headers: {},
|
||||
}
|
||||
const defaultOptions = {
|
||||
|
||||
}
|
||||
|
||||
class Task extends EventEmitter {
|
||||
/**
|
||||
*
|
||||
* @param {String} url download url
|
||||
* @param {Object} chunkInfo
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(url, savePath, filename, options = {}) {
|
||||
super()
|
||||
|
||||
this.resumeLastChunk = null
|
||||
this.downloadUrl = url
|
||||
this.chunkInfo = Object.assign({}, defaultChunkInfo, {
|
||||
path: path.join(savePath, filename),
|
||||
startByte: 0,
|
||||
})
|
||||
if (!this.chunkInfo.endByte) this.chunkInfo.endByte = ''
|
||||
|
||||
this.options = Object.assign({}, defaultOptions, options)
|
||||
|
||||
this.requestOptions = Object.assign({}, defaultRequestOptions, this.options.requestOptions || {})
|
||||
if (!this.requestOptions.headers) this.requestOptions.headers = {}
|
||||
|
||||
this.progress = {
|
||||
total: 0,
|
||||
downloaded: 0,
|
||||
speed: 0,
|
||||
}
|
||||
this.statsEstimate = {
|
||||
time: 0,
|
||||
bytes: 0,
|
||||
prevBytes: 0,
|
||||
}
|
||||
this.status = STATUS.idle
|
||||
}
|
||||
|
||||
__init() {
|
||||
this.status = STATUS.init
|
||||
const { path, startByte, endByte } = this.chunkInfo
|
||||
if (startByte) this.requestOptions.headers.range = `bytes=${startByte}-${endByte}`
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!path) return resolve()
|
||||
fs.stat(path, (errStat, stats) => {
|
||||
if (errStat) {
|
||||
if (errStat.code !== 'ENOENT') {
|
||||
this.__handleError(errStat)
|
||||
reject(errStat)
|
||||
return
|
||||
}
|
||||
} else if (stats.size >= 10) {
|
||||
fs.open(path, 'r', (errOpen, fd) => {
|
||||
if (errOpen) {
|
||||
this.__handleError(errOpen)
|
||||
reject(errOpen)
|
||||
return
|
||||
}
|
||||
fs.read(fd, Buffer.alloc(10), 0, 10, stats.size - 10, (errRead, bytesRead, buffer) => {
|
||||
if (errRead) {
|
||||
this.__handleError(errRead)
|
||||
reject(errRead)
|
||||
return
|
||||
}
|
||||
fs.close(fd, errClose => {
|
||||
if (errClose) {
|
||||
this.__handleError(errClose)
|
||||
reject(errClose)
|
||||
return
|
||||
}
|
||||
|
||||
// resume download
|
||||
// console.log(buffer)
|
||||
this.resumeLastChunk = buffer
|
||||
this.progress.downloaded = stats.size
|
||||
this.requestOptions.headers.range = `bytes=${stats.size - 10}-${endByte || ''}`
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
__httpFetch(url, options) {
|
||||
// console.log(options)
|
||||
this.request = request(url, options)
|
||||
.on('response', response => {
|
||||
if (response.statusCode !== 200 && response.statusCode !== 206) {
|
||||
this.status = STATUS.failed
|
||||
this.emit('fail', response)
|
||||
this.__closeRequest()
|
||||
this.__closeWriteStream()
|
||||
return
|
||||
}
|
||||
this.emit('response', response)
|
||||
try {
|
||||
this.__initDownload(response)
|
||||
} catch (error) {
|
||||
return this.__handleError(error)
|
||||
}
|
||||
this.status = STATUS.running
|
||||
response
|
||||
.on('data', this.__handleWriteData.bind(this))
|
||||
.on('error', err => this.__handleError(err))
|
||||
.on('end', () => {
|
||||
if (response.complete) {
|
||||
this.__handleComplete()
|
||||
} else {
|
||||
this.__handleError(new Error('The connection was terminated while the message was still being sent'))
|
||||
}
|
||||
})
|
||||
})
|
||||
.on('error', err => this.__handleError(err))
|
||||
.on('close', () => this.__closeWriteStream())
|
||||
}
|
||||
|
||||
__initDownload(response) {
|
||||
this.progress.total = parseInt(response.headers['content-length'] || 0)
|
||||
let options = {}
|
||||
let isResumable = this.options.forceResume || response.headers['accept-ranges'] !== 'none'
|
||||
if (isResumable) {
|
||||
options.flags = 'a'
|
||||
if (this.progress.downloaded) this.progress.total -= 10
|
||||
} else {
|
||||
if (this.chunkInfo.startByte > 0) return this.__handleError(new Error('The resource cannot be resumed download.'))
|
||||
}
|
||||
this.progress.total += this.progress.downloaded
|
||||
this.statsEstimate.prevBytes = this.progress.downloaded
|
||||
if (!this.chunkInfo.path) return this.__handleError(new Error('Chunk save Path is not set.'))
|
||||
this.ws = fs.createWriteStream(this.chunkInfo.path, options)
|
||||
|
||||
this.ws.on('finish', () => this.__closeWriteStream())
|
||||
this.ws.on('error', async err => {
|
||||
await this.__handleError(err)
|
||||
fs.unlink(this.chunkInfo.path, () => this.__handleError(err))
|
||||
})
|
||||
}
|
||||
|
||||
__handleComplete() {
|
||||
if (this.status == STATUS.error) return
|
||||
this.__closeWriteStream().then(() => {
|
||||
if (this.progress.downloaded == this.progress.total) {
|
||||
this.status = STATUS.completed
|
||||
this.emit('completed')
|
||||
} else {
|
||||
this.status = STATUS.stopped
|
||||
this.emit('stop')
|
||||
}
|
||||
})
|
||||
console.log('end')
|
||||
}
|
||||
|
||||
__handleError(error) {
|
||||
if (this.status == STATUS.error) return
|
||||
this.status = STATUS.error
|
||||
this.__closeRequest()
|
||||
this.__closeWriteStream()
|
||||
this.emit('error', error)
|
||||
}
|
||||
|
||||
__closeWriteStream() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.ws) return resolve()
|
||||
console.log('close write stream')
|
||||
this.ws.close(err => {
|
||||
if (err) {
|
||||
this.status = STATUS.error
|
||||
this.emit('error', err)
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
this.ws = null
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
__closeRequest() {
|
||||
if (!this.request) return
|
||||
console.log('close request')
|
||||
this.request.abort()
|
||||
this.request = null
|
||||
}
|
||||
|
||||
__handleWriteData(chunk) {
|
||||
if (this.resumeLastChunk) {
|
||||
chunk = this.__handleDiffChunk(chunk)
|
||||
if (!chunk) {
|
||||
this.__handleError(new Error('Resume failed, response chunk does not match.'))
|
||||
this.stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
// console.log('data', chunk)
|
||||
if (this.status == STATUS.stopped || this.ws == null) return console.log('cancel write')
|
||||
this.__calculateProgress(chunk.length)
|
||||
this.ws.write(chunk, err => {
|
||||
if (!err) return
|
||||
console.log(err)
|
||||
this.__handleError(err)
|
||||
this.stop()
|
||||
})
|
||||
}
|
||||
|
||||
__handleDiffChunk(chunk) {
|
||||
// console.log('diff', chunk)
|
||||
let resumeLastChunkLen = this.resumeLastChunk.length
|
||||
let chunkLen = chunk.length
|
||||
let isOk
|
||||
if (chunkLen >= resumeLastChunkLen) {
|
||||
isOk = chunk.slice(0, resumeLastChunkLen).toString('hex') === this.resumeLastChunk.toString('hex')
|
||||
if (!isOk) return null
|
||||
|
||||
this.resumeLastChunk = null
|
||||
return chunk.slice(resumeLastChunkLen)
|
||||
} else {
|
||||
isOk = chunk.slice(0, chunkLen).toString('hex') === this.resumeLastChunk.slice(0, chunkLen).toString('hex')
|
||||
if (!isOk) return null
|
||||
this.resumeLastChunk = this.resumeLastChunk.slice(chunkLen)
|
||||
return chunk.slice(chunkLen)
|
||||
}
|
||||
}
|
||||
|
||||
__handleStop() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.request) {
|
||||
this.request.abort()
|
||||
this.request = null
|
||||
}
|
||||
if (this.ws) {
|
||||
this.ws.close(err => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
this.emit('error', err)
|
||||
return
|
||||
}
|
||||
this.ws = null
|
||||
resolve()
|
||||
})
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
__calculateProgress(receivedBytes) {
|
||||
const currentTime = performance.now()
|
||||
const elaspsedTime = currentTime - this.statsEstimate.time
|
||||
|
||||
const progress = this.progress
|
||||
progress.downloaded += receivedBytes
|
||||
progress.progress = progress.total ? (progress.downloaded / progress.total) * 100 : -1
|
||||
|
||||
|
||||
// emit the progress every second or if finished
|
||||
if (progress.downloaded === progress.total || elaspsedTime > 1000) {
|
||||
this.statsEstimate.time = currentTime
|
||||
this.statsEstimate.bytes = progress.downloaded - this.statsEstimate.prevBytes
|
||||
this.statsEstimate.prevBytes = progress.downloaded
|
||||
this.emit('progress', {
|
||||
total: progress.total,
|
||||
downloaded: progress.downloaded,
|
||||
progress: progress.progress,
|
||||
speed: this.statsEstimate.bytes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.status = STATUS.running
|
||||
await this.__init()
|
||||
this.__httpFetch(this.downloadUrl, this.requestOptions)
|
||||
this.emit('start')
|
||||
}
|
||||
|
||||
async stop() {
|
||||
if (this.status === STATUS.stopped) return
|
||||
this.status = STATUS.stopped
|
||||
await this.__handleStop()
|
||||
this.emit('stop')
|
||||
}
|
||||
|
||||
refreshUrl(url) {
|
||||
this.downloadUrl = url
|
||||
}
|
||||
}
|
||||
|
||||
export default Task
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DownloaderHelper } from 'node-downloader-helper'
|
||||
import Downloader from './Downloader'
|
||||
// import { pauseResumeTimer } from './util'
|
||||
import { sizeFormate } from '../index'
|
||||
import { sizeFormate, getProxyInfo } from '../index'
|
||||
import { debugDownload } from '../env'
|
||||
|
||||
// these are the default options
|
||||
@@ -21,38 +21,34 @@ export default ({
|
||||
fileName,
|
||||
method = 'get',
|
||||
headers,
|
||||
override,
|
||||
forceResume,
|
||||
// resumeTime = 5000,
|
||||
onEnd = () => {},
|
||||
onCompleted = () => {},
|
||||
onError = () => {},
|
||||
onStateChanged = () => {},
|
||||
onDownload = () => {},
|
||||
onPause = () => {},
|
||||
onResume = () => {},
|
||||
onFail = () => {},
|
||||
onStart = () => {},
|
||||
onStop = () => {},
|
||||
onProgress = () => {},
|
||||
resumeInfo,
|
||||
} = {}) => {
|
||||
const dl = new DownloaderHelper(url, path, {
|
||||
fileName,
|
||||
method,
|
||||
headers,
|
||||
override,
|
||||
const dl = new Downloader(url, path, fileName, {
|
||||
requestOptions: {
|
||||
method,
|
||||
headers,
|
||||
proxy: getProxyInfo(),
|
||||
},
|
||||
|
||||
forceResume,
|
||||
})
|
||||
|
||||
dl.on('end', () => {
|
||||
onEnd()
|
||||
dl.on('completed', () => {
|
||||
onCompleted()
|
||||
debugDownload && console.log('Download Completed')
|
||||
}).on('error', err => {
|
||||
if (err.message === 'socket hang up') return
|
||||
onError(err)
|
||||
debugDownload && console.error('Something happend', err)
|
||||
}).on('stateChanged', state => {
|
||||
onStateChanged(state)
|
||||
debugDownload && console.log('State: ', state)
|
||||
}).on('download', () => {
|
||||
onDownload()
|
||||
}).on('start', () => {
|
||||
onStart()
|
||||
// pauseResumeTimer(dl, resumeTime)
|
||||
}).on('progress', stats => {
|
||||
const progress = stats.progress.toFixed(2)
|
||||
@@ -68,24 +64,17 @@ export default ({
|
||||
const total = sizeFormate(stats.total)
|
||||
console.log(`${speed}/s - ${progress}% [${downloaded}/${total}]`)
|
||||
}
|
||||
}).on('pause', () => {
|
||||
onPause()
|
||||
}).on('stop', () => {
|
||||
onStop()
|
||||
debugDownload && console.log('paused')
|
||||
}).on('resume', () => {
|
||||
onResume()
|
||||
debugDownload && console.log('resume')
|
||||
}).on('fail', resp => {
|
||||
onFail(resp)
|
||||
debugDownload && console.log('fail')
|
||||
})
|
||||
|
||||
debugDownload && console.log('Downloading: ', url)
|
||||
|
||||
if (resumeInfo) {
|
||||
dl.__total = resumeInfo.totalFileSize // <--- Workaround
|
||||
// dl.__filePath = resumeInfo.filePath // <--- Workaround
|
||||
dl.__isResumable = true // <--- Workaround
|
||||
dl.resume()
|
||||
} else {
|
||||
dl.start()
|
||||
}
|
||||
dl.start()
|
||||
|
||||
return dl
|
||||
}
|
||||
|
||||
@@ -1,24 +1,11 @@
|
||||
import { DH_STATES } from 'node-downloader-helper'
|
||||
|
||||
|
||||
export const pauseResumeTimer = (_dl, wait) => {
|
||||
setTimeout(() => {
|
||||
if (_dl.state === DH_STATES.FINISHED || _dl.state === DH_STATES.FAILED) {
|
||||
return
|
||||
}
|
||||
|
||||
_dl
|
||||
.pause()
|
||||
.then(() => console.log(`Paused for ${wait / 1000} seconds`))
|
||||
.then(() =>
|
||||
setTimeout(() => {
|
||||
if (!_dl.isResumable()) {
|
||||
console.warn(
|
||||
"This URL doesn't support resume, it will start from the beginning"
|
||||
)
|
||||
}
|
||||
return _dl.resume()
|
||||
}, wait)
|
||||
)
|
||||
}, wait)
|
||||
exports.STATUS = {
|
||||
idle: 'IDLE',
|
||||
init: 'INIT',
|
||||
running: 'RUNNING',
|
||||
paused: 'PAUSED',
|
||||
stopped: 'STOPPED',
|
||||
completed: 'COMPLETED',
|
||||
error: 'ERROR',
|
||||
failed: 'FAILED',
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import fs from 'fs'
|
||||
import { shell, remote, clipboard } from 'electron'
|
||||
import { shell, clipboard } from 'electron'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
import crypto from 'crypto'
|
||||
import { rendererSend } from '../../common/icp'
|
||||
import { rendererSend, rendererInvoke } from '../../common/ipc'
|
||||
|
||||
/**
|
||||
* 获取两个数之间的随机整数,大于等于min,小于max
|
||||
@@ -42,22 +42,35 @@ export const b64DecodeUnicode = str => {
|
||||
|
||||
export const decodeName = str => str.replace(/'/g, '\'')
|
||||
|
||||
export const scrollTo = (element, to, duration = 300, fn = function() {}) => {
|
||||
const easeInOutQuad = (t, b, c, d) => {
|
||||
t /= d / 2
|
||||
if (t < 1) return (c / 2) * t * t + b
|
||||
t--
|
||||
return (-c / 2) * (t * (t - 2) - 1) + b
|
||||
}
|
||||
/**
|
||||
* 设置滚动条位置
|
||||
* @param {*} element 要设置滚动的容器 dom
|
||||
* @param {*} to 滚动的目标位置
|
||||
* @param {*} duration 滚动完成时间 ms
|
||||
* @param {*} fn 滚动完成后的回调
|
||||
*/
|
||||
export const scrollTo = (element, to, duration = 300, fn = () => {}) => {
|
||||
if (!element) return
|
||||
const start = element.scrollTop || element.scrollY || 0
|
||||
if (to > start) {
|
||||
let maxScrollTop = element.scrollHeight - element.clientHeight
|
||||
if (to > maxScrollTop) to = maxScrollTop
|
||||
} else if (to < start) {
|
||||
if (to < 0) to = 0
|
||||
} else return fn()
|
||||
const change = to - start
|
||||
const increment = 10
|
||||
if (!change) {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
let currentTime = 0; let val
|
||||
const easeInOutQuad = (t, b, c, d) => {
|
||||
t /= d / 2
|
||||
if (t < 1) return (c / 2) * t * t + b
|
||||
t--
|
||||
return (-c / 2) * (t * (t - 2) - 1) + b
|
||||
}
|
||||
if (!change) return fn()
|
||||
|
||||
let currentTime = 0
|
||||
let val
|
||||
|
||||
const animateScroll = () => {
|
||||
currentTime += increment
|
||||
val = parseInt(easeInOutQuad(currentTime, start, change, duration))
|
||||
@@ -83,16 +96,16 @@ export const checkPath = path => fs.existsSync(path)
|
||||
|
||||
|
||||
/**
|
||||
* 在资源管理器中打开目录
|
||||
* 选择路径
|
||||
* @param {*} 选项
|
||||
*/
|
||||
export const openSelectDir = options => remote.dialog.showOpenDialog(remote.getCurrentWindow(), options)
|
||||
export const selectDir = options => rendererInvoke('selectDir', options)
|
||||
|
||||
/**
|
||||
* 在资源管理器中打开目录
|
||||
* 打开保存对话框
|
||||
* @param {*} 选项
|
||||
*/
|
||||
export const openSaveDir = options => remote.dialog.showSaveDialog(remote.getCurrentWindow(), options)
|
||||
export const openSaveDir = options => rendererInvoke('showSaveDialog', options)
|
||||
|
||||
/**
|
||||
* 在资源管理器中打开目录
|
||||
@@ -164,7 +177,7 @@ export const isChildren = (parent, children) => {
|
||||
* @param {*} setting
|
||||
*/
|
||||
export const updateSetting = setting => {
|
||||
const defaultVersion = '1.0.13'
|
||||
const defaultVersion = '1.0.14'
|
||||
const defaultSetting = {
|
||||
version: defaultVersion,
|
||||
player: {
|
||||
@@ -202,6 +215,7 @@ export const updateSetting = setting => {
|
||||
},
|
||||
odc: {
|
||||
isAutoClearSearchInput: false,
|
||||
isAutoClearSearchList: false,
|
||||
},
|
||||
search: {
|
||||
searchSource: 'kw',
|
||||
@@ -219,9 +233,8 @@ export const updateSetting = setting => {
|
||||
windowSizeId: 1,
|
||||
themeId: 0,
|
||||
sourceId: 'kw',
|
||||
apiSource: 'test',
|
||||
apiSource: 'temp',
|
||||
randomAnimate: true,
|
||||
ignoreVersion: null,
|
||||
}
|
||||
const overwriteSetting = {
|
||||
version: defaultVersion,
|
||||
@@ -251,8 +264,8 @@ export const openUrl = url => {
|
||||
* 设置标题
|
||||
*/
|
||||
let dom_title = document.getElementsByTagName('title')[0]
|
||||
export const setTitle = title => {
|
||||
dom_title.innerText = title || '洛雪音乐助手'
|
||||
export const setTitle = (title = '洛雪音乐助手') => {
|
||||
dom_title.innerText = title
|
||||
}
|
||||
|
||||
|
||||
@@ -361,19 +374,22 @@ export const asyncSetArray = (from, to, num = 100) => new Promise(resolve => {
|
||||
|
||||
/**
|
||||
* 获取缓存大小
|
||||
* @param {*} win
|
||||
*/
|
||||
export const getCacheSize = () => remote.getCurrentWindow().webContents.session.getCacheSize()
|
||||
export const getCacheSize = () => rendererInvoke('getCacheSize')
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @param {*} win
|
||||
*/
|
||||
export const clearCache = () => remote.getCurrentWindow().webContents.session.clearCache()
|
||||
export const clearCache = () => rendererInvoke('clearCache')
|
||||
|
||||
/**
|
||||
* 设置窗口大小
|
||||
* @param {*} width
|
||||
* @param {*} height
|
||||
*/
|
||||
export const setWindowSize = (width, height) => remote.getCurrentWindow().setBounds({ width, height })
|
||||
export const setWindowSize = (width, height) => rendererSend('setWindowSize', { width, height })
|
||||
|
||||
|
||||
export const getProxyInfo = () => window.globalObj.proxy.enable
|
||||
? `http://${window.globalObj.proxy.username}:${window.globalObj.proxy.password}@${window.globalObj.proxy.host}:${window.globalObj.proxy.port};`
|
||||
: undefined
|
||||
|
||||
@@ -145,9 +145,7 @@ export default {
|
||||
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)
|
||||
)
|
||||
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, ++tryNum)
|
||||
return {
|
||||
|
||||
@@ -4,7 +4,7 @@ import tx from './tx'
|
||||
import wy from './wy'
|
||||
import mg from './mg'
|
||||
import bd from './bd'
|
||||
export default {
|
||||
const sources = {
|
||||
sources: [
|
||||
{
|
||||
name: '酷我音乐',
|
||||
@@ -38,3 +38,12 @@ export default {
|
||||
mg,
|
||||
bd,
|
||||
}
|
||||
export default {
|
||||
...sources,
|
||||
init() {
|
||||
for (let source of sources.sources) {
|
||||
let sm = sources[source.id]
|
||||
sm && sm.init && sm.init()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export default {
|
||||
userid: 2626431536,
|
||||
vip: 1,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
if (body.error_code !== 0) return Promise.reject('图片获取失败')
|
||||
|
||||
@@ -92,7 +92,7 @@ export default {
|
||||
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)
|
||||
this.getSongListUrl(sortId, tagId, page),
|
||||
)
|
||||
return this._requestObj_list.promise.then(({ body }) => {
|
||||
if (body.status !== 1) return this.getSongList(sortId, tagId, page, ++tryNum)
|
||||
@@ -120,7 +120,7 @@ export default {
|
||||
return_min: 6,
|
||||
return_max: 15,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
return this._requestObj_listRecommend.promise.then(({ body }) => {
|
||||
if (body.status !== 1) return this.getSongListRecommend(++tryNum)
|
||||
@@ -253,7 +253,7 @@ export default {
|
||||
this.currentTagInfo.id = tagId
|
||||
this.currentTagInfo.info = Object.assign({}, info)
|
||||
return info
|
||||
})
|
||||
}),
|
||||
)
|
||||
if (!tagId && page === 1 && sortId === this.sortList[0].id) tasks.push(this.getSongListRecommend()) // 如果是所有类别,则顺便获取推荐列表
|
||||
return Promise.all(tasks).then(([list, info, recommendList]) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { httpGet, cancelHttp } from '../../request'
|
||||
import tempSearch from './tempSearch'
|
||||
import musicSearch from './musicSearch'
|
||||
import { formatSinger } from './util'
|
||||
import { formatSinger, getToken } from './util'
|
||||
import leaderboard from './leaderboard'
|
||||
import lyric from './lyric'
|
||||
import pic from './pic'
|
||||
@@ -96,6 +96,10 @@ const kw = {
|
||||
getPic(songInfo) {
|
||||
return pic.getPic(songInfo)
|
||||
},
|
||||
|
||||
init() {
|
||||
getToken()
|
||||
},
|
||||
}
|
||||
|
||||
export default kw
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { httpGet, cancelHttp } from '../../request'
|
||||
import { formatPlayTime, decodeName } from '../../index'
|
||||
import { formatSinger } from './util'
|
||||
|
||||
import { formatSinger, getToken, matchToken } from './util'
|
||||
|
||||
export default {
|
||||
list: [
|
||||
@@ -89,55 +88,65 @@ export default {
|
||||
})
|
||||
})
|
||||
},
|
||||
getData2(url) {
|
||||
async getData2(url) {
|
||||
if (this._cancelRequestObj2 != null) {
|
||||
cancelHttp(this._cancelRequestObj2)
|
||||
this._cancelPromiseCancelFn2(new Error('取消http请求'))
|
||||
}
|
||||
let token = window.kw_token.token
|
||||
if (!token) token = await getToken()
|
||||
return new Promise((resolve, reject) => {
|
||||
this._cancelPromiseCancelFn2 = reject
|
||||
this._cancelRequestObj2 = httpGet(url, (err, resp, body) => {
|
||||
this._cancelRequestObj2 = httpGet(url, {
|
||||
headers: {
|
||||
Referer: 'http://www.kuwo.cn/',
|
||||
csrf: token,
|
||||
cookie: 'kw_token=' + token,
|
||||
},
|
||||
}, (err, resp, body) => {
|
||||
this._cancelRequestObj2 = null
|
||||
this._cancelPromiseCancelFn2 = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
return reject(err)
|
||||
}
|
||||
window.kw_token.token = matchToken(resp.headers)
|
||||
resolve(body)
|
||||
})
|
||||
})
|
||||
},
|
||||
filterData(rawList, rawList2) {
|
||||
// console.log(rawList)
|
||||
// console.log(rawList.length, rawList2.length)
|
||||
return rawList.map((item, inedx) => {
|
||||
let formats = item.formats.split('|')
|
||||
let types = []
|
||||
let _types = {}
|
||||
if (formats.indexOf('MP3128')) {
|
||||
if (formats.includes('MP3128')) {
|
||||
types.push({ type: '128k', size: null })
|
||||
_types['128k'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
// if (formats.indexOf('MP3192')) {
|
||||
// if (formats.includes('MP3192')) {
|
||||
// types.push({ type: '192k', size: null })
|
||||
// _types['192k'] = {
|
||||
// size: null,
|
||||
// }
|
||||
// }
|
||||
if (formats.indexOf('MP3H')) {
|
||||
if (formats.includes('MP3H')) {
|
||||
types.push({ type: '320k', size: null })
|
||||
_types['320k'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
// if (formats.indexOf('AL')) {
|
||||
// if (formats.includes('AL')) {
|
||||
// types.push({ type: 'ape', size: null })
|
||||
// _types.ape = {
|
||||
// size: null,
|
||||
// }
|
||||
// }
|
||||
if (formats.indexOf('ALFLAC')) {
|
||||
if (formats.includes('ALFLAC')) {
|
||||
types.push({ type: 'flac', size: null })
|
||||
_types.flac = {
|
||||
size: null,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { httpFetch } from '../../request'
|
||||
export default {
|
||||
getPic({ songmid }) {
|
||||
const requestObj = httpFetch(`http://artistpicserver.kuwo.cn/pic.web?corp=kuwo&type=rid_pic&pictype=500&size=500&rid=${songmid}`)
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => body)
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => /^http/.test(body) ? body : null)
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
@@ -186,19 +186,19 @@ export default {
|
||||
let formats = item.formats.split('|')
|
||||
let types = []
|
||||
let _types = {}
|
||||
if (formats.indexOf('MP3128')) {
|
||||
if (formats.includes('MP3128')) {
|
||||
types.push({ type: '128k', size: null })
|
||||
_types['128k'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
if (formats.indexOf('MP3H')) {
|
||||
if (formats.includes('MP3H')) {
|
||||
types.push({ type: '320k', size: null })
|
||||
_types['320k'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
if (formats.indexOf('ALFLAC')) {
|
||||
if (formats.includes('ALFLAC')) {
|
||||
types.push({ type: 'flac', size: null })
|
||||
_types.flac = {
|
||||
size: null,
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
import { httpFetch } from '../../request'
|
||||
import { decodeName } from '../../index'
|
||||
import { getToken, matchToken } from './util'
|
||||
|
||||
|
||||
export default {
|
||||
regExps: {
|
||||
relWord: /RELWORD=(.+)/,
|
||||
},
|
||||
requestObj: null,
|
||||
tempSearch(str) {
|
||||
tempSearch(str, token) {
|
||||
this.cancelTempSearch()
|
||||
this.requestObj = httpFetch(`http://www.kuwo.cn/api/www/search/searchKey?key=${encodeURIComponent(str)}`, {
|
||||
headers: {
|
||||
Referer: 'http://www.kuwo.cn/',
|
||||
csrf: token,
|
||||
cookie: 'kw_token=' + token,
|
||||
},
|
||||
})
|
||||
return this.requestObj.promise.then(({ statusCode, body }) => {
|
||||
if (statusCode != 200 || body.code !== 200) return Promise.reject(new Error('请求失败'))
|
||||
return this.requestObj.promise.then(({ statusCode, body, headers }) => {
|
||||
if (statusCode != 200) return Promise.reject(new Error('请求失败'))
|
||||
window.kw_token.token = matchToken(headers)
|
||||
if (body.code !== 200) return Promise.reject(new Error('请求失败'))
|
||||
return body
|
||||
})
|
||||
},
|
||||
@@ -27,7 +33,9 @@ export default {
|
||||
cancelTempSearch() {
|
||||
if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
|
||||
},
|
||||
search(str) {
|
||||
return this.tempSearch(str).then(result => this.handleResult(result.data))
|
||||
async search(str) {
|
||||
let token = window.kw_token.token
|
||||
if (!token) token = await getToken()
|
||||
return this.tempSearch(str, token).then(result => this.handleResult(result.data))
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,2 +1,34 @@
|
||||
import { httpGet } from '../../request'
|
||||
|
||||
if (!window.kw_token) {
|
||||
window.kw_token = {
|
||||
token: null,
|
||||
isGetingToken: false,
|
||||
}
|
||||
}
|
||||
|
||||
export const formatSinger = rawData => rawData.replace(/&/g, '、')
|
||||
|
||||
export const matchToken = headers => {
|
||||
try {
|
||||
return headers['set-cookie'][0].match(/kw_token=(\w+)/)[1]
|
||||
} catch (err) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const wait = time => new Promise(resolve => setTimeout(() => resolve(), time))
|
||||
|
||||
|
||||
export const getToken = () => new Promise((resolve, reject) => {
|
||||
if (window.kw_token.isGetingToken) return wait(1000).then(() => getToken().then(token => resolve(token)))
|
||||
if (window.kw_token.token) return resolve(window.kw_token.token)
|
||||
window.kw_token.isGetingToken = true
|
||||
httpGet('http://www.kuwo.cn', (err, resp) => {
|
||||
window.kw_token.isGetingToken = false
|
||||
if (err) return reject(err)
|
||||
if (resp.statusCode != 200) return reject(new Error('获取失败'))
|
||||
const token = window.kw_token.token = matchToken(resp.headers)
|
||||
resolve(token)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ const api_test = {
|
||||
family: 4,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve({ type, url: encodeURI(body.data) }) : Promise.reject(new Error(requestMsg.fail))
|
||||
return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
|
||||
@@ -109,7 +109,7 @@ export default {
|
||||
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)
|
||||
this.getListUrl(sortId, tagId, page),
|
||||
)
|
||||
return this._requestObj_list.promise.then(({ body }) => {
|
||||
if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
|
||||
|
||||
@@ -4,6 +4,7 @@ import { debugRequest } from './env'
|
||||
import { requestMsg } from './message'
|
||||
import { bHh } from './music/options'
|
||||
import { deflateRawSync } from 'zlib'
|
||||
import { getProxyInfo } from './index'
|
||||
// import fs from 'fs'
|
||||
|
||||
const defaultHeaders = {
|
||||
@@ -219,10 +220,6 @@ export const http_jsonp = (url, options, callback) => {
|
||||
})
|
||||
}
|
||||
|
||||
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, {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<template lang="pug">
|
||||
div(:class="$style.download")
|
||||
//- transition
|
||||
div(:class="$style.header")
|
||||
material-tab(:class="$style.tab" :list="tabs" align="left" item-key="id" item-name="name" v-model="tabId")
|
||||
div(:class="$style.content" v-if="list.length")
|
||||
div(:class="$style.thead")
|
||||
table
|
||||
@@ -17,7 +19,7 @@ div(:class="$style.download")
|
||||
div.scroll(v-if="list.length" :class="$style.tbody")
|
||||
table
|
||||
tbody
|
||||
tr(v-for='(item, index) in list' :key='item.key' @click="handleDoubleClick(index)" :class="isPlayList && playIndex === index ? $style.active : ''")
|
||||
tr(v-for='(item, index) in showList' :key='item.key' @click="handleDoubleClick(index)" :class="playListIndex === index ? $style.active : ''")
|
||||
td.nobreak.center(style="width: 37px;" @click.stop)
|
||||
material-checkbox(:id="index.toString()" v-model="selectdData" :value="item")
|
||||
td.break(style="width: 28%;") {{item.musicInfo.name}} - {{item.musicInfo.singer}}
|
||||
@@ -46,6 +48,29 @@ export default {
|
||||
isIndeterminate: false,
|
||||
isShowEditBtn: false,
|
||||
isShowDownloadMultiple: false,
|
||||
tabs: [
|
||||
{
|
||||
name: '全部任务',
|
||||
id: 'all',
|
||||
},
|
||||
{
|
||||
name: '正在下载',
|
||||
id: 'runing',
|
||||
},
|
||||
{
|
||||
name: '已暂停',
|
||||
id: 'paused',
|
||||
},
|
||||
{
|
||||
name: '出错',
|
||||
id: 'error',
|
||||
},
|
||||
{
|
||||
name: '下载完成',
|
||||
id: 'finished',
|
||||
},
|
||||
],
|
||||
tabId: 'all',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -54,6 +79,27 @@ export default {
|
||||
isPlayList() {
|
||||
return this.listId == 'download'
|
||||
},
|
||||
playListIndex() {
|
||||
if (this.listId != 'download' || !this.list.length) return
|
||||
let info = this.list[this.playIndex]
|
||||
if (!info) return -1
|
||||
let key = info.key
|
||||
return this.showList.findIndex(i => i.key == key)
|
||||
},
|
||||
showList() {
|
||||
switch (this.tabId) {
|
||||
case 'runing':
|
||||
return this.list.filter(i => i.status == this.downloadStatus.RUN || i.status == this.downloadStatus.WAITING)
|
||||
case 'paused':
|
||||
return this.list.filter(i => i.status == this.downloadStatus.PAUSE)
|
||||
case 'error':
|
||||
return this.list.filter(i => i.status == this.downloadStatus.ERROR)
|
||||
case 'finished':
|
||||
return this.list.filter(i => i.status == this.downloadStatus.COMPLETED)
|
||||
default:
|
||||
return this.list
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectdData(n) {
|
||||
@@ -78,14 +124,14 @@ export default {
|
||||
handlePauseTask(index) {
|
||||
let info = this.list[index]
|
||||
let dl = this.dls[info.key]
|
||||
dl ? dl.pause() : this.pauseTask(info)
|
||||
dl ? dl.stop() : this.pauseTask(info)
|
||||
console.log('pause')
|
||||
},
|
||||
handleStartTask(index) {
|
||||
console.log('start')
|
||||
let info = this.list[index]
|
||||
let dl = this.dls[info.key]
|
||||
dl ? dl.resume() : this.startTask(info)
|
||||
dl ? dl.start() : this.startTask(info)
|
||||
},
|
||||
handleDoubleClick(index) {
|
||||
if (
|
||||
@@ -101,6 +147,8 @@ export default {
|
||||
this.clickIndex = -1
|
||||
},
|
||||
handleClick(index) {
|
||||
const key = this.showList[index].key
|
||||
index = this.list.findIndex(i => i.key === key)
|
||||
let info = this.list[index]
|
||||
if (info.isComplate) {
|
||||
this.handlePlay(index)
|
||||
@@ -112,29 +160,32 @@ export default {
|
||||
},
|
||||
handlePlay(index) {
|
||||
if (!checkPath(this.list[index].filePath)) return
|
||||
this.setList({ list: this.list, listId: 'download', index })
|
||||
let path = this.list[index].filePath
|
||||
this.setList({ list: this.list, listId: 'download', index: this.list.findIndex(i => i.filePath === path) })
|
||||
},
|
||||
handleListBtnClick(info) {
|
||||
const key = this.showList[info.index].key
|
||||
let index = this.list.findIndex(i => i.key === key)
|
||||
switch (info.action) {
|
||||
case 'play':
|
||||
this.handlePlay(info.index)
|
||||
this.handlePlay(index)
|
||||
break
|
||||
case 'start':
|
||||
this.handleStartTask(info.index)
|
||||
this.handleStartTask(index)
|
||||
break
|
||||
case 'pause':
|
||||
this.handlePauseTask(info.index)
|
||||
this.handlePauseTask(index)
|
||||
break
|
||||
case 'remove':
|
||||
this.removeTask(info.index)
|
||||
this.removeTask(index)
|
||||
break
|
||||
case 'file':
|
||||
this.handleOpenFolder(info.index)
|
||||
this.handleOpenFolder(index)
|
||||
break
|
||||
}
|
||||
},
|
||||
handleSelectAllData(isSelect) {
|
||||
this.selectdData = isSelect ? [...this.list] : []
|
||||
this.selectdData = isSelect ? [...this.showList] : []
|
||||
},
|
||||
resetSelect() {
|
||||
this.isSelectAll = false
|
||||
@@ -188,16 +239,23 @@ export default {
|
||||
.download {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
// .noItem {
|
||||
|
||||
// }
|
||||
}
|
||||
.header {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
flex: auto;
|
||||
overflow: hidden;
|
||||
// table {
|
||||
// position: relative;
|
||||
// thead {
|
||||
|
||||
@@ -95,7 +95,7 @@ export default {
|
||||
this.listAdd({ id: 'default', musicInfo: targetSong })
|
||||
}
|
||||
let targetIndex = this.defaultList.list.findIndex(
|
||||
s => s.songmid === targetSong.songmid
|
||||
s => s.songmid === targetSong.songmid,
|
||||
)
|
||||
if (targetIndex > -1) {
|
||||
this.setList({
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
table
|
||||
tbody
|
||||
tr(v-for='(item, index) in list' :key='item.songmid' :id="'mid_' + item.songmid"
|
||||
@click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', (isAPITemp && item.source != 'kw') ? $style.disabled : '']")
|
||||
@click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', (isAPITemp && item.source != 'kw') || item.source == 'tx' ? $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%;")
|
||||
@@ -243,7 +243,7 @@ export default {
|
||||
this.clickIndex = -1
|
||||
},
|
||||
testPlay(index) {
|
||||
if (this.isAPITemp && this.list[index].source != 'kw') return
|
||||
if (this.list[index].source == 'tx' || (this.isAPITemp && this.list[index].source != 'kw')) return
|
||||
this.setPlayList({ list: this.list, listId: this.listId, index })
|
||||
},
|
||||
handleRemove(index) {
|
||||
@@ -253,7 +253,7 @@ export default {
|
||||
switch (info.action) {
|
||||
case 'download': {
|
||||
const minfo = this.list[info.index]
|
||||
if (this.isAPITemp && minfo.source != 'kw') return
|
||||
if ((this.isAPITemp && minfo.source != 'kw') || minfo.source == 'tx') return
|
||||
this.musicInfo = minfo
|
||||
this.$nextTick(() => {
|
||||
this.isShowDownload = true
|
||||
@@ -286,7 +286,7 @@ export default {
|
||||
this.selectdData = []
|
||||
},
|
||||
handleAddDownloadMultiple(type) {
|
||||
const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : [...this.selectdData]
|
||||
const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : this.selectdData.filter(s => s.source != 'tx')
|
||||
this.createDownloadMultiple({ list, type })
|
||||
this.resetSelect()
|
||||
this.isShowDownloadMultiple = false
|
||||
|
||||
@@ -26,12 +26,13 @@
|
||||
| {{item.name}}
|
||||
span.badge.badge-info(v-if="item._types['320k']") 高品质
|
||||
span.badge.badge-success(v-if="item._types.ape || item._types.flac") 无损
|
||||
span(:class="$style.labelSource" v-if="searchSourceId == 'all'") {{item.source}}
|
||||
td.break(style="width: 20%;") {{item.singer}}
|
||||
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"
|
||||
:download-btn="item.source == 'kw' || !isAPITemp"
|
||||
:play-btn="item.source != 'tx' && (item.source == 'kw' || !isAPITemp)"
|
||||
:download-btn="item.source != 'tx' && (item.source == 'kw' || !isAPITemp)"
|
||||
@btn-click="handleListBtnClick")
|
||||
td(style="width: 10%;") {{item.interval || '--/--'}}
|
||||
div(:class="$style.pagination")
|
||||
@@ -40,7 +41,7 @@
|
||||
p 搜我所想~~😉
|
||||
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-flow-btn(:show="isShowEditBtn && (searchSourceId == 'kw' || searchSourceId == 'all' || !isAPITemp)" :remove-btn="false" @btn-click="handleFlowBtnClick")
|
||||
material-flow-btn(:show="isShowEditBtn && searchSourceId != 'tx' && (searchSourceId == 'kw' || searchSourceId == 'all' || !isAPITemp)" :remove-btn="false" @btn-click="handleFlowBtnClick")
|
||||
material-list-add-modal(:show="isShowListAdd" :musicInfo="musicInfo" @close="isShowListAdd = false")
|
||||
material-list-add-multiple-modal(:show="isShowListAddMultiple" :musicList="selectdData" @close="handleListAddModalClose")
|
||||
</template>
|
||||
@@ -189,12 +190,12 @@ 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') return
|
||||
if (this.listInfo.list[index].source == 'tx' || (this.isAPITemp && this.listInfo.list[index].source != 'kw')) return
|
||||
targetSong = this.listInfo.list[index]
|
||||
this.listAdd({ id: 'default', musicInfo: targetSong })
|
||||
}
|
||||
let targetIndex = this.defaultList.list.findIndex(
|
||||
s => s.songmid === targetSong.songmid
|
||||
s => s.songmid === targetSong.songmid,
|
||||
)
|
||||
if (targetIndex > -1) {
|
||||
this.setList({
|
||||
@@ -238,7 +239,7 @@ export default {
|
||||
}
|
||||
},
|
||||
filterList(list) {
|
||||
return this.setting.apiSource == 'temp' ? list.filter(s => s.source == 'kw') : [...list]
|
||||
return this.setting.apiSource == 'temp' ? list.filter(s => s.source == 'kw') : list.filter(s => s.source != 'tx')
|
||||
},
|
||||
handleListAddModalClose(isSelect) {
|
||||
if (isSelect) this.resetSelect()
|
||||
@@ -291,6 +292,13 @@ export default {
|
||||
.listBtn {
|
||||
min-height: 24px;
|
||||
}
|
||||
.labelSource {
|
||||
color: @color-theme;
|
||||
padding: 5px;
|
||||
font-size: .8em;
|
||||
line-height: 1;
|
||||
opacity: .75;
|
||||
}
|
||||
.pagination {
|
||||
text-align: center;
|
||||
padding: 15px 0;
|
||||
|
||||
@@ -78,7 +78,7 @@ div.scroll(:class="$style.setting")
|
||||
material-checkbox(id="setting_download_isDownloadLrc" v-model="current_setting.download.isDownloadLrc" label="是否启用")
|
||||
dt 网络设置
|
||||
dd
|
||||
h3 代理设置(歌曲下载暂不支持代理)
|
||||
h3 代理设置
|
||||
div
|
||||
p
|
||||
material-checkbox(id="setting_network_proxy_enable" v-model="current_setting.network.proxy.enable" @change="handleProxyChange('enable')" label="是否启用")
|
||||
@@ -93,6 +93,10 @@ div.scroll(:class="$style.setting")
|
||||
h3 离开搜索界面时清空搜索框
|
||||
div
|
||||
material-checkbox(id="setting_odc_isAutoClearSearchInput" v-model="current_setting.odc.isAutoClearSearchInput" label="是否启用")
|
||||
dd
|
||||
h3 离开搜索界面时清空搜索列表
|
||||
div
|
||||
material-checkbox(id="setting_odc_isAutoClearSearchList" v-model="current_setting.odc.isAutoClearSearchList" label="是否启用")
|
||||
dt 备份与恢复
|
||||
dd
|
||||
h3 部分数据
|
||||
@@ -108,7 +112,7 @@ div.scroll(:class="$style.setting")
|
||||
material-btn(:class="[$style.btn, $style.gapLeft]" min @click="handleExportAllData") 导出
|
||||
dt 其他
|
||||
dd
|
||||
h3 缓存大小(清理缓存后图片等资源将需要重新下载)
|
||||
h3 缓存大小(清理缓存后图片等资源将需要重新下载,不建议清理,软件会根据磁盘空间动态管理缓存大小)
|
||||
div
|
||||
p
|
||||
| 软件已使用缓存大小:
|
||||
@@ -120,10 +124,13 @@ div.scroll(:class="$style.setting")
|
||||
p.small
|
||||
| 最新版本:{{version.newVersion ? version.newVersion.version : '未知'}}
|
||||
p.small 当前版本:{{version.version}}
|
||||
p.small(v-if="version.newVersion")
|
||||
p.small(v-if="this.version.downloadProgress" style="line-height: 1.5;")
|
||||
| 发现新版本并在努力下载中,请稍后...⏳
|
||||
br
|
||||
| 下载进度:{{downloadProgress}}
|
||||
p(v-if="version.newVersion")
|
||||
span(v-if="isLatestVer") 软件已是最新,尽情地体验吧~🥂
|
||||
material-btn(v-else-if="setting.ignoreVersion || version.isError" :class="[$style.btn, $style.gapLeft]" min @click="showUpdateModal") 打开更新窗口 🚀
|
||||
span(v-else) 发现新版本并在努力下载中,请稍等...⏳
|
||||
material-btn(v-else :class="[$style.btn, $style.gapLeft]" min @click="showUpdateModal") 打开更新窗口 🚀
|
||||
p.small(v-else) 检查更新中...
|
||||
dt 关于洛雪音乐
|
||||
dd
|
||||
@@ -137,7 +144,7 @@ div.scroll(:class="$style.setting")
|
||||
span.hover(title="点击复制" @click="clipboardWriteText('glqw')") glqw
|
||||
p.small
|
||||
| 软件的常见问题可转至:
|
||||
span.hover.underline(title="点击打开" @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop#常见问题')") 常见问题
|
||||
span.hover.underline(title="点击打开" @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md')") 常见问题
|
||||
//- p.small
|
||||
| 怀念曾经的
|
||||
strong @messoer
|
||||
@@ -174,7 +181,7 @@ div.scroll(:class="$style.setting")
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import {
|
||||
openDirInExplorer,
|
||||
openSelectDir,
|
||||
selectDir,
|
||||
openSaveDir,
|
||||
updateSetting,
|
||||
openUrl,
|
||||
@@ -184,7 +191,7 @@ import {
|
||||
sizeFormate,
|
||||
setWindowSize,
|
||||
} from '../utils'
|
||||
import { rendererSend } from '../../common/icp'
|
||||
import { rendererSend } from '../../common/ipc'
|
||||
import fs from 'fs'
|
||||
|
||||
export default {
|
||||
@@ -198,6 +205,11 @@ export default {
|
||||
isShowRebootBtn() {
|
||||
return this.current_setting.windowSizeId != window.currentWindowSizeId
|
||||
},
|
||||
downloadProgress() {
|
||||
return this.version.downloadProgress
|
||||
? `${this.version.downloadProgress.percent.toFixed(2)}% - ${sizeFormate(this.version.downloadProgress.transferred)}/${sizeFormate(this.version.downloadProgress.total)} - ${sizeFormate(this.version.downloadProgress.bytesPerSecond)}/s`
|
||||
: '更新初始化中...'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -233,6 +245,7 @@ export default {
|
||||
},
|
||||
odc: {
|
||||
isAutoClearSearchInput: false,
|
||||
isAutoClearSearchList: false,
|
||||
},
|
||||
windowSizeId: 1,
|
||||
themeId: 0,
|
||||
@@ -259,25 +272,14 @@ export default {
|
||||
},
|
||||
],
|
||||
apiSources: [
|
||||
// {
|
||||
// id: 'messoer',
|
||||
// // label: '由 messoer 提供的接口(推荐,软件的所有功能都可用)',
|
||||
// label: '由 messoer 提供的接口(该接口已关闭)',
|
||||
// disabled: true,
|
||||
// },
|
||||
// {
|
||||
// id: 'internal',
|
||||
// label: '内置接口(只能试听或下载128k音质,该接口支持软件的所有功能)',
|
||||
// disabled: false,
|
||||
// },
|
||||
{
|
||||
id: 'test',
|
||||
label: '测试接口(几乎软件的所有功能都可用,该接口访问速度略慢)',
|
||||
label: '测试接口(几乎软件的所有功能都可用)',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: 'temp',
|
||||
label: '临时接口(软件的某些功能不可用,该接口比测试接口快一些,建议测试接口不可用再使用本接口)',
|
||||
label: '临时接口(软件的某些功能不可用,建议测试接口不可用再使用本接口)',
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
@@ -328,7 +330,7 @@ export default {
|
||||
this.getCacheSize()
|
||||
},
|
||||
handleChangeSavePath() {
|
||||
openSelectDir({
|
||||
selectDir({
|
||||
title: '选择歌曲保存路径',
|
||||
defaultPath: this.current_setting.download.savePath,
|
||||
properties: ['openDirectory'],
|
||||
@@ -357,7 +359,7 @@ export default {
|
||||
type: 'setting',
|
||||
data: this.setting,
|
||||
}
|
||||
fs.writeFile(path, JSON.stringify(data, null, 2), 'utf8', err => {
|
||||
fs.writeFile(path, JSON.stringify(data), 'utf8', err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
@@ -387,7 +389,7 @@ export default {
|
||||
this.loveList,
|
||||
],
|
||||
}
|
||||
fs.writeFile(path, JSON.stringify(data, null, 2), 'utf8', err => {
|
||||
fs.writeFile(path, JSON.stringify(data), 'utf8', err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
@@ -416,12 +418,12 @@ export default {
|
||||
this.loveList,
|
||||
],
|
||||
}
|
||||
fs.writeFile(path, JSON.stringify(allData, null, 2), 'utf8', err => {
|
||||
fs.writeFile(path, JSON.stringify(allData), 'utf8', err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
handleImportAllData() {
|
||||
openSelectDir({
|
||||
selectDir({
|
||||
title: '选择备份文件',
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
@@ -443,7 +445,7 @@ export default {
|
||||
})
|
||||
},
|
||||
handleImportSetting() {
|
||||
openSelectDir({
|
||||
selectDir({
|
||||
title: '选择配置文件',
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
@@ -465,7 +467,7 @@ export default {
|
||||
})
|
||||
},
|
||||
handleImportPlayList() {
|
||||
openSelectDir({
|
||||
selectDir({
|
||||
title: '选择列表文件',
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
|
||||
@@ -183,14 +183,15 @@ export default {
|
||||
let targetSong
|
||||
if (index == null) {
|
||||
targetSong = this.selectdData[0]
|
||||
this.listAddMultiple({ id: 'default', list: this.selectdData })
|
||||
this.listAddMultiple({ id: 'default', list: this.filterList(this.selectdData) })
|
||||
this.resetSelect()
|
||||
} else {
|
||||
targetSong = this.listDetail.list[index]
|
||||
if (targetSong.source == 'tx' || (this.isAPITemp && targetSong.source != 'kw')) return
|
||||
this.listAdd({ id: 'default', musicInfo: targetSong })
|
||||
}
|
||||
let targetIndex = this.defaultList.list.findIndex(
|
||||
s => s.songmid === targetSong.songmid
|
||||
s => s.songmid === targetSong.songmid,
|
||||
)
|
||||
if (targetIndex > -1) {
|
||||
this.setList({
|
||||
@@ -234,7 +235,7 @@ export default {
|
||||
case 'wy':
|
||||
type = '128k'
|
||||
}
|
||||
this.createDownloadMultiple({ list: [...this.selectdData], type })
|
||||
this.createDownloadMultiple({ list: this.filterList(this.selectdData), type })
|
||||
this.resetSelect()
|
||||
this.isShowDownloadMultiple = false
|
||||
},
|
||||
@@ -345,6 +346,9 @@ export default {
|
||||
}
|
||||
this.importSongListText = text.replace(regx, '$1')
|
||||
},
|
||||
filterList(list) {
|
||||
return this.setting.apiSource == 'temp' ? list.filter(s => s.source == 'kw') : list.filter(s => s.source != 'tx')
|
||||
},
|
||||
/* addSongListDetail() {
|
||||
// this.detailLoading = true
|
||||
// this.getListDetailAll(this.selectListInfo.id).then(() => {
|
||||
|
||||
Reference in New Issue
Block a user