Compare commits

...

27 Commits

Author SHA1 Message Date
lyswhut
fddfaf23ce 更新配置 2019-11-02 11:51:49 +08:00
lyswhut
ae0ee2048a 新增nodejs版本输出 2019-11-02 11:42:31 +08:00
lyswhut
6659d1fe43 更新nodejs版本配置 2019-11-02 11:22:27 +08:00
lyswhut
f601e61f8d 发布0.10.0版本 2019-11-02 11:00:21 +08:00
lyswhut
f8ed7a112a 更新依赖到最新,更新electron到7.0.1 2019-11-02 10:59:12 +08:00
lyswhut
b0d6181bd7 完善token获取机制 2019-10-31 20:26:53 +08:00
lyswhut
82843b14b4 更新依赖 2019-10-31 00:42:48 +08:00
lyswhut
0df3ca3c5f 新增音乐sdk初始化方法 2019-10-30 22:29:14 +08:00
lyswhut
c881f8e886 更新描述 2019-10-30 01:43:27 +08:00
lyswhut
f1bf274de1 修复咪咕源无法播放的问题 2019-10-29 13:17:06 +08:00
lyswhut
91e4e9c2d5 简化写法 2019-10-28 22:48:30 +08:00
lyswhut
63a5f94a67 修复酷我源搜索提示、排行榜接口挂掉的问题 2019-10-28 22:42:01 +08:00
lyswhut
1bd8694262 减少播放时对CPU与GPU的使用 2019-10-27 17:35:29 +08:00
lyswhut
501231adff 更新依赖到最新,更新electron到7.0.0 2019-10-27 17:34:11 +08:00
lyswhut
3a033b66c4 发布0.9.1版本 2019-10-27 12:00:04 +08:00
lyswhut
46aacba037 修复没有配置文件时程序启动出错的问题 2019-10-27 11:58:55 +08:00
lyswhut
10ef80da75 发布0.9.0版本 2019-10-27 10:54:16 +08:00
lyswhut
75b7897ad6 更新安装版安装失败问题解决方法 2019-10-27 10:47:13 +08:00
lyswhut
14423e0eb3 修复搜索框bug 2019-10-27 10:00:43 +08:00
lyswhut
fe44581a61 修复搜索提示失效的问题 2019-10-25 21:36:07 +08:00
lyswhut
0d612bd6df 修改滚动高度 2019-10-24 15:16:59 +08:00
lyswhut
b7eba95ab2 新增定位当前播放歌曲 2019-10-24 14:04:44 +08:00
lyswhut
59091a5ea9 更改URL重试刷新次数 2019-10-22 01:36:25 +08:00
lyswhut
31e8b466cf 新增窗口大小设置 2019-10-22 01:25:03 +08:00
lyswhut
c97bc5edfc 修复单选框组件v-model为数字时的 bug 2019-10-21 23:05:26 +08:00
lyswhut
74f8e9544b 发布0.8.2版本 2019-10-20 18:21:43 +08:00
lyswhut
0a5e7c44ff 容旧版酷我源搜索列表过滤128k音质的bug 2019-10-20 18:20:58 +08:00
43 changed files with 1129 additions and 784 deletions

View File

@@ -1,19 +1,17 @@
sudo: true
language: node_js
node_js: 12
matrix:
include:
- os: osx
osx_image: xcode10.2
language: node_js
node_js: "12"
env:
- ELECTRON_CACHE=$HOME/.cache/electron
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
- os: linux
language: node_js
node_js: "12"
dist: trusty
services: docker
language: generic
cache:
directories:
@@ -26,6 +24,8 @@ notifications:
email: false
script:
- node --version
- npm --version
- |
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
npm install && npm run publish:gh:linux

View File

@@ -6,6 +6,41 @@ 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.10.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.9.1...v0.10.0) - 2019-11-02
#### 优化
- 大幅减少程序**播放时**对CPU与GPU的使用经测试CPU使用减少60%以上GPU使用减少90%以上这应该能解决MAC系统上的温度上涨的问题
#### 修复
- 修复酷我源**搜索提示**、**排行榜**无法获取的问题
- 修复咪咕源无法播放的问题
## [0.9.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.9.0...v0.9.1) - 2019-10-27
#### 修复
- 修复没有配置文件时程序启动出错的问题
## [0.9.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.8.2...v0.9.0) - 2019-10-27
#### 新增
- 新增窗口大小设置,若觉得软件窗口小可以到设置页调大点
- 新增定位当前播放歌曲,点击播放栏左侧的**歌曲图片**可在播放列表定位当前播放的歌曲(该功能对播放下载列表的歌曲无效)
#### 修复
- 修复搜索提示失效的问题
- 修复从歌单或列表点击搜索按钮搜索目标歌曲时,搜索框未聚焦仍然弹出候选搜索列表的问题
## [0.8.2](https://github.com/lyswhut/lx-music-desktop/compare/v0.8.1...v0.8.2) - 2019-10-20
#### 修复
- 兼容旧版酷我源搜索列表过滤128k音质的bug0.8.1版本仅修复了酷我源的歌曲过滤问题,该修复仅对以后添加的歌曲有效,如果是之前添加的歌曲仍会出现这个问题,现修复对之前旧列表数据的兼容处理)
## [0.8.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.8.0...v0.8.1) - 2019-10-20
#### 修复

7
FAQ.md
View File

@@ -53,7 +53,12 @@
## 安装版安装失败,提示应用未安装
对于部分电脑出现安装失败的问题我也不懂什么原因,,可以尝试清理下安装文件,或者重启电脑试试。
对于部分电脑出现安装失败的问题,可以做出以下尝试:
- 若你之前可以安装成功,但现在安装失败,就去**控制面板-程序和功能**或用第三方卸载工具看下有没有之前的版本残留,若同时在不同路径下安装了多个版本就可能会出现该问题,这种情况卸载掉所有版本重新安装即可
- 清理安装路径下的残留文件
- 以管理员权限打开`cmd`,输入`sfc /scannow`回车等待检查完成重启电脑
- 若还是不行我也没办法了。。
## 缺少`xxx.dll`

View File

@@ -35,8 +35,8 @@
所用技术栈:
- Electron 6.x
- Vue 2.x
- Electron 7
- Vue 2
已支持的平台:
@@ -80,7 +80,7 @@ npm run pack
### 免责声明
本项目**不开发或者破解直接获取音频数据**的功能,所有音频数据均来自**第三方接口**<br>
本软件仅用于**测试 `electron 6.x` 在各种系统上的兼容性**及用于**对比各大音乐平台歌单、排行榜等数据列表的差异性**,使用本软件产生的**任何涉及版权相关的数据**请于**24小时内删除**。<br>
本软件仅用于**测试 `electron 7` 在各种系统上的兼容性**及用于**对比各大音乐平台歌单、排行榜等数据列表的差异性**,使用本软件产生的**任何涉及版权相关的数据**请于**24小时内删除**。<br>
本软件仅用于学习交流使用,禁止用于商业用途,使用本软件所造成的的后果由使用者承担!<br>
若对此有疑问请 mail to: lyswhut@qq.com

1385
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "lx-music-desktop",
"version": "0.8.1",
"version": "0.10.0",
"description": "一个免费的音乐下载助手",
"main": "./dist/electron/main.js",
"productName": "lx-music-desktop",
@@ -138,7 +138,7 @@
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/polyfill": "^7.6.0",
"@babel/preset-env": "^7.6.3",
"autoprefixer": "^9.6.5",
"autoprefixer": "^9.7.1",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"babel-minify-webpack-plugin": "^0.3.1",
@@ -147,16 +147,16 @@
"chalk": "^2.4.2",
"changelog-parser": "^2.8.0",
"copy-webpack-plugin": "^5.0.4",
"core-js": "^3.3.2",
"cos-nodejs-sdk-v5": "^2.5.12",
"core-js": "^3.3.6",
"cos-nodejs-sdk-v5": "^2.5.14",
"cross-env": "^6.0.3",
"css-loader": "^3.2.0",
"del": "^5.1.0",
"electron": "^6.0.12",
"electron-builder": "^21.2.0",
"electron": "^7.0.1",
"electron-builder": "^22.1.0",
"electron-debug": "^3.0.1",
"electron-devtools-installer": "^2.2.4",
"eslint": "^6.5.1",
"eslint": "^6.6.0",
"eslint-config-standard": "^14.1.0",
"eslint-formatter-friendly": "^7.0.0",
"eslint-loader": "^3.0.2",
@@ -182,14 +182,14 @@
"rimraf": "^3.0.0",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2",
"terser-webpack-plugin": "^2.1.3",
"terser-webpack-plugin": "^2.2.1",
"url-loader": "^2.2.0",
"vue-loader": "^15.7.1",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.9",
"webpack-dev-server": "^3.8.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0",
"webpack-hot-middleware": "^2.25.0",
"webpack-merge": "^4.2.2"
},
@@ -198,8 +198,8 @@
"crypto-js": "^3.1.9-1",
"dnscache": "^1.0.2",
"electron-log": "^3.0.8",
"electron-store": "^5.0.0",
"electron-updater": "^4.1.2",
"electron-store": "^5.1.0",
"electron-updater": "^4.2.0",
"flac-metadata": "^0.1.1",
"js-htmlencode": "^0.3.0",
"lrc-file-parser": "^0.1.14",

View File

@@ -1,3 +1,8 @@
#### 优化
- 大幅减少程序**播放时**对CPU与GPU的使用经测试CPU使用减少60%以上GPU使用减少90%以上这应该能解决MAC系统上的温度上涨的问题
#### 修复
- 修复酷我源搜索歌曲结果未添加128k音质导致播放128k音质时显示“该歌曲没有可播放的音频”的问题
- 修复酷我源**搜索提示**、**排行榜**无法获取的问题
- 修复咪咕源无法播放的问题

View File

@@ -1,7 +1,23 @@
{
"version": "0.8.1",
"desc": "<h4>修复</h4>\n<ul>\n<li>修复酷我源搜索歌曲结果未添加128k音质导致播放128k音质时显示“该歌曲没有可播放的音频”的问题</li>\n</ul>\n",
"version": "0.10.0",
"desc": "<h4>优化</h4>\n<ul>\n<li>大幅减少程序<strong>播放时</strong>对CPU与GPU的使用经测试CPU使用减少60%以上GPU使用减少90%以上这应该能解决MAC系统上的温度上涨的问题</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修复酷我源<strong>搜索提示</strong>、<strong>排行榜</strong>无法获取的问题</li>\n<li>修复咪咕源无法播放的问题</li>\n</ul>\n",
"history": [
{
"version": "0.9.1",
"desc": "<h4>修复</h4>\n<ul>\n<li>修复没有配置文件时程序启动出错的问题</li>\n</ul>\n"
},
{
"version": "0.9.0",
"desc": "<h4>新增</h4>\n<ul>\n<li>新增窗口大小设置,若觉得软件窗口小可以到设置页调大点</li>\n<li>新增定位当前播放歌曲,点击播放栏左侧的<strong>歌曲图片</strong>可在播放列表定位当前播放的歌曲(该功能对播放下载列表的歌曲无效)</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修复搜索提示失效的问题</li>\n<li>修复从歌单或列表点击搜索按钮搜索目标歌曲时,搜索框未聚焦仍然弹出候选搜索列表的问题</li>\n</ul>\n"
},
{
"version": "0.8.2",
"desc": "<h4>修复</h4>\n<ul>\n<li>兼容旧版酷我源搜索列表过滤128k音质的bug0.8.1版本仅修复了酷我源的歌曲过滤问题,该修复仅对以后添加的歌曲有效,如果是之前添加的歌曲仍会出现这个问题,现修复对之前旧列表数据的兼容处理)</li>\n</ul>\n"
},
{
"version": "0.8.1",
"desc": "<h4>修复</h4>\n<ul>\n<li>修复酷我源搜索歌曲结果未添加128k音质导致播放128k音质时显示“该歌曲没有可播放的音频”的问题</li>\n</ul>\n"
},
{
"version": "0.8.0",
"desc": "<h4>新增</h4>\n<ul>\n<li>新增网易云源歌曲搜索</li>\n<li>新增网易云源歌单</li>\n<li>新增各平台通过输入歌单链接或歌单ID打开歌单详情列表目前只适配了<strong>网页版歌单链接</strong>其他方式的歌单链接可能无法解析但你可想办法获取歌单ID后输入打开。注各平台歌单ID均为纯数字若遇到链接里存在歌单ID但无法解析的歌单链接可以到GitHub提交issue或发送邮件或加群830125506反馈</li>\n<li>新增音量调整滑动功能,现在支持鼠标左右拖动调整音量了</li>\n</ul>\n<h4>优化</h4>\n<ul>\n<li>优化搜索框搜索体验</li>\n<li>优化音量条交互视觉效果</li>\n<li>缓存歌单详情列表数据</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修复QQ源歌单无法翻页Bug</li>\n<li>修复默认列表没有创建时无法显示收藏列表的Bug</li>\n<li>修复网易云128k直接试听</li>\n<li>修复歌曲音质不存在时仍然播放或下载的Bug</li>\n<li>修复调整音量时,调整的位置与鼠标点击的位置不一致的问题</li>\n</ul>\n"

25
src/common/config.js Normal file
View File

@@ -0,0 +1,25 @@
module.exports = {
windowSizeList: [
{
id: 1,
name: '小',
width: 920,
height: 590,
tabList: '645px',
},
{
id: 2,
name: '中',
width: 1012,
height: 650,
tabList: '719px',
},
{
id: 3,
name: '大',
width: 1104,
height: 708,
tabList: '792px',
},
],
}

View File

@@ -0,0 +1,13 @@
const { mainOn } = require('../../common/icp')
mainOn('restartWindow', (event, name) => {
console.log(name)
switch (name) {
case 'main':
default:
break
}
})

View File

@@ -18,6 +18,7 @@ app.on('second-instance', (event, argv, cwd) => {
require('./events')
const autoUpdate = require('./utils/autoUpdate')
const { isLinux, isMac } = require('../common/utils')
const { getWindowSizeInfo } = require('./utils')
const isDev = process.env.NODE_ENV !== 'production'
@@ -39,13 +40,14 @@ if (isDev) {
}
function createWindow() {
let windowSizeInfo = getWindowSizeInfo()
/**
* Initial window options
*/
mainWindow = global.mainWindow = new BrowserWindow({
height: 590,
height: windowSizeInfo.height,
useContentSize: true,
width: 920,
width: windowSizeInfo.width,
frame: false,
transparent: !isLinux,
// icon: path.join(global.__static, isWin ? 'icons/256x256.ico' : 'icons/512x512.png'),

8
src/main/utils/index.js Normal file
View File

@@ -0,0 +1,8 @@
const Store = require('electron-store')
const { windowSizeList } = require('../../common/config')
exports.getWindowSizeInfo = () => {
let electronStore = new Store()
const { windowSizeId = 1 } = electronStore.get('setting') || {}
return windowSizeList.find(i => i.id === windowSizeId) || windowSizeList[0]
}

View File

@@ -22,6 +22,7 @@ import dnscache from 'dnscache'
import { mapMutations, mapGetters, mapActions } from 'vuex'
import { rendererOn } from '../common/icp'
import { isLinux } from '../common/utils'
import music from './utils/music'
window.ELECTRON_DISABLE_SECURITY_WARNINGS = process.env.ELECTRON_DISABLE_SECURITY_WARNINGS
dnscache({
enable: true,
@@ -145,6 +146,9 @@ export default {
this.globalObj.apiSource = this.setting.apiSource
this.globalObj.proxy = Object.assign({}, this.setting.network.proxy)
window.globalObj = this.globalObj
// 初始化音乐sdk
music.init()
},
enableIgnoreMouseEvents() {
if (isLinux) return

View File

@@ -413,7 +413,7 @@
// Width
@width-app-left: 180px;
@width-app-left: 20%;
// Height
@height-toolbar: 50px;

View File

@@ -68,8 +68,8 @@ export default {
}
.logo {
box-sizing: border-box;
padding: 20px;
height: 100px;
padding: 12% 13%;
// height: 120px;
color: @color-theme-font;
flex: none;
}

View File

@@ -1,6 +1,6 @@
<template lang="pug">
div(:class="$style.player")
div(:class="$style.left")
div(:class="$style.left" @click="handleToMusicLocation")
img(v-if="musicInfo.img" :src="musicInfo.img" @error="imgError")
svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' height='100%' viewBox='0 0 60 60' space='preserve')
use(:xlink:href='`#${$style.iconPic}`')
@@ -27,7 +27,7 @@ div(:class="$style.player")
div(:class="$style.column2")
div(:class="$style.progress")
//- div(:class="[$style.progressBar, $style.progressBar1]" :style="{ transform: `scaleX(${progress || 0})` }")
div(:class="[$style.progressBar, $style.progressBar2]" :style="{ transform: `scaleX(${progress || 0})` }")
div(:class="[$style.progressBar, $style.progressBar2, isActiveTransition ? $style.barTransition : '']" @transitionend="handleTransitionEnd" :style="{ transform: `scaleX(${progress || 0})` }")
div(:class="$style.progressMask" @click='setProgess' ref="dom_progress")
div(:class="$style.column3")
span {{nowPlayTimeStr}}
@@ -88,8 +88,7 @@ export default {
msDownX: 0,
msDownVolume: 0,
},
handleVolumeMsMoveFn: null,
handleVolumeMsUpFn: null,
isActiveTransition: false,
}
},
computed: {
@@ -125,12 +124,14 @@ export default {
this.setVolume(volume)
}, 300)
document.addEventListener('mousemove', this.handleVolumeMsMoveFn = this.handleVolumeMsMove.bind(this))
document.addEventListener('mouseup', this.handleVolumeMsUpFn = this.handleVolumeMsUp.bind(this))
document.addEventListener('mousemove', this.handleVolumeMsMove)
document.addEventListener('mouseup', this.handleVolumeMsUp)
window.addEventListener('resize', this.handleResize)
},
beforeDestroy() {
document.removeEventListener('mousemove', this.handleVolumeMsMoveFn)
document.removeEventListener('mouseup', this.handleVolumeMsUpFn)
document.removeEventListener('mousemove', this.handleVolumeMsMove)
document.removeEventListener('mouseup', this.handleVolumeMsUp)
window.removeEventListener('resize', this.handleResize)
},
watch: {
changePlay(n) {
@@ -211,7 +212,7 @@ export default {
if (!this.musicInfo.songmid) return
console.log('出错')
this.stopPlay()
if (this.listId != 'download' && this.audio.error.code !== 1 && this.retryNum < 3) { // 若音频URL无效则尝试刷新3次URL
if (this.listId != 'download' && this.audio.error.code !== 1 && this.retryNum < 2) { // 若音频URL无效则尝试刷新2次URL
// console.log(this.retryNum)
this.audioErrorTime = this.audio.currentTime // 记录出错的播放时间
this.retryNum++
@@ -367,13 +368,14 @@ export default {
},
setProgess(e) {
if (!this.audio.src) return
this.isActiveTransition = true
this.audio.currentTime =
(e.offsetX / this.pregessWidth) * this.maxPlayTime
if (!this.isPlay) this.audio.play()
},
setProgessWidth() {
this.pregessWidth = parseInt(
window.getComputedStyle(this.$refs.dom_progress, null).width
window.getComputedStyle(this.$refs.dom_progress, null).width,
)
},
togglePlay() {
@@ -489,6 +491,24 @@ export default {
handleCopy(text) {
clipboardWriteText(text)
},
handleResize() {
this.setProgessWidth()
},
handleToMusicLocation() {
if (this.listId == 'download') return
if (this.playIndex == -1) return
this.$router.push({
path: 'list',
query: {
id: this.listId,
scrollIndex: this.playIndex,
},
})
},
handleTransitionEnd(e) {
// console.log(e)
this.isActiveTransition = false
},
},
}
</script>
@@ -517,6 +537,14 @@ export default {
transition-property: color;
flex: none;
padding: 2px;
opacity: 1;
transition: @transition-theme;
transition-property: opacity;
cursor: pointer;
&:hover {
opacity: .8;
}
svg {
fill: currentColor;
@@ -671,21 +699,23 @@ export default {
width: 100%;
height: 100%;
transform-origin: 0;
transition-property: transform;
transition-timing-function: ease-out;
border-radius: @radius-progress-border;
}
.progress-bar1 {
transition-duration: 0.6s;
background-color: @color-player-progress-bar1;
}
.progress-bar2 {
transition-duration: 0.2s;
background-color: @color-player-progress-bar2;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
}
.bar-transition {
transition-property: transform;
transition-timing-function: ease-out;
transition-duration: 0.2s;
}
.column3 {
transition: @transition-theme;
transition-property: color;

View File

@@ -23,6 +23,7 @@ export default {
visibleList: false,
tipList: [],
tipSearch: null,
isFocused: false,
}
},
computed: {
@@ -64,10 +65,12 @@ export default {
handleEvent({ action, data }) {
switch (action) {
case 'focus':
this.isFocused = true
if (!this.visibleList) this.visibleList = true
if (this.searchText) this.handleTipSearch()
break
case 'blur':
this.isFocused = false
setTimeout(() => {
if (this.visibleList) this.visibleList = false
}, 50)
@@ -84,7 +87,7 @@ export default {
},
handleTipSearch() {
if (!this.visibleList) this.visibleList = true
if (!this.visibleList && this.isFocused) this.visibleList = true
this.tipSearch()
},

View File

@@ -2,16 +2,12 @@ import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
'./', true, /\.vue$/
)
const requireComponent = require.context('./', true, /\.vue$/)
requireComponent.keys().forEach(fileName => {
const componentConfig = requireComponent(fileName)
const componentName = upperFirst(
camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, ''))
)
const componentName = upperFirst(camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')))
Vue.component(componentName, componentConfig.default || componentConfig)
})

View File

@@ -67,8 +67,6 @@ export default {
} 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) {
@@ -78,6 +76,8 @@ export default {
// })
}
checked = bool
} else {
checked = this.bool ? this.value : ''
}
this.$emit('input', checked)
this.$emit('change', checked)
@@ -88,14 +88,14 @@ export default {
bool = value.includes(this.value)
} else {
switch (typeof value) {
case 'string':
bool = value === this.value
break
case 'boolean':
bool = value
break
// case 'string':
// case 'number':
default:
return
bool = value === this.value
break
}
}

View File

@@ -5,7 +5,7 @@
div(:class="$style.icon")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.847 451.847' space='preserve')
use(xlink:href='#icon-down')
div.scroll(:class="$style.list" @click.stop ref="dom_list")
div.scroll(:class="$style.list" :style="{ width: listStyle }" @click.stop ref="dom_list")
div(:class="$style.tag" @click="handleClick(null)") 默认
dl(v-for="type in list")
dt(:class="$style.type") {{type.name}}
@@ -14,6 +14,7 @@
<script>
import { isChildren } from '../../utils'
import { mapGetters } from 'vuex'
export default {
props: {
list: {
@@ -26,6 +27,13 @@ export default {
type: Object,
},
},
computed: {
...mapGetters(['setting', 'windowSizeList']),
listStyle() {
let info = this.windowSizeList.find(i => i.id === this.setting.windowSizeId) || this.windowSizeList[0]
return info.tabList
},
},
data() {
return {
show: false,

View File

@@ -4,14 +4,13 @@ import Router from 'vue-router'
import paths from './paths'
function route(path, view, name, meta) {
function route(path, view, name, meta, props) {
return {
name: name || view,
path,
meta,
component: (resovle) => import(
`../views/${view}.vue`
).then(resovle),
props,
component: (resovle) => import(`../views/${view}.vue`).then(resovle),
}
}
@@ -19,7 +18,7 @@ Vue.use(Router)
const router = new Router({
mode: 'hash',
routes: paths.map(path => route(path.path, path.view, path.name, path.meta)).concat([
routes: paths.map(path => route(path.path, path.view, path.name, path.meta, path.props)).concat([
{ path: '*', redirect: '/search' },
]),
linkActiveClass: 'active-link',

View File

@@ -26,6 +26,7 @@ export default [
path: '/list',
name: 'list',
view: 'List',
// props: true,
},
{
path: '/download',

View File

@@ -34,4 +34,7 @@ export default {
route(state) {
return state.route
},
windowSizeList(state) {
return state.windowSizeList
},
}

View File

@@ -75,7 +75,12 @@ const addTask = (list, type, store) => {
const getUrl = (downloadInfo, isRefresh) => {
const url = downloadInfo.musicInfo.typeUrl[downloadInfo.type]
if (!downloadInfo.musicInfo._types[downloadInfo.type]) return Promise.reject(new Error('该歌曲没有可下载的音频'))
if (!downloadInfo.musicInfo._types[downloadInfo.type]) {
// 兼容旧版酷我源搜索列表过滤128k音质的bug
if (!(downloadInfo.musicInfo.source == 'kw' && downloadInfo.type == '128k')) return Promise.reject(new Error('该歌曲没有可下载的音频'))
// return Promise.reject(new Error('该歌曲没有可下载的音频'))
}
return url && !isRefresh ? Promise.resolve({ url }) : music[downloadInfo.musicInfo.source].getMusicUrl(downloadInfo.musicInfo, downloadInfo.type).promise
}
@@ -190,7 +195,7 @@ const actions = {
// console.log(err.code, err.message)
commit('onError', downloadInfo)
// console.log(tryNum[downloadInfo.key])
if (++tryNum[downloadInfo.key] > 5) {
if (++tryNum[downloadInfo.key] > 2) {
_this.dispatch('download/startTask')
return
}

View File

@@ -23,7 +23,12 @@ const getters = {
// actions
const actions = {
getUrl({ commit, state }, { musicInfo, type, isRefresh }) {
if (!musicInfo._types[type]) return Promise.reject(new Error('该歌曲没有可播放的音频'))
if (!musicInfo._types[type]) {
// 兼容旧版酷我源搜索列表过滤128k音质的bug
if (!(musicInfo.source == 'kw' && type == '128k')) return Promise.reject(new Error('该歌曲没有可播放的音频'))
// return Promise.reject(new Error('该歌曲没有可播放的音频'))
}
if (urlRequest && urlRequest.cancelHttp) urlRequest.cancelHttp()
if (musicInfo.typeUrl[type] && !isRefresh) return Promise.resolve()
urlRequest = music[musicInfo.source].getMusicUrl(musicInfo, type)

View File

@@ -2,6 +2,7 @@
// const isDev = process.env.NODE_ENV === 'development'
import Store from 'electron-store'
import { updateSetting } from '../utils'
import { windowSizeList } from '../../common/config'
import { version } from '../../../package.json'
let electronStore = new Store()
const setting = updateSetting(electronStore.get('setting'))
@@ -65,4 +66,5 @@ export default {
userInfo: null,
setting,
electronStore,
windowSizeList,
}

View File

@@ -14,11 +14,11 @@ export const pauseResumeTimer = (_dl, wait) => {
setTimeout(() => {
if (!_dl.isResumable()) {
console.warn(
"This URL doesn't support resume, it will start from the beginning"
"This URL doesn't support resume, it will start from the beginning",
)
}
return _dl.resume()
}, wait)
}, wait),
)
}, wait)
}

View File

@@ -164,7 +164,7 @@ export const isChildren = (parent, children) => {
* @param {*} setting
*/
export const updateSetting = setting => {
const defaultVersion = '1.0.12'
const defaultVersion = '1.0.13'
const defaultSetting = {
version: defaultVersion,
player: {
@@ -216,6 +216,7 @@ export const updateSetting = setting => {
password: '',
},
},
windowSizeId: 1,
themeId: 0,
sourceId: 'kw',
apiSource: 'test',
@@ -369,3 +370,10 @@ export const getCacheSize = () => remote.getCurrentWindow().webContents.session.
* @param {*} win
*/
export const clearCache = () => remote.getCurrentWindow().webContents.session.clearCache()
/**
* 设置窗口大小
* @param {*} width
* @param {*} height
*/
export const setWindowSize = (width, height) => remote.getCurrentWindow().setBounds({ width, height })

View File

@@ -145,9 +145,7 @@ export default {
getList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_list = httpFetch(
this.getListUrl(sortId, tagId, page)
)
this._requestObj_list = httpFetch(this.getListUrl(sortId, tagId, page))
return this._requestObj_list.promise.then(({ body }) => {
if (body.error_code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
return {

View File

@@ -4,7 +4,7 @@ import tx from './tx'
import wy from './wy'
import mg from './mg'
import bd from './bd'
export default {
const sources = {
sources: [
{
name: '酷我音乐',
@@ -38,3 +38,12 @@ export default {
mg,
bd,
}
export default {
...sources,
init() {
for (let source of sources.sources) {
let sm = sources[source.id]
sm && sm.init && sm.init()
}
},
}

View File

@@ -32,7 +32,7 @@ export default {
userid: 2626431536,
vip: 1,
},
}
},
)
requestObj.promise = requestObj.promise.then(({ body }) => {
if (body.error_code !== 0) return Promise.reject('图片获取失败')

View File

@@ -92,7 +92,7 @@ export default {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_list = httpFetch(
this.getSongListUrl(sortId, tagId, page)
this.getSongListUrl(sortId, tagId, page),
)
return this._requestObj_list.promise.then(({ body }) => {
if (body.status !== 1) return this.getSongList(sortId, tagId, page, ++tryNum)
@@ -120,7 +120,7 @@ export default {
return_min: 6,
return_max: 15,
},
}
},
)
return this._requestObj_listRecommend.promise.then(({ body }) => {
if (body.status !== 1) return this.getSongListRecommend(++tryNum)
@@ -253,7 +253,7 @@ export default {
this.currentTagInfo.id = tagId
this.currentTagInfo.info = Object.assign({}, info)
return info
})
}),
)
if (!tagId && page === 1 && sortId === this.sortList[0].id) tasks.push(this.getSongListRecommend()) // 如果是所有类别,则顺便获取推荐列表
return Promise.all(tasks).then(([list, info, recommendList]) => {

View File

@@ -1,7 +1,7 @@
import { httpGet, cancelHttp } from '../../request'
import tempSearch from './tempSearch'
import musicSearch from './musicSearch'
import { formatSinger } from './util'
import { formatSinger, getToken } from './util'
import leaderboard from './leaderboard'
import lyric from './lyric'
import pic from './pic'
@@ -96,6 +96,10 @@ const kw = {
getPic(songInfo) {
return pic.getPic(songInfo)
},
init() {
getToken()
},
}
export default kw

View File

@@ -1,7 +1,6 @@
import { httpGet, cancelHttp } from '../../request'
import { formatPlayTime, decodeName } from '../../index'
import { formatSinger } from './util'
import { formatSinger, getToken, matchToken } from './util'
export default {
list: [
@@ -89,20 +88,29 @@ export default {
})
})
},
getData2(url) {
async getData2(url) {
if (this._cancelRequestObj2 != null) {
cancelHttp(this._cancelRequestObj2)
this._cancelPromiseCancelFn2(new Error('取消http请求'))
}
let token = window.kw_token.token
if (!token) token = await getToken()
return new Promise((resolve, reject) => {
this._cancelPromiseCancelFn2 = reject
this._cancelRequestObj2 = httpGet(url, (err, resp, body) => {
this._cancelRequestObj2 = httpGet(url, {
headers: {
Referer: 'http://www.kuwo.cn/',
csrf: token,
cookie: 'kw_token=' + token,
},
}, (err, resp, body) => {
this._cancelRequestObj2 = null
this._cancelPromiseCancelFn2 = null
if (err) {
console.log(err)
reject(err)
return reject(err)
}
window.kw_token.token = matchToken(resp.headers)
resolve(body)
})
})

View File

@@ -1,25 +1,27 @@
import { httpGet, cancelHttp } from '../../request'
import { httpFetch } from '../../request'
import { decodeName } from '../../index'
import { getToken, matchToken } from './util'
export default {
regExps: {
relWord: /RELWORD=(.+)/,
},
_musicTempSearchRequestObj: null,
_musicTempSearchPromiseCancelFn: null,
tempSearch(str) {
requestObj: null,
tempSearch(str, token) {
this.cancelTempSearch()
return new Promise((resolve, reject) => {
this._musicTempSearchPromiseCancelFn = reject
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)
reject(err)
}
resolve(body)
})
this.requestObj = httpFetch(`http://www.kuwo.cn/api/www/search/searchKey?key=${encodeURIComponent(str)}`, {
headers: {
Referer: 'http://www.kuwo.cn/',
csrf: token,
cookie: 'kw_token=' + token,
},
})
return this.requestObj.promise.then(({ statusCode, body, headers }) => {
if (statusCode != 200) return Promise.reject(new Error('请求失败'))
window.kw_token.token = matchToken(headers)
if (body.code !== 200) return Promise.reject(new Error('请求失败'))
return body
})
},
handleResult(rawData) {
@@ -29,12 +31,11 @@ export default {
})
},
cancelTempSearch() {
if (this._musicTempSearchRequestObj != null) {
cancelHttp(this._musicTempSearchRequestObj)
this._musicTempSearchPromiseCancelFn(new Error('取消http请求'))
}
if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
},
search(str) {
return this.tempSearch(str).then(result => this.handleResult(result.data))
async search(str) {
let token = window.kw_token.token
if (!token) token = await getToken()
return this.tempSearch(str, token).then(result => this.handleResult(result.data))
},
}

View File

@@ -1,2 +1,34 @@
import { httpGet } from '../../request'
if (!window.kw_token) {
window.kw_token = {
token: null,
isGetingToken: false,
}
}
export const formatSinger = rawData => rawData.replace(/&/g, '、')
export const matchToken = headers => {
try {
return headers['set-cookie'][0].match(/kw_token=(\w+)/)[1]
} catch (err) {
return null
}
}
const wait = time => new Promise(resolve => setTimeout(() => resolve(), time))
export const getToken = () => new Promise((resolve, reject) => {
if (window.kw_token.isGetingToken) return wait(1000).then(() => getToken().then(token => resolve(token)))
if (window.kw_token.token) return resolve(window.kw_token.token)
window.kw_token.isGetingToken = true
httpGet('http://www.kuwo.cn', (err, resp) => {
window.kw_token.isGetingToken = false
if (err) return reject(err)
if (resp.statusCode != 200) return reject(new Error('获取失败'))
const token = window.kw_token.token = matchToken(resp.headers)
resolve(token)
})
})

View File

@@ -11,7 +11,7 @@ const api_test = {
family: 4,
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body.code === 0 ? Promise.resolve({ type, url: encodeURI(body.data) }) : Promise.reject(new Error(requestMsg.fail))
return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
})
return requestObj
},

View File

@@ -109,7 +109,7 @@ export default {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_list = httpFetch(
this.getListUrl(sortId, tagId, page)
this.getListUrl(sortId, tagId, page),
)
return this._requestObj_list.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)

View File

@@ -95,7 +95,7 @@ export default {
this.listAdd({ id: 'default', musicInfo: targetSong })
}
let targetIndex = this.defaultList.list.findIndex(
s => s.songmid === targetSong.songmid
s => s.songmid === targetSong.songmid,
)
if (targetIndex > -1) {
this.setList({

View File

@@ -17,7 +17,7 @@
div.scroll(:class="$style.tbody" @scroll="handleScroll" ref="dom_scrollContent")
table
tbody
tr(v-for='(item, index) in list' :key='item.songmid'
tr(v-for='(item, index) in list' :key='item.songmid' :id="'mid_' + item.songmid"
@click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', (isAPITemp && item.source != 'kw') ? $style.disabled : '']")
td.nobreak.center(style="width: 37px;" @click.stop)
material-checkbox(:id="index.toString()" v-model="selectdData" :value="item")
@@ -49,7 +49,7 @@
<script>
import { mapMutations, mapGetters, mapActions } from 'vuex'
import { throttle, asyncSetArray } from '../utils'
import { throttle, asyncSetArray, scrollTo } from '../utils'
export default {
name: 'List',
data() {
@@ -69,6 +69,7 @@ export default {
isShowListAdd: false,
isShowListAddMultiple: false,
delayTimeout: null,
isToggleList: true,
}
},
computed: {
@@ -125,9 +126,18 @@ export default {
list() {
this.resetSelect()
},
'$route.query.scrollIndex'(n) {
if (n == null || this.isToggleList) return
this.restoreScroll(true)
this.isToggleList = true
},
},
beforeRouteUpdate(to, from, next) {
if (to.query.id === undefined) return
if (to.query.id == null) return
else if (to.query.id == this.listId) {
if (to.query.scrollIndex != null) this.isToggleList = false
return next()
}
this.delayShow = false
this.$nextTick(() => {
this.listId = to.query.id
@@ -195,14 +205,29 @@ export default {
this.delayTimeout = null
}
},
restoreScroll() {
restoreScroll(isAnimation) {
if (!this.list.length) return
let location = this.setting.list.scroll.locations[this.listId]
if (this.setting.list.scroll.enable && location) {
this.$nextTick(() => {
this.$refs.dom_scrollContent.scrollTo(0, location)
})
if (this.$route.query.scrollIndex == null) {
let location = this.setting.list.scroll.locations[this.listId]
if (this.setting.list.scroll.enable && location) {
this.$nextTick(() => {
this.$refs.dom_scrollContent.scrollTo(0, location)
})
}
return
}
this.$nextTick(() => {
let location = this.getMusicLocation(this.$route.query.scrollIndex) - 150
if (location < 0) location = 0
isAnimation ? scrollTo(this.$refs.dom_scrollContent, location) : this.$refs.dom_scrollContent.scrollTo(0, location)
this.$router.replace({
path: 'list',
query: {
id: this.listId,
},
})
})
},
handleDoubleClick(index) {
if (
@@ -292,6 +317,10 @@ export default {
break
}
},
getMusicLocation(index) {
let dom = document.getElementById('mid_' + this.list[index].songmid)
return dom ? dom.offsetTop : 0
},
// handleScroll(e) {
// console.log(e.target.scrollTop)
// },

View File

@@ -194,7 +194,7 @@ export default {
this.listAdd({ id: 'default', musicInfo: targetSong })
}
let targetIndex = this.defaultList.list.findIndex(
s => s.songmid === targetSong.songmid
s => s.songmid === targetSong.songmid,
)
if (targetIndex > -1) {
this.setList({

View File

@@ -18,9 +18,15 @@ div.scroll(:class="$style.setting")
dd(title='选择音乐来源')
h3 音乐来源
div
material-checkbox(v-for="item in apiSources" :id="`setting_api_source_${item.id}`" @change="handleAPISourceChange(item.id)" :class="$style.gapTop"
material-checkbox(v-for="item in apiSources" :id="`setting_api_source_${item.id}`" name="setting_api_source" @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")
dd(title='设置软件窗口尺寸')
h3 窗口尺寸
div
material-checkbox(v-for="(item, index) in windowSizeList" :id="`setting_window_size_${item.id}`" name="setting_window_size" @change="handleWindowSizeChange(index)" :class="$style.gapLeft"
need v-model="current_setting.windowSizeId" :value="item.id" :label="item.name" :key="item.id")
dt 播放设置
dd(title="都不选时播放完当前歌曲就停止播放")
h3 歌曲切换方式
@@ -40,8 +46,8 @@ div.scroll(:class="$style.setting")
h3 是否显示歌曲源仅对我的音乐分类有效
div
material-checkbox(id="setting_list_showSource_enable" v-model="current_setting.list.isShowSource" label="是否显示")
dd(title='是否恢复播放列表滚动条位置')
h3 恢复列表滚动位置仅对我的音乐分类有效
dd(title='是否记住播放列表滚动条位置')
h3 记住列表滚动位置仅对我的音乐分类有效
div
material-checkbox(id="setting_list_scroll_enable" v-model="current_setting.list.scroll.enable" label="是否启用")
//- dd(title='播放列表是否显示专辑栏')
@@ -176,19 +182,22 @@ import {
getCacheSize,
clearCache,
sizeFormate,
setWindowSize,
} from '../utils'
import { rendererSend } from '../../common/icp'
import fs from 'fs'
export default {
name: 'Setting',
computed: {
...mapGetters(['setting', 'themes', 'version']),
...mapGetters(['setting', 'themes', 'version', 'windowSizeList']),
...mapGetters('list', ['defaultList', 'loveList']),
isLatestVer() {
return this.version.newVersion && this.version.version === this.version.newVersion.version
},
isShowRebootBtn() {
return this.current_setting.windowSizeId != window.currentWindowSizeId
},
},
data() {
return {
@@ -225,6 +234,7 @@ export default {
odc: {
isAutoClearSearchInput: false,
},
windowSizeId: 1,
themeId: 0,
sourceId: 0,
randomAnimate: true,
@@ -314,6 +324,7 @@ export default {
...mapMutations('list', ['setList']),
init() {
this.current_setting = JSON.parse(JSON.stringify(this.setting))
if (!window.currentWindowSizeId) window.currentWindowSizeId = this.setting.windowSizeId
this.getCacheSize()
},
handleChangeSavePath() {
@@ -502,6 +513,10 @@ export default {
this.getCacheSize()
})
},
handleWindowSizeChange(index) {
let info = this.windowSizeList[index]
setWindowSize(info.width, info.height)
},
},
}
</script>
@@ -549,6 +564,19 @@ export default {
}
.btn-content {
display: inline-block;
transition: @transition-theme;
transition-property: opacity, transform;
opacity: 1;
transform: scale(1);
&.hide {
opacity: 0;
transform: scale(0);
}
}
.gap-left {
+ .gap-left {
margin-left: 20px;

View File

@@ -190,7 +190,7 @@ export default {
this.listAdd({ id: 'default', musicInfo: targetSong })
}
let targetIndex = this.defaultList.list.findIndex(
s => s.songmid === targetSong.songmid
s => s.songmid === targetSong.songmid,
)
if (targetIndex > -1) {
this.setList({