Skip to content

Commit d629dd6

Browse files
authored
Merge pull request #765 from imsyy/dev-download
✨ feat: 支持下载时转换繁体中文
2 parents 58e0f40 + 85efa77 commit d629dd6

3 files changed

Lines changed: 79 additions & 3 deletions

File tree

src/components/Setting/LocalSetting.vue

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,22 @@
349349
class="set"
350350
/>
351351
</n-card>
352+
<n-card class="set-item">
353+
<div class="label">
354+
<n-text class="name">下载歌词转繁体</n-text>
355+
<n-text class="tip" :depth="3">
356+
下载的歌词文件将转换为繁体中文(包括 LRC、YRC、TTML)
357+
<br />
358+
使用歌词设置中的繁体变体:{{ traditionalVariantLabel }}
359+
</n-text>
360+
</div>
361+
<n-switch
362+
v-model:value="settingStore.downloadLyricToTraditional"
363+
:disabled="!settingStore.downloadMeta || !settingStore.downloadLyric"
364+
:round="false"
365+
class="set"
366+
/>
367+
</n-card>
352368
<n-card class="set-item">
353369
<div class="label">
354370
<n-text class="name">下载的歌词文件编码格式</n-text>
@@ -398,6 +414,19 @@ const cacheSizeDisplay = ref<string>("--");
398414
const cacheLimit = ref<number>(10); // 本地状态
399415
const cacheLimited = ref<number>(1); // 是否限制缓存 (1 为限制)
400416
417+
// 繁体变体标签
418+
const variantMap: Record<string, string> = {
419+
s2t: "繁体中文 (标准)",
420+
s2tw: "台湾正体",
421+
s2hk: "香港繁体",
422+
s2twp: "台湾正体 (含词汇)",
423+
};
424+
425+
// 繁体变体标签
426+
const traditionalVariantLabel = computed(() => {
427+
return variantMap[settingStore.traditionalChineseVariant] || "繁体中文";
428+
});
429+
401430
// 默认下载音质选项
402431
const downloadQualityOptions = computed(() => {
403432
const levels = pick(songLevelData, ["l", "m", "h", "sq", "hr", "je", "sk", "db", "jm"]);

src/core/resource/DownloadManager.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { isElectron } from "@/utils/env";
44
import { saveAs } from "file-saver";
55
import { cloneDeep } from "lodash-es";
66
import { songDownloadUrl, songLyric, songLyricTTML, songUrl, unlockSongUrl } from "@/api/song";
7+
import { qqMusicMatch } from "@/api/qqmusic";
78

89
import { songLevelData } from "@/utils/meta";
910
import { getPlayerInfoObj } from "@/utils/format";
11+
import { getConverter, type ConverterMode } from "@/utils/opencc";
1012

1113
interface DownloadTask {
1214
song: SongType;
@@ -379,7 +381,7 @@ class DownloadManager {
379381

380382
if (downloadLyric) {
381383
const lyricResult = (await songLyric(song.id)) as LyricResult;
382-
lyric = this.processLyric(lyricResult);
384+
lyric = await this.processLyric(lyricResult);
383385

384386
// 获取逐字歌词内容用于另存
385387
if (downloadMakeYrc) {
@@ -397,6 +399,26 @@ class DownloadManager {
397399
console.log(
398400
`[Download] YRC fetched from lrcResult: ${!!yrcLyric}, len: ${yrcLyric?.length}`,
399401
);
402+
403+
// Fallback: 如果官方没有 YRC,尝试从 QM 获取
404+
if (!yrcLyric) {
405+
try {
406+
const artistsStr = Array.isArray(song.artists)
407+
? song.artists.map((a) => a.name).join("/")
408+
: String(song.artists || "");
409+
const keyword = `${song.name}-${artistsStr}`;
410+
console.log(`[Download] Trying QM fallback with keyword: ${keyword}`);
411+
const qmResult = await qqMusicMatch(keyword);
412+
if (qmResult?.code === 200 && qmResult?.qrc) {
413+
yrcLyric = qmResult.qrc;
414+
console.log(
415+
`[Download] QM QRC fetched as fallback, len: ${yrcLyric?.length}`,
416+
);
417+
}
418+
} catch (e) {
419+
console.error("[Download] Error fetching QM lyrics as fallback:", e);
420+
}
421+
}
400422
}
401423
} catch (e) {
402424
console.error("[Download] Error fetching verbatim lyrics:", e);
@@ -424,6 +446,9 @@ class DownloadManager {
424446
let content = ttmlLyric || yrcLyric;
425447
if (content) {
426448
try {
449+
// 繁体转换
450+
content = await this._convertToTraditionalIfNeeded(content);
451+
427452
const ext = ttmlLyric ? "ttml" : "yrc";
428453
const fileName = `${safeFileName}.${ext}`;
429454
const encoding = settingStore.downloadLyricEncoding || "utf-8";
@@ -479,7 +504,7 @@ class DownloadManager {
479504
* @param lyricResult 歌词结果
480505
* @returns 处理后的歌词字符串
481506
*/
482-
private processLyric(lyricResult: LyricResult): string {
507+
private async processLyric(lyricResult: LyricResult): Promise<string> {
483508
const settingStore = useSettingStore();
484509
try {
485510
const rawLyric = lyricResult?.lrc?.lyric || "";
@@ -595,7 +620,12 @@ class DownloadManager {
595620
}
596621
}
597622
}
598-
return lines.join("\n");
623+
const result = lines.join("\n");
624+
625+
// 繁体转换
626+
return await this._convertToTraditionalIfNeeded(result);
627+
628+
599629
} catch (e) {
600630
console.error("Lyric processing failed", e);
601631
return "";
@@ -659,6 +689,20 @@ class DownloadManager {
659689
this.retryDownload(id);
660690
});
661691
}
692+
/**
693+
* 繁体转换辅助方法
694+
* @param text 需要转换的文本
695+
* @returns 转换后的文本
696+
*/
697+
private async _convertToTraditionalIfNeeded(text: string): Promise<string> {
698+
const settingStore = useSettingStore();
699+
if (settingStore.downloadLyricToTraditional && text) {
700+
const variant = (settingStore.traditionalChineseVariant || "s2t") as ConverterMode;
701+
const converter = await getConverter(variant);
702+
return converter(text);
703+
}
704+
return text;
705+
}
662706
}
663707

664708
let instance: DownloadManager | null = null;

src/stores/setting.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ export interface SettingState {
103103
useUnlockForDownload: boolean;
104104
/** 内嵌暂逐字歌词 (beta) */
105105
downloadMakeYrc: boolean;
106+
/** 下载歌词转繁体 */
107+
downloadLyricToTraditional: boolean;
106108
/** 下载歌词文件编码 */
107109
downloadLyricEncoding: "utf-8" | "gbk" | "utf-16" | "iso-8859-1";
108110
/** 默认下载音质(弹窗默认选项) */
@@ -443,6 +445,7 @@ export const useSettingStore = defineStore("setting", {
443445
usePlaybackForDownload: false,
444446
useUnlockForDownload: false,
445447
downloadMakeYrc: false,
448+
downloadLyricToTraditional: false,
446449
downloadLyricEncoding: "utf-8",
447450
saveMetaFile: false,
448451
downloadSongLevel: "h",

0 commit comments

Comments
 (0)