From 0c0189bcef55b62b66e7b88f24e62fafa0647f98 Mon Sep 17 00:00:00 2001 From: chengww Date: Sun, 17 May 2026 19:48:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(local-music):=20=E6=B3=A8=E5=86=8C=20local?= =?UTF-8?q?=20=E5=8D=8F=E8=AE=AE=E4=B8=BA=E7=89=B9=E6=9D=83=20scheme=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9C=AC=E5=9C=B0=E9=9F=B3=E4=B9=90=20CORS?= =?UTF-8?q?=20=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - main/index.ts 在 app.whenReady 之前调用 registerSchemesAsPrivileged, 把 local 标记为 standard/secure/supportFetchAPI/stream/corsEnabled/bypassCSP, 让 http 页面(dev server / 生产)可跨协议加载本地音频 - fileManager.ts 用 Electron 25 起推荐的 protocol.handle 替代已弃用的 registerFileProtocol,内部转发到 file:// 自动支持 Range / 流式播放 - 修复 macOS/Linux 上去掉前导斜杠后丢失绝对路径前缀的问题 --- src/main/index.ts | 19 ++++++++++++++++- src/main/modules/fileManager.ts | 37 +++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 40190df..150ebc6 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,7 +1,24 @@ import { electronApp, optimizer } from '@electron-toolkit/utils'; -import { app, ipcMain, nativeImage, session } from 'electron'; +import { app, ipcMain, nativeImage, protocol, session } from 'electron'; import { join } from 'path'; +// 必须在 app.whenReady() 之前注册自定义协议为特权协议, +// 否则 http(s) 页面(dev server、生产环境的 file://)无法把 local:// 当成 +// 安全/可 fetch/可流式的资源加载,会触发 CORS 拦截或 net::ERR_UNKNOWN_URL_SCHEME +protocol.registerSchemesAsPrivileged([ + { + scheme: 'local', + privileges: { + standard: true, + secure: true, + supportFetchAPI: true, + stream: true, + bypassCSP: true, + corsEnabled: true + } + } +]); + import type { Language } from '../i18n/main'; import i18n from '../i18n/main'; import { loadLyricWindow } from './lyric'; diff --git a/src/main/modules/fileManager.ts b/src/main/modules/fileManager.ts index ce6e823..45ae3ef 100644 --- a/src/main/modules/fileManager.ts +++ b/src/main/modules/fileManager.ts @@ -1,7 +1,8 @@ -import { app, dialog, ipcMain, protocol, shell } from 'electron'; +import { app, dialog, ipcMain, net, protocol, shell } from 'electron'; import Store from 'electron-store'; import * as fs from 'fs'; import * as path from 'path'; +import { pathToFileURL } from 'url'; import { getStore } from './config'; @@ -28,31 +29,35 @@ function sanitizeFilename(filename: string): string { */ export function initializeFileManager() { // 注册本地文件协议 - protocol.registerFileProtocol('local', (request, callback) => { + // Electron 25+ 起 registerFileProtocol 已弃用,改用 protocol.handle,并配合 main/index.ts + // 中的 registerSchemesAsPrivileged,让 audio 元素能从 http(s) 页面跨协议加载本地文件 + protocol.handle('local', async (request) => { try { - const url = request.url; - // local://C:/Users/xxx.mp3 - let filePath = decodeURIComponent(url.replace('local:///', '')); + // local:/// + let filePath = decodeURIComponent(request.url.replace(/^local:\/\/\/?/, '')); - // 兼容 local:///C:/Users/xxx.mp3 这种情况 + // Windows: 协议解析后可能是 /C:/...,去掉前导斜杠 if (/^\/[a-zA-Z]:\//.test(filePath)) { filePath = filePath.slice(1); } - // 还原为系统路径格式 - filePath = path.normalize(filePath); - - // 检查文件是否存在 - if (!fs.existsSync(filePath)) { - console.error('File not found:', filePath); - callback({ error: -6 }); // net::ERR_FILE_NOT_FOUND - return; + // macOS/Linux 上去掉前导斜杠后会丢失绝对路径标识,这里补回 + if (process.platform !== 'win32' && !filePath.startsWith('/')) { + filePath = '/' + filePath; } - callback({ path: filePath }); + filePath = path.normalize(filePath); + + if (!fs.existsSync(filePath)) { + console.error('File not found:', filePath); + return new Response(null, { status: 404 }); + } + + // 用 net.fetch 走 file:// 加载,自动支持 Range / streaming,让媒体元素能正常 seek + return net.fetch(pathToFileURL(filePath).toString()); } catch (error) { console.error('Error handling local protocol:', error); - callback({ error: -2 }); // net::FAILED + return new Response(null, { status: 500 }); } });