Compare commits
200
Commits
1e03791e82
...
dd23af7051
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd23af7051 | ||
|
|
7d5fc6de12 | ||
|
|
28233fbe58 | ||
|
|
32a35c6706 | ||
|
|
228ddace91 | ||
|
|
f9ffdbbce7 | ||
|
|
9b233683a2 | ||
|
|
ff78fb6b30 | ||
|
|
8efda6478c | ||
|
|
851c7871e3 | ||
|
|
a78c78f3e5 | ||
|
|
05e8442ff1 | ||
|
|
977b842df3 | ||
|
|
5bde54aac1 | ||
|
|
0f27e3ea42 | ||
|
|
d170725f0c | ||
|
|
a26fcf0cd8 | ||
|
|
18a4455511 | ||
|
|
2ab3286d0e | ||
|
|
d4a411ab4a | ||
|
|
c7facb38b1 | ||
|
|
b7434e9eaf | ||
|
|
4a174c0084 | ||
|
|
db8ad1e8eb | ||
|
|
1feaac003a | ||
|
|
c42b2fb34c | ||
|
|
5d48fa93ba | ||
|
|
527ac04c4e | ||
|
|
9644163dc5 | ||
|
|
d0090abd01 | ||
|
|
918d2ad156 | ||
|
|
b028fb955f | ||
|
|
ba33d5a39b | ||
|
|
fd0b9c55fc | ||
|
|
b28fb5c9b4 | ||
|
|
77ecd4be9b | ||
|
|
6f74003e14 | ||
|
|
63b2c2fb2f | ||
|
|
08da7a37e6 | ||
|
|
6b87a8d91b | ||
|
|
0f1364a8d7 | ||
|
|
9dbf70780d | ||
|
|
58e5592c2b | ||
|
|
02cc842cc8 | ||
|
|
e962e866b3 | ||
|
|
af3b30096d | ||
|
|
aa7ab86b62 | ||
|
|
7f864a9e35 | ||
|
|
609d345443 | ||
|
|
426815fa50 | ||
|
|
5d1b1e48d7 | ||
|
|
28d0412adc | ||
|
|
9d070b3671 | ||
|
|
cd423752d1 | ||
|
|
1f8a0eaf67 | ||
|
|
42d9fcd8b0 | ||
|
|
4e1510c840 | ||
|
|
7ebfd43f65 | ||
|
|
41d6157116 | ||
|
|
f9f8297fd6 | ||
|
|
dfbe3f665a | ||
|
|
b36df472e5 | ||
|
|
0b147f708d | ||
|
|
4e3bdbcc74 | ||
|
|
0345419ef3 | ||
|
|
9237dbbc18 | ||
|
|
68ac1b7641 | ||
|
|
2cfe4d29ba | ||
|
|
b436497555 | ||
|
|
7ac638c52c | ||
|
|
f21d2bc7c7 | ||
|
|
550917bd16 | ||
|
|
c1c0311586 | ||
|
|
489bbb6902 | ||
|
|
86af25bde3 | ||
|
|
a599e0716f | ||
|
|
30fc818771 | ||
|
|
df08f677bd | ||
|
|
7247bcad1b | ||
|
|
c0bcac25ea | ||
|
|
ae06019bdb | ||
|
|
432861780a | ||
|
|
111ee88833 | ||
|
|
487ee7015e | ||
|
|
061cc16c27 | ||
|
|
af8d9dff2d | ||
|
|
cb16bd0324 | ||
|
|
acf8171617 | ||
|
|
da7429f03e | ||
|
|
4a121db39d | ||
|
|
0e8e287fe2 | ||
|
|
cf942121dd | ||
|
|
77c2e962ec | ||
|
|
05f5cc4fe8 | ||
|
|
684f09feaf | ||
|
|
38bea3ced1 | ||
|
|
6a6f9a53bd | ||
|
|
584c12e60f | ||
|
|
bb5b667613 | ||
|
|
0628d3533f | ||
|
|
e1bd4c9a0a | ||
|
|
85c9ec38db | ||
|
|
6ff7cfde11 | ||
|
|
c52034f992 | ||
|
|
b982dd90e4 | ||
|
|
80e36688e8 | ||
|
|
c6c1071d2d | ||
|
|
481817c607 | ||
|
|
2e5739f042 | ||
|
|
0bb9f5ece5 | ||
|
|
d454f51993 | ||
|
|
7f3ab31ad5 | ||
|
|
98fe433819 | ||
|
|
4b01326420 | ||
|
|
3a4acd2693 | ||
|
|
b8e9937083 | ||
|
|
72d44ec783 | ||
|
|
680b60cb07 | ||
|
|
45527b320d | ||
|
|
0acc9a553e | ||
|
|
19f81cf379 | ||
|
|
96ddbdf0f0 | ||
|
|
9eb87911a6 | ||
|
|
b0881726fa | ||
|
|
308248d8ec | ||
|
|
de5ea4f0aa | ||
|
|
305ce6ff11 | ||
|
|
bd4dbb55e4 | ||
|
|
1fefb2bcaf | ||
|
|
e094e80ecf | ||
|
|
9715db1229 | ||
|
|
3c0daa2bf1 | ||
|
|
435fec2f1c | ||
|
|
69ba619f24 | ||
|
|
b7bb21f091 | ||
|
|
77f51a3e9f | ||
|
|
7799e511e8 | ||
|
|
416f4a4dee | ||
|
|
2fd5158839 | ||
|
|
b8287acff3 | ||
|
|
53b470ed09 | ||
|
|
8a20b773fb | ||
|
|
aa576fe7db | ||
|
|
582321c1ae | ||
|
|
541f5041fb | ||
|
|
d16babefbe | ||
|
|
4ac6768b7a | ||
|
|
97d3d3c616 | ||
|
|
2ee7572634 | ||
|
|
8e7fd1ecef | ||
|
|
6b4e6533f5 | ||
|
|
b680a89033 | ||
|
|
ee6a6f4b94 | ||
|
|
6933baafca | ||
|
|
0f3a6782b8 | ||
|
|
f99cc021f8 | ||
|
|
e4de4b8c1b | ||
|
|
793b5a679c | ||
|
|
1b75cf822d | ||
|
|
60604020c7 | ||
|
|
09780d02e3 | ||
|
|
4760f79ab5 | ||
|
|
05363a0d22 | ||
|
|
49797f891c | ||
|
|
9d38fe6764 | ||
|
|
f340b18408 | ||
|
|
358c449b17 | ||
|
|
1f6a6bbb39 | ||
|
|
10028cf500 | ||
|
|
a977458881 | ||
|
|
dc811d8b84 | ||
|
|
5bc119bd9d | ||
|
|
9016796497 | ||
|
|
32f47aaa34 | ||
|
|
c54b38a068 | ||
|
|
a9a708d0f6 | ||
|
|
ab3f118588 | ||
|
|
87852acb14 | ||
|
|
65ab26365d | ||
|
|
e7630e98d9 | ||
|
|
9779c1ebee | ||
|
|
9cef7e3370 | ||
|
|
b6bc545e7d | ||
|
|
85326be7bb | ||
|
|
75750ad38d | ||
|
|
0522114c5d | ||
|
|
6300236cdb | ||
|
|
1dbb950b3c | ||
|
|
bfd2ecd26c | ||
|
|
e60c2f5f19 | ||
|
|
274f71615f | ||
|
|
cd7d5ed665 | ||
|
|
703327ed41 | ||
|
|
a7ce24c84b | ||
|
|
c3c42e33ee | ||
|
|
530ace6b81 | ||
|
|
5345b001e8 | ||
|
|
07c9e2f675 | ||
|
|
7a08cb5ddd | ||
|
|
9c0ae12959 |
@@ -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: 如果你的问题需要进一步解释,或者你所遇到的问题不容易重现,请在此处添加更多信息。(直接把图片、视频拖到编辑框即可添加图片或视频)
|
|
||||||
@@ -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: 如果你的问题需要进一步解释,或者你所遇到的问题不容易重现,请在此处添加更多信息。(直接把图片/视频拖到编辑框即可添加图片/视频)
|
||||||
@@ -1,40 +1,38 @@
|
|||||||
name: ✨功能请求
|
name: ✨ 功能请求
|
||||||
description: 为这个项目提出一个想法,请先查看常见问题及搜索issue列表中有无你要提的问题
|
description: 为这个项目提出一个想法,请先查看常见问题及搜索 Issue 列表中有无你要提的问题。
|
||||||
title: "[Feature]: "
|
title: "[Feature]: "
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: check-answer
|
id: check-answer
|
||||||
attributes:
|
attributes:
|
||||||
label: 解决方案检查
|
label: 解决方案检查
|
||||||
description: 请确保你已完成以下所有操作
|
description: 请确保你已完成以下所有操作。
|
||||||
options:
|
options:
|
||||||
- label: 我已阅读常见问题(<https://lyswhut.github.io/lx-music-doc/desktop/faq>),但没有找到解决方案
|
- label: 我已阅读 [常见问题](https://lyswhut.github.io/lx-music-doc/desktop/faq),但没有找到解决方案。
|
||||||
required: true
|
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
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: problem-description
|
id: problem-description
|
||||||
attributes:
|
attributes:
|
||||||
label: 问题描述
|
label: 问题描述
|
||||||
description: 请添加清晰简洁的描述,说明你希望通过此功能请求解决的问题
|
description: 请添加清晰简洁的描述,说明你希望通过此功能请求解决的问题。
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: proposed-solution
|
id: proposed-solution
|
||||||
attributes:
|
attributes:
|
||||||
label: 描述你想要的解决方案
|
label: 描述你想要的解决方案
|
||||||
description: 简洁明了地描述你要发生的事情
|
description: 简洁明了地描述你要发生的事情。
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: alternatives-considered
|
id: alternatives-considered
|
||||||
attributes:
|
attributes:
|
||||||
label: 描述你考虑过的替代方案
|
label: 描述你考虑过的替代方案
|
||||||
description: 对你考虑过的所有替代解决方案或功能的简洁明了的描述
|
description: 对你考虑过的所有替代解决方案或功能的简洁明了的描述。
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: additional-information
|
id: additional-information
|
||||||
attributes:
|
attributes:
|
||||||
label: 附加信息
|
label: 附加信息
|
||||||
description: 如果你的问题需要进一步解释,或者想要表达其他内容,请在此处添加更多信息。(直接把图片、视频拖到编辑框即可添加图片或视频)
|
description: 如果你的问题需要进一步解释,或者想要表达其他内容,请在此处添加更多信息。(直接把图片/视频拖到编辑框即可添加图片/视频)
|
||||||
@@ -7,19 +7,21 @@ runs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '20'
|
||||||
|
|
||||||
|
# - 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
|
- name: Cache node modules
|
||||||
id: cache-npm
|
id: cache-npm
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: ${{ env.NPM_CACHE }}
|
||||||
${{ env.NPM_CACHE }}
|
key: ${{ runner.os }}-npm-cache-${{ hashFiles('package-lock.json') }}
|
||||||
${{ env.ELECTRON_CACHE }}
|
|
||||||
${{ env.ELECTRON_BUILDERCACHE }}
|
|
||||||
key: ${{ runner.os }}-node-modules-cache-${{ hashFiles('package-lock.json') }}
|
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-node-modules-cache-
|
${{ runner.os }}-npm-cache-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- beta
|
- beta
|
||||||
|
|
||||||
|
env:
|
||||||
|
IS_CI: 'true'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# CheckCode:
|
# CheckCode:
|
||||||
# name: Lint Code
|
# name: Lint Code
|
||||||
@@ -16,7 +19,7 @@ jobs:
|
|||||||
# - name: Install Node.js
|
# - name: Install Node.js
|
||||||
# uses: actions/setup-node@v4
|
# uses: actions/setup-node@v4
|
||||||
# with:
|
# with:
|
||||||
# node-version: '18'
|
# node-version: 20
|
||||||
|
|
||||||
# - name: Cache file
|
# - name: Cache file
|
||||||
# uses: actions/cache@v4
|
# uses: actions/cache@v4
|
||||||
@@ -40,20 +43,24 @@ jobs:
|
|||||||
Windows:
|
Windows:
|
||||||
name: Windows
|
name: Windows
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
env:
|
|
||||||
NPM_CACHE: '%APPDATA%\npm-cache'
|
|
||||||
ELECTRON_CACHE: '%LOCALAPPDATA%\electron\Cache'
|
|
||||||
ELECTRON_BUILDERCACHE: '%LOCALAPPDATA%\electron-builder\Cache'
|
|
||||||
# needs: CheckCode
|
# needs: CheckCode
|
||||||
steps:
|
steps:
|
||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
uses: actions/checkout@v4
|
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
|
- name: Setup Node Env
|
||||||
|
env:
|
||||||
|
NPM_CACHE: ${{ env.NPM_CACHE }}
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
|
|
||||||
- name: Build src code
|
- name: Build src code
|
||||||
run: npm run build
|
run: |
|
||||||
|
git status --porcelain
|
||||||
|
npm run build
|
||||||
|
|
||||||
- name: Build Package Setup x64
|
- name: Build Package Setup x64
|
||||||
run: npm run pack:win:setup:x64
|
run: npm run pack:win:setup:x64
|
||||||
@@ -127,22 +134,26 @@ jobs:
|
|||||||
name: Mac
|
name: Mac
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
# needs: CheckCode
|
# needs: CheckCode
|
||||||
env:
|
|
||||||
NPM_CACHE: $HOME/.npm
|
|
||||||
ELECTRON_CACHE: $HOME/.cache/electron
|
|
||||||
ELECTRON_BUILDERCACHE: $HOME/.cache/electron-builder
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install python setuptools
|
- name: Install python setuptools
|
||||||
run: python3 -m pip install setuptools
|
run: brew install python-setuptools
|
||||||
|
|
||||||
|
- name: Get npm cache directory
|
||||||
|
shell: bash
|
||||||
|
run: echo "NPM_CACHE=$(npm config get cache)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Setup Node Env
|
- name: Setup Node Env
|
||||||
|
env:
|
||||||
|
NPM_CACHE: ${{ env.NPM_CACHE }}
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
|
|
||||||
- name: Build src code
|
- name: Build src code
|
||||||
run: npm run build
|
run: |
|
||||||
|
git status --porcelain
|
||||||
|
npm run build
|
||||||
|
|
||||||
- name: Build Package dmg
|
- name: Build Package dmg
|
||||||
run: |
|
run: |
|
||||||
@@ -170,10 +181,6 @@ jobs:
|
|||||||
Linux:
|
Linux:
|
||||||
name: Linux
|
name: Linux
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
|
||||||
NPM_CACHE: $HOME/.npm
|
|
||||||
ELECTRON_CACHE: $HOME/.cache/electron
|
|
||||||
ELECTRON_BUILDERCACHE: $HOME/.cache/electron-builder
|
|
||||||
# needs: CheckCode
|
# needs: CheckCode
|
||||||
steps:
|
steps:
|
||||||
- name: Install package
|
- name: Install package
|
||||||
@@ -182,11 +189,19 @@ jobs:
|
|||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get npm cache directory
|
||||||
|
shell: bash
|
||||||
|
run: echo "NPM_CACHE=$(npm config get cache)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Setup Node Env
|
- name: Setup Node Env
|
||||||
|
env:
|
||||||
|
NPM_CACHE: ${{ env.NPM_CACHE }}
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
|
|
||||||
- name: Build src code
|
- name: Build src code
|
||||||
run: npm run build
|
run: |
|
||||||
|
git status --porcelain
|
||||||
|
npm run build
|
||||||
|
|
||||||
- name: Build Package deb amd64
|
- name: Build Package deb amd64
|
||||||
run: npm run pack:linux:deb:amd64
|
run: npm run pack:linux:deb:amd64
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: 20
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
IS_CI: 'true'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# CheckCode:
|
# CheckCode:
|
||||||
# name: Lint Code
|
# name: Lint Code
|
||||||
@@ -16,7 +19,7 @@ jobs:
|
|||||||
# - name: Install Node.js
|
# - name: Install Node.js
|
||||||
# uses: actions/setup-node@v4
|
# uses: actions/setup-node@v4
|
||||||
# with:
|
# with:
|
||||||
# node-version: '18'
|
# node-version: 20
|
||||||
|
|
||||||
# - name: Cache file
|
# - name: Cache file
|
||||||
# uses: actions/cache@v4
|
# uses: actions/cache@v4
|
||||||
@@ -40,20 +43,27 @@ jobs:
|
|||||||
Windows:
|
Windows:
|
||||||
name: Windows
|
name: Windows
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
env:
|
|
||||||
NPM_CACHE: '%APPDATA%\npm-cache'
|
|
||||||
ELECTRON_CACHE: '%LOCALAPPDATA%\electron\Cache'
|
|
||||||
ELECTRON_BUILDERCACHE: '%LOCALAPPDATA%\electron-builder\Cache'
|
|
||||||
# needs: CheckCode
|
# needs: CheckCode
|
||||||
steps:
|
steps:
|
||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get npm cache directory
|
||||||
|
shell: pwsh
|
||||||
|
run: echo "NPM_CACHE=$(npm config get cache)" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Show Env
|
||||||
|
run: echo "${{ env.NPM_CACHE }}"
|
||||||
|
|
||||||
- name: Setup Node Env
|
- name: Setup Node Env
|
||||||
|
env:
|
||||||
|
NPM_CACHE: ${{ env.NPM_CACHE }}
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
|
|
||||||
- name: Build src code
|
- name: Build src code
|
||||||
run: npm run build
|
run: |
|
||||||
|
git status --porcelain
|
||||||
|
npm run build
|
||||||
|
|
||||||
- name: Release package
|
- name: Release package
|
||||||
run: |
|
run: |
|
||||||
@@ -72,22 +82,26 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
Windows_7:
|
Windows_7:
|
||||||
name: Windows
|
name: Windows_7
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
env:
|
|
||||||
NPM_CACHE: '%APPDATA%\npm-cache'
|
|
||||||
ELECTRON_CACHE: '%LOCALAPPDATA%\electron\Cache'
|
|
||||||
ELECTRON_BUILDERCACHE: '%LOCALAPPDATA%\electron-builder\Cache'
|
|
||||||
# needs: CheckCode
|
# needs: CheckCode
|
||||||
steps:
|
steps:
|
||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
uses: actions/checkout@v4
|
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
|
- name: Setup Node Env
|
||||||
|
env:
|
||||||
|
NPM_CACHE: ${{ env.NPM_CACHE }}
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
|
|
||||||
- name: Build src code
|
- name: Build src code
|
||||||
run: npm run build
|
run: |
|
||||||
|
git status --porcelain
|
||||||
|
npm run build
|
||||||
|
|
||||||
- name: Prepare win7 electron env
|
- name: Prepare win7 electron env
|
||||||
run: |
|
run: |
|
||||||
@@ -111,23 +125,29 @@ jobs:
|
|||||||
Mac:
|
Mac:
|
||||||
name: Mac
|
name: Mac
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
env:
|
|
||||||
NPM_CACHE: $HOME/.npm
|
|
||||||
ELECTRON_CACHE: $HOME/.cache/electron
|
|
||||||
ELECTRON_BUILDERCACHE: $HOME/.cache/electron-builder
|
|
||||||
# needs: CheckCode
|
# needs: CheckCode
|
||||||
steps:
|
steps:
|
||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install python3 setuptools
|
- name: Install python3 setuptools
|
||||||
run: python3 -m pip install setuptools
|
run: brew install python-setuptools
|
||||||
|
|
||||||
|
- name: Get npm cache directory
|
||||||
|
shell: bash
|
||||||
|
run: echo "NPM_CACHE=$(npm config get cache)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Show Env
|
||||||
|
run: echo "${{ env.NPM_CACHE }}"
|
||||||
- name: Setup Node Env
|
- name: Setup Node Env
|
||||||
|
env:
|
||||||
|
NPM_CACHE: ${{ env.NPM_CACHE }}
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
|
|
||||||
- name: Build src code
|
- name: Build src code
|
||||||
run: npm run build
|
run: |
|
||||||
|
git status --porcelain
|
||||||
|
npm run build
|
||||||
|
|
||||||
- name: Release package
|
- name: Release package
|
||||||
run: |
|
run: |
|
||||||
@@ -145,10 +165,6 @@ jobs:
|
|||||||
Linux:
|
Linux:
|
||||||
name: Linux
|
name: Linux
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
|
||||||
NPM_CACHE: $HOME/.npm
|
|
||||||
ELECTRON_CACHE: $HOME/.cache/electron
|
|
||||||
ELECTRON_BUILDERCACHE: $HOME/.cache/electron-builder
|
|
||||||
# needs: CheckCode
|
# needs: CheckCode
|
||||||
steps:
|
steps:
|
||||||
- name: Install package
|
- name: Install package
|
||||||
@@ -157,11 +173,21 @@ jobs:
|
|||||||
- name: Check out git repository
|
- name: Check out git repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get npm cache directory
|
||||||
|
shell: bash
|
||||||
|
run: echo "NPM_CACHE=$(npm config get cache)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Show Env
|
||||||
|
run: echo "${{ env.NPM_CACHE }}"
|
||||||
- name: Setup Node Env
|
- name: Setup Node Env
|
||||||
|
env:
|
||||||
|
NPM_CACHE: ${{ env.NPM_CACHE }}
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
|
|
||||||
- name: Build src code
|
- name: Build src code
|
||||||
run: npm run build
|
run: |
|
||||||
|
git status --porcelain
|
||||||
|
npm run build
|
||||||
|
|
||||||
- name: Release package
|
- name: Release package
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ module.exports = {
|
|||||||
'image-size',
|
'image-size',
|
||||||
'message2call',
|
'message2call',
|
||||||
'@types/ws',
|
'@types/ws',
|
||||||
|
'eslint',
|
||||||
|
'@types/node',
|
||||||
|
'electron-debug',
|
||||||
// 'eslint-config-standard-with-typescript',
|
// 'eslint-config-standard-with-typescript',
|
||||||
// 'typescript', // https://github.com/microsoft/TypeScript/pull/54567
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// target: 'newest',
|
// target: 'newest',
|
||||||
@@ -27,5 +29,8 @@ module.exports = {
|
|||||||
// target: 'minor',
|
// target: 'minor',
|
||||||
// filter: [
|
// filter: [
|
||||||
// 'electron',
|
// 'electron',
|
||||||
|
// 'eslint',
|
||||||
|
// 'electron-debug',
|
||||||
|
// '@types/node',
|
||||||
// ],
|
// ],
|
||||||
}
|
}
|
||||||
|
|||||||
+114
@@ -6,6 +6,120 @@ Project versioning adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
|
Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
|
||||||
Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
|
Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
|
||||||
|
|
||||||
|
## [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
|
## [2.6.0](https://github.com/lyswhut/lx-music-desktop/compare/v2.5.0...v2.6.0) - 2024-02-01
|
||||||
|
|
||||||
提交祝大家新年快乐!
|
提交祝大家新年快乐!
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# lx-music-desktop 常见问题
|
# lx-music-desktop 常见问题
|
||||||
|
|
||||||
本文档已迁移到:<https://lyswhut.github.io/lx-music-doc/desktop/faq>
|
本文档已迁移至:<https://lyswhut.github.io/lx-music-doc/desktop/faq>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
在阅读本常见问题后,仍然无法解决你的问题,请提交issue或者加企鹅群`830125506`反馈(无事勿加,入群先看群公告),反馈时请**注明**已阅读常见问题!
|
在阅读本常见问题后,仍然无法解决你的问题,请提交issue或者加企鹅群`830125506`反馈(无事勿加,入群先看群公告),反馈时请**注明**已阅读常见问题!
|
||||||
|
|||||||
@@ -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>
|
<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">
|
<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/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>
|
<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,143 +30,131 @@
|
|||||||
[9]: https://img.shields.io/github/license/lyswhut/lx-music-desktop
|
[9]: https://img.shields.io/github/license/lyswhut/lx-music-desktop
|
||||||
[10]: https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE -->
|
[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
|
- Vue 3
|
||||||
|
|
||||||
已支持的平台:
|
已支持的平台:
|
||||||
|
|
||||||
- Windows 7 及以上
|
|
||||||
- Mac OS
|
|
||||||
- Linux
|
- Linux
|
||||||
|
- macOS
|
||||||
|
- Windows 7 及以上
|
||||||
|
|
||||||
软件变化请查看:[更新日志](https://github.com/lyswhut/lx-music-desktop/blob/master/CHANGELOG.md)<br>
|
*移动版项目地址:https://github.com/lyswhut/lx-music-mobile*
|
||||||
软件下载请转到:[发布页面](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)
|
|
||||||
|
|
||||||
目前本项目的原始发布地址只有**GitHub**及**蓝奏网盘**,其他渠道均为第三方转载发布,与本项目无关!
|
软件变化请查看[更新日志](https://github.com/lyswhut/lx-music-desktop/blob/master/CHANGELOG.md)。
|
||||||
|
|
||||||
为了提高使用门槛,本软件内的默认设置、UI操作不以新手友好为目标,所以使用前建议先根据你的喜好浏览调整一遍软件设置,阅读一遍[音乐播放列表机制](https://lyswhut.github.io/lx-music-doc/desktop/faq/playlist)及[可用的鼠标、键盘快捷操作](https://lyswhut.github.io/lx-music-doc/desktop/faq/hotkey)
|
软件下载请查看 [GitHub Releases](https://github.com/lyswhut/lx-music-desktop/releases)。
|
||||||
|
|
||||||
#### Scheme URL支持
|
使用常见问题请参阅[桌面版常见问题](https://lyswhut.github.io/lx-music-doc/desktop/faq)。
|
||||||
|
|
||||||
从v1.17.0起支持 Scheme URL,可以使用此功能从浏览器等场景下调用LX Music,我们开发了一个[油猴脚本](https://github.com/lyswhut/lx-music-script#readme)配套使用,<br>
|
目前本项目的原始发布地址只有 [**GitHub**](https://github.com/lyswhut/lx-music-desktop/releases),其他渠道均为第三方转载发布,与本项目无关!
|
||||||
脚本安装地址:<https://greasyfork.org/zh-CN/scripts/438148><br>
|
|
||||||
|
|
||||||
若你想自己调用LX Music,可以看[Scheme URL支持](https://lyswhut.github.io/lx-music-doc/desktop/scheme-url)
|
为了提高使用门槛,本软件内的默认设置、UI 操作不以新手友好为目标,所以使用前建议先根据你的喜好浏览调整一遍软件设置,阅读一遍[音乐播放列表机制](https://lyswhut.github.io/lx-music-doc/desktop/faq/playlist)及[可用的鼠标、键盘快捷操作](https://lyswhut.github.io/lx-music-doc/desktop/faq/hotkey)。
|
||||||
|
|
||||||
#### 数据同步服务
|
### Scheme URL 支持
|
||||||
|
|
||||||
从v2.2.0起,我们发布了一个独立版的[数据同步服务](https://github.com/lyswhut/lx-music-sync-server#readme),如果你有服务器,可以将其部署到服务器上作为私人多端同步服务使用,详情看该项目说明
|
从 v1.17.0 起支持 Scheme URL,可以使用此功能在浏览器等场景下调用 LX Music,我们开发了一个[油猴脚本](https://github.com/lyswhut/lx-music-script#readme)配套使用。
|
||||||
|
|
||||||
#### 启动参数
|
脚本安装地址:[LX Music 辅助脚本](https://greasyfork.org/zh-CN/scripts/438148)。
|
||||||
|
|
||||||
目前软件已支持的启动参数如下:
|
若你想自己调用 LX Music,可以参考文档「[Scheme URL 支持](https://lyswhut.github.io/lx-music-doc/desktop/scheme-url)」部分。
|
||||||
|
|
||||||
- `-proxy-server` 设置代理服务器,代理应用的所有流量
|
### 数据同步服务
|
||||||
- `-proxy-bypass-list` 以分号分隔的主机列表绕过代理服务器
|
|
||||||
- `-play` 启动时播放指定列表的音乐
|
|
||||||
- `-search` 启动软件时自动在搜索框搜索指定的内容
|
|
||||||
- `-dha` 禁用硬件加速启动(Disable Hardware Acceleration)
|
|
||||||
- `-dt` 以非透明模式启动(Disable Transparent)
|
|
||||||
- `-dhmkh` 禁用硬件媒体密钥处理(Disable Hardware Media Key Handling)
|
|
||||||
|
|
||||||
启动参数的详细说明请看[启动参数说明](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`
|
- Linux:`$XDG_CONFIG_HOME/lx-music-desktop` 或 `~/.config/lx-music-desktop`
|
||||||
- macOS:`~/Library/Application Support/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 能顺利合并,需要注意以下几点:
|
||||||
|
|
||||||
### 常见问题
|
- 对于添加新功能的 PR,建议在提交 PR 前先创建 Issue 进行说明,以确认该功能是否确实需要。
|
||||||
|
- 对于修复 bug 的 PR,请提供修复前后的说明及重现方式。
|
||||||
常见问题已移至:<https://lyswhut.github.io/lx-music-doc/desktop/faq>
|
- 对于其他类型的 PR,则适当附上说明。
|
||||||
|
|
||||||
### 贡献代码
|
|
||||||
|
|
||||||
本项目欢迎PR,但为了PR能顺利合并,需要注意以下几点:
|
|
||||||
|
|
||||||
- 对于添加新功能的PR,建议在PR前先创建issue说明,以确认该功能是否确实需要
|
|
||||||
- 对于修复Bug PR,请提供修复前后的说明及重现方式
|
|
||||||
- 其他类型的PR则适当附上说明
|
|
||||||
|
|
||||||
贡献代码步骤:
|
贡献代码步骤:
|
||||||
|
|
||||||
1. 参照[源码使用方法](https://lyswhut.github.io/lx-music-doc/desktop/use-source-code)设置开发环境
|
1. 参照[源码使用方法](https://lyswhut.github.io/lx-music-doc/desktop/use-source-code)设置开发环境;
|
||||||
2. 克隆本仓库代码并切换到`dev`分支开发
|
2. 克隆本仓库代码并切换至 `dev` 分支进行开发;
|
||||||
3. 提交PR至`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 的补充,如有冲突,以以下协议为准。
|
本项目基于 [Apache License 2.0](https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE) 许可证发行,以下协议是对于 Apache License 2.0 的补充,如有冲突,以以下协议为准。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
词语约定:本协议中的“本项目”指洛雪音乐桌面版项目;“使用者”指签署本协议的使用者;“官方音乐平台”指对本项目内置的包括酷我、酷狗、咪咕等音乐源的官方平台统称;“版权数据”指包括但不限于图像、音频、名字等在内的他人拥有所属版权的数据。
|
*词语约定:本协议中的“本项目”指 LX Music(洛雪音乐助手)桌面版项目;“使用者”指签署本协议的使用者;“官方音乐平台”指对本项目内置的包括酷我、酷狗、咪咕等音乐源的官方平台统称;“版权数据”指包括但不限于图像、音频、名字等在内的他人拥有所属版权的数据。*
|
||||||
|
|
||||||
#### 一、数据来源
|
### 一、数据来源
|
||||||
|
|
||||||
1.1 本项目的各官方平台在线数据来源原理是从其公开服务器中拉取数据(与未登录状态在官方平台APP获取的数据相同),经过对数据简单地筛选与合并后进行展示,因此本项目不对数据的合法性、准确性负责。
|
1.1 本项目的各官方平台在线数据来源原理是从其公开服务器中拉取数据(与未登录状态在官方平台 APP 获取的数据相同),经过对数据简单地筛选与合并后进行展示,因此本项目不对数据的合法性、准确性负责。
|
||||||
|
|
||||||
1.2 本项目本身没有获取某个音频数据的能力,本项目使用的在线音频数据来源来自软件设置内“音乐来源”设置所选择的“源”返回的在线链接。例如播放某首歌,本项目所做的只是将希望播放的歌曲名字、歌手名字等信息传递给“源”,若“源”返回了一个链接,则本项目将认为这就是该歌曲的音频数据而进行使用,至于这是不是正确的音频数据本项目无法校验其准确性,所以使用本项目的过程中可能会出现希望播放的音频与实际播放的音频不对应或者无法播放的问题。
|
1.2 本项目本身没有获取某个音频数据的能力,本项目使用的在线音频数据来源来自软件设置内“音乐来源”设置所选择的“源”返回的在线链接。例如播放某首歌,本项目所做的只是将希望播放的歌曲名字、歌手名字等信息传递给“源”,若“源”返回了一个链接,则本项目将认为这就是该歌曲的音频数据而进行使用,至于这是不是正确的音频数据本项目无法校验其准确性,所以使用本项目的过程中可能会出现希望播放的音频与实际播放的音频不对应或者无法播放的问题。
|
||||||
|
|
||||||
1.3 本项目的非官方平台数据(例如我的收藏列表)来自使用者本地系统或者使用者连接的同步服务,本项目不对这些数据的合法性、准确性负责。
|
1.3 本项目的非官方平台数据(例如我的收藏列表)来自使用者本地系统或者使用者连接的同步服务,本项目不对这些数据的合法性、准确性负责。
|
||||||
|
|
||||||
#### 二、版权数据
|
### 二、版权数据
|
||||||
|
|
||||||
2.1 使用本项目的过程中可能会产生版权数据。对于这些版权数据,本项目不拥有它们的所有权。为了避免侵权,使用者务必在**24小时内**清除使用本项目的过程中所产生的版权数据。
|
2.1 使用本项目的过程中可能会产生版权数据。对于这些版权数据,本项目不拥有它们的所有权。为了避免侵权,使用者务必在 **24 小时内** 清除使用本项目的过程中所产生的版权数据。
|
||||||
|
|
||||||
#### 三、音乐平台别名
|
### 三、音乐平台别名
|
||||||
|
|
||||||
3.1 本项目内的官方音乐平台别名为本项目内对官方音乐平台的一个称呼,不包含恶意。如果官方音乐平台觉得不妥,可联系本项目更改或移除。
|
3.1 本项目内的官方音乐平台别名为本项目内对官方音乐平台的一个称呼,不包含恶意。如果官方音乐平台觉得不妥,可联系本项目更改或移除。
|
||||||
|
|
||||||
#### 四、资源使用
|
### 四、资源使用
|
||||||
|
|
||||||
4.1 本项目内使用的部分包括但不限于字体、图片等资源来源于互联网。如果出现侵权可联系本项目移除。
|
4.1 本项目内使用的部分包括但不限于字体、图片等资源来源于互联网。如果出现侵权可联系本项目移除。
|
||||||
|
|
||||||
#### 五、免责声明
|
### 五、免责声明
|
||||||
|
|
||||||
5.1 由于使用本项目产生的包括由于本协议或由于使用或无法使用本项目而引起的任何性质的任何直接、间接、特殊、偶然或结果性损害(包括但不限于因商誉损失、停工、计算机故障或故障引起的损害赔偿,或任何及所有其他商业损害或损失)由使用者负责。
|
5.1 由于使用本项目产生的包括由于本协议或由于使用或无法使用本项目而引起的任何性质的任何直接、间接、特殊、偶然或结果性损害(包括但不限于因商誉损失、停工、计算机故障或故障引起的损害赔偿,或任何及所有其他商业损害或损失)由使用者负责。
|
||||||
|
|
||||||
#### 六、使用限制
|
### 六、使用限制
|
||||||
|
|
||||||
6.1 本项目完全免费,且开源发布于 GitHub 面向全世界人用作对技术的学习交流。本项目不对项目内的技术可能存在违反当地法律法规的行为作保证。
|
6.1 本项目完全免费,且开源发布于 GitHub 面向全世界人用作对技术的学习交流。本项目不对项目内的技术可能存在违反当地法律法规的行为作保证。
|
||||||
|
|
||||||
6.2 **禁止在违反当地法律法规的情况下使用本项目。** 对于使用者在明知或不知当地法律法规不允许的情况下使用本项目所造成的任何违法违规行为由使用者承担,本项目不承担由此造成的任何直接、间接、特殊、偶然或结果性责任。
|
6.2 **禁止在违反当地法律法规的情况下使用本项目。** 对于使用者在明知或不知当地法律法规不允许的情况下使用本项目所造成的任何违法违规行为由使用者承担,本项目不承担由此造成的任何直接、间接、特殊、偶然或结果性责任。
|
||||||
|
|
||||||
#### 七、版权保护
|
### 七、版权保护
|
||||||
|
|
||||||
7.1 音乐平台不易,请尊重版权,支持正版。
|
7.1 音乐平台不易,请尊重版权,支持正版。
|
||||||
|
|
||||||
#### 八、非商业性质
|
### 八、非商业性质
|
||||||
|
|
||||||
8.1 本项目仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作及捐赠。
|
8.1 本项目仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作及捐赠。
|
||||||
|
|
||||||
#### 九、接受协议
|
### 九、接受协议
|
||||||
|
|
||||||
9.1 若你使用了本项目,将代表你接受本协议。
|
9.1 若你使用了本项目,即代表你接受本协议。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
若对此有疑问请 mail to: lyswhut+qq.com (请将`+`替换成`@`)
|
若对此有疑问请 mail to: lyswhut+qq.com (请将 `+` 替换为 `@`)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const afterPack = require('./build-after-pack')
|
|||||||
*/
|
*/
|
||||||
const options = {
|
const options = {
|
||||||
appId: 'cn.toside.music.desktop',
|
appId: 'cn.toside.music.desktop',
|
||||||
|
productName: 'lx-music-desktop',
|
||||||
beforePack,
|
beforePack,
|
||||||
afterPack,
|
afterPack,
|
||||||
protocols: {
|
protocols: {
|
||||||
@@ -65,7 +66,7 @@ const winOptions = {
|
|||||||
oneClick: false,
|
oneClick: false,
|
||||||
language: '2052',
|
language: '2052',
|
||||||
allowToChangeInstallationDirectory: true,
|
allowToChangeInstallationDirectory: true,
|
||||||
differentialPackage: true,
|
// differentialPackage: true,
|
||||||
license: './licenses/license.rtf',
|
license: './licenses/license.rtf',
|
||||||
shortcutName: 'LX Music',
|
shortcutName: 'LX Music',
|
||||||
},
|
},
|
||||||
@@ -81,6 +82,11 @@ const linuxOptions = {
|
|||||||
icon: './resources/icons',
|
icon: './resources/icons',
|
||||||
category: 'Utility;AudioVideo;Audio;Player;Music;',
|
category: 'Utility;AudioVideo;Audio;Player;Music;',
|
||||||
desktop: {
|
desktop: {
|
||||||
|
// 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: 'LX Music',
|
||||||
'Name[zh_CN]': 'LX Music',
|
'Name[zh_CN]': 'LX Music',
|
||||||
'Name[zh_TW]': 'LX Music',
|
'Name[zh_TW]': 'LX Music',
|
||||||
@@ -89,6 +95,7 @@ const linuxOptions = {
|
|||||||
StartupNotify: 'false',
|
StartupNotify: 'false',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
appImage: {
|
appImage: {
|
||||||
license: './licenses/license_zh.txt',
|
license: './licenses/license_zh.txt',
|
||||||
category: 'Utility;AudioVideo;Audio;Player;Music;',
|
category: 'Utility;AudioVideo;Audio;Player;Music;',
|
||||||
@@ -106,23 +113,22 @@ const macOptions = {
|
|||||||
},
|
},
|
||||||
dmg: {
|
dmg: {
|
||||||
window: {
|
window: {
|
||||||
width: 600,
|
width: 530,
|
||||||
height: 400,
|
height: 380,
|
||||||
},
|
},
|
||||||
contents: [
|
contents: [
|
||||||
{
|
{
|
||||||
x: 106,
|
x: 140,
|
||||||
y: 252,
|
y: 200,
|
||||||
name: 'LX Music',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x: 490,
|
x: 390,
|
||||||
y: 252,
|
y: 200,
|
||||||
type: 'link',
|
type: 'link',
|
||||||
path: '/Applications',
|
path: '/Applications',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
title: '洛雪音乐助手 v${version}',
|
title: 'LX Music v${version}',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ module.exports = {
|
|||||||
modules: {
|
modules: {
|
||||||
localIdentName: isDev ? '[path][name]__[local]--[hash:base64:5]' : '[hash:base64:5]',
|
localIdentName: isDev ? '[path][name]__[local]--[hash:base64:5]' : '[hash:base64:5]',
|
||||||
exportLocalsConvention: 'camelCase',
|
exportLocalsConvention: 'camelCase',
|
||||||
|
namedExport: false,
|
||||||
},
|
},
|
||||||
sourceMap: isDev,
|
sourceMap: isDev,
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -7,12 +7,12 @@ const baseConfig = require('./webpack.config.base')
|
|||||||
|
|
||||||
// const { dependencies } = require('../../package.json')
|
// const { dependencies } = require('../../package.json')
|
||||||
|
|
||||||
const buildConfig = require('../webpack-build-config')
|
// const buildConfig = require('../webpack-build-config')
|
||||||
|
|
||||||
|
|
||||||
module.exports = merge(baseConfig, {
|
module.exports = merge(baseConfig, {
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
devtool: 'source-map',
|
devtool: false,
|
||||||
entry: {
|
entry: {
|
||||||
main: path.join(__dirname, '../../src/main/index.ts'),
|
main: path.join(__dirname, '../../src/main/index.ts'),
|
||||||
// 'dbService.worker': path.join(__dirname, '../../src/main/worker/dbService/index.ts'),
|
// 'dbService.worker': path.join(__dirname, '../../src/main/worker/dbService/index.ts'),
|
||||||
@@ -45,6 +45,6 @@ module.exports = merge(baseConfig, {
|
|||||||
maxAssetSize: 1024 * 1024 * 20,
|
maxAssetSize: 1024 * 1024 * 20,
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: buildConfig.minimize,
|
minimize: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ const { merge } = require('webpack-merge')
|
|||||||
|
|
||||||
const baseConfig = require('./webpack.config.base')
|
const baseConfig = require('./webpack.config.base')
|
||||||
|
|
||||||
|
const gitInfo = {
|
||||||
|
commit_id: '',
|
||||||
|
commit_date: '',
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = merge(baseConfig, {
|
module.exports = merge(baseConfig, {
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
devtool: 'eval-source-map',
|
devtool: 'eval-source-map',
|
||||||
@@ -18,6 +23,8 @@ module.exports = merge(baseConfig, {
|
|||||||
__VUE_OPTIONS_API__: 'true',
|
__VUE_OPTIONS_API__: 'true',
|
||||||
__VUE_PROD_DEVTOOLS__: 'false',
|
__VUE_PROD_DEVTOOLS__: 'false',
|
||||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: '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, '\\\\')}"`,
|
staticPath: `"${path.join(__dirname, '../../src/static').replace(/\\/g, '\\\\')}"`,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const { execSync } = require('child_process')
|
||||||
const webpack = require('webpack')
|
const webpack = require('webpack')
|
||||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
|
||||||
const TerserPlugin = require('terser-webpack-plugin')
|
const TerserPlugin = require('terser-webpack-plugin')
|
||||||
@@ -12,6 +13,19 @@ const buildConfig = require('../webpack-build-config')
|
|||||||
|
|
||||||
// let whiteListedModules = ['vue', 'vue-router', 'vuex', 'vue-i18n']
|
// let whiteListedModules = ['vue', 'vue-router', 'vuex', 'vue-i18n']
|
||||||
|
|
||||||
|
const gitInfo = {
|
||||||
|
commit_id: '',
|
||||||
|
commit_date: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!execSync('git status --porcelain').toString().trim()) {
|
||||||
|
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, {
|
module.exports = merge(baseConfig, {
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
@@ -45,6 +59,8 @@ module.exports = merge(baseConfig, {
|
|||||||
__VUE_OPTIONS_API__: 'true',
|
__VUE_OPTIONS_API__: 'true',
|
||||||
__VUE_PROD_DEVTOOLS__: 'false',
|
__VUE_PROD_DEVTOOLS__: 'false',
|
||||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
|
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
|
||||||
|
COMMIT_ID: `"${gitInfo.commit_id}"`,
|
||||||
|
COMMIT_DATE: `"${gitInfo.commit_date}"`,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
optimization: {
|
optimization: {
|
||||||
|
|||||||
+16
-13
@@ -16,9 +16,10 @@ const rendererLyricConfig = require('./renderer-lyric/webpack.config.dev')
|
|||||||
const rendererScriptConfig = require('./renderer-scripts/webpack.config.dev')
|
const rendererScriptConfig = require('./renderer-scripts/webpack.config.dev')
|
||||||
const { Arch } = require('electron-builder')
|
const { Arch } = require('electron-builder')
|
||||||
const replaceLib = require('./build-before-pack')
|
const replaceLib = require('./build-before-pack')
|
||||||
|
const treeKill = require('tree-kill')
|
||||||
|
const { debounce } = require('./utils')
|
||||||
|
|
||||||
let electronProcess = null
|
let electronProcess = null
|
||||||
let manualRestart = false
|
|
||||||
let hotMiddlewareRenderer
|
let hotMiddlewareRenderer
|
||||||
let hotMiddlewareRendererLyric
|
let hotMiddlewareRendererLyric
|
||||||
|
|
||||||
@@ -60,7 +61,9 @@ function startRenderer() {
|
|||||||
},
|
},
|
||||||
setupMiddlewares(middlewares, devServer) {
|
setupMiddlewares(middlewares, devServer) {
|
||||||
devServer.app.use(hotMiddlewareRenderer)
|
devServer.app.use(hotMiddlewareRenderer)
|
||||||
|
setImmediate(() => {
|
||||||
devServer.middleware.waitUntilValid(resolve)
|
devServer.middleware.waitUntilValid(resolve)
|
||||||
|
})
|
||||||
|
|
||||||
return middlewares
|
return middlewares
|
||||||
},
|
},
|
||||||
@@ -106,7 +109,9 @@ function startRendererLyric() {
|
|||||||
},
|
},
|
||||||
setupMiddlewares(middlewares, devServer) {
|
setupMiddlewares(middlewares, devServer) {
|
||||||
devServer.app.use(hotMiddlewareRenderer)
|
devServer.app.use(hotMiddlewareRenderer)
|
||||||
|
setImmediate(() => {
|
||||||
devServer.middleware.waitUntilValid(resolve)
|
devServer.middleware.waitUntilValid(resolve)
|
||||||
|
})
|
||||||
return middlewares
|
return middlewares
|
||||||
},
|
},
|
||||||
}, compiler)
|
}, compiler)
|
||||||
@@ -132,9 +137,11 @@ function startRendererScripts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startMain() {
|
function startMain() {
|
||||||
|
let firstRun = true
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
|
// mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
|
||||||
// mainConfig.mode = 'development'
|
// mainConfig.mode = 'development'
|
||||||
|
const runElectronDelay = debounce(startElectron, 200)
|
||||||
const compiler = webpack(mainConfig)
|
const compiler = webpack(mainConfig)
|
||||||
|
|
||||||
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
|
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
|
||||||
@@ -146,23 +153,19 @@ function startMain() {
|
|||||||
compiler.watch({}, (err, stats) => {
|
compiler.watch({}, (err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
reject(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// logStats('Main', stats)
|
// logStats('Main', stats)
|
||||||
|
if (electronProcess) {
|
||||||
if (electronProcess && electronProcess.kill) {
|
electronProcess.removeAllListeners()
|
||||||
manualRestart = true
|
treeKill(electronProcess.pid)
|
||||||
process.kill(electronProcess.pid)
|
|
||||||
electronProcess = null
|
|
||||||
startElectron()
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
manualRestart = false
|
|
||||||
}, 5000)
|
|
||||||
}
|
}
|
||||||
|
if (firstRun) {
|
||||||
|
firstRun = false
|
||||||
resolve()
|
resolve()
|
||||||
|
} else runElectronDelay()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -191,7 +194,7 @@ function startElectron() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
electronProcess.on('close', () => {
|
electronProcess.on('close', () => {
|
||||||
if (!manualRestart) process.exit()
|
process.exit()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-1
@@ -31,7 +31,12 @@ exports.mergeCSSLoader = beforeLoader => {
|
|||||||
esModule: false,
|
esModule: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'css-loader',
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
esModule: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
'postcss-loader',
|
'postcss-loader',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -63,3 +68,15 @@ exports.logStats = (proc, data) => {
|
|||||||
console.log(log)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 487 KiB |
+107
-97
@@ -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;}
|
{\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;}
|
{\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 02010600030101010101}@\'cb\'ce\'cc\'e5;}
|
||||||
|
{\f45\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}@\'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;}
|
{\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;}
|
{\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};}
|
{\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;}
|
{\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;}
|
||||||
{\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);}
|
{\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);}
|
||||||
{\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;}
|
{\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;}
|
||||||
{\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);}
|
{\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);}
|
||||||
{\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;}
|
{\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;}
|
||||||
{\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;}
|
{\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;}
|
||||||
{\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;}
|
{\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\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\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;}
|
{\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
|
\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
|
\'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}}
|
\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\rsid3758332\rsid3950508\rsid4133944\rsid4355753\rsid9533173\rsid10447395\rsid11081282\rsid12910709\rsid13643782\rsid14384001\rsid14511311\rsid15226681}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0
|
{\*\rsidtbl \rsid927107\rsid1398824\rsid2109456\rsid3758332\rsid3950508\rsid4133944\rsid4355753\rsid9533173\rsid10447395\rsid11081282\rsid12910709\rsid13643782\rsid14384001\rsid14511311\rsid15225067\rsid15226681}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0
|
||||||
\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author lysyw}{\operator lysyw}{\creatim\yr2019\mo8\dy17\hr10\min22}{\revtim\yr2023\mo10\dy5\hr14\min42}{\version10}{\edmins4}{\nofpages1}{\nofwords193}{\nofchars1105}
|
\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author lysyw}{\operator lysyw}{\creatim\yr2019\mo8\dy17\hr10\min22}{\revtim\yr2024\mo12\dy7\hr14\min56}{\version11}{\edmins5}{\nofpages2}{\nofwords195}
|
||||||
{\nofcharsws1296}{\vern77}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl2253\margr2253\margt1440\margb1440\gutter0\ltrsect
|
{\nofchars1116}{\nofcharsws1309}{\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
|
\deftab420\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\formshade\horzdoc\dgmargin\dghspace180\dgvspace156
|
||||||
\dghorigin450\dgvorigin0\dghshow0\dgvshow2\jcompress\lnongrid
|
\dghorigin2253\dgvorigin1440\dghshow0\dgvshow2\jcompress\lnongrid
|
||||||
\viewkind5\viewscale100\splytwnine\ftnlytwnine\htmautsp\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct\asianbrkrule\rsidroot3950508\newtblstyruls
|
\viewkind1\viewscale160\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
|
\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
|
!%),.:\'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
|
}{\*\ud\uc0{\*\fchars
|
||||||
@@ -53,123 +56,130 @@ $([\'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
|
\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
|
{\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
|
\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\pararsid14511311 \rtlch\fcs1 \af2\afs22\alang1025 \ltrch\fcs0
|
\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}\pard\plain \ltrpar\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid15225067 \rtlch\fcs1 \af0\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\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'cf\'ee\'c4\'bf
|
\fs21\lang1033\langfe2052\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311 \hich\af13\dbch\af13\loch\f13 Apache License 2.0 }{\rtlch\fcs1 \af13 \ltrch\fcs0
|
\'bb\'f9\'d3\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \hich\af13\dbch\af13\loch\f13 Apache License 2.0 }{\rtlch\fcs1 \af13 \ltrch\fcs0
|
||||||
\loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311 \hich\af13\dbch\af13\loch\f13 Apache License 2.0 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'b5\'c4
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \hich\af13\dbch\af13\loch\f13 Apache License 2.0 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311
|
\'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\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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}
|
||||||
\'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
|
{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \hich\af13\dbch\af13\loch\f13 LX Music }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\'c6\'bd\'cc\'a8\'a1\'b1\'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
|
\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\insrsid15225067\charrsid15225067 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0
|
||||||
\'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
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \loch\af13\hich\af13\dbch\f13 \'ca\'b9\'d3\'c3\'d5\'df}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \u8221\'a1\'b1}{\rtlch\fcs1 \af13
|
||||||
\'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\insrsid14511311\charrsid14511311
|
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid15225067\charrsid15225067 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid15225067\charrsid15225067 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid15225067\charrsid15225067 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid15225067\charrsid15225067 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'d2\'bb\'a1\'a2\'ca\'fd\'be\'dd\'c0\'b4\'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0
|
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par \hich\af13\dbch\af13\loch\f13 1.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par \hich\af13\dbch\af13\loch\f13 1.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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}{
|
\'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\insrsid14511311\charrsid14511311 \hich\af13\dbch\af13\loch\f13 APP}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13
|
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \hich\af13\dbch\af13\loch\f13 APP }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
\'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\insrsid14511311\charrsid14511311
|
\'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\insrsid15225067\charrsid15225067
|
||||||
\par \hich\af13\dbch\af13\loch\f13 1.2 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par
|
||||||
|
\par \hich\af13\dbch\af13\loch\f13 1.2 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
\'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\insrsid14511311\charrsid14511311 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'d2\'f4\'c0\'d6\'c0\'b4\'d4\'b4}{
|
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \loch\af13\hich\af13\dbch\f13 \'d2\'f4\'c0\'d6\'c0\'b4\'d4\'b4}{
|
||||||
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'c9\'e8\'d6\'c3
|
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311
|
\'cb\'f9\'d1\'a1\'d4\'f1\'b5\'c4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \u8220\'a1\'b0}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\loch\af13\hich\af13\dbch\f13 \'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311
|
\loch\af13\hich\af13\dbch\f13 \'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \u8221\'a1\'b1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\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
|
\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\'d7\'d6\'a1\'a2\'b8\'e8\'ca\'d6\'c3\'fb\'d7\'d6\'b5\'c8\'d0\'c5\'cf\'a2\'b4\'ab\'b5\'dd\'b8\'f8}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \u8220\'a1\'b0}{
|
\'b7\'c5\'b5\'c4\'b8\'e8\'c7\'fa\'c3\'fb\'d7\'d6\'a1\'a2\'b8\'e8\'ca\'d6\'c3\'fb\'d7\'d6\'b5\'c8\'d0\'c5\'cf\'a2\'b4\'ab\'b5\'dd\'b8\'f8}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \u8220\'a1\'b0}{
|
||||||
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \u8221\'a1\'b1}{
|
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \loch\af13\hich\af13\dbch\f13 \'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \u8221\'a1\'b1}{
|
||||||
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'a3\'ac\'c8\'f4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \u8220\'a1\'b0}{
|
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \loch\af13\hich\af13\dbch\f13 \'a3\'ac\'c8\'f4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \u8220\'a1\'b0}{
|
||||||
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \u8221\'a1\'b1}{
|
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \loch\af13\hich\af13\dbch\f13 \'d4\'b4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \u8221\'a1\'b1}{
|
||||||
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
\'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
|
\'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
|
\'d2\'f4\'c6\'b5\'d3\'eb\'ca\'b5\'bc\'ca\'b2\'a5\'b7\'c5\'b5\'c4\'d2\'f4\'c6\'b5\'b2\'bb\loch\af13\hich\af13\dbch\f13 \'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\insrsid14511311\charrsid14511311
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par \hich\af13\dbch\af13\loch\f13 1.3 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par
|
||||||
|
\par \hich\af13\dbch\af13\loch\f13 1.3 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\'ce\'d2\'b5\'c4\'ca\'d5\'b2\'d8\'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\'a8\'c0\'fd\'c8\'e7\'ce\'d2\'b5\'c4\'ca\'d5\'b2\'d8\'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
|
\'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\insrsid14511311\charrsid14511311
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'b6\'fe\'a1\'a2\'b0\'e6\'c8\'a8\'ca\'fd\'be\'dd}{\rtlch\fcs1 \af13 \ltrch\fcs0
|
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par \hich\af13\dbch\af13\loch\f13 2.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par \hich\af13\dbch\af13\loch\f13 2.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
\'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\insrsid14511311\charrsid14511311 \hich\af13\dbch\af13\loch\f13 **24}{\rtlch\fcs1 \af13
|
\'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\insrsid15225067\charrsid15225067 \hich\af13\dbch\af13\loch\f13 **24 }{\rtlch\fcs1 \af13
|
||||||
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'d0\'a1\'ca\'b1\'c4\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311
|
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \loch\af13\hich\af13\dbch\f13 \'d0\'a1\'ca\'b1\'c4\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\hich\af13\dbch\af13\loch\f13 **}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\hich\af13\dbch\af13\loch\f13 ** }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311
|
\'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\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par \hich\af13\dbch\af13\loch\f13 3.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par \hich\af13\dbch\af13\loch\f13 3.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
\'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
|
\'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\insrsid14511311\charrsid14511311
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'cb\'c4\'a1\'a2\'d7\'ca\'d4\'b4\'ca\'b9\'d3\'c3}{\rtlch\fcs1 \af13 \ltrch\fcs0
|
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par \hich\af13\dbch\af13\loch\f13 4.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par \hich\af13\dbch\af13\loch\f13 4.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
\'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\insrsid14511311\charrsid14511311
|
\'d2\'c6\'b3\'fd\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'ce\'e5\'a1\'a2\'c3\'e2\'d4\'f0\'c9\'f9\'c3\'f7}{\rtlch\fcs1 \af13 \ltrch\fcs0
|
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par \hich\af13\dbch\af13\loch\f13 5.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par \hich\af13\dbch\af13\loch\f13 5.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
\'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
|
\'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\loch\af13\hich\af13\dbch\f13 \'d3\'d0\'c6\'e4\'cb\'fb\'c9\'cc\'d2\'b5\'cb\'f0
|
\'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
|
||||||
\'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\insrsid14511311\charrsid14511311
|
\'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\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'c1\'f9\'a1\'a2\'ca\'b9\'d3\'c3\'cf\'de\'d6\'c6}{\rtlch\fcs1 \af13 \ltrch\fcs0
|
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par \hich\af13\dbch\af13\loch\f13 6.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par \hich\af13\dbch\af13\loch\f13 6.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311 \hich\af13\dbch\af13\loch\f13 GitHub }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311
|
\'b7\'a2\'b2\'bc\'d3\'da}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \hich\af13\dbch\af13\loch\f13 GitHub }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\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
|
\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\insrsid14511311\charrsid14511311
|
\'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\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par \hich\af13\dbch\af13\loch\f13 6.2 **}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\par \hich\af13\dbch\af13\loch\f13 6.2 **}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid14511311\charrsid14511311 \hich\af13\dbch\af13\loch\f13 ** }{\rtlch\fcs1 \af13 \ltrch\fcs0
|
\'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\insrsid15225067\charrsid15225067 \hich\af13\dbch\af13\loch\f13 ** }{\rtlch\fcs1 \af13 \ltrch\fcs0
|
||||||
\loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \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
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
\'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\loch\af13\hich\af13\dbch\f13 \'d3\'c3\'d5\'df\'b3\'d0\'b5\'a3\'a3\'ac
|
||||||
\'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
|
\'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}{
|
||||||
\ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311
|
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par }\pard \ltrpar\s15\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid3758332 {\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid3758332\charrsid3758332 \loch\af13\hich\af13\dbch\f13 \'c6\'df
|
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \loch\af13\hich\af13\dbch\f13 \'c6\'df\'a1\'a2\'b0\'e6\'c8\'a8\'b1\'a3\'bb\'a4}{\rtlch\fcs1 \af13 \ltrch\fcs0
|
||||||
\'a1\'a2\'b0\'e6\'c8\'a8\'b1\'a3\'bb\'a4}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid3758332\charrsid3758332
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par \hich\af13\dbch\af13\loch\f13 7.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid3758332\charrsid3758332 \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
|
\par \hich\af13\dbch\af13\loch\f13 7.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
||||||
\'a3\'ac\'d6\'a7\'b3\'d6\'d5\'fd\'b0\'e6\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid3758332\charrsid3758332
|
\'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\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid3758332\charrsid3758332 \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
|
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid3758332\charrsid3758332
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par \hich\af13\dbch\af13\loch\f13 8.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid3758332\charrsid3758332 \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
|
\par \hich\af13\dbch\af13\loch\f13 8.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
||||||
\'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}{
|
\'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
|
||||||
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid3758332\charrsid3758332
|
\'a1\'a3}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid3758332\charrsid3758332 \loch\af13\hich\af13\dbch\f13 \'be\'c5\'a1\'a2\'bd\'d3\'ca\'dc\'d0\'ad\'d2\'e9}{\rtlch\fcs1 \af13 \ltrch\fcs0
|
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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\insrsid3758332\charrsid3758332
|
\loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067
|
||||||
\par
|
\par
|
||||||
\par \hich\af13\dbch\af13\loch\f13 9.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid3758332\charrsid3758332 \loch\af13\hich\af13\dbch\f13 \'c8\'f4\'c4\'e3\'ca\'b9\'d3\'c3\'c1\'cb\'b1\'be\'cf\'ee\'c4\'bf\'a3\'ac\'bd\'ab\'b4\'fa\'b1\'ed
|
\par \hich\af13\dbch\af13\loch\f13 9.1 }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
||||||
\'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\insrsid14511311
|
\'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\insrsid15225067\charrsid15225067
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid3758332\charrsid14511311
|
\par
|
||||||
\par }\pard \ltrpar\s15\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14511311 {\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \hich\af13\dbch\af13\loch\f13 * }{
|
\par \hich\af13\dbch\af13\loch\f13 * }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \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
|
||||||
\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid14511311 \loch\af13\hich\af13\dbch\f13 \'c8\'f4\'d0\'ad\'d2\'e9\'b8\'fc\'d0\'c2\'a3\'ac\'cb\'a1\loch\af13\hich\af13\dbch\f13 \'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\insrsid15225067\charrsid15225067
|
||||||
\'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\insrsid9533173
|
\par
|
||||||
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid14511311\charrsid15226681
|
\par \hich\af13\dbch\af13\loch\f13 By: }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid15225067\charrsid15225067 \loch\af13\hich\af13\dbch\f13 \'c2\'e4\'d1\'a9\'ce\'de\'ba\'db}{\rtlch\fcs1 \af0 \ltrch\fcs0
|
||||||
\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: }{
|
\insrsid12910709\charrsid15225067
|
||||||
\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 }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
|
\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
|
||||||
9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
|
9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
|
||||||
5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6
|
5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6
|
||||||
@@ -311,8 +321,8 @@ fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000c03d
|
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000f065
|
||||||
9b1957f7d901feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
|
8a1b7548db01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
|
||||||
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000
|
||||||
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000
|
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000
|
||||||
0000000000000000000000000000000000000000000000000105000000000000}}
|
0000000000000000000000000000000000000000000000000105000000000000}}
|
||||||
+27
-23
@@ -1,48 +1,52 @@
|
|||||||
This project is issued based on the Apache License 2.0 license. The following protocols are supplemented by Apache License 2.0.
|
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.
|
||||||
|
|
||||||
Words stipulate: "this project" in this agreement refers to the Luoxue Music Desktop Edition project; "user" refers to the user who signed this agreement; the "official music platform" refers to The official platform of the music source is collectively referred to; "copyright data" refers to the data that includes but not limited to images, audio, names, etc.
|
DEFINITIONS:
|
||||||
|
|
||||||
1. Data source
|
"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.1 The principle of online data sources of various official platforms of this project is to draw data from its open server (the same data obtained from the unsproting state in the official platform app). After simply screening and merging data, this project Responsible for the legitimacy and accuracy of the data.
|
1. Data Source
|
||||||
1.2 The ability of this project itself does not obtain a certain audio data. The online audio data source used in this project comes from the online link of the "Source" selected in the software settings. For example, when playing a song, the project only transmits information such as the song name, singer name and other information to be played to "source". If the "source" returns a link, this project will think that this is the audio data of the song For use, as for whether this is the correct audio data, this project cannot verify its accuracy, so the audio that you want to play may occur during the process of using this project.
|
|
||||||
1.3 The unofficial platform data (such as my collection list) of this project comes from synchronous services connected by the user's local system or user connection. This project is not responsible for the legality and accuracy of these data.
|
|
||||||
|
|
||||||
2. Copyright data
|
1.1 The principle of the online data sources of the various official 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 streaming service app). After simply screening and merging the data, this project is responsible for the legitimacy and accuracy of the data.
|
||||||
|
|
||||||
2.1 During the process of using this project, copyright data may be generated. For these copyright data, this project does not have their ownership. In order to avoid infringement, users must remove copyright data generated during the process of using this project within ** 24 hours.
|
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 of the "source" selected in the software settings. For example, when playing a song, this project only transmits information such as the song name, singer name, and other information to be played to "source". If the "source" returns a link, this project will think that this is the audio data of the song for use, 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.
|
||||||
|
|
||||||
3. Alias of Music Platform
|
1.3 The unofficial streaming service data (such as my collection list) of this project comes from synchronous services connected by the user's local system or user connection. This project is not responsible for the legality and accuracy of this data.
|
||||||
|
|
||||||
3.1 The official music platform in this project is named a name for the official music platform in this project, without maliciousness. If the official music platform feels inappropriate, you can contact this project to change or remove.
|
2. Copyrighted Data
|
||||||
|
|
||||||
Fourth, resource use
|
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 **.
|
||||||
|
|
||||||
4.1 The parts used in this project include, but not limited to fonts, pictures and other resources from the Internet. If infringement occurs, you can contact this project.
|
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. 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 unable to use this item (including but not limited to the loss of goodwill, stop work, computer, computer Damage compensation caused by faults or faults, or any or all other commercial damage or losses) is responsible for users.
|
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. Use Restrictions
|
||||||
|
|
||||||
6.1 This project is completely free, and open source is published in GitHub for the learning exchanges of technology for people all over the world. This project does not guarantee that the technology in the project may violate local laws and regulations.
|
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. ** For the user's use of any illegal and illegal acts caused by the use of the project if the user is not allowed to be allowed to the user, it is undertaken by the user. Sexual responsibility.
|
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. Copyright Protection
|
||||||
|
|
||||||
7.1 Music platform is not easy, please respect the copyright and support genuine.
|
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. 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.
|
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. Accepting Agreement
|
||||||
|
|
||||||
9.1 If you use this project, it will represent you accept this 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 the open source address.
|
* 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
|
* The content of this agreement is translated from the Chinese version of the agreement.
|
||||||
|
|
||||||
By: lyswhut
|
By: lyswhut
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
本项目基于 Apache License 2.0 许可证发行,以下协议是对于 Apache License 2.0 的补充,如有冲突,以以下协议为准。
|
本项目基于 Apache License 2.0 许可证发行,以下协议是对于 Apache License 2.0 的补充,如有冲突,以以下协议为准。
|
||||||
|
|
||||||
词语约定:本协议中的“本项目”指洛雪音乐桌面版项目;“使用者”指签署本协议的使用者;“官方音乐平台”指对本项目内置的包括酷我、酷狗、咪咕等音乐源的官方平台统称;“版权数据”指包括但不限于图像、音频、名字等在内的他人拥有所属版权的数据。
|
词语约定:本协议中的“本项目”指 LX Music 桌面版项目;“使用者”指签署本协议的使用者;“官方音乐平台”指对本项目内置的包括酷我、酷狗、咪咕等音乐源的官方平台统称;“版权数据”指包括但不限于图像、音频、名字等在内的他人拥有所属版权的数据。
|
||||||
|
|
||||||
一、数据来源
|
一、数据来源
|
||||||
|
|
||||||
1.1 本项目的各官方平台在线数据来源原理是从其公开服务器中拉取数据(与未登录状态在官方平台APP获取的数据相同),经过对数据简单地筛选与合并后进行展示,因此本项目不对数据的合法性、准确性负责。
|
1.1 本项目的各官方平台在线数据来源原理是从其公开服务器中拉取数据(与未登录状态在官方平台 APP 获取的数据相同),经过对数据简单地筛选与合并后进行展示,因此本项目不对数据的合法性、准确性负责。
|
||||||
|
|
||||||
1.2 本项目本身没有获取某个音频数据的能力,本项目使用的在线音频数据来源来自软件设置内“音乐来源”设置所选择的“源”返回的在线链接。例如播放某首歌,本项目所做的只是将希望播放的歌曲名字、歌手名字等信息传递给“源”,若“源”返回了一个链接,则本项目将认为这就是该歌曲的音频数据而进行使用,至于这是不是正确的音频数据本项目无法校验其准确性,所以使用本项目的过程中可能会出现希望播放的音频与实际播放的音频不对应或者无法播放的问题。
|
1.2 本项目本身没有获取某个音频数据的能力,本项目使用的在线音频数据来源来自软件设置内“音乐来源”设置所选择的“源”返回的在线链接。例如播放某首歌,本项目所做的只是将希望播放的歌曲名字、歌手名字等信息传递给“源”,若“源”返回了一个链接,则本项目将认为这就是该歌曲的音频数据而进行使用,至于这是不是正确的音频数据本项目无法校验其准确性,所以使用本项目的过程中可能会出现希望播放的音频与实际播放的音频不对应或者无法播放的问题。
|
||||||
|
|
||||||
1.3 本项目的非官方平台数据(例如我的收藏列表)来自使用者本地系统或者使用者连接的同步服务,本项目不对这些数据的合法性、准确性负责。
|
1.3 本项目的非官方平台数据(例如我的收藏列表)来自使用者本地系统或者使用者连接的同步服务,本项目不对这些数据的合法性、准确性负责。
|
||||||
|
|
||||||
二、版权数据
|
二、版权数据
|
||||||
|
|
||||||
2.1 使用本项目的过程中可能会产生版权数据。对于这些版权数据,本项目不拥有它们的所有权。为了避免侵权,使用者务必在**24小时内**清除使用本项目的过程中所产生的版权数据。
|
2.1 使用本项目的过程中可能会产生版权数据。对于这些版权数据,本项目不拥有它们的所有权。为了避免侵权,使用者务必在 **24 小时内** 清除使用本项目的过程中所产生的版权数据。
|
||||||
|
|
||||||
三、音乐平台别名
|
三、音乐平台别名
|
||||||
|
|
||||||
@@ -40,7 +42,7 @@
|
|||||||
|
|
||||||
九、接受协议
|
九、接受协议
|
||||||
|
|
||||||
9.1 若你使用了本项目,将代表你接受本协议。
|
9.1 若你使用了本项目,即代表你接受本协议。
|
||||||
|
|
||||||
* 若协议更新,恕不另行通知,可到开源地址查看。
|
* 若协议更新,恕不另行通知,可到开源地址查看。
|
||||||
|
|
||||||
|
|||||||
Generated
+5996
-18874
File diff suppressed because it is too large
Load Diff
+57
-52
@@ -1,9 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "lx-music-desktop",
|
"name": "lx-music-desktop",
|
||||||
"version": "2.7.0-beta.2",
|
"version": "2.10.0-beta.12",
|
||||||
"description": "一个免费的音乐查找助手",
|
"description": "一个免费的音乐查找助手",
|
||||||
"main": "./dist/main.js",
|
"main": "./dist/main.js",
|
||||||
"productName": "lx-music-desktop",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"pack": "node build-config/pack.js && npm run pack:win:setup:x64",
|
"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",
|
"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",
|
||||||
@@ -108,99 +107,102 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
|
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.3",
|
"@babel/core": "^7.26.7",
|
||||||
"@babel/eslint-parser": "^7.24.1",
|
"@babel/eslint-parser": "^7.26.5",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
"@babel/plugin-transform-modules-umd": "^7.24.1",
|
"@babel/plugin-transform-modules-umd": "^7.25.9",
|
||||||
"@babel/plugin-transform-runtime": "^7.24.3",
|
"@babel/plugin-transform-runtime": "^7.25.9",
|
||||||
"@babel/preset-env": "^7.24.3",
|
"@babel/preset-env": "^7.26.7",
|
||||||
"@babel/preset-typescript": "^7.24.1",
|
"@babel/preset-typescript": "^7.26.0",
|
||||||
"@tsconfig/recommended": "^1.0.3",
|
"@tsconfig/recommended": "^1.0.8",
|
||||||
"@types/better-sqlite3": "^7.6.9",
|
"@types/better-sqlite3": "^7.6.12",
|
||||||
"@types/needle": "^3.3.0",
|
"@types/needle": "^3.3.0",
|
||||||
|
"@types/node": "^20.17.16",
|
||||||
"@types/tunnel": "^0.0.7",
|
"@types/tunnel": "^0.0.7",
|
||||||
"@types/ws": "8.5.4",
|
"@types/ws": "8.5.4",
|
||||||
"@volar/vue-language-plugin-pug": "^1.6.5",
|
"@volar/vue-language-plugin-pug": "^1.6.5",
|
||||||
"@vue/language-plugin-pug": "^2.0.7",
|
"@vue/language-plugin-pug": "^2.2.0",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.2.1",
|
||||||
"browserslist": "^4.23.0",
|
"browserslist": "^4.24.4",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"changelog-parser": "^3.0.1",
|
"changelog-parser": "^3.0.1",
|
||||||
"copy-webpack-plugin": "^12.0.2",
|
"copy-webpack-plugin": "^12.0.2",
|
||||||
"core-js": "^3.36.1",
|
"core-js": "^3.40.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "^6.10.0",
|
"css-loader": "^7.1.2",
|
||||||
"css-minimizer-webpack-plugin": "^6.0.0",
|
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||||
"del": "^6.1.1",
|
"del": "^6.1.1",
|
||||||
"electron": "^27.3.7",
|
"electron": "^32.3.0",
|
||||||
"electron-builder": "^24.13.3",
|
"electron-builder": "^26.0.0-alpha.11",
|
||||||
"electron-debug": "^3.2.0",
|
"electron-debug": "^3.2.0",
|
||||||
"electron-devtools-installer": "^3.2.0",
|
"electron-devtools-installer": "github:lyswhut/electron-devtools-installer#64596d615c1fc891eefd8aef1dfcb2c87aaadf03",
|
||||||
"electron-to-chromium": "^1.4.715",
|
"electron-to-chromium": "^1.5.88",
|
||||||
"electron-updater": "^6.1.8",
|
"electron-updater": "^6.4.0-alpha.4",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-standard": "^17.1.0",
|
"eslint-config-standard": "^17.1.0",
|
||||||
"eslint-config-standard-with-typescript": "^43.0.1",
|
"eslint-config-standard-with-typescript": "^43.0.1",
|
||||||
"eslint-formatter-friendly": "github:lyswhut/eslint-friendly-formatter#2170d1320e2fad13615a9dcf229669f0bb473a53",
|
"eslint-formatter-friendly": "github:lyswhut/eslint-friendly-formatter#2170d1320e2fad13615a9dcf229669f0bb473a53",
|
||||||
"eslint-plugin-html": "^8.0.0",
|
"eslint-plugin-html": "^8.1.2",
|
||||||
"eslint-plugin-vue": "^9.23.0",
|
"eslint-plugin-vue": "^9.32.0",
|
||||||
"eslint-plugin-vue-pug": "^0.6.2",
|
"eslint-plugin-vue-pug": "^0.6.2",
|
||||||
"eslint-webpack-plugin": "^4.1.0",
|
"eslint-webpack-plugin": "^4.2.0",
|
||||||
"html-webpack-plugin": "^5.6.0",
|
"html-webpack-plugin": "^5.6.3",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.2",
|
||||||
"less-loader": "^12.2.0",
|
"less-loader": "^12.2.0",
|
||||||
"mini-css-extract-plugin": "^2.8.1",
|
"mini-css-extract-plugin": "^2.9.2",
|
||||||
"node-loader": "^2.0.0",
|
"node-loader": "^2.1.0",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.5.1",
|
||||||
"postcss-loader": "^8.1.1",
|
"postcss-loader": "^8.1.1",
|
||||||
"postcss-pxtorem": "^6.1.0",
|
"postcss-pxtorem": "^6.1.0",
|
||||||
"pug": "^3.0.2",
|
"pug": "^3.0.3",
|
||||||
"pug-plain-loader": "^1.1.0",
|
"pug-plain-loader": "^1.1.0",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^6.0.1",
|
||||||
"spinnies": "github:lyswhut/spinnies#233305c58694aa3b053e3ab9af9049993f918b9d",
|
"spinnies": "github:lyswhut/spinnies#233305c58694aa3b053e3ab9af9049993f918b9d",
|
||||||
"svg-sprite-loader": "^6.0.11",
|
"svg-sprite-loader": "^6.0.11",
|
||||||
"svg-transform-loader": "^2.0.13",
|
"svg-transform-loader": "^2.0.13",
|
||||||
"svgo-loader": "^4.0.0",
|
"svgo-loader": "^4.0.0",
|
||||||
"terser": "^5.29.2",
|
"terser": "^5.37.0",
|
||||||
"terser-webpack-plugin": "^5.3.10",
|
"terser-webpack-plugin": "^5.3.11",
|
||||||
"ts-loader": "^9.5.1",
|
"tree-kill": "^1.2.2",
|
||||||
"typescript": "^5.4.3",
|
"ts-loader": "^9.5.2",
|
||||||
"vue-eslint-parser": "^9.4.2",
|
"typescript": "5.7.3",
|
||||||
|
"vue-eslint-parser": "^9.4.3",
|
||||||
"vue-loader": "^17.4.2",
|
"vue-loader": "^17.4.2",
|
||||||
"vue-template-compiler": "^2.7.16",
|
"vue-template-compiler": "^2.7.16",
|
||||||
"webpack": "^5.91.0",
|
"webpack": "^5.97.1",
|
||||||
"webpack-cli": "^5.1.4",
|
"webpack-cli": "^6.0.1",
|
||||||
"webpack-dev-server": "^5.0.4",
|
"webpack-dev-server": "5.2.0",
|
||||||
"webpack-hot-middleware": "github:lyswhut/webpack-hot-middleware#329c4375134b89d39da23a56a94db651247c74a1",
|
"webpack-hot-middleware": "github:lyswhut/webpack-hot-middleware#329c4375134b89d39da23a56a94db651247c74a1",
|
||||||
"webpack-merge": "^5.10.0"
|
"webpack-merge": "^6.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@simonwep/pickr": "^1.9.0",
|
"@simonwep/pickr": "^1.9.1",
|
||||||
"better-sqlite3": "^9.4.3",
|
"better-sqlite3": "^11.8.1",
|
||||||
"bufferutil": "^4.0.8",
|
"bufferutil": "^4.0.9",
|
||||||
"comlink": "~4.3.1",
|
"comlink": "~4.3.1",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"electron-log": "^5.1.2",
|
"electron-log": "^5.2.4",
|
||||||
"font-list": "^1.5.1",
|
"font-list": "^1.5.1",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"image-size": "^1.1.0",
|
"image-size": "^1.1.0",
|
||||||
"jschardet": "^3.1.0",
|
"jschardet": "^3.1.4",
|
||||||
"long": "^5.2.3",
|
"long": "^5.2.4",
|
||||||
"message2call": "^0.1.3",
|
"message2call": "^0.1.3",
|
||||||
"music-metadata": "^8.1.4",
|
"music-metadata": "^10.7.1",
|
||||||
"needle": "github:lyswhut/needle#93299ac841b7e9a9f82ca7279b88aaaeda404060",
|
"needle": "github:lyswhut/needle#93299ac841b7e9a9f82ca7279b88aaaeda404060",
|
||||||
"node-id3": "^0.2.6",
|
"node-id3": "^0.2.6",
|
||||||
"sortablejs": "^1.15.2",
|
"sortablejs": "^1.15.6",
|
||||||
"tunnel": "^0.0.6",
|
"tunnel": "^0.0.6",
|
||||||
"utf-8-validate": "^6.0.3",
|
"utf-8-validate": "^6.0.5",
|
||||||
"vue": "~3.3.13",
|
"vue": "~3.3.13",
|
||||||
"vue-router": "^4.3.0",
|
"vue-router": "^4.5.0",
|
||||||
"ws": "^8.16.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"got": "^11",
|
"got": "^11",
|
||||||
"json5": "latest",
|
"json5": "latest",
|
||||||
|
"node-abi": "latest",
|
||||||
"minimatch": "latest",
|
"minimatch": "latest",
|
||||||
"semver": "latest",
|
"semver": "latest",
|
||||||
"svg-transform-loader": {
|
"svg-transform-loader": {
|
||||||
@@ -212,6 +214,9 @@
|
|||||||
"svg-baker": {
|
"svg-baker": {
|
||||||
"postcss": "latest"
|
"postcss": "latest"
|
||||||
},
|
},
|
||||||
|
"braces": "latest",
|
||||||
|
"node-gyp-build": "latest",
|
||||||
|
"micromatch": "latest",
|
||||||
"http-cache-semantics": "latest"
|
"http-cache-semantics": "latest"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-16
@@ -1,29 +1,49 @@
|
|||||||
|
### 不兼容性变更
|
||||||
|
|
||||||
|
Linux 系统至少需要 `GLIBC_2.29` 版本才能运行。
|
||||||
|
|
||||||
|
由于将 Electron 升级到 v32.x,原生库的编译被限制到不低于 C++ 20,试了几次无法在 docker 镜像 `node:16` 安装 gcc-10,最终将构建使用镜像更新到 `node:18`。
|
||||||
|
|
||||||
### 新增
|
### 新增
|
||||||
|
|
||||||
- 主题编辑器添加“深色字体”选项,启用后将减少字体颜色梯度,各类字体(正文、标签字体等)颜色将更接近,这有助于解决创建全透明主题时可能出现的字体配色问题(#1799)
|
- 新增下载的歌曲按列表名分组的功能,默认关闭,可以通过「设置 → 下载设置 → 将文件保存到以对应列表命名的子目录中」启用(#2145)
|
||||||
- 新增在线自定义源导入功能,允许通过http/https链接导入自定义源
|
- 新增托盘图标样式「跟随系统亮暗模式」设置,可以在「设置 → 其他」里启用 (#2016)
|
||||||
|
- 支持本地同名 `.krc` 格式歌词文件的读取(#2053)
|
||||||
|
- 开放 API 新增播放器播放/暂停、切歌、收藏当前播放歌曲等接口调用,详情看文档「开放 API 服务」部分(#2077, @14Kay)
|
||||||
|
|
||||||
### 优化
|
### 优化
|
||||||
|
|
||||||
- 主题设置默认折叠其他主题以优化进入设置界面时的性能
|
- 优化正常播放结束时的下一首歌曲播放衔接度,在歌曲即将结束播放时将预获取下一首歌曲的播放链接,减少自动切歌时的等待时间(#2126)
|
||||||
- 不再丢弃kg源逐行歌词(@helloplhm-qwq)
|
- 优化歌曲换源机制,提升换源正确率
|
||||||
- 支持kw源排行榜显示大小(revert @Folltoshe #1460)
|
- 优化 Windows 平台上桌面歌词窗口大小调整机制,改用原生的窗口调整方式(#2137)
|
||||||
- 托盘菜单添加多语言支持(#1802)
|
- 修正搜索歌曲提示框文案(#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)
|
||||||
- 修复mg歌词在某些情况下获取失败的问题(#1783)
|
- 修复获取自定义环境音效预设列表逻辑问题
|
||||||
- 修复mg歌单搜索(@helloplhm-qwq)
|
- 修复 `.m4a` 文件内嵌歌词无法读取的问题(#2090)
|
||||||
- 修复kg最新评论无法获取的问题(@helloplhm-qwq)
|
- 修复 Windows 任务管理器中的进程名显示为软件描述的问题(#2147)
|
||||||
- 修复更新超时弹窗在非更新阶段意外弹出的问题(#1797)
|
- 修复本地歌曲同名歌词文件调整偏移时间后,下次再播放时调整的设置未被应用的问题(#2139)
|
||||||
- 修复网络代理设置没有对自定义源的网络请求生效的问题(#1814)
|
- 修复首次打开软件后直接创建并删除列表时的报错问题(#2175, @14Kay)
|
||||||
|
|
||||||
### 移除
|
### 变更
|
||||||
|
|
||||||
- 移除未使用的网络代理设置用户名、密码设置,实际上在 v1.20.0 起这两个设置就没有在被内部使用
|
- 不再长期缓存换源歌曲信息
|
||||||
|
- 更新软件默认使用的字体,现在软件尽量使用系统自带的默认字体
|
||||||
|
- Linux 系统至少需要 `GLIBC_2.29` 版本才能运行
|
||||||
|
|
||||||
### 其他
|
### 其他
|
||||||
|
|
||||||
- 更新 electron 到 v27.3.7
|
- 更新 Readme 文档,优化文案编排(#2146, Thanks @3gf8jv4dv)
|
||||||
|
- 更新 Issue 模板(#2153, @3gf8jv4dv)
|
||||||
|
- 更新项目文档(@3gf8jv4dv)
|
||||||
|
- 修订项目协议文件(#2146, #2152, @3gf8jv4dv)
|
||||||
|
- 更新 Electron 到 v32.3.0
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -80,3 +80,5 @@ export const DOWNLOAD_STATUS = {
|
|||||||
} as const
|
} as const
|
||||||
|
|
||||||
export const QUALITYS = ['flac24bit', 'flac', 'wav', 'ape', '320k', '192k', '128k'] as const
|
export const QUALITYS = ['flac24bit', 'flac', 'wav', 'ape', '320k', '192k', '128k'] as const
|
||||||
|
|
||||||
|
export const TRAY_AUTO_ID = -1
|
||||||
|
|||||||
@@ -24,19 +24,21 @@ const defaultSetting: LX.AppSetting = {
|
|||||||
|
|
||||||
'player.startupAutoPlay': false,
|
'player.startupAutoPlay': false,
|
||||||
'player.togglePlayMethod': 'listLoop',
|
'player.togglePlayMethod': 'listLoop',
|
||||||
'player.highQuality': false,
|
'player.playQuality': '128k',
|
||||||
'player.isShowTaskProgess': true,
|
'player.isShowTaskProgess': true,
|
||||||
|
'player.isShowStatusBarLyric': false,
|
||||||
'player.volume': 1,
|
'player.volume': 1,
|
||||||
'player.powerSaveBlocker': true,
|
'player.powerSaveBlocker': true,
|
||||||
'player.isMute': false,
|
'player.isMute': false,
|
||||||
'player.playbackRate': 1,
|
'player.playbackRate': 1,
|
||||||
'player.preservesPitch': true,
|
'player.preservesPitch': true,
|
||||||
|
'player.isMaxOutputChannelCount': false,
|
||||||
'player.mediaDeviceId': 'default',
|
'player.mediaDeviceId': 'default',
|
||||||
'player.isMediaDeviceRemovedStopPlay': false,
|
'player.isMediaDeviceRemovedStopPlay': false,
|
||||||
'player.isShowLyricTranslation': false,
|
'player.isShowLyricTranslation': false,
|
||||||
'player.isShowLyricRoma': false,
|
'player.isShowLyricRoma': false,
|
||||||
'player.isS2t': false,
|
'player.isS2t': false,
|
||||||
'player.isPlayLxlrc': isWin,
|
'player.isPlayLxlrc': !isMac,
|
||||||
'player.isSavePlayTime': false,
|
'player.isSavePlayTime': false,
|
||||||
'player.audioVisualization': false,
|
'player.audioVisualization': false,
|
||||||
'player.waitPlayEndStop': true,
|
'player.waitPlayEndStop': true,
|
||||||
@@ -63,8 +65,9 @@ const defaultSetting: LX.AppSetting = {
|
|||||||
|
|
||||||
'playDetail.isZoomActiveLrc': false,
|
'playDetail.isZoomActiveLrc': false,
|
||||||
'playDetail.isShowLyricProgressSetting': false,
|
'playDetail.isShowLyricProgressSetting': false,
|
||||||
'playDetail.style.fontSize': 100,
|
'playDetail.style.fontSize': 140,
|
||||||
'playDetail.style.align': 'center',
|
'playDetail.style.align': 'center',
|
||||||
|
'playDetail.isDelayScroll': true,
|
||||||
|
|
||||||
'desktopLyric.enable': false,
|
'desktopLyric.enable': false,
|
||||||
'desktopLyric.isLock': false,
|
'desktopLyric.isLock': false,
|
||||||
@@ -104,6 +107,7 @@ const defaultSetting: LX.AppSetting = {
|
|||||||
'list.actionButtonsVisible': false,
|
'list.actionButtonsVisible': false,
|
||||||
|
|
||||||
'download.enable': false,
|
'download.enable': false,
|
||||||
|
'download.isSavePathGroupByListName': false,
|
||||||
'download.savePath': path.join(os.homedir(), 'Desktop'),
|
'download.savePath': path.join(os.homedir(), 'Desktop'),
|
||||||
'download.fileName': '歌名 - 歌手',
|
'download.fileName': '歌名 - 歌手',
|
||||||
'download.maxDownloadNum': 3,
|
'download.maxDownloadNum': 3,
|
||||||
@@ -136,6 +140,10 @@ const defaultSetting: LX.AppSetting = {
|
|||||||
'sync.server.maxSsnapshotNum': 5,
|
'sync.server.maxSsnapshotNum': 5,
|
||||||
'sync.client.host': '',
|
'sync.client.host': '',
|
||||||
|
|
||||||
|
'openAPI.enable': false,
|
||||||
|
'openAPI.port': '23330',
|
||||||
|
'openAPI.bindLan': false,
|
||||||
|
|
||||||
// 'theme.id': 'blue_plus',
|
// 'theme.id': 'blue_plus',
|
||||||
'theme.id': 'green',
|
'theme.id': 'green',
|
||||||
'theme.lightId': 'green',
|
'theme.lightId': 'green',
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const modules = {
|
|||||||
open_dev_tools: 'open_dev_tools',
|
open_dev_tools: 'open_dev_tools',
|
||||||
set_power_save_blocker: 'set_power_save_blocker',
|
set_power_save_blocker: 'set_power_save_blocker',
|
||||||
|
|
||||||
progress: 'progress',
|
player_status: 'player_status',
|
||||||
change_tray: 'change_tray',
|
change_tray: 'change_tray',
|
||||||
quit_update: 'quit_update',
|
quit_update: 'quit_update',
|
||||||
update_check: 'update_check',
|
update_check: 'update_check',
|
||||||
@@ -134,6 +134,7 @@ const modules = {
|
|||||||
clear_music_url: 'clear_music_url',
|
clear_music_url: 'clear_music_url',
|
||||||
get_music_url_count: 'get_music_url_count',
|
get_music_url_count: 'get_music_url_count',
|
||||||
|
|
||||||
|
open_api_action: 'open_api_action',
|
||||||
sync_action: 'sync_action',
|
sync_action: 'sync_action',
|
||||||
sync_get_server_devices: 'sync_get_server_devices',
|
sync_get_server_devices: 'sync_get_server_devices',
|
||||||
sync_remove_server_device: 'sync_remove_server_device',
|
sync_remove_server_device: 'sync_remove_server_device',
|
||||||
|
|||||||
Vendored
+39
-2
@@ -89,15 +89,21 @@ declare global {
|
|||||||
'player.togglePlayMethod': 'listLoop' | 'random' | 'list' | 'singleLoop' | 'none'
|
'player.togglePlayMethod': 'listLoop' | 'random' | 'list' | 'singleLoop' | 'none'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否优先播放320k音质
|
* 优先播放的音质
|
||||||
*/
|
*/
|
||||||
'player.highQuality': boolean
|
'player.playQuality': LX.Quality
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示任务栏进度条
|
* 是否显示任务栏进度条
|
||||||
*/
|
*/
|
||||||
'player.isShowTaskProgess': boolean
|
'player.isShowTaskProgess': boolean
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否将歌词显示在状态栏
|
||||||
|
*/
|
||||||
|
'player.isShowStatusBarLyric': boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 音量大小
|
* 音量大小
|
||||||
*/
|
*/
|
||||||
@@ -123,6 +129,11 @@ declare global {
|
|||||||
*/
|
*/
|
||||||
'player.preservesPitch': boolean
|
'player.preservesPitch': boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用设备能处理的最大声道数输出音频
|
||||||
|
*/
|
||||||
|
'player.isMaxOutputChannelCount': boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 音频输出设备id
|
* 音频输出设备id
|
||||||
*/
|
*/
|
||||||
@@ -288,6 +299,11 @@ declare global {
|
|||||||
*/
|
*/
|
||||||
'playDetail.style.align': 'center' | 'left' | 'right'
|
'playDetail.style.align': 'center' | 'left' | 'right'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放详情页-是否延迟桌面歌词滚动
|
||||||
|
*/
|
||||||
|
'playDetail.isDelayScroll': boolean
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否启用桌面歌词
|
* 是否启用桌面歌词
|
||||||
@@ -469,6 +485,11 @@ declare global {
|
|||||||
*/
|
*/
|
||||||
'download.enable': boolean
|
'download.enable': boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按列表名分组保存
|
||||||
|
*/
|
||||||
|
'download.isSavePathGroupByListName': boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载路径
|
* 下载路径
|
||||||
*/
|
*/
|
||||||
@@ -619,6 +640,22 @@ declare global {
|
|||||||
*/
|
*/
|
||||||
'sync.client.host': string
|
'sync.client.host': string
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用开放API服务
|
||||||
|
*/
|
||||||
|
'openAPI.enable': boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API服务端口号
|
||||||
|
*/
|
||||||
|
'openAPI.port': '23330' | string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否绑定到局域网
|
||||||
|
*/
|
||||||
|
'openAPI.bindLan': boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否在离开搜索界面时自动清空搜索框
|
* 是否在离开搜索界面时自动清空搜索框
|
||||||
*/
|
*/
|
||||||
|
|||||||
Vendored
+1
@@ -59,6 +59,7 @@ declare global {
|
|||||||
ext: FileExt
|
ext: FileExt
|
||||||
fileName: string
|
fileName: string
|
||||||
filePath: string
|
filePath: string
|
||||||
|
listId?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
@@ -22,6 +22,7 @@ declare namespace LX {
|
|||||||
songId: string | number // 歌曲ID,mg源为copyrightId,local为文件路径
|
songId: string | number // 歌曲ID,mg源为copyrightId,local为文件路径
|
||||||
albumName: string // 歌曲专辑名称
|
albumName: string // 歌曲专辑名称
|
||||||
picUrl?: string | null // 歌曲图片链接
|
picUrl?: string | null // 歌曲图片链接
|
||||||
|
toggleMusicInfo?: MusicInfoOnline | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MusicInfoMeta_online extends MusicInfoMetaBase {
|
interface MusicInfoMeta_online extends MusicInfoMetaBase {
|
||||||
|
|||||||
Vendored
+26
@@ -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>
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+15
@@ -15,5 +15,20 @@ declare namespace LX {
|
|||||||
interface LyricInfo extends LX.Music.LyricInfo {
|
interface LyricInfo extends LX.Music.LyricInfo {
|
||||||
rawlrcInfo: 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
|
||||||
|
collect: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ class Task extends EventEmitter {
|
|||||||
if (err) {
|
if (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
this.__handleError(err)
|
this.__handleError(err)
|
||||||
void this.stop()
|
return
|
||||||
}
|
}
|
||||||
if (this.closeWaiting && !this.dataWriteQueueLength) this.ws?.close()
|
if (this.closeWaiting && !this.dataWriteQueueLength) this.ws?.close()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,6 +6,14 @@ export const isWin = process.platform == 'win32'
|
|||||||
export const isMac = process.platform == 'darwin'
|
export const isMac = process.platform == 'darwin'
|
||||||
export const isProd = process.env.NODE_ENV == 'production'
|
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
|
// https://stackoverflow.com/a/53387532
|
||||||
export function compareVer(currentVer: string, targetVer: string): -1 | 0 | 1 {
|
export function compareVer(currentVer: string, targetVer: string): -1 | 0 | 1 {
|
||||||
|
|||||||
@@ -227,4 +227,8 @@ export default class Lyric {
|
|||||||
this._handleLinePlayerOnPlay(num, '', this.linePlayer._currentTime())
|
this._handleLinePlayerOnPlay(num, '', this.linePlayer._currentTime())
|
||||||
} else this.playingLineNum = 0
|
} else this.playingLineNum = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDisabledAutoPause(autoPause) {
|
||||||
|
this.linePlayer.setDisabledAutoPause(autoPause)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,4 +224,16 @@ export default class LinePlayer {
|
|||||||
this.extendedLyrics = extendedLyrics
|
this.extendedLyrics = extendedLyrics
|
||||||
this._init()
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ export const getNow = typeof performance == 'object' && window.performance.now ?
|
|||||||
|
|
||||||
export class TimeoutTools {
|
export class TimeoutTools {
|
||||||
constructor(thresholdTime = 80) {
|
constructor(thresholdTime = 80) {
|
||||||
|
this.nextTick = window.requestAnimationFrame.bind(window)
|
||||||
|
this.cancelNextTick = window.cancelAnimationFrame.bind(window)
|
||||||
this.invokeTime = 0
|
this.invokeTime = 0
|
||||||
this.animationFrameId = null
|
this.animationFrameId = null
|
||||||
this.timeoutId = null
|
this.timeoutId = null
|
||||||
@@ -11,7 +13,7 @@ export class TimeoutTools {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run() {
|
run() {
|
||||||
this.animationFrameId = window.requestAnimationFrame(() => {
|
this.animationFrameId = this.nextTick(() => {
|
||||||
this.animationFrameId = null
|
this.animationFrameId = null
|
||||||
let diff = this.invokeTime - getNow()
|
let diff = this.invokeTime - getNow()
|
||||||
// console.log('diff', diff)
|
// console.log('diff', diff)
|
||||||
@@ -39,7 +41,7 @@ export class TimeoutTools {
|
|||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
if (this.animationFrameId) {
|
if (this.animationFrameId) {
|
||||||
window.cancelAnimationFrame(this.animationFrameId)
|
this.cancelNextTick(this.animationFrameId)
|
||||||
this.animationFrameId = null
|
this.animationFrameId = null
|
||||||
}
|
}
|
||||||
if (this.timeoutId) {
|
if (this.timeoutId) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const encodeNames = {
|
||||||
|
' ': ' ',
|
||||||
|
'&': '&',
|
||||||
|
'<': '<',
|
||||||
|
'>': '>',
|
||||||
|
'"': '"',
|
||||||
|
''': "'",
|
||||||
|
''': "'",
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const decodeName = (str: string | null = '') => {
|
||||||
|
return str?.replace(/(?:&|<|>|"|'|'| )/gm, (s: string) => encodeNames[s as keyof typeof encodeNames]) ?? ''
|
||||||
|
}
|
||||||
@@ -57,7 +57,6 @@ export default (setting: any): Partial<LX.AppSetting> => {
|
|||||||
setting['common.controlBtnPosition'] = setting.controlBtnPosition
|
setting['common.controlBtnPosition'] = setting.controlBtnPosition
|
||||||
|
|
||||||
setting['player.togglePlayMethod'] = setting.player?.togglePlayMethod
|
setting['player.togglePlayMethod'] = setting.player?.togglePlayMethod
|
||||||
setting['player.highQuality'] = setting.player?.highQuality
|
|
||||||
setting['player.isShowTaskProgess'] = setting.player?.isShowTaskProgess
|
setting['player.isShowTaskProgess'] = setting.player?.isShowTaskProgess
|
||||||
setting['player.volume'] = setting.player?.volume
|
setting['player.volume'] = setting.player?.volume
|
||||||
setting['player.isMute'] = setting.player?.isMute
|
setting['player.isMute'] = setting.player?.isMute
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
const http = require('http')
|
const http = require('http')
|
||||||
const https = require('https')
|
const https = require('https')
|
||||||
const fs = require('fs')
|
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 urlParse = new URL(url)
|
||||||
const httpOptions = {
|
const httpOptions = {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
host: urlParse.hostname,
|
host: urlParse.hostname,
|
||||||
port: urlParse.port,
|
port: urlParse.port,
|
||||||
path: urlParse.pathname + urlParse.search,
|
path: urlParse.pathname + urlParse.search,
|
||||||
|
agent: getRequestAgent(url, proxy),
|
||||||
headers: {
|
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',
|
'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)
|
: http.request(httpOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = (url, filePath) => {
|
module.exports = (url, filePath, proxy) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
sendRequest(url)
|
sendRequest(url, proxy)
|
||||||
.on('response', response => {
|
.on('response', response => {
|
||||||
// console.log(response.statusCode)
|
// console.log(response.statusCode)
|
||||||
if (response.statusCode !== 200 && response.statusCode != 206) {
|
if (response.statusCode !== 200 && response.statusCode != 206) {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const writeMeta = async(filePath, meta, picPath) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = (filePath, meta) => {
|
module.exports = (filePath, meta, proxy) => {
|
||||||
if (!meta.APIC) return writeMeta(filePath, meta)
|
if (!meta.APIC) return writeMeta(filePath, meta)
|
||||||
let picUrl = meta.APIC
|
let picUrl = meta.APIC
|
||||||
delete meta.APIC
|
delete meta.APIC
|
||||||
@@ -68,7 +68,7 @@ module.exports = (filePath, meta) => {
|
|||||||
let picPath = filePath.replace(/\.flac$/, '') + (ext ? ext.replace(extReg, '$1') : '.jpg')
|
let picPath = filePath.replace(/\.flac$/, '') + (ext ? ext.replace(extReg, '$1') : '.jpg')
|
||||||
|
|
||||||
if (picUrl.includes('music.126.net')) picUrl += `${picUrl.includes('?') ? '&' : '?'}param=500y500`
|
if (picUrl.includes('music.126.net')) picUrl += `${picUrl.includes('?') ? '&' : '?'}param=500y500`
|
||||||
download(picUrl, picPath).then(success => {
|
download(picUrl, picPath, proxy).then(success => {
|
||||||
if (success) {
|
if (success) {
|
||||||
writeMeta(filePath, meta, picPath).finally(() => {
|
writeMeta(filePath, meta, picPath).finally(() => {
|
||||||
fs.unlink(picPath, err => {
|
fs.unlink(picPath, err => {
|
||||||
|
|||||||
+1
-1
@@ -5,4 +5,4 @@ export interface MusicMeta {
|
|||||||
APIC: string | null
|
APIC: string | null
|
||||||
lyrics: 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
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ const path = require('path')
|
|||||||
const mp3Meta = require('./mp3Meta')
|
const mp3Meta = require('./mp3Meta')
|
||||||
const flacMeta = require('./flacMeta')
|
const flacMeta = require('./flacMeta')
|
||||||
|
|
||||||
exports.setMeta = (filePath, meta) => {
|
exports.setMeta = (filePath, meta, proxy) => {
|
||||||
switch (path.extname(filePath)) {
|
switch (path.extname(filePath)) {
|
||||||
case '.mp3':
|
case '.mp3':
|
||||||
mp3Meta(filePath, meta)
|
mp3Meta(filePath, meta, proxy)
|
||||||
break
|
break
|
||||||
case '.flac':
|
case '.flac':
|
||||||
flacMeta(filePath, meta)
|
flacMeta(filePath, meta, proxy)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const handleWriteMeta = (meta, filePath) => {
|
|||||||
NodeID3.write(meta, filePath)
|
NodeID3.write(meta, filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = (filePath, meta) => {
|
module.exports = (filePath, meta, proxy) => {
|
||||||
if (!meta.APIC) return handleWriteMeta(meta, filePath)
|
if (!meta.APIC) return handleWriteMeta(meta, filePath)
|
||||||
if (!/^http/.test(meta.APIC)) {
|
if (!/^http/.test(meta.APIC)) {
|
||||||
delete meta.APIC
|
delete meta.APIC
|
||||||
@@ -26,7 +26,7 @@ module.exports = (filePath, meta) => {
|
|||||||
|
|
||||||
let picUrl = meta.APIC
|
let picUrl = meta.APIC
|
||||||
if (picUrl.includes('music.126.net')) picUrl += `${picUrl.includes('?') ? '&' : '?'}param=500y500`
|
if (picUrl.includes('music.126.net')) picUrl += `${picUrl.includes('?') ? '&' : '?'}param=500y500`
|
||||||
download(picUrl, picPath).then(success => {
|
download(picUrl, picPath, proxy).then(success => {
|
||||||
if (success) {
|
if (success) {
|
||||||
meta.APIC = picPath
|
meta.APIC = picPath
|
||||||
handleWriteMeta(meta, filePath)
|
handleWriteMeta(meta, filePath)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import fs from 'node:fs'
|
|||||||
import crypto from 'node:crypto'
|
import crypto from 'node:crypto'
|
||||||
import { gzip, gunzip } from 'node:zlib'
|
import { gzip, gunzip } from 'node:zlib'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
import { networkInterfaces } from 'node:os'
|
||||||
import { log } from '@common/utils'
|
import { log } from '@common/utils'
|
||||||
|
|
||||||
export const joinPath = (...paths: string[]): string => path.join(...paths)
|
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> => {
|
export const getFileStats = async(path: string): Promise<fs.Stats | null> => {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
@@ -185,3 +205,20 @@ export const copyFile = async(sourcePath: string, distPath: string) => {
|
|||||||
export const moveFile = async(sourcePath: string, distPath: string) => {
|
export const moveFile = async(sourcePath: string, distPath: string) => {
|
||||||
return fs.promises.rename(sourcePath, distPath)
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,14 @@ export const toNewMusicInfo = (oldMusicInfo: any): LX.Music.MusicInfo => {
|
|||||||
albumName: oldMusicInfo.albumName, // 歌曲专辑名称
|
albumName: oldMusicInfo.albumName, // 歌曲专辑名称
|
||||||
picUrl: oldMusicInfo.img, // 歌曲图片链接
|
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') {
|
if (oldMusicInfo.source == 'local') {
|
||||||
meta.filePath = oldMusicInfo.filePath ?? oldMusicInfo.songmid ?? ''
|
meta.filePath = oldMusicInfo.filePath ?? oldMusicInfo.songmid ?? ''
|
||||||
@@ -27,6 +35,7 @@ export const toNewMusicInfo = (oldMusicInfo: any): LX.Music.MusicInfo => {
|
|||||||
switch (oldMusicInfo.source) {
|
switch (oldMusicInfo.source) {
|
||||||
case 'kg':
|
case 'kg':
|
||||||
meta.hash = oldMusicInfo.hash
|
meta.hash = oldMusicInfo.hash
|
||||||
|
newInfo.id = oldMusicInfo.songmid + '_' + oldMusicInfo.hash
|
||||||
break
|
break
|
||||||
case 'tx':
|
case 'tx':
|
||||||
meta.strMediaMid = oldMusicInfo.strMediaMid
|
meta.strMediaMid = oldMusicInfo.strMediaMid
|
||||||
@@ -42,14 +51,7 @@ export const toNewMusicInfo = (oldMusicInfo: any): LX.Music.MusicInfo => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return newInfo
|
||||||
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'],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const toOldMusicInfo = (minfo: LX.Music.MusicInfo) => {
|
export const toOldMusicInfo = (minfo: LX.Music.MusicInfo) => {
|
||||||
@@ -128,3 +130,21 @@ export const filterMusicList = <T extends LX.Music.MusicInfo>(list: T[]): T[] =>
|
|||||||
return true
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+424
-393
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -18,7 +18,7 @@ const langs = [
|
|||||||
message: zh_cn,
|
message: zh_cn,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '繁体中文',
|
name: '繁體中文',
|
||||||
locale: 'zh-tw',
|
locale: 'zh-tw',
|
||||||
// alternate: 'zh-hk',
|
// alternate: 'zh-hk',
|
||||||
country: 'cn',
|
country: 'cn',
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"fallback": true
|
"fallback": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "繁体中文",
|
"name": "繁體中文",
|
||||||
"locale": "zh-tw",
|
"locale": "zh-tw",
|
||||||
"alternate": "zh-hk",
|
"alternate": "zh-hk",
|
||||||
"country": "cn"
|
"country": "cn"
|
||||||
|
|||||||
+178
-147
@@ -12,7 +12,7 @@
|
|||||||
"cancel_button_text_2": "不不不,点错了",
|
"cancel_button_text_2": "不不不,点错了",
|
||||||
"close": "关闭",
|
"close": "关闭",
|
||||||
"comment__hot_load_error": "热门评论加载失败,点击尝试重新加载",
|
"comment__hot_load_error": "热门评论加载失败,点击尝试重新加载",
|
||||||
"comment__hot_loading": "热门评论加载中",
|
"comment__hot_loading": "热门评论加载中...",
|
||||||
"comment__hot_title": "热门评论",
|
"comment__hot_title": "热门评论",
|
||||||
"comment__location": "来自{location}",
|
"comment__location": "来自{location}",
|
||||||
"comment__new_load_error": "最新评论加载失败,点击尝试重新加载",
|
"comment__new_load_error": "最新评论加载失败,点击尝试重新加载",
|
||||||
@@ -21,13 +21,13 @@
|
|||||||
"comment__no_content": "暂无评论",
|
"comment__no_content": "暂无评论",
|
||||||
"comment__refresh": "刷新评论",
|
"comment__refresh": "刷新评论",
|
||||||
"comment__show": "歌曲评论",
|
"comment__show": "歌曲评论",
|
||||||
"comment__title": "{name} 的评论",
|
"comment__title": "「{name}」的评论",
|
||||||
"comment__unavailable": "此歌曲不支持获取评论",
|
"comment__unavailable": "此歌曲不支持获取评论",
|
||||||
"confirm_button_text": "是的",
|
"confirm_button_text": "是的",
|
||||||
"copy_tip": "(点击复制)",
|
"copy_tip": "(点击复制)",
|
||||||
"date_format_hour": "{num}小时前",
|
"date_format_hour": "{num} 小时前",
|
||||||
"date_format_minute": "{num}分钟前",
|
"date_format_minute": "{num} 分钟前",
|
||||||
"date_format_second": "{num}秒前",
|
"date_format_second": "{num} 秒前",
|
||||||
"deep_link__handle_error_tip": "调用失败:{message}",
|
"deep_link__handle_error_tip": "调用失败:{message}",
|
||||||
"default": "默认",
|
"default": "默认",
|
||||||
"default_list": "试听列表",
|
"default_list": "试听列表",
|
||||||
@@ -38,22 +38,22 @@
|
|||||||
"desktop_lyric__lock": "锁定歌词",
|
"desktop_lyric__lock": "锁定歌词",
|
||||||
"desktop_lyric__lrc_active_zoom_off": "取消缩放当前播放的歌词",
|
"desktop_lyric__lrc_active_zoom_off": "取消缩放当前播放的歌词",
|
||||||
"desktop_lyric__lrc_active_zoom_on": "缩放当前播放的歌词",
|
"desktop_lyric__lrc_active_zoom_on": "缩放当前播放的歌词",
|
||||||
"desktop_lyric__opactiy_decrease": "增加透明度(右击可微调)",
|
"desktop_lyric__opacity_decrease": "增加透明度(右击可微调)",
|
||||||
"desktop_lyric__opactiy_increase": "减小透明度(右击可微调)",
|
"desktop_lyric__opacity_increase": "减小透明度(右击可微调)",
|
||||||
"desktop_lyric__theme": "主题配色",
|
"desktop_lyric__theme": "主题配色",
|
||||||
"desktop_lyric__unlock": "解锁歌词",
|
"desktop_lyric__unlock": "解锁歌词",
|
||||||
"desktop_lyric__win_top_off": "取消置顶歌词界面",
|
"desktop_lyric__win_top_off": "取消置顶歌词界面",
|
||||||
"desktop_lyric__win_top_on": "置顶歌词界面",
|
"desktop_lyric__win_top_on": "置顶歌词界面",
|
||||||
"download": "下载",
|
"download": "下载",
|
||||||
"download___status_complated": "下载完成",
|
"download___status_completed": "下载完成",
|
||||||
"download___status_error": "任务出错",
|
"download___status_error": "任务出错",
|
||||||
"download___status_paused": "暂停下载",
|
"download___status_paused": "暂停下载",
|
||||||
"download___status_runing": "正在下载",
|
"download___status_running": "正在下载",
|
||||||
"download___status_wating": "等待下载",
|
"download___status_waiting": "等待下载",
|
||||||
"download__all": "全部任务",
|
"download__all": "所有任务",
|
||||||
"download__error": "出错",
|
"download__error": "出错",
|
||||||
"download__finished": "下载完成",
|
"download__finished": "下载完成",
|
||||||
"download__high_quality": "高品音质",
|
"download__high_quality": "高清音质",
|
||||||
"download__lossless": "无损音质",
|
"download__lossless": "无损音质",
|
||||||
"download__multiple_tip": "已选择 {len} 首歌曲",
|
"download__multiple_tip": "已选择 {len} 首歌曲",
|
||||||
"download__multiple_tip2": "请选择要优先下载的音质",
|
"download__multiple_tip2": "请选择要优先下载的音质",
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
"download__paused": "已暂停",
|
"download__paused": "已暂停",
|
||||||
"download__progress": "进度",
|
"download__progress": "进度",
|
||||||
"download__quality": "品质",
|
"download__quality": "品质",
|
||||||
"download__runing": "正在下载",
|
"download__running": "正在下载",
|
||||||
"download__status": "状态",
|
"download__status": "状态",
|
||||||
"download_status_error_check_path": "检查下载路径出错,请检查设置的下载目录是否正常",
|
"download_status_error_check_path": "检查下载路径出错,请检查设置的下载目录是否正常",
|
||||||
"download_status_error_check_path_exist": "存在同名文件,跳过下载",
|
"download_status_error_check_path_exist": "存在同名文件,跳过下载",
|
||||||
@@ -71,9 +71,9 @@
|
|||||||
"download_status_error_url_failed": "获取音乐链接失败",
|
"download_status_error_url_failed": "获取音乐链接失败",
|
||||||
"download_status_error_write": "歌曲保存位置被占用或没有写入权限,请尝试更改歌曲保存目录或重启软件或重启电脑,错误详情:",
|
"download_status_error_write": "歌曲保存位置被占用或没有写入权限,请尝试更改歌曲保存目录或重启软件或重启电脑,错误详情:",
|
||||||
"download_status_start": "开始下载",
|
"download_status_start": "开始下载",
|
||||||
"download_status_url_geting": "音乐链接获取中...",
|
"download_status_url_getting": "音乐链接获取中...",
|
||||||
"download_status_write_queue": "数据写入中({num})",
|
"download_status_write_queue": "数据写入中({num})",
|
||||||
"duplicate_list_tip": "你之前已收藏过该列表 [{name}],是否需要更新里面的歌曲?",
|
"duplicate_list_tip": "你之前已收藏过列表「{name}」,是否需要更新里面的歌曲?",
|
||||||
"export": "导出",
|
"export": "导出",
|
||||||
"fullscreen_exit": "退出全屏",
|
"fullscreen_exit": "退出全屏",
|
||||||
"history_clear": "清空搜索历史",
|
"history_clear": "清空搜索历史",
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
"list__file": "定位文件",
|
"list__file": "定位文件",
|
||||||
"list__import_part_button_cancel": "不要啊",
|
"list__import_part_button_cancel": "不要啊",
|
||||||
"list__import_part_button_confirm": "覆盖掉",
|
"list__import_part_button_confirm": "覆盖掉",
|
||||||
"list__import_part_confirm": "导入的列表({importName})与本地列表({localName})的ID相同,是否覆盖本地列表?",
|
"list__import_part_confirm": "导入的列表「{importName}」与本地列表「{localName}」的 ID 相同,是否覆盖本地列表?",
|
||||||
"list__import_part_desc": "选择列表文件",
|
"list__import_part_desc": "选择列表文件",
|
||||||
"list__load_failed": "啊,加载失败了😭",
|
"list__load_failed": "啊,加载失败了😭",
|
||||||
"list__loading": "列表加载中...⏳",
|
"list__loading": "列表加载中...⏳",
|
||||||
@@ -104,68 +104,70 @@
|
|||||||
"list__pause": "暂停任务",
|
"list__pause": "暂停任务",
|
||||||
"list__play": "播放",
|
"list__play": "播放",
|
||||||
"list__play_later": "稍后播放",
|
"list__play_later": "稍后播放",
|
||||||
"list__remove": "删除",
|
"list__remove": "移除",
|
||||||
"list__remove_tip": "你真的想要移除 {name} 吗?",
|
"list__remove_tip": "你真的想要移除「{name}」吗?",
|
||||||
"list__remove_tip_button": "是的 没错",
|
"list__remove_tip_button": "是的,没错",
|
||||||
"list__rename": "重命名",
|
"list__rename": "重命名",
|
||||||
"list__search": "搜索",
|
"list__search": "搜索",
|
||||||
"list__sort": "调整位置",
|
"list__sort": "调整位置",
|
||||||
"list__source_detail": "歌曲详情页",
|
"list__source_detail": "歌曲详情页",
|
||||||
"list__start": "开始任务",
|
"list__start": "开始任务",
|
||||||
"list__sync": "更新",
|
"list__sync": "更新",
|
||||||
"list_add__btn_title": "把该歌曲添加到 {name}",
|
"list__toggle_source": "歌曲换源",
|
||||||
"list_add__multiple_btn_title": "把这些歌曲添加到 {name}",
|
"list_add__btn_title": "把该歌曲添加到「{name}」",
|
||||||
|
"list_add__multiple_btn_title": "把这些歌曲添加到「{name}」",
|
||||||
"list_add__multiple_title_add": "添加已选的 {num} 首歌曲到...",
|
"list_add__multiple_title_add": "添加已选的 {num} 首歌曲到...",
|
||||||
"list_add__multiple_title_move": "移动已选的 {num} 首歌曲到...",
|
"list_add__multiple_title_move": "移动已选的 {num} 首歌曲到...",
|
||||||
"list_add__title_first_add": "添加",
|
"list_add__title_first_add": "添加",
|
||||||
"list_add__title_first_move": "移动",
|
"list_add__title_first_move": "移动",
|
||||||
"list_add__title_last": "到...",
|
"list_add__title_last": "到...",
|
||||||
"list_duplicate_tip": "已存在同名列表,是否仍要继续创建?",
|
"list_duplicate_tip": "已存在同名列表,是否仍要继续创建?",
|
||||||
"list_import_tip__alldata": "导入失败,这是一个所有数据备份文件,你需要去这里导入:\n设置 -> 备份与恢复 -> 所有数据 -> 导入",
|
"list_import_tip__alldata": "导入失败,这是一个「所有数据」备份文件,你需要去这里导入:\n\n设置 → 备份与恢复 → 所有数据 → 导入",
|
||||||
"list_import_tip__playlist": "导入失败,这是一个列表备份文件,你需要去这里导入:\n设置 -> 备份与恢复 -> 部分数据 -> 导入列表",
|
"list_import_tip__playlist": "导入失败,这是一个「列表」备份文件,你需要去这里导入:\n\n设置 → 备份与恢复 → 部分数据 → 导入列表",
|
||||||
"list_import_tip__playlist_part": "导入失败,这是一个单列表文件,你需要去这里导入:\n我的列表 -> 右击任意一个列表名 -> 在弹出的菜单中选择导入",
|
"list_import_tip__playlist_part": "导入失败,这是一个「单列表」文件,你需要去这里导入:\n\n我的列表 → 右击任意一个列表名 → 在弹出的菜单中选择「导入」",
|
||||||
"list_import_tip__setting": "导入失败,这是一个设置备份文件,你需要去这里导入:\n设置 -> 备份与恢复 -> 部分数据 -> 设置导入",
|
"list_import_tip__setting": "导入失败,这是一个「设置」备份文件,你需要去这里导入:\n\n设置 → 备份与恢复 → 部分数据 → 导入设置",
|
||||||
"list_import_tip__unknown": "导入失败,未知的文件类型,请尝试升级到最新版本后再试",
|
"list_import_tip__unknown": "导入失败,未知的文件类型,请尝试升级到最新版本后再试",
|
||||||
"list_sort_modal_by_album": "专辑名",
|
"list_sort_modal_by_album": "专辑名",
|
||||||
"list_sort_modal_by_down": "降序",
|
"list_sort_modal_by_down": "降序",
|
||||||
"list_sort_modal_by_field": "排序字段",
|
"list_sort_modal_by_field": "排序字段",
|
||||||
"list_sort_modal_by_name": "歌曲名",
|
"list_sort_modal_by_name": "歌曲名",
|
||||||
"list_sort_modal_by_random": "随机乱序",
|
"list_sort_modal_by_random": "随机乱序",
|
||||||
"list_sort_modal_by_singer": "歌手名",
|
"list_sort_modal_by_singer": "艺术家",
|
||||||
"list_sort_modal_by_source": "歌曲源",
|
"list_sort_modal_by_source": "歌曲来源平台",
|
||||||
"list_sort_modal_by_time": "时长",
|
"list_sort_modal_by_time": "时长",
|
||||||
"list_sort_modal_by_type": "排序类别",
|
"list_sort_modal_by_type": "排序类别",
|
||||||
"list_sort_modal_by_up": "升序",
|
"list_sort_modal_by_up": "升序",
|
||||||
"list_sort_modal_tip_confirm": "你确定要这么做吗?",
|
"list_sort_modal_tip_confirm": "你确定要这么做吗?",
|
||||||
"list_update_modal__auto_update": "自动更新",
|
"list_update_modal__auto_update": "自动更新",
|
||||||
"list_update_modal__tips": "💡 每次启动软件时将会自动更新已勾选“自动更新”的列表",
|
"list_update_modal__tips": "💡 每次启动软件时将会自动更新已勾选「自动更新」的列表",
|
||||||
"list_update_modal__title": "列表更新管理",
|
"list_update_modal__title": "列表更新管理",
|
||||||
"list_update_modal__update": "立即更新",
|
"list_update_modal__update": "立即更新",
|
||||||
"lists__add_local_file_desc": "选择歌曲文件",
|
"lists__add_local_file_desc": "选择歌曲文件",
|
||||||
"lists__dislike_music_tip": "你真的不喜欢 {name} 吗?",
|
"lists__dislike_music_singer_tip": "你真的不喜欢「{singer}」的「{name}」吗?",
|
||||||
|
"lists__dislike_music_tip": "你真的不喜欢「{name}」吗?",
|
||||||
"lists__duplicate": "重复歌曲",
|
"lists__duplicate": "重复歌曲",
|
||||||
"lists__export": "导出",
|
"lists__export": "导出",
|
||||||
"lists__export_part_desc": "选择列表文件保存位置",
|
"lists__export_part_desc": "选择列表文件保存位置",
|
||||||
"lists__import": "导入",
|
"lists__import": "导入",
|
||||||
"lists__import_part_button_cancel": "不要啊",
|
"lists__import_part_button_cancel": "不要啊",
|
||||||
"lists__import_part_button_confirm": "覆盖掉",
|
"lists__import_part_button_confirm": "覆盖掉",
|
||||||
"lists__import_part_confirm": "导入的列表({importName})与本地列表({localName})的ID相同,是否覆盖本地列表?",
|
"lists__import_part_confirm": "导入的列表「{importName}」与本地列表「{localName}」的 ID 相同,是否覆盖本地列表?",
|
||||||
"lists__import_part_desc": "选择列表文件",
|
"lists__import_part_desc": "选择列表文件",
|
||||||
"lists__new_list_btn": "新建列表",
|
"lists__new_list_btn": "新建列表",
|
||||||
"lists__new_list_input": "新列表...",
|
"lists__new_list_input": "新列表...",
|
||||||
"lists__remove": "删除",
|
"lists__remove": "移除",
|
||||||
"lists__remove_music_tip": "你真的要移除所选的 {len} 首歌曲吗?",
|
"lists__remove_music_tip": "你真的要移除所选的 {len} 首歌曲吗?",
|
||||||
"lists__remove_tip": "你真的想要移除 {name} 吗?",
|
"lists__remove_tip": "你真的想要移除「{name}」吗?",
|
||||||
"lists__remove_tip_button": "是的 没错",
|
"lists__remove_tip_button": "是的,没错",
|
||||||
"lists__rename": "重命名",
|
"lists__rename": "重命名",
|
||||||
"lists__select_local_file": "添加本地歌曲",
|
"lists__select_local_file": "添加本地歌曲",
|
||||||
"lists__sort_list": "排序歌曲",
|
"lists__sort_list": "排序歌曲",
|
||||||
"lists__source_detail": "歌单详情页",
|
"lists__source_detail": "歌单详情页",
|
||||||
"lists__sync": "更新",
|
"lists__sync": "更新",
|
||||||
"lists__sync_confirm_tip": "这将会把 {name} 内的歌曲替换成在线列表的歌曲,你确认要更新吗?",
|
"lists__sync_confirm_tip": "这将会把「{name}」内的歌曲替换成在线列表的歌曲,你确认要更新吗?",
|
||||||
"load_list_file_error_detail": "我们已经帮你把旧的列表文件备份到{path}\n它以 JSON 格式存储,你可以尝试手动修复并恢复它\n\n错误详情:{detail}",
|
"load_list_file_error_detail": "我们已经帮你把旧的列表文件备份到:{path}\n\n它以 JSON 格式存储,你可以尝试手动修复并恢复它。\n\n错误详情:{detail}",
|
||||||
"load_list_file_error_title": "播放列表数据加载错误(建议到GitHub或加群反馈)",
|
"load_list_file_error_title": "播放列表数据加载错误(建议到 GitHub 反馈)",
|
||||||
"loding": "加载中...",
|
"loading": "加载中...",
|
||||||
"love_list": "收藏",
|
"love_list": "收藏",
|
||||||
"lyric__load_error": "歌词获取失败",
|
"lyric__load_error": "歌词获取失败",
|
||||||
"lyric__select": "歌词文本选择",
|
"lyric__select": "歌词文本选择",
|
||||||
@@ -174,23 +176,25 @@
|
|||||||
"lyric_menu__align_left": "居左",
|
"lyric_menu__align_left": "居左",
|
||||||
"lyric_menu__lrc_size": "字体大小 [ {size} ]",
|
"lyric_menu__lrc_size": "字体大小 [ {size} ]",
|
||||||
"lyric_menu__offset": "歌词偏移 [ {offset}ms ]",
|
"lyric_menu__offset": "歌词偏移 [ {offset}ms ]",
|
||||||
"lyric_menu__offset_add_10": "加快10毫秒",
|
"lyric_menu__offset_add_10": "加快 10 毫秒",
|
||||||
"lyric_menu__offset_add_100": "加快100毫秒",
|
"lyric_menu__offset_add_100": "加快 100 毫秒",
|
||||||
"lyric_menu__offset_dec_10": "减慢10毫秒",
|
"lyric_menu__offset_dec_10": "减慢 10 毫秒",
|
||||||
"lyric_menu__offset_dec_100": "减慢100毫秒",
|
"lyric_menu__offset_dec_100": "减慢 100 毫秒",
|
||||||
"lyric_menu__offset_reset": "重置",
|
"lyric_menu__offset_reset": "重置",
|
||||||
"lyric_menu__size_add": "加大字体(右击可微调)",
|
"lyric_menu__size_add": "加大字体(右击可微调)",
|
||||||
"lyric_menu__size_dec": "减小字体(右击可微调)",
|
"lyric_menu__size_dec": "减小字体(右击可微调)",
|
||||||
"lyric_menu__size_reset": "重置",
|
"lyric_menu__size_reset": "重置",
|
||||||
|
"media_device__empty_device_tip": "音频输出设备为空,若出现无法播放的情况,请检查声卡驱动是否已安装或重装声卡驱动!",
|
||||||
"min": "最小化",
|
"min": "最小化",
|
||||||
"music_album": "专辑名",
|
"music_album": "专辑名",
|
||||||
"music_duplicate": "重复歌曲",
|
"music_duplicate": "重复歌曲",
|
||||||
"music_name": "歌曲名",
|
"music_name": "歌曲名",
|
||||||
"music_singer": "艺术家",
|
"music_singer": "艺术家",
|
||||||
"music_sort__input_tip": "请输入要调整到第几个位置",
|
"music_sort__input_tip": "请输入要调整到第几个位置",
|
||||||
"music_sort__title": "将 {name} 的位置调整到:",
|
"music_sort__title": "将「{name}」的位置调整到:",
|
||||||
"music_sort__title_multiple": "将已选的 {num} 首歌曲的位置调整到:",
|
"music_sort__title_multiple": "将已选的 {num} 首歌曲的位置调整到:",
|
||||||
"music_time": "时长",
|
"music_time": "时长",
|
||||||
|
"music_toggle_clean": "取消换源",
|
||||||
"my_list": "我的列表",
|
"my_list": "我的列表",
|
||||||
"no_item": "列表竟然是空的...",
|
"no_item": "列表竟然是空的...",
|
||||||
"not_agree": "不接受",
|
"not_agree": "不接受",
|
||||||
@@ -214,8 +218,8 @@
|
|||||||
"player__desktop_lyric_unlock": "右击解锁歌词",
|
"player__desktop_lyric_unlock": "右击解锁歌词",
|
||||||
"player__end": "播放完毕",
|
"player__end": "播放完毕",
|
||||||
"player__error": "音频加载出错,5 秒后切换下一首",
|
"player__error": "音频加载出错,5 秒后切换下一首",
|
||||||
"player__geting_url": "歌曲链接获取中...",
|
"player__getting_url": "歌曲链接获取中...",
|
||||||
"player__geting_url_delay_retry": "服务繁忙,{time}秒后重试...",
|
"player__getting_url_delay_retry": "服务繁忙,{time} 秒后重试...",
|
||||||
"player__hide_detail_tip": "隐藏详情页(界面内右键双击可快速隐藏详情页)",
|
"player__hide_detail_tip": "隐藏详情页(界面内右键双击可快速隐藏详情页)",
|
||||||
"player__loading": "音乐加载中...",
|
"player__loading": "音乐加载中...",
|
||||||
"player__music_album": "专辑名:",
|
"player__music_album": "专辑名:",
|
||||||
@@ -223,19 +227,19 @@
|
|||||||
"player__music_singer": "艺术家:",
|
"player__music_singer": "艺术家:",
|
||||||
"player__next": "下一首",
|
"player__next": "下一首",
|
||||||
"player__pause": "暂停",
|
"player__pause": "暂停",
|
||||||
"player__pic_tip": "播放详情页(右击在“我的列表”定位当前播放的歌曲)",
|
"player__pic_tip": "播放详情页(右击在「我的列表」定位当前播放的歌曲)",
|
||||||
"player__play": "播放",
|
"player__play": "播放",
|
||||||
"player__play_toggle_mode_list": "顺序播放",
|
"player__play_toggle_mode_list": "顺序播放",
|
||||||
"player__play_toggle_mode_list_loop": "列表循环",
|
"player__play_toggle_mode_list_loop": "列表循环播放",
|
||||||
"player__play_toggle_mode_off": "禁用",
|
"player__play_toggle_mode_off": "禁用歌曲切换",
|
||||||
"player__play_toggle_mode_random": "列表随机",
|
"player__play_toggle_mode_random": "列表随机播放",
|
||||||
"player__play_toggle_mode_single_loop": "单曲循环",
|
"player__play_toggle_mode_single_loop": "单曲循环播放",
|
||||||
"player__playback_preserves_pitch": "音调补偿",
|
"player__playback_preserves_pitch": "音调补偿",
|
||||||
"player__playback_rate": "当前播放速率:",
|
"player__playback_rate": "当前播放速率:",
|
||||||
"player__playback_rate_reset_btn": "重置",
|
"player__playback_rate_reset_btn": "重置",
|
||||||
"player__playing": "播放中...",
|
"player__playing": "播放中...",
|
||||||
"player__prev": "上一首",
|
"player__prev": "上一首",
|
||||||
"player__refresh_url": "URL过期,正在刷新URL...",
|
"player__refresh_url": "URL 过期,正在刷新 URL...",
|
||||||
"player__sound_effect": "音效设置(实验性)",
|
"player__sound_effect": "音效设置(实验性)",
|
||||||
"player__sound_effect_biquad_filter": "均衡器",
|
"player__sound_effect_biquad_filter": "均衡器",
|
||||||
"player__sound_effect_biquad_filter_preset_classical": "古典",
|
"player__sound_effect_biquad_filter_preset_classical": "古典",
|
||||||
@@ -257,8 +261,8 @@
|
|||||||
"player__sound_effect_convolution_file_dining_living_true_stereo": "餐厅",
|
"player__sound_effect_convolution_file_dining_living_true_stereo": "餐厅",
|
||||||
"player__sound_effect_convolution_file_feedback_spring": "反馈弹簧",
|
"player__sound_effect_convolution_file_feedback_spring": "反馈弹簧",
|
||||||
"player__sound_effect_convolution_file_living_bedroom_leveled": "卫生间",
|
"player__sound_effect_convolution_file_living_bedroom_leveled": "卫生间",
|
||||||
"player__sound_effect_convolution_file_matrix_1": "矩阵混响",
|
"player__sound_effect_convolution_file_matrix_1": "矩阵混响(1)",
|
||||||
"player__sound_effect_convolution_file_matrix_2": "矩阵混响2",
|
"player__sound_effect_convolution_file_matrix_2": "矩阵混响(2)",
|
||||||
"player__sound_effect_convolution_file_s2_r4_bd": "教堂",
|
"player__sound_effect_convolution_file_s2_r4_bd": "教堂",
|
||||||
"player__sound_effect_convolution_file_s3_r1_bd": "立体声",
|
"player__sound_effect_convolution_file_s3_r1_bd": "立体声",
|
||||||
"player__sound_effect_convolution_file_spreader50_65ms": "室内",
|
"player__sound_effect_convolution_file_spreader50_65ms": "室内",
|
||||||
@@ -266,14 +270,15 @@
|
|||||||
"player__sound_effect_convolution_file_tim_omni_35_10_magnetic": "磁性立体声",
|
"player__sound_effect_convolution_file_tim_omni_35_10_magnetic": "磁性立体声",
|
||||||
"player__sound_effect_convolution_main_gain": "原始音频增益",
|
"player__sound_effect_convolution_main_gain": "原始音频增益",
|
||||||
"player__sound_effect_convolution_send_gain": "环境音效增益",
|
"player__sound_effect_convolution_send_gain": "环境音效增益",
|
||||||
"player__sound_effect_panner": "3D立体环绕(需使用耳机)",
|
"player__sound_effect_features_tip": "提示:「音效设置」与「自定义音频输出设备」冲突,启用音效设置后音频输出设备将会被重置为默认的输出设备,目前此问题暂无法解决。",
|
||||||
|
"player__sound_effect_panner": "3D 立体环绕(需使用耳机)",
|
||||||
"player__sound_effect_panner_enabled": "启用",
|
"player__sound_effect_panner_enabled": "启用",
|
||||||
"player__sound_effect_panner_sound_r": "声音距离",
|
"player__sound_effect_panner_sound_r": "声音距离",
|
||||||
"player__sound_effect_panner_sound_speed": "环绕速度",
|
"player__sound_effect_panner_sound_speed": "环绕速度",
|
||||||
"player__sound_effect_pitch_shifter": "音调升降调节",
|
"player__sound_effect_pitch_shifter": "音调升降调节",
|
||||||
"player__sound_effect_pitch_shifter_preset_semitones": "{num} 半音",
|
"player__sound_effect_pitch_shifter_preset_semitones": "{num} 半音",
|
||||||
"player__sound_effect_pitch_shifter_reset_btn": "重置",
|
"player__sound_effect_pitch_shifter_reset_btn": "重置",
|
||||||
"player__sound_effect_pitch_shifter_tip": "由于升降调需要实时处理音频数据,这会导致额外的CPU占用\n\n已知问题:\n如果CPU资源不够时将处理导致任务堆积而出现声音异常,这时需要暂停播放一段时间等堆积的任务处理完毕再播放",
|
"player__sound_effect_pitch_shifter_tip": "由于升降调需要实时处理音频数据,这会导致额外的 CPU 占用。\n\n已知问题:\n如果 CPU 资源不够将导致处理任务堆积而出现声音异常。\n这时需要暂停播放一段时间等堆积的任务处理完毕再播放。",
|
||||||
"player__stop": "暂停播放",
|
"player__stop": "暂停播放",
|
||||||
"player__volume": "当前音量:",
|
"player__volume": "当前音量:",
|
||||||
"player__volume_mute_label": "静音",
|
"player__volume_mute_label": "静音",
|
||||||
@@ -284,7 +289,7 @@
|
|||||||
"search__type_songlist": "歌单",
|
"search__type_songlist": "歌单",
|
||||||
"search__welcome": "搜我所想~~😉",
|
"search__welcome": "搜我所想~~😉",
|
||||||
"setting": "设置",
|
"setting": "设置",
|
||||||
"setting__about": "关于洛雪音乐",
|
"setting__about": "关于 LX Music",
|
||||||
"setting__backup": "备份与恢复",
|
"setting__backup": "备份与恢复",
|
||||||
"setting__backup_all": "所有数据(列表数据与设置数据)",
|
"setting__backup_all": "所有数据(列表数据与设置数据)",
|
||||||
"setting__backup_all_export": "导出",
|
"setting__backup_all_export": "导出",
|
||||||
@@ -293,16 +298,16 @@
|
|||||||
"setting__backup_all_import_desc": "选择备份文件",
|
"setting__backup_all_import_desc": "选择备份文件",
|
||||||
"setting__backup_other": "其他备份格式(目前不支持恢复此类备份文件)",
|
"setting__backup_other": "其他备份格式(目前不支持恢复此类备份文件)",
|
||||||
"setting__backup_other_export_dir": "选择文件保存位置",
|
"setting__backup_other_export_dir": "选择文件保存位置",
|
||||||
"setting__backup_other_export_list_csv": "导出 CSV 格式的列表",
|
"setting__backup_other_export_list_csv": "导出 CSV 格式列表",
|
||||||
"setting__backup_other_export_list_text": "导出 TXT 格式的列表",
|
"setting__backup_other_export_list_text": "导出 TXT 格式列表",
|
||||||
"setting__backup_other_export_list_text_confirm": "是否将所有列表合并为一个文件?",
|
"setting__backup_other_export_list_text_confirm": "是否将所有列表合并为一个文件?",
|
||||||
"setting__backup_part": "部分数据(列表数据包括试听列表、收藏列表、用户自定义列表,设置数据不包括快捷键设置)",
|
"setting__backup_part": "部分数据(列表数据包括「试听列表」、「我的收藏」和用户自行创建的列表;设置数据不包括快捷键设置)",
|
||||||
"setting__backup_part_export_list": "导出列表",
|
"setting__backup_part_export_list": "导出列表",
|
||||||
"setting__backup_part_export_list_desc": "选择歌单保存位置",
|
"setting__backup_part_export_list_desc": "选择歌单保存位置",
|
||||||
"setting__backup_part_export_setting": "导出设置",
|
"setting__backup_part_export_setting": "导出设置",
|
||||||
"setting__backup_part_export_setting_desc": "选择设置保存位置",
|
"setting__backup_part_export_setting_desc": "选择设置保存位置",
|
||||||
"setting__backup_part_import_list": "导入列表",
|
"setting__backup_part_import_list": "导入列表",
|
||||||
"setting__backup_part_import_list_confirm": "备份文件中列表与现有列表ID相同时,现有列表内歌曲将会被覆盖,是否要继续?",
|
"setting__backup_part_import_list_confirm": "备份文件中列表与现有列表 ID 相同时,现有列表内歌曲将会被覆盖,是否要继续?",
|
||||||
"setting__backup_part_import_list_desc": "选择列表文件",
|
"setting__backup_part_import_list_desc": "选择列表文件",
|
||||||
"setting__backup_part_import_setting": "导入设置",
|
"setting__backup_part_import_setting": "导入设置",
|
||||||
"setting__backup_part_import_setting_desc": "选择配置文件",
|
"setting__backup_part_import_setting_desc": "选择配置文件",
|
||||||
@@ -325,22 +330,22 @@
|
|||||||
"setting__basic_playbar_progress_style_full": "全宽",
|
"setting__basic_playbar_progress_style_full": "全宽",
|
||||||
"setting__basic_playbar_progress_style_middle": "中等",
|
"setting__basic_playbar_progress_style_middle": "中等",
|
||||||
"setting__basic_playbar_progress_style_mini": "迷你",
|
"setting__basic_playbar_progress_style_mini": "迷你",
|
||||||
"setting__basic_show_animation": "显示切换动画",
|
"setting__basic_show_animation": "显示动画效果",
|
||||||
"setting__basic_source": "音乐来源",
|
"setting__basic_source": "自定义源",
|
||||||
"setting__basic_source_status_failed": "初始化失败",
|
"setting__basic_source_status_failed": "初始化失败",
|
||||||
"setting__basic_source_status_initing": "初始化中",
|
"setting__basic_source_status_initing": "初始化中...",
|
||||||
"setting__basic_source_status_success": "初始化成功",
|
"setting__basic_source_status_success": "初始化成功",
|
||||||
"setting__basic_source_temp": "临时接口(软件的某些功能不可用,建议测试接口不可用再使用本接口)",
|
"setting__basic_source_temp": "临时接口(软件的某些功能不可用,建议测试接口不可用再使用本接口)",
|
||||||
"setting__basic_source_test": "测试接口(几乎软件的所有功能都可用)",
|
"setting__basic_source_test": "测试接口(几乎软件的所有功能都可用)",
|
||||||
"setting__basic_source_user_api_btn": "自定义源管理",
|
"setting__basic_source_user_api_btn": "自定义源管理",
|
||||||
"setting__basic_sourcename": "音源名字",
|
"setting__basic_sourcename": "歌曲来源名称",
|
||||||
"setting__basic_sourcename_alias": "别名",
|
"setting__basic_sourcename_alias": "别名",
|
||||||
"setting__basic_sourcename_real": "原名",
|
"setting__basic_sourcename_real": "原名",
|
||||||
"setting__basic_sourcename_title": "选择音源名字类型",
|
"setting__basic_sourcename_title": "选择歌曲来源名称类型",
|
||||||
"setting__basic_start_in_fullscreen": "以全屏模式启动",
|
"setting__basic_start_in_fullscreen": "以全屏模式启动",
|
||||||
"setting__basic_theme": "主题颜色",
|
"setting__basic_theme": "主题颜色",
|
||||||
"setting__basic_theme_auto_tip": "此乃动态主题,你可以预先设置一个亮色主题及暗色主题,此后将根据系统的亮、暗主题色自动切换为你预先设置的相应主题。\n注:鼠标 右击 此主题项即可打开亮、暗色主题设置窗口。",
|
"setting__basic_theme_auto_tip": "此乃动态主题,你可以预先设置一个亮色主题及暗色主题,此后将根据系统的亮、暗主题色自动切换为你预先设置的相应主题。\n\n注:鼠标右击此主题项即可打开亮、暗色主题设置窗口。",
|
||||||
"setting__basic_to_tray": "关闭软件时不退出软件将其最小化到系统托盘",
|
"setting__basic_to_tray": "关闭窗口时不退出软件将其最小化到系统托盘",
|
||||||
"setting__basic_window_size": "窗口尺寸",
|
"setting__basic_window_size": "窗口尺寸",
|
||||||
"setting__basic_window_size_big": "大",
|
"setting__basic_window_size_big": "大",
|
||||||
"setting__basic_window_size_huge": "巨大",
|
"setting__basic_window_size_huge": "巨大",
|
||||||
@@ -358,7 +363,8 @@
|
|||||||
"setting__desktop_lyric_align_left": "居左",
|
"setting__desktop_lyric_align_left": "居左",
|
||||||
"setting__desktop_lyric_align_right": "居右",
|
"setting__desktop_lyric_align_right": "居右",
|
||||||
"setting__desktop_lyric_always_on_top": "使歌词总是在其他窗口之上",
|
"setting__desktop_lyric_always_on_top": "使歌词总是在其他窗口之上",
|
||||||
"setting__desktop_lyric_always_on_top_loop": "自动刷新歌词置顶(当歌词置顶后仍被某些程序遮挡时可尝试启用此设置)",
|
"setting__desktop_lyric_always_on_top_loop": "自动刷新歌词置顶",
|
||||||
|
"setting__desktop_lyric_always_on_top_loop_tip": "当歌词置顶后仍被某些程序遮挡时可尝试启用此选项",
|
||||||
"setting__desktop_lyric_audio_visualization": "音频可视化(实验性)",
|
"setting__desktop_lyric_audio_visualization": "音频可视化(实验性)",
|
||||||
"setting__desktop_lyric_color": "歌词字体颜色",
|
"setting__desktop_lyric_color": "歌词字体颜色",
|
||||||
"setting__desktop_lyric_color_reset": "重置颜色",
|
"setting__desktop_lyric_color_reset": "重置颜色",
|
||||||
@@ -372,7 +378,8 @@
|
|||||||
"setting__desktop_lyric_font_default": "默认",
|
"setting__desktop_lyric_font_default": "默认",
|
||||||
"setting__desktop_lyric_font_weight": "加粗字体",
|
"setting__desktop_lyric_font_weight": "加粗字体",
|
||||||
"setting__desktop_lyric_fullscreen_hide": "全屏时自动关闭歌词",
|
"setting__desktop_lyric_fullscreen_hide": "全屏时自动关闭歌词",
|
||||||
"setting__desktop_lyric_hover_hide": "鼠标移入歌词区域时降低歌词透明度(此功能存在平台兼容性问题)",
|
"setting__desktop_lyric_hover_hide": "鼠标移入歌词区域时降低歌词透明度",
|
||||||
|
"setting__desktop_lyric_hover_hide_tip": "此功能存在平台兼容性问题,在某些平台上可能会出现预料外的问题",
|
||||||
"setting__desktop_lyric_line_gap": "歌词间距({num})",
|
"setting__desktop_lyric_line_gap": "歌词间距({num})",
|
||||||
"setting__desktop_lyric_line_gap_add": "加大间距",
|
"setting__desktop_lyric_line_gap_add": "加大间距",
|
||||||
"setting__desktop_lyric_line_gap_dec": "减小间距",
|
"setting__desktop_lyric_line_gap_dec": "减小间距",
|
||||||
@@ -385,28 +392,33 @@
|
|||||||
"setting__desktop_lyric_scroll_align_center": "中心",
|
"setting__desktop_lyric_scroll_align_center": "中心",
|
||||||
"setting__desktop_lyric_scroll_align_top": "顶部",
|
"setting__desktop_lyric_scroll_align_top": "顶部",
|
||||||
"setting__desktop_lyric_shadow_color": "阴影颜色",
|
"setting__desktop_lyric_shadow_color": "阴影颜色",
|
||||||
"setting__desktop_lyric_show_taskbar": "在任务栏显示歌词进程(此设置用于在录屏软件无法捕获歌词窗口时的变通解决方法)",
|
"setting__desktop_lyric_show_taskbar": "在任务栏显示歌词进程",
|
||||||
|
"setting__desktop_lyric_show_taskbar_tip": "此选项作为在录屏软件无法捕获歌词窗口时的变通解决方法",
|
||||||
"setting__desktop_lyric_unplay_color": "未播放颜色",
|
"setting__desktop_lyric_unplay_color": "未播放颜色",
|
||||||
"setting__dislike_list_input_tip": "歌曲名@歌手名\n歌曲名\n@歌手名",
|
"setting__dislike_list_input_tip": "歌曲名@艺术家\n歌曲名\n@艺术家",
|
||||||
"setting__dislike_list_save_btn": "保存",
|
"setting__dislike_list_save_btn": "保存",
|
||||||
"setting__dislike_list_tips": "1. 每条一行,若歌曲或者歌手名字中存在“@”符号,需要将其替换成“#”\n2. 指定某歌手的某首歌:歌曲名@歌手名\n3. 指定某首歌:歌曲名\n4. 指定某歌手:@歌手名",
|
"setting__dislike_list_tips": "1. 每条一行,若歌曲或者艺术家名字中存在「@」符号,需要将其替换成「#」\n2. 指定某艺术家的某首歌:歌曲名@艺术家\n3. 指定某首歌:歌曲名\n4. 指定某艺术家:@艺术家",
|
||||||
"setting__dislike_list_title": "不喜欢的歌曲规则列表",
|
"setting__dislike_list_title": "「不喜欢的歌曲」规则列表",
|
||||||
"setting__download": "下载设置",
|
"setting__download": "下载设置",
|
||||||
"setting__download_data_embed": "是否将以下内容嵌入到音频文件中",
|
"setting__download_data_embed": "嵌入到音频文件中的内容",
|
||||||
"setting__download_embed_lyric": "歌词嵌入",
|
"setting__download_embed_lyric": "嵌入歌词",
|
||||||
"setting__download_embed_pic": "封面嵌入",
|
"setting__download_embed_pic": "嵌入封面",
|
||||||
"setting__download_embed_rlyric": "同时嵌入罗马音歌词(如果有)",
|
"setting__download_embed_rlyric": "同时嵌入罗马音歌词(如果有)",
|
||||||
"setting__download_embed_tlyric": "同时嵌入翻译歌词(如果有)",
|
"setting__download_embed_tlyric": "同时嵌入翻译歌词(如果有)",
|
||||||
"setting__download_enable": "是否启用下载功能",
|
"setting__download_enable": "启用下载功能",
|
||||||
"setting__download_lyric": "歌词下载",
|
"setting__download_lyric": "歌词下载",
|
||||||
"setting__download_lyric_format": "下载的歌词文件编码格式",
|
"setting__download_lyric_format": "下载的歌词文件编码格式",
|
||||||
"setting__download_lyric_format_gbk": "GBK(在某些设备上出现中文乱码时可尝试选择此格式)",
|
"setting__download_lyric_format_tip": "在某些设备上出现中文乱码时可尝试选择 GBK 格式",
|
||||||
|
"setting__download_lyric_format_gbk": "GBK",
|
||||||
"setting__download_lyric_format_utf8": "UTF-8",
|
"setting__download_lyric_format_utf8": "UTF-8",
|
||||||
"setting__download_lyric_title": "是否同时下载歌词文件",
|
"setting__download_lyric_title": "同时下载歌词文件",
|
||||||
|
"setting__download_max_num": "同时下载任务数",
|
||||||
|
"setting__download_max_num_tip": "过大的同时下载数量可能会导致你的 IP 被自定义源封禁,是否确认修改?",
|
||||||
|
"setting__download_max_num_tooltip": "设置过大可能会导致 IP 被封,这取决于自定义源",
|
||||||
"setting__download_name": "文件命名方式",
|
"setting__download_name": "文件命名方式",
|
||||||
"setting__download_name1": "歌名 - 歌手",
|
"setting__download_name1": "歌曲名 - 艺术家",
|
||||||
"setting__download_name2": "歌手 - 歌名",
|
"setting__download_name2": "艺术家 - 歌曲名",
|
||||||
"setting__download_name3": "歌名",
|
"setting__download_name3": "歌曲名",
|
||||||
"setting__download_name_title": "下载歌曲时的命名方式",
|
"setting__download_name_title": "下载歌曲时的命名方式",
|
||||||
"setting__download_path": "下载路径",
|
"setting__download_path": "下载路径",
|
||||||
"setting__download_path_change_btn": "更改",
|
"setting__download_path_change_btn": "更改",
|
||||||
@@ -415,10 +427,10 @@
|
|||||||
"setting__download_path_title": "下载歌曲保存的路径",
|
"setting__download_path_title": "下载歌曲保存的路径",
|
||||||
"setting__download_rlyric": "同时将罗马音歌词写入歌词文件中(如果有)",
|
"setting__download_rlyric": "同时将罗马音歌词写入歌词文件中(如果有)",
|
||||||
"setting__download_select_save_path": "选择歌曲保存路径",
|
"setting__download_select_save_path": "选择歌曲保存路径",
|
||||||
"setting__download_skip_exist_file": "下载目录存在同名的文件时跳过下载此任务",
|
"setting__download_skip_exist_file": "下载目录存在同名文件时跳过下载此任务",
|
||||||
"setting__download_tlyric": "同时将翻译歌词写入歌词文件中(如果有)",
|
"setting__download_tlyric": "同时将翻译歌词写入歌词文件中(如果有)",
|
||||||
"setting__download_use_other_source": "自动换源下载",
|
"setting__download_use_other_source": "自动换源下载",
|
||||||
"setting__download_use_other_source_tip": "当无法从歌曲的原始源下载时,尝试切换到其他源下载,注:此功能不100%保证换源后的歌曲版本与原版一致",
|
"setting__download_use_other_source_tip": "当无法从歌曲的原始来源下载时,尝试切换到其他来源下载。\n注:此功能不 100% 保证换源后的歌曲版本与原版一致。",
|
||||||
"setting__hot_key": "快捷键设置",
|
"setting__hot_key": "快捷键设置",
|
||||||
"setting__hot_key_common_focus_search_input": "聚焦搜索框",
|
"setting__hot_key_common_focus_search_input": "聚焦搜索框",
|
||||||
"setting__hot_key_common_min": "最小化程序",
|
"setting__hot_key_common_min": "最小化程序",
|
||||||
@@ -441,40 +453,48 @@
|
|||||||
"setting__hot_key_player_volume_up": "增加音量",
|
"setting__hot_key_player_volume_up": "增加音量",
|
||||||
"setting__hot_key_tip_input": "请输入新的按键",
|
"setting__hot_key_tip_input": "请输入新的按键",
|
||||||
"setting__hot_key_unset_input": "未设置",
|
"setting__hot_key_unset_input": "未设置",
|
||||||
"setting__is_enable": "是否启用",
|
"setting__is_enable": "启用",
|
||||||
"setting__is_show": "是否显示",
|
"setting__is_show": "显示",
|
||||||
"setting__list": "列表设置",
|
"setting__list": "列表设置",
|
||||||
"setting__list_action_btn": "显示列表操作按钮",
|
"setting__list_action_btn": "显示列表操作按钮",
|
||||||
"setting__list_add_music_location_type": "添加歌曲到列表时的位置",
|
"setting__list_add_music_location_type": "添加歌曲到列表时的位置",
|
||||||
"setting__list_add_music_location_type_bottom": "底部",
|
"setting__list_add_music_location_type_bottom": "底部",
|
||||||
"setting__list_add_music_location_type_top": "顶部",
|
"setting__list_add_music_location_type_top": "顶部",
|
||||||
"setting__list_click_action": "双击列表里的歌曲时自动切换到当前列表播放(仅对歌单、排行榜有效)",
|
"setting__list_click_action": "双击列表里的歌曲时自动切换到当前列表播放(仅对「歌单」和「排行榜」有效)",
|
||||||
"setting__list_scroll": "记住播放列表滚动条位置(仅对我的音乐分类有效)",
|
"setting__list_scroll": "记住播放列表滚动条位置(仅对「我的列表」有效)",
|
||||||
"setting__list_source": "显示歌曲源(仅对我的音乐分类有效)",
|
"setting__list_source": "显示歌曲来源平台(仅对「我的列表」有效)",
|
||||||
"setting__network": "网络设置",
|
"setting__network": "网络设置",
|
||||||
"setting__network_proxy_host": "主机",
|
"setting__network_proxy_host": "主机",
|
||||||
"setting__network_proxy_password": "密码",
|
"setting__network_proxy_password": "密码",
|
||||||
"setting__network_proxy_port": "端口",
|
"setting__network_proxy_port": "端口",
|
||||||
"setting__network_proxy_title": "HTTP代理设置(乱设置软件将无法联网)",
|
"setting__network_proxy_title": "HTTP 代理设置(乱设置软件将无法联网)",
|
||||||
"setting__network_proxy_username": "用户名",
|
"setting__network_proxy_username": "用户名",
|
||||||
"setting__odc": "强迫症设置",
|
"setting__odc": "强迫症设置",
|
||||||
"setting__odc_clear_search_input": "离开搜索界面时清空搜索框",
|
"setting__odc_clear_search_input": "离开搜索界面时清空搜索框",
|
||||||
"setting__odc_clear_search_list": "离开搜索界面时清空搜索列表",
|
"setting__odc_clear_search_list": "离开搜索界面时清空搜索列表",
|
||||||
|
"setting__open_api": "开放 API",
|
||||||
|
"setting__open_api_address": "服务地址:",
|
||||||
|
"setting__open_api_bind_lan": "允许来自局域网的访问",
|
||||||
|
"setting__open_api_enable": "启用开放 API 服务",
|
||||||
|
"setting__open_api_port": "服务端口",
|
||||||
|
"setting__open_api_port_tip": "请输入开放 API 服务端口",
|
||||||
|
"setting__open_api_tip": "该功能用于为第三方软件提供调用 LX Music 的能力,目前可用的功能可以看:",
|
||||||
|
"setting__open_api_tip_link": "接入文档",
|
||||||
"setting__other": "其他",
|
"setting__other": "其他",
|
||||||
"setting__other_dislike_list": "不喜欢的歌曲规则",
|
"setting__other_dislike_list": "「不喜欢的歌曲」规则",
|
||||||
"setting__other_dislike_list_label": "规则数量:",
|
"setting__other_dislike_list_label": "规则数量:",
|
||||||
"setting__other_dislike_list_show_btn": "编辑不喜欢歌曲规则",
|
"setting__other_dislike_list_show_btn": "编辑规则",
|
||||||
"setting__other_listdata": "列表数据清理",
|
"setting__other_listdata": "列表数据清理",
|
||||||
"setting__other_listdata_clear_btn": "清空我的列表数据",
|
"setting__other_listdata_clear_btn": "清空「我的列表」数据",
|
||||||
"setting__other_listdata_clear_tip_confirm": "这将清理你创建的 所有列表 及收藏的 所有歌曲,是否真的要继续?",
|
"setting__other_listdata_clear_tip_confirm": "这将清理你创建的「所有列表」及收藏的「所有歌曲」,是否真的要继续?",
|
||||||
"setting__other_lyric_edited_cache": "已调整过偏移时间的歌词管理",
|
"setting__other_lyric_edited_cache": "已调整过偏移时间的歌词管理",
|
||||||
"setting__other_lyric_edited_clear_btn": "清理已调整过时间的歌词",
|
"setting__other_lyric_edited_clear_btn": "清理已调整过时间的歌词",
|
||||||
"setting__other_lyric_edited_clear_tip_confirm": "这将清理所有你之前已调整过偏移时间的歌词,是否确认清理?(手抖确认🤪)",
|
"setting__other_lyric_edited_clear_tip_confirm": "这将清理所有你之前已调整过偏移时间的歌词,是否确认清理?(手抖确认🤪)",
|
||||||
"setting__other_lyric_edited_label": "歌词数量:",
|
"setting__other_lyric_edited_label": "歌词数量:",
|
||||||
"setting__other_lyric_raw_clear_btn": "清理歌词缓存",
|
"setting__other_lyric_raw_clear_btn": "清理歌词缓存",
|
||||||
"setting__other_lyric_raw_label": "歌词数量:",
|
"setting__other_lyric_raw_label": "歌词数量:",
|
||||||
"setting__other_music_url_clear_btn": "清理歌曲URL缓存",
|
"setting__other_music_url_clear_btn": "清理歌曲 URL 缓存",
|
||||||
"setting__other_music_url_label": "歌曲URL数量:",
|
"setting__other_music_url_label": "歌曲 URL 数量:",
|
||||||
"setting__other_other_cache": "其他缓存管理",
|
"setting__other_other_cache": "其他缓存管理",
|
||||||
"setting__other_other_source_clear_btn": "清理换源歌曲缓存",
|
"setting__other_other_source_clear_btn": "清理换源歌曲缓存",
|
||||||
"setting__other_other_source_label": "换源歌曲信息数量:",
|
"setting__other_other_source_label": "换源歌曲信息数量:",
|
||||||
@@ -482,55 +502,62 @@
|
|||||||
"setting__other_resource_cache_clear_btn": "清理资源缓存",
|
"setting__other_resource_cache_clear_btn": "清理资源缓存",
|
||||||
"setting__other_resource_cache_confirm": "我要清掉",
|
"setting__other_resource_cache_confirm": "我要清掉",
|
||||||
"setting__other_resource_cache_label": "软件已使用缓存大小:",
|
"setting__other_resource_cache_label": "软件已使用缓存大小:",
|
||||||
"setting__other_resource_cache_tip": "图片、音频等缓存,清理后图片等资源将需要重新下载,不建议清理,软件会根据磁盘空间动态管理缓存大小",
|
"setting__other_resource_cache_tip": "包括图片、音频等缓存,清理后这些资源将需要重新下载,不建议清理,软件会根据磁盘空间动态管理缓存大小",
|
||||||
"setting__other_resource_cache_tip_confirm": "涉及图片、音频等缓存,清理后图片等资源将需要重新下载,不建议清理,软件会根据磁盘空间动态管理缓存大小,是否仍要清理?",
|
"setting__other_resource_cache_tip_confirm": "涉及图片、音频等缓存,清理后这些资源将需要重新下载,不建议清理,软件会根据磁盘空间动态管理缓存大小,是否仍要清理?",
|
||||||
"setting__other_tray_theme": "托盘图标样式",
|
"setting__other_tray_theme": "托盘图标样式",
|
||||||
|
"setting__other_tray_theme_auto": "跟随系统亮暗模式",
|
||||||
"setting__other_tray_theme_black": "黑色",
|
"setting__other_tray_theme_black": "黑色",
|
||||||
"setting__other_tray_theme_native": "白色",
|
"setting__other_tray_theme_native": "白色",
|
||||||
"setting__other_tray_theme_origin": "原色",
|
"setting__other_tray_theme_origin": "原色",
|
||||||
"setting__play": "播放设置",
|
"setting__play": "播放设置",
|
||||||
"setting__play_auto_clean_played_list": "点击与播放列表相同的列表切歌时是否清空已播放列表(随机模式下列表内所有歌曲会重新参与随机)",
|
"setting__play_advanced_audio_features_tip": "自定义音频输出设备与该功能冲突,启用该功能后音频输出设备将会被重置为默认,目前此问题暂无法解决。是否仍要开启?",
|
||||||
|
"setting__play_auto_clean_played_list": "点击与播放列表相同的列表切歌时清空已播放列表",
|
||||||
|
"setting__play_auto_clean_played_list_tip": "随机模式下列表内所有歌曲会重新参与随机",
|
||||||
"setting__play_auto_skip_on_error": "播放错误时自动切换歌曲",
|
"setting__play_auto_skip_on_error": "播放错误时自动切换歌曲",
|
||||||
"setting__play_detail": "播放详情页设置",
|
"setting__play_detail": "播放详情页设置",
|
||||||
"setting__play_detail_align": "歌词对齐方式",
|
"setting__play_detail_align": "歌词对齐方式",
|
||||||
"setting__play_detail_align_center": "居中",
|
"setting__play_detail_align_center": "居中",
|
||||||
"setting__play_detail_align_left": "居左",
|
"setting__play_detail_align_left": "居左",
|
||||||
"setting__play_detail_align_right": "居右",
|
"setting__play_detail_align_right": "居右",
|
||||||
"setting__play_detail_font_size": "歌词字体大小(可以在播放详情页使用键盘的 + - 调整字体大小)",
|
"setting__play_detail_font_size": "歌词字体大小(可以在播放详情页使用键盘的「+」「-」调整字体大小)",
|
||||||
"setting__play_detail_font_size_current": "当前字体大小:{size}",
|
"setting__play_detail_font_size_current": "当前字体大小:{size}",
|
||||||
"setting__play_detail_font_size_reset": "重置",
|
"setting__play_detail_font_size_reset": "重置",
|
||||||
"setting__play_detail_font_zoom": "缩放当前正在播放的歌词",
|
"setting__play_detail_font_zoom": "缩放当前正在播放的歌词",
|
||||||
"setting__play_detail_lyric_progress": "允许通过歌词调整播放进度",
|
"setting__play_detail_lyric_delay_scroll": "延迟歌词滚动",
|
||||||
"setting__play_lyric_lxlrc": "使用卡拉OK式歌词播放(如果可用,此功能比较耗性能,低配置电脑不建议开启)",
|
"setting__play_detail_lyric_progress": "允许通过拖拽歌词调整播放进度",
|
||||||
|
"setting__play_lyric_lxlrc": "使用卡拉 OK 式歌词播放(如果可用)",
|
||||||
|
"setting__play_lyric_lxlrc_tip": "此功能比较耗性能,低配置电脑不建议开启!",
|
||||||
"setting__play_lyric_roma": "显示歌词罗马音(如果可用)",
|
"setting__play_lyric_roma": "显示歌词罗马音(如果可用)",
|
||||||
"setting__play_lyric_s2t": "将播放与下载的歌词转换为繁体中文",
|
"setting__play_lyric_s2t": "将播放与下载的中文歌词转换为繁体",
|
||||||
"setting__play_lyric_transition": "显示歌词翻译(如果可用)",
|
"setting__play_lyric_transition": "显示歌词翻译(如果可用)",
|
||||||
|
"setting__play_max_output_channel_count": "使用设备能处理的最大声道数输出音频",
|
||||||
"setting__play_mediaDevice": "音频输出",
|
"setting__play_mediaDevice": "音频输出",
|
||||||
"setting__play_mediaDevice_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲",
|
"setting__play_mediaDevice_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲",
|
||||||
"setting__play_mediaDevice_title": "选择声音输出的媒体设备",
|
"setting__play_mediaDevice_title": "选择声音输出的媒体设备",
|
||||||
"setting__play_media_device_error_tip": "此功能与高级音频功能(音频可视化、音效设置)冲突,你本次启动软件时已启用这些功能,此设置暂不可用,请 关闭这些功能 并 重启 软件后,再来修改此设置。",
|
"setting__play_media_device_error_tip": "此功能与高级音频功能(音频可视化、音效设置、使用设备能处理的最大声道数输出音频功能)冲突。你本次启动软件时已启用这些功能,此选项暂不可用。请关闭这些功能并重启软件后,再修改此选项!",
|
||||||
"setting__play_media_device_tip": "此功能与音频可视化功能冲突,两者无法同时启用,是否将音频可视化关闭 并 应用所选音频输出设置?",
|
"setting__play_media_device_tip": "此功能与音频可视化功能冲突,两者无法同时启用。是否关闭「音频可视化」并应用所选音频输出设置?",
|
||||||
|
"setting__play_playQuality": "优先播放的音质(如果可用)",
|
||||||
"setting__play_power_save_blocker": "播放歌曲时阻止电脑休眠",
|
"setting__play_power_save_blocker": "播放歌曲时阻止电脑休眠",
|
||||||
"setting__play_quality": "优先播放320K品质的歌曲(如果可用)",
|
|
||||||
"setting__play_save_play_time": "记住播放进度",
|
"setting__play_save_play_time": "记住播放进度",
|
||||||
"setting__play_startup_auto_play": "启动软件后自动播放音乐",
|
"setting__play_startup_auto_play": "启动软件后自动播放音乐",
|
||||||
|
"setting__play_statusbar_lyric": "在状态栏显示歌词(需要启用托盘)",
|
||||||
"setting__play_task_bar": "在任务栏上显示当前歌曲播放进度",
|
"setting__play_task_bar": "在任务栏上显示当前歌曲播放进度",
|
||||||
"setting__play_timeout": "定时暂停",
|
"setting__play_timeout": "定时暂停",
|
||||||
"setting__player_audio_visualization_tip": "自定义音频输出设备与音频可视化功能会冲突,启用了音频可视化后音频输出设备将会被重置为默认的输出设备,目前此问题暂无法解决,是否仍要开启?",
|
"setting__player_audio_visualization_tip": "自定义音频输出设备与音频可视化功能冲突,启用「音频可视化」后音频输出设备将会被重置为默认,目前此问题暂无法解决。是否仍要开启?",
|
||||||
"setting__search": "搜索设置",
|
"setting__search": "搜索设置",
|
||||||
"setting__search_focus_search_box": "启动时自动聚焦搜索框",
|
"setting__search_focus_search_box": "启动时自动聚焦搜索框",
|
||||||
"setting__search_history": "显示历史搜索记录",
|
"setting__search_history": "显示历史搜索记录",
|
||||||
"setting__search_hot": "显示热门搜索",
|
"setting__search_hot": "显示热门搜索",
|
||||||
"setting__setting__desktop_lyric_font_weight_extended": "翻译、罗马音歌词",
|
"setting__desktop_lyric_font_weight_extended": "翻译、罗马音歌词",
|
||||||
"setting__setting__desktop_lyric_font_weight_font": "逐字歌词",
|
"setting__desktop_lyric_font_weight_font": "逐字歌词",
|
||||||
"setting__setting__desktop_lyric_font_weight_line": "逐行歌词",
|
"setting__desktop_lyric_font_weight_line": "逐行歌词",
|
||||||
"setting__sync": "数据同步",
|
"setting__sync": "数据同步",
|
||||||
"setting__sync_client_address": "当前设备地址:{address}",
|
"setting__sync_client_address": "当前设备地址:{address}",
|
||||||
"setting__sync_client_host": "同步服务地址",
|
"setting__sync_client_host": "同步服务地址",
|
||||||
"setting__sync_client_host_tip": "http://IP地址:端口号",
|
"setting__sync_client_host_tip": "http://<IP 地址>:<端口号>",
|
||||||
"setting__sync_client_mode": "客户端模式",
|
"setting__sync_client_mode": "客户端模式",
|
||||||
"setting__sync_client_status": "状态:{status}",
|
"setting__sync_client_status": "状态:{status}",
|
||||||
"setting__sync_code_blocked_ip": "当前设备的IP已被服务端封禁!",
|
"setting__sync_code_blocked_ip": "当前设备的 IP 已被服务端封禁!",
|
||||||
"setting__sync_code_fail": "连接码无效",
|
"setting__sync_code_fail": "连接码无效",
|
||||||
"setting__sync_enable": "启用同步功能",
|
"setting__sync_enable": "启用同步功能",
|
||||||
"setting__sync_mode": "同步模式",
|
"setting__sync_mode": "同步模式",
|
||||||
@@ -549,9 +576,11 @@
|
|||||||
"setting__sync_server_port_tip": "请输入同步服务端口号",
|
"setting__sync_server_port_tip": "请输入同步服务端口号",
|
||||||
"setting__sync_server_refresh_code": "刷新连接码",
|
"setting__sync_server_refresh_code": "刷新连接码",
|
||||||
"setting__sync_server_show_device_list": "已认证设备列表",
|
"setting__sync_server_show_device_list": "已认证设备列表",
|
||||||
"setting__sync_tip": "使用方式请看常见问题“同步功能”部分",
|
"setting__sync_tip": "使用方式请看常见问题「同步功能」部分",
|
||||||
"setting__update": "软件更新",
|
"setting__update": "软件更新",
|
||||||
"setting__update_checking": "检查更新中...",
|
"setting__update_checking": "检查更新中...",
|
||||||
|
"setting__update_commit_date": "提交日期:",
|
||||||
|
"setting__update_commit_id": "代码版本:",
|
||||||
"setting__update_current_label": "当前版本:",
|
"setting__update_current_label": "当前版本:",
|
||||||
"setting__update_downloading": "发现新版本并在努力下载中,请稍后...⏳",
|
"setting__update_downloading": "发现新版本并在努力下载中,请稍后...⏳",
|
||||||
"setting__update_init": "处理更新中...",
|
"setting__update_init": "处理更新中...",
|
||||||
@@ -563,18 +592,19 @@
|
|||||||
"setting__update_show_change_log": "更新版本后的首次启动时显示更新日志",
|
"setting__update_show_change_log": "更新版本后的首次启动时显示更新日志",
|
||||||
"setting__update_try_auto_update": "发现新版本时尝试自动下载更新",
|
"setting__update_try_auto_update": "发现新版本时尝试自动下载更新",
|
||||||
"setting__update_unknown": "未知",
|
"setting__update_unknown": "未知",
|
||||||
"setting__update_unknown_tip": "❓ 获取最新版本信息失败,建议去关于界面打开项目发布地址查看当前版本是否最新",
|
"setting__update_unknown_tip": "❓ 获取最新版本信息失败,建议去「关于」页面打开项目发布地址查看当前版本是否最新",
|
||||||
|
"setting_download_save_group_list_name": "将文件保存到以对应列表命名的子目录中",
|
||||||
"setting_sync_status_enabled": "已连接",
|
"setting_sync_status_enabled": "已连接",
|
||||||
"song_list": "歌单",
|
"song_list": "歌单",
|
||||||
"songlist__import_input_btn_confirm": "打开",
|
"songlist__import_input_btn_confirm": "打开",
|
||||||
"songlist__import_input_show_btn": "打开歌单",
|
"songlist__import_input_show_btn": "打开歌单",
|
||||||
"songlist__import_input_tip": "输入歌单链接或歌单ID",
|
"songlist__import_input_tip": "输入歌单链接或歌单 ID",
|
||||||
"songlist__import_input_tip_1": "不支持跨源打开歌单,请确认要打开的歌单与当前歌单源是否对应",
|
"songlist__import_input_tip_1": "不支持跨源打开歌单,请确认要打开的歌单与当前选择的歌单来源是否对应",
|
||||||
"songlist__import_input_tip_2": "若遇到无法打开的歌单链接,欢迎反馈",
|
"songlist__import_input_tip_2": "若遇到无法打开的歌单链接,欢迎反馈",
|
||||||
"songlist__import_input_tip_3": "酷狗源不支持用歌单ID打开,但支持酷狗码打开",
|
"songlist__import_input_tip_3": "不支持用歌单 ID 打开酷狗源歌单,但支持用酷狗码打开",
|
||||||
"songlist__import_input_tip_4": "网易源的“我喜欢”歌单需要 Token 才能打开,详情看 ",
|
"songlist__import_input_tip_4": "网易源的「我喜欢」歌单需要 Token 才能打开,详情看 ",
|
||||||
"songlist__import_input_title": "打开分享的歌单",
|
"songlist__import_input_title": "打开分享的歌单",
|
||||||
"songlist__open_list": "打开{name}歌单",
|
"songlist__open_list": "打开「{name}」歌单",
|
||||||
"songlist__tag_info_hot_tag": "热门标签",
|
"songlist__tag_info_hot_tag": "热门标签",
|
||||||
"source_alias_all": "聚合大会",
|
"source_alias_all": "聚合大会",
|
||||||
"source_alias_bd": "小杜音乐",
|
"source_alias_bd": "小杜音乐",
|
||||||
@@ -595,24 +625,24 @@
|
|||||||
"sync__auth_code_input_tip": "请输入连接码",
|
"sync__auth_code_input_tip": "请输入连接码",
|
||||||
"sync__auth_code_title": "需要输入连接码",
|
"sync__auth_code_title": "需要输入连接码",
|
||||||
"sync__dislike_merge_tip_desc": "合并两边列表内容并去重",
|
"sync__dislike_merge_tip_desc": "合并两边列表内容并去重",
|
||||||
"sync__dislike_other_tip_desc": "“取消同步”将不使用不喜欢列表同步功能",
|
"sync__dislike_other_tip_desc": "「取消同步」将不使用「不喜欢的歌曲」列表同步功能",
|
||||||
"sync__dislike_overwrite_tip_desc": "被覆盖者的列表将被替换成覆盖者的列表",
|
"sync__dislike_overwrite_tip_desc": "被覆盖者的列表将被替换成覆盖者的列表",
|
||||||
"sync__dislike_title": "选择与 {name} 的不喜欢列表同步方式",
|
"sync__dislike_title": "选择与「{name}」的「不喜欢的歌曲」列表同步方式",
|
||||||
"sync__list_merge_tip_desc": "将两边的列表合并到一起,相同的歌曲将被去掉(去掉的是被合并者的歌曲),不同的歌曲将被添加。",
|
"sync__list_merge_tip_desc": "将两边的列表合并到一起,相同的歌曲将被去掉(去掉的是被合并者的歌曲),不同的歌曲将被添加。",
|
||||||
"sync__list_other_tip_desc": "“取消同步”将不使用列表同步功能。",
|
"sync__list_other_tip_desc": "「取消同步」将不使用列表同步功能。",
|
||||||
"sync__list_overwrite_tip_desc": "被覆盖者与覆盖者列表ID相同的列表将被删除后替换成覆盖者的列表(列表ID不同的列表将被合并到一起),若勾选完全覆盖,则被覆盖者的所有列表将被移除,然后替换成覆盖者的列表。",
|
"sync__list_overwrite_tip_desc": "被覆盖者与覆盖者列表 ID 相同的列表将被移除后替换成覆盖者的列表(列表 ID 不同的列表将被合并到一起)。若勾选「完全覆盖」,则被覆盖者的所有列表将被移除,然后替换成覆盖者的列表。",
|
||||||
"sync__list_title": "选择与 {name} 的列表同步方式",
|
"sync__list_title": "选择与「{name}」的列表同步方式",
|
||||||
"sync__merge_btn_local_remote": "本机列表 合并 远程列表",
|
"sync__merge_btn_local_remote": "「本机列表」合并「远程列表」",
|
||||||
"sync__merge_btn_remote_local": "远程列表 合并 本机列表",
|
"sync__merge_btn_remote_local": "「远程列表」合并「本机列表」",
|
||||||
"sync__merge_label": "合并",
|
"sync__merge_label": "合并",
|
||||||
"sync__merge_tip": "合并:",
|
"sync__merge_tip": "合并:",
|
||||||
"sync__other_label": "其他",
|
"sync__other_label": "其他",
|
||||||
"sync__other_tip": "其他:",
|
"sync__other_tip": "其他:",
|
||||||
"sync__overwrite": "完全覆盖",
|
"sync__overwrite": "完全覆盖",
|
||||||
"sync__overwrite_btn_cancel": "取消同步",
|
"sync__overwrite_btn_cancel": "取消同步",
|
||||||
"sync__overwrite_btn_local_remote": "本机列表 覆盖 远程列表",
|
"sync__overwrite_btn_local_remote": "「本机列表」覆盖「远程列表」",
|
||||||
"sync__overwrite_btn_none": "仅使用实时同步功能",
|
"sync__overwrite_btn_none": "仅使用实时同步功能",
|
||||||
"sync__overwrite_btn_remote_local": "远程列表 覆盖 本机列表",
|
"sync__overwrite_btn_remote_local": "「远程列表」覆盖「本机列表」",
|
||||||
"sync__overwrite_label": "覆盖",
|
"sync__overwrite_label": "覆盖",
|
||||||
"sync__overwrite_tip": "覆盖:",
|
"sync__overwrite_tip": "覆盖:",
|
||||||
"sync_status_disabled": "未连接",
|
"sync_status_disabled": "未连接",
|
||||||
@@ -621,7 +651,7 @@
|
|||||||
"tag__lossless_24bit": "24bit",
|
"tag__lossless_24bit": "24bit",
|
||||||
"theme_add": "添加主题",
|
"theme_add": "添加主题",
|
||||||
"theme_auto": "道法自然",
|
"theme_auto": "道法自然",
|
||||||
"theme_auto_tip": "鼠标 右击 可打开亮、暗主题设置窗口",
|
"theme_auto_tip": "鼠标右击可打开亮、暗主题设置窗口",
|
||||||
"theme_black": "黑灯瞎火",
|
"theme_black": "黑灯瞎火",
|
||||||
"theme_blue": "蓝田生玉",
|
"theme_blue": "蓝田生玉",
|
||||||
"theme_blue2": "清热版蓝",
|
"theme_blue2": "清热版蓝",
|
||||||
@@ -652,15 +682,16 @@
|
|||||||
"theme_edit_modal__pick_save": "确认",
|
"theme_edit_modal__pick_save": "确认",
|
||||||
"theme_edit_modal__preview": "预览主题",
|
"theme_edit_modal__preview": "预览主题",
|
||||||
"theme_edit_modal__primary": "主题色",
|
"theme_edit_modal__primary": "主题色",
|
||||||
"theme_edit_modal__remove": "删除",
|
"theme_edit_modal__remove": "移除",
|
||||||
"theme_edit_modal__remove_tip": "是否真的要移除这个主题?",
|
"theme_edit_modal__remove_tip": "是否真的要移除这个主题?",
|
||||||
"theme_edit_modal__save_new": "另存",
|
"theme_edit_modal__save_new": "另存",
|
||||||
"theme_edit_modal__select_bg_file": "选择背景图片",
|
"theme_edit_modal__select_bg_file": "选择背景图片",
|
||||||
"theme_edit_modal__title": "编辑主题",
|
"theme_edit_modal__title_add": "添加主题",
|
||||||
|
"theme_edit_modal__title_edit": "编辑主题",
|
||||||
"theme_green": "绿意盎然",
|
"theme_green": "绿意盎然",
|
||||||
"theme_grey": "灰常美丽",
|
"theme_grey": "灰常美丽",
|
||||||
"theme_happy_new_year": "新年快乐",
|
"theme_happy_new_year": "新年快乐",
|
||||||
"theme_max_tip": "最多只能添加10个主题哦,删掉一些再添加吧 😜 ",
|
"theme_max_tip": "最多只能添加 10 个主题哦,删掉一些再添加吧😜",
|
||||||
"theme_mid_autumn": "月里嫦娥",
|
"theme_mid_autumn": "月里嫦娥",
|
||||||
"theme_ming": "青出于黑",
|
"theme_ming": "青出于黑",
|
||||||
"theme_more_btn_show": "更多主题",
|
"theme_more_btn_show": "更多主题",
|
||||||
@@ -674,29 +705,29 @@
|
|||||||
"theme_selector_modal__theme_name": "主题名称",
|
"theme_selector_modal__theme_name": "主题名称",
|
||||||
"theme_selector_modal__title": "跟随系统主题设置",
|
"theme_selector_modal__title": "跟随系统主题设置",
|
||||||
"theme_selector_modal__title_tip": "注:你可以预先设置一个亮色主题及暗色主题,此后将根据系统的亮、暗主题色自动切换为你预先设置的相应主题。",
|
"theme_selector_modal__title_tip": "注:你可以预先设置一个亮色主题及暗色主题,此后将根据系统的亮、暗主题色自动切换为你预先设置的相应主题。",
|
||||||
"toggle_source_failed": "换源失败,请尝试手动在其他源搜索该歌曲播放",
|
"toggle_source_failed": "换源失败,请尝试手动在搜索页指定其他来源搜索该歌曲播放",
|
||||||
"toggle_source_try": "尝试切换到其他源...",
|
"toggle_source_try": "尝试切换到其他来源...",
|
||||||
"update__downgrade_tip": "我们发现你降级了版本({ver}),若使用新版本时遇到问题,请先尝试阅读常见问题解决,若你遇到的问题在常见问题中未记录或无法解决,可以通过文档中提到的反馈渠道给我们反馈😘!\n注意:从新版本降级旧版时建议先备份歌单,若出现异常则可通过清理数据解决,数据目录路径文档有记录。",
|
"update__downgrade_tip": "我们发现你降级了版本({ver}),若使用新版本时遇到问题,请先尝试阅读常见问题解决,若你遇到的问题在常见问题中未记录或无法解决,可以通过文档中提到的反馈渠道给我们反馈😘!\n\n注意:从新版本降级旧版时建议先备份歌单,若出现异常则可通过清理数据解决,数据目录路径文档有记录。",
|
||||||
"update__error_top": "自动下载新版本失败,你可以尝试重新下载更新或者手动去下载更新,\n新版地址在更新弹窗下面有写,下载新版直接覆盖安装即可,若安装失败则看常见问题解决,\n注意:目前只有Windows安装版可以自动更新(Linux的AppImage、deb版似乎也可以,未测试),其他版本请手动下载更新!",
|
"update__error_top": "自动下载新版本失败,你可以尝试重新下载更新或者手动去下载更新。\n\n新版地址在更新弹窗下面有写,下载新版直接覆盖安装即可,若安装失败则看常见问题解决。\n\n注意:目前只有 Windows 安装版可以自动更新(Linux 的 AppImage、deb 版似乎也可以,未测试),其他版本请手动下载更新!",
|
||||||
"update__ignore_cancel": "我就不想更新🤨",
|
"update__ignore_cancel": "我就不想更新🤨",
|
||||||
"update__ignore_confirm": "好 去更新看看❤️",
|
"update__ignore_confirm": "好,去更新看看❤️",
|
||||||
"update__ignore_confirm_tip": "目前只有Windows安装版可以自动更新(Linux的AppImage、deb版似乎也可以,未测试),其他版本请手动下载更新,\n新版地址在更新弹窗下面有写,下载新版直接覆盖安装即可,若安装失败看常见问题解决。",
|
"update__ignore_confirm_tip": "目前只有 Windows 安装版可以自动更新(Linux 的 AppImage、deb 版似乎也可以,未测试),其他版本请手动下载更新,\n新版地址在更新弹窗下面有写,下载新版直接覆盖安装即可,若安装失败看常见问题解决。",
|
||||||
"update__ignore_confirm_tip_confirm": "OK 已了解",
|
"update__ignore_confirm_tip_confirm": "OK,已了解",
|
||||||
"update__ignore_tip": "你现在使用的版本距离最新版本已经落后了 {num} 个版本🤪,为了更好的使用体验,建议更新到最新版本哦~!\n注:若使用新版本时遇到问题,请先尝试阅读常见问题解决,若你遇到的问题在常见问题中未记录或无法解决,可以通过文档中提到的反馈渠道给我们反馈😘!",
|
"update__ignore_tip": "你现在使用的版本距离最新版本已经落后了 {num} 个版本🤪,为了更好的使用体验,建议更新到最新版本哦~!\n\n注:若使用新版本时遇到问题,请先尝试阅读常见问题解决,若你遇到的问题在常见问题中未记录或无法解决,可以通过文档中提到的反馈渠道给我们反馈😘!",
|
||||||
"update__timeout_top": "下载时间过长提示\n\n你当前所在网络访问GitHub较慢,新版本已经下了一个钟了还没完成😳,你仍可选择继续等,但墙裂建议手动更新版本!",
|
"update__timeout_top": "下载时间过长提示\n\n你当前所在网络访问 GitHub 较慢,新版本已经下了一个钟了还没完成😳,你仍可选择继续等,但墙裂建议手动更新版本!",
|
||||||
"user_api__allow_show_update_alert": "允许显示更新弹窗",
|
"user_api__allow_show_update_alert": "允许显示更新弹窗",
|
||||||
"user_api__btn_export": "导出",
|
"user_api__btn_export": "导出",
|
||||||
"user_api__btn_import": "本地导入",
|
"user_api__btn_import": "本地导入",
|
||||||
"user_api__btn_import_online": "在线导入",
|
"user_api__btn_import_online": "在线导入",
|
||||||
"user_api__btn_remove": "移除",
|
"user_api__btn_remove": "移除",
|
||||||
"user_api__import_file": "选择音乐API脚本文件",
|
"user_api__import_file": "选择自定义源 API 脚本文件",
|
||||||
"user_api__init_failed_alert": "自定义源 [{name}] 初始化失败:",
|
"user_api__init_failed_alert": "自定义源「{name}」初始化失败:",
|
||||||
"user_api__max_tip": "最多只能同时存在20个源哦🤪\n想要继续导入的话,请先移除一些旧的源腾出位置吧",
|
"user_api__max_tip": "最多只能同时存在 20 个源哦🤪\n想要继续导入的话,请先移除一些旧的源腾出位置吧",
|
||||||
"user_api__noitem": "这里竟然是空的 😲",
|
"user_api__noitem": "这里竟然是空的😲",
|
||||||
"user_api__note": "提示:虽然我们已经尽可能地隔离了脚本的运行环境,但导入包含恶意行为的脚本仍可能会影响你的系统,请谨慎导入。",
|
"user_api__note": "提示:虽然我们已经尽可能地隔离了脚本的运行环境,但导入包含恶意行为的脚本仍可能会影响你的系统,请谨慎导入。",
|
||||||
"user_api__readme": "源编写说明:",
|
"user_api__readme": "源编写说明:",
|
||||||
"user_api__title": "自定义源管理",
|
"user_api__title": "自定义源管理",
|
||||||
"user_api__update_alert": "自定义源 [{name}] 发现新版本:",
|
"user_api__update_alert": "自定义源「{name}」发现新版本:",
|
||||||
"user_api__update_alert_open_url": "打开更新地址",
|
"user_api__update_alert_open_url": "打开更新地址",
|
||||||
"user_api_import__failed": "自定义源导入失败:\n{message}",
|
"user_api_import__failed": "自定义源导入失败:\n{message}",
|
||||||
"user_api_import_online__input_confirm": "导入",
|
"user_api_import_online__input_confirm": "导入",
|
||||||
|
|||||||
+358
-327
File diff suppressed because it is too large
Load Diff
+58
-32
@@ -5,12 +5,12 @@ import { URL_SCHEME_RXP } from '@common/constants'
|
|||||||
import { getTheme, initHotKey, initSetting, parseEnvParams } from './utils'
|
import { getTheme, initHotKey, initSetting, parseEnvParams } from './utils'
|
||||||
import { navigationUrlWhiteList } from '@common/config'
|
import { navigationUrlWhiteList } from '@common/config'
|
||||||
import defaultSetting from '@common/defaultSetting'
|
import defaultSetting from '@common/defaultSetting'
|
||||||
import { closeWindow, isExistWindow as isExistMainWindow, showWindow as showMainWindow } from './modules/winMain'
|
import { isExistWindow as isExistMainWindow, showWindow as showMainWindow } from './modules/winMain'
|
||||||
import { createAppEvent, createDislikeEvent, createListEvent } from '@main/event'
|
import { createAppEvent, createDislikeEvent, createListEvent } from '@main/event'
|
||||||
import { isMac, log } from '@common/utils'
|
import { isMac, log } from '@common/utils'
|
||||||
import createWorkers from './worker'
|
import createWorkers from './worker'
|
||||||
import { migrateDBData } from './utils/migrate'
|
import { migrateDBData } from './utils/migrate'
|
||||||
import { encodePath, openDirInExplorer } from '@common/utils/electron'
|
import { openDirInExplorer } from '@common/utils/electron'
|
||||||
|
|
||||||
export const initGlobalData = () => {
|
export const initGlobalData = () => {
|
||||||
const envParams = parseEnvParams()
|
const envParams = parseEnvParams()
|
||||||
@@ -18,11 +18,58 @@ export const initGlobalData = () => {
|
|||||||
cmdParams: envParams.cmdParams,
|
cmdParams: envParams.cmdParams,
|
||||||
deeplink: envParams.deeplink,
|
deeplink: envParams.deeplink,
|
||||||
}
|
}
|
||||||
|
global.lx = {
|
||||||
|
inited: false,
|
||||||
|
isSkipTrayQuit: false,
|
||||||
|
// mainWindowClosed: true,
|
||||||
|
event_app: createAppEvent(),
|
||||||
|
event_list: createListEvent(),
|
||||||
|
event_dislike: createDislikeEvent(),
|
||||||
|
appSetting: defaultSetting,
|
||||||
|
worker: createWorkers(),
|
||||||
|
hotKey: {
|
||||||
|
enable: true,
|
||||||
|
config: {
|
||||||
|
local: {
|
||||||
|
enable: false,
|
||||||
|
keys: {},
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
enable: false,
|
||||||
|
keys: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
state: new Map(),
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
shouldUseDarkColors: nativeTheme.shouldUseDarkColors,
|
||||||
|
theme: {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
isDark: false,
|
||||||
|
colors: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
player_status: {
|
||||||
|
status: 'stoped',
|
||||||
|
name: '',
|
||||||
|
singer: '',
|
||||||
|
albumName: '',
|
||||||
|
picUrl: '',
|
||||||
|
progress: 0,
|
||||||
|
duration: 0,
|
||||||
|
playbackRate: 1,
|
||||||
|
lyricLineText: '',
|
||||||
|
lyricLineAllText: '',
|
||||||
|
lyric: '',
|
||||||
|
collect: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
global.staticPath =
|
global.staticPath =
|
||||||
process.env.NODE_ENV !== 'production'
|
process.env.NODE_ENV !== 'production'
|
||||||
? webpackStaticPath
|
? webpackStaticPath
|
||||||
: path.join(encodePath(__dirname), 'static')
|
: path.join(__dirname, 'static')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initSingleInstanceHandle = () => {
|
export const initSingleInstanceHandle = () => {
|
||||||
@@ -158,6 +205,9 @@ export const listenerAppEvent = (startApp: () => void) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.on('before-quit', () => {
|
||||||
|
global.lx.isSkipTrayQuit = true
|
||||||
|
})
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
if (isMac) return
|
if (isMac) return
|
||||||
|
|
||||||
@@ -206,35 +256,11 @@ const initTheme = () => {
|
|||||||
|
|
||||||
let isInitialized = false
|
let isInitialized = false
|
||||||
export const initAppSetting = async() => {
|
export const initAppSetting = async() => {
|
||||||
if (!global.lx) {
|
if (!global.lx.inited) {
|
||||||
const config = await initHotKey()
|
const config = await initHotKey()
|
||||||
global.lx = {
|
global.lx.hotKey.config.local = config.local
|
||||||
isTrafficLightClose: false,
|
global.lx.hotKey.config.global = config.global
|
||||||
isSkipTrayQuit: false,
|
global.lx.inited = true
|
||||||
// mainWindowClosed: true,
|
|
||||||
event_app: createAppEvent(),
|
|
||||||
event_list: createListEvent(),
|
|
||||||
event_dislike: createDislikeEvent(),
|
|
||||||
appSetting: defaultSetting,
|
|
||||||
worker: createWorkers(),
|
|
||||||
hotKey: {
|
|
||||||
enable: true,
|
|
||||||
config: {
|
|
||||||
local: config.local,
|
|
||||||
global: config.global,
|
|
||||||
},
|
|
||||||
state: new Map(),
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
shouldUseDarkColors: nativeTheme.shouldUseDarkColors,
|
|
||||||
theme: {
|
|
||||||
id: '',
|
|
||||||
name: '',
|
|
||||||
isDark: false,
|
|
||||||
colors: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isInitialized) {
|
if (!isInitialized) {
|
||||||
@@ -261,5 +287,5 @@ export const initAppSetting = async() => {
|
|||||||
|
|
||||||
export const quitApp = () => {
|
export const quitApp = () => {
|
||||||
global.lx.isSkipTrayQuit = true
|
global.lx.isSkipTrayQuit = true
|
||||||
closeWindow()
|
app.quit()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { EventEmitter } from 'events'
|
import { EventEmitter } from 'events'
|
||||||
|
|
||||||
import { saveAppHotKeyConfig, updateSetting } from '@main/utils'
|
import { saveAppHotKeyConfig, updateSetting } from '@main/utils'
|
||||||
|
import type { BrowserWindow } from 'electron'
|
||||||
|
|
||||||
export class Event extends EventEmitter {
|
export class Event extends EventEmitter {
|
||||||
// closeAll() {
|
// closeAll() {
|
||||||
@@ -52,6 +53,14 @@ export class Event extends EventEmitter {
|
|||||||
this.emit('deeplink', link)
|
this.emit('deeplink', link)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player_status(status: Partial<LX.Player.Status>) {
|
||||||
|
for (const [key, value] of Object.entries(status)) {
|
||||||
|
// @ts-expect-error
|
||||||
|
global.lx.player_status[key] = value
|
||||||
|
}
|
||||||
|
this.emit('player_status', status)
|
||||||
|
}
|
||||||
|
|
||||||
hot_key_down(keyInfo: LX.HotKeyDownInfo) {
|
hot_key_down(keyInfo: LX.HotKeyDownInfo) {
|
||||||
this.emit('hot_key_down', keyInfo)
|
this.emit('hot_key_down', keyInfo)
|
||||||
}
|
}
|
||||||
@@ -61,6 +70,10 @@ export class Event extends EventEmitter {
|
|||||||
this.emit('hot_key_config_update', config)
|
this.emit('hot_key_config_update', config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main_window_created(win: BrowserWindow) {
|
||||||
|
this.emit('main_window_created', win)
|
||||||
|
}
|
||||||
|
|
||||||
main_window_ready_to_show() {
|
main_window_ready_to_show() {
|
||||||
this.emit('main_window_ready_to_show')
|
this.emit('main_window_ready_to_show')
|
||||||
}
|
}
|
||||||
@@ -92,6 +105,10 @@ export class Event extends EventEmitter {
|
|||||||
main_window_fullscreen(isFullscreen: boolean) {
|
main_window_fullscreen(isFullscreen: boolean) {
|
||||||
this.emit('main_window_fullscreen', isFullscreen)
|
this.emit('main_window_fullscreen', isFullscreen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
desktop_lyric_window_created(win: BrowserWindow) {
|
||||||
|
this.emit('desktop_lyric_window_created', win)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+18
-4
@@ -8,20 +8,34 @@
|
|||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
import electronDebug from 'electron-debug'
|
import electronDebug from 'electron-debug'
|
||||||
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
|
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
|
||||||
|
import { openDevTools } from './utils'
|
||||||
// Install `electron-debug` with `devtron`
|
// Install `electron-debug` with `devtron`
|
||||||
electronDebug({
|
electronDebug({
|
||||||
showDevTools: true,
|
showDevTools: false,
|
||||||
devToolsMode: 'undocked',
|
devToolsMode: 'undocked',
|
||||||
})
|
})
|
||||||
|
|
||||||
// Install `vue-devtools`
|
// Install `vue-devtools`
|
||||||
app.on('ready', () => {
|
app.on('ready', () => {
|
||||||
installExtension(VUEJS_DEVTOOLS)
|
global.lx.event_app.on('main_window_created', (win) => {
|
||||||
|
openDevTools(win.webContents)
|
||||||
|
installExtension(VUEJS_DEVTOOLS, { session: win.webContents.session })
|
||||||
.then((name: string) => {
|
.then((name: string) => {
|
||||||
console.log(`Added Extension: ${name}`)
|
console.log(`[main window] Added Extension: ${name}`)
|
||||||
})
|
})
|
||||||
.catch((err: Error) => {
|
.catch((err: Error) => {
|
||||||
console.log('An error occurred: ', err)
|
console.log('[main window] An error occurred: ', err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
global.lx.event_app.on('desktop_lyric_window_created', (win) => {
|
||||||
|
openDevTools(win.webContents)
|
||||||
|
installExtension(VUEJS_DEVTOOLS, { session: win.webContents.session })
|
||||||
|
.then((name: string) => {
|
||||||
|
console.log(`[lyric window] Added Extension: ${name}`)
|
||||||
|
})
|
||||||
|
.catch((err: Error) => {
|
||||||
|
console.log('[lyric window] An error occurred: ', err)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,253 @@
|
|||||||
|
import http from 'node:http'
|
||||||
|
import querystring from 'node:querystring'
|
||||||
|
import type { Socket } from 'node:net'
|
||||||
|
import { getAddress } from '@common/utils/nodejs'
|
||||||
|
import { sendTaskbarButtonClick } from '@main/modules/winMain'
|
||||||
|
|
||||||
|
const sendResponse = (res: http.ServerResponse, code = 200, msg: string | Record<any, unknown> = 'OK', contentType = 'text/plain; charset=utf-8') => {
|
||||||
|
res.writeHead(code, {
|
||||||
|
'Content-Type': contentType,
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
})
|
||||||
|
if (typeof msg === 'object') {
|
||||||
|
res.end(JSON.stringify(msg))
|
||||||
|
} else {
|
||||||
|
res.end(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let status: LX.OpenAPI.Status = {
|
||||||
|
status: false,
|
||||||
|
message: '',
|
||||||
|
address: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubscribeKeys = keyof LX.Player.Status
|
||||||
|
|
||||||
|
let httpServer: http.Server
|
||||||
|
let sockets = new Set<Socket>()
|
||||||
|
let responses = new Map<http.ServerResponse<http.IncomingMessage>, SubscribeKeys[]>()
|
||||||
|
let playerStatusKeys: SubscribeKeys[]
|
||||||
|
|
||||||
|
const defaultFilter = [
|
||||||
|
'status',
|
||||||
|
'name',
|
||||||
|
'singer',
|
||||||
|
'albumName',
|
||||||
|
'lyricLineText',
|
||||||
|
'duration',
|
||||||
|
'progress',
|
||||||
|
'playbackRate',
|
||||||
|
] satisfies SubscribeKeys[]
|
||||||
|
|
||||||
|
const parseFilter = (filter: any) => {
|
||||||
|
if (typeof filter != 'string') return defaultFilter
|
||||||
|
filter = filter.split(',')
|
||||||
|
const subKeys = playerStatusKeys.filter(k => filter.includes(k))
|
||||||
|
return subKeys.length ? subKeys : defaultFilter
|
||||||
|
}
|
||||||
|
const handleSendStatus = (res: http.ServerResponse<http.IncomingMessage>, query?: string) => {
|
||||||
|
const keys = parseFilter(querystring.parse(query ?? '').filter)
|
||||||
|
const resp: Partial<Record<SubscribeKeys, any>> = {}
|
||||||
|
for (const k of keys) resp[k] = global.lx.player_status[k]
|
||||||
|
sendResponse(res, 200, resp, 'application/json; charset=utf-8')
|
||||||
|
}
|
||||||
|
const handleSubscribePlayerStatus = (req: http.IncomingMessage, res: http.ServerResponse<http.IncomingMessage>, query?: string) => {
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'text/event-stream',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
})
|
||||||
|
req.socket.setTimeout(0)
|
||||||
|
req.on('close', () => {
|
||||||
|
res.end('OK')
|
||||||
|
responses.delete(res)
|
||||||
|
})
|
||||||
|
const keys = parseFilter(querystring.parse(query ?? '').filter)
|
||||||
|
responses.set(res, keys)
|
||||||
|
for (const [k, v] of Object.entries(global.lx.player_status)) {
|
||||||
|
if (!keys.includes(k as SubscribeKeys)) continue
|
||||||
|
res.write(`event: ${k}\n`)
|
||||||
|
res.write(`data: ${JSON.stringify(v)}\n\n`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStartServer = async(port: number, ip: string) => new Promise<void>((resolve, reject) => {
|
||||||
|
playerStatusKeys = Object.keys(global.lx.player_status) as SubscribeKeys[]
|
||||||
|
httpServer = http.createServer((req, res): void => {
|
||||||
|
const [endUrl, query] = `/${req.url?.split('/').at(-1) ?? ''}`.split('?')
|
||||||
|
let code = 200
|
||||||
|
let msg = 'OK'
|
||||||
|
switch (endUrl) {
|
||||||
|
case '/status':
|
||||||
|
handleSendStatus(res, query)
|
||||||
|
return
|
||||||
|
// case '/test':
|
||||||
|
// code = 200
|
||||||
|
// res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
// msg = `<!DOCTYPE html>
|
||||||
|
// <html lang="en">
|
||||||
|
// <head>
|
||||||
|
// <meta charset="UTF-8" />
|
||||||
|
// <meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
// <meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
// <title>Nodejs Server-Sent Events</title>
|
||||||
|
// </head>
|
||||||
|
// <body>
|
||||||
|
// <h1>Hello SSE!</h1>
|
||||||
|
|
||||||
|
// <h2>List of Server-sent events</h2>
|
||||||
|
// <ul id="sse-list"></ul>
|
||||||
|
|
||||||
|
// <script>
|
||||||
|
// const subscription = new EventSource('/subscribe-player-status');
|
||||||
|
|
||||||
|
// // Default events
|
||||||
|
// subscription.addEventListener('open', () => {
|
||||||
|
// console.log('Connection opened')
|
||||||
|
// });
|
||||||
|
|
||||||
|
// subscription.addEventListener('error', (err) => {
|
||||||
|
// console.error(err)
|
||||||
|
// });
|
||||||
|
// subscription.addEventListener('lyricLineText', (event) => {
|
||||||
|
// console.log(event.data)
|
||||||
|
// });
|
||||||
|
// subscription.addEventListener('progress', (event) => {
|
||||||
|
// console.log(event.data)
|
||||||
|
// });
|
||||||
|
// subscription.addEventListener('name', (event) => {
|
||||||
|
// console.log(event.data)
|
||||||
|
// });
|
||||||
|
// subscription.addEventListener('singer', (event) => {
|
||||||
|
// console.log(event.data)
|
||||||
|
// });
|
||||||
|
// </script>
|
||||||
|
// </body>
|
||||||
|
// </html>`
|
||||||
|
// break
|
||||||
|
case '/lyric':
|
||||||
|
msg = global.lx.player_status.lyric
|
||||||
|
break
|
||||||
|
case '/play':
|
||||||
|
sendTaskbarButtonClick('play')
|
||||||
|
break
|
||||||
|
case '/pause':
|
||||||
|
sendTaskbarButtonClick('pause')
|
||||||
|
break
|
||||||
|
case '/skip-next':
|
||||||
|
sendTaskbarButtonClick('next')
|
||||||
|
break
|
||||||
|
case '/skip-prev':
|
||||||
|
sendTaskbarButtonClick('prev')
|
||||||
|
break
|
||||||
|
case '/collect':
|
||||||
|
sendTaskbarButtonClick('collect')
|
||||||
|
break
|
||||||
|
case '/uncollect':
|
||||||
|
sendTaskbarButtonClick('unCollect')
|
||||||
|
break
|
||||||
|
case '/subscribe-player-status':
|
||||||
|
try {
|
||||||
|
handleSubscribePlayerStatus(req, res, query)
|
||||||
|
return
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
code = 500
|
||||||
|
msg = 'Error'
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
code = 401
|
||||||
|
msg = 'Forbidden'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sendResponse(res, code, msg)
|
||||||
|
})
|
||||||
|
httpServer.on('error', error => {
|
||||||
|
console.log(error)
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
httpServer.on('connection', (socket) => {
|
||||||
|
sockets.add(socket)
|
||||||
|
socket.once('close', () => {
|
||||||
|
sockets.delete(socket)
|
||||||
|
})
|
||||||
|
socket.setTimeout(4000)
|
||||||
|
})
|
||||||
|
|
||||||
|
httpServer.on('listening', () => {
|
||||||
|
const addr = httpServer.address()
|
||||||
|
// console.log(addr)
|
||||||
|
if (!addr) {
|
||||||
|
reject(new Error('address is null'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
httpServer.listen(port, ip)
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleStopServer = async() => new Promise<void>((resolve, reject) => {
|
||||||
|
if (!httpServer) return
|
||||||
|
httpServer.close((err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
for (const socket of sockets) socket.destroy()
|
||||||
|
sockets.clear()
|
||||||
|
responses.clear()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const sendStatus = (status: Partial<LX.Player.Status>) => {
|
||||||
|
if (!responses.size) return
|
||||||
|
for (const [resp, keys] of responses) {
|
||||||
|
for (const [k, v] of Object.entries(status)) {
|
||||||
|
if (!keys.includes(k as SubscribeKeys)) continue
|
||||||
|
resp.write(`event: ${k}\n`)
|
||||||
|
resp.write(`data: ${JSON.stringify(v)}\n\n`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const stopServer = async() => {
|
||||||
|
global.lx.event_app.off('player_status', sendStatus)
|
||||||
|
if (!status.status) {
|
||||||
|
status.status = false
|
||||||
|
status.message = ''
|
||||||
|
status.address = ''
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
await handleStopServer().then(() => {
|
||||||
|
status.status = false
|
||||||
|
status.message = ''
|
||||||
|
status.address = ''
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err)
|
||||||
|
status.message = err.message
|
||||||
|
})
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
export const startServer = async(port: number, bindLan: boolean) => {
|
||||||
|
if (status.status) await stopServer()
|
||||||
|
await handleStartServer(port, bindLan ? '0.0.0.0' : '127.0.0.1').then(() => {
|
||||||
|
status.status = true
|
||||||
|
status.message = ''
|
||||||
|
let address = ['127.0.0.1']
|
||||||
|
if (bindLan) address = [...address, ...getAddress()]
|
||||||
|
status.address = address.join(', ')
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err)
|
||||||
|
status.status = false
|
||||||
|
status.message = err.message
|
||||||
|
status.address = ''
|
||||||
|
})
|
||||||
|
global.lx.event_app.on('player_status', sendStatus)
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getStatus = (): LX.OpenAPI.Status => status
|
||||||
@@ -6,10 +6,11 @@ import { callObj } from './sync'
|
|||||||
// import registerSyncListHandler from './syncList'
|
// import registerSyncListHandler from './syncList'
|
||||||
import log from '../log'
|
import log from '../log'
|
||||||
import { dateFormat } from '@common/utils/common'
|
import { dateFormat } from '@common/utils/common'
|
||||||
import { aesEncrypt, getAddress } from '../utils'
|
import { aesEncrypt } from '../utils'
|
||||||
import { sendClientStatus } from '@main/modules/winMain'
|
import { sendClientStatus } from '@main/modules/winMain'
|
||||||
import { createMsg2call } from 'message2call'
|
import { createMsg2call } from 'message2call'
|
||||||
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants_sync'
|
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants_sync'
|
||||||
|
import { getAddress } from '@common/utils/nodejs'
|
||||||
|
|
||||||
let status: LX.Sync.ClientStatus = {
|
let status: LX.Sync.ClientStatus = {
|
||||||
status: false,
|
status: false,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const request = async(url: string, options: RequestOptions = { }) => {
|
|||||||
return httpFetch(url, {
|
return httpFetch(url, {
|
||||||
...options,
|
...options,
|
||||||
timeout: options.timeout ?? 10000,
|
timeout: options.timeout ?? 10000,
|
||||||
|
follow_max: 5,
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
return {
|
return {
|
||||||
text: response.body,
|
text: response.body,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import http, { type IncomingMessage } from 'node:http'
|
|||||||
import { WebSocketServer } from 'ws'
|
import { WebSocketServer } from 'ws'
|
||||||
import { registerLocalSyncEvent, callObj, sync, unregisterLocalSyncEvent } from './sync'
|
import { registerLocalSyncEvent, callObj, sync, unregisterLocalSyncEvent } from './sync'
|
||||||
import { authCode, authConnect } from './auth'
|
import { authCode, authConnect } from './auth'
|
||||||
import { getAddress } from '../../utils'
|
|
||||||
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants_sync'
|
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants_sync'
|
||||||
import { getUserSpace, releaseUserSpace, getServerId, initServerInfo } from '../user'
|
import { getUserSpace, releaseUserSpace, getServerId, initServerInfo } from '../user'
|
||||||
import { createMsg2call } from 'message2call'
|
import { createMsg2call } from 'message2call'
|
||||||
@@ -10,6 +9,8 @@ import log from '../../log'
|
|||||||
import { sendServerStatus } from '@main/modules/winMain'
|
import { sendServerStatus } from '@main/modules/winMain'
|
||||||
import { decryptMsg, encryptMsg, generateCode as handleGenerateCode } from '../utils/tools'
|
import { decryptMsg, encryptMsg, generateCode as handleGenerateCode } from '../utils/tools'
|
||||||
import migrateData from '../../migrate'
|
import migrateData from '../../migrate'
|
||||||
|
import type { Socket } from 'node:net'
|
||||||
|
import { getAddress } from '@common/utils/nodejs'
|
||||||
|
|
||||||
|
|
||||||
let status: LX.Sync.ServerStatus = {
|
let status: LX.Sync.ServerStatus = {
|
||||||
@@ -25,7 +26,7 @@ let stopingServer = false
|
|||||||
let host = 'http://localhost'
|
let host = 'http://localhost'
|
||||||
|
|
||||||
const codeTools: {
|
const codeTools: {
|
||||||
timeout: NodeJS.Timer | null
|
timeout: NodeJS.Timeout | null
|
||||||
start: () => void
|
start: () => void
|
||||||
stop: () => void
|
stop: () => void
|
||||||
} = {
|
} = {
|
||||||
@@ -114,6 +115,7 @@ const authConnection = (req: http.IncomingMessage, callback: (err: string | null
|
|||||||
|
|
||||||
let wss: LX.Sync.Server.SocketServer | null
|
let wss: LX.Sync.Server.SocketServer | null
|
||||||
let httpServer: http.Server
|
let httpServer: http.Server
|
||||||
|
let sockets = new Set<Socket>()
|
||||||
|
|
||||||
function noop() {}
|
function noop() {}
|
||||||
function onSocketError(err: Error) {
|
function onSocketError(err: Error) {
|
||||||
@@ -290,6 +292,12 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
|
|||||||
console.log(error)
|
console.log(error)
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
|
httpServer.on('connection', (socket) => {
|
||||||
|
sockets.add(socket)
|
||||||
|
socket.once('close', () => {
|
||||||
|
sockets.delete(socket)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
httpServer.on('listening', () => {
|
httpServer.on('listening', () => {
|
||||||
const addr = httpServer.address()
|
const addr = httpServer.address()
|
||||||
@@ -321,6 +329,8 @@ const handleStopServer = async() => new Promise<void>((resolve, reject) => {
|
|||||||
}
|
}
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
|
for (const socket of sockets) socket.destroy()
|
||||||
|
sockets.clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
export const stopServer = async() => {
|
export const stopServer = async() => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { networkInterfaces } from 'node:os'
|
|
||||||
import { createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, constants } from 'node:crypto'
|
import { createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, constants } from 'node:crypto'
|
||||||
// import { join } from 'node:path'
|
// import { join } from 'node:path'
|
||||||
import zlib from 'node:zlib'
|
import zlib from 'node:zlib'
|
||||||
@@ -8,23 +7,6 @@ import type http from 'node:http'
|
|||||||
// import { getUserName } from '../user/data'
|
// import { getUserName } from '../user/data'
|
||||||
// import { saveClientKeyInfo } from './data'
|
// import { saveClientKeyInfo } from './data'
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
export const generateCode = (): string => {
|
export const generateCode = (): string => {
|
||||||
return Math.random().toString().substring(2, 8)
|
return Math.random().toString().substring(2, 8)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,10 @@
|
|||||||
import { createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, constants } from 'node:crypto'
|
import { createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, constants } from 'node:crypto'
|
||||||
import os, { networkInterfaces } from 'node:os'
|
import os from 'node:os'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import zlib from 'node:zlib'
|
import zlib from 'node:zlib'
|
||||||
import cp from 'node:child_process'
|
import cp from 'node:child_process'
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/75309339
|
// https://stackoverflow.com/a/75309339
|
||||||
export const getComputerName = () => {
|
export const getComputerName = () => {
|
||||||
let name: string | undefined
|
let name: string | undefined
|
||||||
@@ -40,6 +22,7 @@ export const getComputerName = () => {
|
|||||||
try {
|
try {
|
||||||
name = cp.execSync('hostnamectl --pretty').toString().trim()
|
name = cp.execSync('hostnamectl --pretty').toString().trim()
|
||||||
} catch {}
|
} catch {}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if (!name) name = os.hostname()
|
if (!name) name = os.hostname()
|
||||||
return name
|
return name
|
||||||
|
|||||||
+221
-44
@@ -1,17 +1,28 @@
|
|||||||
import { Tray, Menu, nativeImage } from 'electron'
|
import { Tray, Menu, nativeImage } from 'electron'
|
||||||
import { isWin } from '@common/utils'
|
import { isMac, isWin } from '@common/utils'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import {
|
import {
|
||||||
hideWindow as hideMainWindow,
|
hideWindow as hideMainWindow,
|
||||||
isExistWindow as isExistMainWindow,
|
isExistWindow as isExistMainWindow,
|
||||||
isShowWindow as isShowMainWindow,
|
isShowWindow as isShowMainWindow,
|
||||||
|
sendTaskbarButtonClick,
|
||||||
showWindow as showMainWindow,
|
showWindow as showMainWindow,
|
||||||
} from './winMain'
|
} from './winMain'
|
||||||
import { quitApp } from '@main/app'
|
import { quitApp } from '@main/app'
|
||||||
|
import { TRAY_AUTO_ID } from '@common/constants'
|
||||||
|
|
||||||
let tray: Electron.Tray | null
|
let tray: Electron.Tray | null
|
||||||
let isEnableTray: boolean = false
|
let isEnableTray: boolean = false
|
||||||
let themeId: number
|
let themeId: number
|
||||||
|
let isShowStatusBarLyric: boolean = false
|
||||||
|
|
||||||
|
const playerState = {
|
||||||
|
empty: false,
|
||||||
|
collect: false,
|
||||||
|
play: false,
|
||||||
|
next: true,
|
||||||
|
prev: true,
|
||||||
|
}
|
||||||
|
|
||||||
const watchConfigKeys = [
|
const watchConfigKeys = [
|
||||||
'desktopLyric.enable',
|
'desktopLyric.enable',
|
||||||
@@ -19,6 +30,7 @@ const watchConfigKeys = [
|
|||||||
'desktopLyric.isAlwaysOnTop',
|
'desktopLyric.isAlwaysOnTop',
|
||||||
'tray.themeId',
|
'tray.themeId',
|
||||||
'tray.enable',
|
'tray.enable',
|
||||||
|
'player.isShowStatusBarLyric',
|
||||||
'common.langId',
|
'common.langId',
|
||||||
] satisfies Array<keyof LX.AppSetting>
|
] satisfies Array<keyof LX.AppSetting>
|
||||||
|
|
||||||
@@ -42,17 +54,33 @@ const themeList = [
|
|||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
'en-us': {
|
'en-us': {
|
||||||
|
collect: 'Love',
|
||||||
|
uncollect: 'Unlove',
|
||||||
|
play: 'Play',
|
||||||
|
pause: 'Pause',
|
||||||
|
next: 'Next Song',
|
||||||
|
prev: 'Prev Song',
|
||||||
hide_win_main: 'Hide Main Window',
|
hide_win_main: 'Hide Main Window',
|
||||||
show_win_main: 'Show Main Window',
|
show_win_main: 'Show Main Window',
|
||||||
hide_win_lyric: 'Close desktop lyrics',
|
hide_win_lyric: 'Hide Lyric Window',
|
||||||
show_win_lyric: 'Open desktop lyrics',
|
show_win_lyric: 'Show Lyric Window',
|
||||||
lock_win_lyric: 'Lock desktop lyrics',
|
lock_win_lyric: 'Lock Lyric Window',
|
||||||
unlock_win_lyric: 'Unlock desktop lyrics',
|
unlock_win_lyric: 'Unlock Lyric Window',
|
||||||
top_win_lyric: 'Set top lyrics',
|
top_win_lyric: 'On-top Lyric Window',
|
||||||
untop_win_lyric: 'Cancel top lyrics',
|
untop_win_lyric: 'Un-top Lyric Window',
|
||||||
|
show_statusbar_lyric: 'Show Lyrics on Statusbar',
|
||||||
|
hide_statusbar_lyric: 'Hide Lyrics on Statusbar',
|
||||||
exit: 'Exit',
|
exit: 'Exit',
|
||||||
|
music_name: 'Title: ',
|
||||||
|
music_singer: 'Artist: ',
|
||||||
},
|
},
|
||||||
'zh-cn': {
|
'zh-cn': {
|
||||||
|
collect: '收藏',
|
||||||
|
uncollect: '取消收藏',
|
||||||
|
play: '播放',
|
||||||
|
pause: '暂停',
|
||||||
|
next: '下一曲',
|
||||||
|
prev: '上一曲',
|
||||||
hide_win_main: '隐藏主界面',
|
hide_win_main: '隐藏主界面',
|
||||||
show_win_main: '显示主界面',
|
show_win_main: '显示主界面',
|
||||||
hide_win_lyric: '关闭桌面歌词',
|
hide_win_lyric: '关闭桌面歌词',
|
||||||
@@ -61,18 +89,32 @@ const messages = {
|
|||||||
unlock_win_lyric: '解锁桌面歌词',
|
unlock_win_lyric: '解锁桌面歌词',
|
||||||
top_win_lyric: '置顶歌词',
|
top_win_lyric: '置顶歌词',
|
||||||
untop_win_lyric: '取消置顶',
|
untop_win_lyric: '取消置顶',
|
||||||
|
show_statusbar_lyric: '显示状态栏歌词',
|
||||||
|
hide_statusbar_lyric: '隐藏状态栏歌词',
|
||||||
exit: '退出',
|
exit: '退出',
|
||||||
|
music_name: '歌曲名: ',
|
||||||
|
music_singer: '艺术家: ',
|
||||||
},
|
},
|
||||||
'zh-tw': {
|
'zh-tw': {
|
||||||
hide_win_main: '隱藏主界面',
|
collect: '收藏',
|
||||||
show_win_main: '顯示主界面',
|
uncollect: '取消收藏',
|
||||||
hide_win_lyric: '關閉桌面歌詞',
|
play: '播放',
|
||||||
show_win_lyric: '開啟桌面歌詞',
|
pause: '暫停',
|
||||||
lock_win_lyric: '鎖定桌面歌詞',
|
next: '下一曲',
|
||||||
unlock_win_lyric: '解鎖桌面歌詞',
|
prev: '上一曲',
|
||||||
top_win_lyric: '置頂歌詞',
|
hide_win_main: '隱藏軟體視窗',
|
||||||
untop_win_lyric: '取消置頂',
|
show_win_main: '顯示軟體視窗',
|
||||||
|
hide_win_lyric: '關閉歌詞視窗',
|
||||||
|
show_win_lyric: '開啟歌詞視窗',
|
||||||
|
lock_win_lyric: '鎖定歌詞視窗',
|
||||||
|
unlock_win_lyric: '解鎖歌詞視窗',
|
||||||
|
top_win_lyric: '置頂歌詞視窗',
|
||||||
|
untop_win_lyric: '取消置頂歌詞視窗',
|
||||||
|
show_statusbar_lyric: '顯示狀態列歌詞',
|
||||||
|
hide_statusbar_lyric: '隱藏狀態列歌詞',
|
||||||
exit: '退出',
|
exit: '退出',
|
||||||
|
music_name: '標題: ',
|
||||||
|
music_singer: '演出者: ',
|
||||||
},
|
},
|
||||||
} as const
|
} as const
|
||||||
type Messages = typeof messages
|
type Messages = typeof messages
|
||||||
@@ -90,19 +132,23 @@ const i18n = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getIconPath = (id: number) => {
|
||||||
|
let theme = id == TRAY_AUTO_ID
|
||||||
|
? global.lx.theme.shouldUseDarkColors
|
||||||
|
? themeList[0] : themeList[2]
|
||||||
|
: themeList.find(item => item.id === id) ?? themeList[0]
|
||||||
|
return path.join(global.staticPath, 'images/tray', theme.fileName + (isWin ? '.ico' : '.png'))
|
||||||
|
}
|
||||||
|
|
||||||
export const createTray = () => {
|
export const createTray = () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||||
if ((tray && !tray.isDestroyed()) || !global.lx.appSetting['tray.enable']) return
|
if ((tray && !tray.isDestroyed()) || !global.lx.appSetting['tray.enable']) return
|
||||||
|
|
||||||
themeId = global.lx.appSetting['tray.themeId']
|
|
||||||
let theme = themeList.find(item => item.id === themeId) ?? themeList[0]
|
|
||||||
const iconPath = path.join(global.staticPath, 'images/tray', theme.fileName + '.png')
|
|
||||||
|
|
||||||
// 托盘
|
// 托盘
|
||||||
tray = new Tray(nativeImage.createFromPath(iconPath))
|
tray = new Tray(nativeImage.createFromPath(getIconPath(global.lx.appSetting['tray.themeId'])))
|
||||||
|
|
||||||
tray.setToolTip('LX Music')
|
// tray.setToolTip('LX Music')
|
||||||
createMenu()
|
// createMenu()
|
||||||
tray.setIgnoreDoubleClickEvents(true)
|
tray.setIgnoreDoubleClickEvents(true)
|
||||||
tray.on('click', () => {
|
tray.on('click', () => {
|
||||||
showMainWindow()
|
showMainWindow()
|
||||||
@@ -113,32 +159,58 @@ export const destroyTray = () => {
|
|||||||
if (!tray) return
|
if (!tray) return
|
||||||
tray.destroy()
|
tray.destroy()
|
||||||
isEnableTray = false
|
isEnableTray = false
|
||||||
|
isShowStatusBarLyric = false
|
||||||
tray = null
|
tray = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUpdateConfig = (config: any) => {
|
const handleUpdateConfig = (setting: Partial<LX.AppSetting>) => {
|
||||||
global.lx.event_app.update_config(config)
|
global.lx.event_app.update_config(setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createPlayerMenu = () => {
|
||||||
|
let menu: Electron.MenuItemConstructorOptions[] = []
|
||||||
|
menu.push(playerState.play ? {
|
||||||
|
label: i18n.getMessage('pause'),
|
||||||
|
click() {
|
||||||
|
sendTaskbarButtonClick('pause')
|
||||||
|
},
|
||||||
|
} : {
|
||||||
|
label: i18n.getMessage('play'),
|
||||||
|
click() {
|
||||||
|
sendTaskbarButtonClick('play')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
menu.push({
|
||||||
|
label: i18n.getMessage('prev'),
|
||||||
|
click() {
|
||||||
|
sendTaskbarButtonClick('prev')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
menu.push({
|
||||||
|
label: i18n.getMessage('next'),
|
||||||
|
click() {
|
||||||
|
sendTaskbarButtonClick('next')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
menu.push(playerState.collect ? {
|
||||||
|
label: i18n.getMessage('uncollect'),
|
||||||
|
click() {
|
||||||
|
sendTaskbarButtonClick('unCollect')
|
||||||
|
},
|
||||||
|
} : {
|
||||||
|
label: i18n.getMessage('collect'),
|
||||||
|
click() {
|
||||||
|
sendTaskbarButtonClick('collect')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return menu
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createMenu = () => {
|
export const createMenu = () => {
|
||||||
if (!tray) return
|
if (!tray) return
|
||||||
let menu = []
|
let menu: Electron.MenuItemConstructorOptions[] = createPlayerMenu()
|
||||||
if (isExistMainWindow()) {
|
if (playerState.empty) for (const m of menu) m.enabled = false
|
||||||
const isShow = isShowMainWindow()
|
menu.push({ type: 'separator' })
|
||||||
menu.push(isShow
|
|
||||||
? {
|
|
||||||
label: i18n.getMessage('hide_win_main'),
|
|
||||||
click() {
|
|
||||||
hideMainWindow()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
label: i18n.getMessage('show_win_main'),
|
|
||||||
click() {
|
|
||||||
showMainWindow()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
menu.push(global.lx.appSetting['desktopLyric.enable']
|
menu.push(global.lx.appSetting['desktopLyric.enable']
|
||||||
? {
|
? {
|
||||||
label: i18n.getMessage('hide_win_lyric'),
|
label: i18n.getMessage('hide_win_lyric'),
|
||||||
@@ -178,6 +250,39 @@ export const createMenu = () => {
|
|||||||
handleUpdateConfig({ 'desktopLyric.isAlwaysOnTop': true })
|
handleUpdateConfig({ 'desktopLyric.isAlwaysOnTop': true })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
if (isMac) {
|
||||||
|
menu.push({ type: 'separator' })
|
||||||
|
menu.push(isShowStatusBarLyric
|
||||||
|
? {
|
||||||
|
label: i18n.getMessage('hide_statusbar_lyric'),
|
||||||
|
click() {
|
||||||
|
handleUpdateConfig({ 'player.isShowStatusBarLyric': false })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
label: i18n.getMessage('show_statusbar_lyric'),
|
||||||
|
click() {
|
||||||
|
handleUpdateConfig({ 'player.isShowStatusBarLyric': true })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
menu.push({ type: 'separator' })
|
||||||
|
if (isExistMainWindow()) {
|
||||||
|
const isShow = isShowMainWindow()
|
||||||
|
menu.push(isShow
|
||||||
|
? {
|
||||||
|
label: i18n.getMessage('hide_win_main'),
|
||||||
|
click() {
|
||||||
|
hideMainWindow()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
label: i18n.getMessage('show_win_main'),
|
||||||
|
click() {
|
||||||
|
showMainWindow()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
menu.push({
|
menu.push({
|
||||||
label: i18n.getMessage('exit'),
|
label: i18n.getMessage('exit'),
|
||||||
click() {
|
click() {
|
||||||
@@ -190,9 +295,29 @@ export const createMenu = () => {
|
|||||||
|
|
||||||
export const setTrayImage = (themeId: number) => {
|
export const setTrayImage = (themeId: number) => {
|
||||||
if (!tray) return
|
if (!tray) return
|
||||||
let theme = themeList.find(item => item.id === themeId) ?? themeList[0]
|
tray.setImage(nativeImage.createFromPath(getIconPath(themeId)))
|
||||||
const iconPath = path.join(global.staticPath, 'images/tray', theme.fileName + '.png')
|
}
|
||||||
tray.setImage(nativeImage.createFromPath(iconPath))
|
|
||||||
|
const setLyric = (lyricLineText?: string) => {
|
||||||
|
if (isShowStatusBarLyric && tray && lyricLineText != null) {
|
||||||
|
tray.setTitle(lyricLineText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultTip = 'LX Music'
|
||||||
|
const setTip = () => {
|
||||||
|
if (!tray) return
|
||||||
|
|
||||||
|
let name = global.lx.player_status.name
|
||||||
|
let tip: string
|
||||||
|
if (name) {
|
||||||
|
if (name.length > 20) name = name.substring(0, 20) + '...'
|
||||||
|
let singer = global.lx.player_status.singer
|
||||||
|
if (singer?.length > 20) singer = singer.substring(0, 20) + '...'
|
||||||
|
|
||||||
|
tip = `${defaultTip}\n${i18n.getMessage('music_name')}${name}${singer ? `\n${i18n.getMessage('music_singer')}${singer}` : ''}`
|
||||||
|
} else tip = defaultTip
|
||||||
|
tray.setToolTip(tip)
|
||||||
}
|
}
|
||||||
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
@@ -204,6 +329,15 @@ const init = () => {
|
|||||||
isEnableTray = global.lx.appSetting['tray.enable']
|
isEnableTray = global.lx.appSetting['tray.enable']
|
||||||
global.lx.appSetting['tray.enable'] ? createTray() : destroyTray()
|
global.lx.appSetting['tray.enable'] ? createTray() : destroyTray()
|
||||||
}
|
}
|
||||||
|
if (isShowStatusBarLyric !== global.lx.appSetting['player.isShowStatusBarLyric']) {
|
||||||
|
isShowStatusBarLyric = global.lx.appSetting['player.isShowStatusBarLyric']
|
||||||
|
if (isShowStatusBarLyric) {
|
||||||
|
setLyric(global.lx.player_status.lyricLineText)
|
||||||
|
} else {
|
||||||
|
tray?.setTitle('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTip()
|
||||||
createMenu()
|
createMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,4 +375,47 @@ export default () => {
|
|||||||
i18n.setLang(global.lx.appSetting['common.langId'])
|
i18n.setLang(global.lx.appSetting['common.langId'])
|
||||||
init()
|
init()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
global.lx.event_app.on('system_theme_change', () => {
|
||||||
|
if (global.lx.appSetting['tray.themeId'] != TRAY_AUTO_ID) return
|
||||||
|
setTrayImage(global.lx.appSetting['tray.themeId'])
|
||||||
|
})
|
||||||
|
|
||||||
|
global.lx.event_app.on('player_status', (status) => {
|
||||||
|
let updated = false
|
||||||
|
if (status.status) {
|
||||||
|
switch (status.status) {
|
||||||
|
case 'paused':
|
||||||
|
playerState.play = false
|
||||||
|
playerState.empty &&= false
|
||||||
|
setLyric('')
|
||||||
|
break
|
||||||
|
case 'error':
|
||||||
|
playerState.play = false
|
||||||
|
playerState.empty &&= false
|
||||||
|
setLyric('')
|
||||||
|
break
|
||||||
|
case 'playing':
|
||||||
|
playerState.play = true
|
||||||
|
playerState.empty &&= false
|
||||||
|
setLyric(global.lx.player_status.lyricLineText)
|
||||||
|
break
|
||||||
|
case 'stoped':
|
||||||
|
playerState.play &&= false
|
||||||
|
playerState.empty = true
|
||||||
|
setLyric('')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
updated = true
|
||||||
|
} else {
|
||||||
|
setLyric(status.lyricLineText)
|
||||||
|
}
|
||||||
|
if (status.name != null) setTip()
|
||||||
|
if (status.singer != null) setTip()
|
||||||
|
if (status.collect != null) {
|
||||||
|
playerState.collect = status.collect
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
if (updated) init()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { BrowserWindow } from 'electron'
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { openDevTools as handleOpenDevTools } from '@main/utils'
|
import { openDevTools as handleOpenDevTools } from '@main/utils'
|
||||||
import { encodePath } from '@common/utils/electron'
|
|
||||||
import USER_API_RENDERER_EVENT_NAME from './rendererEvent/name'
|
import USER_API_RENDERER_EVENT_NAME from './rendererEvent/name'
|
||||||
import { getScript } from './utils'
|
import { getScript } from './utils'
|
||||||
|
|
||||||
@@ -58,15 +57,15 @@ const winEvent = () => {
|
|||||||
|
|
||||||
export const createWindow = async(userApi: LX.UserApi.UserApiInfo) => {
|
export const createWindow = async(userApi: LX.UserApi.UserApiInfo) => {
|
||||||
await closeWindow()
|
await closeWindow()
|
||||||
dir ??= process.env.NODE_ENV !== 'production' ? webpackUserApiPath : path.join(encodePath(__dirname), 'userApi')
|
dir ??= process.env.NODE_ENV !== 'production' ? webpackUserApiPath : path.join(__dirname, 'userApi')
|
||||||
|
|
||||||
if (!html) {
|
if (!html) {
|
||||||
// eslint-disable-next-line require-atomic-updates
|
// eslint-disable-next-line require-atomic-updates
|
||||||
html = await fs.promises.readFile(path.join(dir, 'renderer/user-api.html'), 'utf8')
|
html = await fs.promises.readFile(path.join(dir, 'renderer/user-api.html'), 'utf8')
|
||||||
}
|
}
|
||||||
const preloadUrl = process.env.NODE_ENV !== 'production'
|
const preloadUrl = process.env.NODE_ENV !== 'production'
|
||||||
? `${path.join(encodePath(__dirname), '../dist/user-api-preload.js')}`
|
? `${path.join(__dirname, '../dist/user-api-preload.js')}`
|
||||||
: `${path.join(encodePath(__dirname), 'user-api-preload.js')}`
|
: `${path.join(__dirname, 'user-api-preload.js')}`
|
||||||
// console.log(preloadUrl)
|
// console.log(preloadUrl)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,6 +77,7 @@ export const createWindow = async(userApi: LX.UserApi.UserApiInfo) => {
|
|||||||
minimizable: false,
|
minimizable: false,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
fullscreenable: false,
|
fullscreenable: false,
|
||||||
|
hasShadow: false,
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ const matchInfo = (scriptInfo: string) => {
|
|||||||
for (const [key, len] of Object.entries(INFO_NAMES) as Array<{ [K in keyof INFO_NAMES_Type]: [K, INFO_NAMES_Type[K]] }[keyof INFO_NAMES_Type]>) {
|
for (const [key, len] of Object.entries(INFO_NAMES) as Array<{ [K in keyof INFO_NAMES_Type]: [K, INFO_NAMES_Type[K]] }[keyof INFO_NAMES_Type]>) {
|
||||||
infos[key] ||= ''
|
infos[key] ||= ''
|
||||||
if (infos[key] == null) infos[key] = ''
|
if (infos[key] == null) infos[key] = ''
|
||||||
else if (infos[key]!.length > len) infos[key] = infos[key]!.substring(0, len) + '...'
|
else if (infos[key].length > len) infos[key] = infos[key].substring(0, len) + '...'
|
||||||
}
|
}
|
||||||
|
|
||||||
return infos as Record<keyof typeof INFO_NAMES, string>
|
return infos as Record<keyof typeof INFO_NAMES, string>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { BrowserWindow } from 'electron'
|
import { BrowserWindow } from 'electron'
|
||||||
import { debounce, isLinux, isWin } from '@common/utils'
|
import { debounce, getPlatform, isLinux, isWin } from '@common/utils'
|
||||||
import { initWindowSize } from './utils'
|
import { initWindowSize } from './utils'
|
||||||
import { mainSend } from '@common/mainIpc'
|
import { mainSend } from '@common/mainIpc'
|
||||||
import { encodePath } from '@common/utils/electron'
|
import { encodePath } from '@common/utils/electron'
|
||||||
@@ -55,6 +55,7 @@ const winEvent = () => {
|
|||||||
browserWindow.on('resize', () => {
|
browserWindow.on('resize', () => {
|
||||||
// bounds = browserWindow.getBounds()
|
// bounds = browserWindow.getBounds()
|
||||||
// console.log(bounds)
|
// console.log(bounds)
|
||||||
|
isWinBoundsUpdateing = true
|
||||||
const bounds = browserWindow!.getBounds()
|
const bounds = browserWindow!.getBounds()
|
||||||
saveBoundsConfig({
|
saveBoundsConfig({
|
||||||
'desktopLyric.x': bounds.x,
|
'desktopLyric.x': bounds.x,
|
||||||
@@ -119,9 +120,10 @@ export const createWindow = () => {
|
|||||||
useContentSize: true,
|
useContentSize: true,
|
||||||
frame: false,
|
frame: false,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
|
hasShadow: false,
|
||||||
// enableRemoteModule: false,
|
// enableRemoteModule: false,
|
||||||
// icon: join(global.__static, isWin ? 'icons/256x256.ico' : 'icons/512x512.png'),
|
// icon: join(global.__static, isWin ? 'icons/256x256.ico' : 'icons/512x512.png'),
|
||||||
resizable: false,
|
resizable: isWin,
|
||||||
minimizable: false,
|
minimizable: false,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
fullscreenable: false,
|
fullscreenable: false,
|
||||||
@@ -140,10 +142,11 @@ export const createWindow = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const winURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:9081/lyric.html' : `file://${path.join(encodePath(__dirname), 'lyric.html')}`
|
const winURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:9081/lyric.html' : `file://${path.join(encodePath(__dirname), 'lyric.html')}`
|
||||||
void browserWindow.loadURL(winURL + `?dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`)
|
void browserWindow.loadURL(winURL + `?os=${getPlatform()}&dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`)
|
||||||
|
|
||||||
winEvent()
|
winEvent()
|
||||||
// browserWindow.webContents.openDevTools()
|
// browserWindow.webContents.openDevTools()
|
||||||
|
global.lx.event_app.desktop_lyric_window_created(browserWindow)
|
||||||
}
|
}
|
||||||
export const isExistWindow = (): boolean => !!browserWindow
|
export const isExistWindow = (): boolean => !!browserWindow
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import initRendererEvent, { handleKeyDown, hotKeyConfigUpdate } from './rendererEvent'
|
import initRendererEvent, { handleKeyDown, hotKeyConfigUpdate } from './rendererEvent'
|
||||||
|
|
||||||
import { APP_EVENT_NAMES } from '@common/constants'
|
import { APP_EVENT_NAMES } from '@common/constants'
|
||||||
import { createWindow, minimize, toggleHide, toggleMinimize } from './main'
|
import { createWindow, minimize, setProgressBar, setProxy, setThumbarButtons, toggleHide, toggleMinimize } from './main'
|
||||||
import initUpdate from './autoUpdate'
|
import initUpdate from './autoUpdate'
|
||||||
import { HOTKEY_COMMON } from '@common/hotKey'
|
import { HOTKEY_COMMON } from '@common/hotKey'
|
||||||
import { quitApp } from '@main/app'
|
import { quitApp } from '@main/app'
|
||||||
@@ -38,6 +38,80 @@ export default () => {
|
|||||||
global.lx.event_app.on('app_inited', () => {
|
global.lx.event_app.on('app_inited', () => {
|
||||||
createWindow()
|
createWindow()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const keys = (['status', 'collect'] as const) satisfies Array<keyof LX.Player.Status>
|
||||||
|
const taskBarButtonFlags: LX.TaskBarButtonFlags = {
|
||||||
|
empty: true,
|
||||||
|
collect: false,
|
||||||
|
play: false,
|
||||||
|
next: true,
|
||||||
|
prev: true,
|
||||||
|
}
|
||||||
|
const progressStatus = {
|
||||||
|
progress: -1,
|
||||||
|
status: 'none' as Electron.ProgressBarOptions['mode'],
|
||||||
|
}
|
||||||
|
let showProgress = global.lx.appSetting['player.isShowTaskProgess']
|
||||||
|
global.lx.event_app.on('player_status', (status) => {
|
||||||
|
if (status.status) {
|
||||||
|
switch (status.status) {
|
||||||
|
case 'paused':
|
||||||
|
taskBarButtonFlags.play = false
|
||||||
|
taskBarButtonFlags.empty &&= false
|
||||||
|
progressStatus.status = 'paused'
|
||||||
|
break
|
||||||
|
case 'error':
|
||||||
|
taskBarButtonFlags.play = false
|
||||||
|
taskBarButtonFlags.empty &&= false
|
||||||
|
progressStatus.status = 'error'
|
||||||
|
break
|
||||||
|
case 'playing':
|
||||||
|
taskBarButtonFlags.play = true
|
||||||
|
taskBarButtonFlags.empty &&= false
|
||||||
|
progressStatus.status = 'normal'
|
||||||
|
break
|
||||||
|
case 'stoped':
|
||||||
|
taskBarButtonFlags.play &&= false
|
||||||
|
taskBarButtonFlags.empty = true
|
||||||
|
progressStatus.status = 'none'
|
||||||
|
progressStatus.progress = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (showProgress) {
|
||||||
|
setProgressBar(progressStatus.progress, {
|
||||||
|
mode: progressStatus.status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keys.some(k => status[k] != null)) {
|
||||||
|
if (status.collect != null) taskBarButtonFlags.collect = status.collect
|
||||||
|
setThumbarButtons(taskBarButtonFlags)
|
||||||
|
}
|
||||||
|
if (showProgress && status.progress != null) {
|
||||||
|
const progress = global.lx.player_status.duration ? status.progress / global.lx.player_status.duration : 0
|
||||||
|
if (progress.toFixed(2) != progressStatus.progress.toFixed(2)) {
|
||||||
|
progressStatus.progress = progress < 0.01 ? 0.01 : progress
|
||||||
|
setProgressBar(progressStatus.progress, {
|
||||||
|
mode: progressStatus.status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
global.lx.event_app.on('updated_config', (keys, setting) => {
|
||||||
|
if (keys.includes('player.isShowTaskProgess')) {
|
||||||
|
showProgress = setting['player.isShowTaskProgess']!
|
||||||
|
if (showProgress) {
|
||||||
|
setProgressBar(progressStatus.progress, {
|
||||||
|
mode: progressStatus.status,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setProgressBar(-1, { mode: 'none' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keys.includes('network.proxy.enable') || (global.lx.appSetting['network.proxy.enable'] && keys.some(k => k.includes('network.proxy.')))) {
|
||||||
|
setProxy()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from './main'
|
export * from './main'
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { BrowserWindow, dialog, session } from 'electron'
|
import { BrowserWindow, dialog, session } from 'electron'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { createTaskBarButtons, getWindowSizeInfo } from './utils'
|
import { createTaskBarButtons, getWindowSizeInfo } from './utils'
|
||||||
import { isLinux, isWin } from '@common/utils'
|
import { getPlatform, isLinux, isWin } from '@common/utils'
|
||||||
import { openDevTools as handleOpenDevTools } from '@main/utils'
|
import { getProxy, openDevTools as handleOpenDevTools } from '@main/utils'
|
||||||
import { mainSend } from '@common/mainIpc'
|
import { mainSend } from '@common/mainIpc'
|
||||||
import { sendFocus, sendTaskbarButtonClick } from './rendererEvent'
|
import { sendFocus, sendTaskbarButtonClick } from './rendererEvent'
|
||||||
import { encodePath } from '@common/utils/electron'
|
import { encodePath } from '@common/utils/electron'
|
||||||
@@ -13,14 +13,13 @@ const winEvent = () => {
|
|||||||
if (!browserWindow) return
|
if (!browserWindow) return
|
||||||
|
|
||||||
browserWindow.on('close', event => {
|
browserWindow.on('close', event => {
|
||||||
if (global.lx.isSkipTrayQuit || !global.lx.appSetting['tray.enable'] || (!isWin && !global.lx.isTrafficLightClose)) {
|
if (global.lx.isSkipTrayQuit || !global.lx.appSetting['tray.enable']) {
|
||||||
browserWindow!.setProgressBar(-1)
|
browserWindow!.setProgressBar(-1)
|
||||||
// global.lx.mainWindowClosed = true
|
// global.lx.mainWindowClosed = true
|
||||||
global.lx.event_app.main_window_close()
|
global.lx.event_app.main_window_close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
global.lx.isTrafficLightClose &&= false
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
browserWindow!.hide()
|
browserWindow!.hide()
|
||||||
})
|
})
|
||||||
@@ -66,6 +65,12 @@ export const createWindow = () => {
|
|||||||
|
|
||||||
const { shouldUseDarkColors, theme } = global.lx.theme
|
const { shouldUseDarkColors, theme } = global.lx.theme
|
||||||
const ses = session.fromPartition('persist:win-main')
|
const ses = session.fromPartition('persist:win-main')
|
||||||
|
const proxy = getProxy()
|
||||||
|
if (proxy) {
|
||||||
|
void ses.setProxy({
|
||||||
|
proxyRules: `http://${proxy.host}:${proxy.port}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initial window options
|
* Initial window options
|
||||||
@@ -76,6 +81,7 @@ export const createWindow = () => {
|
|||||||
width: windowSizeInfo.width,
|
width: windowSizeInfo.width,
|
||||||
frame: false,
|
frame: false,
|
||||||
transparent: !global.envParams.cmdParams.dt,
|
transparent: !global.envParams.cmdParams.dt,
|
||||||
|
hasShadow: global.envParams.cmdParams.dt,
|
||||||
// enableRemoteModule: false,
|
// enableRemoteModule: false,
|
||||||
// icon: join(global.__static, isWin ? 'icons/256x256.ico' : 'icons/512x512.png'),
|
// icon: join(global.__static, isWin ? 'icons/256x256.ico' : 'icons/512x512.png'),
|
||||||
resizable: false,
|
resizable: false,
|
||||||
@@ -103,7 +109,7 @@ export const createWindow = () => {
|
|||||||
browserWindow = new BrowserWindow(options)
|
browserWindow = new BrowserWindow(options)
|
||||||
|
|
||||||
const winURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:9080' : `file://${path.join(encodePath(__dirname), 'index.html')}`
|
const winURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:9080' : `file://${path.join(encodePath(__dirname), 'index.html')}`
|
||||||
void browserWindow.loadURL(winURL + `?dt=${!!global.envParams.cmdParams.dt}&dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`)
|
void browserWindow.loadURL(winURL + `?os=${getPlatform()}&dt=${!!global.envParams.cmdParams.dt}&dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`)
|
||||||
|
|
||||||
winEvent()
|
winEvent()
|
||||||
|
|
||||||
@@ -111,6 +117,7 @@ export const createWindow = () => {
|
|||||||
|
|
||||||
// global.lx.mainWindowClosed = false
|
// global.lx.mainWindowClosed = false
|
||||||
// browserWindow.webContents.openDevTools()
|
// browserWindow.webContents.openDevTools()
|
||||||
|
global.lx.event_app.main_window_created(browserWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isExistWindow = (): boolean => !!browserWindow
|
export const isExistWindow = (): boolean => !!browserWindow
|
||||||
@@ -124,6 +131,21 @@ export const closeWindow = () => {
|
|||||||
browserWindow.close()
|
browserWindow.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setProxy = () => {
|
||||||
|
if (!browserWindow) return
|
||||||
|
const proxy = getProxy()
|
||||||
|
if (proxy) {
|
||||||
|
void browserWindow.webContents.session.setProxy({
|
||||||
|
proxyRules: `http://${proxy.host}:${proxy.port}`,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
void browserWindow.webContents.session.setProxy({
|
||||||
|
proxyRules: '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const sendEvent = <T = any>(name: string, params?: T) => {
|
export const sendEvent = <T = any>(name: string, params?: T) => {
|
||||||
if (!browserWindow) return
|
if (!browserWindow) return
|
||||||
mainSend(browserWindow, name, params)
|
mainSend(browserWindow, name, params)
|
||||||
|
|||||||
@@ -14,10 +14,8 @@ import {
|
|||||||
getCacheSize,
|
getCacheSize,
|
||||||
toggleDevTools,
|
toggleDevTools,
|
||||||
setWindowBounds,
|
setWindowBounds,
|
||||||
setProgressBar,
|
|
||||||
setIgnoreMouseEvents,
|
setIgnoreMouseEvents,
|
||||||
// setThumbnailClip,
|
// setThumbnailClip,
|
||||||
setThumbarButtons,
|
|
||||||
toggleMinimize,
|
toggleMinimize,
|
||||||
toggleHide,
|
toggleHide,
|
||||||
showSelectDialog,
|
showSelectDialog,
|
||||||
@@ -63,7 +61,6 @@ export default () => {
|
|||||||
app.exit(0)
|
app.exit(0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
global.lx.isTrafficLightClose = true
|
|
||||||
closeWindow()
|
closeWindow()
|
||||||
})
|
})
|
||||||
// 全屏
|
// 全屏
|
||||||
@@ -106,13 +103,6 @@ export default () => {
|
|||||||
setWindowBounds(params)
|
setWindowBounds(params)
|
||||||
})
|
})
|
||||||
|
|
||||||
mainOn<LX.Player.ProgressBarOptions>(WIN_MAIN_RENDERER_EVENT_NAME.progress, ({ params }) => {
|
|
||||||
// console.log(params)
|
|
||||||
setProgressBar(params.progress, {
|
|
||||||
mode: params.mode ?? 'normal',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
mainOn<boolean>(WIN_MAIN_RENDERER_EVENT_NAME.set_ignore_mouse_events, ({ params: isIgnored }) => {
|
mainOn<boolean>(WIN_MAIN_RENDERER_EVENT_NAME.set_ignore_mouse_events, ({ params: isIgnored }) => {
|
||||||
isIgnored
|
isIgnored
|
||||||
? setIgnoreMouseEvents(isIgnored, { forward: true })
|
? setIgnoreMouseEvents(isIgnored, { forward: true })
|
||||||
@@ -123,8 +113,9 @@ export default () => {
|
|||||||
// return setThumbnailClip(params)
|
// return setThumbnailClip(params)
|
||||||
// })
|
// })
|
||||||
|
|
||||||
mainOn<LX.TaskBarButtonFlags>(WIN_MAIN_RENDERER_EVENT_NAME.player_action_set_buttons, ({ params }) => {
|
mainOn<LX.Player.Status>(WIN_MAIN_RENDERER_EVENT_NAME.player_status, ({ params }) => {
|
||||||
setThumbarButtons(params)
|
// setThumbarButtons(params)
|
||||||
|
global.lx.event_app.player_status(params)
|
||||||
})
|
})
|
||||||
|
|
||||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.inited, () => {
|
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.inited, () => {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import data from './data'
|
|||||||
import music from './music'
|
import music from './music'
|
||||||
import download from './download'
|
import download from './download'
|
||||||
import soundEffect from './soundEffect'
|
import soundEffect from './soundEffect'
|
||||||
|
import openAPI from './openAPI'
|
||||||
import { sendEvent } from '../main'
|
import { sendEvent } from '../main'
|
||||||
|
|
||||||
export * from './app'
|
export * from './app'
|
||||||
@@ -37,6 +38,7 @@ export default () => {
|
|||||||
music()
|
music()
|
||||||
download()
|
download()
|
||||||
soundEffect()
|
soundEffect()
|
||||||
|
openAPI()
|
||||||
|
|
||||||
global.lx.event_app.on('updated_config', (keys, setting) => {
|
global.lx.event_app.on('updated_config', (keys, setting) => {
|
||||||
sendConfigChange(setting)
|
sendConfigChange(setting)
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ const decodeLyric = async(buf: Buffer, isGetLyricx: boolean) => {
|
|||||||
// const isLyric = info.includes('\r\nlrcx=0\r\n')
|
// const isLyric = info.includes('\r\nlrcx=0\r\n')
|
||||||
if (buf.toString('utf8', 0, 10) != 'tp=content') return ''
|
if (buf.toString('utf8', 0, 10) != 'tp=content') return ''
|
||||||
// const index = buf.indexOf('\r\n\r\n') + 4
|
// const index = buf.indexOf('\r\n\r\n') + 4
|
||||||
const lrcData = await handleInflate(buf.slice(buf.indexOf('\r\n\r\n') + 4))
|
const lrcData = await handleInflate(buf.subarray(buf.indexOf('\r\n\r\n') + 4))
|
||||||
|
|
||||||
if (!isGetLyricx) return iconv.decode(lrcData, 'gb18030')
|
if (!isGetLyricx) return iconv.decode(lrcData, 'gb18030')
|
||||||
|
|
||||||
const buf_str = Buffer.from(lrcData.toString(), 'base64')
|
const buf_str = Buffer.from(lrcData.toString(), 'base64')
|
||||||
const buf_str_len = buf_str.length
|
const buf_str_len = buf_str.length
|
||||||
const output = new Uint16Array(buf_str_len)
|
const output = new Uint8Array(buf_str_len)
|
||||||
let i = 0
|
let i = 0
|
||||||
while (i < buf_str_len) {
|
while (i < buf_str_len) {
|
||||||
let j = 0
|
let j = 0
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { mainHandle } from '@common/mainIpc'
|
||||||
|
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||||
|
import {
|
||||||
|
startServer,
|
||||||
|
stopServer,
|
||||||
|
getStatus,
|
||||||
|
} from '@main/modules/openApi'
|
||||||
|
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
mainHandle<LX.OpenAPI.Actions, any>(WIN_MAIN_RENDERER_EVENT_NAME.open_api_action, async({ params: data }) => {
|
||||||
|
switch (data.action) {
|
||||||
|
case 'enable':
|
||||||
|
return data.data.enable ? await startServer(parseInt(data.data.port), data.data.bindLan) : await stopServer()
|
||||||
|
case 'status': return getStatus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Vendored
+2
-4
@@ -5,16 +5,13 @@ import { type DislikeType, type AppType, type ListType } from '@main/event'
|
|||||||
import { type DBSeriveTypes } from '@main/worker/utils'
|
import { type DBSeriveTypes } from '@main/worker/utils'
|
||||||
|
|
||||||
interface Lx {
|
interface Lx {
|
||||||
|
inited: boolean
|
||||||
appSetting: LX.AppSetting
|
appSetting: LX.AppSetting
|
||||||
hotKey: {
|
hotKey: {
|
||||||
enable: boolean
|
enable: boolean
|
||||||
config: LX.HotKeyConfigAll
|
config: LX.HotKeyConfigAll
|
||||||
state: LX.HotKeyState
|
state: LX.HotKeyState
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* 是否红绿灯关闭
|
|
||||||
*/
|
|
||||||
isTrafficLightClose: boolean
|
|
||||||
/**
|
/**
|
||||||
* 是否跳过托盘退出
|
* 是否跳过托盘退出
|
||||||
*/
|
*/
|
||||||
@@ -30,6 +27,7 @@ interface Lx {
|
|||||||
dbService: DBSeriveTypes
|
dbService: DBSeriveTypes
|
||||||
}
|
}
|
||||||
theme: LX.ThemeSetting
|
theme: LX.ThemeSetting
|
||||||
|
player_status: LX.Player.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
Vendored
+1
@@ -14,3 +14,4 @@ import '@common/types/ipc_main'
|
|||||||
import '@common/types/sound_effect'
|
import '@common/types/sound_effect'
|
||||||
import '@common/types/dislike_list'
|
import '@common/types/dislike_list'
|
||||||
import '@common/types/dislike_list_sync'
|
import '@common/types/dislike_list_sync'
|
||||||
|
import '@common/types/open_api'
|
||||||
|
|||||||
+30
-1
@@ -290,6 +290,35 @@ export const setPowerSaveBlocker = (enabled: boolean) => {
|
|||||||
powerSaveBlockerId = powerSaveBlocker.start('prevent-app-suspension')
|
powerSaveBlockerId = powerSaveBlocker.start('prevent-app-suspension')
|
||||||
} else {
|
} else {
|
||||||
if (!isEnabled) return
|
if (!isEnabled) return
|
||||||
if (powerSaveBlocker.stop(powerSaveBlockerId!)) powerSaveBlockerId = null
|
powerSaveBlocker.stop(powerSaveBlockerId!)
|
||||||
|
powerSaveBlockerId = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let envProxy: null | { host: string, port: number } = null
|
||||||
|
export const getProxy = () => {
|
||||||
|
if (global.lx.appSetting['network.proxy.enable'] && global.lx.appSetting['network.proxy.host']) {
|
||||||
|
return {
|
||||||
|
host: global.lx.appSetting['network.proxy.host'],
|
||||||
|
port: parseInt(global.lx.appSetting['network.proxy.port'] || '80'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (envProxy) {
|
||||||
|
return {
|
||||||
|
host: envProxy.host,
|
||||||
|
port: envProxy.port,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const envProxyStr = envParams.cmdParams['proxy-server']
|
||||||
|
if (envProxyStr && typeof envProxyStr == 'string') {
|
||||||
|
const [host, port = ''] = envProxyStr.split(':')
|
||||||
|
return envProxy = {
|
||||||
|
host,
|
||||||
|
port: parseInt(port || '80'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import needle, { type NeedleHttpVerbs, type NeedleOptions, type BodyData, type N
|
|||||||
// import progress from 'request-progress'
|
// import progress from 'request-progress'
|
||||||
import { httpOverHttp, httpsOverHttp } from 'tunnel'
|
import { httpOverHttp, httpsOverHttp } from 'tunnel'
|
||||||
import { type ClientRequest } from 'node:http'
|
import { type ClientRequest } from 'node:http'
|
||||||
|
import { getProxy } from './index'
|
||||||
// import fs from 'fs'
|
// import fs from 'fs'
|
||||||
|
|
||||||
export const requestMsg = {
|
export const requestMsg = {
|
||||||
@@ -15,42 +16,9 @@ export const requestMsg = {
|
|||||||
|
|
||||||
|
|
||||||
const httpsRxp = /^https:/
|
const httpsRxp = /^https:/
|
||||||
let envProxy: null | { host: string, port: string } = null
|
|
||||||
|
|
||||||
const getRequestAgent = (url: string) => {
|
const getRequestAgent = (url: string) => {
|
||||||
if (envProxy == null) {
|
const proxy = getProxy()
|
||||||
if (global.envParams.cmdParams['proxy-server'] && typeof global.envParams.cmdParams['proxy-server'] == 'string') {
|
return proxy ? (httpsRxp.test(url) ? httpsOverHttp : httpOverHttp)({ proxy }) : undefined
|
||||||
const [host, port = ''] = global.envParams.cmdParams['proxy-server'].split(':')
|
|
||||||
envProxy = {
|
|
||||||
host,
|
|
||||||
port,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const proxy = {
|
|
||||||
enable: global.lx.appSetting['network.proxy.enable'],
|
|
||||||
host: global.lx.appSetting['network.proxy.host'],
|
|
||||||
port: global.lx.appSetting['network.proxy.port'],
|
|
||||||
envProxy,
|
|
||||||
}
|
|
||||||
|
|
||||||
let options
|
|
||||||
if (global.lx.appSetting['network.proxy.enable'] && proxy.host) {
|
|
||||||
options = {
|
|
||||||
proxy: {
|
|
||||||
host: proxy.host,
|
|
||||||
port: parseInt(proxy.port || '80'),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else if (proxy.envProxy) {
|
|
||||||
options = {
|
|
||||||
proxy: {
|
|
||||||
host: proxy.envProxy.host,
|
|
||||||
port: parseInt(proxy.envProxy.port || '80'),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return options ? (httpsRxp.test(url) ? httpsOverHttp : httpOverHttp)(options) : undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestOptions extends NeedleOptions {
|
export interface RequestOptions extends NeedleOptions {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export const queryDislikeList = () => {
|
|||||||
* 批量插入不喜欢歌曲并刷新顺序
|
* 批量插入不喜欢歌曲并刷新顺序
|
||||||
* @param infos 列表
|
* @param infos 列表
|
||||||
*/
|
*/
|
||||||
export const inertDislikeList = async(infos: LX.DBService.DislikeInfo[]) => {
|
export const insertDislikeList = async(infos: LX.DBService.DislikeInfo[]) => {
|
||||||
const db = getDB()
|
const db = getDB()
|
||||||
const insertStatement = createInsertStatement()
|
const insertStatement = createInsertStatement()
|
||||||
db.transaction((infos: LX.DBService.DislikeInfo[]) => {
|
db.transaction((infos: LX.DBService.DislikeInfo[]) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { SPLIT_CHAR } from '@common/constants'
|
import { SPLIT_CHAR } from '@common/constants'
|
||||||
import {
|
import {
|
||||||
queryDislikeList,
|
queryDislikeList,
|
||||||
inertDislikeList,
|
insertDislikeList,
|
||||||
overwirteDislikeList,
|
overwirteDislikeList,
|
||||||
// updateDislikeList,
|
// updateDislikeList,
|
||||||
// deleteDislikeList,
|
// deleteDislikeList,
|
||||||
@@ -71,7 +71,7 @@ export const getDislikeListInfo = (): LX.Dislike.DislikeInfo => {
|
|||||||
* @param lists 列表信息
|
* @param lists 列表信息
|
||||||
*/
|
*/
|
||||||
export const dislikeInfoAdd = async(lists: LX.Dislike.DislikeMusicInfo[]) => {
|
export const dislikeInfoAdd = async(lists: LX.Dislike.DislikeMusicInfo[]) => {
|
||||||
await inertDislikeList(lists.map(info => ({ content: `${info.name}${SPLIT_CHAR.DISLIKE_NAME}${info.singer}` })))
|
await insertDislikeList(lists.map(info => ({ content: `${info.name}${SPLIT_CHAR.DISLIKE_NAME}${info.singer}` })))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export const queryDownloadList = () => {
|
|||||||
* 批量插入下载歌曲并刷新顺序
|
* 批量插入下载歌曲并刷新顺序
|
||||||
* @param mInfos 列表
|
* @param mInfos 列表
|
||||||
*/
|
*/
|
||||||
export const inertDownloadList = (mInfos: LX.DBService.DownloadMusicInfo[], listPositions: Array<{ id: string, position: number }>) => {
|
export const insertDownloadList = (mInfos: LX.DBService.DownloadMusicInfo[], listPositions: Array<{ id: string, position: number }>) => {
|
||||||
const db = getDB()
|
const db = getDB()
|
||||||
const insertStatement = createInsertStatement()
|
const insertStatement = createInsertStatement()
|
||||||
const updatePositionStatement = createUpdatePositionStatement()
|
const updatePositionStatement = createUpdatePositionStatement()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user