diff --git a/CHANGELOG.md b/CHANGELOG.md
index 338f487b..204f26d5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,39 @@ Project versioning adheres to [Semantic Versioning](http://semver.org/).
Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
+## [1.1.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.0.1...v1.1.0) - 2020-09-18
+
+### 新增
+
+- 在歌单详情界面新增播放当前歌单按钮、收藏歌单按钮,注:播放歌单不会将歌曲添加到试听列表
+- 新增`不允许将歌词窗口拖出主屏幕之外`的设置项,默认开启,在连接多个屏幕时想要拖动到其他屏幕时可关闭此设置
+- 新增大部分平台的歌词翻译,感谢 @InoriHimea 提供的[krc解码算法](https://github.com/lyswhut/lx-music-desktop/issues/296#issuecomment-683285784)
+- 新增`显示歌词翻译`设置,默认开启,仅支持某些平台,注:无论该设置是否开启,嵌入或下载歌词时都不会带上翻译
+- 新增`显示切换动画`设置,默认开启,关闭时将基本禁用软件内的所有切换动画
+- 播放状态栏新增桌面歌词的开关、播放模式的切换、歌曲的收藏按钮,Thanks to @andylow for the [icon](https://github.com/lyswhut/lx-music-desktop/pull/309)!
+
+### 修复
+
+- 修复使用全局快捷键还原窗口时,窗口没有获取焦点的问题
+- 修复我的列表搜索对最后一个字符的匹配问题
+- 修复窗口在`较小`模式下最小化/关闭按钮不居中的问题
+
+### 优化
+
+- 桌面歌词当前播放行改为上下居中
+- 为区分静音状态,静音时音量条会变淡,调整音量条时将会取消静音
+- 优化随机播放机制,现在通过`下一曲`切换歌曲时,直到播放完整个列表之前将不会再随机到之前播放过的歌曲,并且通过`上一曲`可以正确播放上一首歌曲
+- 当下载目录没有写入权限时将显示没有写入权限的提示
+
+### 移除
+
+- 移除默认的全局声音媒体快捷键接管
+- 移除对百度音乐的支持,因百度音乐原有的大部分API失效,而且该平台相对其他平台来说音乐太少了,可有可无,以后再看情况恢复
+
+### 其他
+
+- 更新electron到 10.1.2
+
## [1.0.1](https://github.com/lyswhut/lx-music-desktop/compare/v1.0.0...v1.0.1) - 2020-07-25
### 优化
diff --git a/FAQ.md b/FAQ.md
index f5002b70..2d88ff9b 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -35,7 +35,7 @@
## 播放整个歌单或排行榜
-播放在线列表内的歌曲需要将它们都添加到我的列表才能播放,你可以全选列表内的歌曲然后添加到现有列表或者新创建的列表,然后去播放改列表内的歌曲。
+播放在线列表内的歌曲需要将它们都添加到我的列表才能播放,你可以全选列表内的歌曲然后添加到现有列表或者新创建的列表,然后去播放该列表内的歌曲。
## 桌面歌词显示异常
@@ -108,6 +108,7 @@ Windows 7 未开启 Aero 效果时桌面歌词会有问题,详情看下面的*
- 若你之前可以安装成功,但现在安装失败,就去**控制面板-程序和功能**或用第三方卸载工具看下有没有之前的版本残留,若同时在不同路径下安装了多个版本就可能会出现该问题,这种情况卸载掉所有版本重新安装即可
- 清理安装路径下的残留文件
+- 清理注册表(建议用清理工具清理)
## 缺少`xxx.dll`
@@ -118,7 +119,7 @@ Windows 7 未开启 Aero 效果时桌面歌词会有问题,详情看下面的*
## 杀毒软件提示有病毒或恶意行为
-本人只能保证我写的代码不包含任何**恶意代码**、**收集用户信息**的行为,并且软件代码已开源,请自行查阅,软件安装包也是由CI拉取源代码构建,构建日志:[windows包](https://ci.appveyor.com/project/lyswhut/lx-music-desktop)、[Mac/Linux包](https://travis-ci.org/lyswhut/lx-music-desktop)
+本人只能保证我写的代码不包含任何**恶意代码**、**收集用户信息**的行为,并且软件代码已开源,请自行查阅,软件安装包也是由CI拉取源代码构建,构建日志:[windows包](https://ci.appveyor.com/project/lyswhut/lx-music-desktop)、[Mac/Linux包](https://travis-ci.com/github/lyswhut/lx-music-desktop)
尽管如此,但这不意味着软件是100%安全的,由于软件使用了第三方依赖,当这些依赖存在恶意行为时([供应链攻击](https://docs.microsoft.com/zh-cn/windows/security/threat-protection/intelligence/supply-chain-malware)),软件也将会受到牵连,所以我只能尽量选择使用较多人用、信任度较高的依赖。
当然,以上说明建立的前提是在你所用的安装包是从**本项目主页上写的链接**下载的,或者有相关能力者还可以下载源代码自己构建安装包。
diff --git a/README.md b/README.md
index f57bf9a6..93febdc8 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
-
+
@@ -36,7 +36,7 @@
所用技术栈:
-- Electron 9
+- Electron 10
- Vue 2
已支持的平台:
@@ -47,7 +47,7 @@
软件变化请查看:[更新日志](https://github.com/lyswhut/lx-music-desktop/blob/master/CHANGELOG.md)
软件下载请转到:[发布页面](https://github.com/lyswhut/lx-music-desktop/releases)
-或者到网盘下载(网盘内有MAC、windows版):`https://t-s.lanzous.com/b0bf2cfa/` 密码:`glqw`
+或者到网盘下载(网盘内有MAC、windows版):`https://www.lanzoux.com/b0bf2cfa/` 密码:`glqw`
使用常见问题请转至:[常见问题](https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md)
### 源码使用方法
diff --git a/build-config/css-loader.config.js b/build-config/css-loader.config.js
index b6cb4288..9f2aa3d4 100644
--- a/build-config/css-loader.config.js
+++ b/build-config/css-loader.config.js
@@ -3,6 +3,6 @@ const isDev = process.env.NODE_ENV === 'development'
module.exports = {
modules: {
localIdentName: isDev ? '[folder]-[name]--[local]--[hash:base64:5]' : '[hash:base64:5]',
+ exportLocalsConvention: 'camelCase',
},
- localsConvention: 'camelCase',
}
diff --git a/build-config/pack.js b/build-config/pack.js
index 1ff5470e..c956c3d1 100644
--- a/build-config/pack.js
+++ b/build-config/pack.js
@@ -14,7 +14,7 @@ const okayLog = chalk.bgGreen.white(' OKAY ') + ' '
function build() {
- del.sync(['dist/electron', 'build'])
+ del.sync(['dist/electron/**', 'build/**'])
const spinners = new Spinnies({ color: 'blue' })
spinners.add('main', { text: 'main building' })
diff --git a/build-config/renderer-lyric/webpack.config.base.js b/build-config/renderer-lyric/webpack.config.base.js
index a74a2606..ed08fd5e 100644
--- a/build-config/renderer-lyric/webpack.config.base.js
+++ b/build-config/renderer-lyric/webpack.config.base.js
@@ -1,9 +1,12 @@
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HTMLPlugin = require('html-webpack-plugin')
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const vueLoaderConfig = require('../vue-loader.config')
+const { mergeCSSLoader } = require('../utils')
+const isDev = process.env.NODE_ENV === 'development'
module.exports = {
target: 'electron-renderer',
@@ -46,6 +49,28 @@ module.exports = {
loader: 'babel-loader',
exclude: /node_modules/,
},
+ {
+ test: /\.css$/,
+ oneOf: mergeCSSLoader(),
+ },
+ {
+ test: /\.less$/,
+ oneOf: mergeCSSLoader({
+ loader: 'less-loader',
+ options: {
+ sourceMap: true,
+ },
+ }),
+ },
+ {
+ test: /\.styl(:?us)?$/,
+ oneOf: mergeCSSLoader({
+ loader: 'stylus-loader',
+ options: {
+ sourceMap: true,
+ },
+ }),
+ },
{
test: /\.pug$/,
oneOf: [
@@ -98,5 +123,11 @@ module.exports = {
__dirname,
}),
new VueLoaderPlugin(),
+ new MiniCssExtractPlugin({
+ // Options similar to the same options in webpackOptions.output
+ // both options are optional
+ filename: isDev ? '[name].css' : '[name].[contenthash:8].css',
+ chunkFilename: isDev ? '[id].css' : '[id].[contenthash:8].css',
+ }),
],
}
diff --git a/build-config/renderer-lyric/webpack.config.dev.js b/build-config/renderer-lyric/webpack.config.dev.js
index 5baf32fa..871afd3e 100644
--- a/build-config/renderer-lyric/webpack.config.dev.js
+++ b/build-config/renderer-lyric/webpack.config.dev.js
@@ -6,37 +6,9 @@ const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.config.base')
-const { mergeCSSLoaderDev } = require('../utils')
-
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'eval-source-map',
- module: {
- rules: [
- {
- test: /\.css$/,
- oneOf: mergeCSSLoaderDev(),
- },
- {
- test: /\.less$/,
- oneOf: mergeCSSLoaderDev({
- loader: 'less-loader',
- options: {
- sourceMap: true,
- },
- }),
- },
- {
- test: /\.styl(:?us)?$/,
- oneOf: mergeCSSLoaderDev({
- loader: 'stylus-loader',
- options: {
- sourceMap: true,
- },
- }),
- },
- ],
- },
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
diff --git a/build-config/renderer-lyric/webpack.config.prod.js b/build-config/renderer-lyric/webpack.config.prod.js
index 89986194..e151deac 100644
--- a/build-config/renderer-lyric/webpack.config.prod.js
+++ b/build-config/renderer-lyric/webpack.config.prod.js
@@ -1,6 +1,5 @@
const path = require('path')
const webpack = require('webpack')
-const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
@@ -8,7 +7,6 @@ const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.config.base')
-const { mergeCSSLoaderProd } = require('../utils')
const { dependencies } = require('../../package.json')
let whiteListedModules = ['vue']
@@ -20,32 +18,6 @@ module.exports = merge(baseConfig, {
externals: [
...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)),
],
- module: {
- rules: [
- {
- test: /\.css$/,
- oneOf: mergeCSSLoaderProd(),
- },
- {
- test: /\.less$/,
- oneOf: mergeCSSLoaderProd({
- loader: 'less-loader',
- options: {
- sourceMap: true,
- },
- }),
- },
- {
- test: /\.styl(:?us)?$/,
- oneOf: mergeCSSLoaderProd({
- loader: 'stylus-loader',
- options: {
- sourceMap: true,
- },
- }),
- },
- ],
- },
plugins: [
new CopyWebpackPlugin({
patterns: [
@@ -60,9 +32,6 @@ module.exports = merge(baseConfig, {
NODE_ENV: '"production"',
},
}),
- new MiniCssExtractPlugin({
- filename: '[name].css',
- }),
new webpack.NamedChunksPlugin(),
],
optimization: {
diff --git a/build-config/renderer/webpack.config.base.js b/build-config/renderer/webpack.config.base.js
index ea4f652f..fdf69a36 100644
--- a/build-config/renderer/webpack.config.base.js
+++ b/build-config/renderer/webpack.config.base.js
@@ -1,9 +1,12 @@
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HTMLPlugin = require('html-webpack-plugin')
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const vueLoaderConfig = require('../vue-loader.config')
+const { mergeCSSLoader } = require('../utils')
+const isDev = process.env.NODE_ENV === 'development'
module.exports = {
target: 'electron-renderer',
@@ -46,6 +49,28 @@ module.exports = {
loader: 'babel-loader',
exclude: /node_modules/,
},
+ {
+ test: /\.css$/,
+ oneOf: mergeCSSLoader(),
+ },
+ {
+ test: /\.less$/,
+ oneOf: mergeCSSLoader({
+ loader: 'less-loader',
+ options: {
+ sourceMap: true,
+ },
+ }),
+ },
+ {
+ test: /\.styl(:?us)?$/,
+ oneOf: mergeCSSLoader({
+ loader: 'stylus-loader',
+ options: {
+ sourceMap: true,
+ },
+ }),
+ },
{
test: /\.pug$/,
oneOf: [
@@ -98,5 +123,11 @@ module.exports = {
__dirname,
}),
new VueLoaderPlugin(),
+ new MiniCssExtractPlugin({
+ // Options similar to the same options in webpackOptions.output
+ // both options are optional
+ filename: isDev ? '[name].css' : '[name].[contenthash:8].css',
+ chunkFilename: isDev ? '[id].css' : '[id].[contenthash:8].css',
+ }),
],
}
diff --git a/build-config/renderer/webpack.config.dev.js b/build-config/renderer/webpack.config.dev.js
index 5baf32fa..871afd3e 100644
--- a/build-config/renderer/webpack.config.dev.js
+++ b/build-config/renderer/webpack.config.dev.js
@@ -6,37 +6,9 @@ const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.config.base')
-const { mergeCSSLoaderDev } = require('../utils')
-
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'eval-source-map',
- module: {
- rules: [
- {
- test: /\.css$/,
- oneOf: mergeCSSLoaderDev(),
- },
- {
- test: /\.less$/,
- oneOf: mergeCSSLoaderDev({
- loader: 'less-loader',
- options: {
- sourceMap: true,
- },
- }),
- },
- {
- test: /\.styl(:?us)?$/,
- oneOf: mergeCSSLoaderDev({
- loader: 'stylus-loader',
- options: {
- sourceMap: true,
- },
- }),
- },
- ],
- },
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
diff --git a/build-config/renderer/webpack.config.prod.js b/build-config/renderer/webpack.config.prod.js
index 89986194..e151deac 100644
--- a/build-config/renderer/webpack.config.prod.js
+++ b/build-config/renderer/webpack.config.prod.js
@@ -1,6 +1,5 @@
const path = require('path')
const webpack = require('webpack')
-const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
@@ -8,7 +7,6 @@ const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.config.base')
-const { mergeCSSLoaderProd } = require('../utils')
const { dependencies } = require('../../package.json')
let whiteListedModules = ['vue']
@@ -20,32 +18,6 @@ module.exports = merge(baseConfig, {
externals: [
...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)),
],
- module: {
- rules: [
- {
- test: /\.css$/,
- oneOf: mergeCSSLoaderProd(),
- },
- {
- test: /\.less$/,
- oneOf: mergeCSSLoaderProd({
- loader: 'less-loader',
- options: {
- sourceMap: true,
- },
- }),
- },
- {
- test: /\.styl(:?us)?$/,
- oneOf: mergeCSSLoaderProd({
- loader: 'stylus-loader',
- options: {
- sourceMap: true,
- },
- }),
- },
- ],
- },
plugins: [
new CopyWebpackPlugin({
patterns: [
@@ -60,9 +32,6 @@ module.exports = merge(baseConfig, {
NODE_ENV: '"production"',
},
}),
- new MiniCssExtractPlugin({
- filename: '[name].css',
- }),
new webpack.NamedChunksPlugin(),
],
optimization: {
diff --git a/build-config/utils.js b/build-config/utils.js
index 0a6a9e72..e3a9b5e0 100644
--- a/build-config/utils.js
+++ b/build-config/utils.js
@@ -2,62 +2,21 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const cssLoaderConfig = require('./css-loader.config')
const chalk = require('chalk')
-// merge css-loader in development
-exports.mergeCSSLoaderDev = beforeLoader => {
- const loader = [
- // 这里匹配 `
diff --git a/src/renderer/components/material/InputRange.vue b/src/renderer/components/material/InputRange.vue
deleted file mode 100644
index d63577a7..00000000
--- a/src/renderer/components/material/InputRange.vue
+++ /dev/null
@@ -1,80 +0,0 @@
-
- input(type="range" :class="[$style.range, min ? $style.min : '']" :disabled="disabled" v-model="val" input="handleInput" @change="handleChange")
-
-
-
-
-
-
diff --git a/src/renderer/components/material/ListButtons.vue b/src/renderer/components/material/ListButtons.vue
index 4e5fd3e4..aa588b26 100644
--- a/src/renderer/components/material/ListButtons.vue
+++ b/src/renderer/components/material/ListButtons.vue
@@ -1,31 +1,31 @@
div(:class="$style.btns")
button(type="button" v-if="playBtn" @contextmenu.capture.stop :title="$t('material.list_buttons.play')" @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')
+ 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' v-once)
use(xlink:href='#icon-testPlay')
button(type="button" v-if="listAddBtn" @contextmenu.capture.stop :title="$t('material.list_buttons.add_to')" @click.stop="handleClick('listAdd')")
- 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')
+ 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' v-once)
use(xlink:href='#icon-addTo')
button(type="button" v-if="downloadBtn && setting.download.enable" @contextmenu.capture.stop :title="$t('material.list_buttons.download')" @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')
+ 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' v-once)
use(xlink:href='#icon-download')
//- button(type="button" :title="$t('material.list_buttons.add')" 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" @contextmenu.capture.stop :title="$t('material.list_buttons.start')" @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')
+ 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' v-once)
use(xlink:href='#icon-play')
button(type="button" v-if="pauseBtn" @contextmenu.capture.stop :title="$t('material.list_buttons.pause')" @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')
+ 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' v-once)
use(xlink:href='#icon-pause')
button(type="button" v-if="fileBtn" @contextmenu.capture.stop :title="$t('material.list_buttons.file')" @click.stop="handleClick('file')")
- svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='-61 0 512 512' space='preserve')
+ svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='-61 0 512 512' space='preserve' v-once)
use(xlink:href='#icon-musicFile')
button(type="button" v-if="searchBtn" @contextmenu.capture.stop :title="$t('material.list_buttons.search')" @click.stop="handleClick('search')")
- svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 30.239 30.239' space='preserve')
+ svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 30.239 30.239' space='preserve' v-once)
use(xlink:href='#icon-search')
button(type="button" v-if="removeBtn" :title="$t('material.list_buttons.remove')" @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')
+ 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' v-once)
use(xlink:href='#icon-delete')
diff --git a/src/renderer/components/material/SearchList.vue b/src/renderer/components/material/SearchList.vue
index 4e55a03a..a070478f 100644
--- a/src/renderer/components/material/SearchList.vue
+++ b/src/renderer/components/material/SearchList.vue
@@ -239,7 +239,7 @@ export default {
handleSearch() {
if (!this.text.length) return this.resultList = []
let list = []
- let rxp = new RegExp(this.text.split('').join('.*'), 'i')
+ let rxp = new RegExp(this.text.split('').join('.*') + '.*', 'i')
for (const item of this.list) {
if (rxp.test(`${item.name}${item.singer}${item.albumName ? item.albumName : ''}`)) list.push(item)
}
diff --git a/src/renderer/components/material/VersionModal.vue b/src/renderer/components/material/VersionModal.vue
index feaa7ab7..58a6fa79 100644
--- a/src/renderer/components/material/VersionModal.vue
+++ b/src/renderer/components/material/VersionModal.vue
@@ -46,7 +46,7 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV
| 你可以去
strong.hover.underline(@click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" title="点击打开") 软件发布页
| 或
- strong.hover.underline(@click="handleOpenUrl('https://www.lanzous.com/b906260/')" title="点击打开") 网盘
+ strong.hover.underline(@click="handleOpenUrl('https://www.lanzoux.com/b0bf2cfa/')" title="点击打开") 网盘
| (密码:
strong.hover(@click="handleCopy('glqw')" title="点击复制") glqw
| ) 下载新版本,
@@ -63,7 +63,7 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV
| 你可以去
material-btn(min @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" title="点击打开") 软件发布页
| 或
- material-btn(min @click="handleOpenUrl('https://www.lanzous.com/b906260/')" title="点击打开") 网盘
+ material-btn(min @click="handleOpenUrl('https://www.lanzoux.com/b0bf2cfa/')" title="点击打开") 网盘
| (密码:
strong.hover(@click="handleCopy('glqw')" title="点击复制") glqw
| )下载新版本,
@@ -84,7 +84,7 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV
| 检查方法:打开
material-btn(min @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" title="点击打开") 软件发布页
| 或
- material-btn(min @click="handleOpenUrl('https://www.lanzous.com/b906260/')" title="点击打开") 网盘
+ material-btn(min @click="handleOpenUrl('https://www.lanzoux.com/b0bf2cfa/')" title="点击打开") 网盘
| (密码:
strong.hover(@click="handleCopy('glqw')" title="点击复制") glqw
| )查看它们的
@@ -117,7 +117,7 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV
| 手动更新可以去
strong.hover.underline(@click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" title="点击打开") 软件发布页
| 或
- strong.hover.underline(@click="handleOpenUrl('https://www.lanzous.com/b906260/')" title="点击打开") 网盘
+ strong.hover.underline(@click="handleOpenUrl('https://www.lanzoux.com/b0bf2cfa/')" title="点击打开") 网盘
| (密码:
strong.hover(@click="handleCopy('glqw')" title="点击复制") glqw
| ) 下载,
diff --git a/src/renderer/components/material/listAddModal.vue b/src/renderer/components/material/listAddModal.vue
index 99c27f65..9386f88d 100644
--- a/src/renderer/components/material/listAddModal.vue
+++ b/src/renderer/components/material/listAddModal.vue
@@ -61,7 +61,7 @@ export default {
this.defaultList,
this.loveList,
...this.userList,
- ].filter(l => l.id != this.excludeListId)
+ ].filter(l => l.id != this.excludeListId.includes(l.id))
},
spaceNum() {
return this.lists.length < 2 ? 0 : (3 - this.lists.length % 3 - 1)
@@ -91,7 +91,7 @@ export default {
this.newListName = event.target.value = ''
this.isEditing = false
if (!name) return
- this.createUserList(name)
+ this.createUserList({ name })
},
},
}
diff --git a/src/renderer/components/material/listAddMultipleModal.vue b/src/renderer/components/material/listAddMultipleModal.vue
index 32a4be14..2c9c370d 100644
--- a/src/renderer/components/material/listAddMultipleModal.vue
+++ b/src/renderer/components/material/listAddMultipleModal.vue
@@ -62,7 +62,7 @@ export default {
this.defaultList,
this.loveList,
...this.userList,
- ].filter(l => l.id != this.excludeListId)
+ ].filter(l => l.id != this.excludeListId.includes(l.id))
},
spaceNum() {
return this.lists.length < 2 ? 0 : (3 - this.lists.length % 3 - 1)
@@ -92,7 +92,7 @@ export default {
this.newListName = event.target.value = ''
this.isEditing = false
if (!name) return
- this.createUserList(name)
+ this.createUserList({ name })
},
},
}
diff --git a/src/renderer/event/index.js b/src/renderer/event/index.js
index 495d4085..d818b955 100644
--- a/src/renderer/event/index.js
+++ b/src/renderer/event/index.js
@@ -1,21 +1,22 @@
import Vue from 'vue'
import keyBind from '../utils/keyBind'
-import { rendererOn, rendererSend, NAMES } from '../../common/ipc'
+import { rendererOn, rendererSend, NAMES, rendererInvoke } from '../../common/ipc'
import { base as baseName } from './names'
-import Store from 'electron-store'
import { common as hotKeyNamesCommon } from '../../common/hotKey'
const eventHub = window.eventHub = new Vue()
-const electronStore_hotKey = window.electronStore_hotKey = new Store({
- name: 'hotKey',
-})
-
window.isEditingHotKey = false
-const appHotKeyConfig = window.appHotKeyConfig = {
- local: electronStore_hotKey.get('local'),
- global: electronStore_hotKey.get('global'),
+let appHotKeyConfig = {
+ local: {},
+ global: {},
}
+rendererInvoke(NAMES.mainWindow.get_hot_key).then(({ local, global }) => {
+ appHotKeyConfig = window.appHotKeyConfig = {
+ local,
+ global,
+ }
+})
eventHub.$on(baseName.bindKey, () => {
keyBind.bindKey((key, type, event, keys) => {
diff --git a/src/renderer/lang/en-us/core/player.json b/src/renderer/lang/en-us/core/player.json
index dff70093..d21ff4ad 100644
--- a/src/renderer/lang/en-us/core/player.json
+++ b/src/renderer/lang/en-us/core/player.json
@@ -17,5 +17,13 @@
"hide_detail": "Hide detail page",
"name": "Name: ",
"singer": "Artist: ",
- "album": "Album: "
+ "album": "Album: ",
+ "add_music_to": "Add the current song to...",
+ "desktop_lyric_on": "Open Desktop Lyrics",
+ "desktop_lyric_off": "Close Desktop Lyrics",
+ "play_toggle_mode_list_loop": "List Loop",
+ "play_toggle_mode_random": "List Random",
+ "play_toggle_mode_list": "Play in order",
+ "play_toggle_mode_single_loop": "Single Loop",
+ "play_toggle_mode_off": "Disable"
}
diff --git a/src/renderer/lang/en-us/view/setting.json b/src/renderer/lang/en-us/view/setting.json
index 116b46c4..0b6a455f 100644
--- a/src/renderer/lang/en-us/view/setting.json
+++ b/src/renderer/lang/en-us/view/setting.json
@@ -1,6 +1,7 @@
{
"basic": "General",
"basic_theme": "Theme",
+ "basic_show_animation": "Show switching animation",
"basic_animation_title": "Animation effect of the pop-up layer",
"basic_animation": "Random pop-up animation",
"basic_source_title": "Choose a music source",
@@ -35,6 +36,7 @@
"play_toggle_random": "Playlist shuffle",
"play_toggle_list": "Play in order",
"play_toggle_single_loop": "Single repeat",
+ "play_lyric_transition": "Show lyrics translation",
"play_quality_title": "The 320k quality is preferred for playing",
"play_quality": "Prefer High Quality 320k",
"play_task_bar_title": "Show playing progress on the taskbar",
@@ -48,6 +50,7 @@
"desktop_lyric_enable": "Display lyrics",
"desktop_lyric_lock": "Lock lyrics",
"desktop_lyric_always_on_top": "Make the lyrics always above other windows",
+ "desktop_lyric_lock_screen": "It is not allowed to drag the lyrics window out of the main screen",
"search": "Search",
"search_hot_title": "Select whether to show popular searches",
diff --git a/src/renderer/lang/en-us/view/song_list.json b/src/renderer/lang/en-us/view/song_list.json
index e7993d13..8c600d5b 100644
--- a/src/renderer/lang/en-us/view/song_list.json
+++ b/src/renderer/lang/en-us/view/song_list.json
@@ -6,5 +6,7 @@
"input_text": "Enter songlist link or songlist ID",
"tip_1": "Cross-source playlists are not supported, please confirm whether the playlist to be opened corresponds to the current playlist source",
"tip_2": "If you encounter a link to a playlist that cannot be opened, welcome feedback",
- "tip_3": "Kugou source does not support opening with playlist ID, but supports Kugou code opening"
+ "tip_3": "Kugou source does not support opening with playlist ID, but supports Kugou code opening",
+ "play_all": "Play",
+ "add_all": "Collect"
}
diff --git a/src/renderer/lang/zh-cn/core/player.json b/src/renderer/lang/zh-cn/core/player.json
index 36936ad5..8a32b56c 100644
--- a/src/renderer/lang/zh-cn/core/player.json
+++ b/src/renderer/lang/zh-cn/core/player.json
@@ -17,5 +17,13 @@
"hide_detail": "隐藏详情页",
"name": "歌曲名:",
"singer": "艺术家:",
- "album": "专辑名:"
+ "album": "专辑名:",
+ "add_music_to": "添加当前歌曲到...",
+ "desktop_lyric_on": "开启桌面歌词",
+ "desktop_lyric_off": "关闭桌面歌词",
+ "play_toggle_mode_list_loop": "列表循环",
+ "play_toggle_mode_random": "列表随机",
+ "play_toggle_mode_list": "顺序播放",
+ "play_toggle_mode_single_loop": "单曲循环",
+ "play_toggle_mode_off": "禁用"
}
diff --git a/src/renderer/lang/zh-cn/view/setting.json b/src/renderer/lang/zh-cn/view/setting.json
index 3983fcb3..8d4e2f80 100644
--- a/src/renderer/lang/zh-cn/view/setting.json
+++ b/src/renderer/lang/zh-cn/view/setting.json
@@ -3,6 +3,7 @@
"basic_theme": "主题颜色",
"basic_animation_title": "弹出层的动画效果",
"basic_animation": "弹出层随机动画",
+ "basic_show_animation": "显示切换动画",
"basic_source_title": "选择音乐来源",
"basic_source_test": "测试接口(几乎软件的所有功能都可用)",
"basic_source_temp": "临时接口(软件的某些功能不可用,建议测试接口不可用再使用本接口)",
@@ -35,6 +36,7 @@
"play_toggle_random": "列表随机",
"play_toggle_list": "顺序播放",
"play_toggle_single_loop": "单曲循环",
+ "play_lyric_transition": "显示歌词翻译",
"play_quality_title": "启用时将优先播放320K品质的歌曲",
"play_quality": "优先播放高品质音乐",
"play_task_bar_title": "在任务栏上显示当前歌曲播放进度",
@@ -48,6 +50,7 @@
"desktop_lyric_enable": "显示歌词",
"desktop_lyric_lock": "锁定歌词",
"desktop_lyric_always_on_top": "使歌词总是在其他窗口之上",
+ "desktop_lyric_lock_screen": "不允许歌词窗口拖出主屏幕之外",
"search": "搜索设置",
"search_hot_title": "是否显示热门搜索",
diff --git a/src/renderer/lang/zh-cn/view/song_list.json b/src/renderer/lang/zh-cn/view/song_list.json
index aca6a947..bbbadf6b 100644
--- a/src/renderer/lang/zh-cn/view/song_list.json
+++ b/src/renderer/lang/zh-cn/view/song_list.json
@@ -6,5 +6,7 @@
"input_text": "输入歌单链接或歌单ID",
"tip_1": "不支持跨源打开歌单,请确认要打开的歌单与当前歌单源是否对应",
"tip_2": "若遇到无法打开的歌单链接,欢迎反馈",
- "tip_3": "酷狗源不支持用歌单ID打开,但支持酷狗码打开"
+ "tip_3": "酷狗源不支持用歌单ID打开,但支持酷狗码打开",
+ "play_all": "播放",
+ "add_all": "收藏"
}
diff --git a/src/renderer/lang/zh-tw/core/player.json b/src/renderer/lang/zh-tw/core/player.json
index 513ea11f..573f766c 100644
--- a/src/renderer/lang/zh-tw/core/player.json
+++ b/src/renderer/lang/zh-tw/core/player.json
@@ -17,5 +17,13 @@
"hide_detail": "隱藏詳情頁",
"name": "歌曲名:",
"singer": "藝術家:",
- "album": "專輯名:"
+ "album": "專輯名:",
+ "add_music_to": "添加當前歌曲到...",
+ "desktop_lyric_on": "開啟桌面歌詞",
+ "desktop_lyric_off": "關閉桌面歌詞",
+ "play_toggle_mode_list_loop": "列表循環",
+ "play_toggle_mode_random": "列表隨機",
+ "play_toggle_mode_list": "順序播放",
+ "play_toggle_mode_single_loop": "單曲循環",
+ "play_toggle_mode_off": "禁用"
}
diff --git a/src/renderer/lang/zh-tw/view/setting.json b/src/renderer/lang/zh-tw/view/setting.json
index 0f7ff439..f5eecd1e 100644
--- a/src/renderer/lang/zh-tw/view/setting.json
+++ b/src/renderer/lang/zh-tw/view/setting.json
@@ -3,6 +3,7 @@
"basic_theme": "主題顏色",
"basic_animation_title": "彈出層的動畫效果",
"basic_animation": "彈出層隨機動畫",
+ "basic_show_animation": "顯示切換動畫",
"basic_source_title": "選擇音樂來源",
"basic_source_test": "測試接口(幾乎軟件的所有功能都可用)",
"basic_source_temp": "臨時接口(軟件的某些功能不可用,建議測試接口不可用再使用本接口)",
@@ -34,6 +35,7 @@
"play_toggle_random": "列表隨機",
"play_toggle_list": "順序播放",
"play_toggle_single_loop": "單曲循環",
+ "play_lyric_transition": "顯示歌詞翻譯",
"play_quality_title": "啟用時將優先播放320K品質的歌曲",
"play_quality": "優先播放高品質音樂",
"play_task_bar_title": "在任務欄上顯示當前歌曲播放進度",
@@ -46,6 +48,7 @@
"desktop_lyric_enable": "顯示歌詞",
"desktop_lyric_lock": "鎖定歌詞",
"desktop_lyric_always_on_top": "使歌詞總是在其他窗口之上",
+ "desktop_lyric_lock_screen": "不允許歌詞窗口拖出主屏幕之外",
"search": "搜索設置",
"search_hot_title": "是否顯示熱門搜索",
"search_hot": "熱門搜索",
diff --git a/src/renderer/lang/zh-tw/view/song_list.json b/src/renderer/lang/zh-tw/view/song_list.json
index d47eb2d0..a52e8aa9 100644
--- a/src/renderer/lang/zh-tw/view/song_list.json
+++ b/src/renderer/lang/zh-tw/view/song_list.json
@@ -6,5 +6,7 @@
"input_text": "輸入歌單鏈接或歌單ID",
"tip_1": "不支持跨源打開歌單,請確認要打開的歌單與當前歌單源是否對應",
"tip_2": "若遇到無法打開的歌單鏈接,歡迎反饋",
- "tip_3": "酷狗源不支持用歌單ID打開,但支持酷狗碼打開"
+ "tip_3": "酷狗源不支持用歌單ID打開,但支持酷狗碼打開",
+ "play_all": "播放",
+ "add_all": "收藏"
}
diff --git a/src/renderer/main.js b/src/renderer/main.js
index 18898ec9..bac4f660 100644
--- a/src/renderer/main.js
+++ b/src/renderer/main.js
@@ -16,14 +16,45 @@ import store from './store'
import '../common/error'
+import { getSetting } from './utils'
+import languageList from '@/lang/languages.json'
+import { rendererSend, NAMES } from '../common/ipc'
+
sync(store, router)
Vue.config.productionTip = false
-new Vue({
- router,
- store,
- i18n,
- el: '#root',
- render: h => h(App),
+
+getSetting().then(({ setting, version }) => {
+ // Set language automatically
+ if (!window.i18n.availableLocales.includes(setting.langId)) {
+ let langId = null
+ let locale = window.navigator.language.toLocaleLowerCase()
+ if (window.i18n.availableLocales.includes(locale)) {
+ langId = locale
+ } else {
+ for (const lang of languageList) {
+ if (lang.alternate == locale) {
+ langId = lang.locale
+ break
+ }
+ }
+ if (langId == null) langId = 'en-us'
+ }
+ setting.langId = langId
+ rendererSend(NAMES.mainWindow.set_app_setting, setting)
+ console.log('Set lang', setting.langId)
+ }
+ window.i18n.locale = setting.langId
+ store.commit('setSetting', setting)
+ store.commit('setSettingVersion', version)
+
+ new Vue({
+ router,
+ store,
+ i18n,
+ el: '#root',
+ render: h => h(App),
+ })
})
+
diff --git a/src/renderer/store/modules/download.js b/src/renderer/store/modules/download.js
index 8166686d..e62bbeac 100644
--- a/src/renderer/store/modules/download.js
+++ b/src/renderer/store/modules/download.js
@@ -177,7 +177,7 @@ const saveMeta = (downloadInfo, filePath, isEmbedPic, isEmbedLyric) => {
: Promise.resolve(),
isEmbedLyric
? downloadInfo.musicInfo.lrc
- ? Promise.resolve(downloadInfo.musicInfo.lrc)
+ ? Promise.resolve({ lyric: downloadInfo.musicInfo.lrc, tlyric: downloadInfo.musicInfo.tlrc || '' })
: music[downloadInfo.musicInfo.source].getLyric(downloadInfo.musicInfo).promise.catch(err => {
console.log(err)
return null
@@ -190,7 +190,7 @@ const saveMeta = (downloadInfo, filePath, isEmbedPic, isEmbedLyric) => {
artist: downloadInfo.musicInfo.singer,
album: downloadInfo.musicInfo.albumName,
APIC: imgUrl,
- lyrics,
+ lyrics: lyrics.lyric,
})
})
}
@@ -202,10 +202,10 @@ const saveMeta = (downloadInfo, filePath, isEmbedPic, isEmbedLyric) => {
*/
const downloadLyric = (downloadInfo, filePath) => {
const promise = downloadInfo.musicInfo.lrc
- ? Promise.resolve(downloadInfo.musicInfo.lrc)
+ ? Promise.resolve({ lyric: downloadInfo.musicInfo.lrc, tlyric: downloadInfo.musicInfo.tlrc || '' })
: music[downloadInfo.musicInfo.source].getLyric(downloadInfo.musicInfo).promise
- promise.then(lrc => {
- if (lrc) saveLrc(filePath.replace(/(mp3|flac|ape|wav)$/, 'lrc'), lrc)
+ promise.then(lrcs => {
+ if (lrcs.lyric) saveLrc(filePath.replace(/(mp3|flac|ape|wav)$/, 'lrc'), lrcs.lyric)
})
}
@@ -218,13 +218,13 @@ const refreshUrl = function(commit, downloadInfo) {
if (!dl) return
dl.refreshUrl(result.url)
dl.start().catch(err => {
- commit('onError', downloadInfo)
+ commit('onError', { downloadInfo, errorMsg: err.message })
commit('setStatusText', { downloadInfo, text: err.message })
this.dispatch('download/startTask')
})
}).catch(err => {
// console.log(err)
- commit('onError', downloadInfo)
+ commit('onError', { downloadInfo, errorMsg: err.message })
commit('setStatusText', { downloadInfo, text: err.message })
this.dispatch('download/startTask')
})
@@ -299,7 +299,7 @@ const actions = {
try {
await checkPath(rootState.setting.download.savePath)
} catch (error) {
- commit('onError', downloadInfo)
+ commit('onError', { downloadInfo, errorMsg: error.message })
commit('setStatusText', '检查下载目录出错: ' + error.message)
await dispatch('startTask')
return
@@ -324,9 +324,14 @@ const actions = {
console.log('on complate')
},
onError(err) {
+ // console.log(err)
+ if (err.code == 'EPERM') {
+ commit('onError', { downloadInfo, errorMsg: '歌曲下载目录没有写入权限,请尝试更改歌曲保存路径' })
+ return
+ }
// console.log(tryNum[downloadInfo.key])
if (++tryNum[downloadInfo.key] > 2) {
- commit('onError', downloadInfo)
+ commit('onError', { downloadInfo, errorMsg: err.message })
dispatch('startTask')
return
}
@@ -375,7 +380,7 @@ const actions = {
dls[downloadInfo.key] = download(options)
}).catch(err => {
// console.log(err.message)
- commit('onError', downloadInfo)
+ commit('onError', { downloadInfo, errorMsg: err.message })
commit('setStatusText', { downloadInfo, text: err.message })
dispatch('startTask')
})
@@ -437,7 +442,7 @@ const actions = {
try {
await dl.start()
} catch (error) {
- commit('onError', downloadInfo)
+ commit('onError', { downloadInfo, errorMsg: error.message })
commit('setStatusText', error.message)
await dispatch('startTask')
}
@@ -448,7 +453,7 @@ const actions = {
startTasks(store, list) {
if (isRuningActionTask) return
isRuningActionTask = true
- return startTasks(store, [...list]).finally(() => {
+ return startTasks(store, list.filter(item => !(item.isComplate || item.status == state.downloadStatus.RUN || item.status == state.downloadStatus.WAITING))).finally(() => {
isRuningActionTask = false
})
},
@@ -522,9 +527,9 @@ const mutations = {
downloadInfo.status = state.downloadStatus.COMPLETED
downloadInfo.statusText = '下载完成'
},
- onError(state, downloadInfo) {
+ onError(state, { downloadInfo, errorMsg }) {
downloadInfo.status = state.downloadStatus.ERROR
- downloadInfo.statusText = '任务出错'
+ downloadInfo.statusText = errorMsg || '任务出错'
},
onStart(state, downloadInfo) {
downloadInfo.status = state.downloadStatus.RUN
diff --git a/src/renderer/store/modules/list.js b/src/renderer/store/modules/list.js
index 98560d4a..04978ce3 100644
--- a/src/renderer/store/modules/list.js
+++ b/src/renderer/store/modules/list.js
@@ -28,6 +28,12 @@ const state = {
list: [],
location: 0,
},
+ tempList: {
+ id: 'temp',
+ name: '临时列表',
+ list: [],
+ location: 0,
+ },
userList: [],
}
@@ -140,15 +146,19 @@ const mutations = {
if (!targetList) return
Object.assign(targetList.list[index], data)
},
- createUserList(state, name) {
- let newList = {
- name,
- id: `userlist_${Date.now()}`,
- list: [],
- location: 0,
+ createUserList(state, { name, id = `userlist_${Date.now()}`, list = [] }) {
+ let newList = state.userList.find(item => item.id === id)
+ if (!newList) {
+ newList = {
+ name,
+ id,
+ list: [],
+ location: 0,
+ }
+ state.userList.push(newList)
+ allListUpdate(newList)
}
- state.userList.push(newList)
- allListUpdate(newList)
+ this.commit('list/listAddMultiple', { id, list })
},
removeUserList(state, index) {
let list = state.userList.splice(index, 1)[0]
diff --git a/src/renderer/store/modules/player.js b/src/renderer/store/modules/player.js
index e61ee64f..16fbc8a0 100644
--- a/src/renderer/store/modules/player.js
+++ b/src/renderer/store/modules/player.js
@@ -9,6 +9,7 @@ const state = {
playIndex: -1,
changePlay: false,
isShowPlayerDetail: false,
+ playedList: [],
}
let urlRequest
@@ -22,6 +23,7 @@ const getters = {
changePlay: satte => satte.changePlay,
playIndex: state => state.playIndex,
isShowPlayerDetail: state => state.isShowPlayerDetail,
+ playedList: state => state.playedList,
}
// actions
@@ -58,9 +60,9 @@ const actions = {
getLrc({ commit, state }, musicInfo) {
if (lrcRequest && lrcRequest.cancelHttp) lrcRequest.cancelHttp()
lrcRequest = music[musicInfo.source].getLyric(musicInfo)
- return lrcRequest.promise.then(lrc => {
+ return lrcRequest.promise.then(({ lyric, tlyric }) => {
lrcRequest = null
- commit('setLrc', { musicInfo, lrc })
+ commit('setLrc', { musicInfo, lyric, tlyric })
}).catch(err => {
lrcRequest = null
return Promise.reject(err)
@@ -78,12 +80,14 @@ const mutations = {
datas.musicInfo.img = datas.url
},
setLrc(state, datas) {
- datas.musicInfo.lrc = datas.lrc
+ datas.musicInfo.lrc = datas.lyric
+ datas.musicInfo.tlrc = datas.tlyric
},
setList(state, { list, index }) {
state.listInfo = list
state.playIndex = index
state.changePlay = true
+ if (state.playedList.length) this.commit('player/clearPlayedList')
},
setPlayIndex(state, index) {
state.playIndex = index
@@ -96,6 +100,16 @@ const mutations = {
resetChangePlay(state) {
state.changePlay = false
},
+ setPlayedList(state, item) {
+ if (state.playedList.includes(item)) return
+ state.playedList.push(item)
+ },
+ removePlayedList(state, index) {
+ state.playedList.splice(index, 1)
+ },
+ clearPlayedList(state) {
+ state.playedList = []
+ },
visiblePlayerDetail(state, visible) {
state.isShowPlayerDetail = visible
},
diff --git a/src/renderer/store/modules/search.js b/src/renderer/store/modules/search.js
index f8b06b80..ff3a3e2b 100644
--- a/src/renderer/store/modules/search.js
+++ b/src/renderer/store/modules/search.js
@@ -1,15 +1,5 @@
-import Store from 'electron-store'
import music from '../../utils/music'
-const electronStore_data = window.electronStore_data = new Store({
- name: 'data',
-})
-let historyList = electronStore_data.get('searchHistoryList')
-if (historyList == null) {
- historyList = []
- electronStore_data.set('searchHistoryList', historyList)
-}
-
const sources = []
const sourceList = {}
const sourceMaxPage = {}
@@ -103,7 +93,7 @@ const state = {
allPage: 1,
total: 0,
sourceMaxPage,
- historyList,
+ historyList: [],
}
// getters
@@ -211,6 +201,9 @@ const mutations = {
clearHistory(state) {
state.historyList = []
},
+ setHistory(state, list) {
+ state.historyList = list
+ },
}
export default {
diff --git a/src/renderer/store/modules/songList.js b/src/renderer/store/modules/songList.js
index 1bf8dd93..731b04e1 100644
--- a/src/renderer/store/modules/songList.js
+++ b/src/renderer/store/modules/songList.js
@@ -79,37 +79,29 @@ const actions = {
cache.has(key)
? Promise.resolve(cache.get(key))
: music[source].songList.getListDetail(id, page)
- ).then(result => commit('setListDetail', { result, key, page }))
+ ).then(result => commit('setListDetail', { result, key, source, id, page }))
},
-/* getListDetailAll({ state, rootState }, id) {
+ getListDetailAll({ state, rootState }, id) {
let source = rootState.setting.songList.source
- let key = `sdetail__${source}__${id}__all`
- if (cache.has(key)) return Promise.resolve(cache.get(key))
- music[source].songList.getListDetail(id, 1).then(result => {
- let data = { list: result.list, id }
- if (result.total <= result.limit) {
- data = { list: result.list, id }
- cache.set(key, data)
- return data
- }
+ const loadData = (id, page) => {
+ let key = `sdetail__${source}__${id}__${page}`
+ return cache.has(key) ? Promise.resolve(cache.get(key)) : music[source].songList.getListDetail(id, page).then(result => {
+ cache.set(key, result)
+ return result
+ })
+ }
+ return loadData(id, 1).then(result => {
+ if (result.total <= result.limit) return result.list
let maxPage = Math.ceil(result.total / result.limit)
const loadDetail = (loadPage = 1) => {
- let task = []
- let loadNum = 0
- while (loadPage <= maxPage && loadNum < 3) {
- task.push(music[source].songList.getListDetail(id, ++loadPage))
- loadNum++
- }
return loadPage == maxPage
- ? Promise.all(task)
- : Promise.all(task).then(result => loadDetail(loadPage).then(result2 => [...result, ...result2]))
+ ? loadData(id, ++loadPage).then(result => result.list)
+ : loadData(id, ++loadPage).then(result1 => loadDetail(loadPage).then(result2 => [...result1.list, ...result2]))
}
- return loadDetail().then(result2 => {
- console.log(result2)
- })
+ return loadDetail().then(result2 => [...result.list, ...result2])
})
- }, */
+ },
}
// mitations
@@ -129,8 +121,10 @@ const mutations = {
state.list.key = key
cache.set(key, result)
},
- setListDetail(state, { result, key, page }) {
+ setListDetail(state, { result, key, source, id, page }) {
state.listDetail.list = result.list
+ state.listDetail.id = id
+ state.listDetail.source = source
state.listDetail.total = result.total
state.listDetail.limit = result.limit
state.listDetail.page = page
@@ -153,6 +147,8 @@ const mutations = {
},
clearListDetail(state) {
state.listDetail = {
+ id: null,
+ source: null,
list: [],
desc: null,
total: 0,
diff --git a/src/renderer/store/mutations.js b/src/renderer/store/mutations.js
index 1d9648b4..8e23cb76 100644
--- a/src/renderer/store/mutations.js
+++ b/src/renderer/store/mutations.js
@@ -9,12 +9,12 @@ export default {
setSetting(state, val) {
state.setting = val
},
- setAgreePact(state) {
- state.setting.isAgreePact = true
- },
setSettingVersion(state, val) {
state.settingVersion = val
},
+ setAgreePact(state) {
+ state.setting.isAgreePact = true
+ },
setLeaderboard(state, { tabId, source }) {
if (tabId != null) state.setting.leaderboard.tabId = tabId
if (source != null) state.setting.leaderboard.source = source
@@ -49,6 +49,12 @@ export default {
state.setting.player.volume = val
}
},
+ setPlayNextMode(state, val) {
+ state.setting.player.togglePlayMethod = val
+ },
+ setVisibleDesktopLyric(state, val) {
+ state.setting.desktopLyric.enable = val
+ },
setMediaDeviceId(state, val) {
state.setting.player.mediaDeviceId = val
},
diff --git a/src/renderer/store/state.js b/src/renderer/store/state.js
index 23a968c5..96b7ad03 100644
--- a/src/renderer/store/state.js
+++ b/src/renderer/store/state.js
@@ -1,67 +1,9 @@
-
// const isDev = process.env.NODE_ENV === 'development'
-import Store from 'electron-store'
import { windowSizeList } from '../../common/config'
import { version } from '../../../package.json'
-import { rendererSend, rendererInvoke, NAMES } from '../../common/ipc'
-import languageList from '@/lang/languages.json'
-import path from 'path'
-import { openDirInExplorer } from '../utils'
-
-
-const electronStore_config = window.electronStore_config = new Store({
- name: 'config',
-})
-let setting = electronStore_config.get('setting')
-let settingVersion = electronStore_config.get('version')
process.versions.app = version
-// Set language automatically
-if (!window.i18n.availableLocales.includes(setting.langId)) {
- let langId = null
- let locale = window.navigator.language.toLocaleLowerCase()
- if (window.i18n.availableLocales.includes(locale)) {
- langId = locale
- } else {
- for (const lang of languageList) {
- if (lang.alternate == locale) {
- langId = lang.locale
- break
- }
- }
- if (langId == null) langId = 'en-us'
- }
- setting.langId = langId
- electronStore_config.set('setting', setting)
- rendererSend(NAMES.mainWindow.set_app_setting, setting)
- console.log('Set lang', setting.langId)
-}
-
-window.i18n.locale = setting.langId
-
-try {
- window.electronStore_list = new Store({
- name: 'playList',
- clearInvalidConfig: false,
- })
-} catch (error) {
- rendererInvoke(NAMES.mainWindow.get_data_path).then(dataPath => {
- let filePath = path.join(dataPath, 'playList.json.bak')
- rendererInvoke(NAMES.mainWindow.show_dialog, {
- type: 'error',
- message: window.i18n.t('store.state.load_list_file_error_title'),
- detail: window.i18n.t('store.state.load_list_file_error_detail', {
- path: filePath,
- detail: error.message,
- }),
- }).then(() => openDirInExplorer(filePath))
- })
- window.electronStore_list = new Store({
- name: 'playList',
- })
-}
-
export default {
themes: [
@@ -144,8 +86,8 @@ export default {
downloadProgress: null,
},
userInfo: null,
- setting,
- settingVersion,
+ setting: null,
+ settingVersion: null,
windowSizeList,
}
diff --git a/src/renderer/utils/download/Downloader.js b/src/renderer/utils/download/Downloader.js
index bfe89ca2..1dc2e7fb 100644
--- a/src/renderer/utils/download/Downloader.js
+++ b/src/renderer/utils/download/Downloader.js
@@ -172,7 +172,7 @@ class Task extends EventEmitter {
this.chunkInfo.startByte = 0
this.resumeLastChunk = null
this.progress.downloaded = 0
- if (unlinkErr) this.__handleError(unlinkErr)
+ if (unlinkErr && unlinkErr.code !== 'ENOENT') this.__handleError(unlinkErr)
})
})
}
diff --git a/src/renderer/utils/index.js b/src/renderer/utils/index.js
index d9faabdd..3b30ed43 100644
--- a/src/renderer/utils/index.js
+++ b/src/renderer/utils/index.js
@@ -1,4 +1,5 @@
import fs from 'fs'
+import path from 'path'
import { shell, clipboard } from 'electron'
import crypto from 'crypto'
import { rendererSend, rendererInvoke, NAMES } from '../../common/ipc'
@@ -341,3 +342,22 @@ export const getProxyInfo = () => window.globalObj.proxy.enable
export const assertApiSupport = source => window.globalObj.qualityList[source] != undefined
+
+export const getSetting = () => rendererInvoke(NAMES.mainWindow.get_setting)
+export const saveSetting = () => rendererInvoke(NAMES.mainWindow.set_app_setting)
+
+export const getPlayList = () => rendererInvoke(NAMES.mainWindow.get_playlist).catch(error => {
+ rendererInvoke(NAMES.mainWindow.get_data_path).then(dataPath => {
+ let filePath = path.join(dataPath, 'playList.json.bak')
+ rendererInvoke(NAMES.mainWindow.show_dialog, {
+ type: 'error',
+ message: window.i18n.t('store.state.load_list_file_error_title'),
+ detail: window.i18n.t('store.state.load_list_file_error_detail', {
+ path: filePath,
+ detail: error.message,
+ }),
+ }).then(() => openDirInExplorer(filePath))
+ })
+ return rendererInvoke(NAMES.mainWindow.get_playlist, true)
+})
+
diff --git a/src/renderer/utils/music/api-source-info.js b/src/renderer/utils/music/api-source-info.js
index 2d5b1cfb..74379655 100644
--- a/src/renderer/utils/music/api-source-info.js
+++ b/src/renderer/utils/music/api-source-info.js
@@ -12,7 +12,7 @@ module.exports = [
wy: ['128k'],
mg: ['128k'],
xm: ['128k'],
- bd: ['128k'],
+ // bd: ['128k'],
},
},
{
diff --git a/src/renderer/utils/music/bd/index.js b/src/renderer/utils/music/bd/index.js
index c83fe67e..de780c90 100644
--- a/src/renderer/utils/music/bd/index.js
+++ b/src/renderer/utils/music/bd/index.js
@@ -21,7 +21,7 @@ const bd = {
},
getLyric(songInfo) {
const requestObj = this.getMusicInfo(songInfo)
- requestObj.promise = requestObj.promise.then(info => httpFetch(info.lrclink).promise.then(resp => resp.body))
+ requestObj.promise = requestObj.promise.then(info => httpFetch(info.lrclink).promise.then(resp => ({ lyric: resp.body, tlyric: '' })))
return requestObj
},
// getLyric(songInfo) {
diff --git a/src/renderer/utils/music/index.js b/src/renderer/utils/music/index.js
index 59ecc964..ae882ab5 100644
--- a/src/renderer/utils/music/index.js
+++ b/src/renderer/utils/music/index.js
@@ -34,10 +34,10 @@ const sources = {
name: '虾米音乐',
id: 'xm',
},
- {
- name: '百度音乐',
- id: 'bd',
- },
+ // {
+ // name: '百度音乐',
+ // id: 'bd',
+ // },
],
kw,
kg,
diff --git a/src/renderer/utils/music/kg/lyric.js b/src/renderer/utils/music/kg/lyric.js
index dab3fc00..52b66612 100644
--- a/src/renderer/utils/music/kg/lyric.js
+++ b/src/renderer/utils/music/kg/lyric.js
@@ -1,4 +1,41 @@
import { httpFetch } from '../../request'
+import { decodeLyric } from './util'
+
+const parseLyric = str => {
+ str = str.replace(/(?:<\d+,\d+,\d+>|\r)/g, '')
+ let trans = str.match(/\[language:([\w=\\/+]+)\]/)
+ let tlyric
+ if (trans) {
+ str = str.replace(/\[language:[\w=\\/+]+\]\n/, '')
+ let json = JSON.parse(Buffer.from(trans[1], 'base64').toString())
+ for (const item of json.content) {
+ if (item.type == 1) {
+ tlyric = item.lyricContent
+ break
+ }
+ }
+ }
+ let i = 0
+ let lyric = str.replace(/\[((\d+),\d+)\].*/g, str => {
+ let result = str.match(/\[((\d+),\d+)\].*/)
+ let time = parseInt(result[2])
+ let ms = time % 1000
+ time /= 1000
+ let h = parseInt(time / 3600).toString().padStart(2, '0')
+ time %= 3600
+ let m = parseInt(time / 60).toString().padStart(2, '0')
+ time %= 60
+ let s = parseInt(time).toString().padStart(2, '0')
+ time = `${h}:${m}:${s}.${ms}`
+ if (tlyric) tlyric[i] = `[${time}]${tlyric[i++][0]}`
+ return str.replace(result[1], time)
+ })
+ tlyric = tlyric ? tlyric.join('\n') : ''
+ return {
+ lyric,
+ tlyric,
+ }
+}
export default {
getIntv(interval) {
@@ -11,8 +48,30 @@ export default {
}
return parseInt(intv)
},
- getLyric(songInfo, tryNum = 0) {
- let requestObj = httpFetch(`http://m.kugou.com/app/i/krc.php?cmd=100&keyword=${encodeURIComponent(songInfo.name)}&hash=${songInfo.hash}&timelength=${songInfo._interval || this.getIntv(songInfo.interval)}&d=0.38664927426725626`, {
+ // getLyric(songInfo, tryNum = 0) {
+ // let requestObj = httpFetch(`http://m.kugou.com/app/i/krc.php?cmd=100&keyword=${encodeURIComponent(songInfo.name)}&hash=${songInfo.hash}&timelength=${songInfo._interval || this.getIntv(songInfo.interval)}&d=0.38664927426725626`, {
+ // headers: {
+ // 'KG-RC': 1,
+ // 'KG-THash': 'expand_search_manager.cpp:852736169:451',
+ // 'User-Agent': 'KuGou2012-9020-ExpandSearchManager',
+ // },
+ // })
+ // requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
+ // if (statusCode !== 200) {
+ // if (tryNum > 5) return Promise.reject('歌词获取失败')
+ // let tryRequestObj = this.getLyric(songInfo, ++tryNum)
+ // requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
+ // return tryRequestObj.promise
+ // }
+ // return {
+ // lyric: body,
+ // tlyric: '',
+ // }
+ // })
+ // return requestObj
+ // },
+ searchLyric(name, hash, time, tryNum = 0) {
+ let requestObj = httpFetch(`http://lyrics.kugou.com/search?ver=1&man=yes&client=pc&keyword=${encodeURIComponent(name)}&hash=${hash}&timelength=${time}`, {
headers: {
'KG-RC': 1,
'KG-THash': 'expand_search_manager.cpp:852736169:451',
@@ -22,11 +81,49 @@ export default {
requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
if (statusCode !== 200) {
if (tryNum > 5) return Promise.reject('歌词获取失败')
- let tryRequestObj = this.getLyric(songInfo, ++tryNum)
+ let tryRequestObj = this.searchLyric(name, hash, time, ++tryNum)
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
- return body
+ if (body.candidates.length) {
+ let info = body.candidates[0]
+ return { id: info.id, accessKey: info.accesskey }
+ }
+ return null
+ })
+ return requestObj
+ },
+ getLyricDownload(id, accessKey, tryNum = 0) {
+ let requestObj = httpFetch(`http://lyrics.kugou.com/download?ver=1&client=pc&id=${id}&accesskey=${accessKey}&fmt=krc&charset=utf8`, {
+ headers: {
+ 'KG-RC': 1,
+ 'KG-THash': 'expand_search_manager.cpp:852736169:451',
+ 'User-Agent': 'KuGou2012-9020-ExpandSearchManager',
+ },
+ })
+ requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
+ if (statusCode !== 200) {
+ if (tryNum > 5) return Promise.reject('歌词获取失败')
+ let tryRequestObj = this.getLyric(id, accessKey, ++tryNum)
+ requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
+ return tryRequestObj.promise
+ }
+
+ return decodeLyric(body.content).then(result => parseLyric(result))
+ })
+ return requestObj
+ },
+ getLyric(songInfo, tryNum = 0) {
+ let requestObj = this.searchLyric(songInfo.name, songInfo.hash, songInfo._interval || this.getIntv(songInfo.interval))
+
+ requestObj.promise = requestObj.promise.then(result => {
+ if (!result) return { lyric: '', tlyric: '' }
+
+ let requestObj2 = this.getLyricDownload(result.id, result.accessKey)
+
+ requestObj.cancelHttp = requestObj2.cancelHttp.bind(requestObj2)
+
+ return requestObj2.promise
})
return requestObj
},
diff --git a/src/renderer/utils/music/kg/util.js b/src/renderer/utils/music/kg/util.js
new file mode 100644
index 00000000..73b5e743
--- /dev/null
+++ b/src/renderer/utils/music/kg/util.js
@@ -0,0 +1,19 @@
+import { inflate } from 'zlib'
+
+// https://github.com/lyswhut/lx-music-desktop/issues/296#issuecomment-683285784
+const enc_key = Buffer.from([0x40, 0x47, 0x61, 0x77, 0x5e, 0x32, 0x74, 0x47, 0x51, 0x36, 0x31, 0x2d, 0xce, 0xd2, 0x6e, 0x69], 'binary')
+export const decodeLyric = str => new Promise((resolve, reject) => {
+ if (!str.length) return
+ const buf_str = Buffer.from(str, 'base64').slice(4)
+ for (let i = 0, len = buf_str.length; i < len; i++) {
+ buf_str[i] = buf_str[i] ^ enc_key[i % 16]
+ }
+ inflate(buf_str, (err, result) => {
+ if (err) return reject(err)
+ resolve(result.toString())
+ })
+})
+
+// s.content[0].lyricContent.forEach(([str]) => {
+// console.log(str)
+// })
diff --git a/src/renderer/utils/music/kw/lyric.js b/src/renderer/utils/music/kw/lyric.js
index 29f6a1d7..91fd52f3 100644
--- a/src/renderer/utils/music/kw/lyric.js
+++ b/src/renderer/utils/music/kw/lyric.js
@@ -17,7 +17,10 @@ export default {
return requestObj.promise.then(({ statusCode, body, raw }) => {
if (statusCode != 200) return Promise.reject(new Error(JSON.stringify(body)))
return decodeLyric({ lrcBase64: raw.toString('base64'), isGetLyricx }).then(base64Data => {
- return Buffer.from(base64Data, 'base64').toString()
+ return {
+ lyric: Buffer.from(base64Data, 'base64').toString(),
+ tlyric: '',
+ }
})
})
})
diff --git a/src/renderer/utils/music/mg/lyric.js b/src/renderer/utils/music/mg/lyric.js
index 330aa10d..cd6a49ef 100644
--- a/src/renderer/utils/music/mg/lyric.js
+++ b/src/renderer/utils/music/mg/lyric.js
@@ -12,7 +12,10 @@ export default {
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
- return body
+ return {
+ lyric: body,
+ tlyric: '',
+ }
})
return requestObj
} else {
@@ -28,7 +31,10 @@ export default {
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
- return body.lyric
+ return {
+ lyric: body.lyric,
+ tlyric: '',
+ }
})
return requestObj
}
diff --git a/src/renderer/utils/music/tx/lyric.js b/src/renderer/utils/music/tx/lyric.js
index d86233ba..a371a6e7 100644
--- a/src/renderer/utils/music/tx/lyric.js
+++ b/src/renderer/utils/music/tx/lyric.js
@@ -6,13 +6,17 @@ export default {
matchLrc: /.+"lyric":"([\w=+/]*)".+/,
},
getLyric(songmid) {
- const requestObj = httpFetch(`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`, {
+ const requestObj = httpFetch(`https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid=${songmid}&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&platform=yqq`, {
headers: {
Referer: 'https://y.qq.com/portal/player.html',
},
})
requestObj.promise = requestObj.promise.then(({ body }) => {
- return decodeName(b64DecodeUnicode(body.replace(this.regexps.matchLrc, '$1')))
+ if (body.code != 0) return Promise.reject(new Error('获取歌词失败'))
+ return {
+ lyric: decodeName(b64DecodeUnicode(body.lyric)),
+ tlyric: decodeName(b64DecodeUnicode(body.trans)),
+ }
})
return requestObj
},
diff --git a/src/renderer/utils/music/wy/lyric.js b/src/renderer/utils/music/wy/lyric.js
index 819b3931..6ae95d34 100644
--- a/src/renderer/utils/music/wy/lyric.js
+++ b/src/renderer/utils/music/wy/lyric.js
@@ -1,20 +1,27 @@
import { httpFetch } from '../../request'
-import { weapi } from './utils/crypto'
+import { linuxapi } from './utils/crypto'
export default songmid => {
- const requestObj = httpFetch('http://music.163.com/weapi/song/lyric?csrf_token=', {
+ const requestObj = httpFetch('https://music.163.com/api/linux/forward', {
method: 'post',
- headers: {
- 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
- Referer: 'https://music.163.com/song?id=' + songmid,
- origin: 'https://music.163.com',
- },
- form: weapi({ id: songmid, lv: -1, tv: -1, csrf_token: '' }),
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
+ form: linuxapi({
+ method: 'POST',
+ url: 'https://music.163.com/api/song/lyric',
+ params: {
+ id: songmid,
+ lv: -1,
+ kv: -1,
+ tv: -1,
+ },
+ }),
})
requestObj.promise = requestObj.promise.then(({ body }) => {
- // console.log(body)
if (body.code !== 200) return Promise.reject('获取歌词失败')
- return body.lrc.lyric
+ return {
+ lyric: body.lrc.lyric,
+ tlyric: body.tlyric.lyric,
+ }
})
return requestObj
}
diff --git a/src/renderer/utils/music/xm/leaderboard.js b/src/renderer/utils/music/xm/leaderboard.js
index fa28b59f..34dca4b1 100644
--- a/src/renderer/utils/music/xm/leaderboard.js
+++ b/src/renderer/utils/music/xm/leaderboard.js
@@ -78,7 +78,7 @@ export default {
return arr.join('、')
},
filterData(rawList) {
- console.log(rawList)
+ // console.log(rawList)
let ids = new Set()
const list = []
rawList.forEach(songData => {
diff --git a/src/renderer/utils/music/xm/lyric.js b/src/renderer/utils/music/xm/lyric.js
index 4f83c57f..d1e85d79 100644
--- a/src/renderer/utils/music/xm/lyric.js
+++ b/src/renderer/utils/music/xm/lyric.js
@@ -1,6 +1,23 @@
import { httpGet, httpFetch } from '../../request'
import { xmRequest } from './util'
+const parseLyric = str => {
+ str = str.replace(/(?:<\d+>|\r)/g, '')
+ let tlyric = []
+ let lyric = str.replace(/\[[\d:.]+\].*?\n\[x-trans\].*/g, s => {
+ // console.log(s)
+ let [lrc, tlrc] = s.split('\n')
+ tlrc = tlrc.replace('[x-trans]', lrc.replace(/^(\[[\d:.]+\]).*$/, '$1'))
+ tlyric.push(tlrc)
+ return lrc
+ })
+ tlyric = tlyric.join('\n')
+ return {
+ lyric,
+ tlyric,
+ }
+}
+
export default {
failTime: 0,
expireTime: 60 * 1000 * 1000,
@@ -13,7 +30,10 @@ export default {
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
- return body
+ return url.endsWith('.xtrc') ? parseLyric(body) : {
+ lyric: body,
+ tlyric: '',
+ }
})
return requestObj
},
@@ -27,7 +47,10 @@ export default {
},
}, function(err, resp, body) {
if (err || resp.statusCode !== 200) return this.getLyricFile(url, ++retryNum).then(resolve).catch(reject)
- return resolve(body)
+ return resolve(url.endsWith('.xtrc') ? parseLyric(body) : {
+ lyric: body,
+ tlyric: '',
+ })
})
})
},
@@ -47,8 +70,12 @@ export default {
return tryRequestObj.promise
}
if (!body.result.data.lyrics.length) return Promise.reject(new Error('未找到歌词'))
- let lrc = body.result.data.lyrics.find(lyric => /\.lrc$/.test(lyric.lyricUrl))
- return lrc ? lrc.content : Promise.reject(new Error('未找到歌词'))
+ let lrc = body.result.data.lyrics.find(lyric => /\.(trc|lrc)$/.test(lyric.lyricUrl))
+ return lrc
+ ? lrc.lyricUrl.endsWith('.trc')
+ ? parseLyric(lrc.content)
+ : { lyric: lrc.content, tlyric: '' }
+ : Promise.reject(new Error('未找到歌词'))
})
return requestObj
},
@@ -74,7 +101,7 @@ export default {
return requestObj
},
getLyric(songInfo) {
- if (songInfo.lrcUrl && /\.lrc$/.test(songInfo.lrcUrl)) return this.getLyricFile_1(songInfo.lrcUrl)
+ if (songInfo.lrcUrl && /\.(xtrc|lrc)$/.test(songInfo.lrcUrl)) return this.getLyricFile_1(songInfo.lrcUrl)
return Date.now() - this.failTime > this.expireTime ? this.getLyricUrl_1(songInfo) : this.getLyricUrl_2(songInfo)
},
}
diff --git a/src/renderer/views/Download.vue b/src/renderer/views/Download.vue
index 5d7c74ab..77a327df 100644
--- a/src/renderer/views/Download.vue
+++ b/src/renderer/views/Download.vue
@@ -29,7 +29,6 @@ div(:class="$style.download")
:start-btn="!item.isComplate && item.status != downloadStatus.WAITING && (item.status != downloadStatus.RUN)"
:pause-btn="!item.isComplate && (item.status == downloadStatus.RUN || item.status == downloadStatus.WAITING)" :list-add-btn="false"
:play-btn="item.status == downloadStatus.COMPLETED" :search-btn="item.status == downloadStatus.ERROR" @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")
material-menu(:menus="listItemMenu" :location="listMenu.menuLocation" item-name="name" :isShow="listMenu.isShowItemMenu" @menu-click="handleListItemMenuClick")
div(:class="$style.noItem" v-else)
@@ -47,7 +46,6 @@ export default {
clickTime: window.performance.now(),
clickIndex: -1,
selectdData: [],
- // isShowEditBtn: false,
isShowDownloadMultiple: false,
tabId: 'all',
keyEvent: {
@@ -166,14 +164,6 @@ export default {
},
},
watch: {
- selectdData(n) {
- const len = n.length
- if (len) {
- this.isShowEditBtn = true
- } else {
- this.isShowEditBtn = false
- }
- },
list() {
this.removeAllSelect()
},
diff --git a/src/renderer/views/List.vue b/src/renderer/views/List.vue
index 4a42968e..79b75cba 100644
--- a/src/renderer/views/List.vue
+++ b/src/renderer/views/List.vue
@@ -643,7 +643,7 @@ export default {
this.listsData.isShowNewList = false
})
- this.createUserList(name)
+ this.createUserList({ name })
},
handleShowNewList() {
this.listsData.isShowNewList = true
diff --git a/src/renderer/views/Setting.vue b/src/renderer/views/Setting.vue
index 8940cb05..b28b0829 100644
--- a/src/renderer/views/Setting.vue
+++ b/src/renderer/views/Setting.vue
@@ -10,6 +10,11 @@ div.scroll(:class="$style.setting")
span
label {{$t('store.state.theme_' + theme.class)}}
+ dd
+ h3 {{$t('view.setting.basic_show_animation')}}
+ div
+ material-checkbox(id="setting_show_animate" v-model="current_setting.isShowAnimation" :label="$t('view.setting.is_show')")
+
dd(:title="$t('view.setting.basic_animation_title')")
h3 {{$t('view.setting.basic_animation')}}
div
@@ -58,6 +63,10 @@ div.scroll(:class="$style.setting")
div
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
+ h3 {{$t('view.setting.play_lyric_transition')}}
+ div
+ material-checkbox(id="setting_player_lyric_transition" v-model="current_setting.player.isShowLyricTransition" :label="$t('view.setting.is_show')")
dd(:title="$t('view.setting.play_quality_title')")
h3 {{$t('view.setting.play_quality')}}
div
@@ -82,6 +91,8 @@ div.scroll(:class="$style.setting")
material-checkbox(id="setting_desktop_lyric_lock" v-model="current_setting.desktopLyric.isLock" :label="$t('view.setting.desktop_lyric_lock')")
div(:class="$style.gapTop")
material-checkbox(id="setting_desktop_lyric_alwaysOnTop" v-model="current_setting.desktopLyric.isAlwaysOnTop" :label="$t('view.setting.desktop_lyric_always_on_top')")
+ div(:class="$style.gapTop")
+ material-checkbox(id="setting_desktop_lyric_lockScreen" v-model="current_setting.desktopLyric.isLockScreen" :label="$t('view.setting.desktop_lyric_lock_screen')")
dt {{$t('view.setting.search')}}
dd(:title="$t('view.setting.search_hot_title')")
h3 {{$t('view.setting.search_hot')}}
@@ -228,7 +239,7 @@ div.scroll(:class="$style.setting")
span.hover.underline(:title="$t('view.setting.click_open')" @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop#readme')") https://github.com/lyswhut/lx-music-desktop
p.small
| 最新版网盘下载地址(网盘内有Windows、MAC版):
- span.hover.underline(:title="$t('view.setting.click_open')" @click="handleOpenUrl('https://t-s.lanzous.com/b0bf2cfa')") 网盘地址
+ span.hover.underline(:title="$t('view.setting.click_open')" @click="handleOpenUrl('https://www.lanzoux.com/b0bf2cfa/')") 网盘地址
| 密码:
span.hover(:title="$t('view.setting.click_copy')" @click="clipboardWriteText('glqw')") glqw
p.small
@@ -448,6 +459,7 @@ export default {
langId: 'cns',
themeId: 0,
sourceId: 0,
+ isShowAnimation: true,
randomAnimate: true,
isAgreePact: false,
controlBtnPosition: 'left',
@@ -587,6 +599,15 @@ export default {
'setting.player.mediaDeviceId'(n) {
this.current_setting.player.mediaDeviceId = n
},
+ 'setting.player.isMute'(n) {
+ this.current_setting.player.isMute = n
+ },
+ 'setting.desktopLyric.enable'(n) {
+ this.current_setting.desktopLyric.enable = n
+ },
+ 'setting.player.togglePlayMethod'(n) {
+ this.current_setting.player.togglePlayMethod = n
+ },
'current_setting.player.isShowTaskProgess'(n) {
if (n) return
this.$nextTick(() => {
diff --git a/src/renderer/views/SongList.vue b/src/renderer/views/SongList.vue
index 6d5ff906..8a547035 100644
--- a/src/renderer/views/SongList.vue
+++ b/src/renderer/views/SongList.vue
@@ -10,11 +10,9 @@
h3(:title="listDetail.info.name || selectListInfo.name") {{listDetail.info.name || selectListInfo.name}}
p(:title="listDetail.info.desc || selectListInfo.desc") {{listDetail.info.desc || selectListInfo.desc}}
div(:class="$style.songListHeaderRight")
- //- material-btn(:class="$style.closeDetailButton" :disabled="detailLoading" @click="addSongListDetail") 添加
- //- |
- //- material-btn(:class="$style.closeDetailButton" :disabled="detailLoading" @click="playSongListDetail") 播放
- //- |
- material-btn(:class="$style.closeDetailButton" @click="hideListDetail") {{$t('view.song_list.back')}}
+ material-btn(:class="$style.headerRightBtn" :disabled="detailLoading" @click="playSongListDetail") {{$t('view.song_list.play_all')}}
+ material-btn(:class="$style.headerRightBtn" :disabled="detailLoading" @click="addSongListDetail") {{$t('view.song_list.add_all')}}
+ material-btn(:class="$style.headerRightBtn" @click="hideListDetail") {{$t('view.song_list.back')}}
material-song-list(v-model="selectedData" @action="handleSongListAction" :source="source" :page="listDetail.page" :limit="listDetail.limit"
:total="listDetail.total" :noItem="isGetDetailFailed ? $t('view.song_list.loding_list_fail') : $t('view.song_list.loding_list')" :list="listDetail.list")
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
@@ -84,7 +82,7 @@ export default {
listWidth: 645,
isGetDetailFailed: false,
isInitedTagListWidth: false,
- // detailLoading: true,
+ detailLoading: false,
}
},
computed: {
@@ -97,7 +95,7 @@ export default {
switch (this.source) {
case 'wy':
case 'kw':
- case 'bd':
+ // case 'bd':
case 'tx':
case 'mg':
case 'kg':
@@ -179,11 +177,13 @@ export default {
},
methods: {
...mapMutations(['setSongList']),
- ...mapActions('songList', ['getTags', 'getList', 'getListDetail']),
+ ...mapActions('songList', ['getTags', 'getList', 'getListDetail', 'getListDetailAll']),
...mapMutations('songList', ['setVisibleListDetail', 'setSelectListInfo']),
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
- ...mapMutations('list', ['listAdd', 'listAddMultiple']),
- ...mapMutations('player', ['setList']),
+ ...mapMutations('list', ['listAdd', 'listAddMultiple', 'createUserList']),
+ ...mapMutations('player', {
+ setPlayList: 'setList',
+ }),
listenEvent() {
window.eventHub.$on('key_backspace_down', this.handle_key_backspace_down)
},
@@ -273,7 +273,7 @@ export default {
s => s.songmid === targetSong.songmid,
)
if (targetIndex > -1) {
- this.setList({
+ this.setPlayList({
list: this.defaultList,
index: targetIndex,
})
@@ -402,15 +402,28 @@ export default {
assertApiSupport(source) {
return assertApiSupport(source)
},
- /* addSongListDetail() {
- // this.detailLoading = true
- // this.getListDetailAll(this.selectListInfo.id).then(() => {
- // this.detailLoading = false
- // })
+ async fetchList() {
+ this.detailLoading = true
+ const list = await this.getListDetailAll(this.selectListInfo.id)
+ this.detailLoading = false
+ return list
+ },
+ async addSongListDetail() {
+ if (!this.listDetail.info.name) return
+ const list = await this.fetchList()
+ this.createUserList({ name: this.listDetail.info.name, id: `${this.listDetail.source}__${this.listDetail.id}`, list })
+ },
+ async playSongListDetail() {
+ if (!this.listDetail.info.name) return
+ const list = await this.fetchList()
+ this.setPlayList({
+ list: {
+ list,
+ id: null,
+ },
+ index: 0,
+ })
},
- playSongListDetail() {
-
- }, */
},
}
@@ -517,6 +530,17 @@ export default {
align-items: center;
padding-right: 15px;
}
+.header-right-btn {
+ border-radius: 0;
+ &:first-child {
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ }
+ &:last-child {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ }
+}
.song-list-detail-content {
position: absolute;