Skip to content
Merged

1 #859

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion electron/main/windows/taskbar-lyric-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { app, type BrowserWindow, ipcMain, nativeTheme, screen } from "electron"
import { debounce } from "lodash-es";
import { join } from "node:path";
import { processLog } from "../logger";
import { useStore } from "../store";
import { isDev, port } from "../utils/config";
import { loadNativeModule } from "../utils/native-loader";
import { createWindow } from "./index";
import { useStore } from "../store";

type taskbarLyricModule = typeof import("@native/taskbar-lyric");

Expand Down Expand Up @@ -113,6 +113,19 @@ class TaskbarLyricWindow {

this.win.loadURL(taskbarLyricUrl);

// 因为任务栏窗口非常小,默认嵌入的开发者工具完全无法使用,
// 所以监听 F12 并按分离模式打开开发者工具
this.win.webContents.on("before-input-event", (event, input) => {
if (input.key === "F12" && input.type === "keyDown") {
if (this.win?.webContents.isDevToolsOpened()) {
this.win?.webContents.closeDevTools();
} else {
this.win?.webContents.openDevTools({ mode: "detach" });
}
event.preventDefault();
}
});

const sendTheme = () => {
if (this.win && !this.win.isDestroyed()) {
const isDark = nativeTheme.shouldUseDarkColors;
Expand Down
3 changes: 2 additions & 1 deletion src/components/Player/PlayerLyric/AMLyric.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
:wordFadeWidth="settingStore.wordFadeWidth"
:style="{
'--display-count-down-show': settingStore.countDownShow ? 'flex' : 'none',
'--amll-lp-font-size': settingStore.lyricFontSize + 'px',
'--amll-lp-font-size': getFontSize(settingStore.lyricFontSize, settingStore.lyricFontSizeMode),
'font-weight': settingStore.lyricFontWeight,
'font-family': settingStore.LyricFont !== 'follow' ? settingStore.LyricFont : '',
...lyricLangFontStyle(settingStore),
Expand All @@ -58,6 +58,7 @@ import { getLyricLanguage } from "@/utils/format";
import { usePlayerController } from "@/core/player/PlayerController";
import { cloneDeep } from "lodash-es";
import { lyricLangFontStyle } from "@/utils/lyric/lyricFontConfig";
import { getFontSize } from "@/utils/style";

defineProps({
currentTime: {
Expand Down
7 changes: 4 additions & 3 deletions src/components/Player/PlayerLyric/DefaultLyric.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<div
:key="`lyric-${musicStore.playSong.id}`"
:style="{
'--lrc-size': settingStore.lyricFontSize + 'px',
'--lrc-tran-size': settingStore.lyricTranFontSize + 'px',
'--lrc-roma-size': settingStore.lyricRomaFontSize + 'px',
'--lrc-size': getFontSize(settingStore.lyricFontSize, settingStore.lyricFontSizeMode),
'--lrc-tran-size': getFontSize(settingStore.lyricTranFontSize, settingStore.lyricFontSizeMode),
'--lrc-roma-size': getFontSize(settingStore.lyricRomaFontSize, settingStore.lyricFontSizeMode),
'--lrc-bold': settingStore.lyricFontWeight,
'--lrc-left-padding': `${settingStore.lyricHorizontalOffset}px`,
'font-family': settingStore.LyricFont !== 'follow' ? settingStore.LyricFont : '',
Expand Down Expand Up @@ -133,6 +133,7 @@ import { usePlayerController } from "@/core/player/PlayerController";
import { getLyricLanguage } from "@/utils/format";
import { isElectron } from "@/utils/env";
import { lyricLangFontStyle } from "@/utils/lyric/lyricFontConfig";
import { getFontSize } from "@/utils/style";

const props = defineProps({
currentTime: {
Expand Down
13 changes: 7 additions & 6 deletions src/components/Setting/components/LyricPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
'flex-direction': 'column',
'align-items': settingStore.lyricsPosition,
'--font-weight': settingStore.lyricFontWeight,
'--font-size': settingStore.lyricFontSize,
'--font-tran-size': tranFontSize,
'--font-roma-size': romaFontSize,
'--font-size': getFontSize(settingStore.lyricFontSize, settingStore.lyricFontSizeMode),
'--font-tran-size': getFontSize(tranFontSize, settingStore.lyricFontSizeMode),
'--font-roma-size': getFontSize(romaFontSize, settingStore.lyricFontSizeMode),
'--transform-origin':
settingStore.lyricsPosition === 'center'
? 'center'
Expand Down Expand Up @@ -37,6 +37,7 @@

<script setup lang="ts">
import { useSettingStore } from "@/stores";
import { getFontSize } from "@/utils/style";

const settingStore = useSettingStore();

Expand Down Expand Up @@ -72,15 +73,15 @@ const romaFontSize = fontSizeComputed("lyricRomaFontSize");

&:nth-of-type(1) {
font-weight: var(--font-weight);
font-size: calc(var(--font-size) * 1px);
font-size: var(--font-size);
}
&:nth-of-type(2) {
opacity: 0.6;
font-size: calc(var(--font-tran-size) * 1px);
font-size: var(--font-tran-size);
}
&:nth-of-type(3) {
opacity: 0.6;
font-size: calc(var(--font-roma-size) * 1px);
font-size: var(--font-roma-size);
}
}
}
Expand Down
28 changes: 25 additions & 3 deletions src/components/Setting/config/lyric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,25 @@ export const useLyricSettings = (): SettingConfig => {
noWrapper: true,
component: markRaw(LyricPreview),
},
{
key: "lyricFontSizeMode",
label: "自适应歌词大小",
type: "switch",
description: "开启后歌词大小将根据窗口高度自动缩放,避免全屏时过小或窗口时过大",
value: computed({
get: () => settingStore.lyricFontSizeMode === "adaptive",
set: (v) => (settingStore.lyricFontSizeMode = v ? "adaptive" : "fixed"),
}),
},
{
key: "lyricFontSize",
label: "歌词字体大小",
type: "input-number",
description: "单位 px,最小 12,最大 60",
description: computed(() =>
settingStore.lyricFontSizeMode === "adaptive"
? "作为基准大小 (以 1080p 高度为准)"
: "单位 px,最小 12,最大 60",
),
min: 12,
max: 60,
suffix: "px",
Expand All @@ -111,7 +125,11 @@ export const useLyricSettings = (): SettingConfig => {
key: "lyricTranFontSize",
label: "翻译歌词大小",
type: "input-number",
description: "单位 px,最小 5,最大 40",
description: computed(() =>
settingStore.lyricFontSizeMode === "adaptive"
? "作为基准大小 (以 1080p 高度为准)"
: "单位 px,最小 5,最大 40",
),
min: 5,
max: 40,
suffix: "px",
Expand All @@ -130,7 +148,11 @@ export const useLyricSettings = (): SettingConfig => {
key: "lyricRomaFontSize",
label: "音译歌词大小",
type: "input-number",
description: "单位 px,最小 5,最大 40",
description: computed(() =>
settingStore.lyricFontSizeMode === "adaptive"
? "作为基准大小 (以 1080p 高度为准)"
: "单位 px,最小 5,最大 40",
),
min: 5,
max: 40,
suffix: "px",
Expand Down
3 changes: 3 additions & 0 deletions src/stores/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export interface SettingState {
updateChannel: "stable" | "nightly";
/** 隐藏 VIP 标签 */
hideVipTag: boolean;
/** 歌词字体大小模式 */
lyricFontSizeMode: "fixed" | "adaptive";
/** 歌词字体大小 */
lyricFontSize: number;
/** 歌词翻译字体大小 */
Expand Down Expand Up @@ -549,6 +551,7 @@ export const useSettingStore = defineStore("setting", {
playSongDemo: false,
scrobbleSong: false,
dynamicCover: false,
lyricFontSizeMode: "adaptive",
lyricFontSize: 46,
lyricTranFontSize: 22,
lyricRomaFontSize: 18,
Expand Down
27 changes: 26 additions & 1 deletion src/utils/lyric/qrc-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,37 @@ const domParser: QRCParserFn = (xmlStr) => {
}
};

const decodeXml = (str: string) => {
return str
.replace(/&quot;/g, '"')
.replace(/&apos;/g, "'")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&amp;/g, "&");
};

// 实现正则策略
const regexParser: QRCParserFn = (xmlStr) => {
if (!xmlStr) return "";

// 尝试贪婪匹配 (处理 LyricContent="... "..." ..." 这种非标准 XML 引号未转义的情况)
// 匹配从 LyricContent=" 开始,直到标签结束前的最后一个引号
const greedyMatch = /LyricContent\s*=\s*"([\s\S]*)"\s*\/?>/.exec(xmlStr);
if (greedyMatch) {
const content = greedyMatch[1];
// 启发式检查:如果提取的内容中包含类似 ` Attribute="` 的结构,说明贪婪匹配吃掉了其他属性
// 这种情况下回退到非贪婪匹配
if (!/\s+\w+\s*=\s*"/.test(content)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The regex used in the heuristic check for attributes is a bit too restrictive. \w+ only matches letters, numbers, and underscores. XML attribute names can also contain hyphens (-) and periods (.). To make this check more robust and correctly handle attributes like data-foo, I suggest using [\w.-]+ instead.

Suggested change
if (!/\s+\w+\s*=\s*"/.test(content)) {
if (!/\s+[\w.-]+\s*=\s*"/.test(content)) {

return decodeXml(content);
}
}

// 标准非贪婪匹配 (兼容标准 XML 属性)
const match = /LyricContent\s*=\s*"([^"]*)"/.exec(xmlStr);

// 如果没有匹配到 XML 结构,假设输入本身就是内容 (保持原有逻辑的兼容性)
return match?.[1] || xmlStr;
const result = match?.[1] || xmlStr;
return decodeXml(result);
};

// 避免每次调用时的运行时检查
Expand Down
13 changes: 13 additions & 0 deletions src/utils/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

/**
* 获取自适应或固定像素的字体大小
* @param size 字体大小数值
* @param mode 字体大小模式 ('adaptive' | 'fixed')
* @returns CSS font-size 字符串
*/
export const getFontSize = (size: number, mode: string) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better type safety and code clarity, it's recommended to use a more specific type for the mode parameter instead of a generic string. The store defines this as "fixed" | "adaptive", and using that union type here will prevent potential errors and make the function's contract clearer.

export const getFontSize = (size: number, mode: "fixed" | "adaptive") => {

if (mode === "adaptive") {
return `calc(${size} / 1080 * 100vh)`;
}
return `${size}px`;
};