Skip to content

Commit 2ef37fe

Browse files
committed
fix(mpv): 修复MPV播放UNC路径歌曲失败
1 parent 3849161 commit 2ef37fe

4 files changed

Lines changed: 64 additions & 6 deletions

File tree

electron/main/services/MpvService.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { app } from "electron";
33
import { connect } from "net";
44
import { join } from "path";
55
import { existsSync, unlinkSync } from "fs";
6+
import { fileURLToPath } from "url";
67
import { processLog } from "../logger";
78
import mainWindow from "../windows/main-window";
89

@@ -129,7 +130,7 @@ export class MpvService {
129130

130131
// 连接成功后再加载文件,确保能收到 file-loaded/playback-restart
131132
if (url) {
132-
this.sendCommand("loadfile", [url, "replace"]);
133+
this.sendCommand("loadfile", [this.normalizeMpvLoadTarget(url), "replace"]);
133134
}
134135
} catch (error) {
135136
processLog.error("启动 MPV 失败:", error);
@@ -477,4 +478,39 @@ export class MpvService {
477478
this.mpvProcessNonce = null;
478479
this.pendingFileLoaded = null;
479480
}
481+
482+
private normalizeMpvLoadTarget(url?: string) {
483+
if (!url) return "";
484+
if (!url.startsWith("file://")) {
485+
return this.normalizeLocalPath(url);
486+
}
487+
488+
try {
489+
const parsedUrl = new URL(url);
490+
if (parsedUrl.host) {
491+
const uncPath = `//${parsedUrl.host}${decodeURIComponent(parsedUrl.pathname)}`;
492+
return this.normalizeLocalPath(uncPath);
493+
}
494+
} catch {
495+
// 忽略 URL 解析失败,继续走兜底逻辑
496+
}
497+
498+
try {
499+
return this.normalizeLocalPath(fileURLToPath(url));
500+
} catch {
501+
const rawPath = decodeURIComponent(url.slice("file://".length));
502+
return this.normalizeLocalPath(rawPath);
503+
}
504+
}
505+
506+
private normalizeLocalPath(filePath: string) {
507+
if (process.platform !== "win32") return filePath;
508+
if (filePath.startsWith("//")) {
509+
return filePath.replace(/\//g, "\\");
510+
}
511+
if (/^[A-Za-z]:\//.test(filePath)) {
512+
return filePath.replace(/\//g, "\\");
513+
}
514+
return filePath;
515+
}
480516
}

src/core/automix/AutomixManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { isAudioAnalysis, isTransitionProposal, isAdvancedTransition } from "@/u
1515
import type { SongType } from "@/types/main";
1616
import { isElectron } from "@/utils/env";
1717
import { msToTime } from "@/utils/time";
18+
import { toFileUrl } from "@/utils/fileUrl";
1819

1920
/**
2021
* 自动混音(Automix)管理器
@@ -177,8 +178,7 @@ export class AutomixManager {
177178
const songManager = useSongManager();
178179
const cachedPath = await songManager.ensureMusicCachePath(songId, audioSourceUrl, quality);
179180
if (cachedPath) {
180-
const encodedPath = cachedPath.replace(/#/g, "%23").replace(/\?/g, "%3F");
181-
return `file://${encodedPath}`;
181+
return toFileUrl(cachedPath);
182182
}
183183
}
184184
return audioSourceUrl;

src/core/player/SongManager.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { QualityType, type SongType, type AudioSourceType } from "@/types/main";
1212
import { isLogin } from "@/utils/auth";
1313
import { isElectron } from "@/utils/env";
1414
import { formatSongsList } from "@/utils/format";
15+
import { toFileUrl } from "@/utils/fileUrl";
1516
import { AI_AUDIO_LEVELS } from "@/utils/meta";
1617
import { handleSongQuality } from "@/utils/helper";
1718
import { openUserLogin } from "@/utils/modal";
@@ -154,7 +155,7 @@ class SongManager {
154155
);
155156
if (cachePath) {
156157
console.log(`🚀 [${id}] 由本地音乐缓存提供`);
157-
return `file://${cachePath}`;
158+
return toFileUrl(cachePath);
158159
}
159160
} catch (e) {
160161
console.error(`❌ [${id}] 检查缓存失败:`, e);
@@ -492,8 +493,8 @@ class SongManager {
492493
console.error("❌ 本地文件不存在");
493494
return { id: song.id, url: undefined };
494495
}
495-
const encodedPath = song.path.replace(/#/g, "%23").replace(/\?/g, "%3F");
496-
return { id: song.id, url: `file://${encodedPath}`, source: "local" };
496+
const fileUrl = toFileUrl(song.path);
497+
return { id: song.id, url: fileUrl, source: "local" };
497498
}
498499

499500
// Stream songs (Subsonic / Jellyfin)

src/utils/fileUrl.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const encodeFilePath = (filePath: string) => {
2+
return encodeURI(filePath.replace(/\\/g, "/")).replace(/#/g, "%23").replace(/\?/g, "%3F");
3+
};
4+
5+
export const toFileUrl = (filePath: string): string => {
6+
const normalizedPath = encodeFilePath(filePath);
7+
8+
if (normalizedPath.startsWith("//")) {
9+
return `file:${normalizedPath}`;
10+
}
11+
12+
if (/^[A-Za-z]:\//.test(normalizedPath)) {
13+
return `file:///${normalizedPath}`;
14+
}
15+
16+
if (normalizedPath.startsWith("/")) {
17+
return `file://${normalizedPath}`;
18+
}
19+
20+
return `file:///${normalizedPath}`;
21+
};

0 commit comments

Comments
 (0)