├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── LICENSE
├── README.md
├── Taskbar Lyrics.sln
├── betterncm-plugin.vcxproj
├── betterncm-plugin.vcxproj.filters
├── dist
├── base.js
├── config.html
├── func.js
├── lyric.js
├── manifest.json
├── preview.png
├── style.css
├── taskbar-lyrics.exe
└── view.js
├── src
├── betterncm-plugin
│ ├── base.js
│ ├── config.html
│ ├── func.js
│ ├── lyric.js
│ ├── manifest.json
│ ├── preview.png
│ ├── style.css
│ └── view.js
└── taskbar-lyrics
│ ├── CreateWindow.cpp
│ ├── CreateWindow.hpp
│ ├── NetworkServer.cpp
│ ├── NetworkServer.hpp
│ ├── RenderWindow.cpp
│ ├── RenderWindow.hpp
│ ├── TaskbarLyrics.cpp
│ └── TaskbarLyrics.hpp
├── taskbar-lyrics.vcxproj
└── taskbar-lyrics.vcxproj.filters
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | permissions:
9 | contents: write
10 |
11 | jobs:
12 | build:
13 | runs-on: windows-latest
14 |
15 | steps:
16 | - name: Checkout Code
17 | uses: actions/checkout@v3
18 |
19 | - name: Setup MSBuild
20 | uses: microsoft/setup-msbuild@v1.1
21 |
22 | - name: Install VCPKG Packages
23 | run: |
24 | vcpkg install cpp-httplib:x86-windows
25 | vcpkg install nlohmann-json:x86-windows
26 | vcpkg integrate install
27 |
28 | - name: Build
29 | working-directory: ${{ env.GITHUB_WORKSPACE }}
30 | run: msbuild "Taskbar Lyrics.sln" -p:Configuration=Release -p:Platform="x86"
31 |
32 | - name: Upload Artifacts
33 | uses: actions/upload-artifact@v1
34 | with:
35 | name: dist
36 | path: dist
37 |
38 | deploy:
39 | concurrency: ci-${{ github.ref }}
40 | needs: [build]
41 | runs-on: ubuntu-latest
42 | steps:
43 | - name: Checkout
44 | uses: actions/checkout@v3
45 |
46 | - name: Download Artifacts
47 | uses: actions/download-artifact@v1
48 | with:
49 | name: dist
50 |
51 | - name: Deploy
52 | uses: JamesIves/github-pages-deploy-action@v4
53 | with:
54 | branch: main
55 | target-folder: dist
56 | folder: dist
57 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs
2 | .vscode
3 |
4 | Release
5 | Debug
6 |
7 | dist_debug
8 | dist/taskbar-lyrics.pdb
9 |
10 | betterncm-plugin.vcxproj.user
11 | taskbar-lyrics.vcxproj.user
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 沫烬染
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Taskbar-Lyrics
2 |
3 | BetterNCM插件,在任务栏上嵌入歌词,目前仅建议Windows 11
4 |
5 | 只适配原版任务栏,使用第三方软件修改后出现问题的均不解决!
6 |
7 | 编译需安装以下库:
8 |
9 | `cpp-httplib:x86-windows`
10 |
11 | `nlohmann-json:x86-windows`
12 |
13 | 
14 |
--------------------------------------------------------------------------------
/Taskbar Lyrics.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio Version 17
3 | VisualStudioVersion = 17.3.32922.545
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "taskbar-lyrics", "taskbar-lyrics.vcxproj", "{2CD1B59B-4A33-4BB0-B33F-9D30756C742F}"
6 | EndProject
7 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "betterncm-plugin", "betterncm-plugin.vcxproj", "{AD169A72-1455-4635-97CC-E5EDF1CB41D5}"
8 | EndProject
9 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "杂项", "杂项", "{682F0C3C-6A14-4C74-9834-A9AE0772FB3C}"
10 | ProjectSection(SolutionItems) = preProject
11 | .gitignore = .gitignore
12 | .github\workflows\build.yml = .github\workflows\build.yml
13 | LICENSE = LICENSE
14 | README.md = README.md
15 | EndProjectSection
16 | EndProject
17 | Global
18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 | Debug|x86 = Debug|x86
20 | Release|x86 = Release|x86
21 | EndGlobalSection
22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
23 | {2CD1B59B-4A33-4BB0-B33F-9D30756C742F}.Debug|x86.ActiveCfg = Debug|Win32
24 | {2CD1B59B-4A33-4BB0-B33F-9D30756C742F}.Debug|x86.Build.0 = Debug|Win32
25 | {2CD1B59B-4A33-4BB0-B33F-9D30756C742F}.Release|x86.ActiveCfg = Release|Win32
26 | {2CD1B59B-4A33-4BB0-B33F-9D30756C742F}.Release|x86.Build.0 = Release|Win32
27 | {AD169A72-1455-4635-97CC-E5EDF1CB41D5}.Debug|x86.ActiveCfg = Debug|Win32
28 | {AD169A72-1455-4635-97CC-E5EDF1CB41D5}.Debug|x86.Build.0 = Debug|Win32
29 | {AD169A72-1455-4635-97CC-E5EDF1CB41D5}.Release|x86.ActiveCfg = Release|Win32
30 | {AD169A72-1455-4635-97CC-E5EDF1CB41D5}.Release|x86.Build.0 = Release|Win32
31 | EndGlobalSection
32 | GlobalSection(SolutionProperties) = preSolution
33 | HideSolutionNode = FALSE
34 | EndGlobalSection
35 | GlobalSection(ExtensibilityGlobals) = postSolution
36 | SolutionGuid = {06E44E1D-A535-45B8-A01E-DDD7FC40CE19}
37 | EndGlobalSection
38 | EndGlobal
--------------------------------------------------------------------------------
/betterncm-plugin.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 16.0
27 | Win32Proj
28 | {ad169a72-1455-4635-97cc-e5edf1cb41d5}
29 | betterncmplugin
30 | 10.0
31 |
32 |
33 |
34 | Application
35 | true
36 | v143
37 | Unicode
38 |
39 |
40 | Application
41 | false
42 | v143
43 | true
44 | Unicode
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | $(Configuration)\betterncm-plugin\
66 | $(SolutionDir)\dist_debug\
67 |
68 |
69 | $(Configuration)\betterncm-plugin\
70 | $(SolutionDir)\dist\
71 |
72 |
73 |
74 | Level3
75 | true
76 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
77 | true
78 |
79 |
80 | Console
81 | true
82 |
83 |
84 |
85 |
86 |
87 |
88 | copy src\betterncm-plugin\index.js dist_debug
89 | copy src\betterncm-plugin\manifest.json dist_debug
90 | copy src\betterncm-plugin\preview.png dist_debug
91 |
92 |
93 |
94 |
95 | Level3
96 | true
97 | true
98 | true
99 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
100 | true
101 |
102 |
103 | Console
104 | true
105 | true
106 | true
107 |
108 |
109 |
110 |
111 |
112 |
113 | copy src\betterncm-plugin\* dist
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/betterncm-plugin.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 |
10 |
11 | 源文件
12 |
13 |
14 | 源文件
15 |
16 |
17 | 源文件
18 |
19 |
20 | 源文件
21 |
22 |
23 | 源文件
24 |
25 |
26 | 源文件
27 |
28 |
29 | 源文件
30 |
31 |
32 |
33 |
34 | 源文件
35 |
36 |
37 |
--------------------------------------------------------------------------------
/dist/base.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | plugin.onLoad(async () => {
5 | const TaskbarLyricsPort = BETTERNCM_API_PORT - 2;
6 |
7 | const TaskbarLyricsFetch = (path, params) => fetch(
8 | `http://127.0.0.1:${TaskbarLyricsPort}/taskbar${path}`,
9 | {
10 | method: "POST",
11 | body: JSON.stringify(params),
12 | headers: {
13 | "Content-Type": "application/json"
14 | }
15 | }
16 | );
17 |
18 | const TaskbarLyricsAPI = {
19 | // 字体设置
20 | font: {
21 | font: params => TaskbarLyricsFetch("/font/font", params),
22 | color: params => TaskbarLyricsFetch("/font/color", params),
23 | style: params => TaskbarLyricsFetch("/font/style", params),
24 | },
25 |
26 | // 歌词设置
27 | lyrics: {
28 | lyrics: params => TaskbarLyricsFetch("/lyrics/lyrics", params),
29 | align: params => TaskbarLyricsFetch("/lyrics/align", params),
30 | },
31 |
32 | // 窗口设置
33 | window: {
34 | position: params => TaskbarLyricsFetch("/window/position", params),
35 | margin: params => TaskbarLyricsFetch("/window/margin", params),
36 | screen: params => TaskbarLyricsFetch("/window/screen", params),
37 | },
38 |
39 | // 关闭
40 | close: params => TaskbarLyricsFetch("/close", params)
41 | };
42 |
43 |
44 | // 对应Windows的枚举
45 | const WindowsEnum = {
46 | WindowAlignment: {
47 | WindowAlignmentAdaptive: 0,
48 | WindowAlignmentLeft: 1,
49 | WindowAlignmentCenter: 2,
50 | WindowAlignmentRight: 3
51 | },
52 | DWRITE_TEXT_ALIGNMENT: {
53 | DWRITE_TEXT_ALIGNMENT_LEADING: 0,
54 | DWRITE_TEXT_ALIGNMENT_TRAILING: 1,
55 | DWRITE_TEXT_ALIGNMENT_CENTER: 2,
56 | DWRITE_TEXT_ALIGNMENT_JUSTIFIED: 3
57 | },
58 | DWRITE_FONT_WEIGHT: {
59 | DWRITE_FONT_WEIGHT_THIN: 100,
60 | DWRITE_FONT_WEIGHT_EXTRA_LIGHT: 200,
61 | DWRITE_FONT_WEIGHT_ULTRA_LIGHT: 200,
62 | DWRITE_FONT_WEIGHT_LIGHT: 300,
63 | DWRITE_FONT_WEIGHT_SEMI_LIGHT: 350,
64 | DWRITE_FONT_WEIGHT_NORMAL: 400,
65 | DWRITE_FONT_WEIGHT_REGULAR: 400,
66 | DWRITE_FONT_WEIGHT_MEDIUM: 500,
67 | DWRITE_FONT_WEIGHT_DEMI_BOLD: 600,
68 | DWRITE_FONT_WEIGHT_SEMI_BOLD: 600,
69 | DWRITE_FONT_WEIGHT_BOLD: 700,
70 | DWRITE_FONT_WEIGHT_EXTRA_BOLD: 800,
71 | DWRITE_FONT_WEIGHT_ULTRA_BOLD: 800,
72 | DWRITE_FONT_WEIGHT_BLACK: 900,
73 | DWRITE_FONT_WEIGHT_HEAVY: 900,
74 | DWRITE_FONT_WEIGHT_EXTRA_BLACK: 950,
75 | DWRITE_FONT_WEIGHT_ULTRA_BLACK: 950
76 | },
77 | DWRITE_FONT_STYLE: {
78 | DWRITE_FONT_STYLE_NORMAL: 0,
79 | DWRITE_FONT_STYLE_OBLIQUE: 1,
80 | DWRITE_FONT_STYLE_ITALIC: 2
81 | }
82 | };
83 |
84 |
85 | // 默认的配置
86 | const defaultConfig = {
87 | "font": {
88 | "font_family": "Microsoft YaHei UI"
89 | },
90 | "color": {
91 | "basic": {
92 | "light": {
93 | "hex_color": 0x000000,
94 | "opacity": 1.0
95 | },
96 | "dark": {
97 | "hex_color": 0xFFFFFF,
98 | "opacity": 1.0
99 | }
100 | },
101 | "extra": {
102 | "light": {
103 | "hex_color": 0x000000,
104 | "opacity": 1.0
105 | },
106 | "dark": {
107 | "hex_color": 0xFFFFFF,
108 | "opacity": 1.0
109 | }
110 | }
111 | },
112 | "style": {
113 | "basic": {
114 | "weight": {
115 | "value": WindowsEnum.DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_NORMAL,
116 | "textContent": "Normal (400)"
117 | },
118 | "slope": WindowsEnum.DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL,
119 | "underline": false,
120 | "strikethrough": false
121 | },
122 | "extra": {
123 | "weight": {
124 | "value": WindowsEnum.DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_NORMAL,
125 | "textContent": "Normal (400)"
126 | },
127 | "slope": WindowsEnum.DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL,
128 | "underline": false,
129 | "strikethrough": false
130 | }
131 | },
132 | "lyrics": {
133 | "retrieval_method": {
134 | "value": 1,
135 | "textContent": "使用LibLyric解析获取歌词",
136 | }
137 | },
138 | "effect": {
139 | "next_line_lyrics_position": {
140 | "value": 0,
141 | "textContent": "副歌词,下句歌词显示在这"
142 | },
143 | "extra_show": {
144 | "value": 2,
145 | "textContent": "当前翻译,没则用上个选项"
146 | },
147 | "adjust": 0.0
148 | },
149 | "align": {
150 | "basic": WindowsEnum.DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_LEADING,
151 | "extra": WindowsEnum.DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_LEADING
152 | },
153 | "position": {
154 | "position": {
155 | "value": WindowsEnum.WindowAlignment.WindowAlignmentAdaptive,
156 | "textContent": "自动,自适应选择左或右"
157 | }
158 | },
159 | "margin": {
160 | "left": 0,
161 | "right": 0
162 | },
163 | "screen": {
164 | "parent_taskbar": {
165 | "value": "Shell_TrayWnd",
166 | "textContent": "主屏幕任务栏"
167 | }
168 | }
169 | };
170 |
171 |
172 | const pluginConfig = {
173 | get: name => Object.assign({}, defaultConfig[name], plugin.getConfig(name, defaultConfig[name])),
174 | set: (name, value) => plugin.setConfig(name, value)
175 | };
176 |
177 |
178 | this.base = {
179 | TaskbarLyricsPort,
180 | TaskbarLyricsAPI,
181 | WindowsEnum,
182 | defaultConfig,
183 | pluginConfig
184 | };
185 | });
186 |
--------------------------------------------------------------------------------
/dist/config.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
27 |
28 |
29 |
30 |
31 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 字体样式:
69 |
70 |
71 |
72 |
主歌词-字体字重:
73 |
74 |
Normal (400)
75 |
76 |
77 | - Thin (100)
78 | - Extra-light (200)
79 | - Ultra-light (200)
80 | - Light (300)
81 | - Semi-light (350)
82 | - Normal (400)
83 | - Regular (400)
84 | - Medium (500)
85 | - Demi-bold (600)
86 | - Semi-bold (600)
87 | - Bold (700)
88 | - Extra-bold (800)
89 | - Ultra-bold (800)
90 | - Black (900)
91 | - Heavy (900)
92 | - Extra-black (950)
93 | - Ultra-black (950)
94 |
95 |
96 |
97 |
98 |
99 | 主歌词-字体斜体:
100 |
101 |
102 |
103 |
104 |
105 | 主歌词-下划线:
106 |
107 |
108 |
109 | 主歌词-删除线:
110 |
111 |
112 |
113 | 副歌词-字体字重:
114 |
138 |
139 |
140 | 副歌词-字体斜体:
141 |
142 |
143 |
144 |
145 |
146 | 副歌词-下划线:
147 |
148 |
149 |
150 | 副歌词-删除线:
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | 歌词设置:
162 |
163 |
164 |
165 | 歌词开关:
166 |
167 |
168 |
169 |
获取方法:
170 |
171 |
使用LibLyric解析获取歌词
172 |
173 |
174 | - 使用软件内词栏监听获取歌词
175 | - 使用LibLyric解析获取歌词
176 | - 使用RefinedNowPlaying歌词
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 | 显示效果:
189 |
190 |
191 |
192 |
193 | 延迟调整:
194 |
195 |
196 |
197 |
下句歌词位置:
198 |
199 |
副歌词,下句歌词显示在这
200 |
201 |
202 | - 副歌词,下句歌词显示在这
203 | - 主歌词,下句歌词显示在这
204 | - 交错式,就跟桌面歌词一样
205 |
206 |
207 |
208 |
209 |
210 | 副歌词显示:
211 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 | 对齐方式:
231 |
232 |
233 |
234 | 主歌词:
235 |
236 |
237 |
238 |
239 |
240 | 副歌词:
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 | 修改位置:
254 |
255 |
256 |
257 |
窗口位置:
258 |
259 |
靠左,占满剩余空间宽度
260 |
261 |
262 | - 自动,自适应选择左或右
263 | - 靠左,占满剩余空间宽度
264 | - 中间,基于任务栏的宽度
265 | - 靠右,占满剩余空间宽度
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
290 |
291 |
292 |
293 |
294 |
295 |
296 | 切换屏幕:(实验性功能,目前在副屏有BUG)
297 |
298 |
299 |
300 |
父任务栏:
301 |
302 |
主屏幕任务栏
303 |
304 |
305 | - 主屏幕任务栏
306 | - 副屏幕任务栏
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 | 关于插件:
320 |
321 |
322 |
323 | 作者名称:
324 | 🌈沫烬染🐾
325 |
326 |
327 | 仓库链接:
328 | Taskbar-Lyrics
329 |
330 |
==================================================
331 |
以上链接均为Github,喜欢就来点个Star吧~
332 |
如有BUG或建议欢迎前来发Issue
333 |
有大佬感兴趣来Fork本项目提交PR
334 |
来帮助我们将此插件变得更好!
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 | 注意事项:
344 |
345 |
346 |
本插件适用于Windows 11,且任务栏没用第三方软件重度修改
347 |
如遇到某些选项不生效,请尝试点击选项旁边的“恢复默认”按钮
348 |
如遇到不显示歌词,并且没有个性化的效果,请尝试“重载网易云”
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 | 杂七杂八:
358 |
359 |
360 |
插件名称是“Taskbar-Lyrics”,“任务栏歌词”,不是状态栏呀
361 |
插件并非为BetterNCM提供的Native Plugin(DLL形式注入)
362 |
插件会启动任务栏歌词程序,并嵌入到任务栏中和开启HTTP服务器
363 |
插件端负责获取播放状态与歌词和一些个性化配置提供给软件端
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 | 1.6.3
373 |
374 |
375 |
-> | 优化代码,移除没必要的代码
376 |
-> | 修复打开下拉选择框后,点击其他区域无法收起的问题
377 |
-> | 修复使用RefinedNowPlaying歌词可能对不上的问题
378 |
-> | 将API的端口号从BetterNCM API的端口号+2改成-2
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 | 1.6.2
387 |
388 |
389 |
-> | 修复一些选项失效的BUG
390 |
-> | 添加插件更新日志(之前的日志的会有些差错
391 |
-> | 添加新的歌词获取方式,使用RefinedNowPlaying歌词
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 | 1.6.1
400 |
401 |
402 |
-> | 琐事调整,优化代码,统一风格
403 |
-> | 将一些选项换成下拉式选择框
404 |
-> | 添加窗口位置自适应
405 |
-> | 修复程序某些情况崩溃问题
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 | 1.6.0
414 |
415 |
416 |
-> | 优化代码,区分API与设置的类别
417 |
-> | 添加显示效果设置,下句歌词位置
418 |
-> | 将副歌词显示与延迟调整移动到显示效果内
419 |
-> | 修改默认字体为:Microsoft YaHei UI
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 | 1.5.7
428 |
429 |
430 |
-> | 纯音乐显示歌曲名与作曲家
431 |
-> | 修改设置界面分类
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 | 1.5.6
440 |
441 |
442 |
-> | 修复获取的路径不标准导致无法启动软件的问题
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 | 1.5.5
451 |
452 |
453 |
-> | 尝试解决软件不会启动的问题
454 |
-> | 优化代码,移除没用到的引入
455 |
-> | 解决刚打开软件时获取不到数据而报错的问题
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 | 1.5.4
464 |
465 |
466 |
-> | 优化代码,删除无用的调试语句与注释
467 |
-> | 调整配置界面样式,配置界面适配亮暗主题
468 |
-> | 重写文字排版,修复文字右对齐后最后一个字只剩一半,修复文字超出部分没有被隐藏
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 | 1.5.3
477 |
478 |
479 |
-> | 优化代码,调整配置界面,调整代码位置,删除调试语句
480 |
-> | 读取配置时,没有或无效的将使用默认值
481 |
-> | 解决切换歌曲后再切换到新的歌词获取方式时无法重新获取歌词的问题
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 | 1.5.2
490 |
491 |
492 |
-> | 移除对libsonginfo的依赖
493 |
-> | 解决插件占用文件夹,复制软件到其他目录后运行
494 |
-> | 添加切换歌词获取方式选项
495 |
-> | 修改注意事项
496 |
-> | 优化代码,调整配置界面布局
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 | 1.5.1
505 |
506 |
507 |
-> | 优化代码,调整配置界面
508 |
-> | 添加歌词时间调整
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 | 1.5.0
517 |
518 |
519 |
-> | 重构/优化代码,重构配置界面,移除软件的互斥锁,修改一部分API
520 |
-> | 添加libsonginfo与liblyric依赖
521 |
-> | 添加通过liblyric获取歌词,并设为默认方式
522 |
-> | 添加修改副歌词的选项
523 |
-> | 修复下拉式选择框只能显示值的问题
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 | 1.4.3
532 |
533 |
534 |
-> | 优化代码,修改函数导出导入,拆分代码
535 |
-> | 修复样式设置不生效的问题
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 | 1.4.2
544 |
545 |
546 |
-> | 优化代码
547 |
-> | 添加回字体样式功能
548 |
-> | 修复样式设置不生效的问题
549 |
-> | 修复读取到不兼容的配置导致无法使用的问题
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 | 1.4.1
558 |
559 |
560 |
-> | 优化代码,删除无用代码
561 |
-> | 修复第一次打开某些输入框不显示默认值
562 |
-> | 关闭歌词时停止对歌词变动的监控
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 | 1.4.0
571 |
572 |
573 |
-> | 优化代码,删除无用代码
574 |
-> | 重构代码,从GDI+切换为Direct2D与DirectWrite
575 |
-> | 重构代码,插件端,将单文件拆分代码为多文件并注入
576 |
-> | 修复第一次打开某些输入框不显示默认值
577 |
-> | 关闭歌词时停止对歌词变动的监控
578 |
-> | 修复歌词位置无法设置在左边的问题
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 | 1.3.1
587 |
588 |
589 |
-> | 微调歌词的字体大小与边距
590 |
-> | 优化代码,优化设置界面
591 |
-> | 更改为使用JSON传输数据
592 |
-> | 添加基于整个任务栏宽度居中的按钮
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 | 1.3.0
601 |
602 |
603 |
-> | 添加修改边距选项
604 |
-> | 添加字体样式选项
605 |
-> | 修复歌词在手动关闭后再开启设置不生效的问题
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 | 1.2.4
614 |
615 |
616 |
-> | 优化代码
617 |
-> | 添加歌词开关选项
618 |
-> | 修复关闭程序后本地http服务线程不会立刻结束的问题
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 | 1.2.3
627 |
628 |
629 |
-> | 重构代码,软件端,优化代码
630 |
-> | 微调歌词的字体渲染与边距
631 |
-> | 更新插件预览图
632 |
-> | 修复修改位置某些情况下失效的问题
633 |
-> | 支持更多版本的任务栏
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 | 1.2.2
642 |
643 |
644 |
-> | 修复了会显示控制台的问题
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 | 1.2.1
653 |
654 |
655 |
-> | 修复了插件商店安装插件后无法启动的问题
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 | 1.2.0
664 |
665 |
666 |
-> | 重构代码,优化代码,将软件端单文件拆分代码为多文件
667 |
-> | 添加启动,连接,关闭的提示
668 |
-> | 兼容Windows 11 21H2版本任务栏
669 |
-> | 修复页面刷新后不会关闭程序
670 |
-> | 修复歌词过长导致换行的问题
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 | 1.1.0
679 |
680 |
683 |
684 |
685 |
686 |
687 |
688 |
689 | 1.0.4
690 |
691 |
692 |
-> | 优化代码
693 |
-> | 添加配置界面
694 |
-> | 自适应任务栏剩余宽度
695 |
-> | 添加切换屏幕选项
696 |
-> | 添加更换字体选项
697 |
-> | 添加字体颜色选项
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 | 1.0.3
706 |
707 |
708 |
-> | 修复歌词带问号导致不显示
709 |
-> | 添加互斥锁,修复重复启动软件导致重叠多个窗口
710 |
-> | 监听网易云进程关闭
711 |
-> | 移除心跳检测机制
712 |
-> | 移除Worker
713 |
-> | 移除Hijack
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 | 1.0.2
722 |
723 |
724 |
-> | 自适应windows深浅色模式
725 |
-> | 单行歌词放大居中占满双行
726 |
-> | 修复歌词上右键卡住的问题
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 | 1.0.1
735 |
736 |
737 |
-> | 修复软件路径问题
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 | 1.0.0
746 |
747 |
748 |
-> | 添加任务栏歌词插件
749 |
750 |
751 |
752 |
753 |
--------------------------------------------------------------------------------
/dist/func.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | plugin.onLoad(async () => {
5 | const {
6 | TaskbarLyricsPort,
7 | TaskbarLyricsAPI,
8 | WindowsEnum,
9 | defaultConfig,
10 | pluginConfig
11 | } = { ...this.base };
12 | const { startGetLyric, stopGetLyric } = { ...this.lyric };
13 |
14 |
15 | // 启动任务栏歌词软件
16 | const TaskbarLyricsStart = async () => {
17 | // 这BetterNCM获取的路径是不标准的会出问题,要替换掉下面那俩字符
18 | const dataPath = (await betterncm.app.getDataPath()).replace("/", "\\");
19 | const pluginPath = this.pluginPath.replace("/./", "\\").replace("/", "\\");
20 | const taskkill = `taskkill /F /IM "taskbar-lyrics.exe"`;
21 | const xcopy = `xcopy /C /D /Y "${pluginPath}\\taskbar-lyrics.exe" "${dataPath}"`;
22 | const exec = `"${dataPath}\\taskbar-lyrics.exe" ${TaskbarLyricsPort}`;
23 | const cmd = `${taskkill} & ${xcopy} && ${exec}`;
24 | await betterncm.app.exec(`cmd /S /C ${cmd}`, false, false);
25 | TaskbarLyricsAPI.font.font(pluginConfig.get("font"));
26 | TaskbarLyricsAPI.font.color(pluginConfig.get("color"));
27 | TaskbarLyricsAPI.font.style(pluginConfig.get("style"));
28 | TaskbarLyricsAPI.window.position(pluginConfig.get("position"));
29 | TaskbarLyricsAPI.window.margin(pluginConfig.get("margin"));
30 | TaskbarLyricsAPI.lyrics.align(pluginConfig.get("align"));
31 | TaskbarLyricsAPI.window.screen(pluginConfig.get("screen"));
32 | startGetLyric();
33 | };
34 |
35 |
36 | // 关闭任务栏歌词软件
37 | const TaskbarLyricsClose = async () => {
38 | TaskbarLyricsAPI.close({});
39 | stopGetLyric();
40 | };
41 |
42 |
43 | addEventListener("beforeunload", TaskbarLyricsClose);
44 | TaskbarLyricsStart();
45 |
46 |
47 | // 更换字体
48 | const font = {
49 | apply: elements => {
50 | const config = JSON.parse(JSON.stringify(pluginConfig.get("font")));
51 | config["font_family"] = elements.fontFamily.value;
52 | pluginConfig.set("font", config);
53 | TaskbarLyricsAPI.font.font(config);
54 | },
55 | reset: elements => {
56 | pluginConfig.set("font", undefined);
57 | TaskbarLyricsAPI.font.font(defaultConfig["font"]);
58 | elements.fontFamily.value = defaultConfig["font"]["font_family"];
59 | }
60 | }
61 |
62 |
63 | // 字体颜色
64 | const color = {
65 | apply: elements => {
66 | const config = JSON.parse(JSON.stringify(pluginConfig.get("color")));
67 | config["basic"]["light"]["hex_color"] = parseInt(elements.basicLightColor.value.slice(1), 16);
68 | config["basic"]["light"]["opacity"] = Number(elements.basicLightOpacity.value);
69 | config["basic"]["dark"]["hex_color"] = parseInt(elements.basicDarkColor.value.slice(1), 16);
70 | config["basic"]["dark"]["opacity"] = Number(elements.basicDarkOpacity.value);
71 | config["extra"]["light"]["hex_color"] = parseInt(elements.extraLightColor.value.slice(1), 16);
72 | config["extra"]["light"]["opacity"] = Number(elements.extraLightOpacity.value);
73 | config["extra"]["dark"]["hex_color"] = parseInt(elements.extraDarkColor.value.slice(1), 16);
74 | config["extra"]["dark"]["opacity"] = Number(elements.extraDarkOpacity.value);
75 | pluginConfig.set("color", config);
76 | TaskbarLyricsAPI.font.color(config);
77 | },
78 | reset: elements => {
79 | elements.basicLightColor.value = `#${defaultConfig["color"]["basic"]["light"]["hex_color"].toString(16).padStart(6, "0")}`;
80 | elements.basicLightOpacity.value = defaultConfig["color"]["basic"]["light"]["opacity"];
81 | elements.basicDarkColor.value = `#${defaultConfig["color"]["basic"]["dark"]["hex_color"].toString(16).padStart(6, "0")}`;
82 | elements.basicDarkOpacity.value = defaultConfig["color"]["basic"]["dark"]["opacity"];
83 | elements.extraLightColor.value = `#${defaultConfig["color"]["extra"]["light"]["hex_color"].toString(16).padStart(6, "0")}`;
84 | elements.extraLightOpacity.value = defaultConfig["color"]["extra"]["light"]["opacity"];
85 | elements.extraDarkColor.value = `#${defaultConfig["color"]["extra"]["dark"]["hex_color"].toString(16).padStart(6, "0")}`;
86 | elements.extraDarkOpacity.value = defaultConfig["color"]["extra"]["dark"]["opacity"];
87 | pluginConfig.set("color", undefined);
88 | TaskbarLyricsAPI.font.color(defaultConfig["color"]);
89 | }
90 | }
91 |
92 |
93 | // 字体样式
94 | const style = {
95 | setWeight: (name, value, textContent) => {
96 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
97 | config[name].weight.value = Number(value);
98 | config[name].weight.textContent = textContent;
99 | pluginConfig.set("style", config);
100 | TaskbarLyricsAPI.font.style(config);
101 | },
102 | setSlopeNormal: event => {
103 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
104 | config[event.target.dataset.type].slope = WindowsEnum.DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL;
105 | pluginConfig.set("style", config);
106 | TaskbarLyricsAPI.font.style(config);
107 | },
108 | setSlopeOblique: event => {
109 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
110 | config[event.target.dataset.type].slope = WindowsEnum.DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_OBLIQUE;
111 | pluginConfig.set("style", config);
112 | TaskbarLyricsAPI.font.style(config);
113 | },
114 | setSlopeItalic: event => {
115 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
116 | config[event.target.dataset.type].slope = WindowsEnum.DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_ITALIC;
117 | pluginConfig.set("style", config);
118 | TaskbarLyricsAPI.font.style(config);
119 | },
120 | setUnderline: event => {
121 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
122 | config[event.target.dataset.type].underline = event.target.checked;
123 | pluginConfig.set("style", config);
124 | TaskbarLyricsAPI.font.style(config);
125 | },
126 | setStrikethrough: event => {
127 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
128 | config[event.target.dataset.type].strikethrough = event.target.checked;
129 | pluginConfig.set("style", config);
130 | TaskbarLyricsAPI.font.style(config);
131 | },
132 | reset: elements => {
133 | pluginConfig.set("style", undefined);
134 | TaskbarLyricsAPI.font.style(defaultConfig["style"]);
135 | elements.basicWeightValue.textContent = defaultConfig["style"]["basic"]["weight"]["textContent"];
136 | elements.basicUnderline.checked = defaultConfig["style"]["basic"]["underline"];
137 | elements.basicStrikethrough.checked = defaultConfig["style"]["basic"]["strikethrough"];
138 | elements.extraWeightValue.textContent = defaultConfig["style"]["extra"]["weight"]["textContent"];
139 | elements.extraUnderline.checked = defaultConfig["style"]["extra"]["underline"];
140 | elements.extraStrikethrough.checked = defaultConfig["style"]["extra"]["strikethrough"];
141 | }
142 | }
143 |
144 |
145 | // 歌词设置
146 | const lyrics = {
147 | lyricsSwitch: event => event.target.checked ? TaskbarLyricsStart() : TaskbarLyricsClose(),
148 | setRetrievalMethod: (value, textContent) => {
149 | const config = JSON.parse(JSON.stringify(pluginConfig.get("lyrics")));
150 | config["retrieval_method"]["value"] = Number(value);
151 | config["retrieval_method"]["textContent"] = textContent;
152 | stopGetLyric();
153 | pluginConfig.set("lyrics", config);
154 | startGetLyric();
155 | },
156 | reset: elements => {
157 | elements.retrievalMethodValue.textContent = defaultConfig["lyrics"]["retrieval_method"]["textContent"];
158 | stopGetLyric();
159 | pluginConfig.set("lyrics", undefined);
160 | startGetLyric();
161 | }
162 | }
163 |
164 |
165 | // 显示效果
166 | const effect = {
167 | setNextLineLyricsPosition: (value, textContent) => {
168 | const config = JSON.parse(JSON.stringify(pluginConfig.get("effect")));
169 | config["next_line_lyrics_position"]["value"] = Number(value);
170 | config["next_line_lyrics_position"]["textContent"] = textContent;
171 | pluginConfig.set("effect", config);
172 | },
173 | setExtraShow: (value, textContent) => {
174 | const config = JSON.parse(JSON.stringify(pluginConfig.get("effect")));
175 | config["extra_show"]["value"] = Number(value);
176 | config["extra_show"]["textContent"] = textContent;
177 | pluginConfig.set("effect", config);
178 | },
179 | apply: elements => {
180 | const config = JSON.parse(JSON.stringify(pluginConfig.get("effect")));
181 | config["adjust"] = Number(elements.adjust.value);
182 | pluginConfig.set("effect", config);
183 | },
184 | reset: elements => {
185 | elements.nextLineLyricsPositionValue.textContent = defaultConfig["effect"]["next_line_lyrics_position"]["textContent"];
186 | elements.extraShowValue.textContent = defaultConfig["effect"]["extra_show"]["textContent"];
187 | elements.adjust.value = defaultConfig["effect"]["adjust"];
188 | pluginConfig.set("effect", undefined);
189 | }
190 | }
191 |
192 |
193 | // 对齐方式
194 | const align = {
195 | setLeft: event => {
196 | const config = JSON.parse(JSON.stringify(pluginConfig.get("align")));
197 | config[event.target.dataset.type] = WindowsEnum.DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_LEADING;
198 | pluginConfig.set("align", config);
199 | TaskbarLyricsAPI.lyrics.align(config);
200 | },
201 | setCenter: event => {
202 | const config = JSON.parse(JSON.stringify(pluginConfig.get("align")));
203 | config[event.target.dataset.type] = WindowsEnum.DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_CENTER;
204 | pluginConfig.set("align", config);
205 | TaskbarLyricsAPI.lyrics.align(config);
206 | },
207 | setRight: event => {
208 | const config = JSON.parse(JSON.stringify(pluginConfig.get("align")));
209 | config[event.target.dataset.type] = WindowsEnum.DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_TRAILING;
210 | pluginConfig.set("align", config);
211 | TaskbarLyricsAPI.lyrics.align(config);
212 | },
213 | reset: () => {
214 | pluginConfig.set("align", undefined);
215 | TaskbarLyricsAPI.lyrics.align(defaultConfig["align"]);
216 | }
217 | }
218 |
219 |
220 | // 修改位置
221 | const position = {
222 | setWindowPosition: (value, textContent) => {
223 | const config = JSON.parse(JSON.stringify(pluginConfig.get("position")));
224 | config["position"]["value"] = Number(value);
225 | config["position"]["textContent"] = textContent;
226 | pluginConfig.set("position", config);
227 | TaskbarLyricsAPI.window.position(config);
228 | },
229 | reset: elements => {
230 | elements.windowPositionValue.textContent = defaultConfig["position"]["position"]["textContent"];
231 | pluginConfig.set("position", undefined);
232 | TaskbarLyricsAPI.window.position(defaultConfig["position"]);
233 | }
234 | }
235 |
236 |
237 | // 修改边距
238 | const margin = {
239 | apply: elements => {
240 | const config = JSON.parse(JSON.stringify(pluginConfig.get("margin")));
241 | config["left"] = Number(elements.left.value);
242 | config["right"] = Number(elements.right.value);
243 | pluginConfig.set("margin", config);
244 | TaskbarLyricsAPI.window.margin(config);
245 | },
246 | reset: elements => {
247 | pluginConfig.set("margin", undefined);
248 | TaskbarLyricsAPI.window.margin(defaultConfig["margin"]);
249 | elements.left.value = defaultConfig["margin"]["left"];
250 | elements.right.value = defaultConfig["margin"]["right"];
251 | }
252 | }
253 |
254 |
255 | // 切换屏幕
256 | const screen = {
257 | setParentTaskbar: (value, textContent) => {
258 | const config = JSON.parse(JSON.stringify(pluginConfig.get("screen")));
259 | config["parent_taskbar"]["value"] = value;
260 | config["parent_taskbar"]["textContent"] = textContent;
261 | pluginConfig.set("screen", config);
262 | TaskbarLyricsAPI.window.screen(config);
263 | },
264 | reset: elements => {
265 | elements.parentTaskbarValue.textContent = defaultConfig["screen"]["parent_taskbar"]["textContent"];
266 | pluginConfig.set("screen", undefined);
267 | TaskbarLyricsAPI.window.screen(defaultConfig["screen"]);
268 | }
269 | }
270 |
271 |
272 | this.func = {
273 | font,
274 | color,
275 | style,
276 | lyrics,
277 | effect,
278 | align,
279 | position,
280 | margin,
281 | screen
282 | };
283 | });
284 |
--------------------------------------------------------------------------------
/dist/lyric.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | plugin.onLoad(async () => {
5 | const { TaskbarLyricsAPI, pluginConfig } = { ...this.base };
6 | const liblyric = loadedPlugins.liblyric;
7 |
8 |
9 | let observer = null;
10 | let parsedLyric = null;
11 | let currentIndex = 0;
12 | let musicId = 0;
13 | let currentLine = 0;
14 |
15 |
16 | // 监视软件内歌词变动
17 | const watchLyricsChange = async () => {
18 | const mLyric = await betterncm.utils.waitForElement("#x-g-mn .m-lyric");
19 | const MutationCallback = mutations => {
20 | for (const mutation of mutations) {
21 | let lyrics = {
22 | basic: "",
23 | extra: ""
24 | };
25 |
26 | if (mutation.addedNodes[2]) {
27 | lyrics.basic = mutation.addedNodes[0].firstChild.textContent;
28 | lyrics.extra = mutation.addedNodes[2].firstChild ? mutation.addedNodes[2].firstChild.textContent : "";
29 | } else {
30 | lyrics.basic = mutation.addedNodes[0].textContent;
31 | }
32 |
33 | TaskbarLyricsAPI.lyrics.lyrics(lyrics);
34 | }
35 | }
36 |
37 | observer = new MutationObserver(MutationCallback);
38 | observer.observe(mLyric, { childList: true, subtree: true });
39 | }
40 |
41 |
42 | // 音乐ID发生变化时
43 | const play_load = async () => {
44 | // 获取歌曲信息
45 | const playingSong = betterncm.ncm.getPlayingSong();
46 | musicId = playingSong.data.id ?? 0;
47 | const name = playingSong.data.name ?? "";
48 | const artists = playingSong.data.artists ?? "";
49 |
50 | // 解析歌手名称
51 | let artistName = "";
52 | artists.forEach(item => artistName += ` / ${item.name}`);
53 | artistName = artistName.slice(3);
54 |
55 | // 发送歌曲信息
56 | TaskbarLyricsAPI.lyrics.lyrics({
57 | "basic": name,
58 | "extra": artistName
59 | });
60 |
61 |
62 | // 解析歌词
63 | const config = pluginConfig.get("lyrics");
64 | if ((config["retrieval_method"]["value"] == "2") && window.currentLyrics) {
65 | // 解决RNP歌词对不上的问题
66 | while (true) {
67 | if (window.currentLyrics.hash.includes(musicId)) {
68 | parsedLyric = window.currentLyrics.lyrics;
69 | break;
70 | } else {
71 | await betterncm.utils.delay(100);
72 | }
73 | }
74 | } else {
75 | const lyricData = await liblyric.getLyricData(musicId);
76 | parsedLyric = liblyric.parseLyric(
77 | lyricData?.lrc?.lyric ?? "",
78 | lyricData?.tlyric?.lyric ?? "",
79 | lyricData?.romalrc?.lyric ?? ""
80 | );
81 | }
82 |
83 |
84 | // 清除歌词空白行
85 | parsedLyric = parsedLyric.filter(item => item.originalLyric != "");
86 |
87 |
88 | // 纯音乐只显示歌曲名与作曲家
89 | if (
90 | (parsedLyric.length == 1)
91 | && (parsedLyric[0].time == 0)
92 | && (parsedLyric[0].duration != 0)
93 | ) {
94 | parsedLyric = [];
95 | }
96 |
97 | currentIndex = 0;
98 | }
99 |
100 |
101 | // 音乐进度发生变化时
102 | const play_progress = async (_, time) => {
103 | const adjust = Number(pluginConfig.get("effect")["adjust"]);
104 | if (parsedLyric) {
105 | let nextIndex = parsedLyric.findIndex(item => item.time > (time + adjust) * 1000);
106 | nextIndex = (nextIndex <= -1) ? parsedLyric.length : nextIndex;
107 |
108 | if (nextIndex != currentIndex) {
109 | const currentLyric = parsedLyric[nextIndex - 1] ?? "";
110 | const nextLyric = parsedLyric[nextIndex] ?? "";
111 |
112 | const lyrics = {
113 | "basic": currentLyric?.originalLyric ?? "",
114 | "extra": currentLyric?.translatedLyric ?? nextLyric?.originalLyric ?? ""
115 | };
116 |
117 | const extra_show_value = pluginConfig.get("effect")["extra_show"]["value"];
118 | switch (extra_show_value) {
119 | case 0: {
120 | lyrics.extra = "";
121 | } break;
122 |
123 | case 1: {
124 | const next_line_lyrics_position_value = pluginConfig.get("effect")["next_line_lyrics_position"]["value"];
125 | switch (next_line_lyrics_position_value) {
126 | case 0: {
127 | lyrics.extra = nextLyric?.originalLyric ?? "";
128 | } break;
129 |
130 | case 1: {
131 | lyrics.basic = nextLyric?.originalLyric ?? "";
132 | lyrics.extra = currentLyric?.originalLyric ?? "";
133 | } break;
134 |
135 | case 2: {
136 | if (currentLine == 0) {
137 | lyrics.basic = currentLyric?.originalLyric ?? "";
138 | lyrics.extra = nextLyric?.originalLyric ?? "";
139 | currentLine = 1;
140 | } else {
141 | lyrics.basic = nextLyric?.originalLyric ?? "";
142 | lyrics.extra = currentLyric?.originalLyric ?? "";
143 | currentLine = 0;
144 | }
145 | } break;
146 | }
147 | } break;
148 |
149 | case 2: {
150 | lyrics.extra = currentLyric?.translatedLyric
151 | ?? nextLyric?.originalLyric
152 | ?? "";
153 | } break;
154 |
155 | case 3: {
156 | lyrics.extra = currentLyric?.romanLyric
157 | ?? currentLyric?.translatedLyric
158 | ?? nextLyric?.originalLyric
159 | ?? "";
160 | } break;
161 | }
162 |
163 | TaskbarLyricsAPI.lyrics.lyrics(lyrics);
164 | currentIndex = nextIndex;
165 | }
166 | }
167 | }
168 |
169 |
170 |
171 | // 开始获取歌词
172 | function startGetLyric() {
173 | const config = pluginConfig.get("lyrics");
174 | switch (config["retrieval_method"]["value"]) {
175 | // 软件内词栏
176 | case 0: {
177 | watchLyricsChange();
178 | } break;
179 |
180 | // LibLyric
181 | case 1: {
182 | legacyNativeCmder.appendRegisterCall("Load", "audioplayer", play_load);
183 | legacyNativeCmder.appendRegisterCall("PlayProgress", "audioplayer", play_progress);
184 | const playingSong = betterncm.ncm.getPlayingSong();
185 | if (playingSong && playingSong.data.id != musicId) {
186 | play_load();
187 | }
188 | } break;
189 |
190 | // RefinedNowPlaying
191 | case 2: {
192 | legacyNativeCmder.appendRegisterCall("Load", "audioplayer", play_load);
193 | legacyNativeCmder.appendRegisterCall("PlayProgress", "audioplayer", play_progress);
194 | } break;
195 | }
196 | }
197 |
198 |
199 | // 停止获取歌词
200 | function stopGetLyric() {
201 | const config = pluginConfig.get("lyrics");
202 | switch (config["retrieval_method"]["value"]) {
203 | // 软件内词栏
204 | case 0: {
205 | if (observer) {
206 | observer.disconnect();
207 | observer = null;
208 | }
209 | } break;
210 |
211 | // LibLyric
212 | case 1: {
213 | legacyNativeCmder.removeRegisterCall("Load", "audioplayer", play_load);
214 | legacyNativeCmder.removeRegisterCall("PlayProgress", "audioplayer", play_progress);
215 | } break;
216 |
217 | // RefinedNowPlaying
218 | case 2: {
219 | legacyNativeCmder.removeRegisterCall("Load", "audioplayer", play_load);
220 | legacyNativeCmder.removeRegisterCall("PlayProgress", "audioplayer", play_progress);
221 | } break;
222 | }
223 | }
224 |
225 |
226 | this.lyric = {
227 | startGetLyric,
228 | stopGetLyric
229 | }
230 | });
231 |
--------------------------------------------------------------------------------
/dist/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 1,
3 | "name": "任务栏歌词",
4 | "slug": "Taskbar-Lyrics",
5 | "description": "在任务栏上嵌入歌词,目前仅建议Windows 11,且任务栏未被第三方软件修改",
6 | "preview": "preview.png",
7 | "version": "1.6.3",
8 | "author": "沫烬染",
9 | "author_links": [
10 | "https://github.com/mo-jinran"
11 | ],
12 | "type": "extension",
13 | "loadAfter": [
14 | "liblyric",
15 | "RefinedNowPlaying"
16 | ],
17 | "requirements": [
18 | "liblyric"
19 | ],
20 | "injects": {
21 | "Main": [
22 | {
23 | "file": "base.js"
24 | },
25 | {
26 | "file": "lyric.js"
27 | },
28 | {
29 | "file": "func.js"
30 | },
31 | {
32 | "file": "view.js"
33 | }
34 | ]
35 | }
36 | }
--------------------------------------------------------------------------------
/dist/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mo-jinran/Taskbar-Lyrics/66c45a9a5138c0e6963d0d04027746d6e99276c5/dist/preview.png
--------------------------------------------------------------------------------
/dist/style.css:
--------------------------------------------------------------------------------
1 | /* 根标签 */
2 | #taskbar-lyrics-dom {
3 | width: 100%;
4 | height: 100%;
5 | overflow: hidden;
6 | display: flex;
7 | flex-direction: column;
8 | gap: 16px;
9 | }
10 |
11 |
12 |
13 | /* 上边的tab区域 */
14 | #taskbar-lyrics-dom .tab_box {
15 | width: 100%;
16 | height: 40px;
17 | display: flex;
18 | }
19 |
20 | /* tab选项卡按钮 */
21 | #taskbar-lyrics-dom .tab_button {
22 | width: 60px;
23 | height: 100%;
24 | border: none;
25 | outline: none;
26 | background: none;
27 | margin: 0;
28 | font-size: 1rem;
29 | }
30 |
31 | /* tab选项卡按钮 激活效果 */
32 | #taskbar-lyrics-dom .tab_button.active {
33 | position: relative;
34 | font-weight: bold;
35 | }
36 |
37 | /* tab选项卡按钮 激活后底部黑白条 */
38 | #taskbar-lyrics-dom .tab_button.active::after {
39 | content: "";
40 | height: 5px;
41 | border-radius: 5px;
42 | background-color: rgba(255, 255, 255, 0.5);
43 | position: absolute;
44 | left: 10px;
45 | right: 10px;
46 | bottom: 0px;
47 | backdrop-filter: blur(4px);
48 | }
49 |
50 | body.s-theme-white #taskbar-lyrics-dom .tab_button.active::after {
51 | background-color: rgba(0, 0, 0, 0.5);
52 | }
53 |
54 |
55 |
56 | /* 下边的内容区域 */
57 | #taskbar-lyrics-dom .content_box {
58 | overflow: auto;
59 | flex: 1;
60 | }
61 |
62 | /* 所有内容隐藏 */
63 | #taskbar-lyrics-dom .content {
64 | display: none;
65 | }
66 |
67 | /* 指定内容显示 */
68 | #taskbar-lyrics-dom .content.show {
69 | display: block;
70 | }
71 |
72 |
73 |
74 | #taskbar-lyrics-dom .content h1 {
75 | height: 30px;
76 | display: flex;
77 | align-items: center;
78 | margin: 10px 0;
79 | gap: 10px;
80 | }
81 |
82 | #taskbar-lyrics-dom .content h1 strong {
83 | font-size: 1.25rem;
84 | font-weight: bold;
85 | }
86 |
87 | #taskbar-lyrics-dom .content hr {
88 | margin: 20px 0;
89 | border: none;
90 | height: 1px;
91 | background-color: rgba(255, 255, 255, 0.2);
92 | }
93 |
94 | body.s-theme-white #taskbar-lyrics-dom .content hr {
95 | background-color: rgba(0, 0, 0, 0.2);
96 | }
97 |
98 | #taskbar-lyrics-dom .content section>div {
99 | display: flex;
100 | min-height: 30px;
101 | margin: 5px 0;
102 | gap: 10px;
103 | }
104 |
105 | #taskbar-lyrics-dom .content .item_container {
106 | flex-direction: row;
107 | align-items: center;
108 | }
109 |
110 | #taskbar-lyrics-dom .content .text_container {
111 | flex-direction: column;
112 | align-items: start;
113 | }
114 |
115 | #taskbar-lyrics-dom .content p {
116 | font-size: 14px;
117 | }
118 |
119 | #taskbar-lyrics-dom .content a {
120 | font-weight: bold;
121 | }
122 |
123 |
124 |
125 | #taskbar-lyrics-dom .content input.color-input {
126 | width: 150px;
127 | padding: unset;
128 | }
129 |
130 | #taskbar-lyrics-dom .content input.number-input {
131 | width: 150px;
132 | }
133 |
134 | #taskbar-lyrics-dom .content input.switch-btn {
135 | width: 44px;
136 | height: 22px;
137 | appearance: none;
138 | border-radius: 20px;
139 | border: 1px solid rgba(255, 255, 255, 0.15);
140 | position: relative;
141 | }
142 |
143 | #taskbar-lyrics-dom .content input.switch-btn::before {
144 | content: "关";
145 | text-align: center;
146 | line-height: 18px;
147 | font-size: 12px;
148 | position: absolute;
149 | top: 1px;
150 | left: 1px;
151 | height: 18px;
152 | width: 18px;
153 | background-color: rgba(255, 255, 255, 0.3);
154 | border-radius: 50%;
155 | transition: all 0.1s ease-out;
156 | }
157 |
158 | #taskbar-lyrics-dom .content input.switch-btn:checked {
159 | background-color: rgba(255, 255, 255, 0.1);
160 | }
161 |
162 | #taskbar-lyrics-dom .content input.switch-btn:checked::before {
163 | content: "开";
164 | text-align: center;
165 | line-height: 18px;
166 | font-size: 12px;
167 | left: calc(100% - 19px);
168 | }
169 |
170 | body.s-theme-white #taskbar-lyrics-dom .content input.switch-btn {
171 | border: 1px solid rgba(0, 0, 0, 0.15);
172 | }
173 |
174 | body.s-theme-white #taskbar-lyrics-dom .content input.switch-btn::before {
175 | background-color: rgba(0, 0, 0, 0.3);
176 | }
177 |
178 | body.s-theme-white #taskbar-lyrics-dom .content input.switch-btn:checked {
179 | background-color: rgba(0, 0, 0, 0.1);
180 | }
181 |
182 |
183 |
184 | #taskbar-lyrics-dom .u-select {
185 | display: inline-block;
186 | width: 200px;
187 | }
188 |
189 | #taskbar-lyrics-dom input.u-txt {
190 | border-radius: 4px;
191 | }
--------------------------------------------------------------------------------
/dist/taskbar-lyrics.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mo-jinran/Taskbar-Lyrics/66c45a9a5138c0e6963d0d04027746d6e99276c5/dist/taskbar-lyrics.exe
--------------------------------------------------------------------------------
/dist/view.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | // 创建根视图
5 | const configView = document.createElement("div");
6 | configView.style.overflow = "hidden";
7 | configView.style.height = "100%";
8 | configView.style.width = "100%";
9 |
10 |
11 | plugin.onConfig(tools => configView);
12 |
13 |
14 | plugin.onLoad(async () => {
15 | const pluginConfig = this.base.pluginConfig;
16 | const {
17 | font,
18 | color,
19 | style,
20 | lyrics,
21 | effect,
22 | align,
23 | position,
24 | margin,
25 | screen,
26 | } = { ...this.func };
27 |
28 |
29 | // 加载结构
30 | {
31 | const path = `${this.pluginPath}/config.html`;
32 | const text = await betterncm.fs.readFileText(path);
33 | const parser = new DOMParser();
34 | const dom = parser.parseFromString(text, "text/html");
35 | const element = dom.querySelector("#taskbar-lyrics-dom");
36 | configView.appendChild(element);
37 | }
38 |
39 |
40 | // 加载样式
41 | {
42 | const path = `${this.pluginPath}/style.css`;
43 | const text = await betterncm.fs.readFileText(path);
44 | const element = document.createElement("style");
45 | element.textContent = text;
46 | configView.appendChild(element);
47 | }
48 |
49 |
50 | // 页面切换
51 | {
52 | const tab_box = configView.querySelector(".tab_box");
53 | const content_box = configView.querySelector(".content_box")
54 |
55 | const all_tab_button = tab_box.querySelectorAll(".tab_button");
56 | const all_content = content_box.querySelectorAll(".content");
57 |
58 | all_tab_button.forEach((tab, index) => {
59 | tab.addEventListener("click", () => {
60 | // 激活标签
61 | const active_tab = tab_box.querySelector(".active");
62 | active_tab.classList.remove("active");
63 | tab.classList.add("active");
64 | // 显示内容
65 | const show_content = content_box.querySelector(".show");
66 | show_content.classList.remove("show");
67 | all_content[index].classList.add("show");
68 | });
69 | });
70 | }
71 |
72 |
73 | // 通用的下拉选择框控制函数
74 | function selectController(event) {
75 | const open = event.target.parentElement.classList.contains("z-open");
76 | if (open) event.target.parentElement.classList.remove("z-open");
77 | else event.target.parentElement.classList.add("z-open");
78 | }
79 |
80 |
81 | // 点击其他地方收起下拉选择框
82 | addEventListener("pointerup", event => {
83 | if (!event.target.classList.contains("value")) {
84 | const open = configView.querySelectorAll(".u-select.z-open");
85 | open.forEach(value => value.classList.remove("z-open"));
86 | }
87 | });
88 |
89 |
90 | // 更换字体
91 | {
92 | const apply = configView.querySelector(".content.font .font-settings .apply");
93 | const reset = configView.querySelector(".content.font .font-settings .reset");
94 |
95 | const fontFamily = configView.querySelector(".content.font .font-settings .font-family");
96 |
97 | const elements = {
98 | fontFamily
99 | };
100 |
101 | apply.addEventListener("click", () => font.apply(elements));
102 | reset.addEventListener("click", () => font.reset(elements));
103 |
104 | fontFamily.value = pluginConfig.get("font")["font_family"];
105 | }
106 |
107 |
108 | // 字体颜色
109 | {
110 | const apply = configView.querySelector(".content.font .color-settings .apply");
111 | const reset = configView.querySelector(".content.font .color-settings .reset");
112 |
113 | const basicLightColor = configView.querySelector(".content.font .color-settings .basic-light-color");
114 | const basicLightOpacity = configView.querySelector(".content.font .color-settings .basic-light-opacity");
115 | const basicDarkColor = configView.querySelector(".content.font .color-settings .basic-dark-color");
116 | const basicDarkOpacity = configView.querySelector(".content.font .color-settings .basic-dark-opacity");
117 | const extraLightColor = configView.querySelector(".content.font .color-settings .extra-light-color");
118 | const extraLightOpacity = configView.querySelector(".content.font .color-settings .extra-light-opacity");
119 | const extraDarkColor = configView.querySelector(".content.font .color-settings .extra-dark-color");
120 | const extraDarkOpacity = configView.querySelector(".content.font .color-settings .extra-dark-opacity");
121 |
122 | const elements = {
123 | basicLightColor,
124 | basicLightOpacity,
125 | basicDarkColor,
126 | basicDarkOpacity,
127 | extraLightColor,
128 | extraLightOpacity,
129 | extraDarkColor,
130 | extraDarkOpacity
131 | }
132 |
133 | apply.addEventListener("click", () => color.apply(elements));
134 | reset.addEventListener("click", () => color.reset(elements));
135 |
136 | basicLightColor.value = `#${pluginConfig.get("color")["basic"]["light"]["hex_color"].toString(16)}`;
137 | basicLightOpacity.value = pluginConfig.get("color")["basic"]["light"]["opacity"];
138 | basicDarkColor.value = `#${pluginConfig.get("color")["basic"]["dark"]["hex_color"].toString(16)}`;
139 | basicDarkOpacity.value = pluginConfig.get("color")["basic"]["dark"]["opacity"];
140 | extraLightColor.value = `#${pluginConfig.get("color")["extra"]["light"]["hex_color"].toString(16)}`;
141 | extraLightOpacity.value = pluginConfig.get("color")["extra"]["light"]["opacity"];
142 | extraDarkColor.value = `#${pluginConfig.get("color")["extra"]["dark"]["hex_color"].toString(16)}`;
143 | extraDarkOpacity.value = pluginConfig.get("color")["extra"]["dark"]["opacity"];
144 | }
145 |
146 |
147 | // 字体样式
148 | {
149 | const reset = configView.querySelector(".content.font .style-settings .reset");
150 |
151 | const basicWeightValue = configView.querySelector(".content.font .style-settings .basic-weight .value");
152 | const basicWeightSelect = configView.querySelector(".content.font .style-settings .basic-weight .select");
153 | const basicNormal = configView.querySelector(".content.font .style-settings .basic-normal");
154 | const basicOblique = configView.querySelector(".content.font .style-settings .basic-oblique");
155 | const basicItalic = configView.querySelector(".content.font .style-settings .basic-italic");
156 | const basicUnderline = configView.querySelector(".content.font .style-settings .basic-underline");
157 | const basicStrikethrough = configView.querySelector(".content.font .style-settings .basic-strikethrough");
158 | const extraWeightValue = configView.querySelector(".content.font .style-settings .extra-weight .value");
159 | const extraWeightSelect = configView.querySelector(".content.font .style-settings .extra-weight .select");
160 | const extraNormal = configView.querySelector(".content.font .style-settings .extra-normal");
161 | const extraOblique = configView.querySelector(".content.font .style-settings .extra-oblique");
162 | const extraItalic = configView.querySelector(".content.font .style-settings .extra-italic");
163 | const extraUnderline = configView.querySelector(".content.font .style-settings .extra-underline");
164 | const extraStrikethrough = configView.querySelector(".content.font .style-settings .extra-strikethrough");
165 |
166 | const elements = {
167 | basicWeightValue,
168 | basicUnderline,
169 | basicStrikethrough,
170 | extraWeightValue,
171 | extraUnderline,
172 | extraStrikethrough
173 | }
174 |
175 | reset.addEventListener("click", () => style.reset(elements));
176 |
177 | basicNormal.addEventListener("click", event => style.setSlopeNormal(event));
178 | basicOblique.addEventListener("click", event => style.setSlopeOblique(event));
179 | basicItalic.addEventListener("click", event => style.setSlopeItalic(event));
180 | basicUnderline.addEventListener("change", event => style.setUnderline(event));
181 | basicStrikethrough.addEventListener("change", event => style.setStrikethrough(event));
182 | extraNormal.addEventListener("click", event => style.setSlopeNormal(event));
183 | extraOblique.addEventListener("click", event => style.setSlopeOblique(event));
184 | extraItalic.addEventListener("click", event => style.setSlopeItalic(event));
185 | extraUnderline.addEventListener("change", event => style.setUnderline(event));
186 | extraStrikethrough.addEventListener("change", event => style.setStrikethrough(event));
187 |
188 | basicWeightValue.addEventListener("click", selectController);
189 | basicWeightSelect.addEventListener("click", event => {
190 | const name = event.target.parentElement.dataset.type;
191 | const value = event.target.dataset.value;
192 | const textContent = event.target.textContent;
193 | style.setWeight(name, value, textContent);
194 | basicWeightValue.textContent = textContent;
195 | });
196 |
197 | extraWeightValue.addEventListener("click", selectController);
198 | extraWeightSelect.addEventListener("click", event => {
199 | const name = event.target.parentElement.dataset.type;
200 | const value = event.target.dataset.value;
201 | const textContent = event.target.textContent;
202 | style.setWeight(name, value, textContent);
203 | extraWeightValue.textContent = textContent;
204 | });
205 |
206 | basicWeightValue.textContent = pluginConfig.get("style")["basic"]["weight"]["textContent"];
207 | basicUnderline.checked = pluginConfig.get("style")["basic"]["underline"];
208 | basicStrikethrough.checked = pluginConfig.get("style")["basic"]["strikethrough"];
209 | extraWeightValue.textContent = pluginConfig.get("style")["extra"]["weight"]["textContent"];
210 | extraUnderline.checked = pluginConfig.get("style")["extra"]["underline"];
211 | extraStrikethrough.checked = pluginConfig.get("style")["extra"]["strikethrough"];
212 | }
213 |
214 |
215 | // 歌词设置
216 | {
217 | const reset = configView.querySelector(".content.lyrics .lyrics-settings .reset");
218 |
219 | const lyricsSwitch = configView.querySelector(".content.lyrics .lyrics-settings .lyrics-switch");
220 | const retrievalMethodValue = configView.querySelector(".content.lyrics .lyrics-settings .retrieval-method .value");
221 | const retrievalMethodSelect = configView.querySelector(".content.lyrics .lyrics-settings .retrieval-method .select");
222 |
223 | const elements = {
224 | retrievalMethodValue
225 | }
226 |
227 | reset.addEventListener("click", () => lyrics.reset(elements));
228 |
229 | lyricsSwitch.addEventListener("change", event => lyrics.lyricsSwitch(event));
230 |
231 | retrievalMethodValue.addEventListener("click", selectController);
232 | retrievalMethodSelect.addEventListener("click", event => {
233 | const value = event.target.dataset.value;
234 | const textContent = event.target.textContent;
235 | if ((value == "2") && (!window.currentLyrics)) {
236 | channel.call(
237 | "trayicon.popBalloon",
238 | () => { },
239 | [{
240 | title: "任务栏歌词",
241 | text: "无法使用RefinedNowPlaying歌词!\n是否安装RefinedNowPlaying插件?\n将回退到使用LibLyric解析获取歌词",
242 | icon: "path",
243 | hasSound: true,
244 | delayTime: 2e3
245 | }]
246 | );
247 | return;
248 | }
249 | lyrics.setRetrievalMethod(value, textContent);
250 | retrievalMethodValue.textContent = textContent;
251 | });
252 |
253 | retrievalMethodValue.textContent = pluginConfig.get("lyrics")["retrieval_method"]["textContent"];
254 | }
255 |
256 |
257 | // 显示效果
258 | {
259 | const apply = configView.querySelector(".content.lyrics .effect-settings .apply");
260 | const reset = configView.querySelector(".content.lyrics .effect-settings .reset");
261 |
262 | const nextLineLyricsPositionValue = configView.querySelector(".content.lyrics .effect-settings .next-line-lyrics-position .value");
263 | const nextLineLyricsPositionSelect = configView.querySelector(".content.lyrics .effect-settings .next-line-lyrics-position .select");
264 | const extraShowValue = configView.querySelector(".content.lyrics .effect-settings .extra-show .value");
265 | const extraShowSelect = configView.querySelector(".content.lyrics .effect-settings .extra-show .select");
266 | const adjust = configView.querySelector(".content.lyrics .effect-settings .adjust");
267 |
268 | const elements = {
269 | nextLineLyricsPositionValue,
270 | extraShowValue,
271 | adjust
272 | }
273 |
274 | apply.addEventListener("click", () => effect.apply(elements));
275 | reset.addEventListener("click", () => effect.reset(elements));
276 |
277 | nextLineLyricsPositionValue.addEventListener("click", selectController);
278 | nextLineLyricsPositionSelect.addEventListener("click", event => {
279 | const value = event.target.dataset.value;
280 | const textContent = event.target.textContent;
281 | effect.setNextLineLyricsPosition(value, textContent);
282 | nextLineLyricsPositionValue.textContent = textContent;
283 | });
284 |
285 | extraShowValue.addEventListener("click", selectController);
286 | extraShowSelect.addEventListener("click", event => {
287 | const value = event.target.dataset.value;
288 | const textContent = event.target.textContent;
289 | effect.setExtraShow(value, textContent);
290 | extraShowValue.textContent = textContent;
291 | });
292 |
293 | nextLineLyricsPositionValue.textContent = pluginConfig.get("effect")["next_line_lyrics_position"]["textContent"];
294 | extraShowValue.textContent = pluginConfig.get("effect")["extra_show"]["textContent"];
295 | adjust.value = pluginConfig.get("effect")["adjust"];
296 | }
297 |
298 |
299 | // 对齐方式
300 | {
301 | const reset = configView.querySelector(".content.lyrics .align-settings .reset");
302 |
303 | const basicLeft = configView.querySelector(".content.lyrics .align-settings .basic-left");
304 | const basicCenter = configView.querySelector(".content.lyrics .align-settings .basic-center");
305 | const basicRight = configView.querySelector(".content.lyrics .align-settings .basic-right");
306 | const extraLeft = configView.querySelector(".content.lyrics .align-settings .extra-left");
307 | const extraCenter = configView.querySelector(".content.lyrics .align-settings .extra-center");
308 | const extraRight = configView.querySelector(".content.lyrics .align-settings .extra-right");
309 |
310 | reset.addEventListener("click", () => align.reset());
311 |
312 | basicLeft.addEventListener("click", event => align.setLeft(event));
313 | basicCenter.addEventListener("click", event => align.setCenter(event));
314 | basicRight.addEventListener("click", event => align.setRight(event));
315 | extraLeft.addEventListener("click", event => align.setLeft(event));
316 | extraCenter.addEventListener("click", event => align.setCenter(event));
317 | extraRight.addEventListener("click", event => align.setRight(event));
318 | }
319 |
320 |
321 | // 修改位置
322 | {
323 | const reset = configView.querySelector(".content.window .position-settings .reset");
324 |
325 | const windowPositionValue = configView.querySelector(".content.window .position-settings .window-position .value");
326 | const windowPositionSelect = configView.querySelector(".content.window .position-settings .window-position .select");
327 |
328 | const elements = {
329 | windowPositionValue
330 | }
331 |
332 | reset.addEventListener("click", () => position.reset(elements));
333 |
334 | windowPositionValue.addEventListener("click", selectController);
335 | windowPositionSelect.addEventListener("click", event => {
336 | const value = event.target.dataset.value;
337 | const textContent = event.target.textContent;
338 | position.setWindowPosition(value, textContent);
339 | windowPositionValue.textContent = textContent;
340 | });
341 |
342 | windowPositionValue.textContent = pluginConfig.get("position")["position"]["textContent"];
343 | }
344 |
345 |
346 | // 修改边距
347 | {
348 | const apply = configView.querySelector(".content.window .margin-settings .apply");
349 | const reset = configView.querySelector(".content.window .margin-settings .reset");
350 |
351 | const left = configView.querySelector(".content.window .margin-settings .left");
352 | const right = configView.querySelector(".content.window .margin-settings .right");
353 |
354 | const elements = {
355 | left,
356 | right
357 | }
358 |
359 | apply.addEventListener("click", () => margin.apply(elements));
360 | reset.addEventListener("click", () => margin.reset(elements));
361 |
362 | left.value = pluginConfig.get("margin")["left"];
363 | right.value = pluginConfig.get("margin")["right"];
364 | }
365 |
366 |
367 | // 切换屏幕
368 | {
369 | const reset = configView.querySelector(".content.window .screen-settings .reset");
370 |
371 | const parentTaskbarValue = configView.querySelector(".content.window .screen-settings .parent-taskbar .value");
372 | const parentTaskbarSelect = configView.querySelector(".content.window .screen-settings .parent-taskbar .select");
373 |
374 | const elements = {
375 | parentTaskbarValue
376 | }
377 |
378 | reset.addEventListener("click", () => screen.reset(elements));
379 |
380 | parentTaskbarValue.addEventListener("click", selectController);
381 | parentTaskbarSelect.addEventListener("click", event => {
382 | const value = event.target.dataset.value;
383 | const textContent = event.target.textContent;
384 | screen.setParentTaskbar(value, textContent);
385 | parentTaskbarValue.textContent = textContent;
386 | });
387 |
388 | parentTaskbarValue.textContent = pluginConfig.get("screen")["parent_taskbar"]["textContent"];
389 | }
390 | });
--------------------------------------------------------------------------------
/src/betterncm-plugin/base.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | plugin.onLoad(async () => {
5 | const TaskbarLyricsPort = BETTERNCM_API_PORT - 2;
6 |
7 | const TaskbarLyricsFetch = (path, params) => fetch(
8 | `http://127.0.0.1:${TaskbarLyricsPort}/taskbar${path}`,
9 | {
10 | method: "POST",
11 | body: JSON.stringify(params),
12 | headers: {
13 | "Content-Type": "application/json"
14 | }
15 | }
16 | );
17 |
18 | const TaskbarLyricsAPI = {
19 | // 字体设置
20 | font: {
21 | font: params => TaskbarLyricsFetch("/font/font", params),
22 | color: params => TaskbarLyricsFetch("/font/color", params),
23 | style: params => TaskbarLyricsFetch("/font/style", params),
24 | },
25 |
26 | // 歌词设置
27 | lyrics: {
28 | lyrics: params => TaskbarLyricsFetch("/lyrics/lyrics", params),
29 | align: params => TaskbarLyricsFetch("/lyrics/align", params),
30 | },
31 |
32 | // 窗口设置
33 | window: {
34 | position: params => TaskbarLyricsFetch("/window/position", params),
35 | margin: params => TaskbarLyricsFetch("/window/margin", params),
36 | screen: params => TaskbarLyricsFetch("/window/screen", params),
37 | },
38 |
39 | // 关闭
40 | close: params => TaskbarLyricsFetch("/close", params)
41 | };
42 |
43 |
44 | // 对应Windows的枚举
45 | const WindowsEnum = {
46 | WindowAlignment: {
47 | WindowAlignmentAdaptive: 0,
48 | WindowAlignmentLeft: 1,
49 | WindowAlignmentCenter: 2,
50 | WindowAlignmentRight: 3
51 | },
52 | DWRITE_TEXT_ALIGNMENT: {
53 | DWRITE_TEXT_ALIGNMENT_LEADING: 0,
54 | DWRITE_TEXT_ALIGNMENT_TRAILING: 1,
55 | DWRITE_TEXT_ALIGNMENT_CENTER: 2,
56 | DWRITE_TEXT_ALIGNMENT_JUSTIFIED: 3
57 | },
58 | DWRITE_FONT_WEIGHT: {
59 | DWRITE_FONT_WEIGHT_THIN: 100,
60 | DWRITE_FONT_WEIGHT_EXTRA_LIGHT: 200,
61 | DWRITE_FONT_WEIGHT_ULTRA_LIGHT: 200,
62 | DWRITE_FONT_WEIGHT_LIGHT: 300,
63 | DWRITE_FONT_WEIGHT_SEMI_LIGHT: 350,
64 | DWRITE_FONT_WEIGHT_NORMAL: 400,
65 | DWRITE_FONT_WEIGHT_REGULAR: 400,
66 | DWRITE_FONT_WEIGHT_MEDIUM: 500,
67 | DWRITE_FONT_WEIGHT_DEMI_BOLD: 600,
68 | DWRITE_FONT_WEIGHT_SEMI_BOLD: 600,
69 | DWRITE_FONT_WEIGHT_BOLD: 700,
70 | DWRITE_FONT_WEIGHT_EXTRA_BOLD: 800,
71 | DWRITE_FONT_WEIGHT_ULTRA_BOLD: 800,
72 | DWRITE_FONT_WEIGHT_BLACK: 900,
73 | DWRITE_FONT_WEIGHT_HEAVY: 900,
74 | DWRITE_FONT_WEIGHT_EXTRA_BLACK: 950,
75 | DWRITE_FONT_WEIGHT_ULTRA_BLACK: 950
76 | },
77 | DWRITE_FONT_STYLE: {
78 | DWRITE_FONT_STYLE_NORMAL: 0,
79 | DWRITE_FONT_STYLE_OBLIQUE: 1,
80 | DWRITE_FONT_STYLE_ITALIC: 2
81 | }
82 | };
83 |
84 |
85 | // 默认的配置
86 | const defaultConfig = {
87 | "font": {
88 | "font_family": "Microsoft YaHei UI"
89 | },
90 | "color": {
91 | "basic": {
92 | "light": {
93 | "hex_color": 0x000000,
94 | "opacity": 1.0
95 | },
96 | "dark": {
97 | "hex_color": 0xFFFFFF,
98 | "opacity": 1.0
99 | }
100 | },
101 | "extra": {
102 | "light": {
103 | "hex_color": 0x000000,
104 | "opacity": 1.0
105 | },
106 | "dark": {
107 | "hex_color": 0xFFFFFF,
108 | "opacity": 1.0
109 | }
110 | }
111 | },
112 | "style": {
113 | "basic": {
114 | "weight": {
115 | "value": WindowsEnum.DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_NORMAL,
116 | "textContent": "Normal (400)"
117 | },
118 | "slope": WindowsEnum.DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL,
119 | "underline": false,
120 | "strikethrough": false
121 | },
122 | "extra": {
123 | "weight": {
124 | "value": WindowsEnum.DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_NORMAL,
125 | "textContent": "Normal (400)"
126 | },
127 | "slope": WindowsEnum.DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL,
128 | "underline": false,
129 | "strikethrough": false
130 | }
131 | },
132 | "lyrics": {
133 | "retrieval_method": {
134 | "value": 1,
135 | "textContent": "使用LibLyric解析获取歌词",
136 | }
137 | },
138 | "effect": {
139 | "next_line_lyrics_position": {
140 | "value": 0,
141 | "textContent": "副歌词,下句歌词显示在这"
142 | },
143 | "extra_show": {
144 | "value": 2,
145 | "textContent": "当前翻译,没则用上个选项"
146 | },
147 | "adjust": 0.0
148 | },
149 | "align": {
150 | "basic": WindowsEnum.DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_LEADING,
151 | "extra": WindowsEnum.DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_LEADING
152 | },
153 | "position": {
154 | "position": {
155 | "value": WindowsEnum.WindowAlignment.WindowAlignmentAdaptive,
156 | "textContent": "自动,自适应选择左或右"
157 | }
158 | },
159 | "margin": {
160 | "left": 0,
161 | "right": 0
162 | },
163 | "screen": {
164 | "parent_taskbar": {
165 | "value": "Shell_TrayWnd",
166 | "textContent": "主屏幕任务栏"
167 | }
168 | }
169 | };
170 |
171 |
172 | const pluginConfig = {
173 | get: name => Object.assign({}, defaultConfig[name], plugin.getConfig(name, defaultConfig[name])),
174 | set: (name, value) => plugin.setConfig(name, value)
175 | };
176 |
177 |
178 | this.base = {
179 | TaskbarLyricsPort,
180 | TaskbarLyricsAPI,
181 | WindowsEnum,
182 | defaultConfig,
183 | pluginConfig
184 | };
185 | });
186 |
--------------------------------------------------------------------------------
/src/betterncm-plugin/config.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
27 |
28 |
29 |
30 |
31 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 字体样式:
69 |
70 |
71 |
72 |
主歌词-字体字重:
73 |
74 |
Normal (400)
75 |
76 |
77 | - Thin (100)
78 | - Extra-light (200)
79 | - Ultra-light (200)
80 | - Light (300)
81 | - Semi-light (350)
82 | - Normal (400)
83 | - Regular (400)
84 | - Medium (500)
85 | - Demi-bold (600)
86 | - Semi-bold (600)
87 | - Bold (700)
88 | - Extra-bold (800)
89 | - Ultra-bold (800)
90 | - Black (900)
91 | - Heavy (900)
92 | - Extra-black (950)
93 | - Ultra-black (950)
94 |
95 |
96 |
97 |
98 |
99 | 主歌词-字体斜体:
100 |
101 |
102 |
103 |
104 |
105 | 主歌词-下划线:
106 |
107 |
108 |
109 | 主歌词-删除线:
110 |
111 |
112 |
113 | 副歌词-字体字重:
114 |
138 |
139 |
140 | 副歌词-字体斜体:
141 |
142 |
143 |
144 |
145 |
146 | 副歌词-下划线:
147 |
148 |
149 |
150 | 副歌词-删除线:
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | 歌词设置:
162 |
163 |
164 |
165 | 歌词开关:
166 |
167 |
168 |
169 |
获取方法:
170 |
171 |
使用LibLyric解析获取歌词
172 |
173 |
174 | - 使用软件内词栏监听获取歌词
175 | - 使用LibLyric解析获取歌词
176 | - 使用RefinedNowPlaying歌词
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 | 显示效果:
189 |
190 |
191 |
192 |
193 | 延迟调整:
194 |
195 |
196 |
197 |
下句歌词位置:
198 |
199 |
副歌词,下句歌词显示在这
200 |
201 |
202 | - 副歌词,下句歌词显示在这
203 | - 主歌词,下句歌词显示在这
204 | - 交错式,就跟桌面歌词一样
205 |
206 |
207 |
208 |
209 |
210 | 副歌词显示:
211 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 | 对齐方式:
231 |
232 |
233 |
234 | 主歌词:
235 |
236 |
237 |
238 |
239 |
240 | 副歌词:
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 | 修改位置:
254 |
255 |
256 |
257 |
窗口位置:
258 |
259 |
靠左,占满剩余空间宽度
260 |
261 |
262 | - 自动,自适应选择左或右
263 | - 靠左,占满剩余空间宽度
264 | - 中间,基于任务栏的宽度
265 | - 靠右,占满剩余空间宽度
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
290 |
291 |
292 |
293 |
294 |
295 |
296 | 切换屏幕:(实验性功能,目前在副屏有BUG)
297 |
298 |
299 |
300 |
父任务栏:
301 |
302 |
主屏幕任务栏
303 |
304 |
305 | - 主屏幕任务栏
306 | - 副屏幕任务栏
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 | 关于插件:
320 |
321 |
322 |
323 | 作者名称:
324 | 🌈沫烬染🐾
325 |
326 |
327 | 仓库链接:
328 | Taskbar-Lyrics
329 |
330 |
==================================================
331 |
以上链接均为Github,喜欢就来点个Star吧~
332 |
如有BUG或建议欢迎前来发Issue
333 |
有大佬感兴趣来Fork本项目提交PR
334 |
来帮助我们将此插件变得更好!
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 | 注意事项:
344 |
345 |
346 |
本插件适用于Windows 11,且任务栏没用第三方软件重度修改
347 |
如遇到某些选项不生效,请尝试点击选项旁边的“恢复默认”按钮
348 |
如遇到不显示歌词,并且没有个性化的效果,请尝试“重载网易云”
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 | 杂七杂八:
358 |
359 |
360 |
插件名称是“Taskbar-Lyrics”,“任务栏歌词”,不是状态栏呀
361 |
插件并非为BetterNCM提供的Native Plugin(DLL形式注入)
362 |
插件会启动任务栏歌词程序,并嵌入到任务栏中和开启HTTP服务器
363 |
插件端负责获取播放状态与歌词和一些个性化配置提供给软件端
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 | 1.6.3
373 |
374 |
375 |
-> | 优化代码,移除没必要的代码
376 |
-> | 修复打开下拉选择框后,点击其他区域无法收起的问题
377 |
-> | 修复使用RefinedNowPlaying歌词可能对不上的问题
378 |
-> | 将API的端口号从BetterNCM API的端口号+2改成-2
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 | 1.6.2
387 |
388 |
389 |
-> | 修复一些选项失效的BUG
390 |
-> | 添加插件更新日志(之前的日志的会有些差错
391 |
-> | 添加新的歌词获取方式,使用RefinedNowPlaying歌词
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 | 1.6.1
400 |
401 |
402 |
-> | 琐事调整,优化代码,统一风格
403 |
-> | 将一些选项换成下拉式选择框
404 |
-> | 添加窗口位置自适应
405 |
-> | 修复程序某些情况崩溃问题
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 | 1.6.0
414 |
415 |
416 |
-> | 优化代码,区分API与设置的类别
417 |
-> | 添加显示效果设置,下句歌词位置
418 |
-> | 将副歌词显示与延迟调整移动到显示效果内
419 |
-> | 修改默认字体为:Microsoft YaHei UI
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 | 1.5.7
428 |
429 |
430 |
-> | 纯音乐显示歌曲名与作曲家
431 |
-> | 修改设置界面分类
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 | 1.5.6
440 |
441 |
442 |
-> | 修复获取的路径不标准导致无法启动软件的问题
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 | 1.5.5
451 |
452 |
453 |
-> | 尝试解决软件不会启动的问题
454 |
-> | 优化代码,移除没用到的引入
455 |
-> | 解决刚打开软件时获取不到数据而报错的问题
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 | 1.5.4
464 |
465 |
466 |
-> | 优化代码,删除无用的调试语句与注释
467 |
-> | 调整配置界面样式,配置界面适配亮暗主题
468 |
-> | 重写文字排版,修复文字右对齐后最后一个字只剩一半,修复文字超出部分没有被隐藏
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 | 1.5.3
477 |
478 |
479 |
-> | 优化代码,调整配置界面,调整代码位置,删除调试语句
480 |
-> | 读取配置时,没有或无效的将使用默认值
481 |
-> | 解决切换歌曲后再切换到新的歌词获取方式时无法重新获取歌词的问题
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 | 1.5.2
490 |
491 |
492 |
-> | 移除对libsonginfo的依赖
493 |
-> | 解决插件占用文件夹,复制软件到其他目录后运行
494 |
-> | 添加切换歌词获取方式选项
495 |
-> | 修改注意事项
496 |
-> | 优化代码,调整配置界面布局
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 | 1.5.1
505 |
506 |
507 |
-> | 优化代码,调整配置界面
508 |
-> | 添加歌词时间调整
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 | 1.5.0
517 |
518 |
519 |
-> | 重构/优化代码,重构配置界面,移除软件的互斥锁,修改一部分API
520 |
-> | 添加libsonginfo与liblyric依赖
521 |
-> | 添加通过liblyric获取歌词,并设为默认方式
522 |
-> | 添加修改副歌词的选项
523 |
-> | 修复下拉式选择框只能显示值的问题
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 | 1.4.3
532 |
533 |
534 |
-> | 优化代码,修改函数导出导入,拆分代码
535 |
-> | 修复样式设置不生效的问题
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 | 1.4.2
544 |
545 |
546 |
-> | 优化代码
547 |
-> | 添加回字体样式功能
548 |
-> | 修复样式设置不生效的问题
549 |
-> | 修复读取到不兼容的配置导致无法使用的问题
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 | 1.4.1
558 |
559 |
560 |
-> | 优化代码,删除无用代码
561 |
-> | 修复第一次打开某些输入框不显示默认值
562 |
-> | 关闭歌词时停止对歌词变动的监控
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 | 1.4.0
571 |
572 |
573 |
-> | 优化代码,删除无用代码
574 |
-> | 重构代码,从GDI+切换为Direct2D与DirectWrite
575 |
-> | 重构代码,插件端,将单文件拆分代码为多文件并注入
576 |
-> | 修复第一次打开某些输入框不显示默认值
577 |
-> | 关闭歌词时停止对歌词变动的监控
578 |
-> | 修复歌词位置无法设置在左边的问题
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 | 1.3.1
587 |
588 |
589 |
-> | 微调歌词的字体大小与边距
590 |
-> | 优化代码,优化设置界面
591 |
-> | 更改为使用JSON传输数据
592 |
-> | 添加基于整个任务栏宽度居中的按钮
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 | 1.3.0
601 |
602 |
603 |
-> | 添加修改边距选项
604 |
-> | 添加字体样式选项
605 |
-> | 修复歌词在手动关闭后再开启设置不生效的问题
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 | 1.2.4
614 |
615 |
616 |
-> | 优化代码
617 |
-> | 添加歌词开关选项
618 |
-> | 修复关闭程序后本地http服务线程不会立刻结束的问题
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 | 1.2.3
627 |
628 |
629 |
-> | 重构代码,软件端,优化代码
630 |
-> | 微调歌词的字体渲染与边距
631 |
-> | 更新插件预览图
632 |
-> | 修复修改位置某些情况下失效的问题
633 |
-> | 支持更多版本的任务栏
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 | 1.2.2
642 |
643 |
644 |
-> | 修复了会显示控制台的问题
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 | 1.2.1
653 |
654 |
655 |
-> | 修复了插件商店安装插件后无法启动的问题
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 | 1.2.0
664 |
665 |
666 |
-> | 重构代码,优化代码,将软件端单文件拆分代码为多文件
667 |
-> | 添加启动,连接,关闭的提示
668 |
-> | 兼容Windows 11 21H2版本任务栏
669 |
-> | 修复页面刷新后不会关闭程序
670 |
-> | 修复歌词过长导致换行的问题
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 | 1.1.0
679 |
680 |
683 |
684 |
685 |
686 |
687 |
688 |
689 | 1.0.4
690 |
691 |
692 |
-> | 优化代码
693 |
-> | 添加配置界面
694 |
-> | 自适应任务栏剩余宽度
695 |
-> | 添加切换屏幕选项
696 |
-> | 添加更换字体选项
697 |
-> | 添加字体颜色选项
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 | 1.0.3
706 |
707 |
708 |
-> | 修复歌词带问号导致不显示
709 |
-> | 添加互斥锁,修复重复启动软件导致重叠多个窗口
710 |
-> | 监听网易云进程关闭
711 |
-> | 移除心跳检测机制
712 |
-> | 移除Worker
713 |
-> | 移除Hijack
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 | 1.0.2
722 |
723 |
724 |
-> | 自适应windows深浅色模式
725 |
-> | 单行歌词放大居中占满双行
726 |
-> | 修复歌词上右键卡住的问题
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 | 1.0.1
735 |
736 |
737 |
-> | 修复软件路径问题
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 | 1.0.0
746 |
747 |
748 |
-> | 添加任务栏歌词插件
749 |
750 |
751 |
752 |
753 |
--------------------------------------------------------------------------------
/src/betterncm-plugin/func.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | plugin.onLoad(async () => {
5 | const {
6 | TaskbarLyricsPort,
7 | TaskbarLyricsAPI,
8 | WindowsEnum,
9 | defaultConfig,
10 | pluginConfig
11 | } = { ...this.base };
12 | const { startGetLyric, stopGetLyric } = { ...this.lyric };
13 |
14 |
15 | // 启动任务栏歌词软件
16 | const TaskbarLyricsStart = async () => {
17 | // 这BetterNCM获取的路径是不标准的会出问题,要替换掉下面那俩字符
18 | const dataPath = (await betterncm.app.getDataPath()).replace("/", "\\");
19 | const pluginPath = this.pluginPath.replace("/./", "\\").replace("/", "\\");
20 | const taskkill = `taskkill /F /IM "taskbar-lyrics.exe"`;
21 | const xcopy = `xcopy /C /D /Y "${pluginPath}\\taskbar-lyrics.exe" "${dataPath}"`;
22 | const exec = `"${dataPath}\\taskbar-lyrics.exe" ${TaskbarLyricsPort}`;
23 | const cmd = `${taskkill} & ${xcopy} && ${exec}`;
24 | await betterncm.app.exec(`cmd /S /C ${cmd}`, false, false);
25 | TaskbarLyricsAPI.font.font(pluginConfig.get("font"));
26 | TaskbarLyricsAPI.font.color(pluginConfig.get("color"));
27 | TaskbarLyricsAPI.font.style(pluginConfig.get("style"));
28 | TaskbarLyricsAPI.window.position(pluginConfig.get("position"));
29 | TaskbarLyricsAPI.window.margin(pluginConfig.get("margin"));
30 | TaskbarLyricsAPI.lyrics.align(pluginConfig.get("align"));
31 | TaskbarLyricsAPI.window.screen(pluginConfig.get("screen"));
32 | startGetLyric();
33 | };
34 |
35 |
36 | // 关闭任务栏歌词软件
37 | const TaskbarLyricsClose = async () => {
38 | TaskbarLyricsAPI.close({});
39 | stopGetLyric();
40 | };
41 |
42 |
43 | addEventListener("beforeunload", TaskbarLyricsClose);
44 | TaskbarLyricsStart();
45 |
46 |
47 | // 更换字体
48 | const font = {
49 | apply: elements => {
50 | const config = JSON.parse(JSON.stringify(pluginConfig.get("font")));
51 | config["font_family"] = elements.fontFamily.value;
52 | pluginConfig.set("font", config);
53 | TaskbarLyricsAPI.font.font(config);
54 | },
55 | reset: elements => {
56 | pluginConfig.set("font", undefined);
57 | TaskbarLyricsAPI.font.font(defaultConfig["font"]);
58 | elements.fontFamily.value = defaultConfig["font"]["font_family"];
59 | }
60 | }
61 |
62 |
63 | // 字体颜色
64 | const color = {
65 | apply: elements => {
66 | const config = JSON.parse(JSON.stringify(pluginConfig.get("color")));
67 | config["basic"]["light"]["hex_color"] = parseInt(elements.basicLightColor.value.slice(1), 16);
68 | config["basic"]["light"]["opacity"] = Number(elements.basicLightOpacity.value);
69 | config["basic"]["dark"]["hex_color"] = parseInt(elements.basicDarkColor.value.slice(1), 16);
70 | config["basic"]["dark"]["opacity"] = Number(elements.basicDarkOpacity.value);
71 | config["extra"]["light"]["hex_color"] = parseInt(elements.extraLightColor.value.slice(1), 16);
72 | config["extra"]["light"]["opacity"] = Number(elements.extraLightOpacity.value);
73 | config["extra"]["dark"]["hex_color"] = parseInt(elements.extraDarkColor.value.slice(1), 16);
74 | config["extra"]["dark"]["opacity"] = Number(elements.extraDarkOpacity.value);
75 | pluginConfig.set("color", config);
76 | TaskbarLyricsAPI.font.color(config);
77 | },
78 | reset: elements => {
79 | elements.basicLightColor.value = `#${defaultConfig["color"]["basic"]["light"]["hex_color"].toString(16).padStart(6, "0")}`;
80 | elements.basicLightOpacity.value = defaultConfig["color"]["basic"]["light"]["opacity"];
81 | elements.basicDarkColor.value = `#${defaultConfig["color"]["basic"]["dark"]["hex_color"].toString(16).padStart(6, "0")}`;
82 | elements.basicDarkOpacity.value = defaultConfig["color"]["basic"]["dark"]["opacity"];
83 | elements.extraLightColor.value = `#${defaultConfig["color"]["extra"]["light"]["hex_color"].toString(16).padStart(6, "0")}`;
84 | elements.extraLightOpacity.value = defaultConfig["color"]["extra"]["light"]["opacity"];
85 | elements.extraDarkColor.value = `#${defaultConfig["color"]["extra"]["dark"]["hex_color"].toString(16).padStart(6, "0")}`;
86 | elements.extraDarkOpacity.value = defaultConfig["color"]["extra"]["dark"]["opacity"];
87 | pluginConfig.set("color", undefined);
88 | TaskbarLyricsAPI.font.color(defaultConfig["color"]);
89 | }
90 | }
91 |
92 |
93 | // 字体样式
94 | const style = {
95 | setWeight: (name, value, textContent) => {
96 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
97 | config[name].weight.value = Number(value);
98 | config[name].weight.textContent = textContent;
99 | pluginConfig.set("style", config);
100 | TaskbarLyricsAPI.font.style(config);
101 | },
102 | setSlopeNormal: event => {
103 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
104 | config[event.target.dataset.type].slope = WindowsEnum.DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL;
105 | pluginConfig.set("style", config);
106 | TaskbarLyricsAPI.font.style(config);
107 | },
108 | setSlopeOblique: event => {
109 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
110 | config[event.target.dataset.type].slope = WindowsEnum.DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_OBLIQUE;
111 | pluginConfig.set("style", config);
112 | TaskbarLyricsAPI.font.style(config);
113 | },
114 | setSlopeItalic: event => {
115 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
116 | config[event.target.dataset.type].slope = WindowsEnum.DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_ITALIC;
117 | pluginConfig.set("style", config);
118 | TaskbarLyricsAPI.font.style(config);
119 | },
120 | setUnderline: event => {
121 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
122 | config[event.target.dataset.type].underline = event.target.checked;
123 | pluginConfig.set("style", config);
124 | TaskbarLyricsAPI.font.style(config);
125 | },
126 | setStrikethrough: event => {
127 | const config = JSON.parse(JSON.stringify(pluginConfig.get("style")));
128 | config[event.target.dataset.type].strikethrough = event.target.checked;
129 | pluginConfig.set("style", config);
130 | TaskbarLyricsAPI.font.style(config);
131 | },
132 | reset: elements => {
133 | pluginConfig.set("style", undefined);
134 | TaskbarLyricsAPI.font.style(defaultConfig["style"]);
135 | elements.basicWeightValue.textContent = defaultConfig["style"]["basic"]["weight"]["textContent"];
136 | elements.basicUnderline.checked = defaultConfig["style"]["basic"]["underline"];
137 | elements.basicStrikethrough.checked = defaultConfig["style"]["basic"]["strikethrough"];
138 | elements.extraWeightValue.textContent = defaultConfig["style"]["extra"]["weight"]["textContent"];
139 | elements.extraUnderline.checked = defaultConfig["style"]["extra"]["underline"];
140 | elements.extraStrikethrough.checked = defaultConfig["style"]["extra"]["strikethrough"];
141 | }
142 | }
143 |
144 |
145 | // 歌词设置
146 | const lyrics = {
147 | lyricsSwitch: event => event.target.checked ? TaskbarLyricsStart() : TaskbarLyricsClose(),
148 | setRetrievalMethod: (value, textContent) => {
149 | const config = JSON.parse(JSON.stringify(pluginConfig.get("lyrics")));
150 | config["retrieval_method"]["value"] = Number(value);
151 | config["retrieval_method"]["textContent"] = textContent;
152 | stopGetLyric();
153 | pluginConfig.set("lyrics", config);
154 | startGetLyric();
155 | },
156 | reset: elements => {
157 | elements.retrievalMethodValue.textContent = defaultConfig["lyrics"]["retrieval_method"]["textContent"];
158 | stopGetLyric();
159 | pluginConfig.set("lyrics", undefined);
160 | startGetLyric();
161 | }
162 | }
163 |
164 |
165 | // 显示效果
166 | const effect = {
167 | setNextLineLyricsPosition: (value, textContent) => {
168 | const config = JSON.parse(JSON.stringify(pluginConfig.get("effect")));
169 | config["next_line_lyrics_position"]["value"] = Number(value);
170 | config["next_line_lyrics_position"]["textContent"] = textContent;
171 | pluginConfig.set("effect", config);
172 | },
173 | setExtraShow: (value, textContent) => {
174 | const config = JSON.parse(JSON.stringify(pluginConfig.get("effect")));
175 | config["extra_show"]["value"] = Number(value);
176 | config["extra_show"]["textContent"] = textContent;
177 | pluginConfig.set("effect", config);
178 | },
179 | apply: elements => {
180 | const config = JSON.parse(JSON.stringify(pluginConfig.get("effect")));
181 | config["adjust"] = Number(elements.adjust.value);
182 | pluginConfig.set("effect", config);
183 | },
184 | reset: elements => {
185 | elements.nextLineLyricsPositionValue.textContent = defaultConfig["effect"]["next_line_lyrics_position"]["textContent"];
186 | elements.extraShowValue.textContent = defaultConfig["effect"]["extra_show"]["textContent"];
187 | elements.adjust.value = defaultConfig["effect"]["adjust"];
188 | pluginConfig.set("effect", undefined);
189 | }
190 | }
191 |
192 |
193 | // 对齐方式
194 | const align = {
195 | setLeft: event => {
196 | const config = JSON.parse(JSON.stringify(pluginConfig.get("align")));
197 | config[event.target.dataset.type] = WindowsEnum.DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_LEADING;
198 | pluginConfig.set("align", config);
199 | TaskbarLyricsAPI.lyrics.align(config);
200 | },
201 | setCenter: event => {
202 | const config = JSON.parse(JSON.stringify(pluginConfig.get("align")));
203 | config[event.target.dataset.type] = WindowsEnum.DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_CENTER;
204 | pluginConfig.set("align", config);
205 | TaskbarLyricsAPI.lyrics.align(config);
206 | },
207 | setRight: event => {
208 | const config = JSON.parse(JSON.stringify(pluginConfig.get("align")));
209 | config[event.target.dataset.type] = WindowsEnum.DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_TRAILING;
210 | pluginConfig.set("align", config);
211 | TaskbarLyricsAPI.lyrics.align(config);
212 | },
213 | reset: () => {
214 | pluginConfig.set("align", undefined);
215 | TaskbarLyricsAPI.lyrics.align(defaultConfig["align"]);
216 | }
217 | }
218 |
219 |
220 | // 修改位置
221 | const position = {
222 | setWindowPosition: (value, textContent) => {
223 | const config = JSON.parse(JSON.stringify(pluginConfig.get("position")));
224 | config["position"]["value"] = Number(value);
225 | config["position"]["textContent"] = textContent;
226 | pluginConfig.set("position", config);
227 | TaskbarLyricsAPI.window.position(config);
228 | },
229 | reset: elements => {
230 | elements.windowPositionValue.textContent = defaultConfig["position"]["position"]["textContent"];
231 | pluginConfig.set("position", undefined);
232 | TaskbarLyricsAPI.window.position(defaultConfig["position"]);
233 | }
234 | }
235 |
236 |
237 | // 修改边距
238 | const margin = {
239 | apply: elements => {
240 | const config = JSON.parse(JSON.stringify(pluginConfig.get("margin")));
241 | config["left"] = Number(elements.left.value);
242 | config["right"] = Number(elements.right.value);
243 | pluginConfig.set("margin", config);
244 | TaskbarLyricsAPI.window.margin(config);
245 | },
246 | reset: elements => {
247 | pluginConfig.set("margin", undefined);
248 | TaskbarLyricsAPI.window.margin(defaultConfig["margin"]);
249 | elements.left.value = defaultConfig["margin"]["left"];
250 | elements.right.value = defaultConfig["margin"]["right"];
251 | }
252 | }
253 |
254 |
255 | // 切换屏幕
256 | const screen = {
257 | setParentTaskbar: (value, textContent) => {
258 | const config = JSON.parse(JSON.stringify(pluginConfig.get("screen")));
259 | config["parent_taskbar"]["value"] = value;
260 | config["parent_taskbar"]["textContent"] = textContent;
261 | pluginConfig.set("screen", config);
262 | TaskbarLyricsAPI.window.screen(config);
263 | },
264 | reset: elements => {
265 | elements.parentTaskbarValue.textContent = defaultConfig["screen"]["parent_taskbar"]["textContent"];
266 | pluginConfig.set("screen", undefined);
267 | TaskbarLyricsAPI.window.screen(defaultConfig["screen"]);
268 | }
269 | }
270 |
271 |
272 | this.func = {
273 | font,
274 | color,
275 | style,
276 | lyrics,
277 | effect,
278 | align,
279 | position,
280 | margin,
281 | screen
282 | };
283 | });
284 |
--------------------------------------------------------------------------------
/src/betterncm-plugin/lyric.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | plugin.onLoad(async () => {
5 | const { TaskbarLyricsAPI, pluginConfig } = { ...this.base };
6 | const liblyric = loadedPlugins.liblyric;
7 |
8 |
9 | let observer = null;
10 | let parsedLyric = null;
11 | let currentIndex = 0;
12 | let musicId = 0;
13 | let currentLine = 0;
14 |
15 |
16 | // 监视软件内歌词变动
17 | const watchLyricsChange = async () => {
18 | const mLyric = await betterncm.utils.waitForElement("#x-g-mn .m-lyric");
19 | const MutationCallback = mutations => {
20 | for (const mutation of mutations) {
21 | let lyrics = {
22 | basic: "",
23 | extra: ""
24 | };
25 |
26 | if (mutation.addedNodes[2]) {
27 | lyrics.basic = mutation.addedNodes[0].firstChild.textContent;
28 | lyrics.extra = mutation.addedNodes[2].firstChild ? mutation.addedNodes[2].firstChild.textContent : "";
29 | } else {
30 | lyrics.basic = mutation.addedNodes[0].textContent;
31 | }
32 |
33 | TaskbarLyricsAPI.lyrics.lyrics(lyrics);
34 | }
35 | }
36 |
37 | observer = new MutationObserver(MutationCallback);
38 | observer.observe(mLyric, { childList: true, subtree: true });
39 | }
40 |
41 |
42 | // 音乐ID发生变化时
43 | const play_load = async () => {
44 | // 获取歌曲信息
45 | const playingSong = betterncm.ncm.getPlayingSong();
46 | musicId = playingSong.data.id ?? 0;
47 | const name = playingSong.data.name ?? "";
48 | const artists = playingSong.data.artists ?? "";
49 |
50 | // 解析歌手名称
51 | let artistName = "";
52 | artists.forEach(item => artistName += ` / ${item.name}`);
53 | artistName = artistName.slice(3);
54 |
55 | // 发送歌曲信息
56 | TaskbarLyricsAPI.lyrics.lyrics({
57 | "basic": name,
58 | "extra": artistName
59 | });
60 |
61 |
62 | // 解析歌词
63 | const config = pluginConfig.get("lyrics");
64 | if ((config["retrieval_method"]["value"] == "2") && window.currentLyrics) {
65 | // 解决RNP歌词对不上的问题
66 | while (true) {
67 | if (window.currentLyrics.hash.includes(musicId)) {
68 | parsedLyric = window.currentLyrics.lyrics;
69 | break;
70 | } else {
71 | await betterncm.utils.delay(100);
72 | }
73 | }
74 | } else {
75 | const lyricData = await liblyric.getLyricData(musicId);
76 | parsedLyric = liblyric.parseLyric(
77 | lyricData?.lrc?.lyric ?? "",
78 | lyricData?.tlyric?.lyric ?? "",
79 | lyricData?.romalrc?.lyric ?? ""
80 | );
81 | }
82 |
83 |
84 | // 清除歌词空白行
85 | parsedLyric = parsedLyric.filter(item => item.originalLyric != "");
86 |
87 |
88 | // 纯音乐只显示歌曲名与作曲家
89 | if (
90 | (parsedLyric.length == 1)
91 | && (parsedLyric[0].time == 0)
92 | && (parsedLyric[0].duration != 0)
93 | ) {
94 | parsedLyric = [];
95 | }
96 |
97 | currentIndex = 0;
98 | }
99 |
100 |
101 | // 音乐进度发生变化时
102 | const play_progress = async (_, time) => {
103 | const adjust = Number(pluginConfig.get("effect")["adjust"]);
104 | if (parsedLyric) {
105 | let nextIndex = parsedLyric.findIndex(item => item.time > (time + adjust) * 1000);
106 | nextIndex = (nextIndex <= -1) ? parsedLyric.length : nextIndex;
107 |
108 | if (nextIndex != currentIndex) {
109 | const currentLyric = parsedLyric[nextIndex - 1] ?? "";
110 | const nextLyric = parsedLyric[nextIndex] ?? "";
111 |
112 | const lyrics = {
113 | "basic": currentLyric?.originalLyric ?? "",
114 | "extra": currentLyric?.translatedLyric ?? nextLyric?.originalLyric ?? ""
115 | };
116 |
117 | const extra_show_value = pluginConfig.get("effect")["extra_show"]["value"];
118 | switch (extra_show_value) {
119 | case 0: {
120 | lyrics.extra = "";
121 | } break;
122 |
123 | case 1: {
124 | const next_line_lyrics_position_value = pluginConfig.get("effect")["next_line_lyrics_position"]["value"];
125 | switch (next_line_lyrics_position_value) {
126 | case 0: {
127 | lyrics.extra = nextLyric?.originalLyric ?? "";
128 | } break;
129 |
130 | case 1: {
131 | lyrics.basic = nextLyric?.originalLyric ?? "";
132 | lyrics.extra = currentLyric?.originalLyric ?? "";
133 | } break;
134 |
135 | case 2: {
136 | if (currentLine == 0) {
137 | lyrics.basic = currentLyric?.originalLyric ?? "";
138 | lyrics.extra = nextLyric?.originalLyric ?? "";
139 | currentLine = 1;
140 | } else {
141 | lyrics.basic = nextLyric?.originalLyric ?? "";
142 | lyrics.extra = currentLyric?.originalLyric ?? "";
143 | currentLine = 0;
144 | }
145 | } break;
146 | }
147 | } break;
148 |
149 | case 2: {
150 | lyrics.extra = currentLyric?.translatedLyric
151 | ?? nextLyric?.originalLyric
152 | ?? "";
153 | } break;
154 |
155 | case 3: {
156 | lyrics.extra = currentLyric?.romanLyric
157 | ?? currentLyric?.translatedLyric
158 | ?? nextLyric?.originalLyric
159 | ?? "";
160 | } break;
161 | }
162 |
163 | TaskbarLyricsAPI.lyrics.lyrics(lyrics);
164 | currentIndex = nextIndex;
165 | }
166 | }
167 | }
168 |
169 |
170 |
171 | // 开始获取歌词
172 | function startGetLyric() {
173 | const config = pluginConfig.get("lyrics");
174 | switch (config["retrieval_method"]["value"]) {
175 | // 软件内词栏
176 | case 0: {
177 | watchLyricsChange();
178 | } break;
179 |
180 | // LibLyric
181 | case 1: {
182 | legacyNativeCmder.appendRegisterCall("Load", "audioplayer", play_load);
183 | legacyNativeCmder.appendRegisterCall("PlayProgress", "audioplayer", play_progress);
184 | const playingSong = betterncm.ncm.getPlayingSong();
185 | if (playingSong && playingSong.data.id != musicId) {
186 | play_load();
187 | }
188 | } break;
189 |
190 | // RefinedNowPlaying
191 | case 2: {
192 | legacyNativeCmder.appendRegisterCall("Load", "audioplayer", play_load);
193 | legacyNativeCmder.appendRegisterCall("PlayProgress", "audioplayer", play_progress);
194 | } break;
195 | }
196 | }
197 |
198 |
199 | // 停止获取歌词
200 | function stopGetLyric() {
201 | const config = pluginConfig.get("lyrics");
202 | switch (config["retrieval_method"]["value"]) {
203 | // 软件内词栏
204 | case 0: {
205 | if (observer) {
206 | observer.disconnect();
207 | observer = null;
208 | }
209 | } break;
210 |
211 | // LibLyric
212 | case 1: {
213 | legacyNativeCmder.removeRegisterCall("Load", "audioplayer", play_load);
214 | legacyNativeCmder.removeRegisterCall("PlayProgress", "audioplayer", play_progress);
215 | } break;
216 |
217 | // RefinedNowPlaying
218 | case 2: {
219 | legacyNativeCmder.removeRegisterCall("Load", "audioplayer", play_load);
220 | legacyNativeCmder.removeRegisterCall("PlayProgress", "audioplayer", play_progress);
221 | } break;
222 | }
223 | }
224 |
225 |
226 | this.lyric = {
227 | startGetLyric,
228 | stopGetLyric
229 | }
230 | });
231 |
--------------------------------------------------------------------------------
/src/betterncm-plugin/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 1,
3 | "name": "任务栏歌词",
4 | "slug": "Taskbar-Lyrics",
5 | "description": "在任务栏上嵌入歌词,目前仅建议Windows 11,且任务栏未被第三方软件修改",
6 | "preview": "preview.png",
7 | "version": "1.6.3",
8 | "author": "沫烬染",
9 | "author_links": [
10 | "https://github.com/mo-jinran"
11 | ],
12 | "type": "extension",
13 | "loadAfter": [
14 | "liblyric",
15 | "RefinedNowPlaying"
16 | ],
17 | "requirements": [
18 | "liblyric"
19 | ],
20 | "injects": {
21 | "Main": [
22 | {
23 | "file": "base.js"
24 | },
25 | {
26 | "file": "lyric.js"
27 | },
28 | {
29 | "file": "func.js"
30 | },
31 | {
32 | "file": "view.js"
33 | }
34 | ]
35 | }
36 | }
--------------------------------------------------------------------------------
/src/betterncm-plugin/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mo-jinran/Taskbar-Lyrics/66c45a9a5138c0e6963d0d04027746d6e99276c5/src/betterncm-plugin/preview.png
--------------------------------------------------------------------------------
/src/betterncm-plugin/style.css:
--------------------------------------------------------------------------------
1 | /* 根标签 */
2 | #taskbar-lyrics-dom {
3 | width: 100%;
4 | height: 100%;
5 | overflow: hidden;
6 | display: flex;
7 | flex-direction: column;
8 | gap: 16px;
9 | }
10 |
11 |
12 |
13 | /* 上边的tab区域 */
14 | #taskbar-lyrics-dom .tab_box {
15 | width: 100%;
16 | height: 40px;
17 | display: flex;
18 | }
19 |
20 | /* tab选项卡按钮 */
21 | #taskbar-lyrics-dom .tab_button {
22 | width: 60px;
23 | height: 100%;
24 | border: none;
25 | outline: none;
26 | background: none;
27 | margin: 0;
28 | font-size: 1rem;
29 | }
30 |
31 | /* tab选项卡按钮 激活效果 */
32 | #taskbar-lyrics-dom .tab_button.active {
33 | position: relative;
34 | font-weight: bold;
35 | }
36 |
37 | /* tab选项卡按钮 激活后底部黑白条 */
38 | #taskbar-lyrics-dom .tab_button.active::after {
39 | content: "";
40 | height: 5px;
41 | border-radius: 5px;
42 | background-color: rgba(255, 255, 255, 0.5);
43 | position: absolute;
44 | left: 10px;
45 | right: 10px;
46 | bottom: 0px;
47 | backdrop-filter: blur(4px);
48 | }
49 |
50 | body.s-theme-white #taskbar-lyrics-dom .tab_button.active::after {
51 | background-color: rgba(0, 0, 0, 0.5);
52 | }
53 |
54 |
55 |
56 | /* 下边的内容区域 */
57 | #taskbar-lyrics-dom .content_box {
58 | overflow: auto;
59 | flex: 1;
60 | }
61 |
62 | /* 所有内容隐藏 */
63 | #taskbar-lyrics-dom .content {
64 | display: none;
65 | }
66 |
67 | /* 指定内容显示 */
68 | #taskbar-lyrics-dom .content.show {
69 | display: block;
70 | }
71 |
72 |
73 |
74 | #taskbar-lyrics-dom .content h1 {
75 | height: 30px;
76 | display: flex;
77 | align-items: center;
78 | margin: 10px 0;
79 | gap: 10px;
80 | }
81 |
82 | #taskbar-lyrics-dom .content h1 strong {
83 | font-size: 1.25rem;
84 | font-weight: bold;
85 | }
86 |
87 | #taskbar-lyrics-dom .content hr {
88 | margin: 20px 0;
89 | border: none;
90 | height: 1px;
91 | background-color: rgba(255, 255, 255, 0.2);
92 | }
93 |
94 | body.s-theme-white #taskbar-lyrics-dom .content hr {
95 | background-color: rgba(0, 0, 0, 0.2);
96 | }
97 |
98 | #taskbar-lyrics-dom .content section>div {
99 | display: flex;
100 | min-height: 30px;
101 | margin: 5px 0;
102 | gap: 10px;
103 | }
104 |
105 | #taskbar-lyrics-dom .content .item_container {
106 | flex-direction: row;
107 | align-items: center;
108 | }
109 |
110 | #taskbar-lyrics-dom .content .text_container {
111 | flex-direction: column;
112 | align-items: start;
113 | }
114 |
115 | #taskbar-lyrics-dom .content p {
116 | font-size: 14px;
117 | }
118 |
119 | #taskbar-lyrics-dom .content a {
120 | font-weight: bold;
121 | }
122 |
123 |
124 |
125 | #taskbar-lyrics-dom .content input.color-input {
126 | width: 150px;
127 | padding: unset;
128 | }
129 |
130 | #taskbar-lyrics-dom .content input.number-input {
131 | width: 150px;
132 | }
133 |
134 | #taskbar-lyrics-dom .content input.switch-btn {
135 | width: 44px;
136 | height: 22px;
137 | appearance: none;
138 | border-radius: 20px;
139 | border: 1px solid rgba(255, 255, 255, 0.15);
140 | position: relative;
141 | }
142 |
143 | #taskbar-lyrics-dom .content input.switch-btn::before {
144 | content: "关";
145 | text-align: center;
146 | line-height: 18px;
147 | font-size: 12px;
148 | position: absolute;
149 | top: 1px;
150 | left: 1px;
151 | height: 18px;
152 | width: 18px;
153 | background-color: rgba(255, 255, 255, 0.3);
154 | border-radius: 50%;
155 | transition: all 0.1s ease-out;
156 | }
157 |
158 | #taskbar-lyrics-dom .content input.switch-btn:checked {
159 | background-color: rgba(255, 255, 255, 0.1);
160 | }
161 |
162 | #taskbar-lyrics-dom .content input.switch-btn:checked::before {
163 | content: "开";
164 | text-align: center;
165 | line-height: 18px;
166 | font-size: 12px;
167 | left: calc(100% - 19px);
168 | }
169 |
170 | body.s-theme-white #taskbar-lyrics-dom .content input.switch-btn {
171 | border: 1px solid rgba(0, 0, 0, 0.15);
172 | }
173 |
174 | body.s-theme-white #taskbar-lyrics-dom .content input.switch-btn::before {
175 | background-color: rgba(0, 0, 0, 0.3);
176 | }
177 |
178 | body.s-theme-white #taskbar-lyrics-dom .content input.switch-btn:checked {
179 | background-color: rgba(0, 0, 0, 0.1);
180 | }
181 |
182 |
183 |
184 | #taskbar-lyrics-dom .u-select {
185 | display: inline-block;
186 | width: 200px;
187 | }
188 |
189 | #taskbar-lyrics-dom input.u-txt {
190 | border-radius: 4px;
191 | }
--------------------------------------------------------------------------------
/src/betterncm-plugin/view.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | // 创建根视图
5 | const configView = document.createElement("div");
6 | configView.style.overflow = "hidden";
7 | configView.style.height = "100%";
8 | configView.style.width = "100%";
9 |
10 |
11 | plugin.onConfig(tools => configView);
12 |
13 |
14 | plugin.onLoad(async () => {
15 | const pluginConfig = this.base.pluginConfig;
16 | const {
17 | font,
18 | color,
19 | style,
20 | lyrics,
21 | effect,
22 | align,
23 | position,
24 | margin,
25 | screen,
26 | } = { ...this.func };
27 |
28 |
29 | // 加载结构
30 | {
31 | const path = `${this.pluginPath}/config.html`;
32 | const text = await betterncm.fs.readFileText(path);
33 | const parser = new DOMParser();
34 | const dom = parser.parseFromString(text, "text/html");
35 | const element = dom.querySelector("#taskbar-lyrics-dom");
36 | configView.appendChild(element);
37 | }
38 |
39 |
40 | // 加载样式
41 | {
42 | const path = `${this.pluginPath}/style.css`;
43 | const text = await betterncm.fs.readFileText(path);
44 | const element = document.createElement("style");
45 | element.textContent = text;
46 | configView.appendChild(element);
47 | }
48 |
49 |
50 | // 页面切换
51 | {
52 | const tab_box = configView.querySelector(".tab_box");
53 | const content_box = configView.querySelector(".content_box")
54 |
55 | const all_tab_button = tab_box.querySelectorAll(".tab_button");
56 | const all_content = content_box.querySelectorAll(".content");
57 |
58 | all_tab_button.forEach((tab, index) => {
59 | tab.addEventListener("click", () => {
60 | // 激活标签
61 | const active_tab = tab_box.querySelector(".active");
62 | active_tab.classList.remove("active");
63 | tab.classList.add("active");
64 | // 显示内容
65 | const show_content = content_box.querySelector(".show");
66 | show_content.classList.remove("show");
67 | all_content[index].classList.add("show");
68 | });
69 | });
70 | }
71 |
72 |
73 | // 通用的下拉选择框控制函数
74 | function selectController(event) {
75 | const open = event.target.parentElement.classList.contains("z-open");
76 | if (open) event.target.parentElement.classList.remove("z-open");
77 | else event.target.parentElement.classList.add("z-open");
78 | }
79 |
80 |
81 | // 点击其他地方收起下拉选择框
82 | addEventListener("pointerup", event => {
83 | if (!event.target.classList.contains("value")) {
84 | const open = configView.querySelectorAll(".u-select.z-open");
85 | open.forEach(value => value.classList.remove("z-open"));
86 | }
87 | });
88 |
89 |
90 | // 更换字体
91 | {
92 | const apply = configView.querySelector(".content.font .font-settings .apply");
93 | const reset = configView.querySelector(".content.font .font-settings .reset");
94 |
95 | const fontFamily = configView.querySelector(".content.font .font-settings .font-family");
96 |
97 | const elements = {
98 | fontFamily
99 | };
100 |
101 | apply.addEventListener("click", () => font.apply(elements));
102 | reset.addEventListener("click", () => font.reset(elements));
103 |
104 | fontFamily.value = pluginConfig.get("font")["font_family"];
105 | }
106 |
107 |
108 | // 字体颜色
109 | {
110 | const apply = configView.querySelector(".content.font .color-settings .apply");
111 | const reset = configView.querySelector(".content.font .color-settings .reset");
112 |
113 | const basicLightColor = configView.querySelector(".content.font .color-settings .basic-light-color");
114 | const basicLightOpacity = configView.querySelector(".content.font .color-settings .basic-light-opacity");
115 | const basicDarkColor = configView.querySelector(".content.font .color-settings .basic-dark-color");
116 | const basicDarkOpacity = configView.querySelector(".content.font .color-settings .basic-dark-opacity");
117 | const extraLightColor = configView.querySelector(".content.font .color-settings .extra-light-color");
118 | const extraLightOpacity = configView.querySelector(".content.font .color-settings .extra-light-opacity");
119 | const extraDarkColor = configView.querySelector(".content.font .color-settings .extra-dark-color");
120 | const extraDarkOpacity = configView.querySelector(".content.font .color-settings .extra-dark-opacity");
121 |
122 | const elements = {
123 | basicLightColor,
124 | basicLightOpacity,
125 | basicDarkColor,
126 | basicDarkOpacity,
127 | extraLightColor,
128 | extraLightOpacity,
129 | extraDarkColor,
130 | extraDarkOpacity
131 | }
132 |
133 | apply.addEventListener("click", () => color.apply(elements));
134 | reset.addEventListener("click", () => color.reset(elements));
135 |
136 | basicLightColor.value = `#${pluginConfig.get("color")["basic"]["light"]["hex_color"].toString(16)}`;
137 | basicLightOpacity.value = pluginConfig.get("color")["basic"]["light"]["opacity"];
138 | basicDarkColor.value = `#${pluginConfig.get("color")["basic"]["dark"]["hex_color"].toString(16)}`;
139 | basicDarkOpacity.value = pluginConfig.get("color")["basic"]["dark"]["opacity"];
140 | extraLightColor.value = `#${pluginConfig.get("color")["extra"]["light"]["hex_color"].toString(16)}`;
141 | extraLightOpacity.value = pluginConfig.get("color")["extra"]["light"]["opacity"];
142 | extraDarkColor.value = `#${pluginConfig.get("color")["extra"]["dark"]["hex_color"].toString(16)}`;
143 | extraDarkOpacity.value = pluginConfig.get("color")["extra"]["dark"]["opacity"];
144 | }
145 |
146 |
147 | // 字体样式
148 | {
149 | const reset = configView.querySelector(".content.font .style-settings .reset");
150 |
151 | const basicWeightValue = configView.querySelector(".content.font .style-settings .basic-weight .value");
152 | const basicWeightSelect = configView.querySelector(".content.font .style-settings .basic-weight .select");
153 | const basicNormal = configView.querySelector(".content.font .style-settings .basic-normal");
154 | const basicOblique = configView.querySelector(".content.font .style-settings .basic-oblique");
155 | const basicItalic = configView.querySelector(".content.font .style-settings .basic-italic");
156 | const basicUnderline = configView.querySelector(".content.font .style-settings .basic-underline");
157 | const basicStrikethrough = configView.querySelector(".content.font .style-settings .basic-strikethrough");
158 | const extraWeightValue = configView.querySelector(".content.font .style-settings .extra-weight .value");
159 | const extraWeightSelect = configView.querySelector(".content.font .style-settings .extra-weight .select");
160 | const extraNormal = configView.querySelector(".content.font .style-settings .extra-normal");
161 | const extraOblique = configView.querySelector(".content.font .style-settings .extra-oblique");
162 | const extraItalic = configView.querySelector(".content.font .style-settings .extra-italic");
163 | const extraUnderline = configView.querySelector(".content.font .style-settings .extra-underline");
164 | const extraStrikethrough = configView.querySelector(".content.font .style-settings .extra-strikethrough");
165 |
166 | const elements = {
167 | basicWeightValue,
168 | basicUnderline,
169 | basicStrikethrough,
170 | extraWeightValue,
171 | extraUnderline,
172 | extraStrikethrough
173 | }
174 |
175 | reset.addEventListener("click", () => style.reset(elements));
176 |
177 | basicNormal.addEventListener("click", event => style.setSlopeNormal(event));
178 | basicOblique.addEventListener("click", event => style.setSlopeOblique(event));
179 | basicItalic.addEventListener("click", event => style.setSlopeItalic(event));
180 | basicUnderline.addEventListener("change", event => style.setUnderline(event));
181 | basicStrikethrough.addEventListener("change", event => style.setStrikethrough(event));
182 | extraNormal.addEventListener("click", event => style.setSlopeNormal(event));
183 | extraOblique.addEventListener("click", event => style.setSlopeOblique(event));
184 | extraItalic.addEventListener("click", event => style.setSlopeItalic(event));
185 | extraUnderline.addEventListener("change", event => style.setUnderline(event));
186 | extraStrikethrough.addEventListener("change", event => style.setStrikethrough(event));
187 |
188 | basicWeightValue.addEventListener("click", selectController);
189 | basicWeightSelect.addEventListener("click", event => {
190 | const name = event.target.parentElement.dataset.type;
191 | const value = event.target.dataset.value;
192 | const textContent = event.target.textContent;
193 | style.setWeight(name, value, textContent);
194 | basicWeightValue.textContent = textContent;
195 | });
196 |
197 | extraWeightValue.addEventListener("click", selectController);
198 | extraWeightSelect.addEventListener("click", event => {
199 | const name = event.target.parentElement.dataset.type;
200 | const value = event.target.dataset.value;
201 | const textContent = event.target.textContent;
202 | style.setWeight(name, value, textContent);
203 | extraWeightValue.textContent = textContent;
204 | });
205 |
206 | basicWeightValue.textContent = pluginConfig.get("style")["basic"]["weight"]["textContent"];
207 | basicUnderline.checked = pluginConfig.get("style")["basic"]["underline"];
208 | basicStrikethrough.checked = pluginConfig.get("style")["basic"]["strikethrough"];
209 | extraWeightValue.textContent = pluginConfig.get("style")["extra"]["weight"]["textContent"];
210 | extraUnderline.checked = pluginConfig.get("style")["extra"]["underline"];
211 | extraStrikethrough.checked = pluginConfig.get("style")["extra"]["strikethrough"];
212 | }
213 |
214 |
215 | // 歌词设置
216 | {
217 | const reset = configView.querySelector(".content.lyrics .lyrics-settings .reset");
218 |
219 | const lyricsSwitch = configView.querySelector(".content.lyrics .lyrics-settings .lyrics-switch");
220 | const retrievalMethodValue = configView.querySelector(".content.lyrics .lyrics-settings .retrieval-method .value");
221 | const retrievalMethodSelect = configView.querySelector(".content.lyrics .lyrics-settings .retrieval-method .select");
222 |
223 | const elements = {
224 | retrievalMethodValue
225 | }
226 |
227 | reset.addEventListener("click", () => lyrics.reset(elements));
228 |
229 | lyricsSwitch.addEventListener("change", event => lyrics.lyricsSwitch(event));
230 |
231 | retrievalMethodValue.addEventListener("click", selectController);
232 | retrievalMethodSelect.addEventListener("click", event => {
233 | const value = event.target.dataset.value;
234 | const textContent = event.target.textContent;
235 | if ((value == "2") && (!window.currentLyrics)) {
236 | channel.call(
237 | "trayicon.popBalloon",
238 | () => { },
239 | [{
240 | title: "任务栏歌词",
241 | text: "无法使用RefinedNowPlaying歌词!\n是否安装RefinedNowPlaying插件?\n将回退到使用LibLyric解析获取歌词",
242 | icon: "path",
243 | hasSound: true,
244 | delayTime: 2e3
245 | }]
246 | );
247 | return;
248 | }
249 | lyrics.setRetrievalMethod(value, textContent);
250 | retrievalMethodValue.textContent = textContent;
251 | });
252 |
253 | retrievalMethodValue.textContent = pluginConfig.get("lyrics")["retrieval_method"]["textContent"];
254 | }
255 |
256 |
257 | // 显示效果
258 | {
259 | const apply = configView.querySelector(".content.lyrics .effect-settings .apply");
260 | const reset = configView.querySelector(".content.lyrics .effect-settings .reset");
261 |
262 | const nextLineLyricsPositionValue = configView.querySelector(".content.lyrics .effect-settings .next-line-lyrics-position .value");
263 | const nextLineLyricsPositionSelect = configView.querySelector(".content.lyrics .effect-settings .next-line-lyrics-position .select");
264 | const extraShowValue = configView.querySelector(".content.lyrics .effect-settings .extra-show .value");
265 | const extraShowSelect = configView.querySelector(".content.lyrics .effect-settings .extra-show .select");
266 | const adjust = configView.querySelector(".content.lyrics .effect-settings .adjust");
267 |
268 | const elements = {
269 | nextLineLyricsPositionValue,
270 | extraShowValue,
271 | adjust
272 | }
273 |
274 | apply.addEventListener("click", () => effect.apply(elements));
275 | reset.addEventListener("click", () => effect.reset(elements));
276 |
277 | nextLineLyricsPositionValue.addEventListener("click", selectController);
278 | nextLineLyricsPositionSelect.addEventListener("click", event => {
279 | const value = event.target.dataset.value;
280 | const textContent = event.target.textContent;
281 | effect.setNextLineLyricsPosition(value, textContent);
282 | nextLineLyricsPositionValue.textContent = textContent;
283 | });
284 |
285 | extraShowValue.addEventListener("click", selectController);
286 | extraShowSelect.addEventListener("click", event => {
287 | const value = event.target.dataset.value;
288 | const textContent = event.target.textContent;
289 | effect.setExtraShow(value, textContent);
290 | extraShowValue.textContent = textContent;
291 | });
292 |
293 | nextLineLyricsPositionValue.textContent = pluginConfig.get("effect")["next_line_lyrics_position"]["textContent"];
294 | extraShowValue.textContent = pluginConfig.get("effect")["extra_show"]["textContent"];
295 | adjust.value = pluginConfig.get("effect")["adjust"];
296 | }
297 |
298 |
299 | // 对齐方式
300 | {
301 | const reset = configView.querySelector(".content.lyrics .align-settings .reset");
302 |
303 | const basicLeft = configView.querySelector(".content.lyrics .align-settings .basic-left");
304 | const basicCenter = configView.querySelector(".content.lyrics .align-settings .basic-center");
305 | const basicRight = configView.querySelector(".content.lyrics .align-settings .basic-right");
306 | const extraLeft = configView.querySelector(".content.lyrics .align-settings .extra-left");
307 | const extraCenter = configView.querySelector(".content.lyrics .align-settings .extra-center");
308 | const extraRight = configView.querySelector(".content.lyrics .align-settings .extra-right");
309 |
310 | reset.addEventListener("click", () => align.reset());
311 |
312 | basicLeft.addEventListener("click", event => align.setLeft(event));
313 | basicCenter.addEventListener("click", event => align.setCenter(event));
314 | basicRight.addEventListener("click", event => align.setRight(event));
315 | extraLeft.addEventListener("click", event => align.setLeft(event));
316 | extraCenter.addEventListener("click", event => align.setCenter(event));
317 | extraRight.addEventListener("click", event => align.setRight(event));
318 | }
319 |
320 |
321 | // 修改位置
322 | {
323 | const reset = configView.querySelector(".content.window .position-settings .reset");
324 |
325 | const windowPositionValue = configView.querySelector(".content.window .position-settings .window-position .value");
326 | const windowPositionSelect = configView.querySelector(".content.window .position-settings .window-position .select");
327 |
328 | const elements = {
329 | windowPositionValue
330 | }
331 |
332 | reset.addEventListener("click", () => position.reset(elements));
333 |
334 | windowPositionValue.addEventListener("click", selectController);
335 | windowPositionSelect.addEventListener("click", event => {
336 | const value = event.target.dataset.value;
337 | const textContent = event.target.textContent;
338 | position.setWindowPosition(value, textContent);
339 | windowPositionValue.textContent = textContent;
340 | });
341 |
342 | windowPositionValue.textContent = pluginConfig.get("position")["position"]["textContent"];
343 | }
344 |
345 |
346 | // 修改边距
347 | {
348 | const apply = configView.querySelector(".content.window .margin-settings .apply");
349 | const reset = configView.querySelector(".content.window .margin-settings .reset");
350 |
351 | const left = configView.querySelector(".content.window .margin-settings .left");
352 | const right = configView.querySelector(".content.window .margin-settings .right");
353 |
354 | const elements = {
355 | left,
356 | right
357 | }
358 |
359 | apply.addEventListener("click", () => margin.apply(elements));
360 | reset.addEventListener("click", () => margin.reset(elements));
361 |
362 | left.value = pluginConfig.get("margin")["left"];
363 | right.value = pluginConfig.get("margin")["right"];
364 | }
365 |
366 |
367 | // 切换屏幕
368 | {
369 | const reset = configView.querySelector(".content.window .screen-settings .reset");
370 |
371 | const parentTaskbarValue = configView.querySelector(".content.window .screen-settings .parent-taskbar .value");
372 | const parentTaskbarSelect = configView.querySelector(".content.window .screen-settings .parent-taskbar .select");
373 |
374 | const elements = {
375 | parentTaskbarValue
376 | }
377 |
378 | reset.addEventListener("click", () => screen.reset(elements));
379 |
380 | parentTaskbarValue.addEventListener("click", selectController);
381 | parentTaskbarSelect.addEventListener("click", event => {
382 | const value = event.target.dataset.value;
383 | const textContent = event.target.textContent;
384 | screen.setParentTaskbar(value, textContent);
385 | parentTaskbarValue.textContent = textContent;
386 | });
387 |
388 | parentTaskbarValue.textContent = pluginConfig.get("screen")["parent_taskbar"]["textContent"];
389 | }
390 | });
--------------------------------------------------------------------------------
/src/taskbar-lyrics/CreateWindow.cpp:
--------------------------------------------------------------------------------
1 | #include "CreateWindow.hpp"
2 |
3 |
4 | 任务栏窗口类* 任务栏窗口类::任务栏窗口 = nullptr;
5 |
6 |
7 | 任务栏窗口类::任务栏窗口类(
8 | HINSTANCE 实例句柄,
9 | int 显示方法
10 | ) {
11 | this->任务栏窗口 = this;
12 | this->呈现窗口 = new 呈现窗口类(&this->窗口句柄);
13 |
14 | this->注册窗口(实例句柄);
15 | this->创建窗口(实例句柄, 显示方法);
16 |
17 | this->剩余宽度检测();
18 | this->监听注册表();
19 | }
20 |
21 |
22 | 任务栏窗口类::~任务栏窗口类()
23 | {
24 | delete this->呈现窗口;
25 | this->呈现窗口 = nullptr;
26 |
27 | this->剩余宽度检测_线程->detach();
28 | delete this->剩余宽度检测_线程;
29 | this->剩余宽度检测_线程 = nullptr;
30 |
31 | this->监听注册表_线程->detach();
32 | delete this->监听注册表_线程;
33 | this->监听注册表_线程 = nullptr;
34 | }
35 |
36 |
37 | void 任务栏窗口类::注册窗口(
38 | HINSTANCE hInstance
39 | ) {
40 | WNDCLASSEX wcex = {};
41 | wcex.cbSize = sizeof(WNDCLASSEX);
42 | wcex.hInstance = hInstance;
43 | wcex.lpfnWndProc = this->窗口过程;
44 | wcex.lpszClassName = this->窗口类名.c_str();
45 | wcex.style = CS_HREDRAW | CS_VREDRAW;
46 | wcex.hbrBackground = HBRUSH(GetStockObject(NULL_BRUSH));
47 | wcex.cbClsExtra = 0;
48 | wcex.cbWndExtra = 0;
49 | wcex.hCursor = nullptr;
50 | wcex.hIcon = nullptr;
51 | wcex.hIconSm = nullptr;
52 | wcex.lpszMenuName = nullptr;
53 | RegisterClassEx(&wcex);
54 | }
55 |
56 |
57 | void 任务栏窗口类::创建窗口(
58 | HINSTANCE 实例句柄,
59 | int 显示方法
60 | ) {
61 | HWND 任务栏_句柄 = FindWindow(L"Shell_TrayWnd", NULL);
62 | this->窗口句柄 = CreateWindowEx(
63 | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
64 | this->窗口类名.c_str(),
65 | this->窗口名字.c_str(),
66 | WS_POPUP,
67 | CW_USEDEFAULT,
68 | CW_USEDEFAULT,
69 | CW_USEDEFAULT,
70 | CW_USEDEFAULT,
71 | 任务栏_句柄,
72 | NULL,
73 | 实例句柄,
74 | NULL
75 | );
76 |
77 | SetParent(this->窗口句柄, 任务栏_句柄);
78 | ShowWindow(this->窗口句柄, 显示方法);
79 | PostMessage(this->窗口句柄, WM_PAINT, NULL, NULL);
80 | }
81 |
82 |
83 | void 任务栏窗口类::剩余宽度检测()
84 | {
85 | auto 线程函数 = [&] () {
86 | while (true)
87 | {
88 | std::this_thread::sleep_for(std::chrono::seconds(1));
89 |
90 | RECT 任务栏_矩形;
91 | RECT 开始按钮_矩形;
92 | RECT 活动区域_矩形;
93 | RECT 通知区域_矩形;
94 |
95 | GetWindowRect(this->呈现窗口->任务栏_句柄, &任务栏_矩形);
96 | GetWindowRect(this->呈现窗口->开始按钮_句柄, &开始按钮_矩形);
97 | GetWindowRect(this->呈现窗口->活动区域_句柄, &活动区域_矩形);
98 | GetWindowRect(this->呈现窗口->通知区域_句柄, &通知区域_矩形);
99 |
100 | // 只有不一样才会刷新
101 | if (std::memcmp(&this->呈现窗口->任务栏_矩形, &任务栏_矩形, sizeof(RECT)))
102 | {
103 | this->呈现窗口->任务栏_矩形 = 任务栏_矩形;
104 | PostMessage(this->窗口句柄, WM_PAINT, NULL, NULL);
105 | }
106 |
107 | if (this->呈现窗口->居中对齐)
108 | {
109 | if (std::memcmp(&this->呈现窗口->开始按钮_矩形, &开始按钮_矩形, sizeof(RECT)))
110 | {
111 | this->呈现窗口->开始按钮_矩形 = 开始按钮_矩形;
112 | PostMessage(this->窗口句柄, WM_PAINT, NULL, NULL);
113 | }
114 | }
115 | else
116 | {
117 | if (std::memcmp(&this->呈现窗口->活动区域_矩形, &活动区域_矩形, sizeof(RECT)))
118 | {
119 | this->呈现窗口->活动区域_矩形 = 活动区域_矩形;
120 | PostMessage(this->窗口句柄, WM_PAINT, NULL, NULL);
121 | }
122 | if (std::memcmp(&this->呈现窗口->通知区域_矩形, &通知区域_矩形, sizeof(RECT))) {
123 | this->呈现窗口->通知区域_矩形 = 通知区域_矩形;
124 | PostMessage(this->窗口句柄, WM_PAINT, NULL, NULL);
125 | }
126 | }
127 |
128 | }
129 | };
130 |
131 | this->剩余宽度检测_线程 = new std::thread(线程函数);
132 | }
133 |
134 |
135 | void 任务栏窗口类::监听注册表()
136 | {
137 | auto 读取注册表 = [](
138 | std::wstring path,
139 | std::wstring keyName,
140 | DWORD& value
141 | ) {
142 | HKEY key;
143 | DWORD bufferSize = sizeof(DWORD);
144 | if (RegOpenKeyEx(HKEY_CURRENT_USER, path.c_str(), NULL, KEY_READ, &key)) return true;
145 | if (RegQueryValueEx(key, keyName.c_str(), NULL, NULL, (LPBYTE) &value, &bufferSize)) return true;
146 | RegCloseKey(key);
147 | return false;
148 | };
149 |
150 | auto 获取注册表值 = [this, 读取注册表] () {
151 | {
152 | DWORD 值;
153 | std::wstring 路径 = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
154 | std::wstring 键 = L"SystemUsesLightTheme";
155 | if (!读取注册表(路径, 键, 值))
156 | {
157 | this->呈现窗口->深浅模式 = static_cast(值);
158 | }
159 | }
160 |
161 | {
162 | DWORD 值;
163 | std::wstring 路径 = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
164 | std::wstring 键 = L"TaskbarAl";
165 | if (!读取注册表(路径, 键, 值))
166 | {
167 | this->呈现窗口->居中对齐 = static_cast(值);
168 | }
169 | }
170 |
171 | {
172 | DWORD 值;
173 | std::wstring 路径 = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
174 | std::wstring 键 = L"TaskbarDa";
175 | if (!读取注册表(路径, 键, 值))
176 | {
177 | this->呈现窗口->组件按钮 = static_cast(值);
178 | }
179 | }
180 |
181 | PostMessage(this->窗口句柄, WM_PAINT, NULL, NULL);
182 | };
183 |
184 | 获取注册表值();
185 |
186 | auto 线程函数 = [this, 获取注册表值] () {
187 | // 持续监听注册表
188 | while (true)
189 | {
190 | // 打开注册表
191 | if (!this->注册表句柄)
192 | {
193 | std::wstring path = L"Software\\Microsoft\\Windows\\CurrentVersion";
194 | RegOpenKeyEx(HKEY_CURRENT_USER, path.c_str(), NULL, KEY_NOTIFY, &this->注册表句柄);
195 | continue;
196 | }
197 |
198 | // 监听注册表
199 | if (RegNotifyChangeKeyValue(this->注册表句柄, true, REG_NOTIFY_CHANGE_LAST_SET, NULL, false))
200 | {
201 | continue;
202 | }
203 |
204 | 获取注册表值();
205 | }
206 | };
207 |
208 | this->剩余宽度检测_线程 = new std::thread(线程函数);
209 | }
210 |
211 |
212 | LRESULT CALLBACK 任务栏窗口类::窗口过程(
213 | HWND 窗口句柄,
214 | UINT 消息,
215 | WPARAM 字参数,
216 | LPARAM 长参数
217 | ) {
218 | switch (消息)
219 | {
220 | case WM_PAINT:
221 | {
222 | 任务栏窗口类::任务栏窗口->呈现窗口->更新窗口();
223 | };
224 | break;
225 |
226 | case WM_ERASEBKGND:
227 | {
228 | return 0;
229 | };
230 | break;
231 |
232 | case WM_CLOSE:
233 | {
234 | DestroyWindow(窗口句柄);
235 | };
236 | break;
237 |
238 | case WM_DESTROY:
239 | {
240 | PostQuitMessage(0);
241 | };
242 | break;
243 |
244 | default:
245 | {
246 | return DefWindowProc(窗口句柄, 消息, 字参数, 长参数);
247 | };
248 | break;
249 | }
250 |
251 | return 0;
252 | }
253 |
--------------------------------------------------------------------------------
/src/taskbar-lyrics/CreateWindow.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "RenderWindow.hpp"
4 | #include
5 | #include
6 | #include
7 |
8 |
9 | class 任务栏窗口类
10 | {
11 | private:
12 | static 任务栏窗口类* 任务栏窗口;
13 |
14 |
15 | public:
16 | 呈现窗口类* 呈现窗口 = nullptr;
17 |
18 |
19 | public:
20 | HWND 窗口句柄;
21 |
22 |
23 | private:
24 | std::thread* 剩余宽度检测_线程 = nullptr;
25 | void 剩余宽度检测();
26 |
27 |
28 | private:
29 | HKEY 注册表句柄;
30 | std::thread* 监听注册表_线程 = nullptr;
31 | void 监听注册表();
32 |
33 |
34 | private:
35 | std::wstring 窗口类名 = L"betterncm_taskbar_lyrics";
36 | std::wstring 窗口名字 = L"BetterNCM 任务栏歌词类";
37 |
38 |
39 | public:
40 | 任务栏窗口类(HINSTANCE, int);
41 | ~任务栏窗口类();
42 |
43 |
44 | private:
45 | void 注册窗口(HINSTANCE);
46 | void 创建窗口(HINSTANCE, int);
47 |
48 |
49 | private:
50 | static LRESULT CALLBACK 窗口过程(HWND, UINT, WPARAM, LPARAM);
51 | };
--------------------------------------------------------------------------------
/src/taskbar-lyrics/NetworkServer.cpp:
--------------------------------------------------------------------------------
1 | #include "NetworkServer.hpp"
2 | #include "CreateWindow.hpp"
3 | #include "nlohmann/json.hpp"
4 | #include
5 |
6 |
7 | 网络服务器类::网络服务器类(
8 | 任务栏窗口类* 任务栏窗口,
9 | unsigned short 端口
10 | ) {
11 | this->任务栏窗口 = 任务栏窗口;
12 |
13 | auto handler = [this] (auto func) {
14 | auto bind = std::bind(func,this,std::placeholders::_1,std::placeholders::_2);
15 | return httplib::Server::Handler(bind);
16 | };
17 |
18 | auto 线程函数 = [this, handler, 端口] () {
19 | this->网络服务器.Post("/taskbar/font/font", handler(&网络服务器类::字体));
20 | this->网络服务器.Post("/taskbar/font/color", handler(&网络服务器类::颜色));
21 | this->网络服务器.Post("/taskbar/font/style", handler(&网络服务器类::样式));
22 | this->网络服务器.Post("/taskbar/lyrics/lyrics", handler(&网络服务器类::歌词));
23 | this->网络服务器.Post("/taskbar/lyrics/align", handler(&网络服务器类::对齐));
24 | this->网络服务器.Post("/taskbar/window/position", handler(&网络服务器类::位置));
25 | this->网络服务器.Post("/taskbar/window/margin", handler(&网络服务器类::边距));
26 | this->网络服务器.Post("/taskbar/window/screen", handler(&网络服务器类::屏幕));
27 | this->网络服务器.Post("/taskbar/close", handler(&网络服务器类::关闭));
28 | this->网络服务器.listen("127.0.0.1", 端口);
29 | };
30 |
31 | this->网络服务器_线程 = new std::thread(线程函数);
32 | }
33 |
34 |
35 | 网络服务器类::~网络服务器类()
36 | {
37 | this->网络服务器.stop();
38 |
39 | this->网络服务器_线程->detach();
40 | delete this->网络服务器_线程;
41 | this->网络服务器_线程 = nullptr;
42 |
43 | this->任务栏窗口 = nullptr;
44 | }
45 |
46 |
47 | void 网络服务器类::字体(
48 | const httplib::Request& req,
49 | httplib::Response& res
50 | ) {
51 | auto json = nlohmann::json::parse(req.body);
52 |
53 | this->任务栏窗口->呈现窗口->字体名称 = this->字符转换.from_bytes(
54 | json["font_family"].get()
55 | );
56 |
57 | PostMessage(this->任务栏窗口->窗口句柄, WM_PAINT, NULL, NULL);
58 | res.status = 200;
59 | }
60 |
61 |
62 | void 网络服务器类::颜色(
63 | const httplib::Request& req,
64 | httplib::Response& res
65 | ) {
66 | auto json = nlohmann::json::parse(req.body);
67 |
68 | this->任务栏窗口->呈现窗口->字体颜色_浅色_主歌词 = D2D1::ColorF(
69 | json["basic"]["light"]["hex_color"].get(),
70 | json["basic"]["light"]["opacity"].get()
71 | );
72 | this->任务栏窗口->呈现窗口->字体颜色_深色_主歌词 = D2D1::ColorF(
73 | json["basic"]["dark"]["hex_color"].get(),
74 | json["basic"]["dark"]["opacity"].get()
75 | );
76 | this->任务栏窗口->呈现窗口->字体颜色_浅色_副歌词 = D2D1::ColorF(
77 | json["extra"]["light"]["hex_color"].get(),
78 | json["extra"]["light"]["opacity"].get()
79 | );
80 | this->任务栏窗口->呈现窗口->字体颜色_深色_副歌词 = D2D1::ColorF(
81 | json["extra"]["dark"]["hex_color"].get(),
82 | json["extra"]["dark"]["opacity"].get()
83 | );
84 |
85 | PostMessage(this->任务栏窗口->窗口句柄, WM_PAINT, NULL, NULL);
86 | res.status = 200;
87 | }
88 |
89 |
90 | void 网络服务器类::样式(
91 | const httplib::Request& req,
92 | httplib::Response& res
93 | ) {
94 | auto json = nlohmann::json::parse(req.body);
95 |
96 | this->任务栏窗口->呈现窗口->字体样式_主歌词_字重 = json["basic"]["weight"]["value"].get();
97 | this->任务栏窗口->呈现窗口->字体样式_主歌词_斜体 = json["basic"]["slope"].get();
98 | this->任务栏窗口->呈现窗口->字体样式_主歌词_下划线 = json["basic"]["underline"].get();
99 | this->任务栏窗口->呈现窗口->字体样式_主歌词_删除线 = json["basic"]["strikethrough"].get();
100 | this->任务栏窗口->呈现窗口->字体样式_副歌词_字重 = json["extra"]["weight"]["value"].get();
101 | this->任务栏窗口->呈现窗口->字体样式_副歌词_斜体 = json["extra"]["slope"].get();
102 | this->任务栏窗口->呈现窗口->字体样式_副歌词_下划线 = json["extra"]["underline"].get();
103 | this->任务栏窗口->呈现窗口->字体样式_副歌词_删除线 = json["extra"]["strikethrough"].get();
104 |
105 | PostMessage(this->任务栏窗口->窗口句柄, WM_PAINT, NULL, NULL);
106 | res.status = 200;
107 | }
108 |
109 |
110 | void 网络服务器类::歌词(
111 | const httplib::Request& req,
112 | httplib::Response& res
113 | ) {
114 | auto json = nlohmann::json::parse(req.body);
115 |
116 | this->任务栏窗口->呈现窗口->主歌词 = this->字符转换.from_bytes(
117 | json["basic"].get()
118 | );
119 | this->任务栏窗口->呈现窗口->副歌词 = this->字符转换.from_bytes(
120 | json["extra"].get()
121 | );
122 |
123 | PostMessage(this->任务栏窗口->窗口句柄, WM_PAINT, NULL, NULL);
124 | res.status = 200;
125 | }
126 |
127 |
128 | void 网络服务器类::对齐(
129 | const httplib::Request& req,
130 | httplib::Response& res
131 | ) {
132 | auto json = nlohmann::json::parse(req.body);
133 |
134 | this->任务栏窗口->呈现窗口->对齐方式_主歌词 = json["basic"].get();
135 | this->任务栏窗口->呈现窗口->对齐方式_副歌词 = json["extra"].get();
136 |
137 | PostMessage(this->任务栏窗口->窗口句柄, WM_PAINT, NULL, NULL);
138 | res.status = 200;
139 | }
140 |
141 |
142 | void 网络服务器类::位置(
143 | const httplib::Request& req,
144 | httplib::Response& res
145 | ) {
146 | auto json = nlohmann::json::parse(req.body);
147 |
148 | this->任务栏窗口->呈现窗口->窗口位置 = json["position"]["value"].get();
149 |
150 | PostMessage(this->任务栏窗口->窗口句柄, WM_PAINT, NULL, NULL);
151 | res.status = 200;
152 | }
153 |
154 |
155 | void 网络服务器类::边距(
156 | const httplib::Request& req,
157 | httplib::Response& res
158 | ) {
159 | auto json = nlohmann::json::parse(req.body);
160 |
161 | this->任务栏窗口->呈现窗口->左边距 = json["left"].get();
162 | this->任务栏窗口->呈现窗口->右边距 = json["right"].get();
163 |
164 | PostMessage(this->任务栏窗口->窗口句柄, WM_PAINT, NULL, NULL);
165 | res.status = 200;
166 | }
167 |
168 |
169 | void 网络服务器类::屏幕(
170 | const httplib::Request& req,
171 | httplib::Response& res
172 | ) {
173 | auto json = nlohmann::json::parse(req.body);
174 | auto parent_taskbar = json["parent_taskbar"]["value"].get();
175 |
176 | this->任务栏窗口->呈现窗口->任务栏_句柄 = FindWindow(this->字符转换.from_bytes(parent_taskbar).c_str(), NULL);
177 | this->任务栏窗口->呈现窗口->开始按钮_句柄 = FindWindowEx(this->任务栏窗口->呈现窗口->任务栏_句柄, NULL, L"Start", NULL);
178 |
179 | GetWindowRect(this->任务栏窗口->呈现窗口->任务栏_句柄, &this->任务栏窗口->呈现窗口->任务栏_矩形);
180 | GetWindowRect(this->任务栏窗口->呈现窗口->开始按钮_句柄, &this->任务栏窗口->呈现窗口->开始按钮_矩形);
181 |
182 | SetParent(this->任务栏窗口->窗口句柄, this->任务栏窗口->呈现窗口->任务栏_句柄);
183 |
184 | PostMessage(this->任务栏窗口->窗口句柄, WM_PAINT, NULL, NULL);
185 | res.status = 200;
186 | }
187 |
188 |
189 | void 网络服务器类::关闭(
190 | const httplib::Request& req,
191 | httplib::Response& res
192 | ) {
193 | this->任务栏窗口->呈现窗口->主歌词 = L"检测到网易云音乐重载页面";
194 | this->任务栏窗口->呈现窗口->副歌词 = L"正在尝试关闭任务栏歌词...";
195 |
196 | PostMessage(this->任务栏窗口->窗口句柄, WM_PAINT, NULL, NULL);
197 | PostMessage(this->任务栏窗口->窗口句柄, WM_CLOSE, NULL, NULL);
198 | res.status = 200;
199 | }
--------------------------------------------------------------------------------
/src/taskbar-lyrics/NetworkServer.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "httplib.h"
4 | #include
5 | #include
6 | #include
7 |
8 |
9 | class 网络服务器类
10 | {
11 | private:
12 | httplib::Server 网络服务器;
13 | class 任务栏窗口类* 任务栏窗口 = nullptr;
14 | std::thread* 网络服务器_线程 = nullptr;
15 |
16 |
17 | private:
18 | std::wstring_convert> 字符转换;
19 |
20 |
21 | public:
22 | 网络服务器类(class 任务栏窗口类*, unsigned short);
23 | ~网络服务器类();
24 |
25 |
26 | private:
27 | void 字体(const httplib::Request&, httplib::Response&);
28 | void 颜色(const httplib::Request&, httplib::Response&);
29 | void 样式(const httplib::Request&, httplib::Response&);
30 | void 歌词(const httplib::Request&, httplib::Response&);
31 | void 对齐(const httplib::Request&, httplib::Response&);
32 | void 位置(const httplib::Request&, httplib::Response&);
33 | void 边距(const httplib::Request&, httplib::Response&);
34 | void 屏幕(const httplib::Request&, httplib::Response&);
35 | void 关闭(const httplib::Request&, httplib::Response&);
36 | };
--------------------------------------------------------------------------------
/src/taskbar-lyrics/RenderWindow.cpp:
--------------------------------------------------------------------------------
1 | #include "RenderWindow.hpp"
2 |
3 | #pragma comment (lib, "d2d1.lib")
4 | #pragma comment (lib, "dwrite.lib")
5 |
6 |
7 | 呈现窗口类::呈现窗口类(
8 | HWND* 窗口句柄
9 | ) {
10 | this->窗口句柄 = 窗口句柄;
11 |
12 | this->任务栏_句柄 = FindWindow(L"Shell_TrayWnd", NULL);
13 | this->通知区域_句柄 = FindWindowEx(this->任务栏_句柄, NULL, L"TrayNotifyWnd", NULL);
14 | this->开始按钮_句柄 = FindWindowEx(this->任务栏_句柄, NULL, L"Start", NULL);
15 | HWND 最小化区域_句柄 = FindWindowEx(this->任务栏_句柄, NULL, L"ReBarWindow32", NULL);
16 | this->活动区域_句柄 = FindWindowEx(最小化区域_句柄, NULL, L"MSTaskSwWClass", NULL);
17 |
18 | // 创建D2D工厂
19 | D2D1CreateFactory(
20 | D2D1_FACTORY_TYPE_SINGLE_THREADED,
21 | &this->D2D工厂
22 | );
23 |
24 | D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties = D2D1::RenderTargetProperties();
25 | renderTargetProperties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
26 | renderTargetProperties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
27 |
28 | // 创建DC渲染目标
29 | this->D2D工厂->CreateDCRenderTarget(
30 | &renderTargetProperties,
31 | &this->D2D呈现目标
32 | );
33 |
34 | // 主歌词笔刷
35 | this->D2D呈现目标->CreateSolidColorBrush(
36 | D2D1::ColorF(0x000000, 1),
37 | &this->D2D纯色笔刷
38 | );
39 |
40 | // 创建DWrite工厂
41 | DWriteCreateFactory(
42 | DWRITE_FACTORY_TYPE_SHARED,
43 | __uuidof(IDWriteFactory),
44 | reinterpret_cast(&this->DWrite工厂)
45 | );
46 | }
47 |
48 |
49 | 呈现窗口类::~呈现窗口类()
50 | {
51 | this->D2D工厂->Release();
52 | this->D2D工厂 = nullptr;
53 |
54 | this->D2D呈现目标->Release();
55 | this->D2D呈现目标 = nullptr;
56 |
57 | this->D2D纯色笔刷->Release();
58 | this->D2D纯色笔刷 = nullptr;
59 |
60 | this->DWrite工厂->Release();
61 | this->DWrite工厂 = nullptr;
62 |
63 | this->窗口句柄 = nullptr;
64 | }
65 |
66 |
67 | void 呈现窗口类::更新窗口()
68 | {
69 | GetWindowRect(this->任务栏_句柄, &this->任务栏_矩形);
70 | GetWindowRect(this->通知区域_句柄, &this->通知区域_矩形);
71 | GetWindowRect(this->开始按钮_句柄, &this->开始按钮_矩形);
72 | GetWindowRect(this->活动区域_句柄, &this->活动区域_矩形);
73 |
74 | long 左 = 0;
75 | long 上 = 0;
76 | long 宽 = 0;
77 | long 高 = this->任务栏_矩形.bottom - this->任务栏_矩形.top;
78 |
79 | switch (this->窗口位置)
80 | {
81 | case WindowAlignment::WindowAlignmentAdaptive:
82 | {
83 | if (this->居中对齐)
84 | {
85 | 左 = static_cast(this->组件按钮 ? this->DPI(160) : 0) + this->左边距;
86 | 宽 = this->开始按钮_矩形.left - static_cast(this->组件按钮 ? this->DPI(160) : 0) - this->左边距 - this->右边距;
87 | }
88 | else
89 | {
90 | 左 = this->活动区域_矩形.right + this->左边距;
91 | 宽 = this->通知区域_矩形.left - this->活动区域_矩形.right - this->左边距 - this->右边距;
92 | }
93 | }
94 | break;
95 |
96 | case WindowAlignment::WindowAlignmentLeft:
97 | {
98 | if (this->居中对齐)
99 | {
100 | 左 = static_cast(this->组件按钮 ? this->DPI(160) : 0) + this->左边距;
101 | 宽 = this->开始按钮_矩形.left - static_cast(this->组件按钮 ? this->DPI(160) : 0) - this->左边距 - this->右边距;
102 | }
103 | else
104 | {
105 | 左 = 0 + this->左边距;
106 | 宽 = this->通知区域_矩形.left - 0 - this->左边距 - this->右边距;
107 | }
108 | }
109 | break;
110 |
111 | case WindowAlignment::WindowAlignmentCenter:
112 | {
113 | int center = (this->任务栏_矩形.right - this->任务栏_矩形.left) / 2;
114 | int lw = this->活动区域_矩形.right - this->开始按钮_矩形.left;
115 | int rw = this->通知区域_矩形.right - this->通知区域_矩形.left;
116 |
117 | if (lw > rw)
118 | {
119 | 左 = lw + this->左边距;
120 | 宽 = (center - lw) * 2 - this->左边距 - this->右边距;
121 | }
122 | else
123 | {
124 | 左 = center - (center - rw) + this->左边距;
125 | 宽 = (center - rw) * 2 - this->左边距 - this->右边距;
126 | }
127 | }
128 | break;
129 |
130 | case WindowAlignment::WindowAlignmentRight:
131 | {
132 | 左 = this->活动区域_矩形.right + this->左边距;
133 | 宽 = this->通知区域_矩形.left - this->活动区域_矩形.right - this->左边距 - this->右边距;
134 | }
135 | break;
136 | }
137 |
138 | MoveWindow(*this->窗口句柄, 左, 上, 宽, 高, false);
139 | this->绘制窗口(左, 上, 宽, 高);
140 | }
141 |
142 |
143 | void 呈现窗口类::绘制窗口(
144 | long 左,
145 | long 上,
146 | long 宽,
147 | long 高
148 | ) {
149 | RECT rect = {};
150 | GetClientRect(*this->窗口句柄, &rect);
151 |
152 | HDC hdc = GetDC(*this->窗口句柄);
153 | HDC memDC = CreateCompatibleDC(hdc);
154 | HBITMAP memBitmap = CreateCompatibleBitmap(hdc, 宽, 高);
155 | HBITMAP oldBitmap = HBITMAP(SelectObject(memDC, memBitmap));
156 |
157 | this->绘制歌词(memDC, rect);
158 |
159 | BLENDFUNCTION blend = {
160 | AC_SRC_OVER,
161 | 0,
162 | 255,
163 | AC_SRC_ALPHA
164 | };
165 |
166 | POINT 目标位置 = { 左, 上 };
167 | SIZE 大小 = { 宽, 高 };
168 | POINT 来源位置 = { 0, 0 };
169 |
170 | UpdateLayeredWindow(*this->窗口句柄, hdc, &目标位置, &大小, memDC, &来源位置, 0, &blend, ULW_ALPHA);
171 |
172 | SelectObject(memDC, oldBitmap);
173 | DeleteObject(memBitmap);
174 | DeleteDC(memDC);
175 | ReleaseDC(*this->窗口句柄, hdc);
176 | }
177 |
178 |
179 | void 呈现窗口类::绘制歌词(
180 | HDC& hdc,
181 | RECT& rect
182 | ) {
183 | this->D2D呈现目标->BindDC(hdc, &rect);
184 | this->D2D呈现目标->BeginDraw();
185 |
186 | DWRITE_TRIMMING 歌词裁剪 = {
187 | DWRITE_TRIMMING_GRANULARITY_CHARACTER,
188 | 0,
189 | 0
190 | };
191 |
192 | if (this->副歌词.empty())
193 | {
194 | D2D1_RECT_F 主歌词_矩形 = D2D1::RectF(
195 | max(0, rect.left + this->DPI(10)),
196 | max(0, rect.top + this->DPI(10)),
197 | max(0, rect.right - this->DPI(10)),
198 | max(0, rect.bottom - this->DPI(10))
199 | );
200 |
201 | 主歌词_矩形.right = max(主歌词_矩形.right, 主歌词_矩形.left);
202 | 主歌词_矩形.bottom = max(主歌词_矩形.bottom, 主歌词_矩形.top);
203 |
204 | // 创建文字格式
205 | this->DWrite工厂->CreateTextFormat(
206 | this->字体名称.c_str(),
207 | nullptr,
208 | this->字体样式_主歌词_字重,
209 | this->字体样式_主歌词_斜体,
210 | DWRITE_FONT_STRETCH_NORMAL,
211 | this->DPI(20),
212 | L"zh-CN",
213 | &this->DWrite主歌词文本格式
214 | );
215 |
216 | // 创建文字布局
217 | this->DWrite工厂->CreateTextLayout(
218 | this->主歌词.c_str(),
219 | this->主歌词.size(),
220 | this->DWrite主歌词文本格式,
221 | (float) (主歌词_矩形.right - 主歌词_矩形.left),
222 | (float) (主歌词_矩形.bottom - 主歌词_矩形.top),
223 | &this->DWrite主歌词文本布局
224 | );
225 |
226 | this->DWrite主歌词文本布局->SetTrimming(&歌词裁剪, nullptr);
227 | this->DWrite主歌词文本布局->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
228 | this->DWrite主歌词文本布局->SetTextAlignment(this->对齐方式_主歌词);
229 | this->DWrite主歌词文本布局->SetUnderline(this->字体样式_主歌词_下划线, DWRITE_TEXT_RANGE{0, this->主歌词.size()});
230 | this->DWrite主歌词文本布局->SetStrikethrough(this->字体样式_主歌词_删除线, DWRITE_TEXT_RANGE{0, this->主歌词.size()});
231 | this->D2D纯色笔刷->SetColor(this->深浅模式 ? this->字体颜色_浅色_主歌词 : this->字体颜色_深色_主歌词);
232 |
233 | //绘制文字显示
234 | this->D2D呈现目标->DrawTextLayout(
235 | D2D1::Point2F(主歌词_矩形.left, 主歌词_矩形.top),
236 | this->DWrite主歌词文本布局,
237 | this->D2D纯色笔刷,
238 | D2D1_DRAW_TEXT_OPTIONS_NO_SNAP
239 | );
240 |
241 | this->DWrite主歌词文本格式->Release();
242 | this->DWrite主歌词文本格式 = nullptr;
243 | this->DWrite主歌词文本布局->Release();
244 | this->DWrite主歌词文本布局 = nullptr;
245 | }
246 | else
247 | {
248 | D2D1_RECT_F 主歌词_矩形 = D2D1::RectF(
249 | max(0, rect.left + this->DPI(5)),
250 | max(0, rect.top + this->DPI(5)),
251 | max(0, rect.right - this->DPI(5)),
252 | max(0, rect.bottom / 2.0f)
253 | );
254 |
255 | 主歌词_矩形.right = max(主歌词_矩形.right, 主歌词_矩形.left);
256 | 主歌词_矩形.bottom = max(主歌词_矩形.bottom, 主歌词_矩形.top);
257 |
258 | // 创建文字格式
259 | this->DWrite工厂->CreateTextFormat(
260 | this->字体名称.c_str(),
261 | nullptr,
262 | this->字体样式_主歌词_字重,
263 | this->字体样式_主歌词_斜体,
264 | DWRITE_FONT_STRETCH_NORMAL,
265 | this->DPI(15),
266 | L"zh-CN",
267 | &this->DWrite主歌词文本格式
268 | );
269 |
270 | // 创建文字布局
271 | this->DWrite工厂->CreateTextLayout(
272 | this->主歌词.c_str(),
273 | this->主歌词.size(),
274 | this->DWrite主歌词文本格式,
275 | (float) (主歌词_矩形.right - 主歌词_矩形.left),
276 | (float) (主歌词_矩形.bottom - 主歌词_矩形.top),
277 | &this->DWrite主歌词文本布局
278 | );
279 |
280 | this->DWrite主歌词文本布局->SetTrimming(&歌词裁剪, nullptr);
281 | this->DWrite主歌词文本布局->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
282 | this->DWrite主歌词文本布局->SetTextAlignment(this->对齐方式_主歌词);
283 | this->DWrite主歌词文本布局->SetUnderline(this->字体样式_主歌词_下划线, DWRITE_TEXT_RANGE{0, this->主歌词.size()});
284 | this->DWrite主歌词文本布局->SetStrikethrough(this->字体样式_主歌词_删除线, DWRITE_TEXT_RANGE{0, this->主歌词.size()});
285 | this->D2D纯色笔刷->SetColor(this->深浅模式 ? this->字体颜色_浅色_主歌词 : this->字体颜色_深色_主歌词);
286 |
287 | //绘制文字显示
288 | this->D2D呈现目标->DrawTextLayout(
289 | D2D1::Point2F(主歌词_矩形.left, 主歌词_矩形.top),
290 | this->DWrite主歌词文本布局,
291 | this->D2D纯色笔刷,
292 | D2D1_DRAW_TEXT_OPTIONS_NO_SNAP
293 | );
294 |
295 | /******************************************/
296 |
297 | D2D1_RECT_F 副歌词_矩形 = D2D1::RectF(
298 | max(0, rect.left + this->DPI(5)),
299 | max(0, rect.bottom / 2.0f),
300 | max(0, rect.right - this->DPI(5)),
301 | max(0, rect.bottom - this->DPI(5))
302 | );
303 |
304 | 副歌词_矩形.right = max(副歌词_矩形.right, 副歌词_矩形.left);
305 | 副歌词_矩形.bottom = max(副歌词_矩形.bottom, 副歌词_矩形.top);
306 |
307 | // 创建文字格式
308 | this->DWrite工厂->CreateTextFormat(
309 | this->字体名称.c_str(),
310 | nullptr,
311 | this->字体样式_副歌词_字重,
312 | this->字体样式_副歌词_斜体,
313 | DWRITE_FONT_STRETCH_NORMAL,
314 | this->DPI(15),
315 | L"zh-CN",
316 | &this->DWrite副歌词文本格式
317 | );
318 |
319 | // 创建文字布局
320 | this->DWrite工厂->CreateTextLayout(
321 | this->副歌词.c_str(),
322 | this->副歌词.size(),
323 | this->DWrite副歌词文本格式,
324 | (float) (副歌词_矩形.right - 副歌词_矩形.left),
325 | (float) (副歌词_矩形.bottom - 副歌词_矩形.top),
326 | &this->DWrite副歌词文本布局
327 | );
328 |
329 | this->DWrite副歌词文本布局->SetTrimming(&歌词裁剪, nullptr);
330 | this->DWrite副歌词文本布局->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
331 | this->DWrite副歌词文本布局->SetTextAlignment(this->对齐方式_副歌词);
332 | this->DWrite副歌词文本布局->SetUnderline(this->字体样式_副歌词_下划线, DWRITE_TEXT_RANGE{0, this->副歌词.size()});
333 | this->DWrite副歌词文本布局->SetStrikethrough(this->字体样式_副歌词_删除线, DWRITE_TEXT_RANGE{0, this->副歌词.size()});
334 | this->D2D纯色笔刷->SetColor(this->深浅模式 ? this->字体颜色_浅色_副歌词 : this->字体颜色_深色_副歌词);
335 |
336 | //绘制文字显示
337 | this->D2D呈现目标->DrawTextLayout(
338 | D2D1::Point2F(副歌词_矩形.left, 副歌词_矩形.top),
339 | this->DWrite副歌词文本布局,
340 | this->D2D纯色笔刷,
341 | D2D1_DRAW_TEXT_OPTIONS_NO_SNAP
342 | );
343 |
344 | this->DWrite主歌词文本格式->Release();
345 | this->DWrite主歌词文本格式 = nullptr;
346 | this->DWrite主歌词文本布局->Release();
347 | this->DWrite主歌词文本布局 = nullptr;
348 | this->DWrite副歌词文本格式->Release();
349 | this->DWrite副歌词文本格式 = nullptr;
350 | this->DWrite副歌词文本布局->Release();
351 | this->DWrite副歌词文本布局 = nullptr;
352 | }
353 |
354 | this->D2D呈现目标->EndDraw();
355 | }
356 |
357 |
358 | float 呈现窗口类::DPI(
359 | UINT 像素大小
360 | ) {
361 | auto 屏幕DPI = GetDpiForWindow(*this->窗口句柄);
362 | auto 新像素大小 = static_cast(像素大小 * 屏幕DPI / 96);
363 | return 新像素大小;
364 | }
365 |
--------------------------------------------------------------------------------
/src/taskbar-lyrics/RenderWindow.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 |
9 | enum WindowAlignment
10 | {
11 | WindowAlignmentAdaptive = 0,
12 | WindowAlignmentLeft = 1,
13 | WindowAlignmentCenter = 2,
14 | WindowAlignmentRight = 3
15 | };
16 |
17 |
18 | class 呈现窗口类
19 | {
20 | private:
21 | HWND *窗口句柄 = nullptr;
22 |
23 |
24 | public:
25 | HWND 任务栏_句柄;
26 | HWND 通知区域_句柄;
27 | HWND 开始按钮_句柄;
28 | HWND 活动区域_句柄;
29 |
30 | RECT 任务栏_矩形;
31 | RECT 通知区域_矩形;
32 | RECT 开始按钮_矩形;
33 | RECT 活动区域_矩形;
34 |
35 |
36 | public:
37 | 呈现窗口类(HWND*);
38 | ~呈现窗口类();
39 |
40 |
41 | private:
42 | ID2D1Factory* D2D工厂 = nullptr;
43 | ID2D1DCRenderTarget* D2D呈现目标 = nullptr;
44 | ID2D1SolidColorBrush* D2D纯色笔刷 = nullptr;
45 |
46 | IDWriteFactory* DWrite工厂 = nullptr;
47 | IDWriteTextFormat* DWrite主歌词文本格式 = nullptr;
48 | IDWriteTextLayout* DWrite主歌词文本布局 = nullptr;
49 | IDWriteTextFormat* DWrite副歌词文本格式 = nullptr;
50 | IDWriteTextLayout* DWrite副歌词文本布局 = nullptr;
51 |
52 |
53 | public:
54 | bool 深浅模式 = false;
55 | bool 组件按钮 = false;
56 | bool 居中对齐 = true;
57 |
58 | std::wstring 主歌词 = L"任务栏歌词启动成功";
59 | std::wstring 副歌词 = L"等待插件传输歌词...";
60 |
61 | std::wstring 字体名称 = L"Microsoft YaHei UI";
62 |
63 | D2D1::ColorF 字体颜色_浅色_主歌词 = D2D1::ColorF(0x000000, 1);
64 | D2D1::ColorF 字体颜色_浅色_副歌词 = D2D1::ColorF(0x000000, 1);
65 | D2D1::ColorF 字体颜色_深色_主歌词 = D2D1::ColorF(0xFFFFFF, 1);
66 | D2D1::ColorF 字体颜色_深色_副歌词 = D2D1::ColorF(0xFFFFFF, 1);
67 |
68 | DWRITE_FONT_WEIGHT 字体样式_主歌词_字重 = DWRITE_FONT_WEIGHT_NORMAL;
69 | DWRITE_FONT_WEIGHT 字体样式_副歌词_字重 = DWRITE_FONT_WEIGHT_NORMAL;
70 | DWRITE_FONT_STYLE 字体样式_主歌词_斜体 = DWRITE_FONT_STYLE_NORMAL;
71 | DWRITE_FONT_STYLE 字体样式_副歌词_斜体 = DWRITE_FONT_STYLE_NORMAL;
72 |
73 | bool 字体样式_主歌词_下划线 = false;
74 | bool 字体样式_副歌词_下划线 = false;
75 | bool 字体样式_主歌词_删除线 = false;
76 | bool 字体样式_副歌词_删除线 = false;
77 |
78 | WindowAlignment 窗口位置 = WindowAlignment::WindowAlignmentAdaptive;
79 |
80 | int 左边距 = 0;
81 | int 右边距 = 0;
82 |
83 | DWRITE_TEXT_ALIGNMENT 对齐方式_主歌词 = DWRITE_TEXT_ALIGNMENT::DWRITE_TEXT_ALIGNMENT_LEADING;
84 | DWRITE_TEXT_ALIGNMENT 对齐方式_副歌词 = DWRITE_TEXT_ALIGNMENT::DWRITE_TEXT_ALIGNMENT_LEADING;
85 |
86 |
87 | public:
88 | void 更新窗口();
89 |
90 |
91 | private:
92 | void 绘制窗口(long, long, long, long);
93 | void 绘制歌词(HDC&, RECT&);
94 |
95 |
96 | private:
97 | float DPI(UINT);
98 | };
99 |
--------------------------------------------------------------------------------
/src/taskbar-lyrics/TaskbarLyrics.cpp:
--------------------------------------------------------------------------------
1 | #include "NetworkServer.hpp"
2 | #include "CreateWindow.hpp"
3 | #include "TaskbarLyrics.hpp"
4 |
5 |
6 | 任务栏歌词类::任务栏歌词类(
7 | HINSTANCE 实例句柄,
8 | int 显示方法
9 | ) {
10 | this->获取端口();
11 |
12 | this->任务栏窗口 = new 任务栏窗口类(实例句柄, 显示方法);
13 | this->网络服务器 = new 网络服务器类(this->任务栏窗口, this->端口);
14 |
15 | this->网易云进程检测();
16 | }
17 |
18 |
19 | 任务栏歌词类::~任务栏歌词类()
20 | {
21 | FreeConsole();
22 | static_cast(UnregisterWaitEx(this->等待句柄, INVALID_HANDLE_VALUE));
23 |
24 | delete this->网络服务器;
25 | this->网络服务器 = nullptr;
26 |
27 | delete this->任务栏窗口;
28 | this->任务栏窗口 = nullptr;
29 | }
30 |
31 |
32 | void 任务栏歌词类::获取端口()
33 | {
34 | int argCount;
35 | LPWSTR* szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);
36 |
37 | if (szArgList[1])
38 | {
39 | std::wstringstream 宽字符转换流;
40 | 宽字符转换流 << szArgList[1];
41 | 宽字符转换流 >> this->端口;
42 | }
43 |
44 | LocalFree(szArgList);
45 | }
46 |
47 |
48 | void 任务栏歌词类::网易云进程检测()
49 | {
50 | auto 关闭窗口 = [] (PVOID lpParameter, BOOLEAN TimerOrWaitFired)
51 | {
52 | UNREFERENCED_PARAMETER(TimerOrWaitFired);
53 | 任务栏歌词类* _this = static_cast<任务栏歌词类*>(lpParameter);
54 | SendMessage(_this->任务栏窗口->窗口句柄, WM_CLOSE, NULL, NULL);
55 | };
56 |
57 | HWND 网易云句柄 = FindWindow(L"OrpheusBrowserHost", NULL);
58 | if (网易云句柄)
59 | {
60 | DWORD pid;
61 | GetWindowThreadProcessId(网易云句柄, &pid);
62 | HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
63 | RegisterWaitForSingleObject(&this->等待句柄, process, 关闭窗口, this, INFINITE, WT_EXECUTEONLYONCE);
64 | }
65 | }
66 |
67 |
68 | int APIENTRY wWinMain(
69 | _In_ HINSTANCE 实例句柄,
70 | _In_opt_ HINSTANCE 前一个实例句柄,
71 | _In_ LPWSTR 命令行,
72 | _In_ int 显示方法
73 | ) {
74 | UNREFERENCED_PARAMETER(前一个实例句柄);
75 | UNREFERENCED_PARAMETER(命令行);
76 |
77 | #ifdef _DEBUG
78 | AllocConsole();
79 | SetConsoleOutputCP(65001);
80 | FILE* stream;
81 | freopen_s(&stream, "conout$", "w", stdout);
82 | #endif
83 |
84 | 任务栏歌词类 任务栏歌词(实例句柄, 显示方法);
85 |
86 | MSG msg;
87 | while (GetMessage(&msg, NULL, 0, 0)) {
88 | TranslateMessage(&msg);
89 | DispatchMessage(&msg);
90 | }
91 |
92 | return msg.wParam;
93 | }
94 |
--------------------------------------------------------------------------------
/src/taskbar-lyrics/TaskbarLyrics.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 |
8 | class 任务栏歌词类
9 | {
10 | public:
11 | 任务栏歌词类(HINSTANCE, int);
12 | ~任务栏歌词类();
13 |
14 |
15 | private:
16 | HANDLE 等待句柄;
17 | unsigned short 端口 = 3798;
18 |
19 |
20 | public:
21 | 网络服务器类* 网络服务器;
22 | 任务栏窗口类* 任务栏窗口;
23 |
24 |
25 | private:
26 | void 网易云进程检测();
27 | void 获取端口();
28 | };
29 |
--------------------------------------------------------------------------------
/taskbar-lyrics.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 16.0
25 | Win32Proj
26 | {2cd1b59b-4a33-4bb0-b33f-9d30756c742f}
27 | taskbarlyrics
28 | 10.0
29 |
30 |
31 |
32 | Application
33 | true
34 | v143
35 | Unicode
36 |
37 |
38 | Application
39 | false
40 | v143
41 | true
42 | Unicode
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | $(SolutionDir)\dist\
58 | $(Configuration)\taskbar-lyrics\
59 |
60 |
61 | $(SolutionDir)\dist_debug\
62 | $(Configuration)\taskbar-lyrics\
63 |
64 |
65 |
66 | Level3
67 | true
68 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
69 | true
70 |
71 |
72 | Windows
73 | true
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | Level3
85 | true
86 | true
87 | true
88 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
89 | true
90 | MinSpace
91 | true
92 | Size
93 | stdcpp14
94 |
95 |
96 | Windows
97 | true
98 | true
99 | true
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/taskbar-lyrics.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 |
14 |
15 | 头文件
16 |
17 |
18 | 头文件
19 |
20 |
21 | 头文件
22 |
23 |
24 | 头文件
25 |
26 |
27 | 源文件
28 |
29 |
30 | 源文件
31 |
32 |
33 | 源文件
34 |
35 |
36 | 源文件
37 |
38 |
39 |
--------------------------------------------------------------------------------