You've already forked lx-music-desktop
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aa23b0f9d | ||
|
|
063bbc9f06 | ||
|
|
16bb23b8a7 | ||
|
|
a26bef6317 | ||
|
|
21c41d5af1 | ||
|
|
b270096591 | ||
|
|
be689f088d | ||
|
|
2410409342 | ||
|
|
a24d682747 | ||
|
|
2ada44a2fc | ||
|
|
d6c7fb8dcc | ||
|
|
bb40a8612a | ||
|
|
1182a6eba4 | ||
|
|
0d303889ef | ||
|
|
2b3aab2faf | ||
|
|
893d20b4e9 | ||
|
|
32e421356f | ||
|
|
9858170e61 | ||
|
|
16711e33e8 | ||
|
|
f534e11acb | ||
|
|
2ff0f4b102 | ||
|
|
5b2a44e3bd | ||
|
|
0b06206d34 | ||
|
|
cb93dfa218 | ||
|
|
c92517960e | ||
|
|
3a615a4a87 | ||
|
|
78d2541c14 | ||
|
|
b8d07b365b | ||
|
|
2d5848db94 | ||
|
|
d4c88edb8b | ||
|
|
90288da36d | ||
|
|
601e1a67a1 | ||
|
|
8817318a53 | ||
|
|
153836ea01 | ||
|
|
24c88b828c | ||
|
|
a580cedcb1 | ||
|
|
ba8991a034 | ||
|
|
6e07719c2c | ||
|
|
214745ae87 | ||
|
|
90ed66f0f4 | ||
|
|
f736a60018 | ||
|
|
f4d340f4b1 | ||
|
|
bccadcf13d | ||
|
|
43bc2b6b90 |
@@ -4,14 +4,14 @@ platform:
|
||||
cache:
|
||||
- node_modules
|
||||
- '%APPDATA%\npm-cache'
|
||||
# - '%USERPROFILE%\.electron'
|
||||
- '%LOCALAPPDATA%\electron\Cache'
|
||||
|
||||
install:
|
||||
- ps: Install-Product node 12 x64
|
||||
- npm install
|
||||
|
||||
build_script:
|
||||
- npm run pub:gh
|
||||
- npm run publish:gh
|
||||
|
||||
test: off
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -34,6 +34,7 @@ build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
node_modules.bak*/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
|
||||
43
.travis.yml
Normal file
43
.travis.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
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:
|
||||
- node_modules
|
||||
- $HOME/.cache/electron
|
||||
- $HOME/.cache/electron-builder
|
||||
- $HOME/.npm/_prebuilds
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
script:
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||
npm install && npm run publish:gh:linux
|
||||
else
|
||||
npm run publish:gh:mac
|
||||
fi
|
||||
|
||||
before_cache:
|
||||
- rm -rf $HOME/.cache/electron-builder/wine
|
||||
|
||||
# only run this script on pull requests and merges into
|
||||
# the 'master' and 'prod' branches
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
143
CHANGELOG.md
143
CHANGELOG.md
@@ -6,6 +6,149 @@ 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.3.2](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.1...v0.3.2) - 2019-08-24
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增酷狗排行榜其他音质下载
|
||||
|
||||
## [0.3.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.0...v0.3.1) - 2019-08-24
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复音量条主题适配
|
||||
|
||||
## [0.3.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.2.3...v0.3.0) - 2019-08-24
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增**MAC**及**Linux**版本(需要的可自行下载)
|
||||
- 新增音量调整
|
||||
- 新增任务栏播放进度条控制选项(现在可在设置界面关闭在任务栏显示的播放进度)
|
||||
- 新增更新出错时的弹窗提示
|
||||
- 从该版本起,非安装版也会有更新弹窗提醒了,但仍然需要手动下载新版本更新,版本信息可到设置页面查看
|
||||
|
||||
### 修复
|
||||
|
||||
- 强制把临时接口设置回 `messoer` 接口
|
||||
|
||||
## [0.2.3](https://github.com/lyswhut/lx-music-desktop/compare/v0.2.2...v0.2.3) - 2019-08-22
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增任务栏程序标题改变功能(播放歌曲时任务栏标题将显示当前播放的歌曲)
|
||||
|
||||
### 修复
|
||||
|
||||
- 使用临时接口时,试听列表中的下载按钮仍然能点击的Bug
|
||||
- 修复某些情况下歌曲链接未能缓存的问题
|
||||
|
||||
### 移除
|
||||
|
||||
- 移除临时接口(因服务器被攻击,本接口已关闭)
|
||||
- 移除列表栏设置的隐藏专辑栏选项(感觉这个设置并没有什么luan用,并且还会打破布局)
|
||||
|
||||
## [0.2.2](https://github.com/lyswhut/lx-music-desktop/compare/v0.2.1...v0.2.2) - 2019-08-21
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复下载过程中出错重试5次都失败后不会自动开始下一个任务的Bug
|
||||
- 修复播放到一半URL过期时不会刷新URL直接播放下一首的问题
|
||||
|
||||
## [0.2.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.2.0...v0.2.1) - 2019-08-20
|
||||
|
||||
### 优化
|
||||
|
||||
- 新增歌曲URL存储,当URL无效时才重新获取,以减少接口不稳定的影响
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复歌曲加载无法加载时自动切换混乱的Bug
|
||||
- 修复移除列表最后一首歌曲时播放器不停止播放的问题
|
||||
|
||||
## [0.2.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.6...v0.2.0) - 2019-08-20
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增**百度音乐**排行榜及其音乐直接试听与下载
|
||||
- 新增网易云排行榜音乐直接试听与下载(目前仅支持128k音质)
|
||||
- 新增酷狗排行榜音乐直接试听与下载(目前仅支持128k音质)
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复更新弹窗历史版本描述多余的换行问题
|
||||
- 修复歌曲无法播放的情况下歌词仍会播放的问题
|
||||
|
||||
## [0.1.6](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.5...v0.1.6) - 2019-08-19
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复列表多选音源限制Bug
|
||||
|
||||
## [0.1.5](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.4...v0.1.5) - 2019-08-19
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增搜索列表批量试听与下载功能
|
||||
- 新增排行榜列表批量试听与下载功能
|
||||
- 新增试听列表批量移除与下载功能
|
||||
- 新增下载列表批量开始、暂停与移除功能
|
||||
|
||||
### 优化
|
||||
|
||||
- 优化歌曲切换机制
|
||||
|
||||
## [0.1.4](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.3...v0.1.4) - 2019-08-18
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增音乐来源切换,可到设置页面-基本设置 look look !
|
||||
- 为搜索结果列表添加多选功能。
|
||||
P.S:暂时没想好多选后的操作按钮放哪...
|
||||
|
||||
### 优化
|
||||
|
||||
- 重构与改进checkbox组件,使其支持不定选中状态
|
||||
- 完善上一个版本的http请求封装并切换部分请求到该方法上
|
||||
- 优化其他一些细节
|
||||
|
||||
## [0.1.3](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.2...v0.1.3) - 2019-08-17
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增win32应用构建
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复安装包许可协议乱码问题
|
||||
- **messoer 提供的接口已挂**,暂时切换到临时接口!
|
||||
|
||||
### 移除
|
||||
|
||||
- 由于messoer接口无法使用,QQ音乐排行榜直接播放/下载功能暂时关闭
|
||||
|
||||
## [0.1.2](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.1...v0.1.2) - 2019-08-17
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复更新弹窗的内容显示问题
|
||||
|
||||
## [0.1.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.0...v0.1.1) - 2019-08-17
|
||||
|
||||
### 新增
|
||||
|
||||
- QQ音乐排行榜直接试听与下载(该接口貌似不太稳定,且用且珍惜!)
|
||||
|
||||
### 优化
|
||||
|
||||
- 优化http请求机制
|
||||
- 更新关于本软件说明
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复当上一个歌曲链接正在获取时切换歌曲请求不会取消的问题
|
||||
- 修复切换歌曲时仍然播放上一首歌曲的问题
|
||||
|
||||
## [0.1.0] - 2019-8-16
|
||||
|
||||
* 0.1.0版本发布
|
||||
|
||||
64
README.md
64
README.md
@@ -1,33 +1,63 @@
|
||||
# 洛雪音乐助手桌面版
|
||||
<p align="center"><a href="https://github.com/lyswhut/lx-music-desktop"><img width="200" src="https://github.com/lyswhut/lx-music-desktop/blob/master/doc/images/icon.png" alt="lx-music logo"></a></p>
|
||||
|
||||
[![GitHub release][1]][2]
|
||||
<p align="center">
|
||||
<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://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> -->
|
||||
</p>
|
||||
|
||||
<!-- [![GitHub release][1]][2]
|
||||
[![Build status][3]][4]
|
||||
[![GitHub All Releases Download][5]][6]
|
||||
[![GitHub Releases Download][5]][6]
|
||||
[![dev branch][7]][8]
|
||||
[![GitHub license][9]][10] -->
|
||||
|
||||
[1]: https://img.shields.io/github/release/lyswhut/lx-music-desktop
|
||||
<!-- [1]: https://img.shields.io/github/release/lyswhut/lx-music-desktop
|
||||
[2]: https://github.com/lyswhut/lx-music-desktop/releases
|
||||
[3]: https://ci.appveyor.com/api/projects/status/flrsqd5ymp8fnte5?svg=true
|
||||
[4]: https://ci.appveyor.com/project/lyswhut/lx-music-desktop
|
||||
[5]: https://img.shields.io/github/downloads/lyswhut/lx-music-desktop/latest/total
|
||||
[5]: https://img.shields.io/github/downloads/lyswhut/lx-music-desktop/total
|
||||
[6]: https://github.com/lyswhut/lx-music-desktop/releases
|
||||
[7]: https://img.shields.io/github/package-json/v/lyswhut/lx-music-desktop/dev
|
||||
[8]: https://github.com/lyswhut/lx-music-desktop/tree/dev
|
||||
[9]: https://img.shields.io/github/license/lyswhut/lx-music-desktop
|
||||
[10]: https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE -->
|
||||
|
||||
## 说明
|
||||
<h2 align="center">洛雪音乐助手桌面版</h2>
|
||||
|
||||
一个基于 Electron + Vue 开发的 Windows 版音乐软件。
|
||||
### 说明
|
||||
|
||||
一个基于 Electron + Vue 开发的音乐软件。
|
||||
|
||||
所用技术栈:
|
||||
|
||||
- Electron 7.x
|
||||
- Electron 6.x
|
||||
- Vue 2.x
|
||||
|
||||
其他说明:TODO
|
||||
已支持的平台:
|
||||
|
||||
感谢 <https://github.com/messoer> 提供的部分音乐API!
|
||||
- Windows 7 及以上
|
||||
- Mac OS
|
||||
- Linux
|
||||
|
||||
## 使用方法
|
||||
软件变化请查看:[更新日志](https://github.com/lyswhut/lx-music-desktop/blob/master/CHANGELOG.md)<br>
|
||||
软件下载请转到:[发布页面](https://github.com/lyswhut/lx-music-desktop/releases)<br>
|
||||
或者到网盘下载:`https://www.lanzous.com/b906260/` 密码:`glqw`
|
||||
|
||||
#### 关于软件更新
|
||||
|
||||
软件启动时若发现新版本时会自动从本仓库下载安装包,下载完毕会弹窗提示更新。<br>
|
||||
若下载未完成时软件被关闭,下次启动软件会再次自动下载。<br>
|
||||
目前暂未添加跳过更新某个版本的功能。<br>
|
||||
注意:**绿色版**的软件更新功能**不可用**,为了能及时地获取更新,建议使用安装版!!
|
||||
|
||||
### 源码使用方法
|
||||
|
||||
环境要求:Node.js 12.x
|
||||
|
||||
```bash
|
||||
# 开发模式
|
||||
@@ -36,7 +66,19 @@ npm run dev
|
||||
# 构建免安装版
|
||||
npm run pack:dir
|
||||
|
||||
# 构建安装包
|
||||
# 构建安装包(windows版)
|
||||
npm run pack
|
||||
|
||||
```
|
||||
|
||||
### UI界面
|
||||
|
||||
<p><a href="https://github.com/lyswhut/lx-music-desktop"><img width="100%" src="https://github.com/lyswhut/lx-music-desktop/blob/master/doc/images/app.png" alt="lx-music UI"></a></p>
|
||||
|
||||
### 致谢
|
||||
|
||||
感谢 [@messoer](https://github.com/messoer) 提供的部分音乐API!
|
||||
|
||||
### 许可证
|
||||
|
||||
[Apache License 2.0](https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE)
|
||||
|
||||
@@ -7,11 +7,6 @@ module.exports = {
|
||||
libraryTarget: 'commonjs2',
|
||||
path: path.join(__dirname, '../../dist/electron'),
|
||||
},
|
||||
externals: [
|
||||
// suppress electron-debug warning
|
||||
// see https://github.com/SimulatedGREG/electron-vue/issues/498
|
||||
{ 'electron-debug': 'electron-debug' },
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
common: path.join(__dirname, '../../src/common'),
|
||||
|
||||
@@ -10,7 +10,7 @@ const { mergeCSSLoaderDev } = require('../utils')
|
||||
|
||||
module.exports = merge(baseConfig, {
|
||||
mode: 'development',
|
||||
devtool: '#cheap-module-eval-source-map',
|
||||
devtool: 'eval-source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
@@ -46,7 +46,7 @@ module.exports = merge(baseConfig, {
|
||||
NODE_ENV: '"development"',
|
||||
ELECTRON_DISABLE_SECURITY_WARNINGS: 'true',
|
||||
},
|
||||
'__static': `"${path.join(__dirname, '../../src/static').replace(/\\/g, '\\\\')}"`,
|
||||
__static: `"${path.join(__dirname, '../../src/static').replace(/\\/g, '\\\\')}"`,
|
||||
}),
|
||||
],
|
||||
performance: {
|
||||
|
||||
@@ -9,7 +9,7 @@ const { mergeCSSLoaderDev } = require('../utils')
|
||||
|
||||
module.exports = merge(baseConfig, {
|
||||
mode: 'development',
|
||||
devtool: '#cheap-module-eval-source-map',
|
||||
devtool: 'eval-source-map',
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.join(__dirname, '../../dist/web'),
|
||||
|
||||
BIN
doc/images/app.png
Normal file
BIN
doc/images/app.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
BIN
doc/images/icon.png
Normal file
BIN
doc/images/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -1,5 +0,0 @@
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѧϰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD>ã<EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><EFBFBD>;<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
ʹ<EFBFBD>ñ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɵ<EFBFBD>һ<EFBFBD>к<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD><EFBFBD>߳е<EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
By: <20><>ѩ<EFBFBD><EFBFBD>
|
||||
219
licenses/license.rtf
Normal file
219
licenses/license.rtf
Normal file
@@ -0,0 +1,219 @@
|
||||
{\rtf1\adeflang1025\ansi\ansicpg936\uc2\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe2052\themelang1033\themelangfe2052\themelangcs0{\fonttbl{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;}
|
||||
{\f13\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'cb\'ce\'cc\'e5{\*\falt SimSun};}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}
|
||||
{\f44\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}@\'cb\'ce\'cc\'e5;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
|
||||
{\fdbmajor\f31501\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df Light;}{\fhimajor\f31502\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df Light;}
|
||||
{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
|
||||
{\fdbminor\f31505\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df{\*\falt DengXian};}{\fhiminor\f31506\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df{\*\falt DengXian};}
|
||||
{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f65\fbidi \fmodern\fcharset238\fprq1 Courier New CE;}{\f66\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}
|
||||
{\f68\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f69\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f70\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);}{\f71\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}
|
||||
{\f72\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f73\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f177\fbidi \fnil\fcharset0\fprq2 SimSun Western{\*\falt SimSun};}{\f487\fbidi \fnil\fcharset0\fprq2 @SimSun Western;}
|
||||
{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
|
||||
{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
|
||||
{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31520\fbidi \fnil\fcharset0\fprq2 DengXian Light Western;}
|
||||
{\fdbmajor\f31518\fbidi \fnil\fcharset238\fprq2 DengXian Light CE;}{\fdbmajor\f31519\fbidi \fnil\fcharset204\fprq2 DengXian Light Cyr;}{\fdbmajor\f31521\fbidi \fnil\fcharset161\fprq2 DengXian Light Greek;}
|
||||
{\fhimajor\f31530\fbidi \fnil\fcharset0\fprq2 DengXian Light Western;}{\fhimajor\f31528\fbidi \fnil\fcharset238\fprq2 DengXian Light CE;}{\fhimajor\f31529\fbidi \fnil\fcharset204\fprq2 DengXian Light Cyr;}
|
||||
{\fhimajor\f31531\fbidi \fnil\fcharset161\fprq2 DengXian Light Greek;}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
|
||||
{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
|
||||
{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
|
||||
{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
|
||||
{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
|
||||
{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31560\fbidi \fnil\fcharset0\fprq2 DengXian Western{\*\falt DengXian};}
|
||||
{\fdbminor\f31558\fbidi \fnil\fcharset238\fprq2 DengXian CE{\*\falt DengXian};}{\fdbminor\f31559\fbidi \fnil\fcharset204\fprq2 DengXian Cyr{\*\falt DengXian};}{\fdbminor\f31561\fbidi \fnil\fcharset161\fprq2 DengXian Greek{\*\falt DengXian};}
|
||||
{\fhiminor\f31570\fbidi \fnil\fcharset0\fprq2 DengXian Western{\*\falt DengXian};}{\fhiminor\f31568\fbidi \fnil\fcharset238\fprq2 DengXian CE{\*\falt DengXian};}{\fhiminor\f31569\fbidi \fnil\fcharset204\fprq2 DengXian Cyr{\*\falt DengXian};}
|
||||
{\fhiminor\f31571\fbidi \fnil\fcharset161\fprq2 DengXian Greek{\*\falt DengXian};}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
|
||||
{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
|
||||
{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
|
||||
{\f45\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f46\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f48\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f49\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
|
||||
{\f50\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f51\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f52\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
|
||||
{\f53\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;
|
||||
\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;}
|
||||
{\*\defchp \fs21\kerning2\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{
|
||||
\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs21\lang1033\langfe2052\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp2052
|
||||
\snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
|
||||
\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv
|
||||
\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs21\lang1033\langfe2052\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp2052
|
||||
\snext11 \ssemihidden \sunhideused Normal Table;}{\s15\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af2\afs22\alang1025 \ltrch\fcs0
|
||||
\fs21\lang1033\langfe2052\kerning2\loch\f31505\hich\af2\dbch\af31505\cgrid\langnp1033\langfenp2052 \sbasedon0 \snext15 \slink16 \sunhideused Plain Text;}{\*\cs16 \additive \rtlch\fcs1 \af2 \ltrch\fcs0 \loch\f31505\hich\af2 \sbasedon10 \slink15 \slocked
|
||||
\'b4\'bf\'ce\'c4\'b1\'be \'d7\'d6\'b7\'fb;}}{\*\rsidtbl \rsid927107\rsid3950508\rsid11081282\rsid12910709\rsid13643782\rsid14384001}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0
|
||||
\mnaryLim1}{\info{\author lysyw}{\operator lysyw}{\creatim\yr2019\mo8\dy17\hr10\min22}{\revtim\yr2019\mo8\dy17\hr10\min22}{\version2}{\edmins0}{\nofpages1}{\nofwords8}{\nofchars49}{\nofcharsws56}{\vern111}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.co
|
||||
m/office/word/2003/wordml}}\paperw11906\paperh16838\margl2253\margr2253\margt1440\margb1440\gutter0\ltrsect
|
||||
\deftab420\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\formshade\horzdoc\dgmargin\dghspace180\dgvspace156
|
||||
\dghorigin2253\dgvorigin1440\dghshow0\dgvshow2\jcompress\lnongrid
|
||||
\viewkind1\viewscale100\splytwnine\ftnlytwnine\htmautsp\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct\asianbrkrule\rsidroot3950508\newtblstyruls
|
||||
\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat {\upr{\*\fchars
|
||||
!%),.:\'3b>?]\'7d\'a1\'e9\'a1\'a7\'a1\'e3\'a1\'a4\'a1\'a6\'a1\'a5\'a8\'44\'a1\'ac\'a1\'af\'a1\'b1\'a1\'ad\'a1\'eb\'a1\'e4\'a1\'e5?\'a1\'e6\'a1\'c3\'a1\'a2\'a1\'a3\'a1\'a8\'a1\'b5\'a1\'b7\'a1\'b9\'a1\'bb\'a1\'bf\'a1\'b3\'a1\'bd\'a8\'95\'a6\'e1\'a6\'e3\'a6\'e7\'a6\'e5\'a6\'eb\'a9\'77\'a9\'79\'a9\'7b\'a3\'a1\'a3\'a2\'a3\'a5\'a3\'a7\'a3\'a9\'a3\'ac\'a3\'ae\'a3\'ba\'a3\'bb\'a3\'bf\'a3\'dd\'a3\'e0\'a3\'fc\'a3\'fd\'a1\'ab\'a1\'e9
|
||||
}{\*\ud\uc0{\*\fchars
|
||||
!%),.:\'3b>?]\'7d{\uc2\u162 \'a1\'e9\'a1\'a7\'a1\'e3\'a1\'a4\'a1\'a6\'a1\'a5\'a8D\'a1\'ac\'a1\'af\'a1\'b1\'a1\'ad\'a1\'eb\'a1\'e4\'a1\'e5}{\uc1\u8250 ?\'a1\'e6\'a1\'c3\'a1\'a2\'a1\'a3\'a1\'a8\'a1\'b5\'a1\'b7\'a1\'b9\'a1\'bb\'a1\'bf\'a1\'b3\'a1\'bd\'a8\'95\'a6\'e1\'a6\'e3\'a6\'e7\'a6\'e5\'a6\'eb\'a9w\'a9y\'a9\'7b\'a3\'a1\'a3\'a2\'a3\'a5\'a3\'a7\'a3\'a9\'a3\'ac\'a3\'ae\'a3\'ba\'a3\'bb\'a3\'bf\'a3\'dd\'a3\'e0\'a3\'fc\'a3\'fd\'a1\'ab\'a1\'e9}
|
||||
}}}{\upr{\*\lchars $([\'7b\'a1\'ea\'a3\'a4\'a1\'a4\'a1\'ae\'a1\'b0\'a1\'b4\'a1\'b6\'a1\'b8\'a1\'ba\'a1\'be\'a1\'b2\'a1\'bc\'a8\'94\'a9\'76\'a9\'78\'a9\'7a\'a1\'e7\'a3\'a8\'a3\'ae\'a3\'db\'a3\'fb\'a1\'ea\'a3\'a4}{\*\ud\uc0{\*\lchars
|
||||
$([\'7b{\uc2\u163 \'a1\'ea\u165 \'a3\'a4\'a1\'a4\'a1\'ae\'a1\'b0\'a1\'b4\'a1\'b6\'a1\'b8\'a1\'ba\'a1\'be\'a1\'b2\'a1\'bc\'a8\'94\'a9v\'a9x\'a9z\'a1\'e7\'a3\'a8\'a3\'ae\'a3\'db\'a3\'fb\'a1\'ea\'a3\'a4}}}}\fet0{\*\wgrffmtfilter 2450}\nofeaturethrottle1
|
||||
\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery851\footery992\colsx425\endnhere\sectlinegrid312\sectspecifyl\sectrsid2109456\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang
|
||||
{\pntxta \dbch .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta \dbch )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl6
|
||||
\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl9
|
||||
\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}\pard\plain \ltrpar\s15\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12910709 \rtlch\fcs1 \af2\afs22\alang1025 \ltrch\fcs0
|
||||
\fs21\lang1033\langfe2052\kerning2\loch\af31505\hich\af2\dbch\af31505\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'b3\'cc\'d0\'f2
|
||||
\'bd\'f6\'d3\'c3\'d3\'da\'d1\'a7\'cf\'b0\'bd\'bb\'c1\'f7\'ca\'b9\'d3\'c3\'a3\'a1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709
|
||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709 \loch\af13\hich\af13\dbch\f13 \'c7\'eb\'ce\'f0\'d3\'c3\'d3\'da\'c9\'cc\'d2\'b5\'d3\'c3\'cd\'be\'a3\'a1\'a3\'a1}{\rtlch\fcs1 \af13 \ltrch\fcs0
|
||||
\loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709
|
||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709 \loch\af13\hich\af13\dbch\f13 \'ca\'b9\'d3\'c3\'b1\'be\'c8\'ed\'bc\'fe\'d4\'ec\'b3\'c9\'b5\'c4\'d2\'bb\'c7\'d0\'ba\'f3\'b9\'fb\'d3\'c9\'ca\'b9\'d3\'c3\'d5\'df
|
||||
\'b3\'d0\'b5\'a3\'a3\'a1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709
|
||||
\par
|
||||
\par \hich\af13\dbch\af13\loch\f13 By: }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709 \loch\af13\hich\af13\dbch\f13 \'c2\'e4\'d1\'a9\'ce\'de\'ba\'db}{\rtlch\fcs1 \af13 \ltrch\fcs0
|
||||
\loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709
|
||||
\par
|
||||
\par
|
||||
\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
|
||||
9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
|
||||
5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6
|
||||
b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0
|
||||
0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6
|
||||
a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f
|
||||
c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512
|
||||
0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462
|
||||
a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865
|
||||
6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b
|
||||
4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b
|
||||
4757e8d3f729e245eb2b260a0238fd010000ffff0300504b0304140006000800000021006ab3c999d7060000941a0000160000007468656d652f7468656d652f
|
||||
7468656d65312e786d6cec595b6b1b47147e2ff43f2cfbaee8b6ab8b891c748ddbd8498894943c8ea59176e2d91db133b2234220244fa55028a4250f0d94bef4
|
||||
a194061a68681ffa5feae290b63fa2676657ab1969543bc68550621bb33bfb9d33df9c73f63bb3bb97afdc0fa97388634e58d4708b970aae83a3211b9168d270
|
||||
6f0f7ab99aeb7081a211a22cc20d778eb97b65fbc30f2ea32d11e0103b601ff12dd4700321a65bf93c1fc230e297d81447706dcce21009388d27f9518c8ec06f
|
||||
48f3a542a1920f11895c274221b8bd311e932176fe78f5eb9b6f9ffdfee833f873b7177374294c14092e078634eecb19b061a8b0a383a244f0396fd3d83944b4
|
||||
e1c274237634c0f785eb50c4055c68b805f5e3e6b72fe7d1566a44c5065bcdaea77e52bbd46074505273c693fd6c52cff3bd4a33f3af0054ace3bad56ea55bc9
|
||||
fc29001a0e61a50917d367b5d4f652ac064a0e2dbe3bd54eb968e035ffe535ce4d5ffe1a78054afc7b6bf85eaf0d5134f00a94e0fd35bcdfaab73aa67f054af0
|
||||
95357cb5d0ec7855c3bf02059444076be8825f29b717abcd20634677acf0baeff5aaa5d4f91205d59055979c62cc22b1a9d642748fc53d0048204582448e984f
|
||||
f1180da1985ffff0e9eb5f7e7376c92480ba9ba28871182d940abd4219fecb5f4f1da984a22d8c3463490b88f0b52149c7e1c3984c45c3fd18bcba1ae4e4d5ab
|
||||
e3c72f8f1fff7cfce4c9f1e31fd3b9952bc36e074513ddeeafefbef8fbf923e7cf9fbef9ebe997c9d4ab78aee38da559ddc38a979138f9eac5eb972f4e9e7dfe
|
||||
e6fba716efcd18edebf001093177aee323e7160b61819609f07efc7616830011dda2194d388a909cc5e2bf2b02037d7d8e28b2e05ad88ce39d1894c606bc3abb
|
||||
6710ee07f14c108bc76b416800f718a32d165ba3704dcea58579308b26f6c9e3998ebb85d0a16dee368a8c2c77675390586273d90eb041f326459140131c61e1
|
||||
c86bec0063cbeaee1262c4758f0c63c6d958387789d342c41a9201d937aa6969b44342c8cbdc4610f26dc466ef8ed362d4b6ea0e3e3491706f206a213fc0d408
|
||||
e355341328b4b91ca090ea01df4522b091eccfe3a18eeb7201999e60ca9cee08736eb3b911c37ab5a45f43206ed6b4efd179682263410e6c3e7711633ab2c30e
|
||||
da010aa7366c9f44818efd881f408922e7261336f81e33ef10790e7940d1c674df21d848f7e96a701b1456a7b42c107965165b72791533a37efb733a4658490d
|
||||
e8bf21eb21894ed3f81575f7ff3b75070d3df9fab9654117a3e876c7463ade52cb9b31b1de4c3b2b0abe09b7aadb6d168fc8bb2fdb1d348b6e62b853d67bd77b
|
||||
d57eafdaeeff5eb537ddcf17afd54b7906e596bbd664b3aeb6eee1c69dfb9850da17738a77b9dabc73684aa31e0c4a3bf5f08ab327b9690087f24e86090cdc24
|
||||
46cac68999f88488a01fa0296cf18bae7432e1a9eb0977a68cc3ce5f0d5b7d4b3c9d857b6c943cb0168bf2e134110f8ec472bce067e3f0b0211274a5ba7c08cb
|
||||
dc2bb613f5b0bc20206ddf8684369949a26c21515d0cca20a94773089a85845ad985b0a85b58d4a4fb45aad65800b52c2bb06b7260afd5707d0f4cc0081eaa10
|
||||
c52399a724d58becaa645e64a63705d3a800d8432c2a6099e9bae4ba7179727549a99d21d30609addc4c122a32aa87f1008d705a9d72f42c34de36d7f5654a0d
|
||||
7a32146a3e28ad258d6aeddf589c37d760b7aa0d34d2958246ce51c3ad947d2899219a36dc313cf8c3613885dae172b78be8045ea20d459cdcf0e7519669cc45
|
||||
07f12009b8129d440d422270ec5012365cb9fc2c0d34521aa2b8154b2008ef2cb93ac8cabb460e926e26198fc77828f4b46b2332d2c929287ca215d6abcafcfc
|
||||
6069c96690ee7e303a72f6e92cbe85a0c4fc6a5106704438bc002a26d11c1178a19909d9b2fe561a532abbfa1b455543c938a2d300a51d4517f304aea43ca3a3
|
||||
ceb2186867e99a21a05a48d246b83f910d560faad14db3ae9170d8d8754f379291d34473d9330d55915dd3ae62c60c8b36b012cbf335798dd522c4a0697a874f
|
||||
a47b5572eb0bad5bd927645d02029ec5cfd275cfd010346acbc90c6a92f1ba0c4bcd4e47cddeb158e029d4ced22434d5af2cdcaec42deb11d6e960f05c9d1fec
|
||||
56ab1686c68b7da58ab4fa00a27f9c60fbf7403c3af01a78460557a9844f0f31820d515fed4912d9805be4be486f0d3872663169b80f0a7ed36b97fc76ae50f3
|
||||
bb39afec157235bf59ce357dbf5cecfac542a7557a088d450461d14f3ebef4e02d149da79f60d4f8da679870f1a2edd2908579a63eb3e41571f519a658b27d86
|
||||
19c80f2cae4340741e544abd7ab9deaae4eae5662fe7755ab55cbd5d69e53a9576b5d3ebb4fd5abdf7d0750e15d86b96db5ea55bcb558aed76ceab1424fd5a3d
|
||||
57f54aa5a6576dd6ba5ef361ba8d819527f291c602c2ab786dff030000ffff0300504b0304140006000800000021000dd1909fb60000001b0100002700000074
|
||||
68656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384
|
||||
e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d26
|
||||
2452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe51
|
||||
4173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c0200001300000000000000
|
||||
000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b000000000000
|
||||
00000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000
|
||||
190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d00140006000800000021006ab3c999d7060000941a000016000000
|
||||
00000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b010000
|
||||
2700000000000000000000000000e10900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000dc0a00000000}
|
||||
{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d
|
||||
617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169
|
||||
6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363
|
||||
656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e}
|
||||
{\*\latentstyles\lsdstimax377\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;
|
||||
\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;
|
||||
\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;
|
||||
\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;
|
||||
\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;
|
||||
\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;
|
||||
\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;
|
||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;
|
||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;
|
||||
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;
|
||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;
|
||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;
|
||||
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;
|
||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;
|
||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;
|
||||
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;
|
||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;
|
||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5;
|
||||
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5;
|
||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;
|
||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6;
|
||||
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;
|
||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;
|
||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis;
|
||||
\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4;
|
||||
\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4;
|
||||
\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1;
|
||||
\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1;
|
||||
\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2;
|
||||
\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2;
|
||||
\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3;
|
||||
\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4;
|
||||
\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4;
|
||||
\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5;
|
||||
\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5;
|
||||
\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6;
|
||||
\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6;
|
||||
\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark;
|
||||
\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1;
|
||||
\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1;
|
||||
\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2;
|
||||
\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3;
|
||||
\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3;
|
||||
\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4;
|
||||
\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4;
|
||||
\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5;
|
||||
\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5;
|
||||
\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6;
|
||||
\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Link;
|
||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Link Error;}}{\*\datastore 0105000002000000180000004d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000
|
||||
d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000908a
|
||||
0599a254d501feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
|
||||
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000
|
||||
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000105000000000000}}
|
||||
5
licenses/license_en.txt
Normal file
5
licenses/license_en.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
This program is only for learning to communicate!
|
||||
Do not use for commercial purposes! !
|
||||
All consequences of using this software are borne by the user!
|
||||
|
||||
By: lyswhut
|
||||
5
licenses/license_zh.txt
Normal file
5
licenses/license_zh.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
本程序仅用于学习交流使用!
|
||||
请勿用于商业用途!!
|
||||
使用本软件造成的一切后果由使用者承担!
|
||||
|
||||
By: 落雪无痕
|
||||
24
package-lock.json
generated
24
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lx-music-desktop",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -3404,6 +3404,16 @@
|
||||
"upper-case-first": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"changelog-parser": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npm.taobao.org/changelog-parser/download/changelog-parser-2.8.0.tgz",
|
||||
"integrity": "sha1-wUKT4+j6t5eRPHIt6WVIAZhlAQg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"line-reader": "^0.2.4",
|
||||
"remove-markdown": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"character-parser": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npm.taobao.org/character-parser/download/character-parser-2.2.0.tgz",
|
||||
@@ -8487,6 +8497,12 @@
|
||||
"type-check": "~0.3.2"
|
||||
}
|
||||
},
|
||||
"line-reader": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npm.taobao.org/line-reader/download/line-reader-0.2.4.tgz",
|
||||
"integrity": "sha1-xDkrWH3qOFgMlnhXDm6OSfzlJiI=",
|
||||
"dev": true
|
||||
},
|
||||
"linkify-it": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npm.taobao.org/linkify-it/download/linkify-it-2.2.0.tgz",
|
||||
@@ -11288,6 +11304,12 @@
|
||||
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
|
||||
"dev": true
|
||||
},
|
||||
"remove-markdown": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npm.taobao.org/remove-markdown/download/remove-markdown-0.2.2.tgz",
|
||||
"integrity": "sha1-ZrDO66n7d8qWNrsbAwfOIaMqEqY=",
|
||||
"dev": true
|
||||
},
|
||||
"remove-trailing-separator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npm.taobao.org/remove-trailing-separator/download/remove-trailing-separator-1.1.0.tgz",
|
||||
|
||||
89
package.json
89
package.json
@@ -1,12 +1,41 @@
|
||||
{
|
||||
"name": "lx-music-desktop",
|
||||
"version": "0.1.0",
|
||||
"version": "0.3.2",
|
||||
"description": "一个免费的音乐下载助手",
|
||||
"main": "./dist/electron/main.js",
|
||||
"productName": "lx-music-desktop",
|
||||
"scripts": {
|
||||
"pack": "node build-config/pack.js && npm run pack:win",
|
||||
"pack:win": "npm run pack:win:setup && npm run pack:win:7z",
|
||||
"pack:win:setup": "cross-env TARGET=Setup ARCH=x64_x86 electron-builder -w=nsis --x64 --ia32",
|
||||
"pack:win:portable": "npm run pack:win:portable:x64_x86 && npm run pack:win:portable:x64 && npm run pack:win:portable:x86",
|
||||
"pack:win:portable:x64_x86": "cross-env TARGET=便携版 ARCH=x64_x86 electron-builder -w=portable --x64 --ia32",
|
||||
"pack:win:portable:x64": "cross-env TARGET=便携版 ARCH=x64 electron-builder -w=portable --x64",
|
||||
"pack:win:portable:x86": "cross-env TARGET=便携版 ARCH=x86 electron-builder -w=portable --ia32",
|
||||
"pack:win:7z": "npm run pack:win:7z:x64 && npm run pack:win:7z:x86",
|
||||
"pack:win:7z:x64": "cross-env TARGET=绿色版 ARCH=x64 electron-builder -w=7z --x64",
|
||||
"pack:win:7z:x86": "cross-env TARGET=绿色版 ARCH=x86 electron-builder -w=7z --ia32",
|
||||
"publish": "node publish",
|
||||
"pub:gh": "node build-config/pack.js && electron-builder --win -p always",
|
||||
"pack": "node build-config/pack.js && electron-builder",
|
||||
"publish:gh": "node build-config/pack.js && npm run publish:win",
|
||||
"publish:win": "npm run publish:win:setup && npm run publish:win:7z",
|
||||
"publish:win:setup": "cross-env TARGET=Setup ARCH=x64_x86 electron-builder -w=nsis --x64 --ia32 -p always",
|
||||
"publish:win:portable": "npm run publish:win:portable:x64_x86 && npm run publish:win:portable:x64 && npm run publish:win:portable:x86",
|
||||
"publish:win:portable:x64_x86": "cross-env TARGET=portable ARCH=x64_x86 electron-builder -w=portable --x64 --ia32 -p onTagOrDraft",
|
||||
"publish:win:portable:x64": "cross-env TARGET=portable ARCH=x64 electron-builder -w=portable --x64 -p onTagOrDraft",
|
||||
"publish:win:portable:x86": "cross-env TARGET=portable ARCH=x86 electron-builder -w=portable --ia32 -p onTagOrDraft",
|
||||
"publish:win:7z": "npm run publish:win:7z:x64 && npm run publish:win:7z:x86",
|
||||
"publish:win:7z:x64": "cross-env TARGET=green ARCH=win_x64 electron-builder -w=7z --x64 -p onTagOrDraft",
|
||||
"publish:win:7z:x86": "cross-env TARGET=green ARCH=win_x86 electron-builder -w=7z --ia32 -p onTagOrDraft",
|
||||
"publish:gh:mac": "node build-config/pack.js && npm run publish:mac",
|
||||
"publish:mac": "npm run publish:mac:dmg",
|
||||
"publish:mac:dmg": "electron-builder -m=dmg -p onTagOrDraft",
|
||||
"publish:gh:linux": "node build-config/pack.js && npm run publish:linux",
|
||||
"publish:linux": "npm run publish:linux:appImage && npm run publish:linux:deb",
|
||||
"publish:linux:appImage": "cross-env ARCH=x64 electron-builder -l=AppImage -p onTagOrDraft",
|
||||
"publish:linux:deb": "npm run publish:linux:deb:x64 && npm run publish:linux:deb:x86",
|
||||
"publish:linux:deb:x64": "cross-env ARCH=x64 electron-builder -l=deb --x64 -p onTagOrDraft",
|
||||
"publish:linux:deb:x86": "cross-env ARCH=x86 electron-builder -l=deb --ia32 -p onTagOrDraft",
|
||||
"pack:linux": "node build-config/pack.js && electron-builder -l",
|
||||
"pack:dir": "node build-config/pack.js && electron-builder --dir",
|
||||
"dev": "node build-config/runner-dev.js",
|
||||
"clean:electron": "rimraf dist/electron",
|
||||
@@ -33,16 +62,50 @@
|
||||
"files": [
|
||||
"dist/electron/**/*"
|
||||
],
|
||||
"extraResources": [
|
||||
"./licenses"
|
||||
],
|
||||
"win": {
|
||||
"icon": "src/static/icons/lunch.ico",
|
||||
"legalTrademarks": "lyswhut"
|
||||
"icon": "./resources/icons/256x256.ico",
|
||||
"legalTrademarks": "lyswhut",
|
||||
"artifactName": "${productName} v${version} ${env.ARCH} ${env.TARGET}.${ext}"
|
||||
},
|
||||
"mac": {
|
||||
"icon": "./resources/icons/512x512.png",
|
||||
"category": "public.app-category.music"
|
||||
},
|
||||
"linux": {
|
||||
"maintainer": "lyswhut <lyswuhut@qq.com>",
|
||||
"artifactName": "${productName} v${version} ${env.ARCH}.${ext}"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"language": "2052",
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"differentialPackage": true,
|
||||
"license": "./license.txt"
|
||||
"license": "./licenses/license.rtf",
|
||||
"shortcutName": "lx-music"
|
||||
},
|
||||
"dmg": {
|
||||
"contents": [
|
||||
{
|
||||
"x": 110,
|
||||
"y": 150,
|
||||
"name": "lx-music"
|
||||
},
|
||||
{
|
||||
"x": 240,
|
||||
"y": 150,
|
||||
"type": "link",
|
||||
"path": "/Applications",
|
||||
"name": "lx-music"
|
||||
}
|
||||
],
|
||||
"title": "洛雪音乐助手 v${version}"
|
||||
},
|
||||
"appImage": {
|
||||
"license": "./licenses/license_zh.txt",
|
||||
"category": "Audio"
|
||||
},
|
||||
"publish": [
|
||||
{
|
||||
@@ -56,9 +119,16 @@
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lyswhut/lx-music-desktop.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "lyswhut",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"music-player",
|
||||
"electron-app",
|
||||
"vuejs2"
|
||||
],
|
||||
"author": {
|
||||
"name": "lyswhut",
|
||||
"email": "lyswuhut@qq.com"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/lyswhut/lx-music-desktop/issues"
|
||||
},
|
||||
@@ -75,6 +145,7 @@
|
||||
"babel-preset-minify": "^0.5.0",
|
||||
"cfonts": "^2.4.4",
|
||||
"chalk": "^2.4.2",
|
||||
"changelog-parser": "^2.8.0",
|
||||
"copy-webpack-plugin": "^5.0.4",
|
||||
"core-js": "^3.2.1",
|
||||
"cos-nodejs-sdk-v5": "^2.5.11",
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
* 0.1.0版本发布
|
||||
### 新增
|
||||
|
||||
- 新增酷狗排行榜其他音质下载
|
||||
|
||||
@@ -2,21 +2,22 @@ const fs = require('fs')
|
||||
const path = require('path')
|
||||
const chalk = require('chalk')
|
||||
const clearAssets = require('./utils/clearAssets')
|
||||
const packAssets = require('./utils/packAssets')
|
||||
const compileAssets = require('./utils/compileAssets')
|
||||
// const packAssets = require('./utils/packAssets')
|
||||
// const compileAssets = require('./utils/compileAssets')
|
||||
const updateVersionFile = require('./utils/updateChangeLog')
|
||||
const copyFile = require('./utils/copyFile')
|
||||
const githubRelease = require('./utils/githubRelease')
|
||||
const { parseArgv } = require('./utils')
|
||||
// const copyFile = require('./utils/copyFile')
|
||||
// const githubRelease = require('./utils/githubRelease')
|
||||
// const { parseArgv } = require('./utils')
|
||||
|
||||
const run = async() => {
|
||||
const params = parseArgv(process.argv.slice(2))
|
||||
const bak = await updateVersionFile(params.ver)
|
||||
// const params = parseArgv(process.argv.slice(2))
|
||||
// const bak = await updateVersionFile(params.ver)
|
||||
const bak = await updateVersionFile(process.argv.slice(2)[0])
|
||||
|
||||
try {
|
||||
console.log(chalk.blue('Clearing assets...'))
|
||||
await clearAssets()
|
||||
console.log(chalk.green('Assets clear complated...'))
|
||||
console.log(chalk.green('Assets clear completed...'))
|
||||
|
||||
// console.log(chalk.blue('Compileing assets...'))
|
||||
// await compileAssets()
|
||||
|
||||
@@ -1,5 +1,58 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"desc": "0.1.0版本发布",
|
||||
"history": []
|
||||
"version": "0.3.2",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增酷狗排行榜其他音质下载</li>\n</ul>\n",
|
||||
"history": [
|
||||
{
|
||||
"version": "0.3.1",
|
||||
"desc": "<h3>修复</h3>\n<ul>\n<li>修复音量条主题适配</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.3.0",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增<strong>MAC</strong>及<strong>Linux</strong>版本(需要的可自行下载)</li>\n<li>新增音量调整</li>\n<li>新增任务栏播放进度条控制选项(现在可在设置界面关闭在任务栏显示的播放进度)</li>\n<li>新增更新出错时的弹窗提示</li>\n<li>从该版本起,非安装版也会有更新弹窗提醒了,但仍然需要手动下载新版本更新,版本信息可到设置页面查看</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>强制把临时接口设置回 <code>messoer</code> 接口</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.2.3",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增任务栏程序标题改变功能(播放歌曲时任务栏标题将显示当前播放的歌曲)</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>使用临时接口时,试听列表中的下载按钮仍然能点击的Bug</li>\n<li>修复某些情况下歌曲链接未能缓存的问题</li>\n</ul>\n<h3>移除</h3>\n<ul>\n<li>移除临时接口(因服务器被攻击,本接口已关闭)</li>\n<li>移除列表栏设置的隐藏专辑栏选项(感觉这个设置并没有什么luan用,并且还会打破布局)</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.2.2",
|
||||
"desc": "<h3>修复</h3>\n<ul>\n<li>修复下载过程中出错重试5次都失败后不会自动开始下一个任务的Bug</li>\n<li>修复播放到一半URL过期时不会刷新URL直接播放下一首的问题</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.2.1",
|
||||
"desc": "<h3>优化</h3>\n<ul>\n<li>新增歌曲URL存储,当URL无效时才重新获取,以减少接口不稳定的影响</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复歌曲加载无法加载时自动切换混乱的Bug</li>\n<li>修复移除列表最后一首歌曲时播放器不停止播放的问题</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增<strong>百度音乐</strong>排行榜及其音乐直接试听与下载</li>\n<li>新增网易云排行榜音乐直接试听与下载(目前仅支持128k音质)</li>\n<li>新增酷狗排行榜音乐直接试听与下载(目前仅支持128k音质)</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复更新弹窗历史版本描述多余的换行问题</li>\n<li>修复歌曲无法播放的情况下歌词仍会播放的问题</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.1.6",
|
||||
"desc": "<h3>修复</h3>\n<ul>\n<li>修复列表多选音源限制Bug</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.1.5",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增搜索列表批量试听与下载功能</li>\n<li>新增排行榜列表批量试听与下载功能</li>\n<li>新增试听列表批量移除与下载功能</li>\n<li>新增下载列表批量开始、暂停与移除功能</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>优化歌曲切换机制</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.1.4",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增音乐来源切换,可到设置页面-基本设置 look look !</li>\n<li>为搜索结果列表添加多选功能。<br>\nP.S:暂时没想好多选后的操作按钮放哪…</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>重构与改进checkbox组件,使其支持不定选中状态</li>\n<li>完善上一个版本的http请求封装并切换部分请求到该方法上</li>\n<li>优化其他一些细节</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.1.3",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增win32应用构建</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复安装包许可协议乱码问题</li>\n<li><strong>messoer 提供的接口已挂</strong>,暂时切换到临时接口!</li>\n</ul>\n<h3>移除</h3>\n<ul>\n<li>由于messoer接口无法使用,QQ音乐排行榜直接播放/下载功能暂时关闭</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.1.2",
|
||||
"desc": "<h3>修复</h3>\n<ul>\n<li>修复更新弹窗的内容显示问题</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.1.1",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>QQ音乐排行榜直接试听与下载(该接口貌似不太稳定,且用且珍惜!)</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>优化http请求机制</li>\n<li>更新关于本软件说明</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复当上一个歌曲链接正在获取时切换歌曲请求不会取消的问题</li>\n<li>修复切换歌曲时仍然播放上一首歌曲的问题</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"desc": "0.1.0版本发布"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
BIN
resources/icons/512x512.icns
Normal file
BIN
resources/icons/512x512.icns
Normal file
Binary file not shown.
BIN
resources/icons/512x512.png
Normal file
BIN
resources/icons/512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
4
src/common/utils.js
Normal file
4
src/common/utils.js
Normal file
@@ -0,0 +1,4 @@
|
||||
export const isLinux = process.platform == 'linux'
|
||||
export const isWin = process.platform == 'win32'
|
||||
export const isMac = process.platform == 'darwin'
|
||||
|
||||
@@ -3,7 +3,7 @@ html(lang="cn")
|
||||
meta(charset="UTF-8")
|
||||
meta(name="viewport" content="width=device-width, initial-scale=1.0")
|
||||
meta(http-equiv="X-UA-Compatible" content="ie=edge")
|
||||
title= require('../package.json').name
|
||||
title 洛雪音乐助手
|
||||
|
||||
body
|
||||
#root
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const { app, BrowserWindow, Menu } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
require('./events')
|
||||
const progressBar = require('./events/progressBar')
|
||||
const trafficLight = require('./events/trafficLight')
|
||||
const autoUpdate = require('./utils/autoUpdate')
|
||||
const { isLinux, isMac } = require('../common/utils')
|
||||
|
||||
const isDev = process.env.NODE_ENV !== 'production'
|
||||
|
||||
@@ -33,8 +34,8 @@ function createWindow() {
|
||||
useContentSize: true,
|
||||
width: 920,
|
||||
frame: false,
|
||||
transparent: true,
|
||||
icon: path.join(global.__static, 'icons/lunch.ico'),
|
||||
transparent: !isLinux,
|
||||
// icon: path.join(global.__static, isWin ? 'icons/256x256.ico' : 'icons/512x512.png'),
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
fullscreenable: false,
|
||||
@@ -58,6 +59,24 @@ function createWindow() {
|
||||
if (!isDev) autoUpdate(mainWindow)
|
||||
}
|
||||
|
||||
if (isMac) {
|
||||
const template = [
|
||||
{
|
||||
label: app.getName(),
|
||||
submenu: [{ label: '关于洛雪音乐', role: 'about' }, { type: 'separator' }, { label: '隐藏', role: 'hide' }, { label: '显示其他', role: 'hideothers' }, { label: '显示全部', role: 'unhide' }, { type: 'separator' }, { label: '退出', click: () => app.quit() }],
|
||||
},
|
||||
{
|
||||
label: '窗口',
|
||||
role: 'window',
|
||||
submenu: [{ label: '最小化', role: 'minimize' }, { label: '关闭', role: 'close' }],
|
||||
},
|
||||
]
|
||||
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
|
||||
} else {
|
||||
Menu.setApplicationMenu(null)
|
||||
}
|
||||
|
||||
app.once('ready', createWindow)
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
|
||||
@@ -3,6 +3,7 @@ const { autoUpdater } = require('electron-updater')
|
||||
const { mainOn } = require('../../common/icp')
|
||||
|
||||
autoUpdater.logger = log
|
||||
// autoUpdater.autoDownload = false
|
||||
autoUpdater.logger.transports.file.level = 'info'
|
||||
log.info('App starting...')
|
||||
|
||||
@@ -60,21 +61,30 @@ module.exports = win => {
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
sendStatusToWindow('Checking for update...')
|
||||
})
|
||||
autoUpdater.on('update-available', (ev, info) => {
|
||||
autoUpdater.on('update-available', info => {
|
||||
sendStatusToWindow('Update available.')
|
||||
win.webContents.send('update-available', info)
|
||||
})
|
||||
autoUpdater.on('update-not-available', (ev, info) => {
|
||||
autoUpdater.on('update-not-available', info => {
|
||||
sendStatusToWindow('Update not available.')
|
||||
setTimeout(() => { // 延迟发送事件,过早发送可能渲染进程还启动完成
|
||||
win.webContents.send('update-not-available')
|
||||
}, 5000)
|
||||
})
|
||||
autoUpdater.on('error', (ev, err) => {
|
||||
autoUpdater.on('error', () => {
|
||||
sendStatusToWindow('Error in auto-updater.')
|
||||
setTimeout(() => { // 延迟发送事件,过早发送可能渲染进程还启动完成
|
||||
win.webContents.send('update-error')
|
||||
}, 6000)
|
||||
})
|
||||
autoUpdater.on('download-progress', (ev, progressObj) => {
|
||||
autoUpdater.on('download-progress', progressObj => {
|
||||
sendStatusToWindow('Download progress...')
|
||||
})
|
||||
autoUpdater.on('update-downloaded', (ev, info) => {
|
||||
autoUpdater.on('update-downloaded', info => {
|
||||
sendStatusToWindow('Update downloaded.')
|
||||
win.webContents.send('update-downloaded')
|
||||
setTimeout(() => { // 延迟发送事件,过早发送可能渲染进程还启动完成
|
||||
win.webContents.send('update-downloaded')
|
||||
}, 2000)
|
||||
})
|
||||
mainOn('quit-update', () => {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
#container(v-if="isProd" :class="theme" @mouseenter="enableIgnoreMouseEvents" @mouseleave="dieableIgnoreMouseEvents")
|
||||
#container(v-if="isProd && !isLinux" :class="theme" @mouseenter="enableIgnoreMouseEvents" @mouseleave="dieableIgnoreMouseEvents")
|
||||
core-aside#left
|
||||
#right
|
||||
core-toolbar#toolbar
|
||||
@@ -20,14 +20,23 @@
|
||||
<script>
|
||||
import { mapMutations, mapGetters, mapActions } from 'vuex'
|
||||
import { rendererOn } from '../common/icp'
|
||||
import { isLinux } from '../common/utils'
|
||||
window.ELECTRON_DISABLE_SECURITY_WARNINGS = process.env.ELECTRON_DISABLE_SECURITY_WARNINGS
|
||||
const win = require('electron').remote.getCurrentWindow()
|
||||
const body = document.body
|
||||
let win
|
||||
let body
|
||||
if (!isLinux) {
|
||||
win = require('electron').remote.getCurrentWindow()
|
||||
body = document.body
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isProd: process.env.NODE_ENV === 'production',
|
||||
isLinux,
|
||||
globalObj: {
|
||||
apiSource: 'messoer',
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -35,9 +44,11 @@ export default {
|
||||
...mapGetters('list', ['defaultList']),
|
||||
...mapGetters('download', {
|
||||
downloadList: 'list',
|
||||
downloadStatus: 'downloadStatus',
|
||||
}),
|
||||
},
|
||||
mounted() {
|
||||
document.body.classList.add(this.isLinux ? 'noTransparent' : 'transparent')
|
||||
this.init()
|
||||
},
|
||||
watch: {
|
||||
@@ -49,6 +60,7 @@ export default {
|
||||
},
|
||||
defaultList: {
|
||||
handler(n) {
|
||||
// console.log(n)
|
||||
this.electronStore.set('list.defaultList', n)
|
||||
},
|
||||
deep: true,
|
||||
@@ -59,33 +71,58 @@ export default {
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
'globalObj.apiSource'(n) {
|
||||
if (n != this.setting.apiSource) {
|
||||
this.setSetting(Object.assign({}, this.setting, {
|
||||
apiSource: n,
|
||||
}))
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['getVersionInfo']),
|
||||
...mapMutations(['setNewVersion', 'setVersionVisible']),
|
||||
...mapMutations(['setNewVersion', 'setVersionModalVisible']),
|
||||
...mapMutations('list', ['initDefaultList']),
|
||||
...mapMutations('download', ['updateDownloadList']),
|
||||
...mapMutations(['setSetting']),
|
||||
init() {
|
||||
if (this.isProd) {
|
||||
if (this.isProd && !isLinux) {
|
||||
body.addEventListener('mouseenter', this.dieableIgnoreMouseEvents)
|
||||
body.addEventListener('mouseleave', this.enableIgnoreMouseEvents)
|
||||
}
|
||||
rendererOn('update-available', (e, info) => {
|
||||
// this.showUpdateModal(true)
|
||||
this.setNewVersion({
|
||||
version: info.version,
|
||||
})
|
||||
})
|
||||
rendererOn('update-error', () => {
|
||||
this.setVersionModalVisible({ isError: true })
|
||||
this.$nextTick(() => {
|
||||
this.showUpdateModal()
|
||||
})
|
||||
})
|
||||
rendererOn('update-downloaded', () => {
|
||||
this.getVersionInfo().then(body => {
|
||||
this.setNewVersion(body)
|
||||
this.$nextTick(() => {
|
||||
this.setVersionVisible(true)
|
||||
})
|
||||
this.showUpdateModal()
|
||||
})
|
||||
rendererOn('update-not-available', () => {
|
||||
if (this.setting.ignoreVersion) this.setSetting(Object.assign({}, this.setting, { ignoreVersion: null }))
|
||||
this.setNewVersion({
|
||||
version: this.version.version,
|
||||
})
|
||||
})
|
||||
|
||||
this.initData()
|
||||
this.globalObj.apiSource = this.setting.apiSource
|
||||
window.globalObj = this.globalObj
|
||||
},
|
||||
enableIgnoreMouseEvents() {
|
||||
if (isLinux) return
|
||||
win.setIgnoreMouseEvents(false)
|
||||
// console.log('content enable')
|
||||
},
|
||||
dieableIgnoreMouseEvents() {
|
||||
if (isLinux) return
|
||||
// console.log('content disable')
|
||||
win.setIgnoreMouseEvents(true, { forward: true })
|
||||
},
|
||||
@@ -96,19 +133,40 @@ export default {
|
||||
},
|
||||
initPlayList() {
|
||||
let defaultList = this.electronStore.get('list.defaultList')
|
||||
// console.log(defaultList)
|
||||
if (defaultList) {
|
||||
defaultList.list.forEach(m => {
|
||||
m.typeUrl = {}
|
||||
})
|
||||
// defaultList.list.forEach(m => {
|
||||
// m.typeUrl = {}
|
||||
// })
|
||||
this.initDefaultList(defaultList)
|
||||
}
|
||||
},
|
||||
initDownloadList() {
|
||||
let downloadList = this.electronStore.get('download.list')
|
||||
if (downloadList) {
|
||||
downloadList.forEach(item => {
|
||||
if (item.status == this.downloadStatus.RUN || item.status == this.downloadStatus.WAITING) {
|
||||
item.status = this.downloadStatus.PAUSE
|
||||
item.statusText = '暂停下载'
|
||||
}
|
||||
})
|
||||
this.updateDownloadList(downloadList)
|
||||
}
|
||||
},
|
||||
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
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.setVersionModalVisible({ isShow: true })
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.isProd) {
|
||||
@@ -124,22 +182,30 @@ export default {
|
||||
@import './assets/styles/layout.less';
|
||||
|
||||
body {
|
||||
// background-color: #fff;
|
||||
padding: @shadow-app;
|
||||
user-select: none;
|
||||
height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.transparent {
|
||||
padding: @shadow-app;
|
||||
#container {
|
||||
box-shadow: 0 0 @shadow-app rgba(0, 0, 0, 0.5);
|
||||
border-radius: 4px;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
.noTransparent {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
box-shadow: 0 0 @shadow-app rgba(0, 0, 0, 0.5);
|
||||
// background-color: #fff;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#left {
|
||||
flex: none;
|
||||
width: @width-app-left;
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
.mixin-ellipsis-1;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.break {
|
||||
word-break: break-all;
|
||||
}
|
||||
@@ -92,6 +96,9 @@ table {
|
||||
small {
|
||||
font-size: .8em;
|
||||
}
|
||||
.small {
|
||||
font-size: .9em;
|
||||
}
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -8,6 +8,13 @@ div(:class="$style.player")
|
||||
div(:class="$style.column1")
|
||||
div(:class="$style.container")
|
||||
div(:class="$style.title") {{title}}
|
||||
div(:class="$style.volumeContent")
|
||||
div(:class="$style.volume" @click.stop='handleChangeVolume' :title="`当前音量:${volumeStr}%`")
|
||||
div(:class="$style.volumeBar" :style="{ width: volumeStr + '%' }")
|
||||
|
||||
//- div(:class="$style.playBtn" @click='handleNext' title="音量")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 291.063 291.064' space='preserve')
|
||||
use(xlink:href='#icon-sound')
|
||||
div(:class="$style.playBtn" @click='handleNext' title="下一首")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve')
|
||||
use(xlink:href='#icon-nextMusic')
|
||||
@@ -41,14 +48,16 @@ div(:class="$style.player")
|
||||
<script>
|
||||
import Lyric from 'lrc-file-parser'
|
||||
import { rendererSend } from '../../../common/icp'
|
||||
import { formatPlayTime2, getRandom, checkPath } from '../../utils'
|
||||
import { formatPlayTime2, getRandom, checkPath, setTitle } from '../../utils'
|
||||
import { mapGetters, mapActions, mapMutations } from 'vuex'
|
||||
import { requestMsg } from '../../utils/message'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: true,
|
||||
audio: null,
|
||||
volume: 0,
|
||||
nowPlayTime: 0,
|
||||
maxPlayTime: 0,
|
||||
isPlay: false,
|
||||
@@ -61,12 +70,16 @@ export default {
|
||||
name: '^',
|
||||
singer: '^',
|
||||
},
|
||||
targetSong: null,
|
||||
pregessWidth: 0,
|
||||
lyric: {
|
||||
lrc: null,
|
||||
text: '',
|
||||
line: 0,
|
||||
},
|
||||
delayNextTimeout: null,
|
||||
audioErrorTime: 0,
|
||||
retryNum: 0,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -90,6 +103,12 @@ export default {
|
||||
// return 50
|
||||
return this.nowPlayTime / this.maxPlayTime || 0
|
||||
},
|
||||
isAPITemp() {
|
||||
return this.setting.apiSource == 'temp'
|
||||
},
|
||||
volumeStr() {
|
||||
return parseInt(this.volume * 100)
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setProgessWidth()
|
||||
@@ -98,6 +117,8 @@ export default {
|
||||
watch: {
|
||||
changePlay(n) {
|
||||
if (!n) return
|
||||
// console.log('changePlay')
|
||||
this.handleRemoveMusic()
|
||||
this.resetChangePlay()
|
||||
if (this.playIndex < 0) return
|
||||
this.stopPlay()
|
||||
@@ -112,8 +133,13 @@ export default {
|
||||
? n.findIndex(s => s.musicInfo.songmid === this.musicInfo.songmid)
|
||||
: n.findIndex(s => s.songmid === this.musicInfo.songmid)
|
||||
if (index < 0) {
|
||||
this.handleRemoveMusic()
|
||||
if (n.length) this.handleNext()
|
||||
// console.log(this.playIndex)
|
||||
if (n.length) {
|
||||
this.fixPlayIndex(this.playIndex - 1)
|
||||
this.handleNext()
|
||||
} else {
|
||||
this.setPlayIndex(-1)
|
||||
}
|
||||
} else {
|
||||
this.fixPlayIndex(index)
|
||||
}
|
||||
@@ -124,6 +150,9 @@ export default {
|
||||
if (n.toFixed(2) === o.toFixed(2)) return
|
||||
this.sendProgressEvent(n, 'normal')
|
||||
},
|
||||
volume(n) {
|
||||
this.handleSaveVolume(n)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions('player', ['getUrl', 'getPic', 'getLrc']),
|
||||
@@ -132,8 +161,11 @@ export default {
|
||||
'fixPlayIndex',
|
||||
'resetChangePlay',
|
||||
]),
|
||||
...mapMutations(['setVolume']),
|
||||
...mapMutations('list', ['updateMusicInfo']),
|
||||
init() {
|
||||
this.audio = document.createElement('audio')
|
||||
window.a = this.audio = document.createElement('audio')
|
||||
this.volume = this.audio.volume = this.setting.player.volume
|
||||
this.audio.controls = false
|
||||
this.audio.autoplay = true
|
||||
this.audio.loop = this.setting.player.togglePlayMethod === 'singleLoop'
|
||||
@@ -156,10 +188,17 @@ export default {
|
||||
this.handleNext()
|
||||
})
|
||||
this.audio.addEventListener('error', () => {
|
||||
// console.log('code', this.audio.error.code)
|
||||
if (!this.musicInfo.songmid) return
|
||||
console.log('出错')
|
||||
if (this.audio.error.code !== 1 && this.retryNum < 3) { // 若音频URL无效则尝试刷新3次URL
|
||||
// console.log(this.retryNum)
|
||||
this.audioErrorTime = this.audio.currentTime // 记录出错的播放时间
|
||||
this.retryNum++
|
||||
this.setUrl(this.list[this.playIndex], true)
|
||||
return
|
||||
}
|
||||
this.stopPlay()
|
||||
this.status = '加载出错'
|
||||
this.sendProgressEvent(this.progress, 'error')
|
||||
|
||||
// let urls = this.player_info.targetSong.urls
|
||||
@@ -177,15 +216,21 @@ export default {
|
||||
// } else {
|
||||
// this.handleNext()
|
||||
// }
|
||||
this.handleNext()
|
||||
this.status = '音频加载出错,5 秒后切换下一首'
|
||||
this.addDelayNextTimeout()
|
||||
})
|
||||
this.audio.addEventListener('loadeddata', () => {
|
||||
this.maxPlayTime = this.audio.duration
|
||||
if (this.audioErrorTime) {
|
||||
this.audio.currentTime = this.audioErrorTime
|
||||
this.audioErrorTime = 0
|
||||
}
|
||||
if (!this.targetSong.interval && this.listId != 'download') this.updateMusicInfo({ index: this.playIndex, data: { interval: formatPlayTime2(this.maxPlayTime) } })
|
||||
this.status = '音乐加载中...'
|
||||
})
|
||||
this.audio.addEventListener('loadstart', () => {
|
||||
this.status = '音乐加载中...'
|
||||
})
|
||||
// this.audio.addEventListener('loadstart', () => {
|
||||
// this.status = '开始加载音乐信息...'
|
||||
// })
|
||||
this.audio.addEventListener('canplay', () => {
|
||||
console.log('加载完成开始播放')
|
||||
// if (this.musicInfo.lrc) this.lyric.lrc.play(this.audio.currentTime * 1000)
|
||||
@@ -223,7 +268,10 @@ export default {
|
||||
},
|
||||
play() {
|
||||
console.log('play', this.playIndex)
|
||||
let targetSong = this.list[this.playIndex]
|
||||
this.checkDelayNextTimeout()
|
||||
let targetSong = this.targetSong = this.list[this.playIndex]
|
||||
this.retryNum = 0
|
||||
this.audioErrorTime = 0
|
||||
|
||||
if (this.listId == 'download') {
|
||||
if (!checkPath(targetSong.filePath) || !targetSong.isComplate || /\.ape$/.test(targetSong.filePath)) {
|
||||
@@ -245,46 +293,71 @@ export default {
|
||||
this.setLrc(targetSong)
|
||||
}
|
||||
},
|
||||
checkDelayNextTimeout() {
|
||||
console.log(this.delayNextTimeout)
|
||||
if (this.delayNextTimeout) {
|
||||
clearTimeout(this.delayNextTimeout)
|
||||
this.delayNextTimeout = null
|
||||
}
|
||||
},
|
||||
addDelayNextTimeout() {
|
||||
this.checkDelayNextTimeout()
|
||||
this.delayNextTimeout = setTimeout(() => {
|
||||
this.delayNextTimeout = null
|
||||
this.handleNext()
|
||||
}, 5000)
|
||||
},
|
||||
handleNext() {
|
||||
// if (this.list.listName === null) return
|
||||
if (!this.list.length) return
|
||||
let list
|
||||
if (this.listId == 'download') {
|
||||
list = this.list.filter(s => !(!checkPath(s.filePath) || !s.isComplate || /\.ape$/.test(s.filePath)))
|
||||
} else if (this.isAPITemp) {
|
||||
list = this.list.filter(s => s.source == 'kw')
|
||||
} else {
|
||||
list = this.list
|
||||
}
|
||||
if (!list.length) return this.setPlayIndex(-1)
|
||||
let playIndex = this.list === list ? this.playIndex : list.indexOf(this.list[this.playIndex])
|
||||
// console.log(playIndex)
|
||||
let index
|
||||
switch (this.setting.player.togglePlayMethod) {
|
||||
case 'listLoop':
|
||||
index = this.hanldeListLoop()
|
||||
index = this.hanldeListLoop(list, playIndex)
|
||||
break
|
||||
case 'random':
|
||||
index = this.hanldeListRandom()
|
||||
index = this.hanldeListRandom(list, playIndex)
|
||||
break
|
||||
case 'list':
|
||||
index = this.hanldeListNext()
|
||||
index = this.hanldeListNext(list, playIndex)
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
if (index < 0) return
|
||||
if (this.list !== list) index = this.list.indexOf(list[index])
|
||||
this.setPlayIndex(index)
|
||||
},
|
||||
hanldeListLoop() {
|
||||
return this.playIndex === this.list.length - 1 ? 0 : this.playIndex + 1
|
||||
hanldeListLoop(list, index) {
|
||||
return index === list.length - 1 ? 0 : index + 1
|
||||
},
|
||||
hanldeListNext() {
|
||||
return this.playIndex === this.list.length - 1 ? -1 : this.playIndex + 1
|
||||
hanldeListNext(list, index) {
|
||||
return index === list.length - 1 ? -1 : index + 1
|
||||
},
|
||||
hanldeListRandom() {
|
||||
return getRandom(0, this.list.length)
|
||||
hanldeListRandom(list, index) {
|
||||
return getRandom(0, list.length)
|
||||
},
|
||||
startPlay() {
|
||||
this.isPlay = true
|
||||
if (this.musicInfo.lrc) this.lyric.lrc.play(this.audio.currentTime * 1000)
|
||||
this.setAppName()
|
||||
this.setAppTitle()
|
||||
this.sendProgressEvent(this.progress, 'normal')
|
||||
},
|
||||
stopPlay() {
|
||||
this.isPlay = false
|
||||
this.lyric.lrc.pause()
|
||||
this.sendProgressEvent(this.progress, 'paused')
|
||||
this.clearAppName()
|
||||
this.clearAppTitle()
|
||||
},
|
||||
setProgess(e) {
|
||||
this.audio.currentTime =
|
||||
@@ -305,22 +378,27 @@ export default {
|
||||
this.musicInfo.img = null
|
||||
},
|
||||
getPlayType(highQuality, songInfo) {
|
||||
switch (songInfo.source) {
|
||||
case 'wy':
|
||||
// case 'kg':
|
||||
return '128k'
|
||||
}
|
||||
let type = songInfo._types['192k'] ? '192k' : '128k'
|
||||
if (highQuality && songInfo._types['320k']) type = '320k'
|
||||
return type
|
||||
},
|
||||
setUrl(targetSong) {
|
||||
setUrl(targetSong, isRefresh) {
|
||||
let type = this.getPlayType(this.setting.player.highQuality, targetSong)
|
||||
this.musicInfo.url = targetSong.typeUrl[type]
|
||||
this.status = '歌曲链接获取中...'
|
||||
|
||||
let urlP = this.musicInfo.url
|
||||
? Promise.resolve()
|
||||
: this.getUrl({ musicInfo: targetSong, type }).then(() => {
|
||||
this.musicInfo.url = targetSong.typeUrl[type]
|
||||
})
|
||||
|
||||
urlP.then(() => {
|
||||
this.audio.src = this.musicInfo.url
|
||||
return this.getUrl({ musicInfo: targetSong, type, isRefresh }).then(() => {
|
||||
this.audio.src = this.musicInfo.url = targetSong.typeUrl[type]
|
||||
}).catch(err => {
|
||||
if (err.message == requestMsg.cancelRequest) return
|
||||
this.status = err.message
|
||||
this.addDelayNextTimeout()
|
||||
return Promise.reject(err)
|
||||
})
|
||||
},
|
||||
setImg(targetSong) {
|
||||
@@ -344,7 +422,7 @@ export default {
|
||||
lrcP
|
||||
.then(() => {
|
||||
this.lyric.lrc.setLyric(this.musicInfo.lrc)
|
||||
if (this.isPlay) this.lyric.lrc.play(this.audio.currentTime * 1000)
|
||||
if (this.isPlay && (this.musicInfo.url || this.listId == 'download')) this.lyric.lrc.play(this.audio.currentTime * 1000)
|
||||
})
|
||||
.catch(err => {
|
||||
this.status = err.message
|
||||
@@ -362,22 +440,30 @@ export default {
|
||||
this.musicInfo.url = null
|
||||
this.nowPlayTime = 0
|
||||
this.maxPlayTime = 0
|
||||
this.fixPlayIndex(this.playIndex - 1)
|
||||
},
|
||||
sendProgressEvent(status, mode) {
|
||||
// console.log(status)
|
||||
rendererSend('progress', {
|
||||
this.setting.player.isShowTaskProgess && rendererSend('progress', {
|
||||
status: status < 0.01 ? 0.01 : status,
|
||||
mode: mode || 'normal',
|
||||
})
|
||||
},
|
||||
setAppName() {
|
||||
// rendererSend('appName', {
|
||||
// name: `${this.musicInfo.name} - ${this.musicInfo.singer}`,
|
||||
// })
|
||||
setAppTitle() {
|
||||
setTitle(`${this.musicInfo.name} - ${this.musicInfo.singer}`)
|
||||
},
|
||||
clearAppName() {
|
||||
// rendererSend('appName')
|
||||
clearAppTitle() {
|
||||
setTitle()
|
||||
},
|
||||
handleChangeVolume(e) {
|
||||
let val = e.offsetX / 70
|
||||
if (val < 0) val = 0
|
||||
if (val > 1) val = 1
|
||||
if (val > 0.97) val = 1
|
||||
this.volume = val
|
||||
if (this.audio) this.audio.volume = this.volume
|
||||
},
|
||||
handleSaveVolume(volume) {
|
||||
this.setVolume(volume)
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -450,6 +536,39 @@ export default {
|
||||
.mixin-ellipsis-1;
|
||||
}
|
||||
|
||||
.volume-content {
|
||||
width: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.volume {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 0.25em;
|
||||
border-radius: 10px;
|
||||
// overflow: hidden;
|
||||
transition: @transition-theme;
|
||||
transition-property: background-color;
|
||||
background-color: @color-player-progress;
|
||||
// background-color: #f5f5f5;
|
||||
position: relative;
|
||||
border-radius: @radius-progress-border;
|
||||
}
|
||||
|
||||
.volume-bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
border-radius: @radius-progress-border;
|
||||
transition-duration: 0.2s;
|
||||
background-color: @color-theme;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.play-btn {
|
||||
+ .play-btn {
|
||||
margin-left: 10px;
|
||||
@@ -570,6 +689,14 @@ each(@themes, {
|
||||
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.3));
|
||||
}
|
||||
}
|
||||
.volume {
|
||||
background-color: ~'@{color-@{value}-player-progress}';
|
||||
}
|
||||
|
||||
.volume-bar {
|
||||
background-color: ~'@{color-@{value}-theme}';
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
|
||||
.progress {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
button(:class="[$style.btn, min ? $style.min : '']" @click="$emit('click', $event)")
|
||||
button(:class="[$style.btn, min ? $style.min : '']" :disabled="disabled" @click="$emit('click', $event)")
|
||||
slot
|
||||
</template>
|
||||
|
||||
@@ -9,6 +9,10 @@ export default {
|
||||
min: {
|
||||
type: Boolean,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -27,6 +31,9 @@ export default {
|
||||
outline: none;
|
||||
transition: background-color 0.2s ease;
|
||||
background-color: @color-btn-background;
|
||||
&[disabled] {
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: @color-theme_2-hover;
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
<template lang="pug">
|
||||
div(:class="$style.checkbox")
|
||||
input(:type="need ? 'radio' : 'checkbox'" :id="id" :value="target" :name="name" @change="change" v-model="val")
|
||||
input(:type="need ? 'radio' : 'checkbox'" :id="id" :disabled="disabled" :value="value" :name="name" @change="change" v-model="bool")
|
||||
label(:for="id" :class="$style.content")
|
||||
div
|
||||
div(v-if="indeterminate")
|
||||
svg(v-show="indeterminate" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' width="100%" viewBox='0 32 448 448' space='preserve')
|
||||
use(xlink:href='#icon-check-indeterminate')
|
||||
svg(v-show="!indeterminate" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' width="100%" viewBox='0 0 448 512' space='preserve')
|
||||
use(xlink:href='#icon-check-true')
|
||||
div(v-else)
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' width="100%" viewBox='0 32 448 448' space='preserve')
|
||||
use(xlink:href='#icon-check')
|
||||
span
|
||||
slot
|
||||
use(xlink:href='#icon-check-true')
|
||||
span(v-if="label != null" v-html="label")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
model: {
|
||||
prop: 'checked',
|
||||
event: 'input',
|
||||
},
|
||||
props: {
|
||||
target: {},
|
||||
checked: {},
|
||||
value: {},
|
||||
id: {
|
||||
type: String,
|
||||
@@ -25,41 +33,73 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
label: {},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
indeterminate: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
val: false,
|
||||
bool: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(n) {
|
||||
if (this.target && n !== this.target) {
|
||||
this.val = this.need
|
||||
? n === this.target
|
||||
? this.target
|
||||
: false
|
||||
: n === this.target
|
||||
} else if (n !== this.val) {
|
||||
this.val = n
|
||||
}
|
||||
checked(n) {
|
||||
this.setValue(n)
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.target) {
|
||||
this.val = this.need
|
||||
? this.value === this.target
|
||||
? this.target
|
||||
: false
|
||||
: this.value === this.target
|
||||
} else {
|
||||
this.val = this.value
|
||||
}
|
||||
this.setValue(this.checked)
|
||||
},
|
||||
methods: {
|
||||
change() {
|
||||
let val = this.target == null ? this.val : this.val ? this.target : null
|
||||
this.$emit('input', val)
|
||||
this.$emit('change', val)
|
||||
let checked
|
||||
if (Array.isArray(this.checked)) {
|
||||
checked = [...this.checked]
|
||||
const index = checked.indexOf(this.value)
|
||||
if (index < 0) {
|
||||
checked.push(this.value)
|
||||
} else {
|
||||
checked.splice(index, 1)
|
||||
}
|
||||
} else if (typeof this.checked == 'string') {
|
||||
checked = this.bool ? this.value : ''
|
||||
} else if (typeof this.checked == 'boolean') {
|
||||
let bool = this.bool
|
||||
if (this.indeterminate) {
|
||||
bool = true
|
||||
this.$nextTick(() => {
|
||||
this.bool = true
|
||||
})
|
||||
}
|
||||
checked = bool
|
||||
}
|
||||
this.$emit('input', checked)
|
||||
this.$emit('change', checked)
|
||||
},
|
||||
setValue(value) {
|
||||
let bool
|
||||
if (Array.isArray(value)) {
|
||||
bool = value.includes(this.value)
|
||||
} else {
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
bool = value === this.value
|
||||
break
|
||||
case 'boolean':
|
||||
bool = value
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.bool = this.need ? bool && this.value : bool
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -75,6 +115,11 @@ export default {
|
||||
|
||||
> input {
|
||||
display: none;
|
||||
&[disabled] {
|
||||
+ .content {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
&:checked {
|
||||
+ .content {
|
||||
> div {
|
||||
|
||||
@@ -5,7 +5,7 @@ material-modal(:show="show" :bg-close="bgClose" @close="handleClose")
|
||||
| {{ info.name }}
|
||||
br
|
||||
| {{ info.singer }}
|
||||
material-btn(:class="$style.btn" :key="type.type" @click="handleClick(type.type)" v-for="type in info.types") {{getTypeName(type.type)}} {{ type.type.toUpperCase() }}{{ type.size && ` - ${type.size.toUpperCase()}` }}
|
||||
material-btn(:class="$style.btn" :title="!checkSource(type.type) && '目前网易云音源仅支持下载128k音质'" :disabled="!checkSource(type.type)" :key="type.type" @click="handleClick(type.type)" v-for="type in info.types") {{getTypeName(type.type)}} {{ type.type.toUpperCase() }}{{ type.size && ` - ${type.size.toUpperCase()}` }}
|
||||
|
||||
</template>
|
||||
|
||||
@@ -49,6 +49,16 @@ export default {
|
||||
return '普通音质'
|
||||
}
|
||||
},
|
||||
checkSource(type) {
|
||||
switch (this.musicInfo.source) {
|
||||
case 'wy':
|
||||
// case 'kg':
|
||||
return type == '128k'
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
82
src/renderer/components/material/DownloadMultipleModal.vue
Normal file
82
src/renderer/components/material/DownloadMultipleModal.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template lang="pug">
|
||||
material-modal(:show="show" :bg-close="bgClose" @close="handleClose")
|
||||
main(:class="$style.main")
|
||||
h2
|
||||
| 已选择 {{list.length}} 首歌曲
|
||||
br
|
||||
| 请选择要优先下载的音质
|
||||
material-btn(:class="$style.btn" @click="handleClick('128k')") 普通音质 - 128K
|
||||
material-btn(:class="$style.btn" @click="handleClick('320k')") 高品音质 - 320K
|
||||
material-btn(:class="$style.btn" @click="handleClick('ape')") 无损音质 - APE
|
||||
material-btn(:class="$style.btn" @click="handleClick('flac')") 无损音质 - FLAC
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
bgClose: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleClick(type) {
|
||||
this.$emit('select', type)
|
||||
},
|
||||
handleClose() {
|
||||
this.$emit('close')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../../assets/styles/layout.less';
|
||||
|
||||
.main {
|
||||
padding: 15px;
|
||||
max-width: 300px;
|
||||
min-width: 200px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
h2 {
|
||||
font-size: 13px;
|
||||
color: @color-theme_2-font;
|
||||
line-height: 1.3;
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
.main {
|
||||
h2 {
|
||||
color: ~'@{color-@{value}-theme_2-font}';
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
||||
122
src/renderer/components/material/FlowBtn.vue
Normal file
122
src/renderer/components/material/FlowBtn.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template lang="pug">
|
||||
transition(enter-active-class="animated-fast zoomIn" leave-active-class="animated zoomOut")
|
||||
div(:class="$style.btns" v-show="show")
|
||||
button(type="button" v-if="playBtn" title="播放" @click.stop="handleClick('play')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 287.386 287.386' space='preserve')
|
||||
use(xlink:href='#icon-testPlay')
|
||||
button(type="button" v-if="addBtn" title="添加" @click.stop="handleClick('add')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 42 42' space='preserve')
|
||||
use(xlink:href='#icon-addTo')
|
||||
button(type="button" v-if="downloadBtn" title="下载" @click.stop="handleClick('download')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 475.078 475.077' space='preserve')
|
||||
use(xlink:href='#icon-download')
|
||||
button(type="button" v-if="startBtn" title="开始" @click.stop="handleClick('start')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve')
|
||||
use(xlink:href='#icon-play')
|
||||
button(type="button" v-if="pauseBtn" title="暂停" @click.stop="handleClick('pause')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 277.338 277.338' space='preserve')
|
||||
use(xlink:href='#icon-pause')
|
||||
button(type="button" v-if="removeBtn" title="移除" @click.stop="handleClick('remove')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 212.982 212.982' space='preserve')
|
||||
use(xlink:href='#icon-delete')
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
removeBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
startBtn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
pauseBtn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
downloadBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
addBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
playBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleClick(action) {
|
||||
this.$emit('btn-click', action)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../../assets/styles/layout.less';
|
||||
|
||||
.btns {
|
||||
position: fixed;
|
||||
bottom: 80px;
|
||||
right: 30px;
|
||||
background-color: @color-search-form-background;
|
||||
border-radius: 5px;
|
||||
// padding: 3px 5px;
|
||||
box-shadow: 0 1px 5px 0 rgba(0,0,0,.2);
|
||||
button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
margin-right: 5px;
|
||||
cursor: pointer;
|
||||
padding: 4px 7px;
|
||||
color: @color-btn;
|
||||
outline: none;
|
||||
transition: background-color 0.2s ease;
|
||||
line-height: 0;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 1.2em;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: @color-theme_2-hover;
|
||||
}
|
||||
&:active {
|
||||
background-color: @color-theme_2-active;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
.btns {
|
||||
background-color: ~'@{color-@{value}-search-form-background}';
|
||||
button {
|
||||
color: ~'@{color-@{value}-btn}';
|
||||
&:hover {
|
||||
background-color: ~'@{color-@{value}-theme_2-hover}';
|
||||
}
|
||||
&:active {
|
||||
background-color: ~'@{color-@{value}-theme_2-active}';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</style>
|
||||
80
src/renderer/components/material/InputRange.vue
Normal file
80
src/renderer/components/material/InputRange.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template lang="pug">
|
||||
input(type="range" :class="[$style.range, min ? $style.min : '']" :disabled="disabled" v-model="val" input="handleInput" @change="handleChange")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
min: {
|
||||
type: Boolean,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
val: 0,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.val = this.value
|
||||
},
|
||||
methods: {
|
||||
handleChange(e) {
|
||||
this.$emit('input', this.val)
|
||||
this.$emit('change', e)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../../assets/styles/layout.less';
|
||||
|
||||
.range {
|
||||
display: inline-block;
|
||||
border-radius: .25em;
|
||||
cursor: pointer;
|
||||
color: @color-btn;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
// background-color: @color-btn-background;
|
||||
&[disabled] {
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: @color-theme_2-hover;
|
||||
}
|
||||
&:active {
|
||||
background-color: @color-theme_2-active;
|
||||
}
|
||||
}
|
||||
|
||||
.min {
|
||||
|
||||
}
|
||||
|
||||
// each(@themes, {
|
||||
// :global(#container.@{value}) {
|
||||
// .btn {
|
||||
// color: ~'@{color-@{value}-btn}';
|
||||
// background-color: ~'@{color-@{value}-btn-background}';
|
||||
// &:hover {
|
||||
// background-color: ~'@{color-@{value}-theme_2-hover}';
|
||||
// }
|
||||
// &:active {
|
||||
// background-color: ~'@{color-@{value}-theme_2-active}';
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
</style>
|
||||
@@ -9,6 +9,12 @@ div(:class="$style.btns")
|
||||
button(type="button" title="添加" v-if="userInfo" @click.stop="handleClick('add')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 42 42' space='preserve')
|
||||
use(xlink:href='#icon-addTo')
|
||||
button(type="button" v-if="startBtn" title="开始" @click.stop="handleClick('start')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve')
|
||||
use(xlink:href='#icon-play')
|
||||
button(type="button" v-if="pauseBtn" title="暂停" @click.stop="handleClick('pause')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 277.338 277.338' space='preserve')
|
||||
use(xlink:href='#icon-pause')
|
||||
button(type="button" v-if="removeBtn" title="移除" @click.stop="handleClick('remove')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 212.982 212.982' space='preserve')
|
||||
use(xlink:href='#icon-delete')
|
||||
@@ -27,6 +33,14 @@ export default {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
startBtn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
pauseBtn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
removeBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
<template lang="pug">
|
||||
material-modal(:show="version.showModal" @close="handleClose")
|
||||
main(:class="$style.main" v-if="version.newVersion")
|
||||
h2 🚀程序更新🚀
|
||||
h2 {{ version.isError ? '🌟发现新版本🌟' : '🚀程序更新🚀'}}
|
||||
|
||||
div.scroll(:class="$style.info")
|
||||
div(:class="$style.current")
|
||||
h3 最新版本:{{version.newVersion.version}}
|
||||
h3 当前版本:{{version.version}}
|
||||
h3 版本变化:
|
||||
p(v-html="version.newVersion.desc")
|
||||
div(:class="$style.history" v-if="history.length")
|
||||
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.footer" v-if="version.isError")
|
||||
div(:class="$style.desc")
|
||||
p 发现有新版本啦,但是自动更新功能出问题了
|
||||
p
|
||||
| 如果你所用的软件是
|
||||
strong 安装版
|
||||
| ,可以到QQ群:830125506 反馈哦
|
||||
p
|
||||
| 你现在可以选择继续使用当前版本或
|
||||
strong 去发布页下载新版本
|
||||
div(:class="$style.btns")
|
||||
material-btn(:class="$style.btn" @click.onec="handleIgnoreClick") 忽略该版本
|
||||
material-btn(:class="$style.btn" @click.onec="handleOpenPageClick") 去下载新版本
|
||||
div(:class="$style.footer" v-else)
|
||||
div(:class="$style.desc")
|
||||
p 新版本已下载完毕,
|
||||
p
|
||||
@@ -24,18 +37,18 @@ material-modal(:show="version.showModal" @close="handleClose")
|
||||
| 或稍后
|
||||
strong 关闭程序时
|
||||
| 自动更新~
|
||||
material-btn(:class="$style.btn" @click.onec="handleClick") 立即重启更新
|
||||
material-btn(:class="$style.btn" @click.onec="handleRestartClick") 立即重启更新
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import { rendererSend } from '../../../common/icp'
|
||||
import { checkVersion } from '../../utils'
|
||||
import { checkVersion, openUrl } from '../../utils'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['version']),
|
||||
...mapGetters(['version', 'setting']),
|
||||
history() {
|
||||
if (!this.version.newVersion) return []
|
||||
let arr = []
|
||||
@@ -48,11 +61,21 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['setVersionVisible']),
|
||||
...mapMutations(['setVersionModalVisible', 'setSetting']),
|
||||
handleClose() {
|
||||
this.setVersionVisible(false)
|
||||
this.setVersionModalVisible({
|
||||
isShow: false,
|
||||
})
|
||||
},
|
||||
handleClick(event) {
|
||||
handleIgnoreClick(event) {
|
||||
this.handleClose()
|
||||
// event.target.disabled = true
|
||||
this.setSetting(Object.assign({}, this.setting, { ignoreVersion: this.version.newVersion.version }))
|
||||
},
|
||||
handleOpenPageClick() {
|
||||
openUrl('https://github.com/lyswhut/lx-music-desktop')
|
||||
},
|
||||
handleRestartClick(event) {
|
||||
this.handleClose()
|
||||
event.target.disabled = true
|
||||
rendererSend('quit-update')
|
||||
@@ -68,11 +91,12 @@ export default {
|
||||
.main {
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
max-width: 500px;
|
||||
max-width: 450px;
|
||||
min-width: 300px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
// overflow-y: auto;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
@@ -103,12 +127,30 @@ export default {
|
||||
padding-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
h3, h4 {
|
||||
font-weight: bold;
|
||||
}
|
||||
h3 {
|
||||
padding: 5px 0 3px;
|
||||
}
|
||||
ul {
|
||||
list-style: initial;
|
||||
padding-inline-start: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.history {
|
||||
h3 {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.item {
|
||||
h3 {
|
||||
padding: 5px 0 3px;
|
||||
}
|
||||
padding: 0 15px;
|
||||
+ .item {
|
||||
padding-top: 15px;
|
||||
}
|
||||
@@ -116,7 +158,7 @@ export default {
|
||||
font-weight: 700;
|
||||
}
|
||||
> p {
|
||||
padding-left: 10px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +179,11 @@ export default {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
.btns {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 0 10px;
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { author, name } from '../../../package.json'
|
||||
export default {
|
||||
getVersionInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
httpGet(`https://raw.githubusercontent.com/${author}/${name}/master/publish/version.json`, (err, resp, body) => {
|
||||
httpGet(`https://raw.githubusercontent.com/${author.name}/${name}/master/publish/version.json`, (err, resp, body) => {
|
||||
if (err) return reject(err)
|
||||
resolve(body)
|
||||
})
|
||||
|
||||
@@ -2,18 +2,30 @@ import download from '../../utils/download'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import music from '../../utils/music'
|
||||
import { getMusicType } from '../../utils/music/utils'
|
||||
|
||||
// state
|
||||
const state = {
|
||||
list: [],
|
||||
waitingList: [],
|
||||
downloadStatus: {
|
||||
RUN: 'run',
|
||||
WAITING: 'waiting',
|
||||
PAUSE: 'pause',
|
||||
ERROR: 'error',
|
||||
COMPLETED: 'completed',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
const dls = {}
|
||||
const tryNum = {}
|
||||
|
||||
// getters
|
||||
const getters = {
|
||||
list: state => state.list || [],
|
||||
dls: () => dls || {},
|
||||
downloadStatus: state => state.downloadStatus,
|
||||
}
|
||||
|
||||
const checkPath = path => {
|
||||
@@ -40,19 +52,38 @@ const getExt = type => {
|
||||
|
||||
const checkList = (list, musicInfo, type) => list.some(s => s.musicInfo.songmid === musicInfo.songmid && s.type === type)
|
||||
|
||||
const refreshUrl = downloadInfo => {
|
||||
return music[downloadInfo.musicInfo.source].getMusicUrl(downloadInfo.musicInfo, downloadInfo.type)
|
||||
const getStartTask = (list, downloadStatus, maxDownloadNum) => {
|
||||
let downloadCount = 0
|
||||
const waitList = list.filter(item => item.status == downloadStatus.WAITING ? true : (item.status === downloadStatus.RUN && ++downloadCount && false))
|
||||
// console.log(downloadCount, waitList)
|
||||
return downloadCount < maxDownloadNum && waitList.length > 0 && waitList.shift()
|
||||
}
|
||||
|
||||
const addTask = (list, type, store) => {
|
||||
window.requestAnimationFrame(() => {
|
||||
let item = list.shift()
|
||||
store.dispatch('download/createDownload', {
|
||||
musicInfo: item,
|
||||
type: getMusicType(item, type),
|
||||
})
|
||||
if (list.length) addTask(list, type, store)
|
||||
})
|
||||
}
|
||||
|
||||
const getUrl = (downloadInfo, isRefresh) => {
|
||||
const url = downloadInfo.musicInfo.typeUrl[downloadInfo.type]
|
||||
return url && !isRefresh ? Promise.resolve({ url }) : music[downloadInfo.musicInfo.source].getMusicUrl(downloadInfo.musicInfo, downloadInfo.type).promise
|
||||
}
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
createDownload({ state, rootState }, { musicInfo, type }) {
|
||||
createDownload({ state, rootState, commit }, { musicInfo, type }) {
|
||||
if (checkList(state.list, musicInfo, type)) return
|
||||
let ext = getExt(type)
|
||||
const downloadInfo = {
|
||||
isComplate: false,
|
||||
isDownloading: false,
|
||||
statusText: '任务初始化中',
|
||||
status: state.downloadStatus.WAITING,
|
||||
statusText: '待下载',
|
||||
url: null,
|
||||
fileName: `${rootState.setting.download.fileName
|
||||
.replace('歌名', musicInfo.name)
|
||||
@@ -68,19 +99,33 @@ const actions = {
|
||||
key: `${musicInfo.songmid}${ext}`,
|
||||
}
|
||||
downloadInfo.filePath = path.join(rootState.setting.download.savePath, downloadInfo.fileName)
|
||||
commit('addTask', downloadInfo)
|
||||
if (dls[downloadInfo.key]) {
|
||||
dls[downloadInfo.key].stop().finally(() => {
|
||||
this.dispatch('download/addTask', downloadInfo)
|
||||
delete dls[downloadInfo.key]
|
||||
this.dispatch('download/startTask', downloadInfo)
|
||||
})
|
||||
} else {
|
||||
// console.log(downloadInfo)
|
||||
this.dispatch('download/addTask', downloadInfo)
|
||||
this.dispatch('download/startTask', downloadInfo)
|
||||
}
|
||||
},
|
||||
addTask({ commit, rootState }, downloadInfo) {
|
||||
commit('addTask', downloadInfo)
|
||||
createDownloadMultiple({ state, rootState }, { list, type }) {
|
||||
addTask([...list], type, this)
|
||||
},
|
||||
startTask({ commit, state, rootState }, downloadInfo) {
|
||||
// 检查是否可以开始任务
|
||||
if (downloadInfo && downloadInfo != state.downloadStatus.WAITING) commit('setStatus', { downloadInfo, status: state.downloadStatus.WAITING })
|
||||
let result = getStartTask(state.list, state.downloadStatus, rootState.setting.download.maxDownloadNum)
|
||||
if (!result) return
|
||||
if (!downloadInfo) downloadInfo = result
|
||||
|
||||
// 开始任务
|
||||
commit('onDownload', downloadInfo)
|
||||
commit('setStatusText', { downloadInfo, text: '任务初始化中' })
|
||||
let msg = checkPath(rootState.setting.download.savePath)
|
||||
if (msg) return commit('setStatusText', '检查下载目录出错: ' + msg)
|
||||
const _this = this
|
||||
const options = {
|
||||
url: downloadInfo.url,
|
||||
path: rootState.setting.download.savePath,
|
||||
@@ -89,37 +134,50 @@ const actions = {
|
||||
override: true,
|
||||
onEnd() {
|
||||
commit('onEnd', downloadInfo)
|
||||
_this.dispatch('download/startTask')
|
||||
console.log('on complate')
|
||||
},
|
||||
onError(err) {
|
||||
console.log(err)
|
||||
if (err.message.includes('Response status was')) {
|
||||
const code = err.message.replace(/Response status was (\d+)$/, '$1')
|
||||
switch (code) {
|
||||
case '401':
|
||||
case '403':
|
||||
case '410':
|
||||
commit('setStatusText', { downloadInfo, text: '链接失效,正在刷新链接' })
|
||||
refreshUrl(downloadInfo).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)
|
||||
})
|
||||
return
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
// console.log(err.code, err.message)
|
||||
commit('onError', downloadInfo)
|
||||
// console.log(tryNum[downloadInfo.key])
|
||||
if (++tryNum[downloadInfo.key] > 5) {
|
||||
_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
|
||||
} else {
|
||||
console.log('Download failed, Attempting Retry')
|
||||
dls[downloadInfo.key].resume()
|
||||
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)
|
||||
},
|
||||
// onStateChanged(state) {
|
||||
// console.log(state)
|
||||
// },
|
||||
onDownload() {
|
||||
commit('onDownload', downloadInfo)
|
||||
console.log('on download')
|
||||
@@ -130,88 +188,64 @@ const actions = {
|
||||
},
|
||||
onPause() {
|
||||
commit('pauseTask', downloadInfo)
|
||||
_this.dispatch('download/startTask')
|
||||
},
|
||||
onResume() {
|
||||
commit('resumeTask', downloadInfo)
|
||||
},
|
||||
}
|
||||
let p = options.url ? Promise.resolve() : refreshUrl(downloadInfo).then(result => {
|
||||
commit('setStatusText', { downloadInfo, text: '获取URL中...' })
|
||||
let p = options.url ? Promise.resolve() : getUrl(downloadInfo).then(result => {
|
||||
commit('updateUrl', { downloadInfo, url: result.url })
|
||||
if (!result.url) return Promise.reject(new Error('获取URL失败'))
|
||||
options.url = result.url
|
||||
})
|
||||
p.then(() => {
|
||||
tryNum[downloadInfo.key] = 0
|
||||
dls[downloadInfo.key] = download(options)
|
||||
}).catch(err => {
|
||||
// console.log(err.message)
|
||||
commit('onError', downloadInfo)
|
||||
commit('setStatusText', { downloadInfo, text: err.message })
|
||||
this.dispatch('download/startTask')
|
||||
})
|
||||
},
|
||||
// startTaskMultiple({ state, rootState }, list) {
|
||||
|
||||
// },
|
||||
removeTask({ commit, state }, index) {
|
||||
let info = state.list[index]
|
||||
if (state.list[index].isDownloading) {
|
||||
dls[info.key].stop().finally(() => {
|
||||
delete dls[info.key]
|
||||
})
|
||||
if (state.list[index].status == state.downloadStatus.RUN) {
|
||||
if (dls[info.key]) {
|
||||
dls[info.key].stop().finally(() => {
|
||||
delete dls[info.key]
|
||||
})
|
||||
}
|
||||
}
|
||||
commit('removeTask', index)
|
||||
if (dls[info.key]) delete dls[info.key]
|
||||
this.dispatch('download/startTask')
|
||||
},
|
||||
resumeTask({ commit, rootState }, downloadInfo) {
|
||||
let msg = checkPath(rootState.setting.download.savePath)
|
||||
if (msg) return commit('setStatusText', '检查下载目录出错: ' + msg)
|
||||
const options = {
|
||||
url: downloadInfo.url,
|
||||
path: rootState.setting.download.savePath,
|
||||
fileName: downloadInfo.fileName,
|
||||
method: 'get',
|
||||
override: true,
|
||||
onEnd() {
|
||||
commit('onEnd', downloadInfo)
|
||||
console.log('on complate')
|
||||
},
|
||||
onError(err) {
|
||||
commit('onError', downloadInfo)
|
||||
commit('setStatusText', { downloadInfo, text: '链接失效,正在刷新链接' })
|
||||
refreshUrl(downloadInfo).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)
|
||||
})
|
||||
console.log(err)
|
||||
},
|
||||
onStateChanged(state) {
|
||||
console.log(state)
|
||||
},
|
||||
onDownload() {
|
||||
commit('onDownload', downloadInfo)
|
||||
console.log('on download')
|
||||
},
|
||||
onProgress(status) {
|
||||
commit('onProgress', { downloadInfo, status })
|
||||
console.log(status)
|
||||
},
|
||||
onPause() {
|
||||
commit('pauseTask', downloadInfo)
|
||||
},
|
||||
onResume() {
|
||||
commit('resumeTask', downloadInfo)
|
||||
},
|
||||
}
|
||||
|
||||
let p = options.url ? Promise.resolve() : refreshUrl(downloadInfo).then(result => {
|
||||
commit('updateUrl', { downloadInfo, url: result.url })
|
||||
options.url = result.url
|
||||
})
|
||||
|
||||
if (fs.existsSync(downloadInfo.filePath)) {
|
||||
options.resumeInfo = {
|
||||
totalFileSize: downloadInfo.progress.total,
|
||||
removeTaskMultiple({ commit, rootState, state }, list) {
|
||||
list.forEach(item => {
|
||||
let index = state.list.indexOf(item)
|
||||
if (index < 0) return
|
||||
// this.dispatch('download/removeTask', index)
|
||||
if (state.list[index].status == state.downloadStatus.RUN) {
|
||||
if (dls[item.key]) {
|
||||
dls[item.key].stop().finally(() => {
|
||||
delete dls[item.key]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
p.then(() => {
|
||||
dls[downloadInfo.key] = download(options)
|
||||
commit('removeTask', index)
|
||||
if (dls[item.key]) delete dls[item.key]
|
||||
})
|
||||
let result = getStartTask(state.list, state.downloadStatus, rootState.setting.download.maxDownloadNum)
|
||||
while (result) {
|
||||
this.dispatch('download/startTask', result)
|
||||
result = getStartTask(state.list, state.downloadStatus, rootState.setting.download.maxDownloadNum)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -224,29 +258,57 @@ const mutations = {
|
||||
state.list.splice(index, 1)
|
||||
},
|
||||
pauseTask(state, downloadInfo) {
|
||||
downloadInfo.isDownloading = false
|
||||
downloadInfo.status = state.downloadStatus.PAUSE
|
||||
downloadInfo.statusText = '暂停下载'
|
||||
},
|
||||
resumeTask(state, downloadInfo) {
|
||||
downloadInfo.statusText = '恢复下载'
|
||||
downloadInfo.statusText = '开始下载'
|
||||
},
|
||||
setStatusText(state, { downloadInfo, index, text }) {
|
||||
setStatusText(state, { downloadInfo, index, text }) { // 设置状态文本
|
||||
if (downloadInfo) {
|
||||
downloadInfo.statusText = text
|
||||
} else {
|
||||
state.list[index].statusText = text
|
||||
}
|
||||
},
|
||||
setStatus(state, { downloadInfo, index, status }) { // 设置状态及状态文本
|
||||
let text
|
||||
switch (status) {
|
||||
case state.downloadStatus.RUN:
|
||||
text = '正在下载'
|
||||
break
|
||||
case state.downloadStatus.WAITING:
|
||||
text = '等待下载'
|
||||
break
|
||||
case state.downloadStatus.PAUSE:
|
||||
text = '暂停下载'
|
||||
break
|
||||
case state.downloadStatus.ERROR:
|
||||
text = '任务出错'
|
||||
break
|
||||
case state.downloadStatus.COMPLETED:
|
||||
text = '下载完成'
|
||||
break
|
||||
}
|
||||
if (downloadInfo) {
|
||||
downloadInfo.statusText = text
|
||||
downloadInfo.status = status
|
||||
} else {
|
||||
state.list[index].statusText = text
|
||||
state.list[index].status = status
|
||||
}
|
||||
},
|
||||
onEnd(state, downloadInfo) {
|
||||
downloadInfo.isComplate = true
|
||||
downloadInfo.isDownloading = false
|
||||
downloadInfo.status = state.downloadStatus.COMPLETED
|
||||
downloadInfo.statusText = '下载完成'
|
||||
},
|
||||
onError(state, downloadInfo) {
|
||||
downloadInfo.isDownloading = false
|
||||
downloadInfo.status = state.downloadStatus.ERROR
|
||||
downloadInfo.statusText = '任务出错'
|
||||
},
|
||||
onDownload(state, downloadInfo) {
|
||||
downloadInfo.isDownloading = true
|
||||
downloadInfo.status = state.downloadStatus.RUN
|
||||
downloadInfo.statusText = '正在下载'
|
||||
},
|
||||
onProgress(state, { downloadInfo, status }) {
|
||||
|
||||
@@ -30,9 +30,25 @@ const mutations = {
|
||||
if (state.defaultList.list.some(s => s.songmid === musicInfo.songmid)) return
|
||||
state.defaultList.list.push(musicInfo)
|
||||
},
|
||||
defaultListAddMultiple(state, list) {
|
||||
list.forEach(musicInfo => {
|
||||
if (state.defaultList.list.some(s => s.songmid === musicInfo.songmid)) return
|
||||
state.defaultList.list.push(musicInfo)
|
||||
})
|
||||
},
|
||||
defaultListRemove(state, index) {
|
||||
state.defaultList.list.splice(index, 1)
|
||||
},
|
||||
updateMusicInfo(state, { index, data }) {
|
||||
Object.assign(state.defaultList.list[index], data)
|
||||
},
|
||||
defaultListRemoveMultiple(state, list) {
|
||||
list.forEach(musicInfo => {
|
||||
let index = state.defaultList.list.indexOf(musicInfo)
|
||||
if (index < 0) return
|
||||
state.defaultList.list.splice(index, 1)
|
||||
})
|
||||
},
|
||||
defaultListClear(state) {
|
||||
state.defaultList.list.length = 0
|
||||
},
|
||||
|
||||
@@ -8,6 +8,10 @@ const state = {
|
||||
changePlay: false,
|
||||
}
|
||||
|
||||
let urlRequest
|
||||
let picRequest
|
||||
let lrcRequest
|
||||
|
||||
// getters
|
||||
const getters = {
|
||||
list: state => state.list || [],
|
||||
@@ -18,14 +22,33 @@ const getters = {
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
getUrl({ commit, state }, { musicInfo, type }) {
|
||||
return music[musicInfo.source].getMusicUrl(musicInfo, type).then(result => commit('setUrl', { musicInfo, url: result.url, type }))
|
||||
getUrl({ commit, state }, { musicInfo, type, isRefresh }) {
|
||||
if (urlRequest && urlRequest.cancelHttp) urlRequest.cancelHttp()
|
||||
if (musicInfo.typeUrl[type] && !isRefresh) return Promise.resolve()
|
||||
urlRequest = music[musicInfo.source].getMusicUrl(musicInfo, type)
|
||||
return urlRequest.promise.then(result => {
|
||||
commit('setUrl', { musicInfo, url: result.url, type })
|
||||
}).finally(() => {
|
||||
urlRequest = null
|
||||
})
|
||||
},
|
||||
getPic({ commit, state }, musicInfo) {
|
||||
return music[musicInfo.source].getPic(musicInfo).then(url => commit('getPic', { musicInfo, url }))
|
||||
if (picRequest && picRequest.cancelHttp) picRequest.cancelHttp()
|
||||
picRequest = music[musicInfo.source].getPic(musicInfo)
|
||||
return picRequest.promise.then(url => {
|
||||
commit('getPic', { musicInfo, url })
|
||||
}).finally(() => {
|
||||
picRequest = null
|
||||
})
|
||||
},
|
||||
getLrc({ commit, state }, musicInfo) {
|
||||
return music[musicInfo.source].getLyric(musicInfo).then(lrc => commit('setLrc', { musicInfo, lrc }))
|
||||
if (lrcRequest && lrcRequest.cancelHttp) lrcRequest.cancelHttp()
|
||||
lrcRequest = music[musicInfo.source].getLyric(musicInfo)
|
||||
return lrcRequest.promise.then(lrc => {
|
||||
commit('setLrc', { musicInfo, lrc })
|
||||
}).finally(() => {
|
||||
lrcRequest = null
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -33,7 +56,7 @@ const actions = {
|
||||
// mitations
|
||||
const mutations = {
|
||||
setUrl(state, datas) {
|
||||
datas.musicInfo.typeUrl[datas.type] = datas.url
|
||||
datas.musicInfo.typeUrl = Object.assign({}, datas.musicInfo.typeUrl, { [datas.type]: datas.url })
|
||||
},
|
||||
getPic(state, datas) {
|
||||
datas.musicInfo.img = datas.url
|
||||
@@ -50,6 +73,7 @@ const mutations = {
|
||||
setPlayIndex(state, index) {
|
||||
state.playIndex = index
|
||||
state.changePlay = true
|
||||
// console.log(state.changePlay)
|
||||
},
|
||||
fixPlayIndex(state, index) {
|
||||
state.playIndex = index
|
||||
|
||||
@@ -13,13 +13,17 @@ export default {
|
||||
if (source != null) state.setting.leaderboard.source = source
|
||||
},
|
||||
setNewVersion(state, val) {
|
||||
val.history.forEach(ver => {
|
||||
ver.desc = ver.desc.replace(/\n/g, '<br>')
|
||||
})
|
||||
val.desc = val.desc.replace(/\n/g, '<br>')
|
||||
// val.history.forEach(ver => {
|
||||
// ver.desc = ver.desc.replace(/\n/g, '<br>')
|
||||
// })
|
||||
// val.desc = val.desc.replace(/\n/g, '<br>')
|
||||
state.version.newVersion = val
|
||||
},
|
||||
setVersionVisible(state, val) {
|
||||
state.version.showModal = val
|
||||
setVersionModalVisible(state, { isShow, isError }) {
|
||||
if (isShow !== undefined) state.version.showModal = isShow
|
||||
if (isError !== undefined) state.version.isError = isError
|
||||
},
|
||||
setVolume(state, val) {
|
||||
state.setting.player.volume = val
|
||||
},
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ export default {
|
||||
version,
|
||||
newVersion: null,
|
||||
showModal: false,
|
||||
isError: false,
|
||||
},
|
||||
userInfo: null,
|
||||
setting,
|
||||
|
||||
@@ -45,9 +45,8 @@ export default ({
|
||||
onEnd()
|
||||
debugDownload && console.log('Download Completed')
|
||||
}).on('error', err => {
|
||||
if (err.message === 'socket hang up') return
|
||||
onError(err)
|
||||
dl.resume()
|
||||
console.log('Download failed, Attempting Retry')
|
||||
debugDownload && console.error('Something happend', err)
|
||||
}).on('stateChanged', state => {
|
||||
onStateChanged(state)
|
||||
|
||||
@@ -156,13 +156,19 @@ export const isChildren = (parent, children) => {
|
||||
return children.parentNode ? children.parentNode === parent ? true : isChildren(parent, children.parentNode) : false
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级设置
|
||||
* @param {*} setting
|
||||
*/
|
||||
export const updateSetting = setting => {
|
||||
const defaultVersion = '1.0.1'
|
||||
const defaultVersion = '1.0.4'
|
||||
const defaultSetting = {
|
||||
version: defaultVersion,
|
||||
player: {
|
||||
togglePlayMethod: 'listLoop',
|
||||
highQuality: false,
|
||||
isShowTaskProgess: true,
|
||||
volume: 1,
|
||||
},
|
||||
list: {
|
||||
isShowAlbumName: true,
|
||||
@@ -170,6 +176,7 @@ export const updateSetting = setting => {
|
||||
download: {
|
||||
savePath: path.join(os.homedir(), 'Desktop'),
|
||||
fileName: '歌名 - 歌手',
|
||||
maxDownloadNum: 3,
|
||||
},
|
||||
leaderboard: {
|
||||
source: 'kw',
|
||||
@@ -177,7 +184,9 @@ export const updateSetting = setting => {
|
||||
},
|
||||
themeId: 0,
|
||||
sourceId: 'kw',
|
||||
apiSource: 'messoer',
|
||||
randomAnimate: true,
|
||||
ignoreVersion: null,
|
||||
}
|
||||
const overwriteSetting = {
|
||||
version: defaultVersion,
|
||||
@@ -192,5 +201,22 @@ export const updateSetting = setting => {
|
||||
objectDeepMerge(defaultSetting, overwriteSetting)
|
||||
setting = defaultSetting
|
||||
}
|
||||
setting.apiSource = 'messoer' // 强制设置回 messoer 接口源
|
||||
return setting
|
||||
}
|
||||
|
||||
/**
|
||||
* 在浏览器打开URL
|
||||
* @param {*} url
|
||||
*/
|
||||
export const openUrl = url => {
|
||||
shell.openExternal(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置标题
|
||||
*/
|
||||
let dom_title = document.getElementsByTagName('title')[0]
|
||||
export const setTitle = title => {
|
||||
dom_title.innerText = title || '洛雪音乐助手'
|
||||
}
|
||||
|
||||
7
src/renderer/utils/message.js
Normal file
7
src/renderer/utils/message.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export const requestMsg = {
|
||||
fail: '请求异常😮,可以多试几次,若还是不行就换一首吧。。。',
|
||||
unachievable: '哦No😱...接口无法访问了!',
|
||||
// unachievable: '哦No😱...接口无法访问了!已帮你切换到临时接口,重试下看能不能播放吧~',
|
||||
notConnectNetwork: '无法连接网络',
|
||||
cancelRequest: '取消http请求',
|
||||
}
|
||||
40
src/renderer/utils/music/api-source.js
Normal file
40
src/renderer/utils/music/api-source.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import kw_api_messoer from './kw/api-messoer'
|
||||
import kw_api_temp from './kw/api-temp'
|
||||
import tx_api_messoer from './tx/api-messoer'
|
||||
import kg_api_messoer from './kg/api-messoer'
|
||||
import wy_api_messoer from './wy/api-messoer'
|
||||
import bd_api_messoer from './bd/api-messoer'
|
||||
|
||||
const apis = {
|
||||
kw_api_messoer,
|
||||
tx_api_messoer,
|
||||
kg_api_messoer,
|
||||
wy_api_messoer,
|
||||
bd_api_messoer,
|
||||
kw_api_temp,
|
||||
}
|
||||
|
||||
|
||||
const getAPI = source => {
|
||||
switch (window.globalObj.apiSource) {
|
||||
case 'messoer':
|
||||
return apis[`${source}_api_messoer`]
|
||||
case 'temp':
|
||||
return apis[`${source}_api_temp`]
|
||||
}
|
||||
}
|
||||
|
||||
export default source => {
|
||||
switch (source) {
|
||||
case 'tx':
|
||||
return getAPI('tx')
|
||||
case 'kg':
|
||||
return getAPI('kg')
|
||||
case 'wy':
|
||||
return getAPI('wy')
|
||||
case 'bd':
|
||||
return getAPI('bd')
|
||||
default:
|
||||
return getAPI('kw')
|
||||
}
|
||||
}
|
||||
37
src/renderer/utils/music/bd/api-messoer.js
Normal file
37
src/renderer/utils/music/bd/api-messoer.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/baidu/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo, size = '500') {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/baidu/pic?id=${songInfo.songmid}&imageSize=${size}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/baidu/lrc?id=${songInfo.songmid}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body ? Promise.resolve(body) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_messoer
|
||||
21
src/renderer/utils/music/bd/index.js
Normal file
21
src/renderer/utils/music/bd/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import leaderboard from './leaderboard'
|
||||
import api_source from '../api-source'
|
||||
import musicInfo from './musicInfo'
|
||||
|
||||
const bd = {
|
||||
leaderboard,
|
||||
getMusicUrl(songInfo, type) {
|
||||
return api_source('bd').getMusicUrl(songInfo, type)
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
return api_source('bd').getLyric(songInfo)
|
||||
},
|
||||
getPic(songInfo) {
|
||||
return api_source('bd').getPic(songInfo)
|
||||
},
|
||||
getMusicInfo(songInfo) {
|
||||
return musicInfo.getMusicInfo(songInfo.songmid)
|
||||
},
|
||||
}
|
||||
|
||||
export default bd
|
||||
132
src/renderer/utils/music/bd/leaderboard.js
Normal file
132
src/renderer/utils/music/bd/leaderboard.js
Normal file
@@ -0,0 +1,132 @@
|
||||
import { httpFatch } from '../../request'
|
||||
// import { formatPlayTime } from '../../index'
|
||||
// import jshtmlencode from 'js-htmlencode'
|
||||
|
||||
export default {
|
||||
limit: 20,
|
||||
list: [
|
||||
{
|
||||
id: 'bdrgb',
|
||||
name: '热歌榜',
|
||||
bangid: '2',
|
||||
},
|
||||
{
|
||||
id: 'bdxgb',
|
||||
name: '新歌榜',
|
||||
bangid: '1',
|
||||
},
|
||||
{
|
||||
id: 'bdycb',
|
||||
name: '原创榜',
|
||||
bangid: '200',
|
||||
},
|
||||
{
|
||||
id: 'bdhyjqb',
|
||||
name: '华语榜',
|
||||
bangid: '20',
|
||||
},
|
||||
{
|
||||
id: 'bdomjqb',
|
||||
name: '欧美榜',
|
||||
bangid: '21',
|
||||
},
|
||||
{
|
||||
id: 'bdwugqb',
|
||||
name: '网络榜',
|
||||
bangid: '25',
|
||||
},
|
||||
{
|
||||
id: 'bdjdlgb',
|
||||
name: '老歌榜',
|
||||
bangid: '22',
|
||||
},
|
||||
{
|
||||
id: 'bdysjqb',
|
||||
name: '影视金曲榜',
|
||||
bangid: '24',
|
||||
},
|
||||
{
|
||||
id: 'bdqgdcb',
|
||||
name: '情歌对唱榜',
|
||||
bangid: '23',
|
||||
},
|
||||
{
|
||||
id: 'bdygb',
|
||||
name: '摇滚榜',
|
||||
bangid: '11',
|
||||
},
|
||||
],
|
||||
getUrl(id, p) {
|
||||
return `http://musicmini.qianqian.com/2018/static/bangdan/bangdanList_${id}_${p}.html`
|
||||
},
|
||||
regExps: {
|
||||
item: /data-song="({.+?})"/g,
|
||||
info: /{total[\s:]+"(\d+)", size[\s:]+"(\d+)", page[\s:]+"(\d+)"}/,
|
||||
},
|
||||
requestObj: null,
|
||||
getData(url) {
|
||||
if (this.requestObj) this.requestObj.cancelHttp()
|
||||
this.requestObj = httpFatch(url)
|
||||
return this.requestObj.promise
|
||||
},
|
||||
filterData(rawList) {
|
||||
// console.log(rawList)
|
||||
return rawList.map(item => {
|
||||
const types = []
|
||||
const _types = {}
|
||||
let size = null
|
||||
types.push({ type: '128k', size })
|
||||
_types['128k'] = {
|
||||
size,
|
||||
}
|
||||
if (item.biaoshi) {
|
||||
types.push({ type: '320k', size })
|
||||
_types['320k'] = {
|
||||
size,
|
||||
}
|
||||
types.push({ type: 'flac', size })
|
||||
_types['flac'] = {
|
||||
size,
|
||||
}
|
||||
}
|
||||
// types.reverse()
|
||||
|
||||
return {
|
||||
singer: item.song_artist.replace(',', '、'),
|
||||
name: item.song_title,
|
||||
albumName: item.album_title,
|
||||
albumId: item.album_id,
|
||||
source: 'bd',
|
||||
interval: '',
|
||||
songmid: item.song_id,
|
||||
img: null,
|
||||
lrc: null,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
}
|
||||
})
|
||||
},
|
||||
parseData(rawData) {
|
||||
// return rawData.map(item => JSON.parse(item.replace(this.regExps.item, '$1').replace(/"/g, '"').replace(/\\\//g, '/').replace(/(@s_1,w_)\d+(,h_)\d+/, '$1500$2500')))
|
||||
return rawData.map(item => JSON.parse(item.replace(this.regExps.item, '$1').replace(/"/g, '"').replace(/\\\//g, '/')))
|
||||
},
|
||||
getList(id, page) {
|
||||
let type = this.list.find(s => s.id === id)
|
||||
if (!type) return Promise.reject()
|
||||
return this.getData(this.getUrl(type.bangid, page)).then(({ body }) => {
|
||||
let result = body.match(this.regExps.item)
|
||||
if (!result) return Promise.reject(new Error('匹配list失败'))
|
||||
let info = body.match(this.regExps.info)
|
||||
if (!info) return Promise.reject(new Error('匹配info失败'))
|
||||
const list = this.filterData(this.parseData(result))
|
||||
this.limit = parseInt(info[2])
|
||||
return {
|
||||
total: parseInt(info[1]),
|
||||
list,
|
||||
limit: this.limit,
|
||||
page: parseInt(info[3]),
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
11
src/renderer/utils/music/bd/musicInfo.js
Normal file
11
src/renderer/utils/music/bd/musicInfo.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { httpFatch } from '../../request'
|
||||
|
||||
export default {
|
||||
getMusicInfo(songmid) {
|
||||
const requestObj = httpFatch(`https://musicapi.qianqian.com/v1/restserver/ting?method=baidu.ting.song.getSongLink&format=json&from=bmpc&version=1.0.0&version_d=11.1.6.0&songid=${songmid}&type=1&res=1&s_protocol=1&aac=2&project=tpass`)
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.error_code == 22000 ? body.reqult.songinfo : Promise.reject(new Error('获取音乐信息失败'))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import kw from './kw'
|
||||
import kg from './kg'
|
||||
import tx from './tx'
|
||||
import wy from './wy'
|
||||
import bd from './bd'
|
||||
export default {
|
||||
sources: [
|
||||
{
|
||||
@@ -20,9 +21,14 @@ export default {
|
||||
name: '网易音乐',
|
||||
id: 'wy',
|
||||
},
|
||||
{
|
||||
name: '百度音乐',
|
||||
id: 'bd',
|
||||
},
|
||||
],
|
||||
kw,
|
||||
kg,
|
||||
tx,
|
||||
wy,
|
||||
bd,
|
||||
}
|
||||
|
||||
37
src/renderer/utils/music/kg/api-messoer.js
Normal file
37
src/renderer/utils/music/kg/api-messoer.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/kugou/url?id=${songInfo._types[type].hash}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/kugou/pic?id=${songInfo.hash}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/kugou/lrc?id=${songInfo.hash}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body ? Promise.resolve(body) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_messoer
|
||||
@@ -1,7 +1,18 @@
|
||||
import leaderboard from './leaderboard'
|
||||
import api_source from '../api-source'
|
||||
|
||||
|
||||
const kg = {
|
||||
leaderboard,
|
||||
getMusicUrl(songInfo, type) {
|
||||
return api_source('kg').getMusicUrl(songInfo, type)
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
return api_source('kg').getLyric(songInfo)
|
||||
},
|
||||
getPic(songInfo) {
|
||||
return api_source('kg').getPic(songInfo)
|
||||
},
|
||||
}
|
||||
|
||||
export default kg
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { httpGet, cancelHttp } from '../../request'
|
||||
import { formatPlayTime } from '../../index'
|
||||
import { formatPlayTime, sizeFormate } from '../../index'
|
||||
|
||||
export default {
|
||||
list: [
|
||||
@@ -68,17 +68,17 @@ export default {
|
||||
limit: /pagesize: '(\d+)',/,
|
||||
listData: /global\.features = (\[.+\]);/,
|
||||
},
|
||||
_cancelIndex: null,
|
||||
_requestObj: null,
|
||||
_cancelPromiseCancelFn: null,
|
||||
getData(url) {
|
||||
if (this._cancelIndex != null) {
|
||||
cancelHttp(this._cancelIndex)
|
||||
if (this._requestObj != null) {
|
||||
cancelHttp(this._requestObj)
|
||||
this._cancelPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._cancelPromiseCancelFn = reject
|
||||
this._cancelIndex = httpGet(url, (err, resp, body) => {
|
||||
this._cancelIndex = null
|
||||
this._requestObj = httpGet(url, (err, resp, body) => {
|
||||
this._requestObj = null
|
||||
this._cancelPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
@@ -89,18 +89,58 @@ export default {
|
||||
})
|
||||
},
|
||||
filterData(rawList) {
|
||||
return rawList.map(item => ({
|
||||
singer: item.singername,
|
||||
name: item.songname,
|
||||
albumName: item.album_name,
|
||||
albumId: item.album_id,
|
||||
songmid: item.audio_id,
|
||||
source: 'kg',
|
||||
interval: formatPlayTime(item.duration / 1000),
|
||||
img: null,
|
||||
lrc: null,
|
||||
typeUrl: {},
|
||||
}))
|
||||
// console.log(rawList)
|
||||
return rawList.map(item => {
|
||||
const types = []
|
||||
const _types = {}
|
||||
if (item.filesize !== 0) {
|
||||
let size = sizeFormate(item.filesize)
|
||||
types.push({ type: '128k', size, hash: item.hash })
|
||||
_types['128k'] = {
|
||||
size,
|
||||
hash: item.hash,
|
||||
}
|
||||
}
|
||||
if (item.filesize_320 !== 0) {
|
||||
let size = sizeFormate(item.filesize_320)
|
||||
types.push({ type: '320k', size, hash: item.hash_320 })
|
||||
_types['320k'] = {
|
||||
size,
|
||||
hash: item.hash_320,
|
||||
}
|
||||
}
|
||||
if (item.filesize_ape !== 0) {
|
||||
let size = sizeFormate(item.filesize_ape)
|
||||
types.push({ type: 'ape', size, hash: item.hash_ape })
|
||||
_types.ape = {
|
||||
size,
|
||||
hash: item.hash_ape,
|
||||
}
|
||||
}
|
||||
if (item.filesize_flac !== 0) {
|
||||
let size = sizeFormate(item.filesize_flac)
|
||||
types.push({ type: 'flac', size, hash: item.hash_flac })
|
||||
_types.flac = {
|
||||
size,
|
||||
hash: item.hash_flac,
|
||||
}
|
||||
}
|
||||
return {
|
||||
singer: item.singername,
|
||||
name: item.songname,
|
||||
albumName: item.album_name,
|
||||
albumId: item.album_id,
|
||||
songmid: item.audio_id,
|
||||
source: 'kg',
|
||||
interval: formatPlayTime(item.duration / 1000),
|
||||
img: null,
|
||||
lrc: null,
|
||||
hash: item.HASH,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
}
|
||||
})
|
||||
},
|
||||
getList(id, page) {
|
||||
let type = this.list.find(s => s.id === id)
|
||||
|
||||
27
src/renderer/utils/music/kw/api-messoer.js
Normal file
27
src/renderer/utils/music/kw/api-messoer.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/kuwo/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/kuwo/pic?id=${songInfo.songmid}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_messoer
|
||||
24
src/renderer/utils/music/kw/api-temp.js
Normal file
24
src/renderer/utils/music/kw/api-temp.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { httpFatch } from '../../request'
|
||||
|
||||
const api_temp = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://www.stsky.cn/api/temp/getMusicUrl.php?id=${songInfo.songmid}&type=${type}`, {
|
||||
method: 'get',
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(body.msg))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo) {
|
||||
const requestObj = httpFatch(`https://www.stsky.cn/api/temp/getPic.php?size=320&songmid=${songInfo.songmid}`, {
|
||||
method: 'get',
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(body.msg))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_temp
|
||||
@@ -4,11 +4,12 @@ import musicSearch from './musicSearch'
|
||||
import { formatSinger } from './util'
|
||||
import leaderboard from './leaderboard'
|
||||
import lyric from './lyric'
|
||||
import api_source from '../api-source'
|
||||
|
||||
const kw = {
|
||||
_musicInfoIndex: null,
|
||||
_musicInfoRequestObj: null,
|
||||
_musicInfoPromiseCancelFn: null,
|
||||
_musicPicIndex: null,
|
||||
_musicPicRequestObj: null,
|
||||
_musicPicPromiseCancelFn: null,
|
||||
// context: null,
|
||||
|
||||
@@ -52,26 +53,18 @@ const kw = {
|
||||
},
|
||||
|
||||
getMusicUrl(songInfo, type) {
|
||||
return new Promise((resolve, reject) => {
|
||||
httpGet(`https://v1.itooi.cn/kuwo/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, (err, resp, body) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
return this.getMusicUrl(songInfo, type)
|
||||
}
|
||||
body.code === 200 ? resolve({ type, url: body.data }) : reject(new Error(body.msg))
|
||||
})
|
||||
})
|
||||
return api_source('kw').getMusicUrl(songInfo, type)
|
||||
},
|
||||
|
||||
getMusicInfo(songInfo) {
|
||||
if (this._musicInfoIndex != null) {
|
||||
cancelHttp(this._musicInfoIndex)
|
||||
if (this._musicInfoRequestObj != null) {
|
||||
cancelHttp(this._musicInfoRequestObj)
|
||||
this._musicInfoPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._musicInfoPromiseCancelFn = reject
|
||||
this._musicInfoIndex = httpGet(`http://www.kuwo.cn/api/www/music/musicInfo?mid=${songInfo.songmid}`, (err, resp, body) => {
|
||||
this._musicInfoIndex = null
|
||||
this._musicInfoRequestObj = httpGet(`http://www.kuwo.cn/api/www/music/musicInfo?mid=${songInfo.songmid}`, (err, resp, body) => {
|
||||
this._musicInfoRequestObj = null
|
||||
this._musicInfoPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
@@ -86,7 +79,7 @@ const kw = {
|
||||
let tasks = []
|
||||
let songId = musicInfo.songmid
|
||||
musicInfo.types.forEach(type => {
|
||||
tasks.push(kw.getMusicUrl(songId, type.type))
|
||||
tasks.push(kw.getMusicUrl(songId, type.type).promise)
|
||||
})
|
||||
Promise.all(tasks).then(urlInfo => {
|
||||
let typeUrl = {}
|
||||
@@ -98,23 +91,7 @@ const kw = {
|
||||
},
|
||||
|
||||
getPic(songInfo) {
|
||||
if (this._musicPicIndex != null) {
|
||||
cancelHttp(this._musicPicIndex)
|
||||
this._musicPicPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._musicPicPromiseCancelFn = reject
|
||||
this._musicPicIndex = httpGet(`https://v1.itooi.cn/kuwo/pic?id=${songInfo.songmid}&isRedirect=0`, (err, resp, body) => {
|
||||
this._musicPicIndex = null
|
||||
this._musicPicPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
}
|
||||
console.log(body)
|
||||
body.code === 200 ? resolve(body.data) : reject(new Error(body.msg))
|
||||
})
|
||||
})
|
||||
return api_source('kw').getPic(songInfo)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -65,19 +65,19 @@ export default {
|
||||
|
||||
},
|
||||
limit: 30,
|
||||
_cancelIndex: null,
|
||||
_cancelRequestObj: null,
|
||||
_cancelPromiseCancelFn: null,
|
||||
_cancelIndex2: null,
|
||||
_cancelRequestObj2: null,
|
||||
_cancelPromiseCancelFn2: null,
|
||||
getData(url) {
|
||||
if (this._cancelIndex != null) {
|
||||
cancelHttp(this._cancelIndex)
|
||||
if (this._cancelRequestObj != null) {
|
||||
cancelHttp(this._cancelRequestObj)
|
||||
this._cancelPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._cancelPromiseCancelFn = reject
|
||||
this._cancelIndex = httpGet(url, (err, resp, body) => {
|
||||
this._cancelIndex = null
|
||||
this._cancelRequestObj = httpGet(url, (err, resp, body) => {
|
||||
this._cancelRequestObj = null
|
||||
this._cancelPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
@@ -88,14 +88,14 @@ export default {
|
||||
})
|
||||
},
|
||||
getData2(url) {
|
||||
if (this._cancelIndex2 != null) {
|
||||
cancelHttp(this._cancelIndex2)
|
||||
if (this._cancelRequestObj2 != null) {
|
||||
cancelHttp(this._cancelRequestObj2)
|
||||
this._cancelPromiseCancelFn2(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._cancelPromiseCancelFn2 = reject
|
||||
this._cancelIndex2 = httpGet(url, (err, resp, body) => {
|
||||
this._cancelIndex2 = null
|
||||
this._cancelRequestObj2 = httpGet(url, (err, resp, body) => {
|
||||
this._cancelRequestObj2 = null
|
||||
this._cancelPromiseCancelFn2 = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
@@ -106,6 +106,7 @@ export default {
|
||||
})
|
||||
},
|
||||
filterData(rawList, rawList2) {
|
||||
// console.log(rawList.length, rawList2.length)
|
||||
return rawList.map((item, inedx) => {
|
||||
let formats = item.formats.split('|')
|
||||
let types = []
|
||||
@@ -140,7 +141,7 @@ export default {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
types.reverse()
|
||||
// types.reverse()
|
||||
return {
|
||||
singer: item.artist,
|
||||
name: item.name,
|
||||
@@ -148,7 +149,7 @@ export default {
|
||||
albumId: item.albumid,
|
||||
songmid: item.id,
|
||||
source: 'kw',
|
||||
interval: formatPlayTime(rawList2[inedx].duration),
|
||||
interval: rawList2[inedx] && formatPlayTime(rawList2[inedx].duration),
|
||||
img: item.pic,
|
||||
lrc: null,
|
||||
types,
|
||||
|
||||
@@ -1,31 +1,19 @@
|
||||
import { httpGet, cancelHttp } from '../../request'
|
||||
import { httpFatch } from '../../request'
|
||||
|
||||
export default {
|
||||
_musicLrcIndex: null,
|
||||
_musicLrcPromiseCancelFn: null,
|
||||
formatTime(time) {
|
||||
let m = parseInt(time / 60)
|
||||
let s = (time % 60).toFixed(2)
|
||||
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
|
||||
},
|
||||
transformLrc({ songinfo, lrclist }) {
|
||||
return `[ti:${songinfo.songName}]\n[ar:${songinfo.artist}]\n[al:${songinfo.album}]\n[by:]\n[offset:0]\n${lrclist ? lrclist.map(l => `[${this.formatTime(l.time)}]${l.lineLyric}\n`).join('') : '暂无歌词'}`
|
||||
},
|
||||
getLyric(songId) {
|
||||
if (this._musicLrcIndex != null) {
|
||||
cancelHttp(this._musicLrcIndex)
|
||||
this._musicLrcPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._musicLrcPromiseCancelFn = reject
|
||||
this._musicLrcIndex = httpGet(`https://v1.itooi.cn/kuwo/lrc?id=${songId}`, (err, resp, body) => {
|
||||
this._musicLrcIndex = null
|
||||
this._musicLrcPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
}
|
||||
// console.log(body.data)
|
||||
// console.log(this.transformLrc(body.data))
|
||||
resolve(body)
|
||||
})
|
||||
const requestObj = httpFatch(`http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=${songId}`)
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return this.transformLrc(body.data)
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export default {
|
||||
regExps: {
|
||||
mInfo: /bitrate:(\d+),format:(\w+),size:([\w.]+)/,
|
||||
},
|
||||
_musicSearchIndex: null,
|
||||
_musicSearchRequestObj: null,
|
||||
_musicSearchPromiseCancelFn: null,
|
||||
limit: 30,
|
||||
total: 0,
|
||||
@@ -17,14 +17,14 @@ export default {
|
||||
allPage: 1,
|
||||
// cancelFn: null,
|
||||
musicSearch(str, page) {
|
||||
if (this._musicSearchIndex != null) {
|
||||
cancelHttp(this._musicSearchIndex)
|
||||
if (this._musicSearchRequestObj != null) {
|
||||
cancelHttp(this._musicSearchRequestObj)
|
||||
this._musicSearchPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._musicSearchPromiseCancelFn = reject
|
||||
this._musicSearchIndex = httpGet(`http://search.kuwo.cn/r.s?client=kt&all=${encodeURIComponent(str)}&pn=${page - 1}&rn=${this.limit}&uid=794762570&ver=kwplayer_ar_9.2.2.1&vipver=1&show_copyright_off=1&newver=1&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&vermerge=1&mobi=1&issubtitle=1`, (err, resp, body) => {
|
||||
this._musicSearchIndex = null
|
||||
this._musicSearchRequestObj = httpGet(`http://search.kuwo.cn/r.s?client=kt&all=${encodeURIComponent(str)}&pn=${page - 1}&rn=${this.limit}&uid=794762570&ver=kwplayer_ar_9.2.2.1&vipver=1&show_copyright_off=1&newver=1&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&vermerge=1&mobi=1&issubtitle=1`, (err, resp, body) => {
|
||||
this._musicSearchRequestObj = null
|
||||
this._musicSearchPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
|
||||
@@ -5,17 +5,17 @@ export default {
|
||||
regExps: {
|
||||
relWord: /RELWORD=(.+)/,
|
||||
},
|
||||
_musicTempSearchIndex: null,
|
||||
_musicTempSearchRequestObj: null,
|
||||
_musicTempSearchPromiseCancelFn: null,
|
||||
tempSearch(str) {
|
||||
if (this._musicTempSearchIndex != null) {
|
||||
cancelHttp(this._musicTempSearchIndex)
|
||||
if (this._musicTempSearchRequestObj != null) {
|
||||
cancelHttp(this._musicTempSearchRequestObj)
|
||||
this._musicTempSearchPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._musicTempSearchPromiseCancelFn = reject
|
||||
this._musicTempSearchIndex = httpGet(`http://www.kuwo.cn/api/www/search/searchKey?key=${encodeURIComponent(str)}`, (err, resp, body) => {
|
||||
this._musicTempSearchIndex = null
|
||||
this._musicTempSearchRequestObj = httpGet(`http://www.kuwo.cn/api/www/search/searchKey?key=${encodeURIComponent(str)}`, (err, resp, body) => {
|
||||
this._musicTempSearchRequestObj = null
|
||||
this._musicTempSearchPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
@@ -32,8 +32,8 @@ export default {
|
||||
})
|
||||
},
|
||||
cancelTempSearch() {
|
||||
if (this._musicTempSearchIndex != null) {
|
||||
cancelHttp(this._musicTempSearchIndex)
|
||||
if (this._musicTempSearchRequestObj != null) {
|
||||
cancelHttp(this._musicTempSearchRequestObj)
|
||||
this._musicTempSearchPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
},
|
||||
|
||||
22
src/renderer/utils/music/tx/api-messoer.js
Normal file
22
src/renderer/utils/music/tx/api-messoer.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/tencent/url?id=${songInfo.strMediaMid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo) {
|
||||
return {
|
||||
promise: Promise.resolve(`https://y.gtimg.cn/music/photo_new/T002R500x500M000${songInfo.albumId}.jpg`),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default api_messoer
|
||||
@@ -1,7 +1,20 @@
|
||||
import leaderboard from './leaderboard'
|
||||
import lyric from './lyric'
|
||||
import api_source from '../api-source'
|
||||
|
||||
const tx = {
|
||||
leaderboard,
|
||||
|
||||
getMusicUrl(songInfo, type) {
|
||||
return api_source('tx').getMusicUrl(songInfo, type)
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
// let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
|
||||
return lyric.getLyric(songInfo.songmid)
|
||||
},
|
||||
getPic(songInfo) {
|
||||
return api_source('tx').getPic(songInfo)
|
||||
},
|
||||
}
|
||||
|
||||
export default tx
|
||||
|
||||
@@ -83,17 +83,17 @@ export default {
|
||||
},
|
||||
periods: {},
|
||||
periodUrl: 'https://c.y.qq.com/node/pc/wk_v15/top.html',
|
||||
_cancelIndex: null,
|
||||
_cancelRequestObj: null,
|
||||
_cancelPromiseCancelFn: null,
|
||||
getData(url) {
|
||||
if (this._cancelIndex != null) {
|
||||
cancelHttp(this._cancelIndex)
|
||||
if (this._cancelRequestObj != null) {
|
||||
cancelHttp(this._cancelRequestObj)
|
||||
this._cancelPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._cancelPromiseCancelFn = reject
|
||||
this._cancelIndex = httpGet(url, (err, resp, body) => {
|
||||
this._cancelIndex = null
|
||||
this._cancelRequestObj = httpGet(url, (err, resp, body) => {
|
||||
this._cancelRequestObj = null
|
||||
this._cancelPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
@@ -143,7 +143,7 @@ export default {
|
||||
size,
|
||||
}
|
||||
}
|
||||
types.reverse()
|
||||
// types.reverse()
|
||||
return {
|
||||
singer: this.getSinger(item.singer),
|
||||
name: item.title,
|
||||
|
||||
19
src/renderer/utils/music/tx/lyric.js
Normal file
19
src/renderer/utils/music/tx/lyric.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { b64DecodeUnicode } from '../../index'
|
||||
|
||||
export default {
|
||||
regexps: {
|
||||
matchLrc: /.+"lyric":"([\w=+/]*)".+/,
|
||||
},
|
||||
getLyric(songmid) {
|
||||
const requestObj = httpFatch(`https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid=${songmid}&g_tk=2001461048&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8&platform=yqq`, {
|
||||
headers: {
|
||||
Referer: 'https://y.qq.com/portal/player.html',
|
||||
},
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return b64DecodeUnicode(body.replace(this.regexps.matchLrc, '$1'))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
18
src/renderer/utils/music/utils.js
Normal file
18
src/renderer/utils/music/utils.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 获取音乐音质
|
||||
* @param {*} info
|
||||
* @param {*} type
|
||||
*/
|
||||
|
||||
const types = ['flac', 'ape', '320k', '192k', '128k']
|
||||
export const getMusicType = (info, type) => {
|
||||
switch (window.globalObj.apiSource) {
|
||||
// case 'kg':
|
||||
case 'wy':
|
||||
return '128k'
|
||||
}
|
||||
const rangeType = types.slice(types.indexOf(type))
|
||||
for (const type of rangeType) {
|
||||
if (info._types[type]) return type
|
||||
}
|
||||
}
|
||||
37
src/renderer/utils/music/wy/api-messoer.js
Normal file
37
src/renderer/utils/music/wy/api-messoer.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/netease/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/netease/pic?id=${songInfo.songmid}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/netease/lrc?id=${songInfo.songmid}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body ? Promise.resolve(body) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_messoer
|
||||
@@ -1,7 +1,17 @@
|
||||
import leaderboard from './leaderboard'
|
||||
import api_source from '../api-source'
|
||||
|
||||
const wy = {
|
||||
leaderboard,
|
||||
getMusicUrl(songInfo, type) {
|
||||
return api_source('wy').getMusicUrl(songInfo, type)
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
return api_source('wy').getLyric(songInfo)
|
||||
},
|
||||
getPic(songInfo) {
|
||||
return api_source('wy').getPic(songInfo)
|
||||
},
|
||||
}
|
||||
|
||||
export default wy
|
||||
|
||||
@@ -61,17 +61,17 @@ export default {
|
||||
regExps: {
|
||||
list: /<textarea id="song-list-pre-data" style="display:none;">(.+?)<\/textarea>/,
|
||||
},
|
||||
_cancelIndex: null,
|
||||
_cancelRequestObj: null,
|
||||
_cancelPromiseCancelFn: null,
|
||||
getData(url) {
|
||||
if (this._cancelIndex != null) {
|
||||
cancelHttp(this._cancelIndex)
|
||||
if (this._cancelRequestObj != null) {
|
||||
cancelHttp(this._cancelRequestObj)
|
||||
this._cancelPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._cancelPromiseCancelFn = reject
|
||||
this._cancelIndex = httpGet(url, (err, resp, body) => {
|
||||
this._cancelIndex = null
|
||||
this._cancelRequestObj = httpGet(url, (err, resp, body) => {
|
||||
this._cancelRequestObj = null
|
||||
this._cancelPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
@@ -91,6 +91,42 @@ export default {
|
||||
filterData(rawList) {
|
||||
// console.log(rawList)
|
||||
return rawList.map(item => {
|
||||
const types = []
|
||||
const _types = {}
|
||||
let size
|
||||
switch (item.privilege.maxbr) {
|
||||
case 999000:
|
||||
size = null
|
||||
types.push({ type: 'flac', size })
|
||||
_types['flac'] = {
|
||||
size,
|
||||
}
|
||||
case 320000:
|
||||
size = null
|
||||
types.push({ type: '320k', size })
|
||||
_types['320k'] = {
|
||||
size,
|
||||
}
|
||||
case 192000:
|
||||
case 190000:
|
||||
size = null
|
||||
types.push({ type: '192k', size })
|
||||
_types['192k'] = {
|
||||
size,
|
||||
}
|
||||
// case '160000':
|
||||
|
||||
default:
|
||||
size = null
|
||||
types.push({ type: '128k', size })
|
||||
_types['128k'] = {
|
||||
size,
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
types.reverse()
|
||||
|
||||
return {
|
||||
singer: this.getSinger(item.artists),
|
||||
name: item.name,
|
||||
@@ -101,6 +137,8 @@ export default {
|
||||
songmid: item.id,
|
||||
img: item.album.picUrl,
|
||||
lrc: null,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,48 +1,106 @@
|
||||
import request from 'request'
|
||||
// import progress from 'request-progress'
|
||||
import { debugRequest } from './env'
|
||||
import { requestMsg } from './message'
|
||||
// import fs from 'fs'
|
||||
|
||||
const tasks = []
|
||||
const headers = {
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
|
||||
}
|
||||
|
||||
const fatchData = (url, method, options, callback) => {
|
||||
let index = pushTask(tasks, request(url, {
|
||||
// console.log(url, options)
|
||||
console.log('---start---', url)
|
||||
return request(url, {
|
||||
method,
|
||||
headers: options.headers,
|
||||
headers: Object.assign({}, headers, options.headers || {}),
|
||||
Origin: options.origin,
|
||||
data: options.data,
|
||||
timeout: options.timeout || 10000,
|
||||
json: options.format === undefined || options.format === 'json',
|
||||
}, (err, resp, body) => {
|
||||
tasks[index] = null
|
||||
if (err) return callback(err, null)
|
||||
|
||||
// console.log('---end---', url)
|
||||
callback(null, resp, body)
|
||||
}))
|
||||
return index
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* promise 形式的请求方法
|
||||
* @param {*} url
|
||||
* @param {*} options
|
||||
*/
|
||||
const buildHttpPromose = (url, options) => {
|
||||
let requestObj
|
||||
let cancelFn
|
||||
const p = new Promise((resolve, reject) => {
|
||||
cancelFn = reject
|
||||
debugRequest && console.log(`\n---send request------${url}------------`)
|
||||
requestObj = fatchData(url, options.method, options, (err, resp, body) => {
|
||||
// options.isShowProgress && window.api.hideProgress()
|
||||
debugRequest && console.log(`\n---response------${url}------------`)
|
||||
debugRequest && console.log(JSON.stringify(body))
|
||||
requestObj = null
|
||||
cancelFn = null
|
||||
if (err) {
|
||||
console.log(err.code)
|
||||
if (err.code === 'ETIMEDOUT' || err.code == 'ESOCKETTIMEDOUT') {
|
||||
const { promise, cancelHttp } = httpFatch(url, options)
|
||||
obj.cancelHttp = cancelHttp
|
||||
promise.then()
|
||||
}
|
||||
return reject(err)
|
||||
}
|
||||
resolve(resp)
|
||||
})
|
||||
})
|
||||
const obj = {
|
||||
promise: p,
|
||||
cancelHttp() {
|
||||
console.log('cancel')
|
||||
if (!requestObj) return
|
||||
cancelHttp(requestObj)
|
||||
cancelFn(new Error(requestMsg.cancelRequest))
|
||||
requestObj = null
|
||||
cancelFn = null
|
||||
},
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求超时自动重试
|
||||
* @param {*} url
|
||||
* @param {*} options
|
||||
*/
|
||||
export const httpFatch = (url, options = { method: 'get' }) => {
|
||||
const requestObj = buildHttpPromose(url, options)
|
||||
requestObj.promise = requestObj.promise.catch(err => {
|
||||
if (err.code === 'ETIMEDOUT' || err.code == 'ESOCKETTIMEDOUT') {
|
||||
const { promise, cancelHttp } = httpFatch(url, options)
|
||||
requestObj.cancelHttp()
|
||||
requestObj.cancelHttp = cancelHttp
|
||||
return promise
|
||||
}
|
||||
if (err.message === 'socket hang up') {
|
||||
// window.globalObj.apiSource = 'temp'
|
||||
return Promise.reject(new Error(requestMsg.unachievable))
|
||||
}
|
||||
if (err.code === 'ENOTFOUND') return Promise.reject(new Error(requestMsg.notConnectNetwork))
|
||||
return Promise.reject(err)
|
||||
})
|
||||
return requestObj
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消请求
|
||||
* @param {*} index
|
||||
*/
|
||||
export const cancelHttp = index => {
|
||||
if (index == null) return
|
||||
console.log('cancel: ', index)
|
||||
let r = tasks[index]
|
||||
if (r == null) return
|
||||
r.abort()
|
||||
tasks[index] = null
|
||||
}
|
||||
|
||||
|
||||
const pushTask = (tasks, newTask) => {
|
||||
for (const [index, task] of tasks.entries()) {
|
||||
if (task == null) {
|
||||
tasks[index] = newTask
|
||||
return index
|
||||
}
|
||||
}
|
||||
tasks.push(newTask)
|
||||
return tasks.length - 1
|
||||
export const cancelHttp = requestObj => {
|
||||
if (!requestObj) return
|
||||
console.log('cancel:', requestObj.href)
|
||||
requestObj.abort()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,11 @@ div(:class="$style.download")
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.nobreak(style="width: 30%;") 歌曲名
|
||||
th.nobreak(style="width: 25%;") 进度
|
||||
th.nobreak.center(style="width: 37px;")
|
||||
material-checkbox(id="search_select_all" v-model="isSelectAll" @change="handleSelectAllData"
|
||||
:indeterminate="isIndeterminate" :title="isSelectAll && !isIndeterminate ? '全不选' : '全选'")
|
||||
th.nobreak(style="width: 28%;") 歌曲名
|
||||
th.nobreak(style="width: 22%;") 进度
|
||||
th.nobreak(style="width: 15%;") 状态
|
||||
th.nobreak(style="width: 10%;") 品质
|
||||
th.nobreak(style="width: 20%;") 操作
|
||||
@@ -15,12 +18,17 @@ div(:class="$style.download")
|
||||
table
|
||||
tbody
|
||||
tr(v-for='(item, index) in list' :key='item.key' @click="handleDoubleClick(index)" :class="isPlayList && playIndex === index ? $style.active : ''")
|
||||
td.break(style="width: 30%;") {{item.musicInfo.name}} - {{item.musicInfo.singer}}
|
||||
td.break(style="width: 25%;") {{item.progress.progress}}%
|
||||
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}}
|
||||
td.break(style="width: 22%;") {{item.progress.progress}}%
|
||||
td.break(style="width: 15%;") {{item.statusText}}
|
||||
td.break(style="width: 10%;") {{item.type.toUpperCase()}}
|
||||
td(style="width: 20%; padding-left: 0; padding-right: 0;")
|
||||
material-list-buttons(:index="index" :downloadBtn="false" @btn-click="handleBtnClick")
|
||||
material-list-buttons(:index="index" :download-btn="false" :start-btn="!item.isComplate && item.status != downloadStatus.WAITING && (item.status != downloadStatus.RUN)"
|
||||
:pause-btn="!item.isComplate && (item.status == downloadStatus.RUN || item.status == downloadStatus.WAITING)"
|
||||
:play-btn="item.status == downloadStatus.COMPLETED" @btn-click="handleListBtnClick")
|
||||
material-flow-btn(:show="isShowEditBtn" :play-btn="false" :download-btn="false" :add-btn="false" :start-btn="true" :pause-btn="true" @btn-click="handleFlowBtnClick")
|
||||
div(:class="$style.noItem" v-else)
|
||||
</template>
|
||||
|
||||
@@ -34,26 +42,51 @@ export default {
|
||||
return {
|
||||
clickTime: window.performance.now(),
|
||||
clickIndex: -1,
|
||||
selectdData: [],
|
||||
isSelectAll: false,
|
||||
isIndeterminate: false,
|
||||
isShowEditBtn: false,
|
||||
isShowDownloadMultiple: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('download', ['list', 'dls']),
|
||||
...mapGetters('download', ['list', 'dls', 'downloadStatus']),
|
||||
...mapGetters('player', ['listId', 'playIndex']),
|
||||
isPlayList() {
|
||||
return this.listId == 'download'
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions('download', ['removeTask', 'resumeTask']),
|
||||
...mapMutations('player', ['setList']),
|
||||
pauseTask(index) {
|
||||
let info = this.list[index]
|
||||
this.dls[info.key].pause()
|
||||
watch: {
|
||||
selectdData(n) {
|
||||
const len = n.length
|
||||
if (len) {
|
||||
this.isSelectAll = true
|
||||
this.isIndeterminate = len !== this.list.length
|
||||
this.isShowEditBtn = true
|
||||
} else {
|
||||
this.isSelectAll = false
|
||||
this.isShowEditBtn = false
|
||||
}
|
||||
},
|
||||
startTask(index) {
|
||||
list() {
|
||||
this.resetSelect()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions('download', ['removeTask', 'removeTaskMultiple', 'startTask']),
|
||||
...mapMutations('player', ['setList']),
|
||||
...mapMutations('download', ['pauseTask']),
|
||||
handlePauseTask(index) {
|
||||
let info = this.list[index]
|
||||
let dl = this.dls[info.key]
|
||||
dl ? dl.resume() : this.resumeTask(info)
|
||||
dl ? dl.pause() : 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)
|
||||
},
|
||||
handleDoubleClick(index) {
|
||||
if (
|
||||
@@ -72,26 +105,71 @@ export default {
|
||||
let info = this.list[index]
|
||||
if (info.isComplate) {
|
||||
this.handlePlay(index)
|
||||
} else if (info.isDownloading) {
|
||||
this.pauseTask(index)
|
||||
} else if (info.status === this.downloadStatus.RUN) {
|
||||
this.handlePauseTask(index)
|
||||
} else {
|
||||
this.startTask(index)
|
||||
this.handleStartTask(index)
|
||||
}
|
||||
},
|
||||
handlePlay(index) {
|
||||
if (!checkPath(this.list[index].filePath)) return
|
||||
this.setList({ list: this.list, listId: 'download', index })
|
||||
},
|
||||
handleBtnClick(info) {
|
||||
handleListBtnClick(info) {
|
||||
switch (info.action) {
|
||||
case 'play':
|
||||
this.handlePlay(info.index)
|
||||
break
|
||||
case 'start':
|
||||
this.handleStartTask(info.index)
|
||||
break
|
||||
case 'pause':
|
||||
this.handlePauseTask(info.index)
|
||||
break
|
||||
case 'remove':
|
||||
this.removeTask(info.index)
|
||||
break
|
||||
}
|
||||
},
|
||||
handleSelectAllData(isSelect) {
|
||||
this.selectdData = isSelect ? [...this.list] : []
|
||||
},
|
||||
resetSelect() {
|
||||
this.isSelectAll = false
|
||||
this.selectdData = []
|
||||
},
|
||||
handleFlowBtnClick(action) {
|
||||
switch (action) {
|
||||
case 'start':
|
||||
this.selectdData.forEach(item => {
|
||||
if (item.isComplate || item.status == this.downloadStatus.RUN) return
|
||||
let index = this.list.indexOf(item)
|
||||
if (index < 0) return
|
||||
this.handleStartTask(index)
|
||||
})
|
||||
break
|
||||
case 'pause':
|
||||
let runs = []
|
||||
this.selectdData.forEach(item => {
|
||||
if (item.isComplate || item.status == this.downloadStatus.PAUSE) return
|
||||
if (item.status == this.downloadStatus.RUN) return runs.push(item)
|
||||
let index = this.list.indexOf(item)
|
||||
if (index < 0) return
|
||||
this.handlePauseTask(index)
|
||||
})
|
||||
runs.forEach(item => {
|
||||
if (item.isComplate || item.status == this.downloadStatus.PAUSE) return
|
||||
let index = this.list.indexOf(item)
|
||||
if (index < 0) return
|
||||
this.handlePauseTask(index)
|
||||
})
|
||||
break
|
||||
case 'remove':
|
||||
this.removeTaskMultiple(this.selectdData)
|
||||
break
|
||||
}
|
||||
this.resetSelect()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -9,30 +9,37 @@
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.nobreak(style="width: 27%;") 歌曲名
|
||||
th.nobreak.center(style="width: 37px;")
|
||||
material-checkbox(id="search_select_all" v-model="isSelectAll" @change="handleSelectAllData"
|
||||
:indeterminate="isIndeterminate" :title="isSelectAll && !isIndeterminate ? '全不选' : '全选'")
|
||||
th.nobreak(style="width: 25%;") 歌曲名
|
||||
th.nobreak(style="width: 20%;") 歌手
|
||||
th.nobreak(style="width: 25%;") 专辑
|
||||
th.nobreak(style="width: 22%;") 专辑
|
||||
th.nobreak(style="width: 18%;") 操作
|
||||
th.nobreak(style="width: 10%;") 时长
|
||||
div.scroll(:class="$style.tbody" ref="dom_scrollContent")
|
||||
table
|
||||
tbody
|
||||
tr(v-for='(item, index) in list' :key='item.songmid' @click="handleDoubleClick(index)")
|
||||
td.break(style="width: 27%;")
|
||||
td.nobreak.center(style="width: 37px;" @click.stop)
|
||||
material-checkbox(:id="index.toString()" v-model="selectdData" :value="item")
|
||||
td.break(style="width: 25%;")
|
||||
| {{item.name}}
|
||||
//- span.badge.badge-info(v-if="item._types['320k']") 高品质
|
||||
//- span.badge.badge-success(v-if="item._types.ape || item._types.flac") 无损
|
||||
td.break(style="width: 20%;") {{item.singer}}
|
||||
td.break(style="width: 25%;") {{item.albumName}}
|
||||
td.break(style="width: 22%;") {{item.albumName}}
|
||||
td(style="width: 18%;")
|
||||
material-list-buttons(:index="index" :search-btn="true" :play-btn="item.source == 'kw'" :download-btn="item.source == 'kw'" :remove-btn="false" @btn-click="handleBtnClick")
|
||||
material-list-buttons(:index="index" :search-btn="true" :play-btn="item.source == 'kw' || !isAPITemp" :download-btn="item.source == 'kw' || !isAPITemp" :remove-btn="false" @btn-click="handleListBtnClick")
|
||||
//- 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)') +
|
||||
td(style="width: 10%;") {{item.interval}}
|
||||
td(style="width: 10%;") {{item.interval || '--/--'}}
|
||||
div(:class="$style.pagination")
|
||||
material-pagination(:count="info.total" :limit="info.limit" :page="info.page" @btn-click="handleTogglePage")
|
||||
material-download-modal(:show="showDownload" :musicInfo="musicInfo" @select="handleAddDownload" @close="showDownload = false")
|
||||
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 && (source == 'kw' || !isAPITemp)" :remove-btn="false" @btn-click="handleFlowBtnClick")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -48,8 +55,13 @@ export default {
|
||||
page: 1,
|
||||
clickTime: 0,
|
||||
clickIndex: -1,
|
||||
showDownload: false,
|
||||
isShowDownload: false,
|
||||
musicInfo: null,
|
||||
selectdData: [],
|
||||
isSelectAll: false,
|
||||
isIndeterminate: false,
|
||||
isShowEditBtn: false,
|
||||
isShowDownloadMultiple: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -59,6 +71,9 @@ export default {
|
||||
types() {
|
||||
return this.source ? this.sourceInfo.sourceList[this.source] : []
|
||||
},
|
||||
isAPITemp() {
|
||||
return this.setting.apiSource == 'temp'
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
tabId(n, o) {
|
||||
@@ -73,6 +88,20 @@ export default {
|
||||
this.setLeaderboard({ source: n })
|
||||
if (o) this.tabId = this.types[0] && this.types[0].id
|
||||
},
|
||||
selectdData(n) {
|
||||
const len = n.length
|
||||
if (len) {
|
||||
this.isSelectAll = true
|
||||
this.isIndeterminate = len !== this.list.length
|
||||
this.isShowEditBtn = true
|
||||
} else {
|
||||
this.isSelectAll = false
|
||||
this.isShowEditBtn = false
|
||||
}
|
||||
},
|
||||
list() {
|
||||
this.resetSelect()
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.source = this.setting.leaderboard.source
|
||||
@@ -82,8 +111,8 @@ export default {
|
||||
methods: {
|
||||
...mapMutations(['setLeaderboard']),
|
||||
...mapActions('leaderboard', ['getList']),
|
||||
...mapActions('download', ['createDownload']),
|
||||
...mapMutations('list', ['defaultListAdd']),
|
||||
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
|
||||
...mapMutations('list', ['defaultListAdd', 'defaultListAddMultiple']),
|
||||
...mapMutations('player', ['setList']),
|
||||
handleDoubleClick(index) {
|
||||
if (
|
||||
@@ -94,16 +123,16 @@ export default {
|
||||
this.clickIndex = index
|
||||
return
|
||||
}
|
||||
this.source == 'kw' ? this.testPlay(index) : this.handleSearch(index)
|
||||
(this.source == 'kw' || !this.isAPITemp) ? this.testPlay(index) : this.handleSearch(index)
|
||||
this.clickTime = 0
|
||||
this.clickIndex = -1
|
||||
},
|
||||
handleBtnClick(info) {
|
||||
handleListBtnClick(info) {
|
||||
switch (info.action) {
|
||||
case 'download':
|
||||
this.musicInfo = this.list[info.index]
|
||||
this.$nextTick(() => {
|
||||
this.showDownload = true
|
||||
this.isShowDownload = true
|
||||
})
|
||||
break
|
||||
case 'play':
|
||||
@@ -117,8 +146,14 @@ export default {
|
||||
}
|
||||
},
|
||||
testPlay(index) {
|
||||
let targetSong = this.list[index]
|
||||
this.defaultListAdd(targetSong)
|
||||
let targetSong
|
||||
if (index == null) {
|
||||
targetSong = this.selectdData[0]
|
||||
this.defaultListAddMultiple(this.selectdData)
|
||||
} else {
|
||||
targetSong = this.list[index]
|
||||
this.defaultListAdd(targetSong)
|
||||
}
|
||||
let targetIndex = this.defaultList.list.findIndex(
|
||||
s => s.songmid === targetSong.songmid
|
||||
)
|
||||
@@ -135,7 +170,7 @@ export default {
|
||||
this.$router.push({
|
||||
path: 'search',
|
||||
query: {
|
||||
text: `${info.name} - ${info.singer}`,
|
||||
text: `${info.name} ${info.singer}`,
|
||||
},
|
||||
})
|
||||
},
|
||||
@@ -147,9 +182,41 @@ export default {
|
||||
})
|
||||
})
|
||||
},
|
||||
handleSelectAllData(isSelect) {
|
||||
this.selectdData = isSelect ? [...this.list] : []
|
||||
},
|
||||
resetSelect() {
|
||||
this.isSelectAll = false
|
||||
this.selectdData = []
|
||||
},
|
||||
handleAddDownload(type) {
|
||||
this.createDownload({ musicInfo: this.musicInfo, type })
|
||||
this.showDownload = false
|
||||
this.isShowDownload = false
|
||||
},
|
||||
handleAddDownloadMultiple(type) {
|
||||
switch (this.source) {
|
||||
// case 'kg':
|
||||
case 'wy':
|
||||
type = '128k'
|
||||
}
|
||||
this.createDownloadMultiple({ list: [...this.selectdData], type })
|
||||
this.resetSelect()
|
||||
this.isShowDownloadMultiple = false
|
||||
},
|
||||
handleFlowBtnClick(action) {
|
||||
switch (action) {
|
||||
case 'download':
|
||||
this.isShowDownloadMultiple = true
|
||||
break
|
||||
case 'play':
|
||||
this.testPlay()
|
||||
this.resetSelect()
|
||||
break
|
||||
case 'add':
|
||||
this.defaultListAddMultiple(this.selectdData)
|
||||
this.resetSelect()
|
||||
break
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,15 +6,21 @@
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.nobreak.center(style="width: 37px;")
|
||||
material-checkbox(id="search_select_all" v-model="isSelectAll" @change="handleSelectAllData"
|
||||
:indeterminate="isIndeterminate" :title="isSelectAll && !isIndeterminate ? '全不选' : '全选'")
|
||||
th.nobreak(style="width: 25%;") 歌曲名
|
||||
th.nobreak(style="width: 20%;") 歌手
|
||||
th.nobreak(style="width: 25%;" v-if="setting.list.isShowAlbumName") 专辑
|
||||
th.nobreak(style="width: 20%;") 专辑
|
||||
th.nobreak(style="width: 20%;") 操作
|
||||
th.nobreak(style="width: 10%;") 时长
|
||||
div.scroll(:class="$style.tbody")
|
||||
table
|
||||
tbody
|
||||
tr(v-for='(item, index) in list' :key='item.songmid' @click="handleDoubleClick(index)" :class="isPlayList && playIndex === index ? $style.active : ''")
|
||||
tr(v-for='(item, index) in list' :key='item.songmid'
|
||||
@click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', isAPITemp && item.source != 'kw' ? $style.disabled : '']")
|
||||
td.nobreak.center(style="width: 37px;" @click.stop)
|
||||
material-checkbox(:id="index.toString()" v-model="selectdData" :value="item")
|
||||
td.break(style="width: 25%;") {{item.name}}
|
||||
//- span.badge.badge-light(v-if="item._types['128k']") 128K
|
||||
//- span.badge.badge-light(v-if="item._types['192k']") 192K
|
||||
@@ -22,16 +28,18 @@
|
||||
//- span.badge.badge-info(v-if="item._types.ape") APE
|
||||
//- span.badge.badge-success(v-if="item._types.flac") FLAC
|
||||
td.break(style="width: 20%;") {{item.singer}}
|
||||
td.break(style="width: 25%;" v-if="setting.list.isShowAlbumName") {{item.albumName}}
|
||||
td.break(style="width: 20%;") {{item.albumName}}
|
||||
td(style="width: 20%; padding-left: 0; padding-right: 0;")
|
||||
material-list-buttons(:index="index" @btn-click="handleBtnClick")
|
||||
material-list-buttons(:index="index" @btn-click="handleListBtnClick")
|
||||
//- 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-secondary(type='button' @click.stop='handleRemove(index)') 删除
|
||||
//- button.btn-success(type='button' v-if="(item._types['128k'] || item._types['192k'] || item._types['320k']) && userInfo" @click.stop='showListModal(index)') +
|
||||
td(style="width: 10%;") {{item.interval}}
|
||||
td(style="width: 10%;") {{item.interval || '--/--'}}
|
||||
div(:class="$style.noItem" v-else)
|
||||
material-download-modal(:show="showDownload" :musicInfo="musicInfo" @select="handleAddDownload" @close="showDownload = false")
|
||||
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" :add-btn="false" :play-btn="false" @btn-click="handleFlowBtnClick")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -42,8 +50,13 @@ export default {
|
||||
return {
|
||||
clickTime: window.performance.now(),
|
||||
clickIndex: -1,
|
||||
showDownload: false,
|
||||
isShowDownload: false,
|
||||
musicInfo: null,
|
||||
selectdData: [],
|
||||
isSelectAll: false,
|
||||
isIndeterminate: false,
|
||||
isShowEditBtn: false,
|
||||
isShowDownloadMultiple: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -61,6 +74,25 @@ export default {
|
||||
? this.defaultList.list
|
||||
: this.userList.find(l => l._id == this.$route.query.id) || []
|
||||
},
|
||||
isAPITemp() {
|
||||
return this.setting.apiSource == 'temp'
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectdData(n) {
|
||||
const len = n.length
|
||||
if (len) {
|
||||
this.isSelectAll = true
|
||||
this.isIndeterminate = len !== this.list.length
|
||||
this.isShowEditBtn = true
|
||||
} else {
|
||||
this.isSelectAll = false
|
||||
this.isShowEditBtn = false
|
||||
}
|
||||
},
|
||||
list() {
|
||||
this.resetSelect()
|
||||
},
|
||||
},
|
||||
// beforeRouteUpdate(to, from, next) {
|
||||
// // if (to.query.id === undefined) return
|
||||
@@ -88,9 +120,9 @@ export default {
|
||||
// }
|
||||
// },
|
||||
methods: {
|
||||
...mapMutations('list', ['defaultListRemove']),
|
||||
...mapMutations('list', ['defaultListRemove', 'defaultListRemoveMultiple']),
|
||||
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
|
||||
...mapMutations('player', ['setList']),
|
||||
...mapActions('download', ['createDownload']),
|
||||
handleDoubleClick(index) {
|
||||
if (
|
||||
window.performance.now() - this.clickTime > 400 ||
|
||||
@@ -110,19 +142,20 @@ export default {
|
||||
handleRemove(index) {
|
||||
this.defaultListRemove(index)
|
||||
},
|
||||
handleBtnClick(info) {
|
||||
handleListBtnClick(info) {
|
||||
switch (info.action) {
|
||||
case 'download':
|
||||
this.musicInfo = this.list[info.index]
|
||||
const minfo = this.list[info.index]
|
||||
if (this.isAPITemp && minfo.source != 'kw') return
|
||||
this.musicInfo = minfo
|
||||
this.$nextTick(() => {
|
||||
this.showDownload = true
|
||||
this.isShowDownload = true
|
||||
})
|
||||
break
|
||||
case 'play':
|
||||
if (this.isAPITemp && this.list[info.index].source != 'kw') return
|
||||
this.testPlay(info.index)
|
||||
break
|
||||
case 'add':
|
||||
break
|
||||
case 'remove':
|
||||
this.handleRemove(info.index)
|
||||
break
|
||||
@@ -130,7 +163,31 @@ export default {
|
||||
},
|
||||
handleAddDownload(type) {
|
||||
this.createDownload({ musicInfo: this.musicInfo, type })
|
||||
this.showDownload = false
|
||||
this.isShowDownload = false
|
||||
},
|
||||
handleSelectAllData(isSelect) {
|
||||
this.selectdData = isSelect ? [...this.list] : []
|
||||
},
|
||||
resetSelect() {
|
||||
this.isSelectAll = false
|
||||
this.selectdData = []
|
||||
},
|
||||
handleAddDownloadMultiple(type) {
|
||||
const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : [...this.selectdData]
|
||||
this.createDownloadMultiple({ list, type })
|
||||
this.resetSelect()
|
||||
this.isShowDownloadMultiple = false
|
||||
},
|
||||
handleFlowBtnClick(action) {
|
||||
switch (action) {
|
||||
case 'download':
|
||||
this.isShowDownloadMultiple = true
|
||||
break
|
||||
case 'remove':
|
||||
this.defaultListRemoveMultiple(this.selectdData)
|
||||
this.resetSelect()
|
||||
break
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -178,6 +235,10 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
.tbody {
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.nobreak(style="width: 30%;") 歌曲名
|
||||
th.nobreak.center(style="width: 37px;")
|
||||
material-checkbox(id="search_select_all" v-model="isSelectAll" @change="handleSelectAllData"
|
||||
:indeterminate="isIndeterminate" :title="isSelectAll && !isIndeterminate ? '全不选' : '全选'")
|
||||
th.nobreak(style="width: 25%;") 歌曲名
|
||||
th.nobreak(style="width: 20%;") 歌手
|
||||
th.nobreak(style="width: 25%;") 专辑
|
||||
th.nobreak(style="width: 15%;") 操作
|
||||
@@ -15,19 +18,24 @@
|
||||
table
|
||||
tbody
|
||||
tr(v-for='(item, index) in list' :key='item.songmid' @click="handleDoubleClick(index)")
|
||||
td.break(style="width: 30%;")
|
||||
td.nobreak.center(style="width: 37px;" @click.stop)
|
||||
material-checkbox(:id="index.toString()" v-model="selectdData" :value="item")
|
||||
td.break(style="width: 25%;")
|
||||
| {{item.name}}
|
||||
span.badge.badge-info(v-if="item._types['320k']") 高品质
|
||||
span.badge.badge-success(v-if="item._types.ape || item._types.flac") 无损
|
||||
td.break(style="width: 20%;") {{item.singer}}
|
||||
td.break(style="width: 25%;") {{item.albumName}}
|
||||
td(style="width: 15%;")
|
||||
material-list-buttons(:index="index" :remove-btn="false" @btn-click="handleBtnClick")
|
||||
material-list-buttons(:index="index" :remove-btn="false" @btn-click="handleListBtnClick")
|
||||
td(style="width: 10%;") {{item.interval}}
|
||||
div(:class="$style.pagination")
|
||||
material-pagination(:count="listInfo.total" :limit="limit" :page="page" @btn-click="handleTogglePage")
|
||||
div(v-else :class="$style.noItem")
|
||||
material-download-modal(:show="showDownload" :musicInfo="musicInfo" @select="handleAddDownload" @close="showDownload = false")
|
||||
div(v-else :class="$style.noitem")
|
||||
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" :remove-btn="false" @btn-click="handleFlowBtnClick")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -42,8 +50,13 @@ export default {
|
||||
text: '',
|
||||
clickTime: 0,
|
||||
clickIndex: -1,
|
||||
showDownload: false,
|
||||
isShowDownload: false,
|
||||
musicInfo: null,
|
||||
selectdData: [],
|
||||
isSelectAll: false,
|
||||
isIndeterminate: false,
|
||||
isShowEditBtn: false,
|
||||
isShowDownloadMultiple: false,
|
||||
}
|
||||
},
|
||||
beforeRouteUpdate(to, from, next) {
|
||||
@@ -71,6 +84,22 @@ export default {
|
||||
this.handleSearch(this.text, this.page)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectdData(n) {
|
||||
const len = n.length
|
||||
if (len) {
|
||||
this.isSelectAll = true
|
||||
this.isIndeterminate = len !== this.list.length
|
||||
this.isShowEditBtn = true
|
||||
} else {
|
||||
this.isSelectAll = false
|
||||
this.isShowEditBtn = false
|
||||
}
|
||||
},
|
||||
list() {
|
||||
this.resetSelect()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo']),
|
||||
...mapGetters('search', ['list', 'limit', 'listInfo']),
|
||||
@@ -78,9 +107,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
...mapActions('search', ['search']),
|
||||
...mapActions('download', ['createDownload']),
|
||||
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
|
||||
...mapMutations('search', ['clearList', 'setPage']),
|
||||
...mapMutations('list', ['defaultListAdd']),
|
||||
...mapMutations('list', ['defaultListAdd', 'defaultListAddMultiple']),
|
||||
...mapMutations('player', ['setList']),
|
||||
handleSearch(text, page) {
|
||||
this.search({ text, page, limit: this.limit }).then(data => {
|
||||
@@ -103,12 +132,12 @@ export default {
|
||||
this.clickTime = 0
|
||||
this.clickIndex = -1
|
||||
},
|
||||
handleBtnClick(info) {
|
||||
handleListBtnClick(info) {
|
||||
switch (info.action) {
|
||||
case 'download':
|
||||
this.musicInfo = this.list[info.index]
|
||||
this.$nextTick(() => {
|
||||
this.showDownload = true
|
||||
this.isShowDownload = true
|
||||
})
|
||||
break
|
||||
case 'play':
|
||||
@@ -118,10 +147,15 @@ export default {
|
||||
break
|
||||
}
|
||||
},
|
||||
openDownloadModal() {},
|
||||
testPlay(index) {
|
||||
let targetSong = this.list[index]
|
||||
this.defaultListAdd(targetSong)
|
||||
let targetSong
|
||||
if (index == null) {
|
||||
targetSong = this.selectdData[0]
|
||||
this.defaultListAddMultiple(this.selectdData)
|
||||
} else {
|
||||
targetSong = this.list[index]
|
||||
this.defaultListAdd(targetSong)
|
||||
}
|
||||
let targetIndex = this.defaultList.list.findIndex(
|
||||
s => s.songmid === targetSong.songmid
|
||||
)
|
||||
@@ -138,7 +172,34 @@ export default {
|
||||
},
|
||||
handleAddDownload(type) {
|
||||
this.createDownload({ musicInfo: this.musicInfo, type })
|
||||
this.showDownload = false
|
||||
this.isShowDownload = false
|
||||
},
|
||||
handleAddDownloadMultiple(type) {
|
||||
this.createDownloadMultiple({ list: [...this.selectdData], type })
|
||||
this.resetSelect()
|
||||
this.isShowDownloadMultiple = false
|
||||
},
|
||||
handleSelectAllData(isSelect) {
|
||||
this.selectdData = isSelect ? [...this.list] : []
|
||||
},
|
||||
resetSelect() {
|
||||
this.isSelectAll = false
|
||||
this.selectdData = []
|
||||
},
|
||||
handleFlowBtnClick(action) {
|
||||
switch (action) {
|
||||
case 'download':
|
||||
this.isShowDownloadMultiple = true
|
||||
break
|
||||
case 'play':
|
||||
this.testPlay()
|
||||
this.resetSelect()
|
||||
break
|
||||
case 'add':
|
||||
this.defaultListAddMultiple(this.selectdData)
|
||||
this.resetSelect()
|
||||
break
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -183,7 +244,17 @@ export default {
|
||||
// left: 50%;
|
||||
// transform: translateX(-50%);
|
||||
}
|
||||
// .noItem {
|
||||
.noitem {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
// }
|
||||
p {
|
||||
font-size: 24px;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,18 +13,28 @@ div.scroll(:class="$style.setting")
|
||||
dd(title='弹出层的动画效果')
|
||||
h3 弹出层随机动画
|
||||
div
|
||||
material-checkbox(id="setting_animate" v-model="current_setting.randomAnimate") 是否启用
|
||||
material-checkbox(id="setting_animate" v-model="current_setting.randomAnimate" label="是否启用")
|
||||
|
||||
dd(title='选择音乐来源')
|
||||
h3 音乐来源
|
||||
div
|
||||
material-checkbox(v-for="item in apiSources" :id="`setting_api_source_${item.id}`" @change="handleAPISourceChange(item.id)" :class="$style.gapTop"
|
||||
need v-model="current_setting.apiSource" :disabled="item.disabled" :value="item.id" :label="item.label" :key="item.id")
|
||||
|
||||
dt 播放设置
|
||||
dd(title="都不选时播放完当前歌曲就停止播放")
|
||||
h3 歌曲切换方式
|
||||
div
|
||||
material-checkbox(:id="`setting_player_togglePlay_${item.value}`" :class="$style.gap" :target="item.value" :key="item.value"
|
||||
v-model="current_setting.player.togglePlayMethod" v-for="item in togglePlayMethods") {{item.name}}
|
||||
material-checkbox(:id="`setting_player_togglePlay_${item.value}`" :class="$style.gapLeft" :value="item.value" :key="item.value"
|
||||
v-model="current_setting.player.togglePlayMethod" v-for="item in togglePlayMethods" :label="item.name")
|
||||
dd(title='启用时将优先播放320K品质的歌曲')
|
||||
h3 优先播放高品质音乐
|
||||
div
|
||||
material-checkbox(id="setting_player_highQuality" v-model="current_setting.player.highQuality") 是否启用
|
||||
material-checkbox(id="setting_player_highQuality" v-model="current_setting.player.highQuality" label="是否启用")
|
||||
dd(title='在任务栏上显示当前歌曲播放进度')
|
||||
h3 是否启用任务栏播放进度条
|
||||
div
|
||||
material-checkbox(id="setting_player_showTaskProgess" v-model="current_setting.player.isShowTaskProgess" label="是否启用")
|
||||
dt 下载设置
|
||||
dd(title='下载歌曲保存的路径')
|
||||
h3 下载路径
|
||||
@@ -37,64 +47,80 @@ div.scroll(:class="$style.setting")
|
||||
dd(title='下载歌曲时的命名方式')
|
||||
h3 文件命名方式
|
||||
div
|
||||
material-checkbox(:id="`setting_download_musicName_${item.value}`" :class="$style.gap" name="setting_download_musicName" :target="item.value" :key="item.value" need
|
||||
v-model="current_setting.download.fileName" v-for="item in musicNames") {{item.name}}
|
||||
dt 列表设置
|
||||
dd(title='播放列表是否显示专辑栏')
|
||||
material-checkbox(:id="`setting_download_musicName_${item.value}`" :class="$style.gapLeft" name="setting_download_musicName" :value="item.value" :key="item.value" need
|
||||
v-model="current_setting.download.fileName" v-for="item in musicNames" :label="item.name")
|
||||
//- dt 列表设置
|
||||
//- dd(title='播放列表是否显示专辑栏')
|
||||
h3 专辑栏
|
||||
div
|
||||
material-checkbox(id="setting_list_showalbum" v-model="current_setting.list.isShowAlbumName") 是否显示专辑栏
|
||||
material-checkbox(id="setting_list_showalbum" v-model="current_setting.list.isShowAlbumName" label="是否显示专辑栏")
|
||||
dt 备份与恢复
|
||||
dd
|
||||
h3 部分数据
|
||||
div
|
||||
material-btn(:class="[$style.btn, $style.gap]" min @click="handleImportPlayList") 导入列表
|
||||
material-btn(:class="[$style.btn, $style.gap]" min @click="handleExportPlayList") 导出列表
|
||||
material-btn(:class="[$style.btn, $style.gap]" min @click="handleImportSetting") 导入设置
|
||||
material-btn(:class="[$style.btn, $style.gap]" min @click="handleExportSetting") 导出设置
|
||||
material-btn(:class="[$style.btn, $style.gapLeft]" min @click="handleImportPlayList") 导入列表
|
||||
material-btn(:class="[$style.btn, $style.gapLeft]" min @click="handleExportPlayList") 导出列表
|
||||
material-btn(:class="[$style.btn, $style.gapLeft]" min @click="handleImportSetting") 导入设置
|
||||
material-btn(:class="[$style.btn, $style.gapLeft]" min @click="handleExportSetting") 导出设置
|
||||
dd
|
||||
h3 所有数据(设置与试听列表)
|
||||
div
|
||||
material-btn(:class="[$style.btn, $style.gap]" min @click="handleImportAllData") 导入
|
||||
material-btn(:class="[$style.btn, $style.gap]" min @click="handleExportAllData") 导出
|
||||
material-btn(:class="[$style.btn, $style.gapLeft]" min @click="handleImportAllData") 导入
|
||||
material-btn(:class="[$style.btn, $style.gapLeft]" min @click="handleExportAllData") 导出
|
||||
dt 软件更新
|
||||
dd
|
||||
p.small
|
||||
| 最新版本:{{version.newVersion ? version.newVersion.version : '未知'}}
|
||||
p.small 当前版本:{{version.version}}
|
||||
p.small(v-if="version.newVersion")
|
||||
span(v-if="isLatestVer") 软件已是最新,尽情地体验吧~🥂
|
||||
material-btn(v-else-if="setting.ignoreVersion" :class="[$style.btn, $style.gapLeft]" min @click="showUpdateModal") 打开更新窗口
|
||||
span(v-else) 发现新版本并在努力下载中,请稍等...⏳
|
||||
p.small(v-else) 检查更新中...
|
||||
dt 关于洛雪音乐
|
||||
dd
|
||||
p
|
||||
small 当前版本:
|
||||
| {{version.version}}
|
||||
p
|
||||
small By:
|
||||
| 落雪无痕
|
||||
p
|
||||
small
|
||||
| 本软件仅用于学习交流使用,禁止将本软件用于
|
||||
strong 非法用途
|
||||
| 或
|
||||
strong 商业用途
|
||||
| 。
|
||||
p
|
||||
small
|
||||
p.small
|
||||
| 本软件完全免费,代码已开源,开源地址:
|
||||
span.hover(@click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop')") https://github.com/lyswhut/lx-music-desktop
|
||||
p.small
|
||||
| 本软件仅用于学习交流使用,禁止将本软件用于
|
||||
strong 非法用途
|
||||
| 或
|
||||
strong 商业用途
|
||||
| 。
|
||||
p.small
|
||||
| 使用本软件造成的一切后果由
|
||||
strong 使用者
|
||||
| 承担!
|
||||
p
|
||||
small
|
||||
p.small
|
||||
| 本软件的部分接口使用自 https://github.com/messoer ,非常感谢
|
||||
strong @messoer
|
||||
| !
|
||||
p.small 若有问题可 mail to:lyswhut@qq.com 或到 GitHub 提交 issue
|
||||
p.small
|
||||
| 若觉得好用的话可以去 GitHub 点个
|
||||
strong star
|
||||
| 支持作者哦~~🍻
|
||||
p
|
||||
small 若有问题可 mail to:lyswhut@qq.com
|
||||
small By:
|
||||
| 落雪无痕
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import { openDirInExplorer, openSelectDir, openSaveDir, updateSetting } from '../utils'
|
||||
import { openDirInExplorer, openSelectDir, openSaveDir, updateSetting, openUrl } from '../utils'
|
||||
import { rendererSend } from '../../common/icp'
|
||||
import fs from 'fs'
|
||||
|
||||
|
||||
export default {
|
||||
name: 'Setting',
|
||||
computed: {
|
||||
...mapGetters(['setting', 'themes', 'version']),
|
||||
...mapGetters('list', ['defaultList']),
|
||||
isLatestVer() {
|
||||
return this.version.newVersion && this.version.version === this.version.newVersion.version
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -103,17 +129,19 @@ export default {
|
||||
player: {
|
||||
togglePlayMethod: 'random',
|
||||
highQuality: false,
|
||||
isShowTaskProgess: true,
|
||||
},
|
||||
list: {
|
||||
isShowAlbumName: true,
|
||||
},
|
||||
download: {
|
||||
savePath: 'C:\\',
|
||||
savePath: '',
|
||||
fileName: '歌名 - 歌手',
|
||||
},
|
||||
themeId: 0,
|
||||
sourceId: 0,
|
||||
randomAnimate: true,
|
||||
apiSource: 'messoer',
|
||||
},
|
||||
togglePlayMethods: [
|
||||
{
|
||||
@@ -133,6 +161,19 @@ export default {
|
||||
value: 'singleLoop',
|
||||
},
|
||||
],
|
||||
apiSources: [
|
||||
{
|
||||
id: 'messoer',
|
||||
label: '由 messoer 提供的接口(推荐,软件的所有功能都可用)',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: 'temp',
|
||||
// label: '临时接口(软件的某些功能将不可用,建议在messoer不可用时再切换到本选项)',
|
||||
label: '临时接口(因服务器被攻击,本接口已关闭)',
|
||||
disabled: true,
|
||||
},
|
||||
],
|
||||
musicNames: [
|
||||
{
|
||||
name: '歌名 - 歌手',
|
||||
@@ -157,12 +198,21 @@ export default {
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
'current_setting.player.isShowTaskProgess'(n) {
|
||||
if (n) return
|
||||
this.$nextTick(() => {
|
||||
rendererSend('progress', {
|
||||
status: -1,
|
||||
mode: 'normal',
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['setSetting']),
|
||||
...mapMutations(['setSetting', 'setVersionModalVisible']),
|
||||
...mapMutations('list', ['setDefaultList']),
|
||||
init() {
|
||||
this.current_setting = JSON.parse(JSON.stringify(this.setting))
|
||||
@@ -308,6 +358,17 @@ export default {
|
||||
this.exportPlayList(result.filePath)
|
||||
})
|
||||
},
|
||||
handleOpenUrl(url) {
|
||||
openUrl(url)
|
||||
},
|
||||
handleAPISourceChange(id) {
|
||||
this.$nextTick(() => {
|
||||
window.globalObj.apiSource = id
|
||||
})
|
||||
},
|
||||
showUpdateModal() {
|
||||
this.setVersionModalVisible({ isShow: true })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -345,7 +406,7 @@ export default {
|
||||
margin: 25px 0 15px;
|
||||
}
|
||||
p {
|
||||
padding: 3px 0;
|
||||
padding: 5px 0;
|
||||
.btn {
|
||||
+ .btn {
|
||||
margin-left: 10px;
|
||||
@@ -355,11 +416,16 @@ export default {
|
||||
|
||||
}
|
||||
|
||||
.gap {
|
||||
+ .gap {
|
||||
.gap-left {
|
||||
+ .gap-left {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
.gap-top {
|
||||
+ .gap-top {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.theme {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user