Merge branch 'dev'

pull/930/merge v1.22.0
lyswhut 2022-06-19 12:34:09 +08:00
commit d695cdef08
51 changed files with 1315 additions and 853 deletions

View File

@ -8,7 +8,7 @@ body:
label: 解决方案检查
description: 请确保你已完成以下所有操作
options:
- label: 我已阅读常见问题(<https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md>),但没有找到解决方案
- label: 我已阅读常见问题(<https://lyswhut.github.io/lx-music-doc/desktop/faq>),但没有找到解决方案
required: true
- label: 我已搜索issue列表(<https://github.com/lyswhut/lx-music-desktop/issues?utf8=✓&q=>),但没有发现类似的问题
required: true

View File

@ -8,7 +8,7 @@ body:
label: 解决方案检查
description: 请确保你已完成以下所有操作
options:
- label: 我已阅读常见问题(<https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md>),并没有找到解决方案
- label: 我已阅读常见问题(<https://lyswhut.github.io/lx-music-doc/desktop/faq>),并没有找到解决方案
required: true
- label: 我已搜索issue列表(<https://github.com/lyswhut/lx-music-desktop/issues?utf8=✓&q=>),并没有发现类似的问题
required: true

View File

@ -6,6 +6,35 @@ 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.22.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.21.0...v1.22.0) - 2022-06-19
### 新增
- 新增设置-以全屏模式启动设置
- 新增设置-桌面歌词设置-鼠标移入歌词区域时降低歌词透明度(#883默认关闭此设置不支持linux此功能存在兼容性问题若鼠标移出后无法恢复到正常透明度可尝试再移入移出即可恢复
### 优化
- 添加歌曲到“我的列表”时,若按住`ctrl`键Mac对应`Command`),则不会自动关闭添加窗口,这对想要将同一首(一批)歌曲添加到多个列表时会很有用
- 支持mg源逐字歌词的播放感谢 @mozbugbox 提供的帮助
- 添加歌曲列表更新操作的二次确认
- 添加导入文件错误时的指引提示
### 修复
- 修复若配置了`http_proxy`环境变量时,会意外使用此代理配置的问题
- 修复多选后切换列表后不会清空多选内容的问题
- 修复设置快捷键时的处理逻辑问题
- 修复在新建歌单输入框、歌单内歌曲搜索输入框会意外触发设置的全局快捷键的问题(#879
### 文档
桌面版文档已迁移到:<https://lyswhut.github.io/lx-music-doc/desktop>
### 其他
- 更新 Electron 到 v17.4.7
## [1.21.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.20.0...v1.21.0) - 2022-05-22
### 新增

15
FAQ.md
View File

@ -1,5 +1,7 @@
# lx-music-desktop 常见问题
本文档已迁移到:<https://lyswhut.github.io/lx-music-doc/desktop/faq>
在阅读本常见问题后仍然无法解决你的问题请提交issue或者加企鹅群`830125506`反馈(无事勿加,入群先看群公告),反馈时请**注明**已阅读常见问题!
## ~~软件为什么没有桌面歌词与自定义列表功能~~
@ -34,6 +36,7 @@
- 在我的列表按住`Ctrl`键可以进入列表拖动模式,此时可以用鼠标拖动列表调整列表的位置
- 编辑列表名时按`Esc`键可以取消编辑
- 按`F11`可以进入、退出全屏状态v1.19.0新增)
- 在歌曲添加弹窗中,若按住`Ctrl`键后再点击列表名将不会自动关闭添加窗口这对想要将同一首一批歌曲添加到多个列表时会很有用v1.22.0新增)
在macOS上`Ctrl`键对应`Command`键
@ -71,6 +74,16 @@
1. 将DNS改成自动获取试试
2. 手动把DNS改一下不要用360的DNS可以把DNS改成`223.6.6.6`、`8.8.8.8`
### Windows版所有歌曲都提示 `音频加载错误5秒后切换下一首`
尝试关闭 Internet选项 的代理设置。
如果你不知道怎么做,可以尝试按以下步骤去做:
<kbd>windows</kbd>+<kbd>r</kbd>键打开“运行”窗口,输入`inetcpl.cpl`后回车,在打开的 Internet选项 对话框中,切换到 连接 -> 局域网设置,在弹出的新窗口中把代理服务器下的勾去掉,如果自动配置下的勾也有被勾选,那么建议也去掉,最后按确定关闭所有弹窗。
> 来源:<https://github.com/lyswhut/lx-music-desktop/issues/873#issuecomment-1146945724>
## 列表多选
从v0.18.0起,列表多选需要键盘配合,想要多选前需按下`Shift`或`Ctrl`键然后再鼠标点击想要选中的内容即可触发多选机制,其中`Shift`键用于连续选择,`Ctrl`键用于不连续选择,`Ctrl+a`用于快速全选。
@ -114,7 +127,7 @@
这些浏览器打开此窗口时界面可能是中文也可能是英文,英文的话按括号里的来
1. 点击窗口顶部`应用程序(application)`
1. 点击窗口顶部`应用程序(application)`(若找不到此选项,则可能是被折叠起来了,看看顶部菜单的`>>`
2. 展开左侧 `Cookies`
3. 点击 `https://music.163.com`
4. 在右侧窗口找到 `名称(Name)``MUSIC_U` 的这行,这行的第二列(`值(Value)`)内的那串内容就是`token`,双击它进入编辑状态,然后按`ctrl + c`键就可以将它复制

View File

@ -36,7 +36,7 @@
所用技术栈:
- Electron 13
- Electron 17
- Vue 3
已支持的平台:
@ -48,7 +48,7 @@
软件变化请查看:[更新日志](https://github.com/lyswhut/lx-music-desktop/blob/master/CHANGELOG.md)<br>
软件下载请转到:[发布页面](https://github.com/lyswhut/lx-music-desktop/releases)<br>
或者到网盘下载网盘内有MAC、windows版`https://www.lanzoui.com/b0bf2cfa/` 密码:`glqw`(若链接无法打开请百度:蓝奏云链接打不开)<br>
使用常见问题请转至:[常见问题](https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md)<br>
使用常见问题请转至:[常见问题](https://lyswhut.github.io/lx-music-doc/desktop/faq)<br>
移动版项目地址:<https://github.com/lyswhut/lx-music-mobile>
#### Scheme URL支持
@ -56,7 +56,7 @@
从v1.17.0起支持 Scheme URL可以使用此功能从浏览器等场景下调用LX Music我们开发了一个[油猴脚本](https://github.com/lyswhut/lx-music-script#readme)配套使用,<br>
脚本安装地址:<https://greasyfork.org/zh-CN/scripts/438148><br>
若你想自己调用LX Music可以看[Scheme URL支持](https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md#scheme-url%E6%94%AF%E6%8C%81)
若你想自己调用LX Music可以看[Scheme URL支持](https://lyswhut.github.io/lx-music-doc/desktop/scheme-url)
#### 启动参数
@ -70,7 +70,7 @@
- `-dt` 以非透明模式启动Disable Transparent
- `-dhmkh` 禁用硬件媒体密钥处理Disable Hardware Media Key Handling
启动参数的详细说明请看[启动参数说明](https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md#%E5%90%AF%E5%8A%A8%E5%8F%82%E6%95%B0)
启动参数的详细说明请看[启动参数说明](https://lyswhut.github.io/lx-music-doc/desktop/run-params)
#### 数据存储路径
@ -110,7 +110,7 @@ npm run pack:linux
### 常见问题
常见问题已移至:<https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md>
常见问题已移至:<https://lyswhut.github.io/lx-music-doc/desktop/faq>
### 项目协议

View File

@ -14,6 +14,7 @@ module.exports = merge(baseConfig, {
NODE_ENV: '"development"',
ELECTRON_DISABLE_SECURITY_WARNINGS: 'true',
},
ENVIRONMENT: 'process.env',
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false',
__static: `"${path.join(__dirname, '../../src/static').replace(/\\/g, '\\\\')}"`,

View File

@ -31,6 +31,7 @@ module.exports = merge(baseConfig, {
'process.env': {
NODE_ENV: '"production"',
},
ENVIRONMENT: 'process.env',
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false',
}),

1284
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": "1.21.0",
"version": "1.22.0",
"description": "一个免费的音乐查找助手",
"main": "./dist/main.js",
"productName": "lx-music-desktop",
@ -70,7 +70,7 @@
"up": "cross-env ELECTRON_GET_USE_PROXY=true GLOBAL_AGENT_HTTPS_PROXY=http://localhost:1081 npm i"
},
"browserslist": [
"Electron 13.6.8"
"Electron 17.4.7"
],
"engines": {
"node": ">= 16",
@ -178,32 +178,32 @@
},
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
"devDependencies": {
"@babel/core": "^7.18.0",
"@babel/eslint-parser": "^7.17.0",
"@babel/core": "^7.18.5",
"@babel/eslint-parser": "^7.18.2",
"@babel/plugin-proposal-class-properties": "^7.17.12",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-modules-umd": "^7.18.0",
"@babel/plugin-transform-runtime": "^7.18.0",
"@babel/plugin-transform-runtime": "^7.18.5",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.18.0",
"@babel/preset-env": "^7.18.2",
"babel-loader": "^8.2.5",
"babel-preset-minify": "^0.5.2",
"browserslist": "^4.20.3",
"browserslist": "^4.20.4",
"chalk": "^4.1.2",
"changelog-parser": "^2.8.1",
"copy-webpack-plugin": "^11.0.0",
"core-js": "^3.22.5",
"core-js": "^3.23.1",
"cross-env": "^7.0.3",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^4.0.0",
"del": "^6.1.0",
"electron": "^13.6.9",
"electron-builder": "^23.0.9",
"del": "^6.1.1",
"electron": "^17.4.7",
"electron-builder": "^23.1.0",
"electron-debug": "^3.2.0",
"electron-devtools-installer": "^3.2.0",
"electron-to-chromium": "^1.4.137",
"electron-updater": "^5.0.4",
"eslint": "^8.16.0",
"electron-to-chromium": "^1.4.161",
"electron-updater": "^5.0.5",
"eslint": "^8.18.0",
"eslint-config-standard": "^17.0.0",
"eslint-formatter-friendly": "git+https://github.com/lyswhut/eslint-friendly-formatter.git#2170d1320e2fad13615a9dcf229669f0bb473a53",
"eslint-plugin-html": "^6.2.0",
@ -214,9 +214,9 @@
"eslint-webpack-plugin": "^3.1.1",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0",
"less": "^4.1.2",
"less": "^4.1.3",
"less-loader": "^11.0.0",
"mini-css-extract-plugin": "^2.6.0",
"mini-css-extract-plugin": "^2.6.1",
"node-loader": "^2.0.0",
"postcss": "^8.4.14",
"postcss-loader": "^7.0.0",
@ -229,14 +229,14 @@
"spinnies": "git+https://github.com/lyswhut/spinnies.git#233305c58694aa3b053e3ab9af9049993f918b9d",
"svg-sprite-loader": "^6.0.11",
"svg-transform-loader": "^2.0.13",
"svgo-loader": "^3.0.0",
"terser-webpack-plugin": "^5.3.1",
"svgo-loader": "^3.0.1",
"terser-webpack-plugin": "^5.3.3",
"url-loader": "^4.1.1",
"vue-loader": "^17.0.0",
"vue-template-compiler": "^2.6.14",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0",
"vue-template-compiler": "^2.7.0-beta.2",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.2",
"webpack-hot-middleware": "git+https://github.com/lyswhut/webpack-hot-middleware.git#329c4375134b89d39da23a56a94db651247c74a1",
"webpack-merge": "^5.8.0"
},
@ -244,7 +244,7 @@
"bufferutil": "^4.0.6",
"crypto-js": "^4.1.1",
"electron-log": "^4.4.7",
"electron-store": "^8.0.1",
"electron-store": "^8.0.2",
"font-list": "git+https://github.com/lyswhut/node-font-list.git#4edbb1933b49a9bac1eedd63a31da16b487fe57d",
"http-terminator": "^3.2.0",
"iconv-lite": "^0.6.3",
@ -259,8 +259,8 @@
"sortablejs": "^1.15.0",
"tunnel": "^0.0.6",
"utf-8-validate": "^5.0.9",
"vue": "^3.2.35",
"vue-i18n": "^9.2.0-beta.35",
"vue": "^3.2.37",
"vue-i18n": "^9.2.0-beta.36",
"vue-router": "^4.1.0-aabe509",
"vuex": "^4.0.2"
},

View File

@ -1,23 +1,26 @@
### 新增
- 新增设置-播放设置-显示歌词罗马音,默认关闭,注:目前只有网易源能获取到罗马音歌词(得益于 Binaryify/NeteaseCloudMusicApi/pull/1523如果你知道其他源的歌词罗马音获取方式欢迎PR或开issue交流
- 新增设置-以全屏模式启动设置
- 新增设置-桌面歌词设置-鼠标移入歌词区域时降低歌词透明度(#883默认关闭此设置不支持linux此功能存在兼容性问题若鼠标移出后无法恢复到正常透明度可尝试再移入移出即可恢复
### 优化
- 同时删除一首歌以上时将需要二次确认删除
- 禁用透明窗口时右侧不再偏移5px距离在win7、Ubuntu等系统上测试发现不偏移也不影响滚动条的拖动了
- 删除未下载完成的任务时,只同时尝试删除已有下载进度的本地文件
- 在全屏状态下使用`Esc`键可以退出全屏(#827
- 添加歌曲到“我的列表”时,若按住`ctrl`键Mac对应`Command`),则不会自动关闭添加窗口,这对想要将同一首(一批)歌曲添加到多个列表时会很有用
- 支持mg源逐字歌词的播放感谢 @mozbugbox 提供的帮助
- 添加歌曲列表更新操作的二次确认
- 添加导入文件错误时的指引提示
### 修复
- 修复某些情况下歌曲播放出错时不会自动切歌的问题
- 修复关闭“显示切换动画”设置后,在应用启动时该设置没有被应用的问题
- 修复原始歌词存在偏移时,歌词偏移设置的重置未按预期工作的问题
- 修复长度大于一行的歌词在使用歌词调整播放进度时的时间不准问题
- 修复潜在歌单更新失败的问题
- 修复若配置了`http_proxy`环境变量时,会意外使用此代理配置的问题
- 修复多选后切换列表后不会清空多选内容的问题
- 修复设置快捷键时的处理逻辑问题
- 修复在新建歌单输入框、歌单内歌曲搜索输入框会意外触发设置的全局快捷键的问题(#879
### 文档
- 将歌曲添加“稍后播放”后,它们会被放在一个优先级最高的特殊队列中,点击“下一曲”时会消耗该队列中的歌曲,并且无法通过“上一曲”功能播放该队列的上一首歌曲
- 在切歌时若不是通过“上一曲”、“下一曲”功能切歌(例如直接点击“排行榜列表”、“我的列表”中的歌曲切歌),“稍后播放”队列将会被清空
桌面版文档已迁移到:<https://lyswhut.github.io/lx-music-doc/desktop>
### 其他
- 更新 Electron 到 v17.4.7

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@ const path = require('path')
const os = require('os')
const defaultSetting = {
version: '1.0.57',
version: '1.0.59',
player: {
togglePlayMethod: 'listLoop',
highQuality: false,
@ -41,6 +41,7 @@ const defaultSetting = {
theme: 0,
isLockScreen: true,
isDelayScroll: true,
isHoverHide: false,
style: {
font: '',
fontSize: 120,
@ -108,6 +109,7 @@ const defaultSetting = {
port: '23332',
},
windowSizeId: 2,
startInFullscreen: false,
theme: {
id: 0,
lightId: 0,

View File

@ -95,6 +95,11 @@
"list_add__title_first_add": "Add",
"list_add__title_first_move": "Move",
"list_add__title_last": "to...",
"list_import_tip__alldata": "Import failed, this is an all data backup file, you need to go here to import: \nSettings -> Backup and Restore -> All Data -> Import",
"list_import_tip__playlist": "Import failed, this is a list backup file, you need to go here to import: \nSettings -> Backup and Restore -> Import List",
"list_import_tip__playlist_part": "The import failed, this is a single-list file, you need to go here to import: \nMy List -> Right-click on any list name -> Select Import in the pop-up menu",
"list_import_tip__setting": "Import failed, this is a settings backup file, you need to go here to import: \nSettings -> Backup and Restore -> Partial Data -> Settings Import",
"list_import_tip__unknown": "Import failed, unknown file type, please try to upgrade to the latest version and try again",
"list_sort_modal_by_album": "Album name",
"list_sort_modal_by_down": "Descending",
"list_sort_modal_by_field": "Sort field",
@ -127,6 +132,7 @@
"lists__sort_list": "Sort songs",
"lists__source_detail": "Playlist Page",
"lists__sync": "Update",
"lists__sync_confirm_tip": "This will replace the songs in {name} with the songs in the online list, are you sure you want to update?",
"load_list_file_error_detail": "We have helped you back up the old list file to {path}\nIt is stored in JSON format, you can try to repair and restore it manually\n\nError details: {detail}",
"load_list_file_error_title": "Error loading playlist data",
"loding_list": "Loading...",
@ -246,6 +252,7 @@
"setting__basic_sourcename_alias": "Aliases",
"setting__basic_sourcename_real": "Original",
"setting__basic_sourcename_title": "Select the name of music source",
"setting__basic_start_in_fullscreen": "Start in fullscreen mode",
"setting__basic_theme": "Theme",
"setting__basic_theme_auto_tip": "This is a dynamic theme, you can preset a light theme and a dark theme, and then it will automatically switch to the corresponding theme you preset according to the system's light and dark theme colors.\nNote: Right-click this theme item to open the light and dark theme setting window.",
"setting__basic_to_tray": "Do not exit the software when closing the software and minimize it to the system tray",
@ -267,6 +274,7 @@
"setting__desktop_lyric_enable": "Display lyrics",
"setting__desktop_lyric_font": "Lyric font",
"setting__desktop_lyric_font_default": "Default",
"setting__desktop_lyric_hover_hide": "Reduce the transparency of lyrics when the mouse moves into the lyrics area (this feature has platform compatibility issues)",
"setting__desktop_lyric_lock": "Lock lyrics",
"setting__desktop_lyric_lock_screen": "It is not allowed to drag the lyrics window out of the main screen",
"setting__download": "Download",

View File

@ -95,6 +95,11 @@
"list_add__title_first_add": "添加",
"list_add__title_first_move": "移动",
"list_add__title_last": "到...",
"list_import_tip__alldata": "导入失败,这是一个所有数据备份文件,你需要去这里导入:\n设置 -> 备份与恢复 -> 所有数据 -> 导入",
"list_import_tip__playlist": "导入失败,这是一个列表备份文件,你需要去这里导入:\n设置 -> 备份与恢复 -> 部分数据 -> 导入列表",
"list_import_tip__playlist_part": "导入失败,这是一个单列表文件,你需要去这里导入:\n我的列表 -> 右击任意一个列表名 -> 在弹出的菜单中选择导入",
"list_import_tip__setting": "导入失败,这是一个设置备份文件,你需要去这里导入:\n设置 -> 备份与恢复 -> 部分数据 -> 设置导入",
"list_import_tip__unknown": "导入失败,未知的文件类型,请尝试升级到最新版本后再试",
"list_sort_modal_by_album": "专辑名",
"list_sort_modal_by_down": "降序",
"list_sort_modal_by_field": "排序字段",
@ -127,6 +132,7 @@
"lists__sort_list": "排序歌曲",
"lists__source_detail": "歌单详情页",
"lists__sync": "更新",
"lists__sync_confirm_tip": "这将会把 {name} 内的歌曲替换成在线列表的歌曲,你确认要更新吗?",
"load_list_file_error_detail": "我们已经帮你把旧的列表文件备份到{path}\n它以 JSON 格式存储,你可以尝试手动修复并恢复它\n\n错误详情{detail}",
"load_list_file_error_title": "播放列表数据加载错误建议到GitHub或加群反馈",
"loding": "加载中...",
@ -246,6 +252,7 @@
"setting__basic_sourcename_alias": "别名",
"setting__basic_sourcename_real": "原名",
"setting__basic_sourcename_title": "选择音源名字类型",
"setting__basic_start_in_fullscreen": "以全屏模式启动",
"setting__basic_theme": "主题颜色",
"setting__basic_theme_auto_tip": "此乃动态主题,你可以预先设置一个亮色主题及暗色主题,此后将根据系统的亮、暗主题色自动切换为你预先设置的相应主题。\n注鼠标 右击 此主题项即可打开亮、暗色主题设置窗口。",
"setting__basic_to_tray": "关闭软件时不退出软件将其最小化到系统托盘",
@ -267,6 +274,7 @@
"setting__desktop_lyric_enable": "显示歌词",
"setting__desktop_lyric_font": "歌词字体",
"setting__desktop_lyric_font_default": "默认",
"setting__desktop_lyric_hover_hide": "鼠标移入歌词区域时降低歌词透明度(此功能存在平台兼容性问题)",
"setting__desktop_lyric_lock": "锁定歌词",
"setting__desktop_lyric_lock_screen": "不允许歌词窗口拖出主屏幕之外",
"setting__download": "下载设置",

View File

@ -95,6 +95,11 @@
"list_add__title_first_add": "添加",
"list_add__title_first_move": "移動",
"list_add__title_last": "到...",
"list_import_tip__alldata": "導入失敗,這是一個所有數據備份文件,你需要去這裡導入:\n設置 -> 備份與恢復 -> 所有數據 -> 導入",
"list_import_tip__playlist": "導入失敗,這是一個列表備份文件,你需要去這裡導入:\n設置 -> 備份與恢復 -> 部分數據 -> 導入列表",
"list_import_tip__playlist_part": "導入失敗,這是一個單列表文件,你需要去這裡導入:\n我的列表 -> 右擊任意一個列表名 -> 在彈出的菜單中選擇導入",
"list_import_tip__setting": "導入失敗,這是一個設置備份文件,你需要去這裡導入:\n設置 -> 備份與恢復 -> 部分數據 -> 設置導入",
"list_import_tip__unknown": "導入失敗,未知的文件類型,請嘗試升級到最新版本後再試",
"list_sort_modal_by_album": "專輯名",
"list_sort_modal_by_down": "降序",
"list_sort_modal_by_field": "排序字段",
@ -127,6 +132,7 @@
"lists__sort_list": "排序歌曲",
"lists__source_detail": "歌單詳情頁",
"lists__sync": "更新",
"lists__sync_confirm_tip": "這將會把 {name} 內的歌曲替換成在線列表的歌曲,你確認要更新嗎?",
"load_list_file_error_detail": "我們已經幫你把舊的列表文件備份到{path}\n它以 JSON 格式存儲,你可以嘗試手動修復並恢復它\n\n錯誤詳情{detail}",
"load_list_file_error_title": "播放列表數據加載錯誤",
"loding": "加載中...",
@ -246,6 +252,7 @@
"setting__basic_sourcename_alias": "別名",
"setting__basic_sourcename_real": "原名",
"setting__basic_sourcename_title": "選擇音源名字類型",
"setting__basic_start_in_fullscreen": "以全屏模式啟動",
"setting__basic_theme": "主題顏色",
"setting__basic_theme_auto_tip": "此乃動態主題,你可以預先設置一個亮色主題及暗色主題,此後將根據系統的亮、暗主題色自動切換為你預先設置的相應主題。\n注鼠標 右擊 此主題項即可打開亮、暗色主題設置窗口。",
"setting__basic_to_tray": "關閉軟件時不退出軟件將其最小化到系統托盤",
@ -267,6 +274,7 @@
"setting__desktop_lyric_enable": "顯示歌詞",
"setting__desktop_lyric_font": "歌詞字體",
"setting__desktop_lyric_font_default": "默認",
"setting__desktop_lyric_hover_hide": "鼠標移入歌詞區域時降低歌詞透明度(此功能存在平台兼容性問題)",
"setting__desktop_lyric_lock": "鎖定歌詞",
"setting__desktop_lyric_lock_screen": "不允許歌詞窗口拖出主屏幕之外",
"setting__download": "下載設置",

View File

@ -160,7 +160,7 @@ function createWindow() {
/**
* Initial window options
*/
global.modules.mainWindow = new BrowserWindow({
const options = {
height: windowSizeInfo.height,
useContentSize: true,
width: windowSizeInfo.width,
@ -178,7 +178,12 @@ function createWindow() {
nodeIntegration: true,
spellcheck: false, // 禁用拼写检查器
},
})
}
if (global.appSetting.startInFullscreen) {
options.fullscreen = true
if (isLinux) options.resizable = true
}
global.modules.mainWindow = new BrowserWindow(options)
const shouldUseDarkColors = nativeTheme.shouldUseDarkColors
const themeId = global.appSetting.theme.id == 'auto'
@ -190,6 +195,7 @@ function createWindow() {
global.modules.mainWindow.loadURL(winURL + `?dt=${!!global.envParams.cmdParams.dt}&dark=${shouldUseDarkColors}&theme=${themeClass}`)
winEvent(global.modules.mainWindow)
if (global.envParams.cmdParams.odt) require('@main/utils').openDevTools(global.modules.mainWindow.webContents)
// global.modules.mainWindow.webContents.openDevTools()
if (!isDev) autoUpdate()

View File

@ -3,6 +3,10 @@ const needle = require('needle')
const { createCipheriv, publicEncrypt, constants, randomBytes, createHash } = require('crypto')
const USER_API_RENDERER_EVENT_NAME = require('../rendererEvent/name')
for (const key of Object.keys(process.env)) {
if (/^(?:http_proxy|https_proxy|NO_PROXY)$/i.test(key)) delete process.env[key]
}
const sendMessage = (action, data, status, message) => {
ipcRenderer.send(action, { data, status, message })
}

View File

@ -2,6 +2,7 @@ const { common: COMMON_EVENT_NAME, winLyric: WIN_LYRIC_EVENT_NAME, hotKey: HOT_K
const { mainSend, NAMES: { winLyric: ipcWinLyricNames } } = require('../../../common/ipc')
const { desktop_lyric } = require('../../../common/hotKey')
const { getLyricWindowBounds } = require('./utils')
const { isLinux } = require('@common/utils')
let isLock = null
let isEnable = null
@ -47,9 +48,9 @@ const setLrcConfig = () => {
if (isLock != desktopLyric.isLock) {
isLock = desktopLyric.isLock
if (desktopLyric.isLock) {
global.modules.lyricWindow.setIgnoreMouseEvents(true, { forward: false })
global.modules.lyricWindow.setIgnoreMouseEvents(true, { forward: !isLinux })
} else {
global.modules.lyricWindow.setIgnoreMouseEvents(false)
global.modules.lyricWindow.setIgnoreMouseEvents(false, { forward: !isLinux })
}
}
if (isAlwaysOnTop != desktopLyric.isAlwaysOnTop) {

View File

@ -64,7 +64,7 @@ const winEvent = lyricWindow => {
lyricWindow.once('ready-to-show', () => {
lyricWindow.show()
if (global.appSetting.desktopLyric.isLock) {
global.modules.lyricWindow.setIgnoreMouseEvents(true, { forward: false })
global.modules.lyricWindow.setIgnoreMouseEvents(true, { forward: !isLinux })
}
// linux下每次重开时貌似要重新设置置顶
if (isLinux && global.appSetting.desktopLyric.isAlwaysOnTop) {

View File

@ -1,6 +1,6 @@
<template lang="pug">
#container(:class="[theme, lrcConfig.isLock ? 'lock' : null]")
#main
#container(:class="[theme, { lock: lrcConfig.isLock }, { hide: isHoverHide && isMouseEnter }]")
#main(@mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave" @mousemove="handleMouseMoveMain")
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
.control-bar(v-show="!lrcConfig.isLock")
core-control-bar(:lrcConfig="lrcConfig" :themes="themeList")
@ -21,6 +21,51 @@ import { rendererOn, rendererInvoke, rendererSend, NAMES } from '../common/ipc'
window.ELECTRON_DISABLE_SECURITY_WARNINGS = process.env.ELECTRON_DISABLE_SECURITY_WARNINGS
let mouseCheckTools = {
x: 0,
y: 0,
preX: 0,
preY: 0,
timeout: null,
handleCheck(setShow) {
let xDiff = Math.abs(this.x - this.preX)
let yDiff = Math.abs(this.y - this.preY)
if (xDiff > 8) {
if (this.x > this.preX) {
if (this.x + xDiff * 1.25 > window.innerWidth - 16) return setShow()
} else {
if (this.x - xDiff * 1.25 < 8) return setShow()
}
}
if (yDiff > 8) {
if (this.y > this.preY) {
if (this.y + yDiff * 1.25 > window.innerHeight - 16) return setShow()
} else {
if (this.y - yDiff * 1.25 < 8) return setShow()
}
}
// setShow(false)
},
handleMove(x, y, setShow) {
// console.log(x, y, this.x, this.y)
this.preX = this.x
this.preY = this.y
this.x = x
this.y = y
this.startTimeout(setShow)
},
startTimeout(setShow) {
this.stopTimeout()
this.timeout = setTimeout(this.handleCheck.bind(this), 200, setShow)
},
stopTimeout() {
if (!this.timeout) return
clearTimeout(this.timeout)
this.timeout = null
},
}
export default {
data() {
return {
@ -39,6 +84,7 @@ export default {
x: -1,
y: -1,
theme: 0,
isHoverHide: false,
style: {
font: '',
fontSize: 125,
@ -91,6 +137,7 @@ export default {
className: 'blue2',
},
],
isMouseEnter: false,
}
},
computed: {
@ -98,6 +145,9 @@ export default {
let theme = this.themeList.find(t => t.id == this.lrcConfig.theme) || this.themeList[0]
return theme.className
},
isHoverHide() {
return this.lrcConfig.isLock && this.lrcConfig.isHoverHide
},
},
created() {
rendererOn(NAMES.winLyric.set_lyric_config, (event, config) => this.handleUpdateConfig(config))
@ -147,7 +197,7 @@ export default {
}
},
handleMove(clientX, clientY) {
if (!this.resize.origin) return
if (!this.resize.origin || this.lrcConfig.isLock) return
// if (!event.target.classList.contains('resize-' + this.resize.origin)) return
// console.log(event.target)
let bounds = {
@ -211,6 +261,24 @@ export default {
// handleMouseOver() {
// // this.handleMouseUp()
// },
handleMouseMoveMain(event) {
if (!this.isHoverHide) return
this.handleMouseEnter()
mouseCheckTools.handleMove(event.clientX, event.clientY, () => {
this.handleMouseLeave()
})
},
handleMouseEnter() {
// console.log('enter - >')
if (!this.isHoverHide || this.isMouseEnter) return
this.isMouseEnter = true
},
handleMouseLeave() {
// console.log('leave - <')
if (!this.isHoverHide) return
this.isMouseEnter = false
mouseCheckTools.stopTimeout()
},
},
}
</script>
@ -240,11 +308,16 @@ body {
padding: 8px;
box-sizing: border-box;
height: 100%;
transition: opacity .3s ease;
opacity: 1;
&.lock {
#main {
background-color: transparent;
}
}
&.hide {
opacity: .05;
}
}
.resize-left {

View File

@ -8,7 +8,7 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" viewBox="0 0 42 42" space="preserve">
<use xlink:href="#icon-addTo"></use>
</svg>
<input class="key-bind" :class="$style.newListInput" :value="newListName" type="text" :placeholder="$t('lists__new_list_input')" @keyup.enter="handleSaveList($event)" @blur="handleSaveList($event)"/>
<input :class="$style.newListInput" :value="newListName" type="text" :placeholder="$t('lists__new_list_input')" @keyup.enter="handleSaveList($event)" @blur="handleSaveList($event)"/>
</base-btn>
<span :class="$style.btn" :key="i" v-for="i in spaceNum"></span>
</div>
@ -21,6 +21,7 @@ import { mapMutations } from 'vuex'
import { computed } from '@renderer/utils/vueTools'
import { defaultList, loveList, userLists } from '@renderer/core/share/list'
import { getList } from '@renderer/core/share/utils'
import useKeyDown from '@renderer/utils/compositions/useKeyDown'
export default {
props: {
@ -57,6 +58,8 @@ export default {
},
emits: ['update:show'],
setup(props) {
const keyModDown = useKeyDown('mod')
const lists = computed(() => {
if (!props.musicInfo) return []
const targetMid = props.musicInfo.songmid
@ -67,6 +70,7 @@ export default {
].filter(l => !props.excludeListId.includes(l.id)).map(l => ({ ...l, isExist: getList(l.id).some(s => s.songmid == targetMid) }))
})
return {
keyModDown,
lists,
}
},
@ -103,6 +107,8 @@ export default {
this.isMove
? this.listMove({ fromId: this.fromListId, toId: this.lists[index].id, musicInfo: this.musicInfo })
: this.listAdd({ id: this.lists[index].id, musicInfo: this.musicInfo })
if (this.keyModDown && !this.isMove) return
this.$nextTick(() => {
this.handleClose()
})

View File

@ -8,7 +8,7 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" viewBox="0 0 42 42" space="preserve">
<use xlink:href="#icon-addTo"></use>
</svg>
<input class="key-bind" :class="$style.newListInput" :value="newListName" type="text" :placeholder="$t('lists__new_list_input')" @keyup.enter="handleSaveList($event)" @blur="handleSaveList($event)"/>
<input :class="$style.newListInput" :value="newListName" type="text" :placeholder="$t('lists__new_list_input')" @keyup.enter="handleSaveList($event)" @blur="handleSaveList($event)"/>
</base-btn>
<span :class="$style.btn" :key="i" v-for="i in spaceNum"></span>
</div>
@ -20,6 +20,7 @@
import { mapMutations } from 'vuex'
import { computed } from '@renderer/utils/vueTools'
import { defaultList, loveList, userLists } from '@renderer/core/share/list'
import useKeyDown from '@renderer/utils/compositions/useKeyDown'
export default {
props: {
@ -59,6 +60,8 @@ export default {
},
emits: ['update:show', 'confirm'],
setup(props) {
const keyModDown = useKeyDown('mod')
const lists = computed(() => {
return [
defaultList,
@ -67,6 +70,7 @@ export default {
].filter(l => !props.excludeListId.includes(l.id))
})
return {
keyModDown,
lists,
}
},
@ -104,6 +108,8 @@ export default {
this.isMove
? this.listMoveMultiple({ fromId: this.fromListId, toId: this.lists[index].id, list: this.musicList })
: this.listAddMultiple({ id: this.lists[index].id, list: this.musicList })
if (this.keyModDown && !this.isMove) return
this.$nextTick(() => {
this.handleClose()
this.$emit('confirm')

View File

@ -2,7 +2,7 @@ import { isLinux } from '@common/utils'
import { getEnvParams, setIgnoreMouseEvents } from '@renderer/utils/tools'
import { useRefGetter } from '@renderer/utils/vueTools'
import { sync, apiSource, proxy } from '@renderer/core/share'
import { sync, apiSource, proxy, isFullscreen } from '@renderer/core/share'
import useSync from './useSync'
import useUpdate from './useUpdate'
@ -22,6 +22,7 @@ export default () => {
sync.enable = setting.value.sync.enable
apiSource.value = setting.value.apiSource
Object.assign(proxy, setting.value.network.proxy)
isFullscreen.value = setting.value.startInFullscreen
const dieableIgnoreMouseEvents = () => {
if (window.dt) return
@ -51,6 +52,12 @@ export default () => {
getEnvParams().then(envParams => {
// 移除代理相关的环境变量设置,防止请求库自动应用它们
// eslint-disable-next-line no-undef
const processEnv = ENVIRONMENT
for (const key of Object.keys(processEnv)) {
if (/^(?:http_proxy|https_proxy|NO_PROXY)$/i.test(key)) delete processEnv[key]
}
const envProxy = envParams.cmdParams['proxy-server']
if (envProxy && typeof envProxy == 'string') {
const [host, port = ''] = envProxy.split(':')

View File

@ -12,9 +12,10 @@ import {
useRefGetter,
} from '@renderer/utils/vueTools'
const handle_key_esc_down = ({ event }) => {
if (event.repeat) return
if (event.target.tagName != 'INPUT' || event.target.classList.contains('ignore-esc')) {
const handle_key_down = ({ event, type, key }) => {
// console.log(key)
if (key != 'escape' || !event || event.repeat || type == 'up' || window.isEditingHotKey || event.target.classList.contains('ignore-esc')) return
if (event.target.tagName != 'INPUT') {
if (isFullscreen.value) {
event.lx_handled = true
rendererInvoke(NAMES.mainWindow.fullscreen, false).then(fullscreen => {
@ -113,7 +114,7 @@ export default ({
})
window.eventHub.emit(eventBaseName.bindKey)
window.eventHub.on('key_escape_down', handle_key_esc_down)
window.eventHub.on(eventBaseName.key_down, handle_key_down)
window.eventHub.on('key_mod+f12_down', handle_open_devtools)
window.eventHub.on('key_f11_down', handle_fullscreen)
window.eventHub.on(eventBaseName.fullscreenToggle, handle_fullscreen)
@ -128,7 +129,7 @@ export default ({
}
onBeforeUnmount(() => {
window.eventHub.off('key_escape_down', handle_key_esc_down)
window.eventHub.off(eventBaseName.key_down, handle_key_down)
window.eventHub.off('key_mod+f12_down', handle_open_devtools)
window.eventHub.off('key_f11_down', handle_fullscreen)
window.eventHub.off(eventBaseName.fullscreenToggle, handle_fullscreen)

View File

@ -18,8 +18,7 @@ export default () => {
return () => {
const waitUpdateLists = Object.entries(getListUpdateInfo())
.filter(([id, info]) => info.isAutoUpdate)
.map(([id]) => userLists.find(l => l.id === id))
.map(([id, info]) => info.isAutoUpdate && userLists.find(l => l.id === id))
.filter(_ => _)
for (let i = 2; i > 0; i--) {
handleSyncSourceList(waitUpdateLists)

View File

@ -20,11 +20,14 @@ rendererInvoke(NAMES.mainWindow.get_hot_key).then(({ local, global }) => {
})
eventHub.on(baseName.bindKey, () => {
keyBind.bindKey((key, eventKey, type, event, keys) => {
keyBind.bindKey((key, eventKey, type, event, keys, isEditing) => {
// console.log(`key_${key}_${type}`)
eventHub.emit(baseName.key_down, { event, keys, key, type })
// console.log(event, key)
if (!window.isEditingHotKey && appHotKeyConfig.local.enable && appHotKeyConfig.local.keys[key]) {
// console.log(key, eventKey, type, event, keys)
if (window.isEditingHotKey || (isEditing && type == 'down') || event?.lx_handled) return
if (event && appHotKeyConfig.local.enable && appHotKeyConfig.local.keys[key] && (key != 'escape' || !event.target.classList.contains('ignore-esc'))) {
// console.log(key, eventKey, type, keys, isEditing)
event.preventDefault()
if (type == 'up') return
@ -38,6 +41,7 @@ eventHub.on(baseName.bindKey, () => {
eventHub.emit(appHotKeyConfig.local.keys[key].action)
return
}
// console.log(`key_${key}_${type}`)
eventHub.emit(`key_${key}_${type}`, { event, keys, key, eventKey, type })
if (key != eventKey) eventHub.emit(`key_${eventKey}_${type}`, { event, keys, key, eventKey, type })
})

View File

@ -222,8 +222,8 @@ const actions = {
})
},
async getLrc({ commit, state }, musicInfo) {
const lrcInfo = await getStoreLyric(musicInfo)
// let lrcInfo = {}
let lrcInfo = await getStoreLyric(musicInfo)
// lrcInfo = {}
// if (lrcRequest && lrcRequest.cancelHttp) lrcRequest.cancelHttp()
if (existTimeExp.test(lrcInfo.lyric) && lrcInfo.tlyric != null) {
// if (musicInfo.lrc.startsWith('\ufeff[id:$00000000]')) {
@ -238,6 +238,7 @@ const actions = {
switch (musicInfo.source) {
case 'kg':
case 'kw':
case 'mg':
break
default:
return buildLyricInfo(lrcInfo, musicInfo)

View File

@ -0,0 +1,35 @@
import { useI18n } from '@renderer/utils/vueTools'
import { dialog } from '@renderer/plugins/Dialog'
export default () => {
const { t } = useI18n()
return (type) => {
let message
switch (type) {
case 'defautlList':
case 'playList':
message = t('list_import_tip__playlist')
break
case 'setting':
message = t('list_import_tip__setting')
break
case 'allData':
message = t('list_import_tip__alldata')
break
case 'playListPart':
message = t('list_import_tip__playlist_part')
break
default:
message = t('list_import_tip__unknown')
break
}
dialog({
message,
confirmButtonText: t('ok'),
})
}
}

View File

@ -0,0 +1,38 @@
import { onMounted, onBeforeUnmount, ref } from '@renderer/utils/vueTools'
export default name => {
const keyDown = ref(false)
const down = `key_${name}_down`
const up = `key_${name}_up`
const handle_key_down = event => {
if (!keyDown.value) {
// console.log(event)
switch (event.event.target.tagName) {
case 'INPUT':
case 'SELECT':
case 'TEXTAREA':
return
default: if (event.event.target.isContentEditable) return
}
keyDown.value = true
}
}
const handle_key_up = () => {
if (keyDown.value) keyDown.value = false
}
onMounted(() => {
window.eventHub.on(down, handle_key_down)
window.eventHub.on(up, handle_key_up)
})
onBeforeUnmount(() => {
window.eventHub.off(down, handle_key_down)
window.eventHub.off(up, handle_key_up)
})
return keyDown
}

View File

@ -2,7 +2,7 @@ import { isMac } from '../../common/utils'
const downKeys = new Set()
const handleEvent = (type, event, keys) => {
const handleEvent = (type, event, keys, isEditing) => {
let eventKey = event.key
if (isMac) {
let index = keys.indexOf('meta')
@ -23,7 +23,7 @@ const handleEvent = (type, event, keys) => {
downKeys.delete(key)
break
}
handleSendEvent(key, eventKey, type, event, keys)
handleSendEvent(key, eventKey, type, event, keys, isEditing)
}
// 修饰键处理
@ -54,7 +54,7 @@ const assertStopCallback = element => {
}
const handleKeyDown = event => {
if (assertStopCallback(event.target)) return
// if (assertStopCallback(event.target)) return
// event.preventDefault()
let keys = eventModifiers(event)
switch (event.key) {
@ -70,11 +70,11 @@ const handleKeyDown = event => {
keys.push((event.code.includes('Numpad') ? event.code.replace(/^Numpad(\w{1,3})\w*$/i, 'num$1') : event.key).toLowerCase())
break
}
handleEvent('down', event, keys)
handleEvent('down', event, keys, assertStopCallback(event.target))
}
const handleKeyUp = event => {
if (assertStopCallback(event.target)) return
// if (assertStopCallback(event.target)) return
event.preventDefault()
let keys = eventModifiers(event)
switch (event.key) {
@ -88,7 +88,7 @@ const handleKeyUp = event => {
keys.push((event.code.includes('Numpad') ? event.code.replace(/^Numpad(\w{1,3})\w*$/i, 'num$1') : event.key).toLowerCase())
break
}
handleEvent('up', event, keys)
handleEvent('up', event, keys, assertStopCallback(event.target))
}
let handleSendEvent
@ -107,7 +107,7 @@ const unbindKey = () => {
const clearDownKeys = () => {
let keys = Array.from(downKeys)
for (let i = keys.length - 1; i > -1; i--) {
handleSendEvent(keys[i], 'up')
handleSendEvent(keys[i], keys[i], 'up', null, [keys[i]])
}
downKeys.clear()
}

View File

@ -68,6 +68,8 @@ export default {
const item = arr[i]
if (callback(item)) {
delete item.sortedSinger
delete item.lowerCaseName
delete item.lowerCaseAlbumName
tempResult.push(item)
arr.splice(i, 1)
}
@ -80,10 +82,11 @@ export default {
const musicName = trimStr(musicInfo.name)
const lowerCaseName = String(musicName).toLowerCase()
const lowerCaseAlbumName = String(musicInfo.albumName).toLowerCase()
const excludeSource = ['xm']
for (const source of sources.sources) {
if (!sources[source.id].musicSearch || source.id === musicInfo.source || source.id === 'xm') continue
if (!sources[source.id].musicSearch || source.id === musicInfo.source || excludeSource.includes(source.id)) continue
tasks.push(sources[source.id].musicSearch.search(`${musicName} ${musicInfo.singer || ''}`.trim(), 1, { limit: 10 }).then(res => {
tasks.push(sources[source.id].musicSearch.search(`${musicName} ${musicInfo.singer || ''}`.trim(), 1, { limit: 25 }).then(res => {
for (const item of res.list) {
item.sortedSinger = String(sortSingle(item.singer)).toLowerCase()
item.name = trimStr(item.name)
@ -110,18 +113,6 @@ export default {
return item
}
}
for (const item of res.list) {
item.sortedSinger = String(sortSingle(item.singer)).toLowerCase()
item.name = trimStr(item.name)
item.lowerCaseName = String(item.name).toLowerCase()
item.lowerCaseAlbumName = String(item.albumName).toLowerCase()
// console.log(lowerCaseName, item.lowerCaseName)
if (
item.sortedSinger === sortedSinger && item.interval === musicInfo.interval
) {
return item
}
}
return null
}).catch(_ => null))
}
@ -135,6 +126,7 @@ export default {
for (const item of result) {
delete item.sortedSinger
delete item.lowerCaseName
delete item.lowerCaseAlbumName
}
newResult.push(...result)
}

View File

@ -192,8 +192,10 @@ export default {
let result = timeExp.exec(line)
if (result) {
const text = line.replace(timeExp, '').trim()
let time = RegExp.$1
if (/\.\d\d$/.test(time)) time += '0'
lrcArr.push({
time: RegExp.$1,
time,
text,
})
} else if (lrcTools.rxps.tagLine.test(line)) {

View File

@ -89,14 +89,26 @@ export const lrcTools = {
isOK: false,
lines: [],
tags: [],
getWordInfo(str, str2) {
getWordInfo(str, str2, prevWord) {
const offset = parseInt(str)
const offset2 = parseInt(str2)
const startTime = Math.floor((offset + offset2) / (this.offset * 2))
const timeLength = Math.floor((offset - offset2) / (this.offset2 * 2))
let startTime = Math.abs((offset + offset2) / (this.offset * 2))
let endTime = Math.abs((offset - offset2) / (this.offset2 * 2)) + startTime
if (prevWord) {
if (startTime < prevWord.endTime) {
prevWord.endTime = startTime
if (prevWord.startTime > prevWord.endTime) {
prevWord.startTime = prevWord.endTime
}
prevWord.newTimeStr = `<${prevWord.startTime},${prevWord.endTime - prevWord.startTime}>`
// console.log(prevWord)
}
}
return {
startTime,
timeLength,
endTime,
timeStr: `<${startTime},${endTime - startTime}>`,
}
},
parseLine(line) {
@ -111,10 +123,13 @@ export const lrcTools = {
const wordTimes = words.match(this.rxps.wordTimeAll)
if (!wordTimes) return
// console.log(wordTimes)
let preTimeInfo
for (const timeStr of wordTimes) {
const result = this.rxps.wordTime.exec(timeStr)
const wordInfo = this.getWordInfo(result[1], result[2])
words = words.replace(timeStr, `<${wordInfo.startTime},${wordInfo.timeLength}>`)
const wordInfo = this.getWordInfo(result[1], result[2], preTimeInfo)
words = words.replace(timeStr, wordInfo.timeStr)
if (preTimeInfo?.newTimeStr) words = words.replace(preTimeInfo.timeStr, preTimeInfo.newTimeStr)
preTimeInfo = wordInfo
}
this.lines.push(time + words)
return
@ -127,8 +142,8 @@ export const lrcTools = {
content = content.substring(0, content.indexOf(']['))
}
const valueOf = parseInt(content, 8)
this.offset = Math.floor(valueOf / 10)
this.offset2 = Math.floor(valueOf % 10)
this.offset = Math.trunc(valueOf / 10)
this.offset2 = Math.trunc(valueOf % 10)
if (this.offset == 0 || Number.isNaN(this.offset) || this.offset2 == 0 || Number.isNaN(this.offset2)) {
this.isOK = false
}
@ -147,12 +162,13 @@ export const lrcTools = {
tools.tags = []
for (const line of lines) {
if (!tools.isOK) return ''
if (!tools.isOK) throw new Error('failed')
tools.parseLine(line)
}
if (!tools.lines.length) return ''
let lrcs = tools.lines.join('\n')
if (tools.tags.length) lrcs = `${tools.tags.join('\n')}\n${lrcs}`
// console.log(lrcs)
return lrcs
},
}

View File

@ -148,6 +148,8 @@ export default {
img: item.albumImgs && item.albumImgs.length ? item.albumImgs[0].img : null,
lrc: null,
lrcUrl: item.lrcUrl,
mrcUrl: item.mrcUrl,
trcUrl: item.trcUrl,
otherSource: null,
types,
_types,

View File

@ -1,14 +1,112 @@
import { httpFetch } from '../../request'
import musicSearch from './musicSearch'
import { decrypt } from './mrc'
const mrcTools = {
rxps: {
lineTime: /^\s*\[(\d+),\d+\]/,
wordTime: /\(\d+,\d+\)/,
wordTimeAll: /(\(\d+,\d+\))/g,
},
parseLyric(str) {
str = str.replace(/\r/g, '')
const lines = str.split('\n')
const lxlrcLines = []
const lrcLines = []
for (const line of lines) {
if (line.length < 6) continue
let result = this.rxps.lineTime.exec(line)
if (!result) continue
const startTime = parseInt(result[1])
let time = startTime
let ms = time % 1000
time /= 1000
let m = parseInt(time / 60).toString().padStart(2, '0')
time %= 60
let s = parseInt(time).toString().padStart(2, '0')
time = `${m}:${s}.${ms}`
let words = line.replace(this.rxps.lineTime, '')
lrcLines.push(`[${time}]${words.replace(this.rxps.wordTimeAll, '')}`)
let times = words.match(this.rxps.wordTimeAll)
if (!times) continue
times = times.map(time => {
const result = /\((\d+),(\d+)\)/.exec(time)
return `<${parseInt(result[1]) - startTime},${result[2]}>`
})
const wordArr = words.split(this.rxps.wordTime)
const newWords = times.map((time, index) => `${time}${wordArr[index]}`).join('')
lxlrcLines.push(`[${time}]${newWords}`)
}
return {
lyric: lrcLines.join('\n'),
lxlyric: lxlrcLines.join('\n'),
}
},
getText(url, tryNum = 0) {
const requestObj = httpFetch(url, {
headers: {
Referer: 'https://app.c.nf.migu.cn/',
'User-Agent': 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36',
channel: '0146921',
},
})
return requestObj.promise.then(({ statusCode, body }) => {
if (statusCode == 200) return body
if (tryNum > 5 || statusCode == 404) return Promise.reject('歌词获取失败')
return this.getText(url, ++tryNum)
})
},
getMrc(url) {
return this.getText(url).then(text => {
return this.parseLyric(decrypt(text))
})
},
getLrc(url) {
return this.getText(url).then(text => ({ lxlyric: '', lyric: text }))
},
getTrc(url) {
if (!url) return Promise.resolve('')
return this.getText(url)
},
getMusicInfo(songInfo) {
return songInfo.mrcUrl == null
? musicSearch.search(`${songInfo.name} ${songInfo.singer || ''}`.trim(), 1, { limit: 25 }).then(({ list }) => {
const targetSong = list.find(s => s.songmid == songInfo.songmid)
return targetSong ? { lrcUrl: targetSong.lrcUrl, mrcUrl: targetSong.mrcUrl, trcUrl: targetSong.trcUrl } : Promise.reject('获取歌词失败')
})
: Promise.resolve({ lrcUrl: songInfo.lrcUrl, mrcUrl: songInfo.mrcUrl, trcUrl: songInfo.trcUrl })
},
getLyric(songInfo) {
return {
promise: this.getMusicInfo(songInfo).then(info => {
let p
if (info.mrcUrl) p = this.getMrc(info.mrcUrl)
else if (info.lrcUrl) p = this.getLrc(info.lrcUrl)
if (p == null) return Promise.reject('获取歌词失败')
return Promise.all([p, this.getTrc(info.trcUrl)]).then(([lrcInfo, tlyric]) => {
lrcInfo.tlyric = tlyric
return lrcInfo
})
}),
cancelHttp() {},
}
},
}
export default {
getLyric(songInfo, tryNum = 0) {
getLyricWeb(songInfo, tryNum = 0) {
// console.log(songInfo.copyrightId)
if (songInfo.lrcUrl) {
let requestObj = httpFetch(songInfo.lrcUrl)
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.getLyricWeb(songInfo, ++tryNum)
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
@ -19,15 +117,15 @@ export default {
})
return requestObj
} else {
let requestObj = httpFetch(`http://music.migu.cn/v3/api/music/audioPlayer/getLyric?copyrightId=${songInfo.copyrightId}`, {
let requestObj = httpFetch(`https://music.migu.cn/v3/api/music/audioPlayer/getLyric?copyrightId=${songInfo.copyrightId}`, {
headers: {
Referer: 'http://music.migu.cn/v3/music/player/audio?from=migu',
Referer: 'https://music.migu.cn/v3/music/player/audio?from=migu',
},
})
requestObj.promise = requestObj.promise.then(({ body }) => {
if (body.returnCode !== '000000' || !body.lyric) {
if (tryNum > 5) return Promise.reject(new Error('Get lyric failed'))
let tryRequestObj = this.getLyric(songInfo, ++tryNum)
let tryRequestObj = this.getLyricWeb(songInfo, ++tryNum)
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
@ -39,4 +137,14 @@ export default {
return requestObj
}
},
getLyric(songInfo) {
let requestObj = mrcTools.getLyric(songInfo)
requestObj.promise = requestObj.promise.catch(() => {
let webRequestObj = this.getLyricWeb(songInfo)
requestObj.cancelHttp = webRequestObj.cancelHttp.bind(webRequestObj)
return webRequestObj.promise
})
return requestObj
},
}

View File

@ -0,0 +1,104 @@
// const key = 'karakal@123Qcomyidongtiantianhaoting'
const DELTA = 2654435769n
const MIN_LENGTH = 32
// const SPECIAL_CHAR = '0'
const keyArr = [
27303562373562475n,
18014862372307051n,
22799692160172081n,
34058940340699235n,
30962724186095721n,
27303523720101991n,
27303523720101998n,
31244139033526382n,
28992395054481524n,
]
const teaDecrypt = (data, key) => {
const length = data.length
const lengthBitint = BigInt(length)
if (length >= 1) {
// let j = data[data.length - 1];
let j2 = data[0]
let j3 = toLong((6n + (52n / lengthBitint)) * DELTA)
while (true) {
let j4 = j3
if (j4 == 0n) break
let j5 = toLong(3n & toLong(j4 >> 2n))
let j6 = lengthBitint
while (true) {
j6--
if (j6 > 0n) {
let j7 = data[(j6 - 1n)]
let i = j6
j2 = toLong(data[i] - (toLong(toLong(j2 ^ j4) + toLong(j7 ^ key[toLong(toLong(3n & j6) ^ j5)])) ^ toLong(toLong(toLong(j7 >> 5n) ^ toLong(j2 << 2n)) + toLong(toLong(j2 >> 3n) ^ toLong(j7 << 4n)))))
data[i] = j2
} else break
}
let j8 = data[lengthBitint - 1n]
j2 = toLong(data[0n] - toLong(toLong(toLong(key[toLong(toLong(j6 & 3n) ^ j5)] ^ j8) + toLong(j2 ^ j4)) ^ toLong(toLong(toLong(j8 >> 5n) ^ toLong(j2 << 2n)) + toLong(toLong(j2 >> 3n) ^ toLong(j8 << 4n)))))
data[0] = j2
j3 = toLong(j4 - DELTA)
}
}
return data
}
const longArrToString = (data) => {
const arrayList = []
for (const j of data) arrayList.push(longToBytes(j).toString('utf16le'))
return arrayList.join('')
}
// https://stackoverflow.com/a/29132118
const longToBytes = (l) => {
const result = Buffer.alloc(8)
for (let i = 0; i < 8; i++) {
result[i] = parseInt(l & 0xFFn)
l >>= 8n
}
return result
}
const toBigintArray = (data) => {
const length = Math.floor(data.length / 16)
const jArr = Array(length)
for (let i = 0; i < length; i++) {
jArr[i] = toLong(data.substring(i * 16, (i * 16) + 16))
}
return jArr
}
// https://github.com/lyswhut/lx-music-desktop/issues/445#issuecomment-1139338682
const MAX = 9223372036854775807n
const MIN = -9223372036854775808n
const toLong = str => {
const num = typeof str == 'string' ? BigInt('0x' + str) : str
if (num > MAX) return toLong(num - (1n << 64n))
else if (num < MIN) return toLong(num + (1n << 64n))
return num
}
export const decrypt = (data) => {
// console.log(data.length)
// -3551594764563790630
// console.log(toLongArrayFromArr(Buffer.from(key)))
// console.log(teaDecrypt(toBigintArray(data), keyArr))
// console.log(longArrToString(teaDecrypt(toBigintArray(data), keyArr)))
// console.log(toByteArray(teaDecrypt(toBigintArray(data), keyArr)))
return (data == null || data.length < MIN_LENGTH)
? data
: longArrToString(teaDecrypt(toBigintArray(data), keyArr))
}
// console.log(14895149309145760986n - )
// console.log(toLong('14895149309145760986'))
// console.log(decrypt(str))
// console.log(decrypt(str))
// console.log(toByteArray([6048138644744000495n]))
// console.log(toByteArray([16325999628386395n]))
// console.log(toLong(90994076459972177136n))

View File

@ -89,7 +89,7 @@ export default {
name: item.name,
albumName: albumNInfo.name,
albumId: albumNInfo.id,
songmid: item.id,
songmid: item.copyrightId,
songId: item.songId,
copyrightId: item.copyrightId,
source: 'mg',
@ -97,6 +97,8 @@ export default {
img: item.imgItems && item.imgItems.length ? item.imgItems[0].img : null,
lrc: null,
lrcUrl: item.lyricUrl,
mrcUrl: item.mrcurl,
trcUrl: item.trcUrl,
otherSource: null,
types,
_types,

View File

@ -212,6 +212,8 @@ export default {
img: item.albumImgs && item.albumImgs.length ? item.albumImgs[0].img : null,
lrc: null,
lrcUrl: item.lrcUrl,
mrcUrl: item.mrcUrl,
trcUrl: item.trcUrl,
otherSource: null,
types,
_types,

View File

@ -90,7 +90,7 @@ export default {
listItemHeight,
handleSelectData,
removeAllSelect,
} = useList({ list, setting, emit })
} = useList({ list, setting })
const {
handlePlayMusic,

View File

@ -94,7 +94,7 @@ export default ({ list, setting }) => {
}
}
watch(() => list, removeAllSelect)
watch(list, removeAllSelect)
return {
selectedList,

View File

@ -30,12 +30,12 @@
@contextmenu="handleListsItemRigthClick($event, index)" :aria-label="item.name" v-for="(item, index) in userLists" :key="item.id" :aria-selected="defaultList.id == listId"
>
<span :class="$style.listsLabel" @click="handleListToggle(item.id, index + 2)">{{item.name}}</span>
<input class="key-bind" :class="$style.listsInput" @contextmenu.stop type="text"
<input :class="$style.listsInput" @contextmenu.stop type="text"
@keyup.enter="handleListsSave(index, $event)" @blur="handleListsSave(index, $event)" :value="item.name" :placeholder="item.name"/>
</li>
<transition enter-active-class="animated-fast slideInLeft" leave-active-class="animated-fast fadeOut" @after-leave="handleListsNewAfterLeave" @after-enter="$refs.dom_listsNewInput.focus()">
<li :class="[$style.listsItem, $style.listsNew, listsData.isNewLeave ? $style.newLeave : null]" v-if="listsData.isShowNewList">
<input class="key-bind" :class="$style.listsInput" @contextmenu.stop ref="dom_listsNewInput" type="text" @keyup.enter="handleListsCreate"
<input :class="$style.listsInput" @contextmenu.stop ref="dom_listsNewInput" type="text" @keyup.enter="handleListsCreate"
@blur="handleListsCreate" :placeholder="$t('lists__new_list_input')"/>
</li>
</transition>
@ -60,6 +60,7 @@ import { getList } from '@renderer/core/share/utils'
import useDarg from '@renderer/utils/compositions/useDrag'
import { getListUpdateInfo } from '@renderer/utils/data'
import useSyncSourceList from '@renderer/utils/compositions/useSyncSourceList'
import useImportTip from '@renderer/utils/compositions/useImportTip'
export default {
name: 'MyLists',
@ -78,6 +79,7 @@ export default {
const dom_lists_list = ref(null)
const lists = computed(() => [defaultList, loveList, ...userLists])
const setUserListPosition = useCommit('list', 'setUserListPosition')
const showImportTip = useImportTip()
const syncSourceList = useSyncSourceList()
@ -100,6 +102,7 @@ export default {
dom_lists_list,
setDisabledSort: setDisabled,
syncSourceList,
showImportTip,
}
},
emits: ['show-menu'],
@ -352,7 +355,13 @@ export default {
this.handleExportList(index)
break
case 'sync':
this.handleSyncSourceList(userLists[index])
this.$dialog.confirm({
message: this.$t('lists__sync_confirm_tip', { name: userLists[index].name }),
confirmButtonText: this.$t('lists__remove_tip_button'),
}).then(isSync => {
if (!isSync) return
this.handleSyncSourceList(userLists[index])
})
break
case 'remove':
this.$dialog.confirm({
@ -449,7 +458,7 @@ export default {
} catch (error) {
return
}
if (listData.type !== 'playListPart') return
if (listData.type !== 'playListPart') return this.showImportTip(listData.type)
const targetList = this.lists.find(l => l.id == listData.data.id)
if (targetList) {
const confirm = await this.$dialog.confirm({

View File

@ -4,12 +4,16 @@ teleport(to="#view")
transition(enter-active-class="animated-fast zoomIn" leave-active-class="animated zoomOut" @after-leave="handleAnimated")
div(:class="$style.search" v-show="visible")
div(:class="$style.form")
input.key-bind.ignore-esc(:placeholder="placeholder" v-model.trim="text" ref="dom_input"
input.ignore-esc(:placeholder="placeholder" v-model.trim="text" ref="dom_input"
@input="handleDelaySearch"
@keyup.enter="handleTemplistClick(selectIndex)"
@keyup.arrow-down.prevent="handleKeyDown"
@keyup.arrow-up.prevent="handleKeyUp"
@keyup.escape.prevent="handleKeyEsc"
@keydown.control.prevent="handle_key_mod_down"
@keydown.meta.prevent="handle_key_mod_down"
@keyup.control.prevent="handle_key_mod_up"
@keyup.meta.prevent="handle_key_mod_up"
@contextmenu="handleContextMenu")
button(type="button" @click="handleHide")
slot
@ -147,13 +151,13 @@ export default {
},
mounted() {
this.init()
window.eventHub.on('key_mod_down', this.handle_key_mod_down)
window.eventHub.on('key_mod_up', this.handle_key_mod_up)
// window.eventHub.on('key_mod_down', this.handle_key_mod_down)
// window.eventHub.on('key_mod_up', this.handle_key_mod_up)
window.eventHub.on('key_mod+f_down', this.handle_key_mod_f_down)
},
beforeUnmount() {
window.eventHub.off('key_mod_down', this.handle_key_mod_down)
window.eventHub.off('key_mod_up', this.handle_key_mod_up)
// window.eventHub.off('key_mod_down', this.handle_key_mod_down)
// window.eventHub.off('key_mod_up', this.handle_key_mod_up)
window.eventHub.off('key_mod+f_down', this.handle_key_mod_f_down)
},
methods: {

View File

@ -11,7 +11,7 @@ dd
span.hover(:aria-label="$t('setting__click_copy')" @click="clipboardWriteText('glqw')") glqw
p.small
| 软件的常见问题可转至
span.hover.underline(:aria-label="$t('setting__click_open')" @click="openUrl('https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md')") 常见问题
span.hover.underline(:aria-label="$t('setting__click_open')" @click="openUrl('https://lyswhut.github.io/lx-music-doc/desktop/faq')") 常见问题
p.small
strong 本软件没有客服
| 但我们整理了一些常见的使用问题

View File

@ -39,6 +39,7 @@ import { getList } from '@renderer/core/share/utils'
import path from 'path'
import { dialog } from '@renderer/plugins/Dialog'
import iconv from 'iconv-lite'
import useImportTip from '@renderer/utils/compositions/useImportTip'
export default {
name: 'SettingUpdate',
@ -48,6 +49,7 @@ export default {
const settingVersion = useRefGetter('settingVersion')
const setSettingVersion = useCommit('setSettingVersion')
const setList = useCommit('list', 'setList')
const showImportTip = useImportTip()
const handleUpdateSetting = (config) => {
currentStting.value = JSON.parse(JSON.stringify(config))
@ -66,7 +68,7 @@ export default {
} catch (error) {
return
}
if (allData.type !== 'allData') return
if (allData.type !== 'allData') return showImportTip(allData.type)
// 0.6.2
if (allData.defaultList) return setList({ id: 'default', list: allData.defaultList.list, name: '试听列表' })
@ -145,7 +147,7 @@ export default {
} catch (error) {
return
}
if (settingData.type !== 'setting') return
if (settingData.type !== 'setting') return showImportTip(settingData.type)
const { version: settingVersion, setting } = mergeSetting(settingData.data)
setting.isAgreePact = false
refreshSetting(setting, settingVersion)
@ -202,7 +204,7 @@ export default {
// 0.6.2
if (listData.type === 'defautlList') return setList({ id: 'default', list: listData.data.list, name: '试听列表' })
if (listData.type !== 'playList') return
if (listData.type !== 'playList') return showImportTip(listData.type)
for (const list of listData.data) {
setList(list)

View File

@ -20,6 +20,8 @@ dd
base-checkbox(id="setting_show_animate" v-model="currentStting.isShowAnimation" :label="$t('setting__basic_show_animation')")
.gap-top
base-checkbox(id="setting_animate" v-model="currentStting.randomAnimate" :label="$t('setting__basic_animation')")
.gap-top
base-checkbox(id="setting_start_in_fullscreen" v-model="currentStting.startInFullscreen" :label="$t('setting__basic_start_in_fullscreen')")
.gap-top
base-checkbox(id="setting_to_tray" v-model="currentStting.tray.isShow" :label="$t('setting__basic_to_tray')")
p.gap-top

View File

@ -13,6 +13,8 @@ dd
base-checkbox(id="setting_desktop_lyric_alwaysOnTopLoop" v-model="currentStting.desktopLyric.isAlwaysOnTopLoop" :label="$t('setting__desktop_lyric_always_on_top_loop')")
.gap-top
base-checkbox(id="setting_desktop_lyric_lockScreen" v-model="currentStting.desktopLyric.isLockScreen" :label="$t('setting__desktop_lyric_lock_screen')")
.gap-top(v-if="!isLinux")
base-checkbox(id="setting_desktop_lyric_hoverHide" v-model="currentStting.desktopLyric.isHoverHide" :label="$t('setting__desktop_lyric_hover_hide')")
dd
h3#desktop_lyric_font {{$t('setting__desktop_lyric_font')}}
div
@ -23,6 +25,7 @@ dd
import { ref, computed, useI18n } from '@renderer/utils/vueTools'
import { getSystemFonts } from '@renderer/utils/tools'
import { currentStting } from '../setting'
import { isLinux } from '@common/utils'
export default {
name: 'SettingDesktopLyric',
@ -39,6 +42,7 @@ export default {
return {
currentStting,
fontList,
isLinux,
}
},
}

View File

@ -7,7 +7,7 @@ dd
div(:class="$style.hotKeyContainer" :style="{ opacity: current_hot_key.local.enable ? 1 : .6 }")
div(:class="$style.hotKeyItem" v-for="item in allHotKeys.local")
h4(:class="$style.hotKeyItemTitle") {{$t('setting__hot_key_' + item.name)}}
base-input.key-bind(:class="$style.hotKeyItemInput" readonly @keyup.prevent :placeholder="$t('setting__hot_key_unset_input')"
base-input(:class="$style.hotKeyItemInput" readonly @keyup.prevent :placeholder="$t('setting__hot_key_unset_input')"
:value="hotKeyConfig.local[item.name] && formatHotKeyName(hotKeyConfig.local[item.name].key)"
@focus="handleHotKeyFocus($event, item, 'local')"
@blur="handleHotKeyBlur($event, item, 'local')")
@ -18,7 +18,7 @@ dd
div(:class="$style.hotKeyContainer" :style="{ opacity: current_hot_key.global.enable ? 1 : .6 }")
div(:class="$style.hotKeyItem" v-for="item in allHotKeys.global")
h4(:class="$style.hotKeyItemTitle") {{$t('setting__hot_key_' + item.name)}}
base-input.key-bind(:class="[$style.hotKeyItemInput, hotKeyConfig.global[item.name] && hotKeyStatus[hotKeyConfig.global[item.name].key] && hotKeyStatus[hotKeyConfig.global[item.name].key].status === false ? $style.hotKeyFailed : null]"
base-input(:class="[$style.hotKeyItemInput, hotKeyConfig.global[item.name] && hotKeyStatus[hotKeyConfig.global[item.name].key] && hotKeyStatus[hotKeyConfig.global[item.name].key].status === false ? $style.hotKeyFailed : null]"
:value="hotKeyConfig.global[item.name] && formatHotKeyName(hotKeyConfig.global[item.name].key)" @input.prevent readonly :placeholder="$t('setting__hot_key_unset_input')"
@focus="handleHotKeyFocus($event, item, 'global')"
@blur="handleHotKeyBlur($event, item, 'global')")
@ -102,65 +102,67 @@ export default {
})
}
const handleHotKeyBlur = async(event, info, type) => {
await hotKeySetEnable(true)
window.isEditingHotKey = false
isEditHotKey = false
const prevInput = hotKeyTargetInput
hotKeyTargetInput = null
if (prevInput.value == t('setting__hot_key_tip_input')) {
prevInput.value = newHotKey ? formatHotKeyName(newHotKey) : ''
return
}
let config = hotKeyConfig.value[type][info.name]
let originKey
if (type == 'global' && newHotKey && current_hot_key.value.global.enable) {
try {
await hotKeySetConfig({
action: 'register',
data: {
key: newHotKey,
info,
},
})
} catch (error) {
console.log(error)
const handleHotKeyBlur = (event, info, type) => {
setTimeout(async() => {
await hotKeySetEnable(true)
window.isEditingHotKey = false
isEditHotKey = false
const prevInput = hotKeyTargetInput
hotKeyTargetInput = null
if (prevInput?.value == t('setting__hot_key_tip_input')) {
prevInput.value = newHotKey ? formatHotKeyName(newHotKey) : ''
return
}
}
if (config) {
if (config.key == newHotKey) return
originKey = config.key
delete current_hot_key.value[type].keys[config.key]
} else if (!newHotKey) return
if (newHotKey) {
for (const [tempType, tempInfo] of Object.entries(current_hot_key.value)) {
if (tempType == type) continue
config = tempInfo.keys[newHotKey]
if (config) {
console.log(newHotKey, info, config, info.name, config.name)
delete current_hot_key.value[tempType].keys[newHotKey]
break
let config = hotKeyConfig.value[type][info.name]
let originKey
if (type == 'global' && newHotKey && current_hot_key.value.global.enable) {
try {
await hotKeySetConfig({
action: 'register',
data: {
key: newHotKey,
info,
},
})
} catch (error) {
console.log(error)
return
}
}
current_hot_key.value[type].keys[newHotKey] = info
}
if (config) {
if (config.key == newHotKey) return
originKey = config.key
delete current_hot_key.value[type].keys[config.key]
} else if (!newHotKey) return
initHotKeyConfig()
// console.log(this.current_hot_key.global.keys)
if (originKey && current_hot_key.value.global.enable) {
try {
await hotKeySetConfig({
action: 'unregister',
data: originKey,
})
} catch (error) {
console.log(error)
if (newHotKey) {
for (const [tempType, tempInfo] of Object.entries(current_hot_key.value)) {
if (tempType == type) continue
config = tempInfo.keys[newHotKey]
if (config) {
console.log(newHotKey, info, config, info.name, config.name)
delete current_hot_key.value[tempType].keys[newHotKey]
break
}
}
current_hot_key.value[type].keys[newHotKey] = info
}
}
await handleHotKeySaveConfig()
await getHotKeyStatus()
initHotKeyConfig()
// console.log(this.current_hot_key.global.keys)
if (originKey && current_hot_key.value.global.enable) {
try {
await hotKeySetConfig({
action: 'unregister',
data: originKey,
})
} catch (error) {
console.log(error)
}
}
await handleHotKeySaveConfig()
await getHotKeyStatus()
})
}
const handleKeyDown = ({ event, keys, key, type }) => {

View File

@ -1,7 +1,7 @@
<template lang="pug">
dt#sync
| {{$t('setting__sync')}}
button(class="help-btn" @click="openUrl('https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md#%E5%90%8C%E6%AD%A5%E5%8A%9F%E8%83%BD%E7%9A%84%E4%BD%BF%E7%94%A8%E5%AE%9E%E9%AA%8C%E6%80%A7%E9%A6%96%E6%AC%A1%E4%BD%BF%E7%94%A8%E5%89%8D%E5%BB%BA%E8%AE%AE%E5%85%88%E5%A4%87%E4%BB%BD%E4%B8%80%E6%AC%A1%E5%88%97%E8%A1%A8')" :aria-label="$t('setting__sync_tip')")
button(class="help-btn" @click="openUrl('https://lyswhut.github.io/lx-music-doc/desktop/faq/sync')" :aria-label="$t('setting__sync_tip')")
svg-icon(name="help-circle-outline")
dd
base-checkbox(id="setting_sync_enable" v-model="currentStting.sync.enable" :label="syncEnableTitle")

View File

@ -3,7 +3,7 @@ dt#update {{$t('setting__update')}}
dd
p.small
| {{$t('setting__update_latest_label')}}{{versionInfo.newVersion ? versionInfo.newVersion.version : $t('setting__update_unknown')}}
p.small {{$t('setting__update_current_label')}}{{versionInfo.version}}
p.small(@click="handleOPenDevTools") {{$t('setting__update_current_label')}}{{versionInfo.version}}
p.small(v-if="versionInfo.downloadProgress" style="line-height: 1.5;")
| {{$t('setting__update_downloading')}}
br
@ -20,12 +20,30 @@ import { versionInfo } from '@renderer/core/share'
import { sizeFormate } from '@renderer/utils/tools'
// import { openDirInExplorer, selectDir } from '@renderer/utils'
import { currentStting } from '../setting'
import { rendererSend, NAMES } from '@common/ipc'
export default {
name: 'SettingUpdate',
setup() {
let lastClickTime = 0
let clickNum = 0
const { t } = useI18n()
const handleOPenDevTools = () => {
if (window.performance.now() - lastClickTime > 1000) {
if (clickNum > 0) clickNum = 0
} else {
if (clickNum > 6) {
rendererSend(NAMES.mainWindow.open_dev_tools)
clickNum = 0
return
}
}
clickNum++
lastClickTime = window.performance.now()
}
const downloadProgress = computed(() => {
return versionInfo.downloadProgress
? `${versionInfo.downloadProgress.percent.toFixed(2)}% - ${sizeFormate(versionInfo.downloadProgress.transferred)}/${sizeFormate(versionInfo.downloadProgress.total)} - ${sizeFormate(versionInfo.downloadProgress.bytesPerSecond)}/s`
@ -40,6 +58,7 @@ export default {
currentStting,
versionInfo,
downloadProgress,
handleOPenDevTools,
showUpdateModal,
}
},

View File

@ -17,7 +17,7 @@ material-modal(:show="modelValue" bg-close @close="handleClose" teleport="#view"
div(:class="$style.note")
p(:class="[$style.ruleLink]")
| {{$t('user_api__readme')}}
span.hover.underline(@click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%BA%90%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E8%AF%B4%E6%98%8E')" aria-label="https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md") FAQ.md
span.hover.underline(@click="handleOpenUrl('https://lxmusic.toside.cn/desktop/custom-source')" aria-label="https://lxmusic.toside.cn/desktop/custom-source") FAQ
p {{$t('user_api__note')}}
div(:class="$style.footer")
base-btn(:class="$style.footerBtn" @click="handleImport") {{$t('user_api__btn_import')}}

View File

@ -36,6 +36,7 @@ export const currentStting = ref({
theme: '',
isLockScreen: true,
isDelayScroll: true,
isHoverHide: false,
style: {
font: '',
fontSize: 125,
@ -87,6 +88,7 @@ export const currentStting = ref({
port: '23332',
},
windowSizeId: 1,
startInFullscreen: false,
langId: 'cns',
theme: {
id: 0,