Skip to content

Commit 43476bb

Browse files
authored
Merge pull request #761 from imsyy/dev-download
✨ feat: 利用配置的解锁服务获取下载链接
2 parents ff57d00 + 6047c07 commit 43476bb

File tree

10 files changed

+280
-6
lines changed

10 files changed

+280
-6
lines changed

components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ declare module 'vue' {
8888
NH1: typeof import('naive-ui')['NH1']
8989
NH2: typeof import('naive-ui')['NH2']
9090
NH3: typeof import('naive-ui')['NH3']
91+
NHelpTip: typeof import('naive-ui')['NHelpTip']
9192
NIcon: typeof import('naive-ui')['NIcon']
9293
NImage: typeof import('naive-ui')['NImage']
9394
NInput: typeof import('naive-ui')['NInput']
@@ -120,6 +121,7 @@ declare module 'vue' {
120121
NSelect: typeof import('naive-ui')['NSelect']
121122
NSkeleton: typeof import('naive-ui')['NSkeleton']
122123
NSlider: typeof import('naive-ui')['NSlider']
124+
NSpace: typeof import('naive-ui')['NSpace']
123125
NSpin: typeof import('naive-ui')['NSpin']
124126
NSwitch: typeof import('naive-ui')['NSwitch']
125127
NTab: typeof import('naive-ui')['NTab']

electron/main/ipc/ipc-file.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ const initFileIpc = (): void => {
240240
musicPath: string, // 参数名改为 musicPath 以示区分
241241
): Promise<{
242242
lyric: string;
243-
format: "lrc" | "ttml";
243+
format: "lrc" | "ttml" | "yrc";
244244
}> => {
245245
try {
246246
// 获取文件基本信息
@@ -257,7 +257,7 @@ const initFileIpc = (): void => {
257257
throw error;
258258
}
259259
// 遍历优先级
260-
for (const format of ["lrc", "ttml"] as const) {
260+
for (const format of ["ttml", "yrc", "lrc"] as const) {
261261
// 构造期望目标文件名
262262
const targetNameLower = `${baseName}.${format}`.toLowerCase();
263263
// 在文件列表中查找是否存在匹配项(忽略大小写)
@@ -695,6 +695,52 @@ const initFileIpc = (): void => {
695695
return relativePath && !relativePath.startsWith("..") && !isAbsolute(relativePath);
696696
});
697697
});
698+
699+
// 保存文件内容 (用于保存文本文件等)
700+
ipcMain.handle(
701+
"save-file-content",
702+
async (
703+
_,
704+
options: { path: string; fileName: string; content: string; encoding?: string },
705+
): Promise<{ success: boolean; message?: string }> => {
706+
try {
707+
const { path, fileName, content, encoding = "utf-8" } = options;
708+
// 规范化路径
709+
const dirPath = resolve(path);
710+
// 检查文件夹是否存在,不存在则自动递归创建
711+
try {
712+
await access(dirPath);
713+
} catch {
714+
await mkdir(dirPath, { recursive: true });
715+
}
716+
const filePath = join(dirPath, fileName);
717+
718+
if (encoding !== "utf-8") {
719+
try {
720+
// 使用动态导入,避免启动时加载问题
721+
const { encode } = await import("iconv-lite");
722+
// iconv-lite support 'utf16' as alias for 'utf-16' etc.
723+
const buffer = encode(content, encoding);
724+
await writeFile(filePath, buffer);
725+
} catch (e) {
726+
ipcLog.error(`❌ ${encoding} encoding failed:`, e);
727+
// Fallback to UTF-8 on error
728+
await writeFile(filePath, content, "utf-8");
729+
}
730+
} else {
731+
await writeFile(filePath, content, "utf-8");
732+
}
733+
734+
return { success: true };
735+
} catch (error) {
736+
ipcLog.error("❌ Error saving file content:", error);
737+
return {
738+
success: false,
739+
message: error instanceof Error ? error.message : "Unknown error",
740+
};
741+
}
742+
},
743+
);
698744
};
699745

700746
export default initFileIpc;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"get-port": "^7.1.0",
6767
"github-markdown-css": "^5.8.1",
6868
"got": "^14.6.5",
69+
"iconv-lite": "^0.7.2",
6970
"js-cookie": "^3.0.5",
7071
"jss": "^10.10.0",
7172
"jss-preset-default": "^10.10.0",

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/Player/PlayerMeta/PlayerData.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
@click="handlePopselectClick"
6464
>
6565
{{
66-
statusStore.playUblock || !statusStore.songQuality
66+
!statusStore.songQuality
6767
? "未知音质"
6868
: statusStore.songQuality
6969
}}

src/components/Setting/LocalSetting.vue

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,53 @@
319319
@update:value="handlePlaybackDownloadChange"
320320
/>
321321
</n-card>
322+
<n-card class="set-item">
323+
<div class="label">
324+
<n-text class="name">
325+
使用解锁接口下载
326+
<n-tag type="warning" size="small" round>Beta</n-tag>
327+
</n-text>
328+
<n-text class="tip" :depth="3">利用配置的解锁服务获取下载链接(优先于默认方式)</n-text>
329+
</div>
330+
<n-switch
331+
:value="settingStore.useUnlockForDownload"
332+
:round="false"
333+
class="set"
334+
@update:value="handleUnlockDownloadChange"
335+
/>
336+
</n-card>
337+
<n-card class="set-item">
338+
<div class="label">
339+
<n-text class="name">
340+
下载时另存逐字歌词文件
341+
<n-tag type="warning" size="small" round>Beta</n-tag>
342+
</n-text>
343+
<n-text class="tip" :depth="3">在有条件时保存独立的 YRC/TTML 逐字歌词文件(源文件仍内嵌LRC)</n-text>
344+
</div>
345+
<n-switch
346+
v-model:value="settingStore.downloadMakeYrc"
347+
:disabled="!settingStore.downloadMeta || !settingStore.downloadLyric"
348+
:round="false"
349+
class="set"
350+
/>
351+
</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">部分车载或老旧播放器可能仅支持 GBK 编码</n-text>
356+
</div>
357+
<n-select
358+
:value="settingStore.downloadLyricEncoding"
359+
:options="[
360+
{ label: 'UTF-8', value: 'utf-8' },
361+
{ label: 'GBK', value: 'gbk' },
362+
{ label: 'UTF-16', value: 'utf-16' },
363+
{ label: 'ISO-8859-1', value: 'iso-8859-1' },
364+
]"
365+
class="set"
366+
@update:value="handleLyricEncodingChange"
367+
/>
368+
</n-card>
322369
<n-card class="set-item">
323370
<div class="label">
324371
<n-text class="name">保留元信息文件</n-text>
@@ -475,6 +522,37 @@ const handlePlaybackDownloadChange = (value: boolean) => {
475522
}
476523
};
477524
525+
// 解锁接口下载开关
526+
const handleUnlockDownloadChange = (value: boolean) => {
527+
if (value) {
528+
window.$dialog.warning({
529+
title: "开启提示",
530+
content: "开启此功能可能导致音质下降和与原曲不一致等情况,确认要打开吗?",
531+
positiveText: "确认打开",
532+
negativeText: "取消",
533+
onPositiveClick: () => {
534+
settingStore.useUnlockForDownload = true;
535+
},
536+
});
537+
} else {
538+
settingStore.useUnlockForDownload = false;
539+
}
540+
};
541+
542+
// 歌词编码更改
543+
const handleLyricEncodingChange = (value: "utf-8" | "gbk" | "utf-16" | "iso-8859-1") => {
544+
if (value === settingStore.downloadLyricEncoding) return;
545+
window.$dialog.warning({
546+
title: "更改编码提示",
547+
content: "请确保你的编码为相应编码再开启,改变编码可能导致文件播放乱码。确认要更改吗?",
548+
positiveText: "确认更改",
549+
negativeText: "取消",
550+
onPositiveClick: () => {
551+
settingStore.downloadLyricEncoding = value;
552+
},
553+
});
554+
};
555+
478556
onMounted(async () => {
479557
try {
480558
const path = await window.api.store.get("cachePath");

src/core/player/LyricManager.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,9 +517,15 @@ class LyricManager {
517517
const musicStore = useMusicStore();
518518
const statusStore = useStatusStore();
519519
const settingStore = useSettingStore();
520-
const { lyric, format }: { lyric?: string; format?: "lrc" | "ttml" } =
520+
const { lyric, format }: { lyric?: string; format?: "lrc" | "ttml" | "yrc" } =
521521
await window.electron.ipcRenderer.invoke("get-music-lyric", path);
522522
if (!lyric) return { lrcData: [], yrcData: [] };
523+
// YRC 直接解析
524+
if (format === "yrc") {
525+
const lines = parseYrc(lyric) || [];
526+
statusStore.usingTTMLLyric = false;
527+
return await this.applyChineseVariant({ lrcData: [], yrcData: lines });
528+
}
523529
// TTML 直接返回
524530
if (format === "ttml") {
525531
const sorted = this.cleanTTMLTranslations(lyric);

src/core/player/SongManager.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,16 @@ class SongManager {
196196
const unlockUrl = r.value?.result?.url;
197197
// 解锁成功后,触发下载
198198
this.triggerCacheDownload(songId, unlockUrl);
199+
// 推断音质
200+
let quality = QualityType.HQ;
201+
if (unlockUrl && (unlockUrl.includes(".flac") || unlockUrl.includes(".wav"))) {
202+
quality = QualityType.SQ;
203+
}
199204
return {
200205
id: songId,
201206
url: unlockUrl,
202207
isUnlocked: true,
208+
quality,
203209
};
204210
}
205211
}

0 commit comments

Comments
 (0)