Compare commits

...

409 Commits

Author SHA1 Message Date
lyswhut
8d6b207833 发布 v2.12.1 2026-02-16 21:11:25 +08:00
lyswhut
3371a42822 更新依赖 2026-02-16 21:03:26 +08:00
lyswhut
fe712318b8 更新依赖&版本号(2.12.1-beta.3) 2026-01-31 12:44:16 +08:00
ikun0014
593d8327b2 fix: 网易云部分歌曲无法被搜索 (#2666)
* fix: 网易云部分歌曲无法被搜索

将搜索请求由原有 eapiRequest 改为通过 httpFetch 调用 /eapi/search/song/list/page 并使用 eapi 加密表单;调整导入(使用 eapi)、移除部分注释和冗余代码。更新结果解析以兼容新响应结构(使用 item.baseInfo.simpleSongData、result.data.resources 和 result.data.totalCount 等字段),并保留原有的分页与重试逻辑。

* update

* update

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2026-01-31 12:36:28 +08:00
lyswhut
d5c54b770f 更新依赖&版本号(2.12.1-beta.2) 2026-01-01 11:58:30 +08:00
lyswhut
32a9a21be1 优化托盘图标行为:在非 Windows 系统中,点击托盘图标时不再显示主窗口 2025-12-31 09:53:47 +08:00
lyswhut
e0735f8928 更新依赖&版本号(2.12.1-beta.1) 2025-12-21 13:46:31 +08:00
lyswhut
c6dc962134 修复某些情况下搜索框的搜索按钮布局错位的问题(#2622) 2025-12-21 13:45:40 +08:00
lyswhut
e068103e4d 修复构建 2025-12-12 12:52:31 +08:00
lyswhut
6eb5ade9ea 更新依赖&版本号(2.12.1-beta.0) 2025-12-12 12:37:16 +08:00
lyswhut
c40168cacd 更新构建流程,集成 undici 库并优化请求处理 2025-12-12 12:28:04 +08:00
lyswhut
a546b75986 修复音量条在调整音量时实际音量与显示的数值不一致的问题(#2606) 2025-12-07 14:07:12 +08:00
lyswhut
8686ecfa54 更新二进制库 2025-11-29 16:31:23 +08:00
lyswhut
270c48a1dd 发布 v2.12.0 2025-11-29 16:27:28 +08:00
lyswhut
fdbec2cbac 更新说明 2025-11-29 16:26:14 +08:00
lyswhut
46049fd15c 更新版本号(2.12.0-beta.15) 2025-11-28 12:06:21 +08:00
lyswhut
a81fe6ff7f 修复MG最新评论翻页 2025-11-28 12:06:17 +08:00
lyswhut
fd4e8ecb01 更新版本号(2.12.0-beta.14) 2025-11-26 13:41:57 +08:00
lyswhut
8142e5e79e 修复MG源评论加载失败 2025-11-26 13:41:22 +08:00
lyswhut
0c71772953 修复MG源歌单加载失败 2025-11-26 13:41:22 +08:00
lyswhut
d54bc21441 更新版本号(2.12.0-beta.13) 2025-11-23 10:21:40 +08:00
lyswhut
be4bca2c15 更新字体设置,支持最多设置两种字体,并调整选择组件宽度 2025-11-23 10:20:27 +08:00
lyswhut
2bd54673de 更新依赖&版本号(2.12.0-beta.12) 2025-11-21 09:09:01 +08:00
lyswhut
4a3359f773 更新依赖 2025-11-14 18:52:51 +08:00
lyswhut
c35095f90e 修复获取歌曲图片的函数,改为直接返回图片链接 2025-11-14 18:52:51 +08:00
lyswhut
32abccd8c2 更新版本号(2.12.0-beta.11) 2025-11-02 21:21:15 +08:00
Folltoshe
5d68ca3e18 修复TX源搜索失败 (#2575)
* 修复TX源搜索失败

* 清理音乐搜索模块中的多余空行

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2025-11-02 21:19:26 +08:00
lyswhut
8efa5335b0 更新版本号(2.12.0-beta.10) 2025-10-01 13:30:12 +08:00
lyswhut
4710bb16d8 下载列表菜单新增歌曲添加弹窗(#2537) 2025-10-01 13:29:35 +08:00
lyswhut
db4f3e4e1b 更新版本号(2.12.0-beta.9) 2025-09-29 12:48:33 +08:00
lyswhut
e18272a6d7 更新依赖 2025-09-29 12:43:24 +08:00
lyswhut
85aed9ce9d 修复隐藏状态下的透明度问题,增加鼠标悬停时恢复不透明度的效果 2025-09-29 12:43:12 +08:00
lyswhut
d03f93166f 新增完整歌词相关字段和API支持 2025-09-29 12:37:22 +08:00
lyswhut
a3b1a433c6 更新版本号(2.12.0-beta.8) 2025-09-19 23:54:44 +08:00
lyswhut
608f63bc9c 临时修复桌面歌词交互时背景异常的问题 2025-09-19 23:53:45 +08:00
lyswhut
a4c118648a mac下默认不使用透明窗口 2025-09-19 23:51:00 +08:00
lyswhut
42866b8e4f 修复构建 2025-09-07 12:14:31 +08:00
lyswhut
0b7455b74f 更新依赖&版本号(2.12.0-beta.7) 2025-09-07 11:53:50 +08:00
lyswhut
eab7bc3f5f 优化歌词解析 2025-08-11 13:49:26 +08:00
lyswhut
af2623d16c 更新版本号(2.12.0-beta.6) 2025-08-10 13:46:52 +08:00
lyswhut
e683034e77 新增 Any Listen 歌词标签数据嵌入、下载、读取播放(#2485) 2025-08-10 13:42:54 +08:00
lyswhut
ffa979ddc8 更新依赖版本,优化组件逻辑 2025-08-09 18:07:16 +08:00
3gf8jv4dv
8a8c8a5699 Revise text for lyric position switching feature (#2463)
- Fix incorrect i18n key
- Revise English translation
- Revise changelog descriptions to be more accurate
2025-08-06 12:24:03 +08:00
lyswhut
2aea956fd0 更新依赖&版本号(2.12.0-beta.5) 2025-08-06 12:22:20 +08:00
lyswhut
1803517943 优化 hidden 启动参数的行为 2025-08-06 12:21:47 +08:00
lyswhut
49c8081f68 尝试修复进度为0时仍然显示下载完成的问题(#2471) 2025-08-05 12:16:24 +08:00
lyswhut
3d64ca37e7 更新版本号(2.12.0-beta.4) 2025-07-19 14:29:32 +08:00
lyswhut
ccc88745a6 降级 electron 版本至 35.3.0 2025-07-19 14:28:47 +08:00
lyswhut
0bf6dbd402 更新依赖&版本号(2.12.0-beta.3) 2025-07-19 02:16:52 +08:00
lyswhut
b9abb4ce47 新增启动参数 -hidden,在启动时将软件最小化到系统托盘(#2459) 2025-07-18 22:24:15 +08:00
lyswhut
ad0c0f37c9 新增调换翻译歌词与罗马音歌词位置设置,优化歌词显示逻辑(#2451) 2025-07-09 08:58:34 +08:00
Folltoshe
4c6257c145 更新代理配置规则,现在不启用代理时,图片、音频加载将不再走系统代理 (#2382)
* 完善代理设置

* 更新

* update

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2025-05-10 10:43:58 +08:00
lyswhut
6c5231dbff 恢复更新日志 2025-05-10 10:07:45 +08:00
lyswhut
0454bb9b3c 更新依赖&版本号(2.12.0-beta.2) 2025-05-10 01:30:48 +08:00
lyswhut
021f89da29 新增播放器声音大小、静音、播放进度控制(#2386) 2025-05-10 00:58:19 +08:00
lyswhut
2ccca633ed 优化滚动事件监听,启用 passive 选项以提升性能 2025-05-09 10:02:37 +08:00
lyswhut
fe15e025aa 更新数据库备份逻辑 2025-05-09 10:02:37 +08:00
3gf8jv4dv
9e0c416db5 Revise chinese terminology for window and font size (#2384)
addresses https://github.com/lyswhut/lx-music-mobile/issues/769
2025-05-07 21:53:24 +08:00
3gf8jv4dv
fa9b84a9c9 Revise wording of main window style options - 2 (#2379)
- Not all operating systems use rounded corners. Like Windows 10.
- Further optimize wording.
- Forgot to add line breaks to CHANGELOG :(
2025-05-06 09:15:31 +08:00
lyswhut
b3b1bd7bc8 更新版本号(2.12.0-beta.1) 2025-05-03 11:23:53 +08:00
lyswhut
91e806a619 为数据库启用 WAL 模式 2025-05-03 11:22:35 +08:00
3gf8jv4dv
6df6eefd3d Revise wording of main window style options (#2372)
- Correct typos
- Edit i18n key name
- Avoid displaying long descriptions directly
2025-05-03 11:06:28 +08:00
lyswhut
ae35d05649 修正透明窗口设置逻辑 2025-05-02 13:02:01 +08:00
lyswhut
72939db2df 更新版本号(2.12.0-beta.0) 2025-05-02 12:36:08 +08:00
lyswhut
b6f5770dc2 新增「设置 → 其他设置 → 主窗口使用软件内置的圆角及阴影」设置 2025-05-02 12:28:16 +08:00
lyswhut
c1e7faa7bf 发布 2.11.0 2025-05-01 16:55:22 +08:00
lyswhut
37a18eae9c 更新依赖 2025-05-01 16:55:14 +08:00
lyswhut
010bc5e328 修复 kg 歌单打开失败的问题 2025-04-23 09:13:45 +08:00
lyswhut
a166051781 更新版本号(2.11.0-beta.6) 2025-04-19 19:55:36 +08:00
3gf8jv4dv
c2341d6697 Disable rounded corners for lyric window (#2356)
* Disable rounded corners for lyric window

Ensure that the lyric window looks the same
on Windows 11 as it does on other systems.

Ref: https://github.com/electron/electron/issues/46468

* update

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2025-04-19 19:54:43 +08:00
lyswhut
03bba4259e 更新版本号(2.11.0-beta.5) 2025-04-19 15:35:56 +08:00
lyswhut
80844580df 更新 Electron 到 35(#2354) 2025-04-19 15:34:29 +08:00
lyswhut
a8790b9835 更新依赖 2025-04-09 09:39:34 +08:00
lyswhut
36262ff166 更新 歌曲换源 机制 2025-04-09 09:24:45 +08:00
lyswhut
98d67be9d9 歌词选择模式下的歌词调整为跟随歌词字体大小显示 2025-03-29 13:11:07 +08:00
lyswhut
131f077c3f 更新空音频输出设备提示信息 2025-03-21 09:31:47 +08:00
lyswhut
2c428ef099 减少桌面歌词窗口自动置顶刷新间隔,修复窗口被遮挡时歌词会被暂停的问题(#2320) 2025-03-21 09:15:39 +08:00
lyswhut
08887cd6ed 更新 Node.js 版本至 22 2025-03-20 13:49:37 +08:00
lyswhut
f786a29b70 更新依赖&版本号(2.11.0-beta.3) 2025-03-20 13:08:01 +08:00
lyswhut
d3f1d7f378 更新依赖&版本号(2.11.0-beta.2) 2025-03-20 12:54:54 +08:00
lyswhut
c48a31fd63 修复 kw 歌单详情打开失败和 kg 热门评论获取问题 2025-03-20 09:22:21 +08:00
lyswhut
1217f06324 修复潜在播放暂停的问题 2025-03-07 12:20:41 +08:00
lyswhut
4cc3197be8 修复构建 2025-03-04 12:04:20 +08:00
lyswhut
eb940d5968 更新依赖&版本号(2.11.0-beta.1) 2025-03-04 11:59:44 +08:00
lyswhut
6381389cac 减少歌词延迟隐藏时间 2025-03-04 11:57:08 +08:00
3gf8jv4dv
ab939e859a Improve description of seek backward/forward shortcuts (english only) (#2297)
Match the style of other shortcuts' descriptions
2025-03-04 11:56:51 +08:00
3gf8jv4dv
9bdc71c487 Fix description of transparent lyric window in desktop lyrics settings (#2296)
- Fix incorrect i18n key
- Fix the wrong description
2025-03-04 11:55:40 +08:00
lyswhut
56a4abfaff 更新版本号(2.11.0-beta.1) 2025-03-03 22:32:57 +08:00
lyswhut
769bc7f109 新增「设置 → 桌面歌词设置 → 暂停时降低歌词透明度」设置,默认启用(#2294) 2025-03-03 22:14:21 +08:00
lyswhut
a587847e41 更新版本号(2.11.0-beta.0) 2025-03-01 20:04:08 +08:00
lyswhut
bda4650de8 新增 快进/快退5秒 自定义快捷键设置(#2289) 2025-03-01 20:03:25 +08:00
lyswhut
4f0af93b72 允许拖动桌面歌词控制栏空白处移动歌词窗口(#2280) 2025-02-28 18:40:47 +08:00
lyswhut
4a656268b8 update electron to 34.3.0 2025-02-28 13:01:10 +08:00
lyswhut
2ad9d5990f 更新依赖&版本号(2.10.1-beta.4) 2025-02-22 15:37:03 +08:00
3gf8jv4dv
32585bc453 Revise License (#2273)
* Revise License

* update rtf file

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2025-02-22 15:35:48 +08:00
3gf8jv4dv
66307d4545 Refine English translation (#2259) 2025-02-22 15:33:16 +08:00
3gf8jv4dv
8b9ae2734f Update publish/changeLog.md [skip ci] (#2270) 2025-02-20 00:29:22 +08:00
3gf8jv4dv
96f9e12d93 Optimize statusbar lyrics option description (#2269)
Clarify the meaning of enabling the system tray.

ref: https://support.apple.com/guide/mac-help/mchlp1446

related: comments on https://github.com/lyswhut/lx-music-desktop/pull/1940
2025-02-17 22:58:34 +08:00
lyswhut
43c4e13c11 修复本地 FLAC 文件内嵌歌词无法读取的问题 2025-02-12 22:00:03 +08:00
3gf8jv4dv
27f6168a29 Revise description about Kugou on "Open Playlist" dialog (#2266)
Related: https://github.com/lyswhut/lx-music-desktop/issues/2246
2025-02-12 20:27:24 +08:00
lyswhut
70dbbc93f4 更新依赖&版本号(2.10.1-beta.3) 2025-02-10 18:17:45 +08:00
lyswhut
92c9808bfb 修复 Windows 下桌面歌词最小高度与宽度设置问题,允许更小的桌面歌词窗口宽度(#2244) 2025-02-10 18:17:45 +08:00
3gf8jv4dv
1f8df84b87 Fix music api management dialog layout (#2247) 2025-02-08 09:57:46 +08:00
3gf8jv4dv
271c1bcd51 Change window title to LX Music (#2248) 2025-02-08 09:57:22 +08:00
lyswhut
f830b1199a 更新依赖&版本号(2.10.1-beta.2) 2025-02-07 12:55:24 +08:00
lyswhut
b80d48aa91 修复windows下界面缩放后移动桌面歌词会改变歌词窗口大小的问题(#2244) 2025-02-07 12:54:41 +08:00
lyswhut
691d835d16 更新依赖&版本号(2.10.1-beta.1) 2025-02-04 23:26:00 +08:00
lyswhut
3ac6d0ff5f 修复 tx 歌单搜索名字、描述出现乱码的问题(#2250) 2025-02-04 23:20:51 +08:00
lyswhut
68b32a20a4 更新版本号(2.10.1-beta.0) 2025-01-28 19:50:56 +08:00
lyswhut
88e4df4388 修复 Windows 下桌面歌词最小高度设置问题(#2244) 2025-01-28 19:49:33 +08:00
lyswhut
3e13980870 发布 v2.10.0 2025-01-27 00:59:37 +08:00
lyswhut
dd23af7051 更新依赖 2025-01-27 00:57:32 +08:00
lyswhut
7d5fc6de12 更新依赖&版本号(2.10.0-beta.12) 2025-01-25 20:11:32 +08:00
lyswhut
28233fbe58 更新依赖 2025-01-25 20:11:32 +08:00
3gf8jv4dv
32a35c6706 i18n cleanup 3 (#2229) 2025-01-18 22:15:39 +08:00
3gf8jv4dv
228ddace91 Update publish/changeLog.md (#2230) 2025-01-14 14:22:24 +08:00
lyswhut
f9ffdbbce7 更新依赖&版本号(2.10.0-beta.11) 2024-12-28 13:17:38 +08:00
3gf8jv4dv
9b233683a2 i18n cleanup 2 (#2215) 2024-12-28 13:14:05 +08:00
3gf8jv4dv
ff78fb6b30 i18n cleanup (#2211)
* i18n cleanup

- A final trimming of the language. (I hope it's the last.)
- For en-US and zh-TW, some of the terms have been revised again.
- For zh-CN, some of the terms have been revised to ensure that they do
not change the habits of users.

* update

* update

* update

* Apply suggestions from lyswhut

* Apply suggestions from lyswhut

* update

* update

* update

* Apply suggestions from lyswhut
2024-12-26 23:35:42 +08:00
3gf8jv4dv
8efda6478c Update publish/changeLog.md (#2212)
* Update publish/changeLog.md

* Apply suggestions from lyswhut

* update

* update

* update

* update
2024-12-26 23:31:57 +08:00
3gf8jv4dv
851c7871e3 Disable randomize popup animation option when animation is not turned on (#2210)
This option has no real purpose if "Show switch animation" is not turned
on. So I mimicked the option about lyrics in the download settings page.
2024-12-25 23:35:14 +08:00
3gf8jv4dv
a78c78f3e5 Revise app language text (3) (#2209) 2024-12-25 00:17:46 +08:00
3gf8jv4dv
05e8442ff1 Fix change music source dialog layout (#2204)
* Fix change music source dialog layout

* Apply suggestions from lyswhut

Co-authored-by: lyswhut <lyswhut@qq.com>

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2024-12-24 13:53:05 +08:00
3gf8jv4dv
977b842df3 Add missing quotes to language files (#2206) 2024-12-24 09:33:37 +08:00
3gf8jv4dv
5bde54aac1 Fix edit theme dialog title logic (#2205)
* Fix edit theme dialog title logic

As you can see from the code and the actual behavior, both share the
same code whether adding or editing a theme. So why does the title show
up as "Edit Theme" no matter what? It does seem strange in a way.

This feature appears to have been added in v2.0.0, but no one seems to
have cared about the issue during the two years.

Nonetheless, when I realized this recently, I decided to fix it.
Hopefully this will make things clearer for those users who may not be
able to tell the difference.

Now, when the "Add theme" button is clicked, the dialog title will
show "Add theme". If you right-click on a user-created theme, the dialog
title will show "Edit theme".

* Apply suggestions from lyswhut

Simpler code

Co-authored-by: lyswhut <lyswhut@qq.com>

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2024-12-23 23:53:10 +08:00
3gf8jv4dv
0f27e3ea42 Fix duplicate songs dialog layout (#2203) 2024-12-23 23:41:31 +08:00
3gf8jv4dv
d170725f0c Add scrollbar to "open shared playlist" dialog box (#2201) 2024-12-22 23:39:05 +08:00
3gf8jv4dv
a26fcf0cd8 Fix search page UI (#2200) 2024-12-22 23:38:33 +08:00
3gf8jv4dv
18a4455511 Fix edit theme dialog layout 3 (#2199) 2024-12-22 23:37:59 +08:00
3gf8jv4dv
2ab3286d0e Add scrollbar to the Sort Songs dialog box (#2197) 2024-12-22 23:32:59 +08:00
3gf8jv4dv
d4a411ab4a Fix list update management dialog UI (#2198) 2024-12-22 01:19:45 +08:00
lyswhut
c7facb38b1 更新依赖&版本号(2.10.0-beta.10) 2024-12-22 00:09:31 +08:00
3gf8jv4dv
b7434e9eaf Revise app language text (2) (#2195) 2024-12-21 23:57:50 +08:00
3gf8jv4dv
4a174c0084 Fix edit theme dialog layout again (#2192)
* Fix edit theme dialog layout again

Ensures that the layout looks better and doesn't get out of place in the
smallest windows and largest fonts, as well as in both English and
Chinese environments.

* update

* update

* fix

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2024-12-21 23:52:15 +08:00
3gf8jv4dv
db8ad1e8eb Optimize theme item name display (#2189)
Co-authored-by: lyswhut <lyswhut@qq.com>
2024-12-19 00:27:06 +08:00
3gf8jv4dv
1feaac003a Revise app language text (#2187)
* Revise app language text

Some additional revisions.

* Apply suggestion from lyswhut

Co-authored-by: lyswhut <lyswhut@qq.com>

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2024-12-18 23:54:02 +08:00
3gf8jv4dv
c42b2fb34c Fix edit theme dialog layout (#2188)
Co-authored-by: lyswhut <lyswhut@qq.com>
2024-12-18 23:37:59 +08:00
14K
5d48fa93ba fix: 修正拼写错误 inert*insert* (#2180) 2024-12-17 19:49:19 +08:00
3gf8jv4dv
527ac04c4e Revise zh-TW language (#2174)
* Revise zh-TW language

Revise according to Taiwanese language habits

Special thanks: Fanhuaji <https://zhconvert.org/>

* update

* Apply suggestion from lyswhut
2024-12-17 19:44:48 +08:00
14K
9644163dc5 fix: 修复了当listPosition未初始化时错误的删除操作 (#2178)
* feat: 新增了 当`列表歌曲位置`未初始化时删除列表时的判断

* fix

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2024-12-17 17:32:49 +08:00
3gf8jv4dv
d0090abd01 Optimize sort songs dialog layout (#2176)
Slightly increased width to ensure no layout issues in various languages
2024-12-17 17:16:13 +08:00
3gf8jv4dv
918d2ad156 Fix English (#2173)
- Fix some `playlist` terms
2024-12-16 23:28:41 +08:00
3gf8jv4dv
b028fb955f Optimize options with too long descriptions in settings (#2172) 2024-12-16 09:52:55 +08:00
3gf8jv4dv
ba33d5a39b Fix i18n keys (#2170)
`setting__setting__desktop_lyric_font_weight` to
`setting__desktop_lyric_font_weight`
2024-12-15 14:45:38 +08:00
3gf8jv4dv
fd0b9c55fc Revise zh-CN language (2) (#2169)
* Revise zh-CN language (2)

* Apply suggestion from lyswhut
2024-12-15 14:36:40 +08:00
3gf8jv4dv
b28fb5c9b4 Overhaul English (#2166)
* Overhaul English

Let's go!!!

Overhaul with reference to Simplified Chinese text
and English user habits.

* Clarify `Music API` and `Song Source`

* fix
2024-12-15 14:08:11 +08:00
lyswhut
77ecd4be9b 更新依赖&版本号(2.10.0-beta.9) 2024-12-14 12:48:20 +08:00
lyswhut
6f74003e14 新增下载的歌曲按列表名分组的功能(#2145) 2024-12-14 12:48:19 +08:00
lyswhut
63b2c2fb2f 修复本地歌曲同名歌词文件调整偏移时间后,下次再播放时调整的设置未被应用的问题(#2139) 2024-12-14 12:47:59 +08:00
3gf8jv4dv
08da7a37e6 Revise zh-CN language (#2159)
* Revise zh-CN language

- Optimize text typesetting.
- Fix some typos.
- Unify some terms.

* update

* update

* update
2024-12-10 23:30:29 +08:00
3gf8jv4dv
6b87a8d91b Fix zh-TW language name (#2155)
Write it in the native language.
2024-12-08 13:48:14 +08:00
3gf8jv4dv
0f1364a8d7 Revise ISSUE_TEMPLATE [skip ci] (#2153)
* Revise ISSUE_TEMPLATE [skip ci]

- Optimize typesetting and fix links.
- Rename the template file.
  - I don't know why git shows it as deleted & new files.

* update [skip ci]

* update [skip ci]

* update [skip ci]
2024-12-08 13:47:16 +08:00
3gf8jv4dv
9dbf70780d Revise English license (#2152)
* Revise English license

Fix some terms and mistakes.

* update
2024-12-08 13:40:55 +08:00
lyswhut
58e5592c2b 修复构建 2024-12-07 16:27:12 +08:00
lyswhut
02cc842cc8 更新依赖&版本号(2.10.0-beta.8) 2024-12-07 15:59:33 +08:00
lyswhut
e962e866b3 修复 Windows 任务管理器中的进程名显示为描述的问题(#2147) 2024-12-07 15:47:48 +08:00
lyswhut
af3b30096d 移除多余逻辑 2024-12-07 15:47:48 +08:00
3gf8jv4dv
aa7ab86b62 Fix some i18n keys (#2148) 2024-12-07 15:47:00 +08:00
3gf8jv4dv
7f864a9e35 Revise repo docs [skip ci] (#2146)
* Revise repo docs [skip ci]

Fix typos, optimize typesetting and descriptions.

* update

* update

* update

* update

* update

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2024-12-07 15:03:43 +08:00
lyswhut
609d345443 更新依赖&版本号(2.10.0-beta.7) 2024-12-04 09:07:04 +08:00
lyswhut
426815fa50 优化换源歌曲查找 2024-12-03 17:54:16 +08:00
lyswhut
5d1b1e48d7 Windows 下改用原生窗口调整方式(#2137) 2024-12-02 00:07:00 +08:00
lyswhut
28d0412adc 更新依赖&版本号(2.10.0-beta.6) 2024-11-30 18:50:11 +08:00
lyswhut
9d070b3671 修复kg id重复导致的问题 2024-11-30 11:54:26 +08:00
lyswhut
cd423752d1 优化歌曲换源机制,提升换源正确率 2024-11-29 12:39:42 +08:00
lyswhut
1f8a0eaf67 更新依赖与更新日志 2024-11-28 08:52:39 +08:00
lyswhut
42d9fcd8b0 更新依赖&版本号(2.10.0-beta.5) 2024-11-26 18:04:06 +08:00
lyswhut
4e1510c840 更新软件默认使用的字体 2024-11-26 18:03:52 +08:00
lyswhut
7ebfd43f65 更新关于说明 2024-11-24 00:06:05 +08:00
lyswhut
41d6157116 修复歌曲检查事件清理 2024-11-23 17:41:45 +08:00
lyswhut
f9f8297fd6 优化歌曲URL检查机制 2024-11-23 17:15:05 +08:00
lyswhut
dfbe3f665a 修复构建 2024-11-23 14:49:34 +08:00
lyswhut
b36df472e5 更新更新日志 2024-11-23 14:30:27 +08:00
lyswhut
0b147f708d 更新依赖&版本号(2.10.0-beta.4) 2024-11-23 14:29:31 +08:00
lyswhut
4e3bdbcc74 添加歌曲预加载功能 2024-11-23 14:21:59 +08:00
lyswhut
0345419ef3 客户端模式的同步服务连接允许重定向 5 次(#2109) 2024-11-10 11:05:59 +08:00
lyswhut
9237dbbc18 播放本地歌曲时,将优先尝试读取本地同名字的 jpgpng 图片作为播放封面显示 2024-10-28 23:04:48 +08:00
lyswhut
68ac1b7641 更新依赖&版本号(2.10.0-beta.3) 2024-10-25 20:13:30 +08:00
lyswhut
2cfe4d29ba 更新 electron 到 v32.2.2 2024-10-25 20:12:29 +08:00
lyswhut
b436497555 添加debug日志 2024-10-25 19:56:59 +08:00
lyswhut
7ac638c52c 修复 m4a 文件歌曲内嵌歌词无法读取的问题(#2090) 2024-10-25 19:56:41 +08:00
lyswhut
f21d2bc7c7 更新readme 2024-10-19 00:25:38 +08:00
lyswhut
550917bd16 更新依赖&版本号(2.10.0-beta.2) 2024-10-17 12:38:05 +08:00
lyswhut
c1c0311586 不再缓存换源歌曲信息 2024-10-17 12:36:59 +08:00
lyswhut
489bbb6902 Open API 新增播放器播放/暂停、切歌、收藏当前播放歌曲调用,详情看开放API文档 (原始 PR #2077) 2024-10-12 09:59:04 +08:00
lyswhut
86af25bde3 Scheme URL 的播放歌曲允许更长的专辑名称 2024-09-27 21:25:46 +08:00
lyswhut
a599e0716f 支持本地同名 krc 格式歌词文件的读取(#2053) 2024-09-25 18:04:42 +08:00
lyswhut
30fc818771 更新依赖&版本号(2.10.0-beta.1) 2024-09-24 18:49:46 +08:00
lyswhut
df08f677bd 优化播放详情页UI,歌曲名字、歌手等文字过长时被截断的问题(#2049) 2024-09-24 18:47:40 +08:00
lyswhut
7247bcad1b 修正搜索歌曲提示框文案(#2050) 2024-09-24 18:40:32 +08:00
lyswhut
c0bcac25ea fix build 2024-09-21 14:10:33 +08:00
lyswhut
ae06019bdb 更新依赖&版本号(2.10.0-beta.0) 2024-09-21 14:01:59 +08:00
lyswhut
432861780a 修复获取自定义环境音效预设列表逻辑问题 2024-09-21 12:45:04 +08:00
lyswhut
111ee88833 更新依赖 2024-09-06 09:44:30 +08:00
lyswhut
487ee7015e 修复歌单详情页内歌单名字过长时的UI显示问题(#2028) 2024-09-04 13:04:41 +08:00
lyswhut
061cc16c27 新增托盘图标颜色 跟随系统亮暗模式 设置,可以在 设置-其他 启用 (#2016) 2024-08-30 20:21:18 +08:00
lyswhut
af8d9dff2d 更新检查连接链接顺序 2024-08-24 13:39:27 +08:00
lyswhut
cb16bd0324 发布 v2.9.0 2024-08-24 12:57:27 +08:00
lyswhut
acf8171617 npm audit fix 2024-08-24 12:56:59 +08:00
lyswhut
da7429f03e 将下载歌曲的歌手信息中的分隔符从 替换为 ; 以确保音乐元数据在写入时的兼容性和一致性(#1989 @qnnp-me) 2024-08-24 12:51:09 +08:00
lyswhut
4a121db39d 更新 better-sqlite3 11.2.1 二进制文件 2024-08-24 12:28:50 +08:00
lyswhut
0e8e287fe2 update actions cache dir 2024-08-23 21:22:31 +08:00
lyswhut
cf942121dd 更新依赖&版本号(2.9.0-beta.12) 2024-08-23 20:07:09 +08:00
lyswhut
77c2e962ec 优化本地歌曲内嵌封面过大时的加载方式 2024-08-23 20:06:17 +08:00
lyswhut
05f5cc4fe8 更新依赖&版本号(2.9.0-beta.11) 2024-08-18 13:14:01 +08:00
lyswhut
684f09feaf 修复列表数据同步问题 2024-08-18 10:10:38 +08:00
lyswhut
38bea3ced1 优化换源机制 2024-08-17 20:30:07 +08:00
lyswhut
6a6f9a53bd 更新依赖&版本号(2.9.0-beta.10) 2024-08-17 13:45:33 +08:00
lyswhut
584c12e60f 新增 我的列表-换源播放 功能 2024-08-17 13:44:11 +08:00
lyswhut
bb5b667613 更新版本号(2.9.0-beta.9) 2024-08-11 11:24:25 +08:00
lyswhut
0628d3533f 新增设置-下载设置-同时下载任务数设置(#1498) 2024-08-10 14:31:35 +08:00
lyswhut
e1bd4c9a0a 更新依赖&版本号(2.9.0-beta.8) 2024-08-10 10:27:05 +08:00
lyswhut
85c9ec38db 新增鼠标在音量按钮使用滚轮时可以调整音量大小的功能(#2000) 2024-08-10 10:18:27 +08:00
lyswhut
6ff7cfde11 优化某些UI的字体可能出现被截断的问题 2024-08-04 11:33:44 +08:00
lyswhut
c52034f992 更新版本号(2.9.0-beta.7) 2024-08-04 10:49:41 +08:00
lyswhut
b982dd90e4 修复程序目录路径存在 #% 时,自定义源、托盘等图标异常的问题(#1997) 2024-08-04 10:48:31 +08:00
lyswhut
80e36688e8 更新依赖 2024-08-03 12:26:53 +08:00
lyswhut
c6c1071d2d 优化添加不喜欢歌曲时的提示文案 2024-08-02 12:32:20 +08:00
lyswhut
481817c607 更新依赖&版本号(2.9.0-beta.6) 2024-07-27 17:23:02 +08:00
lyswhut
2e5739f042 Revert "优化代码"
This reverts commit 72d44ec783.
2024-07-27 17:21:09 +08:00
lyswhut
0bb9f5ece5 更新依赖&版本号(2.9.0-beta.5) 2024-07-26 18:55:59 +08:00
lyswhut
d454f51993 新增设置-播放详情页设置-延迟歌词滚动设置(#1985) 2024-07-26 18:52:27 +08:00
lyswhut
7f3ab31ad5 尝试修复某些情况下播放音频时,处于播放状态但是进度条不走的问题 2024-07-26 18:50:45 +08:00
lyswhut
98fe433819 更新依赖&版本号(2.9.0-beta.4) 2024-07-24 19:32:39 +08:00
lyswhut
4b01326420 更新依赖 2024-07-20 13:11:35 +08:00
lyswhut
3a4acd2693 更新依赖&版本号(2.9.0-beta.3) 2024-07-16 19:06:16 +08:00
lyswhut
b8e9937083 托盘图标添加当前播放歌曲名字显示 2024-07-15 19:42:51 +08:00
lyswhut
72d44ec783 优化代码 2024-07-11 18:52:48 +08:00
lyswhut
680b60cb07 更新依赖&版本号(2.9.0-beta.2) 2024-07-04 19:49:39 +08:00
lyswhut
45527b320d 修复开发模式下 vue devtools 扩展没有加载的问题 2024-07-04 19:41:49 +08:00
lyswhut
0acc9a553e 优化侧栏图标显示,修复图标可能被裁切的问题(#1960) 2024-06-26 19:21:55 +08:00
lyswhut
19f81cf379 优化代码 2024-06-19 19:25:21 +08:00
lyswhut
96ddbdf0f0 更新依赖&版本号(2.9.0-beta.1) 2024-06-19 12:40:04 +08:00
lyswhut
9eb87911a6 修复界面不可见时状态栏歌词没有更新的问题 2024-06-19 12:39:44 +08:00
lyswhut
b0881726fa 更新依赖&版本号(2.9.0-beta.0) 2024-06-18 19:33:46 +08:00
lyswhut
308248d8ec 新增 是否将歌词显示在状态栏 设置,默认关闭,该功能只在 MacOS 下可用(#1940) 2024-06-18 19:30:54 +08:00
lyswhut
de5ea4f0aa 修复切换音源时可能出现切换死循环的问题 2024-06-17 12:37:26 +08:00
lyswhut
305ce6ff11 更新依赖&版本号(2.8.1-beta.3) 2024-06-11 19:47:40 +08:00
lyswhut
bd4dbb55e4 现在如果在设置或者启动参数配置了代理服务,那么应用内的图片、音频加载,歌曲下载也将走代理 2024-06-11 19:41:10 +08:00
lyswhut
1fefb2bcaf 更新 electron 到 v29.4.2 2024-06-11 14:09:31 +08:00
3gf8jv4dv
e094e80ecf Some fixes to lang files (#1935)
* `Closure` -> `Close`

* Fix `player__sound_effect_features_tip`
2024-06-10 22:44:00 +08:00
lyswhut
9715db1229 更新版本号(2.8.1-beta.2) 2024-06-04 18:44:46 +08:00
lyswhut
3c0daa2bf1 修复 OpenAPI 的 lyricLineAllText 在切换到无歌词的音乐时内容没有更新的问题(#1925) 2024-06-04 18:42:52 +08:00
lyswhut
435fec2f1c 更新版本号(2.8.1-beta.1) 2024-06-03 18:30:26 +08:00
lyswhut
69ba619f24 修复 MacOS 下点击 dock 右键菜单的退出按钮时,程序没有退出的问题(#1923) 2024-06-03 18:29:54 +08:00
lyswhut
b7bb21f091 更新版本号(2.8.1-beta.0) 2024-06-03 12:50:19 +08:00
lyswhut
77f51a3e9f 修复 MacOS 下点击 dock 右键菜单的退出按钮时,程序没有退出的问题(#1923) 2024-06-03 12:49:26 +08:00
lyswhut
7799e511e8 修复构建 2024-06-01 14:15:35 +08:00
lyswhut
416f4a4dee 发布v2.8.0 2024-06-01 12:01:33 +08:00
lyswhut
2fd5158839 更新依赖 2024-06-01 11:45:27 +08:00
lyswhut
b8287acff3 更新导入歌单的id生成方式 2024-05-25 19:54:46 +08:00
lyswhut
53b470ed09 更新ci npm缓存设置 2024-05-18 18:55:41 +08:00
lyswhut
8a20b773fb fix code 2024-05-17 09:55:02 +08:00
lyswhut
aa576fe7db 修复依赖关系 2024-05-17 09:41:45 +08:00
lyswhut
582321c1ae 更新版本号(2.8.0-beta.5) 2024-05-16 19:23:37 +08:00
lyswhut
541f5041fb 更新依赖 2024-05-16 19:23:00 +08:00
lyswhut
d16babefbe 修复翻页按钮栏切页按钮只显示前几页的问题 2024-05-15 13:21:26 +08:00
lyswhut
4ac6768b7a 修复潜在自定义源切换问题 2024-05-14 19:28:24 +08:00
lyswhut
97d3d3c616 添加媒体输出设备检测提示 2024-05-14 19:27:55 +08:00
lyswhut
2ee7572634 Scheme URL API新增 music/searchPlay 支持(#1886) 2024-05-14 09:58:07 +08:00
lyswhut
8e7fd1ecef 更新版本号(2.8.0-beta.4) 2024-05-03 18:54:47 +08:00
lyswhut
6b4e6533f5 修复文本提示气泡在内容过长时,文本未被换行而被截断的问题 2024-05-03 18:52:39 +08:00
lyswhut
b680a89033 修复歌曲时文件名过长导致歌曲无法下载的问题(#1877) 2024-05-03 18:49:48 +08:00
lyswhut
ee6a6f4b94 默认禁用 使用设备能处理的最大声道数输出音频 设置,修复高级音频API与自定义音频输出设备功能冲突的提示显示 2024-05-02 14:13:16 +08:00
lyswhut
6933baafca 修复事件监听潜在问题 2024-05-02 13:03:02 +08:00
lyswhut
0f3a6782b8 修复与优化繁体中文、英语翻译显示(#1845) 2024-05-02 12:56:41 +08:00
lyswhut
f99cc021f8 更新版本号(2.8.0-beta.3) 2024-05-02 12:41:28 +08:00
lyswhut
e4de4b8c1b 新增 设置-播放设置-使用设备能处理的最大声道数输出音频 设置(#1873) 2024-05-02 12:33:07 +08:00
lyswhut
793b5a679c 修复播放详情页的歌词无法使用触碰拖动的问题(#1865) 2024-05-01 17:58:36 +08:00
筱莱Ceale
1b75cf822d 开放API支持跨域请求 (#1872)
* 增加 Access-Control-Allow-Origin: *

* 完善

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2024-05-01 17:57:47 +08:00
lyswhut
60604020c7 更新版本号(2.8.0-beta.2) 2024-04-28 21:19:58 +08:00
lyswhut
09780d02e3 修复Mac下即使开启了托盘, cmd+w 仍会中断播放的问题(#1844) 2024-04-28 18:59:09 +08:00
lyswhut
4760f79ab5 修正描述,统一在线导入音源大小限制 2024-04-28 18:57:22 +08:00
zclorne
05363a0d22 fix #731 #887,修复桌面歌词与主窗口阴影残留 (#1869)
* fix #731

* fix #887

* Revert "在歌词滚动结束后清理窗口阴影,缓解Mac下桌面歌词出现残留阴影的问题"

This reverts commit 1f6a6bbb39.

* update

* fix

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2024-04-28 18:53:04 +08:00
lyswhut
49797f891c 修复构建 2024-04-27 15:40:14 +08:00
lyswhut
9d38fe6764 更新版本号(2.8.0-beta.1) 2024-04-27 15:07:52 +08:00
lyswhut
f340b18408 允许添加 m4agoa 格式的本地歌曲到列表中(#1864) 2024-04-27 15:06:27 +08:00
lyswhut
358c449b17 修正文案 2024-04-25 18:58:07 +08:00
lyswhut
1f6a6bbb39 在歌词滚动结束后清理窗口阴影,缓解Mac下桌面歌词出现残留阴影的问题 2024-04-24 18:51:53 +08:00
lyswhut
10028cf500 增大在线导入自定义源文件的大小限制问题(#1857) 2024-04-23 19:24:37 +08:00
lyswhut
a977458881 暂时固定ci node版本 2024-04-17 00:44:15 +08:00
lyswhut
dc811d8b84 更新版本号(2.8.0-beta.0) 2024-04-16 18:52:15 +08:00
lyswhut
5bc119bd9d 修复win7下的电源锁清理问题 2024-04-16 18:37:35 +08:00
lyswhut
9016796497 优化托盘图标显示(#1842) 2024-04-16 18:13:52 +08:00
lyswhut
32f47aaa34 开放API 添加歌词翻译字段 2024-04-16 16:18:07 +08:00
lyswhut
c54b38a068 修复存在多级弹窗时的背景显示问题 2024-04-16 11:25:21 +08:00
lyswhut
a9a708d0f6 更新描述 2024-04-15 20:22:21 +08:00
NKXingXh
ab3f118588 允许设置其他优先播放的歌曲音质 (#1839)
* 在线播放更高音质

* 修改对应翻译

* 设置-播放设置-优先播放320k音质选项改为“优先播放的音质”

* 修复拼写

* 优化

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2024-04-15 19:46:28 +08:00
lyswhut
87852acb14 发布v2.7.0 2024-04-14 16:43:14 +08:00
lyswhut
65ab26365d 拒绝在CI环境使用不干净的工作区构建应用 2024-04-13 18:59:42 +08:00
lyswhut
e7630e98d9 修复工作区不干净的问题 2024-04-13 18:46:36 +08:00
lyswhut
9779c1ebee 更新版本号(2.7.0-beta.7) 2024-04-13 18:11:37 +08:00
lyswhut
9cef7e3370 添加当前软件版本所对应的代码提交版本、提交时间的显示 2024-04-13 18:08:45 +08:00
lyswhut
b6bc545e7d 更新默认设置 2024-04-13 12:08:32 +08:00
lyswhut
85326be7bb 更新依赖&版本号(2.7.0-beta.6) 2024-04-11 13:22:28 +08:00
lyswhut
75750ad38d 修复封面链接事件在封面链接相同时仍然触发的问题 2024-04-11 13:07:54 +08:00
lyswhut
0522114c5d 修复切歌时的状态栏进度条显示问题 2024-04-11 12:42:58 +08:00
lyswhut
6300236cdb 更新依赖 2024-04-10 11:42:09 +08:00
lyswhut
1dbb950b3c 修复开发模式启动问题 2024-04-08 19:25:48 +08:00
lyswhut
bfd2ecd26c 更新electron到28.2.10,修复开发模式重启electron进程时偶现旧进程残留的问题 2024-04-05 13:10:45 +08:00
lyswhut
e60c2f5f19 修复注册的事件未被清理的问题 2024-04-02 11:07:50 +08:00
lyswhut
274f71615f 更新依赖&版本号(2.7.0-beta.5) 2024-04-01 22:49:00 +08:00
lyswhut
cd7d5ed665 添加开发API允许局域网访问选项(#1828) 2024-04-01 22:44:34 +08:00
lyswhut
703327ed41 添加SSE订阅字段过滤参数 2024-03-30 12:20:12 +08:00
lyswhut
a7ce24c84b 更新版本号(2.7.0-beta.4) 2024-03-29 18:22:21 +08:00
lyswhut
c3c42e33ee 添加SSE支持 2024-03-29 18:22:08 +08:00
lyswhut
530ace6b81 添加播放速率 2024-03-29 16:48:25 +08:00
lyswhut
5345b001e8 修复http服务关闭时没有断开所有连接的问题 2024-03-29 16:38:07 +08:00
lyswhut
07c9e2f675 更新依赖&版本号(2.7.0-beta.3) 2024-03-29 15:59:04 +08:00
lyswhut
7a08cb5ddd 托盘菜单新增播放、切歌、收藏控制 2024-03-29 15:59:00 +08:00
lyswhut
9c0ae12959 新增HTTP开放API服务 2024-03-29 14:43:32 +08:00
lyswhut
1e03791e82 更新依赖 2024-03-23 21:55:50 +08:00
lyswhut
d92e34949c 更新依赖&版本号(2.7.0-beta.2) 2024-03-17 16:34:39 +08:00
lyswhut
c8423c0b4f 移除未使用的网络代理设置用户名、密码设置 2024-03-14 18:23:54 +08:00
lyswhut
2c1b4acfcc 修复网络代理设置没有对自定义源的网络请求生效的问题(#1814) 2024-03-14 18:23:47 +08:00
lyswhut
b14883c9af 新增在线自定义源导入功能,允许通过http/https链接导入自定义源 2024-03-12 14:33:21 +08:00
lyswhut
53245e9e4f 更新依赖 2024-03-11 21:58:02 +08:00
lyswhut
45fc8c77b6 优化本地歌曲换源匹配机制 2024-03-08 11:54:00 +08:00
lyswhut
9d8fe4be06 托盘菜单添加多语言支持(#1802) 2024-03-03 19:20:20 +08:00
lyswhut
03781ea692 修复kg最新评论无法获取的问题(@helloplhm-qwq) 2024-03-02 15:04:05 +08:00
lyswhut
3c28c4861b 修复构建问题 2024-03-01 15:18:25 +08:00
lyswhut
0c986ee68c 更新依赖&版本号(2.7.0-beta.0) 2024-03-01 15:08:46 +08:00
lyswhut
17d45d7406 主题编辑器添加“深色字体”选项(#1799) 2024-03-01 14:56:12 +08:00
lyswhut
07ed408129 修复更新超时弹窗在非更新阶段意外弹出的问题(#1797) 2024-02-29 12:06:06 +08:00
lyswhut
c001803d71 主题设置默认折叠其他主题以优化进入设置界面时的性能 2024-02-20 22:57:19 +08:00
lyswhut
4ce54ea395 更新运行项目nodejs版本要求 2024-02-19 12:09:51 +08:00
梓澄qwq
2039431735 update (#1791)
- 支持kw排行榜显示大小
- 支持某些kg逐行歌词
- 修复mg歌单搜索
2024-02-19 12:08:21 +08:00
SamLukeYes
15c9207542 修复 Linux 端 hostnamectl 命令不可用时无法与手机端同步的问题 (#1784)
* Don't fail to sync without hostnamectl

* update

---------

Co-authored-by: lyswhut <lyswhut@qq.com>
2024-02-15 00:00:14 +08:00
lyswhut
79d8d79b9d 更新依赖&版本号 2024-02-14 11:13:25 +08:00
lyswhut
806925333f 修复mg歌词在某些情况下获取失败的问题(#1783) 2024-02-14 10:52:03 +08:00
lyswhut
2e196656ba 更新 electron 到 v27.3.1 2024-02-06 11:09:53 +08:00
lyswhut
55204df130 加长自定义源返回的歌词长度 2024-02-06 10:38:24 +08:00
lyswhut
627c50910c 修复某些情况下歌曲加载时间过长时不会自动跳到下一首的问题 2024-02-04 18:05:08 +08:00
lyswhut
959e11b77c 发布v2.6.0 2024-02-01 12:44:58 +08:00
lyswhut
6cc9da338b 更新版本号(2.6.0-beta.10) 2024-01-30 20:17:11 +08:00
lyswhut
1cd93344dd 自定义源新增支持local源的musicUrlpiclyric的获取操作 2024-01-30 19:25:01 +08:00
lyswhut
7893814da4 优化自定义源调用处理逻辑 2024-01-30 17:40:23 +08:00
lyswhut
da2d3237b6 简化自定义源名称显示 2024-01-30 12:33:25 +08:00
lyswhut
431b04a011 添加创建同名列表时的二次确认(#1621) 2024-01-30 12:33:25 +08:00
lyswhut
f13c6f0153 更新action依赖版本 2024-01-30 12:33:25 +08:00
梓澄qwq
160693f608 fix: migu search (#1761) 2024-01-30 12:14:48 +08:00
lyswhut
b127a1e98c 修复封面图片嵌入后没有被删除的问题 2024-01-26 16:14:25 +08:00
lyswhut
b0a9cd70ac 更新 sqlite3 预编译文件 2024-01-26 16:06:40 +08:00
lyswhut
5955831809 更新依赖&版本号(2.6.0-beta.9) 2024-01-26 15:44:22 +08:00
lyswhut
b13f10d624 新增播放歌曲时阻止电脑休眠(#1563) 2024-01-22 12:42:46 +08:00
lyswhut
47635c95e1 添加对下载歌曲时封面图片大小的控制处理(#1609) 2024-01-22 12:35:34 +08:00
lyswhut
c238d49584 修复潜在自定义源切换问题 2024-01-22 12:35:34 +08:00
lyswhut
2ff6455231 更新依赖 2024-01-22 12:35:34 +08:00
lyswhut
110db561a9 修复最小化后再隐藏窗口时,托盘菜单的显示主界面功能异常的问题 2024-01-22 12:35:34 +08:00
lyswhut
61013e9d2d 修复临时列表变更会意外触发同步的问题 2024-01-22 12:35:34 +08:00
lyswhut
2de6d1c5f1 更新版本号(2.6.0-beta.8) 2024-01-07 19:11:46 +08:00
lyswhut
991e453881 降级vue修复某些情况下切换界面时出现空白的问题 2024-01-07 19:11:06 +08:00
lyswhut
edcf501a22 更新依赖 2024-01-06 20:26:30 +08:00
lyswhut
1fc5cf0aa5 不再丢弃酷我某些带翻译歌词 2024-01-06 20:14:56 +08:00
lyswhut
ce072ae9d3 不再丢弃大部分带翻译的酷我歌词 2024-01-01 19:46:31 +08:00
lyswhut
a811b1da1a 更新依赖&版本号(2.6.0-beta.7) 2023-12-30 14:20:31 +08:00
lyswhut
1de087eac7 优化任务下载状态显示 2023-12-30 12:31:18 +08:00
lyswhut
2337a3e8a3 修复本地歌曲歌词获取问题 2023-12-28 14:53:12 +08:00
lyswhut
1637ae4d17 修复构建顺序导致的更新信息被覆盖的问题 2023-12-25 14:28:16 +08:00
lyswhut
f46a08fe89 更新依赖&更新版本号(2.6.0-beta.6) 2023-12-23 18:20:16 +08:00
lyswhut
2c0d2c4497 优化黑色主题颜色计算 2023-12-17 21:50:12 +08:00
lyswhut
f75eeb07be 改进本地音乐在线信息的匹配机制 2023-12-14 20:43:43 +08:00
lyswhut
965a1d03ba 修复下载模块在数据写入速度较慢的情况下出现任务及文件异常的问题 2023-12-13 01:19:17 +08:00
lyswhut
12f947ed2e 注释调试代码 2023-12-12 14:33:09 +08:00
lyswhut
97d684fde1 修复构建 2023-12-12 14:20:39 +08:00
lyswhut
75c1069251 修复构建 2023-12-12 14:14:35 +08:00
lyswhut
050b641d79 更新依赖&版本号(2.6.0-beta.5) 2023-12-12 14:00:22 +08:00
lyswhut
e9539b5c5c 添加对wy源某些歌曲有问题的歌词进行修复 2023-12-12 13:44:12 +08:00
lyswhut
6fc5a47970 优化搜索框交互逻辑 2023-12-12 13:25:25 +08:00
lyswhut
11317154f6 优化GitHub actions工作流 2023-12-11 11:31:57 +08:00
lyswhut
98b4abfa19 为播放详情页歌词选择界面添加全选快捷键支持 2023-12-08 16:34:33 +08:00
lyswhut
f40f019d2e 禁止默认的全选行为 2023-12-08 16:34:28 +08:00
lyswhut
42109ce000 更新依赖&版本号(2.6.0-beta.4) 2023-12-08 11:52:02 +08:00
lyswhut
a38df1280b 修复歌单分类标签列表无法打开的问题 2023-12-08 11:45:29 +08:00
lyswhut
e19002caca 构建阶段压缩代码、外置map 2023-12-08 11:13:22 +08:00
lyswhut
b67d99e5a5 修复 MacOS 平台在 Finder 打开文件或目录时应用卡死的问题(#1684) 2023-12-08 10:47:41 +08:00
lyswhut
15942a7b96 优化窗口控制按钮聚焦效果显示时机 2023-12-05 13:06:48 +08:00
lyswhut
cac2d0e050 优化列表全选机制,修复列表未获得焦点时仍然可以全选的问题 2023-12-05 11:14:44 +08:00
lyswhut
7d504f9231 添加win7_x64架构的安装版安装包构建 2023-12-05 10:43:14 +08:00
lyswhut
c0f78b023f 显示源版本号信息 2023-12-04 13:35:21 +08:00
lyswhut
3803ab045c 更新版本号(2.6.0-beta.3) 2023-12-04 12:36:13 +08:00
lyswhut
bee2790af3 切换到简易的 store 实现 2023-12-04 12:34:12 +08:00
lyswhut
182a3076fc 更新依赖 2023-12-03 15:34:37 +08:00
lyswhut
a9bcb71afe 隐藏源描述 2023-12-03 15:34:32 +08:00
lyswhut
4f5baa1ac8 添加超时 2023-12-03 15:06:04 +08:00
lyswhut
323743e426 更新依赖(v2.6.0-beta.2) 2023-11-25 12:31:45 +08:00
lyswhut
3ba51fd6fe 自定义源列显示源版本号、作者名字,优化自定义源脚本数据存储 2023-11-25 12:19:36 +08:00
lyswhut
ff98e06afa 关闭mac下的圆角 2023-11-25 10:29:06 +08:00
lyswhut
6f22c73d99 fix code 2023-11-22 12:59:30 +08:00
lyswhut
87bd27f814 更新依赖 2023-11-22 12:47:55 +08:00
lyswhut
3e7db5617b 修复亮暗主题自动切换功能无效的问题(#1697) 2023-11-22 12:20:15 +08:00
lyswhut
1712e466d5 更新翻译 2023-11-13 13:59:58 +08:00
梓澄
abf49529d6 fix: kg源在海外无法搜索 (#1686) 2023-11-11 13:59:48 +08:00
lyswhut
3e31a78af1 修复win构建 2023-11-11 13:20:58 +08:00
lyswhut
41fc46c8b5 修复构建 2023-11-11 12:56:07 +08:00
lyswhut
db8cfec7ad 更新依赖 2023-11-10 15:08:06 +08:00
lyswhut
66376daad4 优化移除源时的行为 2023-11-09 10:54:57 +08:00
lyswhut
182f08d221 优化 2023-11-09 00:19:07 +08:00
lyswhut
887cf39321 修复错误处理问题 2023-11-08 15:27:36 +08:00
lyswhut
e2f6ec308c 更新依赖 2023-11-08 13:43:08 +08:00
lyswhut
b1f2a27eac 自定义源变更 2023-11-07 22:18:56 +08:00
lyswhut
0c6cedf879 更新依赖 2023-10-25 14:22:41 +08:00
lyswhut
1ff6a00653 移除所有内置源 2023-10-18 19:46:05 +08:00
lyswhut
016821a8ce 更新依赖 2023-10-17 18:14:49 +08:00
lyswhut
a786ae24e9 Windows、MacOS平台下的字体列表取消使用原生方式获取(#1596) 2023-10-07 11:17:04 +08:00
lyswhut
799b768367 修复备份文件无法导入json格式的问题 2023-10-05 14:45:37 +08:00
lyswhut
24610f9c0a 更新依赖 2023-10-05 14:45:27 +08:00
lyswhut
172307d53b 更新许可协议的排版,使其看起来更加清晰明了 2023-10-05 14:45:15 +08:00
lyswhut
d7816729d0 优化代码逻辑 2023-10-03 15:10:54 +08:00
386 changed files with 17715 additions and 23926 deletions

View File

@@ -21,6 +21,6 @@
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-transform-modules-umd",
"@babel/plugin-transform-runtime",
"@babel/plugin-proposal-class-properties"
"@babel/plugin-transform-class-properties"
]
}

View File

@@ -42,6 +42,7 @@ const typescriptRule = {
'@typescript-eslint/return-await': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/comma-dangle': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
}
const vueRule = {
...typescriptRule,

View File

@@ -1,55 +0,0 @@
name: 🐞报告Bug
description: 报告bug请先查看常见问题及搜索issue列表中有无你要提的问题
title: "[Bug]: "
body:
- type: checkboxes
id: check-answer
attributes:
label: 解决方案检查
description: 请确保你已完成以下所有操作
options:
- 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
- type: textarea
id: expected-behavior
attributes:
label: 预期行为
description: 对期望发生的事情的清晰简明描述
validations:
required: true
- type: textarea
id: actual-behavior
attributes:
label: 实际行为
description: 对实际发生的事情的清晰简明描述
validations:
required: true
- type: input
id: version
attributes:
label: Lx Music 版本
description: 你使用什么版本的LX Music
placeholder: 1.15.0
validations:
required: true
- type: input
id: last-known-working-version
attributes:
label: 最后正常的版本
description: 如果有,请在此处填写最后正常的版本是多少?
placeholder: 1.15.0
- type: input
id: operating-system-version
attributes:
label: 操作系统版本
description: 您使用的是什么操作系统版本?在 Windows 上,单击开始按钮 > 设置 > 系统 > 关于;在 macOS 上,单击 Apple 菜单 > 关于本机;在 Linux 上,使用 lsb_release 或 uname -a
placeholder: "例如 Windows 10 版本 1909、macOS Catalina 10.15.7 或 Ubuntu 20.04"
validations:
required: true
- type: textarea
id: additional-information
attributes:
label: 附加信息
description: 如果你的问题需要进一步解释,或者你所遇到的问题不容易重现,请在此处添加更多信息。(直接把图片、视频拖到编辑框即可添加图片或视频)

59
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View File

@@ -0,0 +1,59 @@
name: 🐞 报告错误
description: 报告一个错误Bug请先查看常见问题及搜索 Issue 列表中有无你要提的问题。
title: "[Bug]: "
body:
- type: checkboxes
id: check-answer
attributes:
label: 解决方案检查
description: 请确保你已完成以下所有操作。
options:
- label: 我已阅读 [常见问题](https://lyswhut.github.io/lx-music-doc/desktop/faq),并没有找到解决方案。
required: true
- label: 我已搜索 [Issue 列表](https://github.com/lyswhut/lx-music-desktop/issues?q=is%3Aissue+),并没有发现类似的问题。
required: true
- type: textarea
id: expected-behavior
attributes:
label: 预期行为
description: 对期望发生的事情的清晰简明描述。
validations:
required: true
- type: textarea
id: actual-behavior
attributes:
label: 实际行为
description: 对实际发生的事情的清晰简明描述。
validations:
required: true
- type: input
id: version
attributes:
label: LX Music 版本
description: 你使用什么版本的 LX Music
placeholder: 例如 2.9.0
validations:
required: true
- type: input
id: last-known-working-version
attributes:
label: 最后正常的版本
description: 如果有,请在此处填写最后正常的版本。
placeholder: 例如 2.8.0
- type: input
id: operating-system-version
attributes:
label: 操作系统版本
description: |
你使用什么版本的操作系统?
在 macOS 上单击「Apple 菜单 > 关于本机」;
在 Linux 上,执行 `lsb_release` 或 `uname -a` 命令;
在 Windows 上,单击「开始按钮 > 设置 > 系统 > 关于」。
placeholder: "例如 Windows 11 版本 24H2、macOS Sequoia 15.1.1 或 Ubuntu 24.10"
validations:
required: true
- type: textarea
id: additional-information
attributes:
label: 附加信息
description: 如果你的问题需要进一步解释,或者你所遇到的问题不容易重现,请在此处添加更多信息。(直接把图片/视频拖到编辑框即可添加图片/视频)

View File

@@ -1,40 +1,38 @@
name: ✨功能请求
description: 为这个项目提出一个想法,请先查看常见问题及搜索issue列表中有无你要提的问题
name: 功能请求
description: 为这个项目提出一个想法,请先查看常见问题及搜索 Issue 列表中有无你要提的问题
title: "[Feature]: "
body:
- type: checkboxes
id: check-answer
attributes:
label: 解决方案检查
description: 请确保你已完成以下所有操作
description: 请确保你已完成以下所有操作
options:
- label: 我已阅读常见问题(<https://lyswhut.github.io/lx-music-doc/desktop/faq>),但没有找到解决方案
- label: 我已阅读 [常见问题](https://lyswhut.github.io/lx-music-doc/desktop/faq),但没有找到解决方案
required: true
- label: 我已搜索issue列表(<https://github.com/lyswhut/lx-music-desktop/issues?utf8=✓&q=>),但没有发现类似的问题
- label: 我已搜索 [Issue 列表](https://github.com/lyswhut/lx-music-desktop/issues?q=is%3Aissue+),但没有发现类似的问题
required: true
- type: textarea
id: problem-description
attributes:
label: 问题描述
description: 请添加清晰简洁的描述,说明你希望通过此功能请求解决的问题
description: 请添加清晰简洁的描述,说明你希望通过此功能请求解决的问题
validations:
required: true
- type: textarea
id: proposed-solution
attributes:
label: 描述你想要的解决方案
description: 简洁明了地描述你要发生的事情
description: 简洁明了地描述你要发生的事情
validations:
required: true
- type: textarea
id: alternatives-considered
attributes:
label: 描述你考虑过的替代方案
description: 对你考虑过的所有替代解决方案或功能的简洁明了的描述
validations:
required: false
description: 对你考虑过的所有替代解决方案或功能的简洁明了的描述
- type: textarea
id: additional-information
attributes:
label: 附加信息
description: 如果你的问题需要进一步解释,或者想要表达其他内容,请在此处添加更多信息。(直接把图片视频拖到编辑框即可添加图片视频)
description: 如果你的问题需要进一步解释,或者想要表达其他内容,请在此处添加更多信息。(直接把图片/视频拖到编辑框即可添加图片/视频)

28
.github/actions/setup/action.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Setup
description: Setup Node Env
runs:
using: composite
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
# - name: Get npm cache directory
# run: node -p -e '`NPM_CACHE_DIR=${require("child_process").execSync("npm config get cache").toString()}`' >> $GITHUB_ENV
# run: echo "NPM_CACHE_DIR=$(npm config get cache)" >> $GITHUB_ENV
# https://docs.npmjs.com/cli/v10/configuring-npm/folders#cache
- name: Cache node modules
id: cache-npm
uses: actions/cache@v4
with:
path: ${{ env.NPM_CACHE }}
key: ${{ runner.os }}-npm-cache-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-cache-
- name: Install dependencies
run: npm ci
shell: bash

View File

@@ -5,21 +5,24 @@ on:
branches:
- beta
env:
IS_CI: 'true'
jobs:
# CheckCode:
# name: Lint Code
# runs-on: ubuntu-latest
# steps:
# - name: Check out git repository
# uses: actions/checkout@v3
# uses: actions/checkout@v4
# - name: Install Node.js
# uses: actions/setup-node@v3
# uses: actions/setup-node@v4
# with:
# node-version: '16'
# node-version: 20
# - name: Cache file
# uses: actions/cache@v3
# uses: actions/cache@v4
# with:
# path: |
# node_modules
@@ -43,36 +46,26 @@ jobs:
# needs: CheckCode
steps:
- name: Check out git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Get npm cache directory
shell: pwsh
run: echo "NPM_CACHE=$(npm config get cache)" >> $env:GITHUB_ENV
- name: Cache file
uses: actions/cache@v3
with:
path: |
node_modules
%APPDATA%\npm-cache
%LOCALAPPDATA%\electron\Cache
%LOCALAPPDATA%\electron-builder\Cache
key: ${{ runner.os }}-build-caches-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-
- name: Install Dependencies
run: |
npm ci
- name: Setup Node Env
env:
NPM_CACHE: ${{ env.NPM_CACHE }}
uses: ./.github/actions/setup
- name: Build src code
run: npm run build
run: |
git status --porcelain
npm run build
- name: Build Package Setup x64
run: npm run pack:win:setup:x64
- name: Upload Artifact Setup x64
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-x64-Setup
path: build/*-x64-Setup.exe
@@ -80,7 +73,7 @@ jobs:
- name: Build Package 7z x64
run: npm run pack:win:7z:x64
- name: Upload Artifact 7z x64
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-win_x64-green
path: build/*win_x64-green.7z
@@ -88,7 +81,7 @@ jobs:
- name: Build Package Setup arm64
run: npm run pack:win:setup:arm64
- name: Upload Artifact Setup arm64
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-arm64-Setup
path: build/*-arm64-Setup.exe
@@ -96,19 +89,64 @@ jobs:
- name: Build Package 7z arm64
run: npm run pack:win:7z:arm64
- name: Upload Artifact 7z arm64
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-win_arm64-green
path: build/*win_arm64-green.7z
- name: Generate file MD5
run: |
cd build
Get-FileHash *.exe,*.7z -Algorithm MD5 | Format-List
- name: Install old electron
run: npm install electron@22
Windows_7:
name: Windows_7
runs-on: windows-latest
env:
BUILD_WIN7: true
# needs: CheckCode
steps:
- name: Check out git repository
uses: actions/checkout@v4
- name: Get npm cache directory
shell: pwsh
run: echo "NPM_CACHE=$(npm config get cache)" >> $env:GITHUB_ENV
- name: Setup Node Env
env:
NPM_CACHE: ${{ env.NPM_CACHE }}
uses: ./.github/actions/setup
- name: Prepare win7 undici env
run: |
npm install undici@5
Set-Content -Path .\src\common\utils\request.ts -Value "export * from './request_node16'"
- name: Build src code
env:
BUILD_WIN7: true
run: |
git status --porcelain
npm run build
- name: Prepare win7 electron env
run: |
npm install electron@22
pip.exe install setuptools
- name: Build Package win7 Setup x64
run: npm run pack:win7:setup:x64
- name: Upload Artifact win7 Setup x64
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-win7_x64-Setup
path: build/*win7_x64-Setup.exe
- name: Build Package win7 7z x64
run: npm run pack:win7:7z:x64
- name: Upload Artifact win7 7z x64
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-win7_x64-green
path: build/*win7_x64-green.7z
@@ -116,7 +154,7 @@ jobs:
- name: Build Package win7 7z x86
run: npm run pack:win7:7z:x86
- name: Upload Artifact win7 7z x86
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-win7_x86-green
path: build/*win7_x86-green.7z
@@ -132,49 +170,39 @@ jobs:
# needs: CheckCode
steps:
- name: Check out git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install python setuptools
run: brew install python-setuptools
- name: Cache file
uses: actions/cache@v3
with:
path: |
node_modules
$HOME/.cache/electron
$HOME/.cache/electron-builder
$HOME/.npm/_prebuilds
key: ${{ runner.os }}-build-caches-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-
- name: Get npm cache directory
shell: bash
run: echo "NPM_CACHE=$(npm config get cache)" >> $GITHUB_ENV
- name: Install Dependencies
run: |
npm ci
- name: Setup Node Env
env:
NPM_CACHE: ${{ env.NPM_CACHE }}
uses: ./.github/actions/setup
- name: Build src code
run: npm run build
run: |
git status --porcelain
npm run build
- name: Build Package dmg
run: |
npm run pack:mac:dmg
npm run pack:mac:dmg:arm64
env:
ELECTRON_CACHE: $HOME/.cache/electron
ELECTRON_BUILDERCACHE: $HOME/.cache/electron-builder
- name: Upload Artifact dmg
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-mac-dmg
path: |
build/*.dmg
!build/*-arm64.dmg
- name: Upload Artifact dmg
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-mac-dmg-arm64
path: build/*-arm64.dmg
@@ -193,36 +221,26 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y rpm libarchive-tools
- name: Check out git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Get npm cache directory
shell: bash
run: echo "NPM_CACHE=$(npm config get cache)" >> $GITHUB_ENV
- name: Cache file
uses: actions/cache@v3
with:
path: |
node_modules
$HOME/.cache/electron
$HOME/.cache/electron-builder
$HOME/.npm/_prebuilds
key: ${{ runner.os }}-build-caches-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-
- name: Install Dependencies
run: |
npm ci
- name: Setup Node Env
env:
NPM_CACHE: ${{ env.NPM_CACHE }}
uses: ./.github/actions/setup
- name: Build src code
run: npm run build
run: |
git status --porcelain
npm run build
- name: Build Package deb amd64
run: npm run pack:linux:deb:amd64
- name: Upload Artifact deb amd64
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-deb-amd64
path: build/*_amd64.deb
@@ -230,7 +248,7 @@ jobs:
- name: Build Package deb arm64
run: npm run pack:linux:deb:arm64
- name: Upload Artifact deb arm64
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-deb-arm64
path: build/*_arm64.deb
@@ -238,7 +256,7 @@ jobs:
- name: Build Package deb armv7l
run: npm run pack:linux:deb:armv7l
- name: Upload Artifact deb armv7l
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-deb-armv7l
path: build/*_armv7l.deb
@@ -246,7 +264,7 @@ jobs:
- name: Build Package x64 appImage
run: npm run pack:linux:appImage
- name: Upload Artifact x64 appImage
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-x64-appImage
path: build/*_x64.AppImage
@@ -254,7 +272,7 @@ jobs:
- name: Build Package x64 rpm
run: npm run pack:linux:rpm
- name: Upload Artifact x64 rpm
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-x64-rpm
path: build/*.x64.rpm
@@ -262,7 +280,7 @@ jobs:
- name: Build Package x64 pacman
run: npm run pack:linux:pacman
- name: Upload Artifact x64 pacman
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lx-music-desktop-x64-pacman
path: build/*_x64.pacman

View File

@@ -11,12 +11,12 @@ jobs:
steps:
- name: Check out git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '16'
node-version: 22
- name: Install Dependencies
run: npm ci

View File

@@ -5,21 +5,24 @@ on:
branches:
- master
env:
IS_CI: 'true'
jobs:
# CheckCode:
# name: Lint Code
# runs-on: ubuntu-latest
# steps:
# - name: Check out git repository
# uses: actions/checkout@v3
# uses: actions/checkout@v4
# - name: Install Node.js
# uses: actions/setup-node@v3
# uses: actions/setup-node@v4
# with:
# node-version: '16'
# node-version: 20
# - name: Cache file
# uses: actions/cache@v3
# uses: actions/cache@v4
# with:
# path: |
# node_modules
@@ -43,31 +46,24 @@ jobs:
# needs: CheckCode
steps:
- name: Check out git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Get npm cache directory
shell: pwsh
run: echo "NPM_CACHE=$(npm config get cache)" >> $env:GITHUB_ENV
- name: Cache file
uses: actions/cache@v3
with:
path: |
node_modules
%APPDATA%\npm-cache
%LOCALAPPDATA%\electron\Cache
%LOCALAPPDATA%\electron-builder\Cache
key: ${{ runner.os }}-build-caches-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-
- name: Show Env
run: echo "${{ env.NPM_CACHE }}"
- name: Install Dependencies
run: |
npm ci
- name: Setup Node Env
env:
NPM_CACHE: ${{ env.NPM_CACHE }}
uses: ./.github/actions/setup
- name: Build src code
run: npm run build
run: |
git status --porcelain
npm run build
- name: Release package
run: |
@@ -75,7 +71,53 @@ jobs:
npm run publish:win:7z:arm64
npm run publish:win:setup:arm64
npm run publish:win:setup:x64
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BT_TOKEN: ${{ secrets.BT_TOKEN }}
- name: Generate file MD5
run: |
cd build
Get-FileHash *.exe,*.7z -Algorithm MD5 | Format-List
Windows_7:
name: Windows_7
runs-on: windows-latest
# needs: CheckCode
steps:
- name: Check out git repository
uses: actions/checkout@v4
- name: Get npm cache directory
shell: pwsh
run: echo "NPM_CACHE=$(npm config get cache)" >> $env:GITHUB_ENV
- name: Setup Node Env
env:
NPM_CACHE: ${{ env.NPM_CACHE }}
uses: ./.github/actions/setup
- name: Prepare win7 undici env
run: |
npm install undici@5
Set-Content -Path .\src\common\utils\request.ts -Value "export * from './request_node16'"
- name: Build src code
env:
BUILD_WIN7: true
run: |
git status --porcelain
npm run build
- name: Prepare win7 electron env
run: |
npm install electron@22
pip.exe install setuptools
- name: Release win7 package
run: |
npm run publish:win7:setup:x64
npm run publish:win7:7z:x64
npm run publish:win7:7z:x86
env:
@@ -93,39 +135,32 @@ jobs:
# needs: CheckCode
steps:
- name: Check out git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install python3 setuptools
run: brew install python-setuptools
- name: Cache file
uses: actions/cache@v3
with:
path: |
node_modules
$HOME/.cache/electron
$HOME/.cache/electron-builder
$HOME/.npm/_prebuilds
key: ${{ runner.os }}-build-caches-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-
- name: Get npm cache directory
shell: bash
run: echo "NPM_CACHE=$(npm config get cache)" >> $GITHUB_ENV
- name: Install Dependencies
run: |
npm ci
- name: Show Env
run: echo "${{ env.NPM_CACHE }}"
- name: Setup Node Env
env:
NPM_CACHE: ${{ env.NPM_CACHE }}
uses: ./.github/actions/setup
- name: Build src code
run: npm run build
run: |
git status --porcelain
npm run build
- name: Release package
run: |
npm run publish:mac:dmg
npm run publish:mac:dmg:arm64
env:
ELECTRON_CACHE: $HOME/.cache/electron
ELECTRON_BUILDERCACHE: $HOME/.cache/electron-builder
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BT_TOKEN: ${{ secrets.BT_TOKEN }}
@@ -143,31 +178,23 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y rpm libarchive-tools
- name: Check out git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Get npm cache directory
shell: bash
run: echo "NPM_CACHE=$(npm config get cache)" >> $GITHUB_ENV
- name: Cache file
uses: actions/cache@v3
with:
path: |
node_modules
$HOME/.cache/electron
$HOME/.cache/electron-builder
$HOME/.npm/_prebuilds
key: ${{ runner.os }}-build-caches-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-
- name: Install Dependencies
run: |
npm ci
- name: Show Env
run: echo "${{ env.NPM_CACHE }}"
- name: Setup Node Env
env:
NPM_CACHE: ${{ env.NPM_CACHE }}
uses: ./.github/actions/setup
- name: Build src code
run: npm run build
run: |
git status --porcelain
npm run build
- name: Release package
run: |

View File

@@ -6,25 +6,42 @@ module.exports = {
'del',
'comlink',
'vue',
'vue-router',
'image-size',
'message2call',
'@types/ws',
'eslint',
'@types/node',
'electron-debug',
'eslint-webpack-plugin',
'eslint-plugin-vue',
'vue-eslint-parser',
// 'eslint-config-standard-with-typescript',
'typescript', // https://github.com/microsoft/TypeScript/pull/54567
],
// target: 'newest',
// filter: [
// 'electron-builder',
// 'electron-updater',
// 'electron-log',
// ],
// target: 'patch',
// filter: [
// 'electron',
// 'vue',
// 'vue-router',
// ],
// target: 'minor',
// filter: [
// 'electron',
// // 'electron',
// 'eslint',
// 'eslint-webpack-plugin',
// 'electron-debug',
// '@types/node',
// 'eslint-plugin-vue',
// 'vue-eslint-parser',
// ],
}

View File

@@ -6,6 +6,328 @@ 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/).
## [2.12.1](https://github.com/lyswhut/lx-music-desktop/compare/v2.12.0...v2.12.1) - 2026-02-16
我们很高兴地宣布新项目 Any Listen 的桌面版已发布目前已支持列表跟随本地文件自动更新、加载并播放WebDAV上的歌曲等功能更多功能仍在积极开发中桌面版与Web版将同步更新。
对于有播放本地音乐或播放服务器上音乐需求的人可以试试,若遇到任何问题可以发 issue 反馈。
### 优化
- 优化托盘图标行为:在非 Windows 系统中,点击托盘图标时不再显示主窗口
### 修复
- 修复音量条在调整音量时实际音量与显示的数值不一致的问题(#2606
- 修复某些情况下搜索框的搜索按钮布局错位的问题(#2622
## [2.12.0](https://github.com/lyswhut/lx-music-desktop/compare/v2.11.0...v2.12.0) - 2025-11-29
我们很高兴地宣布新项目 Any Listen 的桌面版已发布目前已支持列表跟随本地文件自动更新、加载并播放WebDAV上的歌曲等功能更多功能仍在积极开发中桌面版与Web版将同步更新。
对于有播放本地音乐或播放服务器上音乐需求的人可以试试,若遇到任何问题可以发 issue 反馈。
### 新增
- 新增「设置 → 其他设置 → 主窗口使用软件内置的圆角及阴影」选项 (#2360)
*默认启用,关闭后将使用系统原生的窗口样式,该选项重启软件后生效*
- 开放 API 新增播放器声音大小、静音、播放进度控制、完整歌词获取,详情看接入文档 (#2386)
- 新增「设置 → 播放设置 → 调换歌词翻译与歌词罗马音位置」选项,默认关闭 (#2451)
- 新增启动参数 `-hidden`,在启动时将软件最小化到系统托盘 (#2459)
- 新增 Any Listen 歌词(用于支持已下载歌曲的歌词逐字播放)标签数据读取与播放 (#2485)
- 新增 Any Listen 歌词(包含逐字歌词、翻译、罗马音歌词,如果有)嵌入与下载,默认启用
- 下载列表菜单新增歌曲添加弹窗,允许将所选歌曲的在线版本添加到收藏列表 (#2537)
### 修复
- 尝试修复进度为0时仍然显示下载完成的问题 (#2471)
- 修复TX源搜索失败 (#2575 @Folltoshe)
- 修复MG源歌单加载失败
- 修复MG源评论加载失败
### 变更
- 调换「歌词翻译」与「歌词罗马音」的位置,现在歌词罗马音在歌词翻译的上方展示
*若你想要恢复以前的行为,可以开启「调换歌词翻译与歌词罗马音位置」选项*
- 更新代理配置规则,现在不启用代理时,图片、音频加载将不再走系统代理 (#2382 @Folltoshe)
- 字体设置可以最多设置两种字体([any-listen#82](https://github.com/any-listen/any-listen/issues/82)
### 其他
- 更新 Electron 到 37.6.0
## [2.11.0](https://github.com/lyswhut/lx-music-desktop/compare/v2.10.0...v2.11.0) - 2025-05-01
### 新增
- 新增「快进/快退5秒」自定义快捷键设置#2289
- 新增「设置 → 桌面歌词设置 → 暂停时提高歌词透明度」设置,默认启用(#2294
### 修复
- 修复 Windows 下桌面歌词最小高度与宽度设置问题(#2244
- 修复 Windows 下界面缩放后移动桌面歌词会改变歌词窗口大小的问题(#2244
- 修复 tx 歌单搜索名字、描述出现乱码的问题(#2250
- 修复本地 FLAC 文件内嵌歌词无法读取的问题
- 修复潜在播放暂停的问题
- 修复 kw 歌单详情出现打开失败的问题(#2317
- 修复 kg 热门评论无法获取的问题
- 修复桌面歌词被遮挡时会被暂停的问题(#2320
- 修复 kg 歌单打开失败的问题thanks @Folltoshe
### 优化
- 允许更小的桌面歌词窗口宽度
- 允许拖动桌面歌词控制栏空白处移动歌词窗口(#2280
- 优化「自定义源管理」对话框在小窗口下的布局(#2247, @3gf8jv4dv
- 优化软件文案编排(#2259, #2266, #2269, #2296, @3gf8jv4dv
### 变更
- 我的列表-歌曲菜单中的 歌曲换源 功能从之前的类似软连接的形式改成替换歌曲的形式,也就是说,现在该功能相当于快速在线搜索歌曲,确认换源后将自动将原来的歌曲删除再将选择的歌曲插入被删除歌曲的位置。
### 其他
- 更新项目文档(@3gf8jv4dv
- 更新 Electron 到 35.2.2
## [2.10.0](https://github.com/lyswhut/lx-music-desktop/compare/v2.9.0...v2.10.0) - 2025-01-27
落雪祝大家新年快乐!
### 关于之前提到的新项目
新项目我取名叫 Any Listen希望它能像它的名字一样让我们能到处任意听歌。
经过一年多的开发因各种原因实际进度比预期的慢但还是赶在年前发布了第一个web服务预览版第一个版本仅支持播放服务器上的歌曲扩展功能暂时未能开放但已趋于完成一两个月内可以搞定。
目前的版本仅是“能用”的状态因时间关系部分UI未能重新设计但后面会继续完善。
该项目目前的目标用户是拥有自己服务器且上面存储有歌曲的人使用。
项目刚发布,文档未能完善,遇到使用问题或有任何建议欢迎提 issue 交流,
项目地址: https://github.com/any-listen/any-listen
---
*感谢 @3gf8jv4dv 对 LX 系列项目翻译、文档等文案的大幅修订优化。*
### 不兼容性变更
Linux 系统至少需要 `GLIBC_2.29` 版本才能运行。
由于将 Electron 升级到 v32.x原生库的编译被限制到不低于 C++ 20试了几次无法在 docker 镜像 `node:16` 安装 gcc-10最终将构建使用镜像更新到 `node:18`
### 新增
- 新增下载的歌曲按列表名分组的功能,默认关闭,可以通过「设置 → 下载设置 → 将文件保存到以对应列表命名的子目录中」启用(#2145
- 新增托盘图标样式「跟随系统亮暗模式」设置,可以在「设置 → 其他」里启用 #2016
- 支持本地同名 `.krc` 格式歌词文件的读取(#2053
- 开放 API 新增播放器播放/暂停、切歌、收藏当前播放歌曲等接口调用,详情看文档「开放 API 服务」部分(#2077, @14Kay
### 优化
- 优化正常播放结束时的下一首歌曲播放衔接度,在歌曲即将结束播放时将预获取下一首歌曲的播放链接,减少自动切歌时的等待时间(#2126
- 优化歌曲换源机制,提升换源正确率
- 优化 Windows 平台上桌面歌词窗口大小调整机制,改用原生的窗口调整方式(#2137
- 修正搜索歌曲提示框文案(#2050
- 优化播放详情页 UI修复「歌曲名」「艺术家」等文字过长时被截断的问题#2049
- Scheme URL 的播放歌曲允许更长的专辑名称
- 播放本地歌曲时,将优先尝试读取本地同名 `.jpg``.png` 图片作为播放封面显示,若文件不存在则从音频文件内读取,最后再尝试使用在线图片(#2096
- 客户端模式的同步服务连接允许重定向 5 次(#2109
- 更新软件默认使用的字体,修复 macOS Sequoia (15) 上界面出现乱码的问题(#2076
- 优化简体、繁体中文文案编排,大幅修订英语文案编排(#2159, #2166, #2174 等, @3gf8jv4dv
- 优化排序歌曲、主题名称、添加/编辑主题、列表更新管理等对话框布局及长文本显示效果(#2176, #2188, #2189, #2198 等, @3gf8jv4dv
### 修复
- 修复歌单详情页内歌单名字过长时的 UI 显示问题(#2028
- 修复获取自定义环境音效预设列表逻辑问题
- 修复 `.m4a` 文件内嵌歌词无法读取的问题(#2090
- 修复 Windows 任务管理器中的进程名显示为软件描述的问题(#2147
- 修复本地歌曲同名歌词文件调整偏移时间后,下次再播放时调整的设置未被应用的问题(#2139
- 修复首次打开软件后直接创建并删除列表时的报错问题(#2175, @14Kay
### 变更
- 不再长期缓存换源歌曲信息
- 更新软件默认使用的字体,现在软件尽量使用系统自带的默认字体
- Linux 系统至少需要 `GLIBC_2.29` 版本才能运行
### 其他
- 更新 Readme 文档,优化文案编排(#2146, Thanks @3gf8jv4dv
- 更新 Issue 模板(#2153, @3gf8jv4dv
- 更新项目文档(@3gf8jv4dv
- 修订项目协议文件(#2146, #2152, @3gf8jv4dv
- 更新 Electron 到 v32.3.0
## [2.9.0](https://github.com/lyswhut/lx-music-desktop/compare/v2.8.0...v2.9.0) - 2024-08-24
### 新增
- 新增 设置-播放设置-是否将歌词显示在状态栏 设置,默认关闭,该功能只在 MacOS 下可用(#1940
- 新增设置-播放详情页设置-延迟歌词滚动设置(#1985
- 新增鼠标在音量按钮使用滚轮时可以调整音量大小的功能(#2000
- 新增设置-下载设置-同时下载任务数设置(#1498
- 新增 我的列表-歌曲右击菜单-歌曲换源 功能,换源后下次再播放该列表的该歌曲时将优先尝试播放所选源的歌曲,该功能允许你手动指定来源以解决自动换源失败或者换源不准确的问题
### 优化
- 优化侧栏图标显示,修复图标可能被裁切的问题(#1960
- 托盘图标添加当前播放歌曲名字显示
- 优化本地歌曲内嵌封面过大时的加载方式
- 将下载歌曲的歌手信息中的分隔符从 `、` 替换为 `;` 以确保音乐元数据在写入时的兼容性和一致性(#1989 @qnnp-me
### 修复
- 修复 MacOS 下点击 dock 右键菜单的退出按钮时,程序没有退出的问题(#1923
- 修复 OpenAPI 的 `lyricLineAllText` 在切换到无歌词的音乐时内容没有更新的问题(#1925
- 修复切换音源时可能出现切换死循环的问题
- 尝试修复某些情况下播放音频时,处于播放状态但是进度条不走的问题
- 修复程序目录路径存在 `#``%` 时,自定义源、托盘等图标异常的问题(#1997
### 变更
- 简化了应用退出行为,据测试,现在 linux 下若启用了托盘dock 右键菜单的 退出、关闭所有 之类的功能将不再退出程序,需改用托盘的退出按钮退出程序
- 现在如果在设置或者启动参数配置了代理服务,那么应用内的图片、音频加载,歌曲下载也将走代理
### 其他
- 更新 electron 到 v30.4.0
## [2.8.0](https://github.com/lyswhut/lx-music-desktop/compare/v2.7.0...v2.8.0) - 2024-06-01
我们发布了关于 LX Music 项目发展调整与新项目计划的说明,
详情看: https://github.com/lyswhut/lx-music-desktop/issues/1912
### 新增
- 新增 设置-播放设置-使用设备能处理的最大声道数输出音频 设置未启用时固定为2声道输出由于这用到高级音频API考虑到在某些设备上的兼容问题默认禁用#1873
- 允许添加 `m4a``oga` 格式的本地歌曲到列表中(#1864
- 开放API支持跨域请求#1872 @Ceale
- Scheme URL API新增 `music/searchPlay` 支持,用于搜索并播放指定的歌曲名字,详细入参请阅读 Scheme URL 支持文档(#1886
### 优化
- 优化白色托盘图标显示修复windows下托盘图标不清晰的问题#1842
### 修复
- 修复存在多级弹窗时的背景显示问题
- 增大在线导入自定义源文件的大小限制问题(#1857
- 修复Mac下窗口出现残留阴影的问题这解决了Mac下桌面歌词出现残留阴影的远古bug感谢 @zclorne #1869, Thanks @zclorne
- 增大在线导入自定义源文件的大小限制,解决某些音源无法导入的问题(#1857
- 修复Mac下即使开启了托盘 `cmd+w` 仍会中断播放的问题(#1844
- 修复播放详情页的歌词无法使用触碰拖动的问题(#1865
- 修复与优化繁体中文、英语翻译显示(#1845
- 修复歌曲时文件名过长导致歌曲无法下载的问题(#1877
- 修复文本提示气泡在内容过长时,文本未被换行而被截断的问题
- 修复翻页按钮栏切页按钮只显示前几页的问题
### 变更
- 设置-播放设置-优先播放320k音质选项改为“优先播放的音质”允许选择更高优先播放的音质如果歌曲及音源支持的话#1839
### 开放API变更
- `/status` 的入参现在与 `/subscribe-player-status` 保持一致
- `/status` 新增 `filter` 入参用于过滤返回的字段,并内置了默认值,与之前相比默认不再返回 `picUrl`
- `/status``/subscribe-player-status` 的可用字段名添加了 `lyricLineAllText`,它对应的值是当前句歌词及扩展歌词文本(扩展歌词包含翻译、罗马音等,按换行符分割)
详情看开放API接入文档
### 其他
- 更新 electron 到 v28.3.3
## [2.7.0](https://github.com/lyswhut/lx-music-desktop/compare/v2.6.0...v2.7.0) - 2024-04-14
### 新增
- 主题编辑器添加“深色字体”选项,启用后将减少字体颜色梯度,各类字体(正文、标签字体等)颜色将更接近,这有助于解决创建全透明主题时可能出现的字体配色问题(#1799
- 新增在线自定义源导入功能允许通过http/https链接导入自定义源
- 新增HTTP开放API服务默认关闭该服务可以为第三方软件提供调用LX的能力可用API看[说明文档](https://lyswhut.github.io/lx-music-doc/desktop/open-api)#1824
- 托盘菜单新增播放、切歌、收藏控制
- 添加当前软件版本所对应的代码提交版本、提交时间的显示,可到设置-版本更新查看
### 优化
- 主题设置默认折叠其他主题以优化进入设置界面时的性能
- 不再丢弃kg源逐行歌词@helloplhm-qwq
- 支持kw源排行榜显示大小revert @Folltoshe #1460
- 托盘菜单添加多语言支持(#1802
- 优化本地歌曲换源匹配机制
### 修复
- 修复某些情况下歌曲加载时间过长时不会自动跳到下一首的问题
- 修复mg歌词在某些情况下获取失败的问题#1783
- 修复mg歌单搜索@helloplhm-qwq
- 修复kg最新评论无法获取的问题@helloplhm-qwq
- 修复更新超时弹窗在非更新阶段意外弹出的问题(#1797
- 修复网络代理设置没有对自定义源的网络请求生效的问题(#1814
### 移除
- 移除未使用的网络代理设置用户名、密码设置,实际上在 v1.20.0 起这两个设置就没有在被内部使用
### 其他
- 更新 electron 到 v28.3.0
## [2.6.0](https://github.com/lyswhut/lx-music-desktop/compare/v2.5.0...v2.6.0) - 2024-02-01
提交祝大家新年快乐!
更新前需要注意:
由于自定义源的调用方式变更可能会导致某些第三方源停止工作如果出现这种情况你需要将LX回退到 v2.5.0
### 新增
- 若自定义源初始化失败,将会出现弹窗提示初始化失败的详情
- 添加win7_x64架构的安装版安装包构建
- 新增播放歌曲时阻止电脑休眠,默认启用,可到设置-播放设置关闭(#1563
### 优化
- 更新zh-tw翻译
- 自定义源列显示源版本号、作者名字
- 优化列表全选机制,修复列表未获得焦点时仍然可以全选的问题
- 优化搜索框交互逻辑,防止鼠标操作时意外搜索候选列表的内容
- 添加对wy源某些歌曲有问题的歌词进行修复
- 改进本地音乐在线信息的匹配机制
- 优化任务下载状态显示,现在下载时若数据传输完成但数据写入未完成时会显示相应的状态
- 添加对下载歌曲时封面图片大小的控制处理(#1609
- 添加创建同名列表时的二次确认(#1621
### 修复
- 修复备份文件无法导入json格式的问题
- Windows、MacOS平台下的字体列表取消使用原生方式获取以修复某些字体应用后无效的问题#1596
- 修复亮暗主题自动切换功能无效的问题(#1697
- 修复 MacOS 平台在 Finder 打开文件或目录时应用卡死的问题(#1684
- 修复下载模块在数据写入速度较慢的情况下出现任务及文件异常的问题
- 修复临时列表变更会意外触发同步的问题
- 修复最小化后再隐藏窗口时,托盘菜单的显示主界面功能异常的问题
### 变更
- 播放歌曲时默认会阻止系统进入休眠状态,若你不行软件阻止系统休眠,可以到设置-播放设置取消勾选“播放歌曲时阻止电脑休眠”设置
### 其他
- 移除所有内置源由于收到腾讯投诉要求停止提供软件内置的连接到他们平台的在线播放及下载服务所以从即日2023年10月18日起LX本身不再提供上述服务
- 更新 electron 到 v25.9.8
- 更新许可协议的排版,使其看起来更加清晰明了,更新数据来源原理说明
### 自定义源的不兼容变更与新增内容(源开发者需要看)
自定义源的调用方式已改变:
- 为了与移动端的调用方式统一,不再推荐使用 `window.lx` 对象(移动端无`window`对象),改用 `globalThis.lx`
- `inited` 事件不再需要传递 `status` 属性,脚本运行过程中,在成功调用 `inited` 事件之前的任何首次未捕获的错误都将视为初始化失败,所以现在若想人为让脚本初始化失败,直接抛出一个错误即可
- 新增 `globalThis.lx.env` 属性,桌面端环境固定为 `desktop`,移动端环境固定为 `mobile`
- 新增 `globalThis.lx.currentScriptInfo` 对象,可以从这里获取解析后的脚本头部注释信息及脚本原始内容,具体可用属性看文档说明
- `globalThis.lx.version` 属性更新到 `2.0.0`
- 自定义源不再使用`script`标签的形式执行,若要获取脚本原始代码字符串需从 `globalThis.lx.currentScriptInfo.rawScript` 属性获取
- 自定义源新增支持`local`源的`musicUrl``pic``lyric`的获取操作详情看自定义源文档说明
## [2.5.0](https://github.com/lyswhut/lx-music-desktop/compare/v2.4.1...v2.5.0) - 2023-09-28
落雪提前祝大家中秋快乐~🥮😘!

2
FAQ.md
View File

@@ -1,6 +1,6 @@
# lx-music-desktop 常见问题
本文档已迁移<https://lyswhut.github.io/lx-music-doc/desktop/faq>
本文档已迁移<https://lyswhut.github.io/lx-music-doc/desktop/faq>
<!--
在阅读本常见问题后仍然无法解决你的问题请提交issue或者加企鹅群`830125506`反馈(无事勿加,入群先看群公告),反馈时请**注明**已阅读常见问题!

151
README.md
View File

@@ -1,5 +1,7 @@
<p align="center"><a href="https://github.com/lyswhut/lx-music-desktop"><img width="200" src="https://github.com/lyswhut/lx-music-desktop/blob/master/doc/images/icon.png" alt="lx-music logo"></a></p>
<h1 align="center">LX Music 桌面版</h1>
<p align="center">
<a href="https://github.com/lyswhut/lx-music-desktop/releases"><img src="https://img.shields.io/github/release/lyswhut/lx-music-desktop" alt="Release version"></a>
<a href="https://github.com/lyswhut/lx-music-desktop/actions/workflows/release.yml"><img src="https://github.com/lyswhut/lx-music-desktop/workflows/Build/badge.svg" alt="Build status"></a>
@@ -28,108 +30,133 @@
[9]: https://img.shields.io/github/license/lyswhut/lx-music-desktop
[10]: https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE -->
<h2 align="center">洛雪音乐助手桌面版</h2>
<p align="center">一个基于 Electron & Vue 开发的音乐软件</p>
### 说明
一个基于 Electron + Vue 开发的音乐软件。
## 说明
所用技术栈:
- Electron 15+
- Electron 30+
- Vue 3
已支持的平台:
- Windows 7 及以上
- Mac OS
- Linux
- macOS
- Windows 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://lyswhut.github.io/lx-music-doc/desktop/faq)
*移动版项目地址:https://github.com/lyswhut/lx-music-mobile*
目前本项目的原始发布地址只有**GitHub**及**蓝奏网盘**,其他渠道均为第三方转载发布,与本项目无关!
*LX Music 项目发展调整与新项目计划https://github.com/lyswhut/lx-music-desktop/issues/1912*
为了提高使用门槛本软件内的默认设置、UI操作不以新手友好为目标所以使用前建议先根据你的喜好浏览调整一遍软件设置阅读一遍[音乐播放列表机制](https://lyswhut.github.io/lx-music-doc/desktop/faq/playlist)及[可用的鼠标、键盘快捷操作](https://lyswhut.github.io/lx-music-doc/desktop/faq/hotkey)
软件变化请查看[更新日志](https://github.com/lyswhut/lx-music-desktop/blob/master/CHANGELOG.md)
#### Scheme URL支持
软件下载请查看 [GitHub Releases](https://github.com/lyswhut/lx-music-desktop/releases)。
从v1.17.0起支持 Scheme URL可以使用此功能从浏览器等场景下调用LX Music我们开发了一个[油猴脚本](https://github.com/lyswhut/lx-music-script#readme)配套使用,<br>
脚本安装地址:<https://greasyfork.org/zh-CN/scripts/438148><br>
使用常见问题请参阅[桌面版常见问题](https://lyswhut.github.io/lx-music-doc/desktop/faq)。
若你想自己调用LX Music可以看[Scheme URL支持](https://lyswhut.github.io/lx-music-doc/desktop/scheme-url)
目前本项目的原始发布地址只有 [**GitHub**](https://github.com/lyswhut/lx-music-desktop/releases),其他渠道均为第三方转载发布,与本项目无关!
#### 数据同步服务
为了提高使用门槛本软件内的默认设置、UI 操作不以新手友好为目标,所以使用前建议先根据你的喜好浏览调整一遍软件设置,阅读一遍[音乐播放列表机制](https://lyswhut.github.io/lx-music-doc/desktop/faq/playlist)及[可用的鼠标、键盘快捷操作](https://lyswhut.github.io/lx-music-doc/desktop/faq/hotkey)。
从v2.2.0起,我们发布了一个独立版的[数据同步服务](https://github.com/lyswhut/lx-music-sync-server#readme),如果你有服务器,可以将其部署到服务器上作为私人多端同步服务使用,详情看该项目说明
### Scheme URL 支持
#### 启动参数
从 v1.17.0 起支持 Scheme URL可以使用此功能在浏览器等场景下调用 LX Music我们开发了一个[油猴脚本](https://github.com/lyswhut/lx-music-script#readme)配套使用。
目前软件已支持的启动参数如下:
脚本安装地址:[LX Music 辅助脚本](https://greasyfork.org/zh-CN/scripts/438148)。
- `-proxy-server` 设置代理服务器,代理应用的所有流量
- `-proxy-bypass-list` 以分号分隔的主机列表绕过代理服务器
- `-play` 启动时播放指定列表的音乐
- `-search` 启动软件时自动在搜索框搜索指定的内容
- `-dha` 禁用硬件加速启动Disable Hardware Acceleration
- `-dt` 以非透明模式启动Disable Transparent
- `-dhmkh` 禁用硬件媒体密钥处理Disable Hardware Media Key Handling
若你想自己调用 LX Music可以参考文档「[Scheme URL 支持](https://lyswhut.github.io/lx-music-doc/desktop/scheme-url)」部分。
启动参数的详细说明请看[启动参数说明](https://lyswhut.github.io/lx-music-doc/desktop/run-params)
### 数据同步服务
#### 数据存储路径
从 v2.2.0 起,我们发布了一个独立的[数据同步服务](https://github.com/lyswhut/lx-music-sync-server#readme)。如果你有服务器,可以将其部署到服务器上作为私人多端同步服务使用,详情看该项目说明。
### 开放 API 支持
从 v2.7.0 起支持开放 API 服务。启用该功能后,将会在本地启动一个 HTTP 服务,提供播放器相关的接口供第三方软件调用,详情看文档「[开放 API 服务](https://lyswhut.github.io/lx-music-doc/desktop/open-api)」部分。
### 数据存储目录
默认情况下,软件的数据存储在:
- Windows`%APPDATA%/lx-music-desktop`
- Linux`$XDG_CONFIG_HOME/lx-music-desktop``~/.config/lx-music-desktop`
- macOS`~/Library/Application Support/lx-music-desktop`
- Windows`%APPDATA%/lx-music-desktop`
在Windows平台,若程序目录下存在`portable`目录,则自动使用此目录作为数据存储目录(v1.17.0新增)。
Windows 平台,若程序文件夹中存在 `portable` 文件夹,则自动使用此文件夹作为数据存储文件夹(适用于 v1.17.0 及以上版本)。
### 源码使用方法
## 用户界面
已迁移至:<https://lyswhut.github.io/lx-music-doc/desktop/use-source-code>
<p><img width="100%" src="./doc/images/app.png" alt="lx-music desktop UI"></p>
### UI界面
## 贡献代码
<p><a href="https://github.com/lyswhut/lx-music-desktop"><img width="100%" src="https://github.com/lyswhut/lx-music-desktop/blob/master/doc/images/app.png" alt="lx-music UI"></a></p>
本项目欢迎 PR但为了 PR 能顺利合并,需要注意以下几点:
### 常见问题
常见问题已移至:<https://lyswhut.github.io/lx-music-doc/desktop/faq>
### 贡献代码
本项目欢迎PR但为了PR能顺利合并需要注意以下几点
- 对于添加新功能的PR建议在PR前先创建issue说明以确认该功能是否确实需要
- 对于修复Bug PR请提供修复前后的说明及重现方式
- 其他类型的PR则适当附上说明
- 对于添加新功能的 PR建议在提交 PR 前先创建 Issue 进行说明,以确认该功能是否确实需要。
- 对于修复 bug 的 PR请提供修复前后的说明及重现方式。
- 对于其他类型的 PR则适当附上说明。
贡献代码步骤:
1. 参照[源码使用方法](https://lyswhut.github.io/lx-music-doc/desktop/use-source-code)设置开发环境
2. 克隆本仓库代码并切换`dev`分支开发
3. 提交PR`dev`分支
1. 参照[源码使用方法](https://lyswhut.github.io/lx-music-doc/desktop/use-source-code)设置开发环境
2. 克隆本仓库代码并切换`dev` 分支进行开发
3. 提交 PR`dev` 分支
### 项目协议
## 源码使用方法
请参阅:<https://lyswhut.github.io/lx-music-doc/desktop/use-source-code>
## 项目协议
本项目基于 [Apache License 2.0](https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE) 许可证发行,以下协议是对于 Apache License 2.0 的补充,如有冲突,以以下协议为准。
词语约定:本协议中的“本项目”指洛雪音乐桌面版项目;“使用者”指签署本协议的使用者;“官方音乐平台”指对本项目内置的包括酷我、酷狗、咪咕等音乐源的官方平台统称;“版权数据”指包括但不限于图像、音频、名字等在内的他人拥有所属版权的数据。
---
1. 本项目的数据来源原理是从各官方音乐平台的公开服务器中拉取数据,经过对数据简单地筛选与合并后进行展示,因此本项目不对数据的准确性负责。
2. 使用本项目的过程中可能会产生版权数据,对于这些版权数据,本项目不拥有它们的所有权,为了避免造成侵权,使用者务必在**24小时**内清除使用本项目的过程中所产生的版权数据。
3. 本项目内的官方音乐平台别名为本项目内对官方音乐平台的一个称呼,不包含恶意,如果官方音乐平台觉得不妥,可联系本项目更改或移除。
4. 本项目内使用的部分包括但不限于字体、图片等资源来源于互联网,如果出现侵权可联系本项目移除。
5. 由于使用本项目产生的包括由于本协议或由于使用或无法使用本项目而引起的任何性质的任何直接、间接、特殊、偶然或结果性损害(包括但不限于因商誉损失、停工、计算机故障或故障引起的损害赔偿,或任何及所有其他商业损害或损失)由使用者负责。
6. 本项目完全免费,且开源发布于 GitHub 面向全世界人用作对技术的学习交流,本项目不对项目内的技术可能存在违反当地法律法规的行为作保证,**禁止在违反当地法律法规的情况下使用本项目**,对于使用者在明知或不知当地法律法规不允许的情况下使用本项目所造成的任何违法违规行为由使用者承担,本项目不承担由此造成的任何直接、间接、特殊、偶然或结果性责任。
*词语约定:本协议中的“本项目”指 LX Music洛雪音乐助手桌面版项目“使用者”指签署本协议的使用者“官方音乐平台”指对本项目内置的包括酷我、酷狗、咪咕等音乐源的官方平台统称“版权数据”指包括但不限于图像、音频、名字等在内的他人拥有所属版权的数据。*
若你使用了本项目,将代表你接受以上协议。
### 一、数据来源
音乐平台不易,请尊重版权,支持正版。<br>
本项目仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作及捐赠。<br>
若对此有疑问请 mail to: lyswhut+qq.com (请将`+`替换成`@`)<br>
1.1 本项目的各官方平台在线数据来源原理是从其公开服务器中拉取数据(与未登录状态在官方平台 APP 获取的数据相同),经过对数据简单地筛选与合并后进行展示,因此本项目不对数据的合法性、准确性负责。
1.2 本项目本身没有获取某个音频数据的能力,本项目使用的在线音频数据来源来自软件设置内“自定义源”设置所选择的“源”返回的在线链接。例如播放某首歌,本项目所做的只是将希望播放的歌曲名、艺术家等信息传递给“源”,若“源”返回了一个链接,则本项目将认为这就是该歌曲的音频数据而进行使用,至于这是不是正确的音频数据本项目无法校验其准确性,所以使用本项目的过程中可能会出现希望播放的音频与实际播放的音频不对应或者无法播放的问题。
1.3 本项目的非官方平台数据(例如“我的列表”内列表)来自使用者本地系统或者使用者连接的同步服务,本项目不对这些数据的合法性、准确性负责。
### 二、版权数据
2.1 使用本项目的过程中可能会产生版权数据。对于这些版权数据,本项目不拥有它们的所有权。为了避免侵权,使用者务必在 **24 小时内** 清除使用本项目的过程中所产生的版权数据。
### 三、音乐平台别名
3.1 本项目内的官方音乐平台别名为本项目内对官方音乐平台的一个称呼,不包含恶意。如果官方音乐平台觉得不妥,可联系本项目更改或移除。
### 四、资源使用
4.1 本项目内使用的部分包括但不限于字体、图片等资源来源于互联网。如果出现侵权可联系本项目移除。
### 五、免责声明
5.1 由于使用本项目产生的包括由于本协议或由于使用或无法使用本项目而引起的任何性质的任何直接、间接、特殊、偶然或结果性损害(包括但不限于因商誉损失、停工、计算机故障或故障引起的损害赔偿,或任何及所有其他商业损害或损失)由使用者负责。
### 六、使用限制
6.1 本项目完全免费,且开源发布于 GitHub 面向全世界人用作对技术的学习交流。本项目不对项目内的技术可能存在违反当地法律法规的行为作保证。
6.2 **禁止在违反当地法律法规的情况下使用本项目。** 对于使用者在明知或不知当地法律法规不允许的情况下使用本项目所造成的任何违法违规行为由使用者承担,本项目不承担由此造成的任何直接、间接、特殊、偶然或结果性责任。
### 七、版权保护
7.1 音乐平台不易,请尊重版权,支持正版。
### 八、非商业性质
8.1 本项目仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作及捐赠。
### 九、接受协议
9.1 若你使用了本项目,即代表你接受本协议。
---
若对此有疑问请 mail to: lyswhut+qq.com (请将 `+` 替换为 `@`)

View File

@@ -53,7 +53,7 @@ const replaceQrcDecodeLib = async(electronNodeAbi, platform, arch) => {
module.exports = async(context) => {
const { electronPlatformName, arch } = context
const electronVersion = context.packager?.info?._framework?.version ?? require('../package.json').devDependencies.electron.replace(/.*?(\d+)/, '$1')
const electronVersion = context.packager?.info?._framework?.version ?? require('../package.json').devDependencies.electron.replace(/^[^\d]*?(\d+)/, '$1')
const electronNodeAbi = nodeAbi.getAbi(electronVersion, 'electron')
await replaceQrcDecodeLib(electronNodeAbi, electronPlatformName, arch)
if (electronPlatformName !== 'linux' || process.env.FORCE) return

View File

@@ -10,6 +10,7 @@ const afterPack = require('./build-after-pack')
*/
const options = {
appId: 'cn.toside.music.desktop',
productName: 'lx-music-desktop',
beforePack,
afterPack,
protocols: {
@@ -65,7 +66,7 @@ const winOptions = {
oneClick: false,
language: '2052',
allowToChangeInstallationDirectory: true,
differentialPackage: true,
// differentialPackage: true,
license: './licenses/license.rtf',
shortcutName: 'LX Music',
},
@@ -81,12 +82,18 @@ const linuxOptions = {
icon: './resources/icons',
category: 'Utility;AudioVideo;Audio;Player;Music;',
desktop: {
Name: 'LX Music',
'Name[zh_CN]': 'LX Music',
'Name[zh_TW]': 'LX Music',
Encoding: 'UTF-8',
MimeType: 'x-scheme-handler/lxmusic',
StartupNotify: 'false',
// https://www.electron.build/app-builder-lib.interface.linuxdesktopfile
// https://www.electronjs.org/docs/latest/tutorial/linux-desktop-actions
// https://specifications.freedesktop.org/desktop-entry-spec/latest/example.html
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files
entry: {
Name: 'LX Music',
'Name[zh_CN]': 'LX Music',
'Name[zh_TW]': 'LX Music',
Encoding: 'UTF-8',
MimeType: 'x-scheme-handler/lxmusic',
StartupNotify: 'false',
},
},
},
appImage: {
@@ -106,23 +113,22 @@ const macOptions = {
},
dmg: {
window: {
width: 600,
height: 400,
width: 530,
height: 380,
},
contents: [
{
x: 106,
y: 252,
name: 'LX Music',
x: 140,
y: 200,
},
{
x: 490,
y: 252,
x: 390,
y: 200,
type: 'link',
path: '/Applications',
},
],
title: '洛雪音乐助手 v${version}',
title: 'LX Music v${version}',
},
}
@@ -179,6 +185,12 @@ const createTarget = {
buildOptions: { win: ['7z'] },
options: winOptions,
}
case 'win7_setup':
winOptions.artifactName = `\${productName}-v\${version}-win7_${arch}-Setup.\${ext}`
return {
buildOptions: { win: ['nsis'] },
options: winOptions,
}
case 'win7_green':
winOptions.artifactName = `\${productName}-v\${version}-win7_${arch}-green.\${ext}`
return {

View File

@@ -4,6 +4,7 @@ module.exports = {
modules: {
localIdentName: isDev ? '[path][name]__[local]--[hash:base64:5]' : '[hash:base64:5]',
exportLocalsConvention: 'camelCase',
namedExport: false,
},
sourceMap: isDev,
}

View File

@@ -6,9 +6,9 @@ const libDir = path.join(__dirname, 'lib')
const getGzipFiles = async() => {
const names = await fs.promises.readdir(libDir)
for (const name of names) {
if (name.endsWith('.node')) await fs.promises.unlink(path.join(libDir, name))
}
// for (const name of names) {
// if (name.endsWith('.node')) await fs.promises.unlink(path.join(libDir, name))
// }
return names.filter(name => name.endsWith('.gz'))
}

View File

@@ -7,11 +7,12 @@ const baseConfig = require('./webpack.config.base')
// const { dependencies } = require('../../package.json')
const buildConfig = require('../webpack-build-config')
// const buildConfig = require('../webpack-build-config')
module.exports = merge(baseConfig, {
mode: 'production',
devtool: false,
entry: {
main: path.join(__dirname, '../../src/main/index.ts'),
// 'dbService.worker': path.join(__dirname, '../../src/main/worker/dbService/index.ts'),
@@ -44,6 +45,6 @@ module.exports = merge(baseConfig, {
maxAssetSize: 1024 * 1024 * 20,
},
optimization: {
minimize: buildConfig.minimize,
minimize: false,
},
})

View File

@@ -16,6 +16,7 @@ module.exports = merge(baseConfig, {
},
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false',
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
staticPath: `"${path.join(__dirname, '../../src/static').replace(/\\/g, '\\\\')}"`,
}),
],

View File

@@ -15,7 +15,7 @@ const buildConfig = require('../webpack-build-config')
module.exports = merge(baseConfig, {
mode: 'production',
devtool: false,
devtool: 'source-map',
externals: [
// ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)),
],
@@ -35,6 +35,7 @@ module.exports = merge(baseConfig, {
},
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false',
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
}),
],
optimization: {

View File

@@ -14,7 +14,7 @@ const buildConfig = require('../webpack-build-config')
module.exports = merge(baseConfig, {
mode: 'production',
devtool: false,
devtool: 'source-map',
externals: [
// ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)),
],

View File

@@ -5,6 +5,11 @@ const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.config.base')
const gitInfo = {
commit_id: '',
commit_date: '',
}
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'eval-source-map',
@@ -17,6 +22,9 @@ module.exports = merge(baseConfig, {
// ENVIRONMENT: 'process.env',
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false',
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
COMMIT_ID: `"${gitInfo.commit_id}"`,
COMMIT_DATE: `"${gitInfo.commit_date}"`,
staticPath: `"${path.join(__dirname, '../../src/static').replace(/\\/g, '\\\\')}"`,
}),
],

View File

@@ -1,4 +1,5 @@
const path = require('path')
const { execSync } = require('child_process')
const webpack = require('webpack')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
@@ -12,10 +13,29 @@ const buildConfig = require('../webpack-build-config')
// let whiteListedModules = ['vue', 'vue-router', 'vuex', 'vue-i18n']
const gitInfo = {
commit_id: '',
commit_date: '',
}
try {
let isClean = !execSync('git status --porcelain').toString().trim()
if (process.env.BUILD_WIN7) {
console.warn('BUILD_WIN7 is set, skipping git status check.')
console.log('Workspace status:', execSync('git status --porcelain').toString().trim())
isClean = true
}
if (isClean) {
gitInfo.commit_id = execSync('git log -1 --pretty=format:"%H"').toString().trim()
gitInfo.commit_date = execSync('git log -1 --pretty=format:"%ad" --date=iso-strict').toString().trim()
} else if (process.env.IS_CI) {
throw new Error('Working directory is not clean')
}
} catch {}
module.exports = merge(baseConfig, {
mode: 'production',
devtool: false,
devtool: 'source-map',
externals: [
// ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)),
],
@@ -44,6 +64,9 @@ module.exports = merge(baseConfig, {
// ENVIRONMENT: 'process.env',
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false',
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
COMMIT_ID: `"${gitInfo.commit_id}"`,
COMMIT_DATE: `"${gitInfo.commit_date}"`,
}),
],
optimization: {

View File

@@ -16,9 +16,10 @@ const rendererLyricConfig = require('./renderer-lyric/webpack.config.dev')
const rendererScriptConfig = require('./renderer-scripts/webpack.config.dev')
const { Arch } = require('electron-builder')
const replaceLib = require('./build-before-pack')
const treeKill = require('tree-kill')
const { debounce } = require('./utils')
let electronProcess = null
let manualRestart = false
let hotMiddlewareRenderer
let hotMiddlewareRendererLyric
@@ -60,7 +61,9 @@ function startRenderer() {
},
setupMiddlewares(middlewares, devServer) {
devServer.app.use(hotMiddlewareRenderer)
devServer.middleware.waitUntilValid(resolve)
setImmediate(() => {
devServer.middleware.waitUntilValid(resolve)
})
return middlewares
},
@@ -106,7 +109,9 @@ function startRendererLyric() {
},
setupMiddlewares(middlewares, devServer) {
devServer.app.use(hotMiddlewareRenderer)
devServer.middleware.waitUntilValid(resolve)
setImmediate(() => {
devServer.middleware.waitUntilValid(resolve)
})
return middlewares
},
}, compiler)
@@ -132,9 +137,11 @@ function startRendererScripts() {
}
function startMain() {
let firstRun = true
return new Promise((resolve, reject) => {
// mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
// mainConfig.mode = 'development'
const runElectronDelay = debounce(startElectron, 200)
const compiler = webpack(mainConfig)
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
@@ -146,23 +153,19 @@ function startMain() {
compiler.watch({}, (err, stats) => {
if (err) {
console.log(err)
reject(err)
return
}
// logStats('Main', stats)
if (electronProcess && electronProcess.kill) {
manualRestart = true
process.kill(electronProcess.pid)
electronProcess = null
startElectron()
setTimeout(() => {
manualRestart = false
}, 5000)
if (electronProcess) {
electronProcess.removeAllListeners()
treeKill(electronProcess.pid)
}
resolve()
if (firstRun) {
firstRun = false
resolve()
} else runElectronDelay()
})
})
}
@@ -191,7 +194,7 @@ function startElectron() {
})
electronProcess.on('close', () => {
if (!manualRestart) process.exit()
process.exit()
})
}

View File

@@ -31,7 +31,12 @@ exports.mergeCSSLoader = beforeLoader => {
esModule: false,
},
},
'css-loader',
{
loader: 'css-loader',
options: {
esModule: false,
},
},
'postcss-loader',
],
},
@@ -63,3 +68,15 @@ exports.logStats = (proc, data) => {
console.log(log)
}
exports.debounce = (fn, delay = 100) => {
let timer = null
let _args
return (...args) => {
_args = args
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
timer = null
fn(..._args)
}, delay)
}
}

View File

@@ -1,3 +1,3 @@
module.exports = {
minimize: false,
minimize: true,
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 487 KiB

View File

@@ -1,16 +1,19 @@
{\rtf1\adeflang1025\ansi\ansicpg936\uc2\adeff0\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang1033\deflangfe2052\themelang1033\themelangfe2052\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;}
{\f13\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'cb\'ce\'cc\'e5{\*\falt SimSun};}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}
{\f44\fbidi \fnil\fcharset134\fprq2{\*\panose 00000000000000000000}@\'cb\'ce\'cc\'e5;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\f36\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df{\*\falt DengXian};}{\f44\fbidi \fnil\fcharset134\fprq2{\*\panose 00000000000000000000}@\'cb\'ce\'cc\'e5;}
{\f45\fbidi \fnil\fcharset134\fprq2{\*\panose 00000000000000000000}@\'b5\'c8\'cf\'df;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fdbmajor\f31501\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df Light;}{\fhimajor\f31502\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df Light;}
{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fdbminor\f31505\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df{\*\falt DengXian};}{\fhiminor\f31506\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df{\*\falt DengXian};}
{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f45\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f46\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\f48\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f49\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f50\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f51\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\f52\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f53\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f65\fbidi \fmodern\fcharset238\fprq1 Courier New CE;}{\f66\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}
{\f68\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f69\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f70\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);}{\f71\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}
{\f72\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f73\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f177\fbidi \fnil\fcharset0\fprq2 SimSun Western{\*\falt SimSun};}{\f385\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}
{\f386\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f388\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f389\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f392\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}
{\f393\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\f487\fbidi \fnil\fcharset0\fprq2 @SimSun Western;}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f46\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f47\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\f49\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f50\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f51\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f52\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\f53\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f54\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f66\fbidi \fmodern\fcharset238\fprq1 Courier New CE;}{\f67\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}
{\f69\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f70\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f71\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);}{\f72\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}
{\f73\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f74\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f178\fbidi \fnil\fcharset0\fprq2 SimSun Western{\*\falt SimSun};}{\f386\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}
{\f387\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f389\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f390\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f393\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}
{\f394\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\f408\fbidi \fnil\fcharset0\fprq2 DengXian Western{\*\falt DengXian};}{\f406\fbidi \fnil\fcharset238\fprq2 DengXian CE{\*\falt DengXian};}
{\f407\fbidi \fnil\fcharset204\fprq2 DengXian Cyr{\*\falt DengXian};}{\f409\fbidi \fnil\fcharset161\fprq2 DengXian Greek{\*\falt DengXian};}{\f488\fbidi \fnil\fcharset0\fprq2 @SimSun Western;}{\f498\fbidi \fnil\fcharset0\fprq2 @DengXian Western;}
{\f496\fbidi \fnil\fcharset238\fprq2 @DengXian CE;}{\f497\fbidi \fnil\fcharset204\fprq2 @DengXian Cyr;}{\f499\fbidi \fnil\fcharset161\fprq2 @DengXian Greek;}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}
{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31520\fbidi \fnil\fcharset0\fprq2 DengXian Light Western;}{\fdbmajor\f31518\fbidi \fnil\fcharset238\fprq2 DengXian Light CE;}
@@ -38,12 +41,12 @@
\fs21\lang1033\langfe2052\kerning2\loch\f31505\hich\af2\dbch\af31505\cgrid\langnp1033\langfenp2052 \sbasedon0 \snext15 \slink16 \sunhideused Plain Text;}{\*\cs16 \additive \rtlch\fcs1 \af2 \ltrch\fcs0 \loch\f31505\hich\af2 \sbasedon10 \slink15 \slocked
\'b4\'bf\'ce\'c4\'b1\'be \'d7\'d6\'b7\'fb;}{\*\cs17 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf19 \sbasedon10 \sunhideused \styrsid9533173 Hyperlink;}{\*\cs18 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \cf20\chshdng0\chcfpat0\chcbpat21
\sbasedon10 \ssemihidden \sunhideused \styrsid9533173 Unresolved Mention;}{\*\cs19 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf22 \sbasedon10 \ssemihidden \sunhideused \styrsid9533173 FollowedHyperlink;}}{\*\pgptbl {\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}
{\*\rsidtbl \rsid927107\rsid1398824\rsid2109456\rsid3950508\rsid4133944\rsid4355753\rsid9533173\rsid10447395\rsid11081282\rsid12910709\rsid13643782\rsid14384001\rsid15226681}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0
\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author lysyw}{\operator lysyw}{\creatim\yr2019\mo8\dy17\hr10\min22}{\revtim\yr2020\mo4\dy28\hr13\min46}{\version8}{\edmins3}{\nofpages1}{\nofwords135}{\nofchars772}{\nofcharsws906}{\vern1}}
{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl2253\margr2253\margt1440\margb1440\gutter0\ltrsect
{\*\rsidtbl \rsid927107\rsid1398824\rsid2109456\rsid2766548\rsid3758332\rsid3950508\rsid4133944\rsid4355753\rsid9533173\rsid10447395\rsid11081282\rsid12910709\rsid13643782\rsid14384001\rsid14511311\rsid15225067\rsid15226681}{\mmathPr\mmathFont34\mbrkBin0
\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author lysyw}{\operator lysyw}{\creatim\yr2019\mo8\dy17\hr10\min22}{\revtim\yr2025\mo2\dy22\hr15\min34}{\version12}{\edmins5}{\nofpages2}
{\nofwords195}{\nofchars1117}{\nofcharsws1310}{\vern77}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl2253\margr2253\margt1440\margb1440\gutter0\ltrsect
\deftab420\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\formshade\horzdoc\dgmargin\dghspace180\dgvspace156
\dghorigin2253\dgvorigin1440\dghshow0\dgvshow2\jcompress\lnongrid
\viewkind1\viewscale120\splytwnine\ftnlytwnine\htmautsp\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct\asianbrkrule\rsidroot3950508\newtblstyruls
\viewkind1\viewscale100\splytwnine\ftnlytwnine\htmautsp\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct\asianbrkrule\rsidroot3950508\newtblstyruls
\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat {\upr{\*\fchars
!%),.:\'3b>?]\'7d\'a1\'e9\'a1\'a7\'a1\'e3\'a1\'a4\'a1\'a6\'a1\'a5\'a8\'44\'a1\'ac\'a1\'af\'a1\'b1\'a1\'ad\'a1\'eb\'a1\'e4\'a1\'e5?\'a1\'e6\'a1\'c3\'a1\'a2\'a1\'a3\'a1\'a8\'a1\'b5\'a1\'b7\'a1\'b9\'a1\'bb\'a1\'bf\'a1\'b3\'a1\'bd\'a8\'95\'a6\'e1\'a6\'e3\'a6\'e7\'a6\'e5\'a6\'eb\'a9\'77\'a9\'79\'a9\'7b\'a3\'a1\'a3\'a2\'a3\'a5\'a3\'a7\'a3\'a9\'a3\'ac\'a3\'ae\'a3\'ba\'a3\'bb\'a3\'bf\'a3\'dd\'a3\'e0\'a3\'fc\'a3\'fd\'a1\'ab\'a1\'e9
}{\*\ud\uc0{\*\fchars
@@ -53,66 +56,128 @@ $([\'7b{\uc2\u163 \'a1\'ea\u165 \'a3\'a4\'a1\'a4\'a1\'ae\'a1\'b0\'a1\'b4\'a1\'b6
\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery851\footery992\colsx425\endnhere\sectlinegrid312\sectspecifyl\sectrsid2109456\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang
{\pntxta \dbch .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta \dbch )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl6
\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl9
\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}\pard\plain \ltrpar\s15\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid1398824 \rtlch\fcs1 \af2\afs22\alang1025 \ltrch\fcs0
\fs21\lang1033\langfe2052\kerning2\loch\af31505\hich\af2\dbch\af31505\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'cf\'ee\'c4\'bf\'a3\'a8
\'c8\'ed\'bc\'fe\'a3\'a9\'bb\'f9\'d3\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid4133944 \hich\af13\dbch\af13\loch\f13 Apache License 2.0}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \hich\af13\dbch\af13\loch\f13 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'d0\'ed\'bf\'c9\'d6\'a4\'b7\'a2
\'d0\'d0\'a3\'ac\'d4\'da\'ca\'b9\'d3\'c3\'b1\'be\'c8\'ed\'bc\'fe\'c7\'b0\'a3\'ac\'c4\'e3\'a3\'a8\'ca\'b9\'d3\'c3\'d5\'df\'a3\'a9\'d0\'e8\'c7\'a9\'ca\'f0\'b1\'be\'d0\'ad\'d2\'e9\'b2\'c5\'bf\'c9\'bc\'cc\'d0\'f8\'ca\'b9\'d3\'c3\'a3\'ac\'d2\'d4\'cf\'c2
\'d0\'ad\'d2\'e9\'ca\'c7\'b6\'d4\'d3\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \hich\af13\dbch\af13\loch\f13 Apache Li\hich\af13\dbch\af13\loch\f13 cense 2.0 }{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'b5\'c4\'b2\'b9\'b3\'e4\'a3\'ac\'c8\'e7\'d3\'d0\'b3\'e5\'cd\'bb\'a3\'ac\'d2\'d4\'d2\'d4\'cf\'c2\'d0\'ad\'d2\'e9\'ce\'aa\'d7\'bc\'a1\'a3}{\rtlch\fcs1 \af13
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15226681
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid10447395\charrsid1398824
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'b4\'ca\'d3\'ef\'d4\'bc\'b6\'a8\'a3\'ba\'b1\'be\'d0\'ad\'d2\'e9\'d6\'d0\'b5\'c4\'a1\'b0\'b1\'be\'c8\'ed\'bc\'fe\'a1\'b1\'d6\'b8
\'c2\'e5\'d1\'a9\'d2\'f4\'c0\'d6\'d7\'c0\'c3\'e6\'b0\'e6\'cf\'ee\'c4\'bf\'a3\'bb\'a1\'b0\'ca\'b9\'d3\'c3\'d5\'df\'a1\'b1\'d6\'b8\'c7\'a9\'ca\'f0\'b1\'be\'d0\'ad\'d2\'e9\'b5\'c4\'ca\'b9\'d3\'c3\'d5\'df\'a3\'bb\'a1\'b0\'b9\'d9\'b7\'bd\'d2\'f4\'c0\'d6
\'c6\'bd\'cc\'a8\'a1\'b1\'d6\'b8\'b6\'d4\'b1\'be\'c8\'ed\'bc\'fe\'c4\'da\'d6\'c3\'b5\'c4\'b0\'fc\'c0\'a8\'bf\'e1\'ce\'d2\'a1\'a2\'bf\'e1\'b9\'b7\'a1\'a2\'df\'e4\'b9\'be\'b5\'c8\'d2\'f4\'c0\'d6\'d4\'b4\'b5\'c4\'b9\'d9\'b7\'bd\'c6\'bd\'cc\'a8\'cd\'b3
\'b3\'c6\'a3\'bb\'a1\'b0\'b0\'e6\'c8\'a8\'ca\'fd\'be\'dd\'a1\'b1\'d6\'b8\'b0\'fc\'c0\'a8\'b5\'ab\'b2\'bb\'cf\'de\'d3\'da\'cd\'bc\'cf\'f1\'a1\'a2\'d2\'f4\'c6\'b5\'a1\'a2\'c3\'fb\'d7\'d6\'b5\'c8\'d4\'da\'c4\'da\'b5\'c4\'cb\'fb\'c8\'cb\'d3\'b5\'d3\'d0
\'cb\'f9\'ca\'f4\'b0\'e6\'c8\'a8\'b5\'c4\'ca\'fd\'be\'dd\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824
\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}\pard\plain \ltrpar\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid2766548 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0
\fs21\lang1033\langfe2052\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'cf\'ee\'c4\'bf
\'bb\'f9\'d3\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \hich\af13\dbch\af13\loch\f13 Apache License 2.0 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\loch\af13\hich\af13\dbch\f13 \'d0\'ed\'bf\'c9\'d6\'a4\'b7\'a2\'d0\'d0\'a3\'ac\'d2\'d4\'cf\'c2\'d0\'ad\'d2\'e9\'ca\'c7\'b6\'d4\'d3\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\hich\af13\dbch\af13\loch\f13 Apache License 2.0 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b5\'c4\'b2\'b9\'b3\'e4\'a3\'ac\'c8\'e7\'d3\'d0\'b3\'e5\'cd\'bb\'a3\'ac\'d2\'d4
\'d2\'d4\'cf\'c2\'d0\'ad\'d2\'e9\'ce\'aa\'d7\'bc\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'a1\'a2\'b1\'be\'c8\'ed\'bc\'fe\'b5\'c4\'ca\'fd\'be\'dd\'c0\'b4\'d4\'b4\'d4\'ad\'c0\'ed\'ca\'c7
\'b4\'d3\'b8\'f7\'b9\'d9\'b7\'bd\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8\'b5\'c4\'b9\'ab\'bf\'aa\'b7\'fe\'ce\'f1\'c6\'f7\'d6\'d0\'c0\'ad\'c8\'a1\'ca\'fd\'be\'dd\'a3\'ac\'be\'ad\'b9\'fd\'b6\'d4\'ca\'fd\'be\'dd\'bc\'f2\'b5\'a5\'b5\'d8\'c9\'b8\'d1\'a1\'d3\'eb
\'ba\'cf\'b2\'a2\'ba\'f3\'bd\'f8\'d0\'d0\'d5\'b9\'ca\'be\'a3\'ac\'d2\'f2\'b4\'cb\'b1\'be\'c8\'ed\'bc\'fe\'b2\'bb\'b6\'d4\'ca\'fd\'be\'dd\'b5\'c4\'d7\'bc\'c8\'b7\'d0\'d4\'b8\'ba\'d4\'f0\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824
\par \hich\af13\dbch\af13\loch\f13 2}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'a1\'a2\'ca\'b9\'d3\'c3\'b1\'be\'c8\'ed\'bc\'fe\'b5\'c4\'b9\'fd\'b3\'cc\'d6\'d0\'bf\'c9\'c4\'dc
\'bb\'e1\'b2\'fa\'c9\'fa\'b0\'e6\'c8\'a8\'ca\'fd\'be\'dd\'a3\'ac\'b6\'d4\'d3\'da\'d5\'e2\'d0\'a9\'b0\'e6\'c8\'a8\'ca\'fd\'be\'dd\'a3\'ac\'b1\'be\'c8\'ed\'bc\'fe\'b2\'bb\'d3\'b5\'d3\'d0\'cb\'fc\loch\af13\hich\af13\dbch\f13 \'c3\'c7\'b5\'c4\'cb\'f9\'d3\'d0
\'c8\'a8\'a3\'ac\'ce\'aa\'c1\'cb\'b1\'dc\'c3\'e2\'d4\'ec\'b3\'c9\'c7\'d6\'c8\'a8\'a3\'ac\'ca\'b9\'d3\'c3\'d5\'df\'ce\'f1\'b1\'d8\'d4\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824
\hich\af13\dbch\af13\loch\f13 24}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'d0\'a1\'ca\'b1\'c4\'da\'c7\'e5\'b3\'fd\'ca\'b9\'d3\'c3\'b1\'be\'c8\'ed\'bc\'fe\'b5\'c4\'b9\'fd
\'b3\'cc\'d6\'d0\'cb\'f9\'b2\'fa\'c9\'fa\'b5\'c4\'b0\'e6\'c8\'a8\'ca\'fd\'be\'dd\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824
\par \hich\af13\dbch\af13\loch\f13 3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'a1\'a2\'b1\'be\'c8\'ed\'bc\'fe\'c4\'da\'b5\'c4\'b9\'d9\'b7\'bd\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8
\'b1\'f0\'c3\'fb\'ce\'aa\'b1\'be\'c8\'ed\'bc\'fe\'c4\'da\'b6\'d4\'b9\'d9\'b7\'bd\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8\'b5\'c4\'d2\'bb\'b8\'f6\'b3\'c6\'ba\'f4\'a3\'ac\'b2\'bb\'b0\'fc\'ba\'ac\'b6\'f1\'d2\'e2\'a3\'ac\'c8\'e7\'b9\'fb\'b9\'d9\'b7\'bd\'d2\'f4
\'c0\'d6\'c6\'bd\'cc\'a8\'be\'f5\'b5\'c3\'b2\'bb\'cd\'d7\'a3\'ac\'bf\'c9\'c1\'aa\'cf\'b5\'b1\'be\'c8\'ed\'bc\'fe\'b8\'fc\'b8\'c4\'bb\'f2\'d2\'c6\'b3\'fd\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824
\par \hich\af13\dbch\af13\loch\f13 4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'a1\'a2\'b1\'be\'c8\'ed\'bc\'fe\'c4\'da\'ca\'b9\'d3\'c3\'b5\'c4\'b2\'bf\'b7\'d6\'b0\'fc\'c0\'a8
\'b5\'ab\'b2\'bb\'cf\'de\'d3\'da\'d7\'d6\'cc\'e5\'a1\'a2\'cd\'bc\'c6\'ac\'b5\'c8\'d7\'ca\'d4\'b4\'c0\'b4\'d4\'b4\'d3\'da\'bb\'a5\'c1\'aa\'cd\'f8\'a3\'ac\'c8\'e7\'b9\'fb\'b3\'f6\'cf\'d6\'c7\'d6\'c8\'a8\'bf\'c9\'c1\'aa\'cf\'b5\'b1\'be\'c8\'ed\'bc\'fe
\'d2\'c6\'b3\'fd\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824
\par \hich\af13\dbch\af13\loch\f13 5}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'a1\'a2\'d3\'c9\'d3\'da\'ca\'b9\'d3\'c3\'b1\'be\'c8\'ed\'bc\'fe\'b2\'fa\'c9\'fa\'b5\'c4\'b0\'fc
\'c0\'a8\'d3\'c9\'d3\'da\'b1\'be\'d0\'ad\'d2\'e9\'bb\'f2\'d3\'c9\'d3\'da\'ca\'b9\'d3\'c3\'bb\'f2\'ce\'de\'b7\'a8\'ca\'b9\'d3\'c3\'b1\'be\'c8\'ed\'bc\'fe\'b6\'f8\'d2\'fd\'c6\'f0\'b5\'c4\'c8\'ce\'ba\'ce\'d0\'d4\'d6\'ca\'b5\'c4\'c8\'ce\'ba\'ce\'d6\'b1
\'bd\'d3\'a1\'a2\'bc\'e4\'bd\'d3\'a1\'a2\'cc\'d8\'ca\'e2\'a1\'a2\'c5\'bc\'c8\'bb\'bb\'f2\'bd\'e1\'b9\'fb\'d0\'d4\'cb\'f0\'ba\'a6\'a3\'a8\'b0\'fc\'c0\'a8\'b5\'ab\'b2\'bb\'cf\'de\'d3\'da\'d2\'f2\'c9\'cc\'d3\'fe\'cb\'f0\'ca\'a7\'a1\'a2\'cd\'a3\'b9\'a4
\'a1\'a2\'bc\'c6\'cb\'e3\'bb\'fa\'b9\'ca\'d5\'cf\'bb\'f2\'b9\'ca\'d5\'cf\'d2\'fd\'c6\'f0\'b5\'c4\'cb\'f0\'ba\'a6\'c5\'e2\'b3\'a5\'a3\'ac\'bb\'f2\'c8\'ce\'ba\'ce\'bc\'b0\loch\af13\hich\af13\dbch\f13 \'cb\'f9\'d3\'d0\'c6\'e4\'cb\'fb\'c9\'cc\'d2\'b5\'cb\'f0
\'ba\'a6\'bb\'f2\'cb\'f0\'ca\'a7\'a3\'a9\'d3\'c9\'ca\'b9\'d3\'c3\'d5\'df\'b8\'ba\'d4\'f0\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824
\par \hich\af13\dbch\af13\loch\f13 6}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13 \'a1\'a2\'b1\'be\'cf\'ee\'c4\'bf\'cd\'ea\'c8\'ab\'c3\'e2\'b7\'d1\'a3\'ac\'c7\'d2\'bf\'aa\'d4\'b4
\'b7\'a2\'b2\'bc\'d3\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \hich\af13\dbch\af13\loch\f13 GitHub }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824
\loch\af13\hich\af13\dbch\f13 \'c3\'e6\'cf\'f2\'c8\'ab\'ca\'c0\'bd\'e7\'c8\'cb\'d3\'c3\'d7\'f7\'b6\'d4\'bc\'bc\'ca\'f5\'b5\'c4\'d1\'a7\'cf\'b0\'bd\'bb\'c1\'f7\'a3\'ac\'b1\'be\'c8\'ed\'bc\'fe\'b2\'bb\'b6\'d4\'cf\'ee\'c4\'bf\'c4\'da\'b5\'c4\'bc\'bc\'ca\'f5
\'bf\'c9\'c4\'dc\'b4\'e6\'d4\'da\'ce\'a5\'b7\'b4\'b5\'b1\'b5\'d8\'b7\'a8\'c2\'c9\'b7\'a8\'b9\'e6\'b5\'c4\'d0\'d0\'ce\'aa\'d7\'f7\'b1\'a3\'d6\'a4\'a3\'ac\'bd\'fb\'d6\'b9\'d4\'da\'ce\'a5\'b7\'b4\'b5\'b1\'b5\'d8\'b7\'a8\'c2\'c9\'b7\'a8\'b9\'e6\'b5\'c4
\'c7\'e9\'bf\'f6\'cf\'c2\'ca\'b9\'d3\'c3\'b1\'be\'c8\'ed\'bc\'fe\'a3\'ac\'b6\'d4\'d3\'da\'ca\'b9\'d3\'c3\'d5\'df\'d4\'da\'c3\'f7\'d6\'aa\'bb\'f2\'b2\'bb\'d6\'aa\'b5\'b1\'b5\'d8\'b7\'a8\'c2\'c9\'b7\'a8\'b9\'e6\'b2\'bb\'d4\'ca\'d0\'ed\'b5\'c4\'c7\'e9
\'bf\'f6\'cf\'c2\'ca\'b9\'d3\'c3\'b1\'be\'c8\'ed\'bc\'fe\'cb\'f9\'d4\'ec\'b3\'c9\'b5\'c4\'c8\'ce\'ba\'ce\'ce\'a5\'b7\'a8\'ce\'a5\'b9\'e6\'d0\'d0\'ce\'aa\'d3\'c9\'ca\'b9\'d3\'c3\'d5\'df\'b3\'d0\'b5\'a3\'a3\'ac\'b1\'be\'c8\'ed\'bc\'fe\'b2\'bb\'b3\'d0
\'b5\'a3\'d3\'c9\'b4\'cb\'d4\'ec\'b3\'c9\'b5\'c4\'c8\'ce\'ba\'ce\'d6\'b1\'bd\'d3\'a1\'a2\'bc\'e4\'bd\'d3\'a1\'a2\'cc\'d8\'ca\'e2\'a1\'a2\'c5\'bc\'c8\'bb\'bb\'f2\'bd\'e1\'b9\'fb\'d0\'d4\'d4\'f0\'c8\'ce\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid1398824
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15226681
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid10447395\charrsid1398824
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \hich\af13\dbch\af13\loch\f13 * }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13
\'c8\'f4\'d0\'ad\'d2\'e9\'b8\'fc\'d0\'c2\'a3\'ac\'cb\'a1\'b2\'bb\'c1\'ed\'d0\'d0\'cd\'a8\'d6\'aa\'a3\'ac\'bf\'c9\'b5\'bd\'bf\'aa\'d4\'b4\'b5\'d8\'d6\'b7\'b2\'e9\'bf\'b4\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid15226681
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \hich\af13\dbch\af13\loch\f13 * }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13
\'b1\'be\'c8\'ed\'bc\'fe\'b5\'c4\'b3\'f5\'d6\'d4\'ca\'c7\'b0\'ef\'d6\'fa\'b9\'d9\'b7\'bd\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8\'bc\'f2\'bb\'af\'ca\'fd\'be\'dd\'ba\'f3\'b4\'fa\'ce\'aa\'d5\'b9\'ca\'be\'a3\'ac\'b0\'ef\'d6\'fa\'ca\'b9\'d3\'c3\'d5\'df\'b8\'f9
\'be\'dd\'b8\'e8\'c7\'fa\loch\af13\hich\af13\dbch\f13 \'c3\'fb\'a1\'a2\'d2\'d5\'ca\'f5\'bc\'d2\'b5\'c8\'b9\'d8\'bc\'fc\'d7\'d6\'bf\'ec\'cb\'d9\'b5\'d8\'b6\'a8\'ce\'bb\'cb\'f9\'d0\'e8\'c4\'da\'c8\'dd\'cb\'f9\'d4\'da\'b5\'c4\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8
\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid15226681
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \hich\af13\dbch\af13\loch\f13 * }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid1398824\charrsid1398824 \loch\af13\hich\af13\dbch\f13
\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8\'b2\'bb\'d2\'d7\'a3\'ac\'bd\'a8\'d2\'e9\'b5\'bd\'b6\'d4\'d3\'a6\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8\'d6\'a7\'b3\'d6\'d5\'fd\'b0\'e6\'d7\'ca\'d4\'b4\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid12910709
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid9533173\charrsid15226681
\par }\pard \ltrpar\s15\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12910709 {\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709 \hich\af13\dbch\af13\loch\f13 By: }{
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709 \loch\af13\hich\af13\dbch\f13 \'c2\'e4\'d1\'a9\'ce\'de\'ba\'db}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b4\'ca\'d3\'ef\'d4\'bc\'b6\'a8\'a3\'ba\'b1\'be\'d0\'ad\'d2\'e9\'d6\'d0\'b5\'c4\'a1\'b0\'b1\'be\'cf\'ee\'c4\'bf\'a1\'b1\'d6\'b8}{
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \hich\af13\dbch\af13\loch\f13 LX Music }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\loch\af13\hich\af13\dbch\f13 \'d7\'c0\'c3\'e6\'b0\'e6\'cf\'ee\'c4\'bf\'a3\'bb}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'ca\'b9\'d3\'c3\'d5\'df}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8221\'a1\'b1}{\rtlch\fcs1 \af13
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'d6\'b8\'c7\'a9\'ca\'f0\'b1\'be\'d0\'ad\'d2\'e9\'b5\'c4\'ca\'b9\'d3\'c3\'d5\'df\'a3\'bb}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b9\'d9\'b7\'bd\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8}{
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'d6\'b8\'b6\'d4\'b1\'be
\'cf\'ee\'c4\'bf\'c4\'da\'d6\'c3\'b5\'c4\'b0\'fc\'c0\'a8\'bf\'e1\'ce\'d2\'a1\'a2\'bf\'e1\'b9\'b7\'a1\'a2\'df\'e4\'b9\'be\'b5\'c8\'d2\'f4\'c0\'d6\'d4\'b4\'b5\'c4\'b9\'d9\'b7\'bd\'c6\'bd\'cc\'a8\'cd\'b3\'b3\'c6\'a3\'bb}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b0\'e6\'c8\'a8\'ca\'fd\'be\'dd}{\rtlch\fcs1 \af13
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'d6\'b8\'b0\'fc\'c0\'a8\'b5\'ab\'b2\'bb
\'cf\'de\'d3\'da\'cd\'bc\'cf\'f1\'a1\'a2\'d2\'f4\'c6\'b5\'a1\'a2\'c3\'fb\'d7\'d6\'b5\'c8\'d4\'da\'c4\'da\'b5\'c4\'cb\'fb\'c8\'cb\'d3\'b5\'d3\'d0\'cb\'f9\'ca\'f4\'b0\'e6\'c8\'a8\'b5\'c4\'ca\'fd\'be\'dd\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'d2\'bb\'a1\'a2\'ca\'fd\'be\'dd\'c0\'b4\'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 1.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'cf\'ee\'c4\'bf\'b5\'c4\'b8\'f7\'b9\'d9\'b7\'bd\'c6\'bd\'cc\'a8\'d4\'da\'cf\'df\'ca\'fd
\'be\'dd\'c0\'b4\'d4\'b4\'d4\'ad\'c0\'ed\'ca\'c7\'b4\'d3\'c6\'e4\'b9\'ab\'bf\'aa\'b7\'fe\'ce\'f1\'c6\'f7\'d6\'d0\'c0\'ad\'c8\'a1\'ca\'fd\'be\'dd\'a3\'a8\'d3\'eb\'ce\'b4\'b5\'c7\'c2\'bc\'d7\'b4\'cc\'ac\'d4\'da\'b9\'d9\'b7\'bd\'c6\'bd\'cc\'a8}{\rtlch\fcs1
\af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \hich\af13\dbch\af13\loch\f13 APP }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'bb\'f1\'c8\'a1
\'b5\'c4\'ca\'fd\'be\'dd\'cf\'e0\'cd\'ac\'a3\'a9\'a3\'ac\'be\'ad\'b9\'fd\'b6\'d4\'ca\'fd\'be\'dd\'bc\'f2\'b5\'a5\'b5\'d8\'c9\'b8\'d1\'a1\'d3\'eb\'ba\'cf\'b2\'a2\'ba\'f3\'bd\'f8\'d0\'d0\'d5\'b9\'ca\'be\'a3\'ac\'d2\'f2\'b4\'cb\'b1\'be\'cf\'ee\'c4\'bf
\'b2\'bb\'b6\'d4\'ca\'fd\'be\'dd\'b5\'c4\'ba\'cf\'b7\'a8\'d0\'d4\'a1\'a2\'d7\'bc\'c8\'b7\'d0\'d4\'b8\'ba\'d4\'f0\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 1.2 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'cf\'ee\'c4\'bf\'b1\'be\'c9\'ed\'c3\'bb\'d3\'d0\'bb\'f1\'c8\'a1\'c4\'b3\'b8\'f6\'d2\'f4
\'c6\'b5\'ca\'fd\'be\'dd\'b5\'c4\'c4\'dc\'c1\'a6\'a3\'ac\'b1\'be\'cf\'ee\'c4\'bf\'ca\'b9\'d3\'c3\'b5\'c4\'d4\'da\'cf\'df\'d2\'f4\'c6\'b5\'ca\'fd\'be\'dd\'c0\'b4\'d4\'b4\'c0\'b4\'d7\'d4\'c8\'ed\'bc\'fe\'c9\'e8\'d6\'c3\'c4\'da}{\rtlch\fcs1 \af13
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'d7\'d4\'b6\'a8\'d2\'e5\'d4\'b4}{
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'c9\'e8\'d6\'c3\'cb\'f9
\'d1\'a1\'d4\'f1\'b5\'c4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\loch\af13\hich\af13\dbch\f13 \'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\loch\af13\hich\af13\dbch\f13 \'b7\'b5\'bb\'d8\'b5\'c4\'d4\'da\'cf\'df\'c1\'b4\'bd\'d3\'a1\'a3\'c0\'fd\'c8\'e7\'b2\'a5\'b7\'c5\'c4\'b3\'ca\'d7\'b8\'e8\'a3\'ac\'b1\'be\'cf\'ee\'c4\'bf\'cb\'f9\'d7\'f6\'b5\'c4\'d6\'bb\'ca\'c7\'bd\'ab\'cf\'a3\'cd\'fb\'b2\'a5
\'b7\'c5\'b5\'c4\'b8\'e8\'c7\'fa\'c3\'fb\'a1\'a2\'d2\'d5\'ca\'f5\'bc\'d2\'b5\'c8\'d0\'c5\'cf\'a2\'b4\'ab\'b5\'dd\'b8\'f8}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8220\'a1\'b0}{\rtlch\fcs1 \af13
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'a3\'ac\'c8\'f4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b7\'b5\'bb\'d8\'c1\'cb\'d2\'bb\'b8\'f6\'c1\'b4\'bd\'d3\'a3\'ac\'d4\'f2\'b1\'be\'cf\'ee\'c4\'bf\'bd\'ab\'c8\'cf\'ce\'aa\'d5\'e2\'be\'cd\'ca\'c7\'b8\'c3\'b8\'e8
\'c7\'fa\'b5\'c4\'d2\'f4\'c6\'b5\'ca\'fd\'be\'dd\'b6\'f8\'bd\'f8\'d0\'d0\'ca\'b9\'d3\'c3\'a3\'ac\'d6\'c1\'d3\'da\'d5\'e2\'ca\'c7\'b2\'bb\'ca\'c7\'d5\'fd\'c8\'b7\'b5\'c4\'d2\'f4\'c6\'b5\'ca\'fd\'be\'dd\'b1\'be\'cf\'ee\'c4\'bf\'ce\'de\'b7\'a8\'d0\'a3
\'d1\'e9\'c6\'e4\'d7\'bc\'c8\'b7\'d0\'d4\'a3\'ac\'cb\'f9\'d2\'d4\'ca\'b9\'d3\'c3\'b1\'be\'cf\'ee\'c4\'bf\'b5\'c4\'b9\'fd\'b3\'cc\'d6\'d0\'bf\'c9\'c4\'dc\'bb\'e1\'b3\'f6\'cf\'d6\'cf\'a3\'cd\'fb\'b2\'a5\'b7\'c5\'b5\'c4\'d2\'f4\'c6\'b5\'d3\'eb\'ca\'b5
\'bc\'ca\'b2\'a5\'b7\'c5\'b5\'c4\'d2\'f4\'c6\'b5\'b2\'bb\'b6\'d4\'d3\'a6\'bb\'f2\'d5\'df\'ce\'de\'b7\'a8\'b2\'a5\'b7\'c5\'b5\'c4\'ce\'ca\'cc\'e2\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 1.3 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'cf\'ee\'c4\'bf\'b5\'c4\'b7\'c7\'b9\'d9\'b7\'bd\'c6\'bd\'cc\'a8\'ca\'fd\'be\'dd\'a3\'a8
\'c0\'fd\'c8\'e7}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13
\'ce\'d2\'b5\'c4\'c1\'d0\'b1\'ed}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\loch\af13\hich\af13\dbch\f13 \'c4\'da\'c1\'d0\'b1\'ed\'a3\'a9\'c0\'b4\'d7\'d4\'ca\'b9\'d3\'c3\'d5\'df\'b1\'be\'b5\'d8\'cf\'b5\'cd\'b3\'bb\'f2\'d5\'df\'ca\'b9\'d3\'c3\'d5\'df\'c1\'ac\'bd\'d3\'b5\'c4\'cd\'ac\'b2\'bd\'b7\'fe\'ce\'f1\'a3\'ac\'b1\'be\'cf\'ee
\'c4\'bf\'b2\'bb\'b6\'d4\'d5\'e2\'d0\'a9\'ca\'fd\'be\'dd\'b5\'c4\'ba\'cf\'b7\'a8\'d0\'d4\'a1\'a2\'d7\'bc\'c8\'b7\'d0\'d4\'b8\'ba\'d4\'f0\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b6\'fe\'a1\'a2\'b0\'e6\'c8\'a8\'ca\'fd\'be\'dd}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 2\hich\af13\dbch\af13\loch\f13 .1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'ca\'b9\'d3\'c3\'b1\'be\'cf\'ee\'c4\'bf\'b5\'c4\'b9\'fd\'b3\'cc
\'d6\'d0\'bf\'c9\'c4\'dc\'bb\'e1\'b2\'fa\'c9\'fa\'b0\'e6\'c8\'a8\'ca\'fd\'be\'dd\'a1\'a3\'b6\'d4\'d3\'da\'d5\'e2\'d0\'a9\'b0\'e6\'c8\'a8\'ca\'fd\'be\'dd\'a3\'ac\'b1\'be\'cf\'ee\'c4\'bf\'b2\'bb\'d3\'b5\'d3\'d0\'cb\'fc\'c3\'c7\'b5\'c4\'cb\'f9\'d3\'d0
\'c8\'a8\'a1\'a3\'ce\'aa\'c1\'cb\'b1\'dc\'c3\'e2\'c7\'d6\'c8\'a8\'a3\'ac\'ca\'b9\'d3\'c3\'d5\'df\'ce\'f1\'b1\'d8\'d4\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \hich\af13\dbch\af13\loch\f13 **24 }{
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'d0\'a1\'ca\'b1\'c4\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\hich\af13\dbch\af13\loch\f13 ** }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'c7\'e5\'b3\'fd\'ca\'b9\'d3\'c3\'b1\'be\'cf\'ee\'c4\'bf\'b5\'c4\'b9\'fd\'b3\'cc\'d6\'d0\'cb\'f9
\'b2\'fa\'c9\'fa\'b5\'c4\'b0\'e6\'c8\'a8\'ca\'fd\'be\'dd\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'c8\'fd\'a1\'a2\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8\'b1\'f0\'c3\'fb}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 3.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'cf\'ee\'c4\'bf\'c4\'da\'b5\'c4\'b9\'d9\'b7\'bd\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8\'b1\'f0
\'c3\'fb\'ce\'aa\'b1\'be\'cf\'ee\'c4\'bf\'c4\'da\'b6\'d4\'b9\'d9\'b7\'bd\'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8\'b5\'c4\'d2\'bb\'b8\'f6\'b3\'c6\'ba\'f4\'a3\'ac\'b2\'bb\'b0\'fc\'ba\'ac\'b6\'f1\'d2\'e2\'a1\'a3\'c8\'e7\'b9\'fb\'b9\'d9\'b7\'bd\'d2\'f4\'c0\'d6
\'c6\'bd\'cc\'a8\'be\'f5\'b5\'c3\'b2\'bb\'cd\'d7\'a3\'ac\'bf\'c9\'c1\'aa\'cf\'b5\'b1\'be\'cf\'ee\'c4\'bf\'b8\'fc\'b8\'c4\'bb\'f2\'d2\'c6\'b3\'fd\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'cb\'c4\'a1\'a2\'d7\'ca\'d4\'b4\'ca\'b9\'d3\'c3}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 4.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'cf\'ee\'c4\'bf\'c4\'da\'ca\'b9\'d3\'c3\'b5\'c4\'b2\'bf\'b7\'d6\'b0\'fc\'c0\'a8\'b5\'ab
\'b2\'bb\'cf\'de\'d3\'da\'d7\'d6\'cc\'e5\'a1\'a2\'cd\'bc\'c6\'ac\'b5\'c8\'d7\'ca\'d4\'b4\'c0\'b4\'d4\'b4\'d3\'da\'bb\'a5\'c1\'aa\'cd\'f8\'a1\'a3\'c8\'e7\'b9\'fb\'b3\'f6\'cf\'d6\'c7\'d6\'c8\'a8\'bf\'c9\'c1\'aa\'cf\'b5\'b1\'be\'cf\'ee\'c4\'bf\'d2\'c6
\'b3\'fd\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'ce\'e5\'a1\'a2\'c3\'e2\'d4\'f0\'c9\'f9\'c3\'f7}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 5.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'d3\'c9\'d3\'da\'ca\'b9\'d3\'c3\'b1\'be\'cf\'ee\'c4\'bf\'b2\'fa\'c9\'fa\'b5\'c4\'b0\'fc\'c0\'a8
\'d3\'c9\'d3\'da\'b1\'be\'d0\'ad\'d2\'e9\'bb\'f2\'d3\'c9\'d3\'da\'ca\'b9\'d3\'c3\'bb\'f2\'ce\'de\'b7\'a8\'ca\'b9\'d3\'c3\'b1\'be\'cf\'ee\'c4\'bf\'b6\'f8\'d2\'fd\'c6\'f0\'b5\'c4\'c8\'ce\'ba\'ce\'d0\'d4\'d6\'ca\'b5\'c4\'c8\'ce\'ba\'ce\'d6\'b1\'bd\'d3
\'a1\'a2\'bc\'e4\'bd\'d3\'a1\'a2\'cc\'d8\'ca\'e2\'a1\'a2\'c5\'bc\'c8\'bb\'bb\'f2\'bd\'e1\'b9\'fb\'d0\'d4\'cb\'f0\'ba\'a6\'a3\'a8\'b0\'fc\'c0\'a8\'b5\'ab\'b2\'bb\'cf\'de\'d3\'da\'d2\'f2\'c9\'cc\'d3\'fe\'cb\'f0\'ca\'a7\'a1\'a2\'cd\'a3\'b9\'a4\'a1\'a2
\'bc\'c6\'cb\'e3\'bb\'fa\'b9\'ca\'d5\'cf\'bb\'f2\'b9\'ca\'d5\'cf\'d2\'fd\'c6\'f0\'b5\'c4\'cb\'f0\'ba\'a6\'c5\'e2\'b3\'a5\'a3\'ac\'bb\'f2\'c8\'ce\'ba\'ce\'bc\'b0\'cb\'f9\'d3\'d0\'c6\'e4\'cb\'fb\'c9\'cc\'d2\'b5\'cb\'f0\'ba\'a6\'bb\'f2\'cb\'f0\'ca\'a7
\'a3\'a9\'d3\'c9\'ca\'b9\'d3\'c3\'d5\'df\'b8\'ba\'d4\'f0\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'c1\'f9\'a1\'a2\'ca\'b9\'d3\'c3\'cf\'de\'d6\'c6}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 6.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'cf\'ee\'c4\'bf\'cd\'ea\'c8\'ab\'c3\'e2\'b7\'d1\'a3\'ac\'c7\'d2\'bf\'aa\'d4\'b4\'b7\'a2
\'b2\'bc\'d3\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \hich\af13\dbch\af13\loch\f13 GitHub }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\loch\af13\hich\af13\dbch\f13 \'c3\'e6\'cf\'f2\'c8\'ab\'ca\'c0\'bd\'e7\'c8\'cb\'d3\'c3\'d7\'f7\'b6\'d4\'bc\'bc\'ca\'f5\'b5\'c4\'d1\'a7\'cf\'b0\'bd\'bb\'c1\'f7\'a1\'a3\'b1\'be\'cf\'ee\'c4\'bf\'b2\'bb\'b6\'d4\'cf\'ee\'c4\'bf\'c4\'da\'b5\'c4\'bc\'bc\'ca\'f5
\'bf\'c9\'c4\'dc\'b4\'e6\'d4\'da\'ce\'a5\'b7\'b4\'b5\'b1\'b5\'d8\'b7\'a8\'c2\'c9\'b7\'a8\'b9\'e6\'b5\'c4\'d0\'d0\'ce\'aa\'d7\'f7\'b1\'a3\'d6\'a4\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 6.2 **}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'bd\'fb\'d6\'b9\'d4\'da\'ce\'a5\'b7\'b4\'b5\'b1\'b5\'d8\'b7\'a8\'c2\'c9\'b7\'a8\'b9\'e6
\'b5\'c4\'c7\'e9\'bf\'f6\'cf\'c2\'ca\'b9\'d3\'c3\'b1\'be\'cf\'ee\'c4\'bf\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \hich\af13\dbch\af13\loch\f13 ** }{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b6\'d4\'d3\'da\'ca\'b9\'d3\'c3\'d5\'df\'d4\'da\'c3\'f7\'d6\'aa\'bb\'f2\'b2\'bb\'d6\'aa\'b5\'b1\'b5\'d8\'b7\'a8\'c2\'c9\'b7\'a8\'b9\'e6\'b2\'bb\'d4\'ca\'d0\'ed
\'b5\'c4\'c7\'e9\'bf\'f6\'cf\'c2\'ca\'b9\'d3\'c3\'b1\'be\'cf\'ee\'c4\'bf\'cb\'f9\'d4\'ec\'b3\'c9\'b5\'c4\'c8\'ce\'ba\'ce\'ce\'a5\'b7\'a8\'ce\'a5\'b9\'e6\'d0\'d0\'ce\'aa\'d3\'c9\'ca\'b9\'d3\'c3\'d5\'df\'b3\'d0\'b5\'a3\'a3\'ac\'b1\'be\'cf\'ee\'c4\'bf
\'b2\'bb\'b3\'d0\'b5\'a3\'d3\'c9\'b4\'cb\'d4\'ec\'b3\'c9\'b5\'c4\'c8\'ce\'ba\'ce\'d6\'b1\'bd\'d3\'a1\'a2\'bc\'e4\'bd\'d3\'a1\'a2\'cc\'d8\'ca\'e2\'a1\'a2\'c5\'bc\'c8\'bb\'bb\'f2\'bd\'e1\'b9\'fb\'d0\'d4\'d4\'f0\'c8\'ce\'a1\'a3}{\rtlch\fcs1 \af13
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'c6\'df\'a1\'a2\'b0\'e6\'c8\'a8\'b1\'a3\'bb\'a4}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 7.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'d2\'f4\'c0\'d6\'c6\'bd\'cc\'a8\'b2\'bb\'d2\'d7\'a3\'ac\'c7\'eb\'d7\'f0\'d6\'d8\'b0\'e6\'c8\'a8
\'a3\'ac\'d6\'a7\'b3\'d6\'d5\'fd\'b0\'e6\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b0\'cb\'a1\'a2\'b7\'c7\'c9\'cc\'d2\'b5\'d0\'d4\'d6\'ca}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 8.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'cf\'ee\'c4\'bf\'bd\'f6\'d3\'c3\'d3\'da\'b6\'d4\'bc\'bc\'ca\'f5\'bf\'c9\'d0\'d0\'d0\'d4
\'b5\'c4\'cc\'bd\'cb\'f7\'bc\'b0\'d1\'d0\'be\'bf\'a3\'ac\'b2\'bb\'bd\'d3\'ca\'dc\'c8\'ce\'ba\'ce\'c9\'cc\'d2\'b5\'a3\'a8\'b0\'fc\'c0\'a8\'b5\'ab\'b2\'bb\'cf\'de\'d3\'da\'b9\'e3\'b8\'e6\'b5\'c8\'a3\'a9\'ba\'cf\'d7\'f7\'bc\'b0\'be\'e8\'d4\'f9\'a1\'a3}{
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'be\'c5\'a1\'a2\'bd\'d3\'ca\'dc\'d0\'ad\'d2\'e9}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 9.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'c8\'f4\'c4\'e3\'ca\'b9\'d3\'c3\'c1\'cb\'b1\'be\'cf\'ee\'c4\'bf\'a3\'ac\'bc\'b4\'b4\'fa\'b1\'ed
\'c4\'e3\'bd\'d3\'ca\'dc\'b1\'be\'d0\'ad\'d2\'e9\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 * }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'c8\'f4\'d0\'ad\'d2\'e9\'b8\'fc\'d0\'c2\'a3\'ac\'cb\'a1\'b2\'bb\'c1\'ed\'d0\'d0\'cd\'a8\'d6\'aa
\'a3\'ac\'bf\'c9\'b5\'bd\'bf\'aa\'d4\'b4\'b5\'d8\'d6\'b7\'b2\'e9\'bf\'b4\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548
\par
\par \hich\af13\dbch\af13\loch\f13 By: }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid2766548\charrsid2766548 \loch\af13\hich\af13\dbch\f13 \'c2\'e4\'d1\'a9\'ce\'de\'ba\'db}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid12910709\charrsid2766548
\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
@@ -255,8 +320,8 @@ fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000b026
8d59201dd601feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000d096
bf3efc84db01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000105000000000000}}

View File

@@ -1,18 +1,52 @@
This project (software) is issued based on the Apache License 2.0 license. Before using this software, you (users) need to sign this agreement before you can continue to use it. The following agreement is a supplement to the Apache License 2.0. In case of conflict, use the following agreement Shall prevail.
This project is issued based on the Apache License 2.0 license. The following agreements are supplemental to the Apache License 2.0. In the event of a conflict, the following agreements shall prevail.
DEFINITIONS:
Terms agreed: "this software" in this agreement refers to the Luo Xue Music desktop version of the project; "user" refers to the user who signed this agreement; "official music platform" refers to the built-in software including Kuwo, Kugou, Migu The official platforms of music sources, etc. are collectively referred to; "copyright data" refers to data including but not limited to images, audio, names, etc. that others own the copyright.
"This Project" refers to the LX Music Desktop Edition (aka lx-music-desktop) project. "User" refers to the user who signed this agreement. "Official Music Streaming Service" refers to collectively the official streaming service corresponding to the music source. "Copyrighted Data" refers to data including, but not limited to, pictures, audio, names, etc., for which others own the copyright.
1. The principle of the data source of this software is to pull data from the public servers of each official music platform. After simply filtering and merging the data for display, this software is not responsible for the accuracy of the data.
2. Copyright data may be generated during the use of this software. For this copyright data, the software does not own their ownership. In order to avoid infringement, users must clear the copyright generated during the use of this software within 24 hours data.
3. The alias of the official music platform in this software is a name for the official music platform in the software. It does not contain maliciousness. If the official music platform feels inappropriate, you can contact the software to change or remove it.
4. The parts used in this software include but are not limited to fonts, pictures and other resources from the Internet. If there is infringement, you can contact this software to remove it.
5. Any direct, indirect, special, accidental or consequential damage of any nature arising from the use of this software, including this agreement or the use or inability to use this software (including but not limited to due to loss of goodwill, work stoppage, Compensation for computer failure or damage caused by the failure, or any and all other commercial damage or loss) is the responsibility of the user.
6. This project is completely free, and the open source is published on GitHub. It is used by people all over the world to learn and exchange technology. This software does not guarantee that the technology in the project may violate local laws and regulations. It is prohibited to violate local laws and regulations. Using this software, the user shall be responsible for any violations caused by the user's use of the software without knowing or not permitted by local laws and regulations. The software shall not be responsible for any direct, indirect, special, accidental Or consequential responsibility.
1. Data Source
1.1 The principle of the online data sources of the various official music streaming services of this project is to draw data from its open servers (the same as the data obtained in the unlogged-in state in the official music streaming service app). After simply screening and merging the data, this project is responsible for the legitimacy and accuracy of the data.
* If the agreement is updated without prior notice, you can check it at the open source address.
* The original intention of this software is to help the official music platform to simplify the data generation for display, and to help users quickly locate the music platform where the desired content is based on the song title, artist and other keywords.
* Music platform is not easy, it is recommended to support genuine resources to the corresponding music platform.
1.2 The ability of this project itself does not obtain certain audio data. The online audio data source used in this project comes from the online link provided by the "API" selected in the software settings. For example, when playing a song, this project only transmits information such as the song title, artist, and other information to the "API". If the "API" returns a link, this project will think that this is the audio data of the song for use, but as for whether this is the correct audio data, this project cannot verify its accuracy. So, in the process of using this project, there may be a problem that the audio you want to play does not correspond to the audio you actually play, or you cannot play it.
1.3 Data in this project other than official music streaming service (such as lists in "Your Library") comes from the user's local system or a synchronization service that the user connected to. This project is not responsible for the legality and accuracy of this data.
2. Copyrighted Data
2.1 During the process of using this project, copyrighted data may be generated. For this copyrighted data, this project does not have their ownership. In order to avoid infringement, users must remove copyrighted data generated during the process of using this project ** within 24 hours **.
3. Alias of Music Streaming Service
3.1 The official music streaming service alias within this project is a term used within this project to refer to the official music streaming service and does not contain malicious intent. If the operator of the official music streaming service feels it is inappropriate, you can contact this project to request changes or removals.
4. Use of Resources
4.1 The parts used in this project include, but are not limited to, fonts, pictures, and other resources from the Internet. If infringement occurs, you can contact this project.
5. Disclaimer
5.1 The use of this project includes any direct, indirect, special, accidental, or result damage due to any nature caused by this agreement or use or inability to use this item (including but not limited to the loss of goodwill, stop work, computer, computer Damage compensation caused by faults or any or all other commercial damage or losses) is the responsibility of users.
6. Use Restrictions
6.1 This project is completely free and open source and is published on GitHub for the learning exchanges of technology for people all over the world. This project does not guarantee that the technology in this project may violate local laws and regulations.
6.2 ** This project is prohibited in violation of local laws and regulations. ** The user is solely responsible for any violation of law caused by the use of this project that the user knows or does not know is not permitted by local law and regulations.
7. Copyright Protection
7.1 The operation of a music streaming service is not an easy task. Please respect copyrights and support the genuine.
8. Non-commercial Nature
8.1 This project is only used for exploring and research on technical feasibility and does not accept any business (including but not limited to advertising, etc.) cooperation and donations.
9. Accepting Agreement
9.1 If you use this project, it will represent you accept this agreement.
* If the agreement is updated, you will not be notified separately. You can check it out by visiting the project address.
* The content of this agreement is translated from the Chinese version of the agreement.
By: lyswhut

View File

@@ -1,18 +1,49 @@
本项目(软件)基于Apache License 2.0 许可证发行,在使用本软件前,你(使用者)需签署本协议才可继续使用,以下协议是对于 Apache License 2.0 的补充,如有冲突,以以下协议为准。
本项目基于 Apache License 2.0 许可证发行,以下协议是对于 Apache License 2.0 的补充,如有冲突,以以下协议为准。
词语约定:本协议中的“本项目”指 LX Music 桌面版项目;“使用者”指签署本协议的使用者;“官方音乐平台”指对本项目内置的包括酷我、酷狗、咪咕等音乐源的官方平台统称;“版权数据”指包括但不限于图像、音频、名字等在内的他人拥有所属版权的数据。
词语约定:本协议中的“本软件”指洛雪音乐桌面版项目;“使用者”指签署本协议的使用者;“官方音乐平台”指对本软件内置的包括酷我、酷狗、咪咕等音乐源的官方平台统称;“版权数据”指包括但不限于图像、音频、名字等在内的他人拥有所属版权的数据。
一、数据来源
1、本软件的数据来源原理是从各官方音乐平台的公开服务器中拉取数据,经过对数据简单地筛选与合并后进行展示,因此本软件不对数据的准确性负责。
2、使用本软件的过程中可能会产生版权数据对于这些版权数据本软件不拥有它们的所有权为了避免造成侵权使用者务必在24小时内清除使用本软件的过程中所产生的版权数据。
3、本软件内的官方音乐平台别名为本软件内对官方音乐平台的一个称呼不包含恶意如果官方音乐平台觉得不妥可联系本软件更改或移除。
4、本软件内使用的部分包括但不限于字体、图片等资源来源于互联网如果出现侵权可联系本软件移除。
5、由于使用本软件产生的包括由于本协议或由于使用或无法使用本软件而引起的任何性质的任何直接、间接、特殊、偶然或结果性损害包括但不限于因商誉损失、停工、计算机故障或故障引起的损害赔偿或任何及所有其他商业损害或损失由使用者负责。
6、本项目完全免费且开源发布于 GitHub 面向全世界人用作对技术的学习交流,本软件不对项目内的技术可能存在违反当地法律法规的行为作保证,禁止在违反当地法律法规的情况下使用本软件,对于使用者在明知或不知当地法律法规不允许的情况下使用本软件所造成的任何违法违规行为由使用者承担,本软件不承担由此造成的任何直接、间接、特殊、偶然或结果性责任。
1.1 本项目的各官方平台在线数据来源原理是从其公开服务器中拉取数据(与未登录状态在官方平台 APP 获取的数据相同),经过对数据简单地筛选与合并后进行展示,因此本项目不对数据的合法性、准确性负责。
1.2 本项目本身没有获取某个音频数据的能力,本项目使用的在线音频数据来源来自软件设置内“自定义源”设置所选择的“源”返回的在线链接。例如播放某首歌,本项目所做的只是将希望播放的歌曲名、艺术家等信息传递给“源”,若“源”返回了一个链接,则本项目将认为这就是该歌曲的音频数据而进行使用,至于这是不是正确的音频数据本项目无法校验其准确性,所以使用本项目的过程中可能会出现希望播放的音频与实际播放的音频不对应或者无法播放的问题。
1.3 本项目的非官方平台数据(例如“我的列表”内列表)来自使用者本地系统或者使用者连接的同步服务,本项目不对这些数据的合法性、准确性负责。
二、版权数据
2.1 使用本项目的过程中可能会产生版权数据。对于这些版权数据,本项目不拥有它们的所有权。为了避免侵权,使用者务必在 **24 小时内** 清除使用本项目的过程中所产生的版权数据。
三、音乐平台别名
3.1 本项目内的官方音乐平台别名为本项目内对官方音乐平台的一个称呼,不包含恶意。如果官方音乐平台觉得不妥,可联系本项目更改或移除。
四、资源使用
4.1 本项目内使用的部分包括但不限于字体、图片等资源来源于互联网。如果出现侵权可联系本项目移除。
五、免责声明
5.1 由于使用本项目产生的包括由于本协议或由于使用或无法使用本项目而引起的任何性质的任何直接、间接、特殊、偶然或结果性损害(包括但不限于因商誉损失、停工、计算机故障或故障引起的损害赔偿,或任何及所有其他商业损害或损失)由使用者负责。
六、使用限制
6.1 本项目完全免费,且开源发布于 GitHub 面向全世界人用作对技术的学习交流。本项目不对项目内的技术可能存在违反当地法律法规的行为作保证。
6.2 **禁止在违反当地法律法规的情况下使用本项目。** 对于使用者在明知或不知当地法律法规不允许的情况下使用本项目所造成的任何违法违规行为由使用者承担,本项目不承担由此造成的任何直接、间接、特殊、偶然或结果性责任。
七、版权保护
7.1 音乐平台不易,请尊重版权,支持正版。
八、非商业性质
8.1 本项目仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作及捐赠。
九、接受协议
9.1 若你使用了本项目,即代表你接受本协议。
* 若协议更新,恕不另行通知,可到开源地址查看。
* 本软件的初衷是帮助官方音乐平台简化数据后代为展示,帮助使用者根据歌曲名、艺术家等关键字快速地定位所需内容所在的音乐平台。
* 音乐平台不易,建议到对应音乐平台支持正版资源。
By: 落雪无痕

28150
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,8 @@
{
"name": "lx-music-desktop",
"version": "2.5.0",
"version": "2.12.1",
"description": "一个免费的音乐查找助手",
"main": "./dist/main.js",
"productName": "lx-music-desktop",
"scripts": {
"pack": "node build-config/pack.js && npm run pack:win:setup:x64",
"pack:win": "node build-config/pack.js && npm run pack:win:setup:x64 && npm run pack:win:setup:x86 && npm run pack:win:setup:arm64 && npm run pack:win:setup:x86_64 && npm run pack:win:7z",
@@ -18,6 +17,7 @@
"pack:win:7z": "npm run pack:win:7z:x64",
"pack:win:7z:x64": "node build-config/build-pack.js target=win arch=x64 type=green",
"pack:win:7z:arm64": "node build-config/build-pack.js target=win arch=arm64 type=green",
"pack:win7:setup:x64": "node build-config/build-pack.js target=win arch=x64 type=win7_setup",
"pack:win7:7z:x64": "node build-config/build-pack.js target=win arch=x64 type=win7_green",
"pack:win7:7z:x86": "node build-config/build-pack.js target=win arch=x86 type=win7_green",
"pack:linux": "node build-config/pack.js && npm run pack:linux:deb && npm run pack:linux:appImage && npm run pack:linux:rpm && npm run pack:linux:pacman",
@@ -43,6 +43,7 @@
"publish:win:portable:x86": "node build-config/build-pack.js target=win arch=x86 type=portable publish=always",
"publish:win:7z:x64": "node build-config/build-pack.js target=win arch=x64 type=green publish=always",
"publish:win:7z:arm64": "node build-config/build-pack.js target=win arch=arm64 type=green publish=always",
"publish:win7:setup:x64": "node build-config/build-pack.js target=win arch=x64 type=win7_setup publish=always",
"publish:win7:7z:x64": "node build-config/build-pack.js target=win arch=x64 type=win7_green publish=always",
"publish:win7:7z:x86": "node build-config/build-pack.js target=win arch=x86 type=win7_green publish=always",
"publish:mac:dmg": "node build-config/build-pack.js target=mac arch=x64 type=dmg publish=always",
@@ -70,7 +71,7 @@
"Electron 22.3.0"
],
"engines": {
"node": ">= 16",
"node": ">= 22",
"npm": ">=8.5.2"
},
"macLanguagesInfoPlistStrings": {
@@ -106,109 +107,116 @@
},
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
"devDependencies": {
"@babel/core": "^7.23.0",
"@babel/eslint-parser": "^7.22.15",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/core": "^7.29.0",
"@babel/eslint-parser": "^7.28.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-modules-umd": "^7.22.5",
"@babel/plugin-transform-runtime": "^7.22.15",
"@babel/preset-env": "^7.22.20",
"@babel/preset-typescript": "^7.23.0",
"@tsconfig/recommended": "^1.0.3",
"@types/better-sqlite3": "^7.6.5",
"@types/needle": "^3.2.1",
"@types/tunnel": "^0.0.4",
"@babel/plugin-transform-class-properties": "^7.28.6",
"@babel/plugin-transform-modules-umd": "^7.27.1",
"@babel/plugin-transform-runtime": "^7.29.0",
"@babel/preset-env": "^7.29.0",
"@babel/preset-typescript": "^7.28.5",
"@babel/runtime": "^7.28.6",
"@tsconfig/recommended": "^1.0.13",
"@types/better-sqlite3": "^7.6.13",
"@types/needle": "^3.3.0",
"@types/node": "^20.19.33",
"@types/tunnel": "^0.0.7",
"@types/ws": "8.5.4",
"@volar/vue-language-plugin-pug": "^1.6.5",
"@vue/language-plugin-pug": "^1.8.15",
"babel-loader": "^9.1.3",
"browserslist": "^4.22.0",
"@vue/language-plugin-pug": "^3.2.4",
"babel-loader": "^10.0.0",
"browserslist": "^4.28.1",
"chalk": "^4.1.2",
"changelog-parser": "^3.0.1",
"copy-webpack-plugin": "^11.0.0",
"core-js": "^3.32.2",
"cross-env": "^7.0.3",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.1",
"copy-webpack-plugin": "^13.0.1",
"core-js": "^3.48.0",
"cross-env": "^10.1.0",
"css-loader": "^7.1.3",
"css-minimizer-webpack-plugin": "^7.0.4",
"del": "^6.1.1",
"electron": "^25.8.3",
"electron-builder": "^24.7.0",
"electron": "37.6.1",
"electron-builder": "^26.8.0",
"electron-debug": "^3.2.0",
"electron-devtools-installer": "^3.2.0",
"electron-to-chromium": "^1.4.532",
"electron-updater": "^6.1.5",
"eslint": "^8.50.0",
"electron-devtools-installer": "github:lyswhut/electron-devtools-installer#64596d615c1fc891eefd8aef1dfcb2c87aaadf03",
"electron-to-chromium": "^1.5.286",
"electron-updater": "6.8.3",
"eslint": "^8.57.1",
"eslint-config-standard": "^17.1.0",
"eslint-config-standard-with-typescript": "^39.1.0",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-formatter-friendly": "github:lyswhut/eslint-friendly-formatter#2170d1320e2fad13615a9dcf229669f0bb473a53",
"eslint-plugin-html": "^7.1.0",
"eslint-plugin-vue": "^9.17.0",
"eslint-plugin-vue-pug": "^0.6.0",
"eslint-webpack-plugin": "^4.0.1",
"html-webpack-plugin": "^5.5.3",
"less": "^4.2.0",
"less-loader": "^11.1.3",
"mini-css-extract-plugin": "^2.7.6",
"node-loader": "^2.0.0",
"postcss": "^8.4.30",
"postcss-loader": "^7.3.3",
"postcss-pxtorem": "^6.0.0",
"pug": "^3.0.2",
"eslint-plugin-html": "^8.1.4",
"eslint-plugin-vue": "^9.33.0",
"eslint-plugin-vue-pug": "^0.6.2",
"eslint-webpack-plugin": "^4.2.0",
"html-webpack-plugin": "^5.6.6",
"less": "^4.5.1",
"less-loader": "^12.3.1",
"mini-css-extract-plugin": "^2.10.0",
"node-loader": "^2.1.0",
"postcss": "^8.5.6",
"postcss-loader": "^8.2.1",
"postcss-pxtorem": "^6.1.0",
"pug": "^3.0.3",
"pug-plain-loader": "^1.1.0",
"rimraf": "^5.0.5",
"rimraf": "^6.1.3",
"spinnies": "github:lyswhut/spinnies#233305c58694aa3b053e3ab9af9049993f918b9d",
"svg-sprite-loader": "^6.0.11",
"svg-transform-loader": "^2.0.13",
"svgo-loader": "^4.0.0",
"terser": "^5.20.0",
"terser-webpack-plugin": "^5.3.9",
"ts-loader": "^9.4.4",
"typescript": "^5.1.6",
"vue-eslint-parser": "^9.3.1",
"vue-loader": "^17.2.2",
"vue-template-compiler": "^2.7.14",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"terser": "^5.46.0",
"terser-webpack-plugin": "^5.3.16",
"tree-kill": "^1.2.2",
"ts-loader": "^9.5.4",
"typescript": "5.9.3",
"vue-eslint-parser": "^9.4.3",
"vue-loader": "^17.4.2",
"webpack": "^5.105.2",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "5.2.3",
"webpack-hot-middleware": "github:lyswhut/webpack-hot-middleware#329c4375134b89d39da23a56a94db651247c74a1",
"webpack-merge": "^5.9.0"
"webpack-merge": "^6.0.1"
},
"dependencies": {
"@simonwep/pickr": "^1.8.2",
"better-sqlite3": "^8.6.0",
"bufferutil": "^4.0.7",
"@simonwep/pickr": "^1.9.1",
"better-sqlite3": "^12.6.2",
"bufferutil": "^4.1.0",
"comlink": "~4.3.1",
"crypto-js": "^4.1.1",
"electron-font-manager": "github:lyswhut/electron-font-manager#6d2f5ecf850c4fe34812b9394913680462ee0dae",
"electron-log": "^5.0.0-rc.1",
"electron-store": "^8.1.0",
"font-list": "^1.5.1",
"iconv-lite": "^0.6.3",
"image-size": "^1.0.2",
"jschardet": "^3.0.0",
"long": "^5.2.3",
"crypto-js": "^4.2.0",
"electron-log": "^5.4.3",
"font-list": "^2.0.2",
"iconv-lite": "^0.7.2",
"image-size": "^1.1.0",
"jschardet": "^3.1.4",
"long": "^5.3.2",
"message2call": "^0.1.3",
"music-metadata": "^8.1.4",
"music-metadata": "^11.12.0",
"needle": "github:lyswhut/needle#93299ac841b7e9a9f82ca7279b88aaaeda404060",
"node-id3": "^0.2.6",
"sortablejs": "^1.15.0",
"node-id3": "^0.2.9",
"sortablejs": "^1.15.7",
"tunnel": "^0.0.6",
"utf-8-validate": "^6.0.3",
"vue": "~3.2.47",
"vue-router": "^4.2.5",
"ws": "^8.14.2"
"undici": "^7.22.0",
"utf-8-validate": "^6.0.6",
"vue": "~3.3.13",
"vue-router": "~4.5.1",
"ws": "^8.19.0"
},
"overrides": {
"got": "^11",
"json5": "latest",
"node-abi": "latest",
"minimatch": "latest",
"semver": "latest",
"svg-transform-loader": {
"postcss": "^8"
},
"svg-sprite-loader": {
"postcss": "latest"
"postcss": "^8"
},
"svg-baker": {
"postcss": "latest"
"postcss": "^8"
},
"braces": "latest",
"node-gyp-build": "latest",
"micromatch": "latest",
"http-cache-semantics": "latest"
}
}

View File

@@ -1,25 +1,12 @@
落雪提前祝大家中秋快乐~🥮😘!
### 不兼容性变更
- 由于微软及Electron即将结束对 Windows 7、Windows 8 的支持所以从这个版本起LX的默认 Windows 版也不再支持这些版本的系统,但考虑到仍然有许多人使用 Windows 7我们特别构建了能在 Windows 7 上使用的免安装版文件名带win7需要注意的是这个版本将缺乏安全更新若非必要情况不要使用该版本
- 由于微软在 Windows 10 2004版本已删除对32位的OEM支持所以在这个版本起LX的默认 Windows 版已不再提供32位的支持
- 更改构建的文件名格式主要修改linux下deb、rpm文件命名格式
### 新增
- 新增Scheme URL对播放器的控制操作新增的操作包含 播放、暂停、下一首、上一首等详情看Scheme URL文档
我们很高兴地宣布新项目 Any Listen 的桌面版已发布目前已支持列表跟随本地文件自动更新、加载并播放WebDAV上的歌曲等功能更多功能仍在积极开发中桌面版与Web版将同步更新。
对于有播放本地音乐或播放服务器上音乐需求的人可以试试,若遇到任何问题可以发 issue 反馈。
### 优化
- 通过歌曲菜单添加不喜欢歌曲时需要二次确认防止手抖
- 优化托盘图标行为:在非 Windows 系统中,点击托盘图标时不再显示主窗口
### 修复
- 修复音频输出设备设置在重启软件后被重置的问题(#1568
- 修复更换语言设置后源名称未更新的问题
- 修复点击搜索、排行榜等在线列表歌曲右键菜单歌曲详情页会意外将该歌曲添加不喜欢的问题
### 其他
- 更新 electron 到 v25.8.3
- 修复音量条在调整音量时实际音量与显示的数值不一致的问题(#2606
- 修复某些情况下搜索框的搜索按钮布局错位的问题(#2622
- 修复 wyy 部分歌曲无法被搜索的问题(#2666, @ikun0014

View File

@@ -0,0 +1,40 @@
/**
*
* @param {string} text
* @returns
*/
export const parseChangelog = async(text) => {
const versions = []
const lines = text.split(/\r\n|\r|\n/)
let currentVersion = null
let currentDate = null
let currentDesc = ''
for (const line of lines) {
const versionMatch = line.match(/^\s*##\s+\[?(\d+\.\d+\.\d+)\]?.*?-\s+(\d{4}-\d{2}-\d{2})$/)
if (versionMatch) {
if (currentVersion) {
versions.push({
version: currentVersion,
date: currentDate,
desc: currentDesc.trim(),
})
}
currentVersion = versionMatch[1]
currentDate = versionMatch[3]
currentDesc = ''
} else {
currentDesc += `${line}\n`
}
}
if (currentVersion) {
versions.push({
version: currentVersion,
date: currentDate,
desc: currentDesc.trim(),
})
}
return versions
}

View File

@@ -6,8 +6,8 @@ const version = require('../version.json')
const chalk = require('chalk')
const pkg_bak = JSON.stringify(pkg, null, 2)
const version_bak = JSON.stringify(version, null, 2)
const parseChangelog = require('changelog-parser')
const changelogPath = jp('../../CHANGELOG.md')
const { parseChangelog } = require('./parseChangelog')
// const md_renderer = markdownStr => new (require('markdown-it'))({
// html: true,
@@ -16,27 +16,23 @@ const changelogPath = jp('../../CHANGELOG.md')
// breaks: true,
// }).render(markdownStr)
const getPrevVer = () => parseChangelog(changelogPath).then(res => {
if (!res.versions.length) throw new Error('CHANGELOG 无法解析到版本号')
return res.versions[0].version
const getPrevVer = () => parseChangelog(fs.readFileSync(changelogPath, 'utf-8').toString()).then(versions => {
if (!versions.length) throw new Error('CHANGELOG 无法解析到版本号')
return versions[0].version
})
const updateChangeLog = async(newVerNum, newChangeLog) => {
let changeLog = fs.readFileSync(changelogPath, 'utf-8')
const prevVer = await getPrevVer()
const log = `## [${newVerNum}](${pkg.repository.url.replace(/^git\+(http.+)\.git$/, '$1')}/compare/v${prevVer}...v${newVerNum}) - ${formatTime()}\n\n${newChangeLog}`
fs.writeFileSync(changelogPath, changeLog.replace(new RegExp('(## [?0.1.1]?)'), log + '\n$1'), 'utf-8')
fs.writeFileSync(changelogPath, changeLog.replace(/(## \[(?:\d+\.))/, log + '\n$1'), 'utf-8')
}
// const renderChangeLog = md => md_renderer(md)
module.exports = async newVerNum => {
if (!newVerNum) {
let verArr = pkg.version.split('.')
verArr[verArr.length - 1] = parseInt(verArr[verArr.length - 1]) + 1
newVerNum = verArr.join('.')
}
if (!newVerNum) newVerNum = pkg.version
const newMDChangeLog = fs.readFileSync(jp('../changeLog.md'), 'utf-8')
// const newChangeLog = renderChangeLog(newMDChangeLog)
version.history.unshift({

File diff suppressed because one or more lines are too long

View File

@@ -80,3 +80,5 @@ export const DOWNLOAD_STATUS = {
} as const
export const QUALITYS = ['flac24bit', 'flac', 'wav', 'ape', '320k', '192k', '128k'] as const
export const TRAY_AUTO_ID = -1

View File

@@ -19,23 +19,28 @@ const defaultSetting: LX.AppSetting = {
'common.isAgreePact': false,
'common.controlBtnPosition': isMac ? 'left' : 'right',
'common.playBarProgressStyle': 'mini',
'common.transparentWindow': !isMac,
'common.tryAutoUpdate': true,
'common.showChangeLog': true,
'player.startupAutoPlay': false,
'player.togglePlayMethod': 'listLoop',
'player.highQuality': false,
'player.playQuality': '128k',
'player.isShowTaskProgess': true,
'player.isShowStatusBarLyric': false,
'player.volume': 1,
'player.powerSaveBlocker': true,
'player.isMute': false,
'player.playbackRate': 1,
'player.preservesPitch': true,
'player.isMaxOutputChannelCount': false,
'player.mediaDeviceId': 'default',
'player.isMediaDeviceRemovedStopPlay': false,
'player.isShowLyricTranslation': false,
'player.isShowLyricRoma': false,
'player.isSwapLyricTranslationAndRoma': false,
'player.isS2t': false,
'player.isPlayLxlrc': isWin,
'player.isPlayLxlrc': !isMac,
'player.isSavePlayTime': false,
'player.audioVisualization': false,
'player.waitPlayEndStop': true,
@@ -62,8 +67,9 @@ const defaultSetting: LX.AppSetting = {
'playDetail.isZoomActiveLrc': false,
'playDetail.isShowLyricProgressSetting': false,
'playDetail.style.fontSize': 100,
'playDetail.style.fontSize': 140,
'playDetail.style.align': 'center',
'playDetail.isDelayScroll': true,
'desktopLyric.enable': false,
'desktopLyric.isLock': false,
@@ -72,6 +78,7 @@ const defaultSetting: LX.AppSetting = {
'desktopLyric.isShowTaskbar': false,
'desktopLyric.audioVisualization': false,
'desktopLyric.fullscreenHide': true,
'desktopLyric.pauseHide': true,
'desktopLyric.width': 450,
'desktopLyric.height': 300,
'desktopLyric.x': null,
@@ -103,16 +110,19 @@ const defaultSetting: LX.AppSetting = {
'list.actionButtonsVisible': false,
'download.enable': false,
'download.isSavePathGroupByListName': false,
'download.savePath': path.join(os.homedir(), 'Desktop'),
'download.fileName': '歌名 - 歌手',
'download.maxDownloadNum': 3,
'download.skipExistFile': true,
'download.isDownloadLrc': false,
'download.isDownloadLxLrc': true,
'download.isDownloadTLrc': false,
'download.isDownloadRLrc': false,
'download.lrcFormat': 'utf8',
'download.isEmbedPic': true,
'download.isEmbedLyric': false,
'download.isEmbedLyricLx': true,
'download.isEmbedLyricT': false,
'download.isEmbedLyricR': false,
'download.isUseOtherSource': false,
@@ -124,8 +134,6 @@ const defaultSetting: LX.AppSetting = {
'network.proxy.enable': false,
'network.proxy.host': '',
'network.proxy.port': '',
'network.proxy.username': '',
'network.proxy.password': '',
'tray.enable': false,
// 'tray.isToTray': false,
@@ -137,6 +145,10 @@ const defaultSetting: LX.AppSetting = {
'sync.server.maxSsnapshotNum': 5,
'sync.client.host': '',
'openAPI.enable': false,
'openAPI.port': '23330',
'openAPI.bindLan': false,
// 'theme.id': 'blue_plus',
'theme.id': 'green',
'theme.lightId': 'green',

View File

@@ -51,6 +51,16 @@ const hotKey = {
action: 'prev',
type: '',
},
seekbackward: {
name: 'seekbackward',
action: 'seekbackward',
type: '',
},
seekforward: {
name: 'seekforward',
action: 'seekforward',
type: '',
},
volume_up: {
name: 'volume_up',
action: 'volume_up',

View File

@@ -55,9 +55,11 @@ const modules = {
show_save_dialog: 'show_save_dialog',
show_select_dialog: 'show_select_dialog',
show_dialog: 'show_dialog',
open_dir_in_explorer: 'open_dir_in_explorer',
open_dev_tools: 'open_dev_tools',
set_power_save_blocker: 'set_power_save_blocker',
progress: 'progress',
player_status: 'player_status',
change_tray: 'change_tray',
quit_update: 'quit_update',
update_check: 'update_check',
@@ -132,6 +134,7 @@ const modules = {
clear_music_url: 'clear_music_url',
get_music_url_count: 'get_music_url_count',
open_api_action: 'open_api_action',
sync_action: 'sync_action',
sync_get_server_devices: 'sync_get_server_devices',
sync_remove_server_device: 'sync_remove_server_device',
@@ -159,6 +162,7 @@ const modules = {
on_config_change: 'on_config_change',
main_window_inited: 'main_window_inited',
set_win_bounds: 'set_win_bounds',
set_win_resizeable: 'set_win_resizeable',
key_down: 'key_down',
request_main_window_channel: 'request_main_window_channel',
provide_main_window_channel: 'provide_main_window_channel',

View File

@@ -8,7 +8,7 @@
* @param {string} c1 rgb(a) color2
* @returns color
*/
export const RGB_Linear_Blend=(p,c0,c1)=>{
exports.RGB_Linear_Blend=(p,c0,c1)=>{
var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
return"rgb"+(x?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+i(e[3]=="a"?e.slice(5):e.slice(4))*p)+","+r(i(b)*P+i(f)*p)+","+r(i(c)*P+i(g)*p)+j;
}
@@ -20,7 +20,7 @@ export const RGB_Linear_Blend=(p,c0,c1)=>{
* @param {string} c1 rgb(a) color2
* @returns color
*/
export const RGB_Log_Blend=(p,c0,c1)=>{
exports.RGB_Log_Blend=(p,c0,c1)=>{
var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
return"rgb"+(x?"a(":"(")+r((P*i(a[3]=="a"?a.slice(5):a.slice(4))**2+p*i(e[3]=="a"?e.slice(5):e.slice(4))**2)**0.5)+","+r((P*i(b)**2+p*i(f)**2)**0.5)+","+r((P*i(c)**2+p*i(g)**2)**0.5)+j;
}
@@ -32,7 +32,7 @@ export const RGB_Log_Blend=(p,c0,c1)=>{
* @param {string} c0 rgb(a) color
* @returns color
*/
export const RGB_Linear_Shade=(p,c0)=>{
exports.RGB_Linear_Shade=(p,c0)=>{
var i=parseInt,r=Math.round,[a,b,c,d]=c0.split(","),n=p<0,t=n?0:255*p,P=n?1+p:1-p;
return"rgb"+(d?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+t)+","+r(i(b)*P+t)+","+r(i(c)*P+t)+(d?","+d:")");
}
@@ -44,7 +44,7 @@ export const RGB_Linear_Shade=(p,c0)=>{
* @param {string} c0 rgb(a) color
* @returns color
*/
export const RGB_Log_Shade=(p,c0)=>{
exports.RGB_Log_Shade=(p,c0)=>{
var i=parseInt,r=Math.round,[a,b,c,d]=c0.split(","),n=p<0,t=n?0:p*255**2,P=n?1+p:1-p;
return"rgb"+(d?"a(":"(")+r((P*i(a[3]=="a"?a.slice(5):a.slice(4))**2+t)**0.5)+","+r((P*i(b)**2+t)**0.5)+","+r((P*i(c)**2+t)**0.5)+(d?","+d:")");
}
@@ -56,7 +56,7 @@ export const RGB_Log_Shade=(p,c0)=>{
* @param {string} color
* @returns color
*/
export const RGB_Alpha_Shade = (p, color) => {
exports.RGB_Alpha_Shade = (p, color) => {
var i = parseInt
var n = p < 0
var [r, g, b, a] = color.split(",")

View File

@@ -1,14 +1,15 @@
//! 更新默认主题配置后,需要执行 npm run build:theme 重新构建index.json
import fs from 'fs'
import path from 'path'
import { createThemeColors } from './utils'
const fs = require('fs')
const path = require('path')
const { createThemeColors } = require('./utils')
const defaultThemes = [
{
id: 'green',
name: '绿意盎然',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(77, 175, 124)',
font: 'rgb(33, 33, 33)',
@@ -32,6 +33,7 @@ const defaultThemes = [
id: 'blue',
name: '蓝田生玉',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(52, 152, 219)',
font: 'rgb(33, 33, 33)',
@@ -55,6 +57,7 @@ const defaultThemes = [
id: 'blue_plus',
name: '蛋雅深蓝',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(77, 131, 175)',
font: 'rgb(33, 33, 33)',
@@ -78,6 +81,7 @@ const defaultThemes = [
id: 'orange',
name: '橙黄橘绿',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(245, 171, 53)',
font: 'rgb(33, 33, 33)',
@@ -101,6 +105,7 @@ const defaultThemes = [
id: 'red',
name: '热情似火',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(214, 69, 65)',
font: 'rgb(33, 33, 33)',
@@ -124,6 +129,7 @@ const defaultThemes = [
id: 'pink',
name: '粉装玉琢',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(241, 130, 141)',
font: 'rgb(33, 33, 33)',
@@ -147,6 +153,7 @@ const defaultThemes = [
id: 'purple',
name: '重斤球紫',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(155, 89, 182)',
font: 'rgb(33, 33, 33)',
@@ -170,6 +177,7 @@ const defaultThemes = [
id: 'grey',
name: '灰常美丽',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(108, 122, 137)',
font: 'rgb(33, 33, 33)',
@@ -193,6 +201,7 @@ const defaultThemes = [
id: 'ming',
name: '青出于黑',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(51, 110, 123)',
font: 'rgb(33, 33, 33)',
@@ -216,6 +225,7 @@ const defaultThemes = [
id: 'blue2',
name: '清热板蓝',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(79, 98, 208)',
font: 'rgb(33, 33, 33)',
@@ -239,6 +249,7 @@ const defaultThemes = [
id: 'black',
name: '黑灯瞎火',
isDark: true,
isDarkFont: false,
config: {
primary: 'rgb(150, 150, 150)',
font: 'rgb(229, 229, 229)',
@@ -262,6 +273,7 @@ const defaultThemes = [
id: 'mid_autumn',
name: '月里嫦娥',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(74, 55, 82)',
font: 'rgb(33, 33, 33)',
@@ -286,6 +298,7 @@ const defaultThemes = [
id: 'naruto',
name: '木叶之村',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(87, 144, 167)',
font: 'rgb(33, 33, 33)',
@@ -309,6 +322,7 @@ const defaultThemes = [
id: 'china_ink',
name: '近墨者黑',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgba(47, 47, 47, 1)',
font: 'rgb(33, 33, 33)',
@@ -333,6 +347,7 @@ const defaultThemes = [
id: 'happy_new_year',
name: '新年快乐',
isDark: false,
isDarkFont: false,
config: {
primary: 'rgb(192, 57, 43)',
font: 'rgb(33, 33, 33)',

View File

@@ -3,6 +3,7 @@
"id": "green",
"name": "绿意盎然",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -259,6 +260,7 @@
"id": "blue",
"name": "蓝田生玉",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -515,6 +517,7 @@
"id": "blue_plus",
"name": "蛋雅深蓝",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -771,6 +774,7 @@
"id": "orange",
"name": "橙黄橘绿",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -1027,6 +1031,7 @@
"id": "red",
"name": "热情似火",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -1283,6 +1288,7 @@
"id": "pink",
"name": "粉装玉琢",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -1539,6 +1545,7 @@
"id": "purple",
"name": "重斤球紫",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -1795,6 +1802,7 @@
"id": "grey",
"name": "灰常美丽",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -2051,6 +2059,7 @@
"id": "ming",
"name": "青出于黑",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -2307,6 +2316,7 @@
"id": "blue2",
"name": "清热板蓝",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -2563,6 +2573,7 @@
"id": "black",
"name": "黑灯瞎火",
"isDark": true,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -2766,16 +2777,16 @@
"--color-primary-light-900-alpha-700": "rgba(59, 59, 59, 0.30)",
"--color-primary-light-900-alpha-800": "rgba(59, 59, 59, 0.20)",
"--color-primary-light-900-alpha-900": "rgba(59, 59, 59, 0.10)",
"--color-primary-light-1000": "rgb(47,47,47)",
"--color-primary-light-1000-alpha-100": "rgba(47, 47, 47, 0.90)",
"--color-primary-light-1000-alpha-200": "rgba(47, 47, 47, 0.80)",
"--color-primary-light-1000-alpha-300": "rgba(47, 47, 47, 0.70)",
"--color-primary-light-1000-alpha-400": "rgba(47, 47, 47, 0.60)",
"--color-primary-light-1000-alpha-500": "rgba(47, 47, 47, 0.50)",
"--color-primary-light-1000-alpha-600": "rgba(47, 47, 47, 0.40)",
"--color-primary-light-1000-alpha-700": "rgba(47, 47, 47, 0.30)",
"--color-primary-light-1000-alpha-800": "rgba(47, 47, 47, 0.20)",
"--color-primary-light-1000-alpha-900": "rgba(47, 47, 47, 0.10)",
"--color-primary-light-1000": "rgb(38,38,38)",
"--color-primary-light-1000-alpha-100": "rgba(38, 38, 38, 0.90)",
"--color-primary-light-1000-alpha-200": "rgba(38, 38, 38, 0.80)",
"--color-primary-light-1000-alpha-300": "rgba(38, 38, 38, 0.70)",
"--color-primary-light-1000-alpha-400": "rgba(38, 38, 38, 0.60)",
"--color-primary-light-1000-alpha-500": "rgba(38, 38, 38, 0.50)",
"--color-primary-light-1000-alpha-600": "rgba(38, 38, 38, 0.40)",
"--color-primary-light-1000-alpha-700": "rgba(38, 38, 38, 0.30)",
"--color-primary-light-1000-alpha-800": "rgba(38, 38, 38, 0.20)",
"--color-primary-light-1000-alpha-900": "rgba(38, 38, 38, 0.10)",
"--color-theme": "rgb(59,59,59)",
"--color-1000": "rgb(229, 229, 229)",
"--color-950": "rgb(218,218,218)",
@@ -2819,6 +2830,7 @@
"id": "mid_autumn",
"name": "月里嫦娥",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -3075,6 +3087,7 @@
"id": "naruto",
"name": "木叶之村",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -3331,6 +3344,7 @@
"id": "china_ink",
"name": "近墨者黑",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {
@@ -3587,6 +3601,7 @@
"id": "happy_new_year",
"name": "新年快乐",
"isDark": false,
"isDarkFont": false,
"isCustom": false,
"config": {
"themeColors": {

View File

@@ -1,6 +1,6 @@
import { RGB_Linear_Shade, RGB_Alpha_Shade } from './colorUtils'
const { RGB_Linear_Shade, RGB_Alpha_Shade } = require('./colorUtils')
export const createThemeColors = (rgbaColor, fontRgbaColor, isDark) => {
exports.createThemeColors = (rgbaColor, fontRgbaColor, isDark, isDarkFont) => {
const colors = {
'--color-primary': rgbaColor,
}
@@ -22,7 +22,7 @@ export const createThemeColors = (rgbaColor, fontRgbaColor, isDark) => {
colors[`--color-primary-light-${i * 100}-alpha-${j * 100}`] = RGB_Alpha_Shade(0.1 * j, preColor)
}
}
preColor = RGB_Linear_Shade(isDark ? -0.2 : 1, preColor)
preColor = RGB_Linear_Shade(isDark ? -0.35 : 1, preColor)
colors[`--color-primary-light-${1000}`] = preColor
for (let j = 1; j < 10; j += 1) {
colors[`--color-primary-light-${1000}-alpha-${j * 100}`] = RGB_Alpha_Shade(0.1 * j, preColor)
@@ -30,19 +30,19 @@ export const createThemeColors = (rgbaColor, fontRgbaColor, isDark) => {
colors['--color-theme'] = isDark ? colors['--color-primary-light-900'] : rgbaColor
return { ...colors, ...createFontColors(fontRgbaColor, isDark) }
return { ...colors, ...createFontColors(fontRgbaColor, isDark, isDarkFont) }
}
const createFontColors = (rgbaColor, isDark) => {
const createFontColors = (rgbaColor, isDark, isDarkFont) => {
// rgb(238, 238, 238)
// let prec = 'rgb(255, 255, 255)'
rgbaColor ??= isDark ? 'rgb(229, 229, 229)' : 'rgb(33, 33, 33)'
if (isDark) return createFontDarkColors(rgbaColor)
if (isDark) return createFontDarkColors(rgbaColor, isDarkFont)
let colors = {
'--color-1000': rgbaColor,
}
let step = isDark ? -0.05 : 0.05
let step = (isDarkFont ? 0.02 : 0.05) * (isDark ? -1 : 1)
for (let i = 1; i < 21; i += 1) {
colors[`--color-${String(1000 - 50 * i).padStart(3, '0')}`] = RGB_Linear_Shade(step * i, rgbaColor)
}
@@ -50,14 +50,14 @@ const createFontColors = (rgbaColor, isDark) => {
return colors
}
const createFontDarkColors = (rgbaColor) => {
const createFontDarkColors = (rgbaColor, isDarkFont) => {
// rgb(238, 238, 238)
// let prec = 'rgb(255, 255, 255)'
let colors = {
'--color-1000': rgbaColor,
}
const step = -0.05
const step = isDarkFont ? -0.015 : -0.05
let preColor = rgbaColor
for (let i = 1; i < 21; i += 1) {
preColor = RGB_Linear_Shade(step, preColor)

View File

@@ -1,4 +1,4 @@
import type { I18n } from '@root/lang/i18n'
import type { I18n } from '../../lang/i18n'
declare global {
@@ -68,6 +68,11 @@ declare global {
*/
'common.playBarProgressStyle': 'mini' | 'full' | 'middle'
/**
* 启用透明窗口
*/
'common.transparentWindow': boolean
/**
* 尝试自动更新
*/
@@ -89,20 +94,31 @@ declare global {
'player.togglePlayMethod': 'listLoop' | 'random' | 'list' | 'singleLoop' | 'none'
/**
* 是否优先播放320k音质
* 优先播放音质
*/
'player.highQuality': boolean
'player.playQuality': LX.Quality
/**
* 是否显示任务栏进度条
*/
'player.isShowTaskProgess': boolean
/**
* 是否将歌词显示在状态栏
*/
'player.isShowStatusBarLyric': boolean
/**
* 音量大小
*/
'player.volume': number
/**
* 播放歌曲时是否阻止电脑休眠
*/
'player.powerSaveBlocker': boolean
/**
* 是否静音
*/
@@ -118,6 +134,11 @@ declare global {
*/
'player.preservesPitch': boolean
/**
* 使用设备能处理的最大声道数输出音频
*/
'player.isMaxOutputChannelCount': boolean
/**
* 音频输出设备id
*/
@@ -138,6 +159,11 @@ declare global {
*/
'player.isShowLyricRoma': boolean
/**
* 是否调换翻译歌词与罗马音歌词位置
*/
'player.isSwapLyricTranslationAndRoma': boolean
/**
* 是否将歌词从简体转换为繁体
*/
@@ -283,6 +309,11 @@ declare global {
*/
'playDetail.style.align': 'center' | 'left' | 'right'
/**
* 播放详情页-是否延迟桌面歌词滚动
*/
'playDetail.isDelayScroll': boolean
/**
* 是否启用桌面歌词
@@ -319,6 +350,11 @@ declare global {
*/
'desktopLyric.fullscreenHide': boolean
/**
* 是否在暂停时隐藏歌词
*/
'desktopLyric.pauseHide': boolean
/**
* 桌面歌词窗口宽度
*/
@@ -464,6 +500,11 @@ declare global {
*/
'download.enable': boolean
/**
* 按列表名分组保存
*/
'download.isSavePathGroupByListName': boolean
/**
* 下载路径
*/
@@ -489,6 +530,11 @@ declare global {
*/
'download.isDownloadLrc': boolean
/**
* 是否在下载 lx 歌词
*/
'download.isDownloadLxLrc': boolean
/**
* 是否下载翻译歌词文件
*/
@@ -509,6 +555,11 @@ declare global {
*/
'download.isEmbedPic': boolean
/**
* 是否在音频文件中嵌入 lx 歌词
*/
'download.isEmbedLyricLx': boolean
/**
* 是否在音频文件中嵌入歌词
*/
@@ -574,16 +625,6 @@ declare global {
*/
'network.proxy.port': string
/**
* 代理服务器用户名
*/
'network.proxy.username': string
/**
* 代理服务器密码
*/
'network.proxy.password': string
/**
* 是否启用托盘
*/
@@ -624,6 +665,22 @@ declare global {
*/
'sync.client.host': string
/**
* 是否启用开放API服务
*/
'openAPI.enable': boolean
/**
* API服务端口号
*/
'openAPI.port': '23330' | string
/**
* 是否绑定到局域网
*/
'openAPI.bindLan': boolean
/**
* 是否在离开搜索界面时自动清空搜索框
*/

View File

@@ -37,6 +37,11 @@ declare namespace LX {
*/
play?: string
/**
* 启动后最小化到系统托盘
*/
hidden?: boolean
[key: string]: boolean | number | string
}

View File

@@ -6,6 +6,7 @@ declare namespace LX {
'desktopLyric.isAlwaysOnTop': LX.AppSetting['desktopLyric.isAlwaysOnTop']
'desktopLyric.isAlwaysOnTopLoop': LX.AppSetting['desktopLyric.isAlwaysOnTopLoop']
'desktopLyric.isShowTaskbar': LX.AppSetting['desktopLyric.isShowTaskbar']
'desktopLyric.pauseHide': LX.AppSetting['desktopLyric.pauseHide']
'desktopLyric.audioVisualization': LX.AppSetting['desktopLyric.audioVisualization']
'desktopLyric.width': LX.AppSetting['desktopLyric.width']
'desktopLyric.height': LX.AppSetting['desktopLyric.height']
@@ -33,6 +34,7 @@ declare namespace LX {
'common.langId': LX.AppSetting['common.langId']
'player.isShowLyricTranslation': LX.AppSetting['player.isShowLyricTranslation']
'player.isShowLyricRoma': LX.AppSetting['player.isShowLyricRoma']
'player.isSwapLyricTranslationAndRoma': LX.AppSetting['player.isSwapLyricTranslationAndRoma']
'player.isPlayLxlrc': LX.AppSetting['player.isPlayLxlrc']
'player.playbackRate': LX.AppSetting['player.playbackRate']
}

View File

@@ -21,6 +21,7 @@ declare global {
speed: string
downloaded: number
total: number
writeQueue: number
}
interface DownloadTaskActionBase <A> {
@@ -50,6 +51,7 @@ declare global {
total: number
progress: number
speed: string
writeQueue: number
metadata: {
musicInfo: LX.Music.MusicInfoOnline
url: string | null
@@ -57,6 +59,7 @@ declare global {
ext: FileExt
fileName: string
filePath: string
listId?: string
}
}

View File

@@ -22,6 +22,7 @@ declare namespace LX {
songId: string | number // 歌曲IDmg源为copyrightIdlocal为文件路径
albumName: string // 歌曲专辑名称
picUrl?: string | null // 歌曲图片链接
toggleMusicInfo?: MusicInfoOnline | null
}
interface MusicInfoMeta_online extends MusicInfoMetaBase {

26
src/common/types/open_api.d.ts vendored Normal file
View File

@@ -0,0 +1,26 @@
declare namespace LX {
namespace OpenAPI {
interface Status {
status: boolean
message: string
address: string
}
interface EnableServer {
enable: boolean
port: string
bindLan: boolean
}
interface ActionBase <A> {
action: A
}
interface ActionData<A, D> extends ActionBase<A> {
data: D
}
type Action<A, D = undefined> = D extends undefined ? ActionBase<A> : ActionData<A, D>
type Actions = Action<'status'>
| Action<'enable', EnableServer>
}
}

View File

@@ -11,9 +11,32 @@ declare namespace LX {
| 'pause'
| 'play'
| 'next'
| 'seek'
| 'volume'
| 'mute'
interface LyricInfo extends LX.Music.LyricInfo {
rawlrcInfo: LX.Music.LyricInfo
}
interface Status {
status: 'playing' | 'paused' | 'error' | 'stoped'
name: string
singer: string
albumName: string
picUrl: string
progress: number
duration: number
playbackRate: number
lyricLineText: string
lyricLineAllText: string
lyric: string
tlyric: string
rlyric: string
lxlyric: string
collect: boolean
volume: number
mute: boolean
}
}
}

View File

@@ -262,6 +262,7 @@ declare namespace LX {
id: string
name: string
isDark: boolean
isDarkFont: boolean
isCustom: boolean
config: {
themeColors: ThemeColors

View File

@@ -1,7 +1,7 @@
declare namespace LX {
namespace UserApi {
type UserApiSourceInfoType = 'music'
type UserApiSourceInfoActions = 'musicUrl'
type UserApiSourceInfoActions = 'musicUrl' | 'lyric' | 'pic'
interface UserApiSourceInfo {
name: string
@@ -13,15 +13,20 @@ declare namespace LX {
type UserApiSources = Record<LX.Source, UserApiSourceInfo>
interface UserApiInfo {
interface UserApiInfoFull {
id: string
name: string
description: string
script: string
allowShowUpdateAlert: boolean
author?: string
homepage?: string
version?: string
sources?: UserApiSources
}
type UserApiInfo = Omit<UserApiInfoFull, 'script'>
interface UserApiStatus {
status: boolean
message?: string

View File

@@ -71,19 +71,6 @@ export const formatPlayTime2 = (time: number) => {
}
const encodeNames = {
'&nbsp;': ' ',
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&apos;': "'",
'&#039;': "'",
} as const
export const decodeName = (str: string | null = '') => {
return str?.replace(/(?:&amp;|&lt;|&gt;|&quot;|&apos;|&#039;|&nbsp;)/gm, (s: string) => encodeNames[s as keyof typeof encodeNames]) ?? ''
}
export const isUrl = (path: string) => /https?:\/\//.test(path)
// 解析URL参数为对象

View File

@@ -8,6 +8,7 @@ import { request, type Options as RequestOptions } from './request'
export interface Options {
forceResume: boolean
timeout: number
requestOptions: RequestOptions
}
@@ -23,6 +24,7 @@ const defaultRequestOptions: Options['requestOptions'] = {
}
const defaultOptions: Options = {
forceResume: true,
timeout: 20_000,
requestOptions: { ...defaultRequestOptions },
}
@@ -39,6 +41,9 @@ class Task extends EventEmitter {
requestInstance: http.ClientRequest | null = null
maxRedirectNum = 2
private redirectNum = 0
private dataWriteQueueLength = 0
private closeWaiting = false
private timeout: null | NodeJS.Timeout = null
constructor(url: string, savePath: string, filename: string, options: Partial<Options> = {}) {
@@ -65,6 +70,10 @@ class Task extends EventEmitter {
this.progress.downloaded = 0
this.progress.progress = 0
this.progress.speed = 0
this.dataWriteQueueLength = 0
this.closeWaiting = false
this.__clearTimeout()
this.__startTimeout()
if (startByte) this.requestOptions.headers!.range = `bytes=${startByte}-${endByte}`
if (!path) return
@@ -140,6 +149,7 @@ class Task extends EventEmitter {
}
this.status = STATUS.failed
this.emit('fail', response)
this.__clearTimeout()
this.__closeRequest()
void this.__closeWriteStream()
return
@@ -152,6 +162,7 @@ class Task extends EventEmitter {
return
}
this.status = STATUS.running
this.__startTimeout()
response
.on('data', this.__handleWriteData.bind(this))
.on('error', err => { this.__handleError(err) })
@@ -202,6 +213,7 @@ class Task extends EventEmitter {
this.ws = fs.createWriteStream(this.chunkInfo.path, options)
this.ws.on('finish', () => {
if (this.closeWaiting) return
void this.__closeWriteStream()
})
this.ws.on('error', err => {
@@ -217,6 +229,12 @@ class Task extends EventEmitter {
__handleComplete() {
if (this.status == STATUS.error) return
this.__clearTimeout()
if (this.progress.progress <= 0) {
this.status = STATUS.error
this.emit('error', new Error('Progress is 0, download failed.'))
return
}
void this.__closeWriteStream().then(() => {
if (this.progress.downloaded == this.progress.total) {
this.status = STATUS.completed
@@ -232,6 +250,7 @@ class Task extends EventEmitter {
__handleError(error: Error) {
if (this.status == STATUS.error) return
this.status = STATUS.error
this.__clearTimeout()
this.__closeRequest()
void this.__closeWriteStream()
if (error.message == 'aborted') return
@@ -245,16 +264,21 @@ class Task extends EventEmitter {
return
}
// console.log('close write stream')
this.ws.close(err => {
if (err) {
this.status = STATUS.error
this.emit('error', err)
reject(err)
return
}
this.ws = null
resolve()
})
if (this.closeWaiting || this.dataWriteQueueLength) {
this.closeWaiting ||= true
this.ws.on('close', resolve)
} else {
this.ws.close(err => {
if (err) {
this.status = STATUS.error
this.emit('error', err)
reject(err)
return
}
this.ws = null
resolve()
})
}
})
}
@@ -293,12 +317,18 @@ class Task extends EventEmitter {
console.log('cancel write')
return
}
this.dataWriteQueueLength++
this.__startTimeout()
this.__calculateProgress(chunk.length)
this.ws.write(chunk, err => {
if (!err) return
console.log(err)
this.__handleError(err)
void this.stop()
this.dataWriteQueueLength--
if (this.status == STATUS.running) this.__calculateProgress(0)
if (err) {
console.log(err)
this.__handleError(err)
return
}
if (this.closeWaiting && !this.dataWriteQueueLength) this.ws?.close()
})
}
@@ -308,36 +338,36 @@ class Task extends EventEmitter {
let chunkLen = chunk.length
let isOk
if (chunkLen >= resumeLastChunkLen) {
isOk = chunk.slice(0, resumeLastChunkLen).toString('hex') === this.resumeLastChunk!.toString('hex')
isOk = chunk.subarray(0, resumeLastChunkLen).toString('hex') === this.resumeLastChunk!.toString('hex')
if (!isOk) return null
this.resumeLastChunk = null
return chunk.slice(resumeLastChunkLen)
return chunk.subarray(resumeLastChunkLen)
} else {
isOk = chunk.slice(0, chunkLen).toString('hex') === this.resumeLastChunk!.slice(0, chunkLen).toString('hex')
isOk = chunk.subarray(0, chunkLen).toString('hex') === this.resumeLastChunk!.subarray(0, chunkLen).toString('hex')
if (!isOk) return null
this.resumeLastChunk = this.resumeLastChunk!.slice(chunkLen)
return chunk.slice(chunkLen)
this.resumeLastChunk = this.resumeLastChunk!.subarray(chunkLen)
return chunk.subarray(chunkLen)
}
}
async __handleStop() {
return new Promise<void>((resolve, reject) => {
this.__closeRequest()
if (this.ws) {
this.ws.close(err => {
if (err) {
reject(err)
this.emit('error', err)
return
}
this.ws = null
resolve()
})
} else {
resolve()
}
})
this.__clearTimeout()
this.__closeRequest()
return this.__closeWriteStream()
}
private __clearTimeout() {
if (!this.timeout) return
clearTimeout(this.timeout)
this.timeout = null
}
private __startTimeout() {
this.__clearTimeout()
this.timeout = setTimeout(() => {
this.__handleError(new Error('download timeout'))
}, this.options.timeout)
}
__calculateProgress(receivedBytes: number) {
@@ -350,7 +380,7 @@ class Task extends EventEmitter {
// emit the progress every second or if finished
if (progress.downloaded === progress.total || elaspsedTime > 1000) {
if ((progress.downloaded === progress.total && this.dataWriteQueueLength == 0) || elaspsedTime > 1000) {
this.statsEstimate.time = currentTime
this.statsEstimate.bytes = progress.downloaded - this.statsEstimate.prevBytes
this.statsEstimate.prevBytes = progress.downloaded
@@ -359,6 +389,7 @@ class Task extends EventEmitter {
downloaded: progress.downloaded,
progress: progress.progress,
speed: this.statsEstimate.bytes,
writeQueue: this.dataWriteQueueLength,
})
}
}

View File

@@ -74,6 +74,7 @@ export const createDownload = ({
speed,
downloaded: stats.downloaded,
total: stats.total,
writeQueue: stats.writeQueue,
})
// if (debugDownload) {
// const downloaded = sizeFormate(stats.downloaded)

View File

@@ -27,7 +27,7 @@ const sendRequest = (url: string, options: Options, callback?: HttpCallback) =>
}
if (options.params) {
(httpOptions.path as string) += `${urlParse.search ? '&' : '?'}${Object.entries(options.params)
(httpOptions.path!) += `${urlParse.search ? '&' : '?'}${Object.entries(options.params)
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join('&')}`
}

View File

@@ -1,4 +1,4 @@
import log from 'electron-log'
import log from 'electron-log/node'
export const isLinux = process.platform == 'linux'
@@ -6,6 +6,14 @@ export const isWin = process.platform == 'win32'
export const isMac = process.platform == 'darwin'
export const isProd = process.env.NODE_ENV == 'production'
export const getPlatform = (platform: NodeJS.Platform = process.platform) => {
switch (platform) {
case 'win32': return 'windows'
case 'darwin': return 'mac'
default: return 'linux'
}
}
// https://stackoverflow.com/a/53387532
export function compareVer(currentVer: string, targetVer: string): -1 | 0 | 1 {

View File

@@ -227,4 +227,8 @@ export default class Lyric {
this._handleLinePlayerOnPlay(num, '', this.linePlayer._currentTime())
} else this.playingLineNum = 0
}
setDisabledAutoPause(autoPause) {
this.linePlayer.setDisabledAutoPause(autoPause)
}
}

View File

@@ -224,4 +224,16 @@ export default class LinePlayer {
this.extendedLyrics = extendedLyrics
this._init()
}
setDisabledAutoPause(disabledAutoPause) {
if (disabledAutoPause) {
timeoutTools.nextTick = (handler) => {
return setTimeout(handler, 20)
}
timeoutTools.cancelNextTick = clearTimeout.bind(global)
} else {
timeoutTools.nextTick = window.requestAnimationFrame.bind(window)
timeoutTools.cancelNextTick = window.cancelAnimationFrame.bind(window)
}
}
}

View File

@@ -3,6 +3,8 @@ export const getNow = typeof performance == 'object' && window.performance.now ?
export class TimeoutTools {
constructor(thresholdTime = 80) {
this.nextTick = window.requestAnimationFrame.bind(window)
this.cancelNextTick = window.cancelAnimationFrame.bind(window)
this.invokeTime = 0
this.animationFrameId = null
this.timeoutId = null
@@ -11,7 +13,7 @@ export class TimeoutTools {
}
run() {
this.animationFrameId = window.requestAnimationFrame(() => {
this.animationFrameId = this.nextTick(() => {
this.animationFrameId = null
let diff = this.invokeTime - getNow()
// console.log('diff', diff)
@@ -39,7 +41,7 @@ export class TimeoutTools {
clear() {
if (this.animationFrameId) {
window.cancelAnimationFrame(this.animationFrameId)
this.cancelNextTick(this.animationFrameId)
this.animationFrameId = null
}
if (this.timeoutId) {

View File

@@ -0,0 +1,74 @@
import { inflate } from 'zlib'
import { decodeName } from './util'
// 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')
const decodeLyric = str => new Promise((resolve, reject) => {
if (!str.length) return
const buf_str = Buffer.from(str, 'base64').subarray(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())
})
})
const headExp = /^.*\[id:\$\w+\]\n/
const parseLyric = str => {
str = str.replace(/\r/g, '')
if (headExp.test(str)) str = str.replace(headExp, '')
let trans = str.match(/\[language:([\w=\\/+]+)\]/)
let lyric
let rlyric
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) {
switch (item.type) {
case 0:
rlyric = item.lyricContent
break
case 1:
tlyric = item.lyricContent
break
}
}
}
let i = 0
let lxlyric = str.replace(/\[((\d+),\d+)\].*/g, str => {
let result = str.match(/\[((\d+),\d+)\].*/)
let time = parseInt(result[2])
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}`
if (rlyric) rlyric[i] = `[${time}]${rlyric[i]?.join('') ?? ''}`
if (tlyric) tlyric[i] = `[${time}]${tlyric[i]?.join('') ?? ''}`
i++
return str.replace(result[1], time)
})
rlyric = rlyric ? rlyric.join('\n') : ''
tlyric = tlyric ? tlyric.join('\n') : ''
lxlyric = lxlyric.replace(/<(\d+,\d+),\d+>/g, '<$1>')
lxlyric = decodeName(lxlyric)
lyric = lxlyric.replace(/<\d+,\d+>/g, '')
rlyric = decodeName(rlyric)
tlyric = decodeName(tlyric)
return {
lyric,
tlyric,
rlyric,
lxlyric,
}
}
export const decodeKrc = async(data) => {
return decodeLyric(data).then(parseLyric)
}

View File

@@ -0,0 +1,13 @@
const encodeNames = {
'&nbsp;': ' ',
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&apos;': "'",
'&#039;': "'",
} as const
export const decodeName = (str: string | null = '') => {
return str?.replace(/(?:&amp;|&lt;|&gt;|&quot;|&apos;|&#039;|&nbsp;)/gm, (s: string) => encodeNames[s as keyof typeof encodeNames]) ?? ''
}

View File

@@ -57,7 +57,6 @@ export default (setting: any): Partial<LX.AppSetting> => {
setting['common.controlBtnPosition'] = setting.controlBtnPosition
setting['player.togglePlayMethod'] = setting.player?.togglePlayMethod
setting['player.highQuality'] = setting.player?.highQuality
setting['player.isShowTaskProgess'] = setting.player?.isShowTaskProgess
setting['player.volume'] = setting.player?.volume
setting['player.isMute'] = setting.player?.isMute
@@ -117,8 +116,6 @@ export default (setting: any): Partial<LX.AppSetting> => {
setting['network.proxy.enable'] = setting.network?.proxy?.enable
setting['network.proxy.host'] = setting.network?.proxy?.host
setting['network.proxy.port'] = setting.network?.proxy?.port
setting['network.proxy.username'] = setting.network?.proxy?.username
setting['network.proxy.password'] = setting.network?.proxy?.password
setting['tray.enable'] = setting.tray?.enable
setting['tray.themeId'] = setting.tray?.themeId

View File

@@ -1,14 +1,21 @@
const http = require('http')
const https = require('https')
const fs = require('fs')
const { httpOverHttp, httpsOverHttp } = require('tunnel')
const sendRequest = (url) => {
const httpsRxp = /^https:/
const getRequestAgent = (url, proxy) => {
return proxy ? (httpsRxp.test(url) ? httpsOverHttp : httpOverHttp)({ proxy }) : undefined
}
const sendRequest = (url, proxy) => {
const urlParse = new URL(url)
const httpOptions = {
method: 'get',
host: urlParse.hostname,
port: urlParse.port,
path: urlParse.pathname + urlParse.search,
agent: getRequestAgent(url, proxy),
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
},
@@ -20,9 +27,9 @@ const sendRequest = (url) => {
: http.request(httpOptions)
}
module.exports = (url, filePath) => {
module.exports = (url, filePath, proxy) => {
return new Promise((resolve) => {
sendRequest(url)
sendRequest(url, proxy)
.on('response', response => {
// console.log(response.statusCode)
if (response.statusCode !== 200 && response.statusCode != 206) {

View File

@@ -57,9 +57,9 @@ const writeMeta = async(filePath, meta, picPath) => {
})
}
module.exports = (filePath, meta) => {
module.exports = (filePath, meta, proxy) => {
if (!meta.APIC) return writeMeta(filePath, meta)
const picUrl = meta.APIC
let picUrl = meta.APIC
delete meta.APIC
if (!/^http/.test(picUrl)) {
return writeMeta(filePath, meta)
@@ -67,7 +67,8 @@ module.exports = (filePath, meta) => {
let ext = path.extname(picUrl)
let picPath = filePath.replace(/\.flac$/, '') + (ext ? ext.replace(extReg, '$1') : '.jpg')
download(picUrl, picPath).then(success => {
if (picUrl.includes('music.126.net')) picUrl += `${picUrl.includes('?') ? '&' : '?'}param=500y500`
download(picUrl, picPath, proxy).then(success => {
if (success) {
writeMeta(filePath, meta, picPath).finally(() => {
fs.unlink(picPath, err => {

View File

@@ -5,4 +5,4 @@ export interface MusicMeta {
APIC: string | null
lyrics: string | null
}
export function setMeta(filePath: string, meta: MusicMeta): void
export function setMeta(filePath: string, meta: MusicMeta, proxy?: { host: string, port: number }): void

View File

@@ -2,13 +2,13 @@ const path = require('path')
const mp3Meta = require('./mp3Meta')
const flacMeta = require('./flacMeta')
exports.setMeta = (filePath, meta) => {
exports.setMeta = (filePath, meta, proxy) => {
switch (path.extname(filePath)) {
case '.mp3':
mp3Meta(filePath, meta)
mp3Meta(filePath, meta, proxy)
break
case '.flac':
flacMeta(filePath, meta)
flacMeta(filePath, meta, proxy)
break
}
}

View File

@@ -15,7 +15,7 @@ const handleWriteMeta = (meta, filePath) => {
NodeID3.write(meta, filePath)
}
module.exports = (filePath, meta) => {
module.exports = (filePath, meta, proxy) => {
if (!meta.APIC) return handleWriteMeta(meta, filePath)
if (!/^http/.test(meta.APIC)) {
delete meta.APIC
@@ -23,7 +23,10 @@ module.exports = (filePath, meta) => {
}
let ext = path.extname(meta.APIC)
let picPath = filePath.replace(/\.mp3$/, '') + (ext ? ext.replace(extReg, '$1') : '.jpg')
download(meta.APIC, picPath).then(success => {
let picUrl = meta.APIC
if (picUrl.includes('music.126.net')) picUrl += `${picUrl.includes('?') ? '&' : '?'}param=500y500`
download(picUrl, picPath, proxy).then(success => {
if (success) {
meta.APIC = picPath
handleWriteMeta(meta, filePath)

View File

@@ -2,6 +2,7 @@ import fs from 'node:fs'
import crypto from 'node:crypto'
import { gzip, gunzip } from 'node:zlib'
import path from 'node:path'
import { networkInterfaces } from 'node:os'
import { log } from '@common/utils'
export const joinPath = (...paths: string[]): string => path.join(...paths)
@@ -30,6 +31,25 @@ export const checkPath = async(path: string): Promise<boolean> => {
})
}
/**
* 检查路径并创建目录
* @param path
* @returns
*/
export const checkAndCreateDir = async(path: string) => {
return fs.promises.access(path, fs.constants.F_OK | fs.constants.W_OK)
.catch(async(err: NodeJS.ErrnoException) => {
if (err.code != 'ENOENT') throw err as Error
return fs.promises.mkdir(path, { recursive: true })
})
.then(() => true)
.catch((err) => {
console.error(err)
return false
})
}
export const getFileStats = async(path: string): Promise<fs.Stats | null> => {
return new Promise(resolve => {
if (!path) {
@@ -140,8 +160,8 @@ export const saveLxConfigFile = async(path: string, data: any) => {
export const readLxConfigFile = async(path: string): Promise<any> => {
let isJSON = path.endsWith('.json')
let data: string | Buffer = await fs.promises.readFile(path, isJSON ? 'utf8' : 'binary')
if (!data || isJSON) return data
data = await gunzipData(Buffer.from(data, 'binary'))
if (!data) return data
if (!isJSON) data = await gunzipData(Buffer.from(data, 'binary'))
data = JSON.parse(data)
// 修复v1.14.0出现的导出数据被序列化两次的问题
@@ -185,3 +205,20 @@ export const copyFile = async(sourcePath: string, distPath: string) => {
export const moveFile = async(sourcePath: string, distPath: string) => {
return fs.promises.rename(sourcePath, distPath)
}
export const getAddress = (): string[] => {
const nets = networkInterfaces()
const results: string[] = []
// console.log(nets)
for (const interfaceInfos of Object.values(nets)) {
if (!interfaceInfos) continue
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
for (const interfaceInfo of interfaceInfos) {
if (interfaceInfo.family === 'IPv4' && !interfaceInfo.internal) {
results.push(interfaceInfo.address)
}
}
}
return results
}

View File

@@ -342,7 +342,7 @@ export const scrollXRTo = (element: ScrollElement<HTMLElement>, to: number, dura
*/
let dom_title = document.getElementsByTagName('title')[0]
export const setTitle = (title: string | null) => {
title ||= '洛雪音乐助手'
title ||= 'LX Music'
dom_title.innerText = title
}

274
src/common/utils/request.ts Normal file
View File

@@ -0,0 +1,274 @@
import qs from 'node:querystring'
import {
FormData,
getGlobalDispatcher,
interceptors,
request as nodeRrequest,
ProxyAgent,
setGlobalDispatcher,
type Dispatcher,
} from 'undici'
const defaultOptions: Options = {
timeout: 15000,
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
},
maxRedirect: 5,
} as const
const redirectDispatcher = interceptors.redirect({ maxRedirections: defaultOptions.maxRedirect })
const dispatchers = [
interceptors.retry({
maxRetries: 3,
minTimeout: 1000,
maxTimeout: 10000,
timeoutFactor: 2,
retryAfter: true,
}),
// interceptors.responseError(),
] as const
let proxyAgent: ProxyAgent | null = null
let globalDispatcher = getGlobalDispatcher()
const buildDispatcher = (redirectDispatcher: Dispatcher.DispatcherComposeInterceptor | null, retryNum = 3) => {
const otherInterceptors =
retryNum == 3
? dispatchers
: [
interceptors.retry({
maxRetries: retryNum,
minTimeout: 1000,
maxTimeout: 6000,
timeoutFactor: 2,
retryAfter: true,
}),
]
if (redirectDispatcher) {
return (proxyAgent ?? globalDispatcher).compose(redirectDispatcher, ...otherInterceptors)
}
return (proxyAgent ?? globalDispatcher).compose(...otherInterceptors)
}
setGlobalDispatcher(buildDispatcher(redirectDispatcher))
export const setProxy = (url?: string) => {
proxyAgent = url ? new ProxyAgent(url) : null
setGlobalDispatcher(buildDispatcher(redirectDispatcher))
}
export const setProxyByHost = (host?: string, port?: string) => {
setProxy(host ? `http://${host}:${port}` : undefined)
}
const CONTENT_TYPE = {
json: 'application/json',
form: 'application/x-www-form-urlencoded',
text: 'text/plain',
formdata: 'multipart/form-data',
xml: 'application/xml',
binary: 'application/octet-stream',
}
type ParamsData = Record<string, string | number | null | undefined | boolean>
export interface Options {
method?:
| 'GET'
| 'HEAD'
| 'POST'
| 'PUT'
| 'DELETE'
| 'OPTIONS'
| 'PATCH'
| 'PROPFIND'
| 'COPY'
| 'MOVE'
| 'MKCOL'
| 'PROPPATCH'
| 'QUOTA'
query?: ParamsData
headers?: Record<string, string | string[]>
timeout?: number
maxRedirect?: number
signal?: AbortController['signal']
json?: Record<string, unknown>
form?: ParamsData
binary?: Buffer | Uint8Array
text?: string
formdata?: Record<string, string | Buffer | Uint8Array>
xml?: string
needBody?: boolean
needRaw?: boolean
retryNum?: number
}
export interface Response<Res> {
headers: {
accept?: string | undefined
'accept-language'?: string | undefined
'accept-patch'?: string | undefined
'accept-ranges'?: string | undefined
'access-control-allow-credentials'?: string | undefined
'access-control-allow-headers'?: string | undefined
'access-control-allow-methods'?: string | undefined
'access-control-allow-origin'?: string | undefined
'access-control-expose-headers'?: string | undefined
'access-control-max-age'?: string | undefined
'access-control-request-headers'?: string | undefined
'access-control-request-method'?: string | undefined
age?: string | undefined
allow?: string | undefined
'alt-svc'?: string | undefined
authorization?: string | undefined
'cache-control'?: string | undefined
connection?: string | undefined
'content-disposition'?: string | undefined
'content-encoding'?: string | undefined
'content-language'?: string | undefined
'content-length'?: string | undefined
'content-location'?: string | undefined
'content-range'?: string | undefined
'content-type'?: string | undefined
cookie?: string | undefined
date?: string | undefined
etag?: string | undefined
expect?: string | undefined
expires?: string | undefined
forwarded?: string | undefined
from?: string | undefined
host?: string | undefined
'if-match'?: string | undefined
'if-modified-since'?: string | undefined
'if-none-match'?: string | undefined
'if-unmodified-since'?: string | undefined
'last-modified'?: string | undefined
location?: string | undefined
origin?: string | undefined
pragma?: string | undefined
'proxy-authenticate'?: string | undefined
'proxy-authorization'?: string | undefined
'public-key-pins'?: string | undefined
range?: string | undefined
referer?: string | undefined
'retry-after'?: string | undefined
'sec-websocket-accept'?: string | undefined
'sec-websocket-extensions'?: string | undefined
'sec-websocket-key'?: string | undefined
'sec-websocket-protocol'?: string | undefined
'sec-websocket-version'?: string | undefined
'set-cookie'?: string[] | undefined
'strict-transport-security'?: string | undefined
tk?: string | undefined
trailer?: string | undefined
'transfer-encoding'?: string | undefined
upgrade?: string | undefined
'user-agent'?: string | undefined
vary?: string | undefined
via?: string | undefined
warning?: string | undefined
'www-authenticate'?: string | undefined
}
body: Res
raw: Uint8Array
statusCode?: number
statusMessage?: string
}
const buildRequestBody = (options: Options) => {
let contentType: string | undefined
let body: string | Buffer | Uint8Array | FormData | undefined
if (options.json != null) {
contentType = CONTENT_TYPE.json
body = JSON.stringify(options.json)
} else if (options.form != null) {
contentType = CONTENT_TYPE.form
body = qs.stringify(options.form)
} else if (options.binary != null) {
contentType = CONTENT_TYPE.binary
body = options.binary
} else if (options.formdata != null) {
const formdata = new FormData()
// eslint-disable-next-line guard-for-in
for (const key in options.formdata) {
formdata.append(key, options.formdata[key])
}
contentType = CONTENT_TYPE.formdata
body = formdata
} else if (options.xml != null) {
contentType = CONTENT_TYPE.xml
body = options.xml
} else if (options.text != null) {
contentType = CONTENT_TYPE.text
body = options.text
}
const headers = options.headers ?? {}
if (headers['Content-Type']) {
contentType = headers['Content-Type'] as string
}
const finalHeaders: NonNullable<Options['headers']> = { ...defaultOptions.headers, ...headers }
if (contentType) finalHeaders['Content-Type'] = contentType
return [finalHeaders, body] as const
}
const buildRequestDispatcher = (options: Options) => {
let dispatcher: Dispatcher.ComposedDispatcher | undefined
if (options.maxRedirect != null) {
if (options.maxRedirect != defaultOptions.maxRedirect) {
if (options.maxRedirect) {
dispatcher = buildDispatcher(interceptors.redirect({ maxRedirections: options.maxRedirect }), options.retryNum)
} else {
dispatcher = buildDispatcher(null, options.retryNum)
}
}
}
return dispatcher
}
export const request = async <T = unknown>(url: string, options: Options = {}): Promise<Response<T>> => {
const method = options.method?.toUpperCase() ?? 'GET'
const timeout = options.timeout ?? defaultOptions.timeout
const [headers, body] = buildRequestBody(options)
// console.log(url, {
// method,
// bodyTimeout: timeout,
// headersTimeout: timeout,
// headers,
// query: options.query,
// body,
// signal: options.signal,
// dispatcher: buildRequestDispatcher(options),
// })
return nodeRrequest(url, {
method,
bodyTimeout: timeout,
headersTimeout: timeout,
headers,
query: options.query,
body,
signal: options.signal,
dispatcher: buildRequestDispatcher(options),
}).then(async(response) => {
if (options.needBody) {
return {
headers: response.headers,
statusCode: response.statusCode,
body: response.body as unknown as T,
} satisfies Omit<Response<T>, 'raw'> as Response<T>
}
if (options.needRaw) {
return {
headers: response.headers,
statusCode: response.statusCode,
raw: await response.body.bytes(),
} satisfies Omit<Response<T>, 'body'> as Response<T>
}
// console.log(response)
let body = (await response.body.text()) as T
if (!headers['Content-Type'] || headers['Content-Type'].includes(CONTENT_TYPE.json)) {
try {
body = JSON.parse(body as string) as T
} catch {}
}
return {
body,
headers: response.headers,
statusCode: response.statusCode,
} satisfies Omit<Response<T>, 'raw'> as Response<T>
})
}

View File

@@ -0,0 +1,237 @@
import qs from 'node:querystring'
import {
FormData,
getGlobalDispatcher,
request as nodeRrequest,
ProxyAgent,
setGlobalDispatcher,
type Dispatcher,
} from 'undici'
const defaultOptions: Options = {
timeout: 15000,
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
},
maxRedirect: 5,
} as const
let proxyAgent: ProxyAgent | null = null
let globalDispatcher = getGlobalDispatcher()
const buildDispatcher = () => {
return proxyAgent ?? globalDispatcher
}
setGlobalDispatcher(buildDispatcher())
export const setProxy = (url?: string) => {
proxyAgent = url ? new ProxyAgent(url) : null
setGlobalDispatcher(buildDispatcher())
}
export const setProxyByHost = (host?: string, port?: string) => {
console.log(host)
setProxy(host ? `http://${host}:${port}` : undefined)
}
const CONTENT_TYPE = {
json: 'application/json',
form: 'application/x-www-form-urlencoded',
text: 'text/plain',
formdata: 'multipart/form-data',
xml: 'application/xml',
binary: 'application/octet-stream',
}
type ParamsData = Record<string, string | number | null | undefined | boolean>
export interface Options {
method?:
| 'GET'
| 'HEAD'
| 'POST'
| 'PUT'
| 'DELETE'
| 'OPTIONS'
| 'PATCH'
| 'PROPFIND'
| 'COPY'
| 'MOVE'
| 'MKCOL'
| 'PROPPATCH'
| 'QUOTA'
query?: ParamsData
headers?: Record<string, string | string[]>
timeout?: number
maxRedirect?: number
signal?: AbortController['signal']
json?: Record<string, unknown>
form?: ParamsData
binary?: Buffer | Uint8Array
text?: string
formdata?: Record<string, string | Buffer | Uint8Array>
xml?: string
needBody?: boolean
needRaw?: boolean
retryNum?: number
}
export interface Response<Res> {
headers: {
accept?: string | undefined
'accept-language'?: string | undefined
'accept-patch'?: string | undefined
'accept-ranges'?: string | undefined
'access-control-allow-credentials'?: string | undefined
'access-control-allow-headers'?: string | undefined
'access-control-allow-methods'?: string | undefined
'access-control-allow-origin'?: string | undefined
'access-control-expose-headers'?: string | undefined
'access-control-max-age'?: string | undefined
'access-control-request-headers'?: string | undefined
'access-control-request-method'?: string | undefined
age?: string | undefined
allow?: string | undefined
'alt-svc'?: string | undefined
authorization?: string | undefined
'cache-control'?: string | undefined
connection?: string | undefined
'content-disposition'?: string | undefined
'content-encoding'?: string | undefined
'content-language'?: string | undefined
'content-length'?: string | undefined
'content-location'?: string | undefined
'content-range'?: string | undefined
'content-type'?: string | undefined
cookie?: string | undefined
date?: string | undefined
etag?: string | undefined
expect?: string | undefined
expires?: string | undefined
forwarded?: string | undefined
from?: string | undefined
host?: string | undefined
'if-match'?: string | undefined
'if-modified-since'?: string | undefined
'if-none-match'?: string | undefined
'if-unmodified-since'?: string | undefined
'last-modified'?: string | undefined
location?: string | undefined
origin?: string | undefined
pragma?: string | undefined
'proxy-authenticate'?: string | undefined
'proxy-authorization'?: string | undefined
'public-key-pins'?: string | undefined
range?: string | undefined
referer?: string | undefined
'retry-after'?: string | undefined
'sec-websocket-accept'?: string | undefined
'sec-websocket-extensions'?: string | undefined
'sec-websocket-key'?: string | undefined
'sec-websocket-protocol'?: string | undefined
'sec-websocket-version'?: string | undefined
'set-cookie'?: string[] | undefined
'strict-transport-security'?: string | undefined
tk?: string | undefined
trailer?: string | undefined
'transfer-encoding'?: string | undefined
upgrade?: string | undefined
'user-agent'?: string | undefined
vary?: string | undefined
via?: string | undefined
warning?: string | undefined
'www-authenticate'?: string | undefined
}
body: Res
raw: Uint8Array
statusCode?: number
statusMessage?: string
}
const buildRequestBody = (options: Options) => {
let contentType: string | undefined
let body: string | Buffer | Uint8Array | FormData | undefined
if (options.json != null) {
contentType = CONTENT_TYPE.json
body = JSON.stringify(options.json)
} else if (options.form != null) {
contentType = CONTENT_TYPE.form
body = qs.stringify(options.form)
} else if (options.binary != null) {
contentType = CONTENT_TYPE.binary
body = options.binary
} else if (options.formdata != null) {
const formdata = new FormData()
// eslint-disable-next-line guard-for-in
for (const key in options.formdata) {
formdata.append(key, options.formdata[key])
}
contentType = CONTENT_TYPE.formdata
body = formdata
} else if (options.xml != null) {
contentType = CONTENT_TYPE.xml
body = options.xml
} else if (options.text != null) {
contentType = CONTENT_TYPE.text
body = options.text
}
const headers = options.headers ?? {}
if (headers['Content-Type']) {
contentType = headers['Content-Type'] as string
}
const finalHeaders: NonNullable<Options['headers']> = { ...defaultOptions.headers, ...headers }
if (contentType) finalHeaders['Content-Type'] = contentType
return [finalHeaders, body] as const
}
const buildRequestDispatcher = (options: Options) => {
return buildDispatcher()
}
export const request = async <T = unknown>(url: string, options: Options = {}): Promise<Response<T>> => {
const method = (options.method?.toUpperCase() ?? 'GET') as Dispatcher.RequestOptions['method']
const timeout = options.timeout ?? defaultOptions.timeout
const [headers, body] = buildRequestBody(options)
// console.log(url, {
// method,
// bodyTimeout: timeout,
// headersTimeout: timeout,
// headers,
// query: options.query,
// body,
// signal: options.signal,
// dispatcher: buildRequestDispatcher(options),
// })
return nodeRrequest(url, {
method,
bodyTimeout: timeout,
headersTimeout: timeout,
headers,
query: options.query,
body,
signal: options.signal,
dispatcher: buildRequestDispatcher(options),
}).then(async(response) => {
if (options.needBody) {
return {
headers: response.headers,
statusCode: response.statusCode,
body: response.body as unknown as T,
} satisfies Omit<Response<T>, 'raw'> as Response<T>
}
if (options.needRaw) {
return {
headers: response.headers,
statusCode: response.statusCode,
raw: new Uint8Array(await response.body.arrayBuffer()),
} satisfies Omit<Response<T>, 'body'> as unknown as Response<T>
}
// console.log(response)
let body = (await response.body.text()) as T
if (!headers['Content-Type'] || headers['Content-Type'].includes(CONTENT_TYPE.json)) {
try {
body = JSON.parse(body as string) as T
} catch {}
}
return {
body,
headers: response.headers,
statusCode: response.statusCode,
} satisfies Omit<Response<T>, 'raw'> as Response<T>
})
}

View File

@@ -6,6 +6,14 @@ export const toNewMusicInfo = (oldMusicInfo: any): LX.Music.MusicInfo => {
albumName: oldMusicInfo.albumName, // 歌曲专辑名称
picUrl: oldMusicInfo.img, // 歌曲图片链接
}
const newInfo = {
id: `${oldMusicInfo.source}_${oldMusicInfo.songmid}`,
name: oldMusicInfo.name,
singer: oldMusicInfo.singer,
source: oldMusicInfo.source,
interval: oldMusicInfo.interval,
meta: meta as LX.Music.MusicInfoOnline['meta'],
}
if (oldMusicInfo.source == 'local') {
meta.filePath = oldMusicInfo.filePath ?? oldMusicInfo.songmid ?? ''
@@ -27,6 +35,7 @@ export const toNewMusicInfo = (oldMusicInfo: any): LX.Music.MusicInfo => {
switch (oldMusicInfo.source) {
case 'kg':
meta.hash = oldMusicInfo.hash
newInfo.id = oldMusicInfo.songmid + '_' + oldMusicInfo.hash
break
case 'tx':
meta.strMediaMid = oldMusicInfo.strMediaMid
@@ -42,14 +51,7 @@ export const toNewMusicInfo = (oldMusicInfo: any): LX.Music.MusicInfo => {
}
}
return {
id: `${oldMusicInfo.source as string}_${oldMusicInfo.songmid as string}`,
name: oldMusicInfo.name,
singer: oldMusicInfo.singer,
source: oldMusicInfo.source,
interval: oldMusicInfo.interval,
meta: meta as LX.Music.MusicInfoOnline['meta'],
}
return newInfo
}
export const toOldMusicInfo = (minfo: LX.Music.MusicInfo) => {
@@ -128,3 +130,21 @@ export const filterMusicList = <T extends LX.Music.MusicInfo>(list: T[]): T[] =>
return true
})
}
const MAX_NAME_LENGTH = 80
const MAX_FILE_NAME_LENGTH = 150
export const clipNameLength = (name: string) => {
if (name.length <= MAX_NAME_LENGTH || !name.includes('、')) return name
const names = name.split('、')
let newName = names.shift()!
for (const name of names) {
if (newName.length + name.length > MAX_NAME_LENGTH) break
newName = newName + '、' + name
}
return newName
}
export const clipFileNameLength = (name: string) => {
return name.length > MAX_FILE_NAME_LENGTH ? name.substring(0, MAX_FILE_NAME_LENGTH) : name
}

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,7 @@ const langs = [
message: zh_cn,
},
{
name: '繁中文',
name: '繁中文',
locale: 'zh-tw',
// alternate: 'zh-hk',
country: 'cn',

View File

@@ -7,7 +7,7 @@
"fallback": true
},
{
"name": "繁中文",
"name": "繁中文",
"locale": "zh-tw",
"alternate": "zh-hk",
"country": "cn"

Some files were not shown because too many files have changed in this diff Show More