@lynker-desktop/electron-sdk 0.0.9-alpha.57 → 0.0.9-alpha.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/main/downloader.d.ts +134 -0
- package/esm/main/downloader.d.ts.map +1 -0
- package/esm/main/downloader.js +397 -0
- package/esm/main/downloader.js.map +1 -0
- package/esm/main/index.d.ts.map +1 -1
- package/esm/main/index.js +2 -0
- package/esm/main/index.js.map +1 -1
- package/main/downloader.d.ts +134 -0
- package/main/downloader.d.ts.map +1 -0
- package/main/downloader.js +397 -0
- package/main/downloader.js.map +1 -0
- package/main/index.d.ts.map +1 -1
- package/main/index.js +2 -0
- package/main/index.js.map +1 -1
- package/package.json +5 -5
package/esm/main/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/main/index.ts"],"sourcesContent":["import type { WebContents } from 'electron'\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport {promisify} from 'node:util';\nimport electron, { app, webContents } from 'electron';\nimport md5 from 'md5';\nimport { v5 as uuidv5 } from 'uuid';\nimport lodash from 'lodash';\nimport * as remote from '@electron/remote/main'\nimport * as ipc from '@lynker-desktop/electron-ipc/main'\nimport * as logs from '@lynker-desktop/electron-logs/main'\nimport * as windowManager from '@lynker-desktop/electron-window-manager/main'\nimport type { PreloadWebContentsConfig } from '@lynker-desktop/electron-window-manager/common'\nimport { IPC_GET_APP_METRICS, type ServeOptions, type VideoDownloadOptions, type VideoProgressCallback, type VideoDownloadResult, type CacheOptions } from '../common';\nimport { initializeStore } from './store';\nimport { initializeClipboard } from './clipboard';\nimport { ResourceCache, type ResourceCacheOptions } from './resource-cache';\nimport { downloadVideo, clearVideoCache, getVideoCacheStats, removeVideoCache, getFileUrl, setFFmpegPath } from './video-downloader';\n\n// 重新导出 ResourceCache 相关类型和类,以便外部使用\nexport { ResourceCache, type ResourceCacheOptions };\n\nif (!remote.isInitialized()) {\n remote.initialize();\n}\n\nexport let isInitialized = false;\n\nexport interface SDK_TYPE {\n ipc: typeof ipc.mainIPC;\n windowManager: windowManager.WindowsManager,\n getPreload: () => string;\n downloadVideo: (options: VideoDownloadOptions, callbacks?: VideoProgressCallback) => Promise<VideoDownloadResult>;\n clearVideoCache: (outputDir: string) => void;\n getVideoCacheStats: (outputDir: string) => { size: number; entries: Array<{ fileName: string; filePath: string; fileSize: number; mtime: number }> };\n removeVideoCache: (options: VideoDownloadOptions) => boolean;\n}\n\n\nfunction findWebContentsByPid(pid: number): {id: number, url: string} | undefined {\n const all = webContents.getAllWebContents();\n\n const wc = all.find((wc) => {\n try {\n return wc.getOSProcessId() === pid;\n } catch (e) {\n return false;\n }\n });\n // @ts-ignore\n return wc ? {id: wc.id, url: wc.getURL()} : undefined;\n}\n\n\n/**\n * 初始化\n * @param protocol 唤起协议 例:dgz\n * @param preload 预加载js 必须传\n * @param loadingViewUrl 窗口管理默认loading页面\n * @returns\n */\nexport const initialize = (options: { protocol: string, preload: string, loadingViewUrl?: string, errorViewUrl?: string, preloadWebContentsConfig?: PreloadWebContentsConfig, webviewDomainWhiteList?: string[], serveOptions?: ServeOptions, resourceCacheOptions?: ResourceCacheOptions, ffmpegPath?: string }): SDK_TYPE => {\n let {protocol, preload, loadingViewUrl, serveOptions = [], errorViewUrl, preloadWebContentsConfig, webviewDomainWhiteList, resourceCacheOptions} = options;\n if (isInitialized) {\n // @ts-ignore\n return global['__GLBOAL_ELECTRON_SDK__'];\n }\n isInitialized = true;\n ipc.initialize();\n logs.initialize();\n initializeStore();\n initializeClipboard();\n if (options.ffmpegPath) {\n setFFmpegPath(options.ffmpegPath);\n }\n const wm = windowManager.initialize(preload, loadingViewUrl, errorViewUrl, preloadWebContentsConfig, webviewDomainWhiteList || []);\n if (protocol) {\n registerProtocol(protocol);\n }\n if (!serveOptions) {\n serveOptions = []\n }\n if (resourceCacheOptions) {\n if (resourceCacheOptions.cacheDir) {\n serveOptions.push({\n scheme: ResourceCache.scheme,\n directory: resourceCacheOptions.cacheDir,\n })\n }\n }\n if (serveOptions && serveOptions.length) {\n serve(serveOptions);\n }\n if (resourceCacheOptions) {\n app.on('ready', () => {\n new ResourceCache(electron.session.defaultSession, resourceCacheOptions);\n });\n }\n class SDK {\n static ipc = ipc.mainIPC;\n static windowManager = wm;\n static getPreload = () => preload;\n static downloadVideo = downloadVideo;\n static clearVideoCache = clearVideoCache;\n static getVideoCacheStats = getVideoCacheStats;\n static removeVideoCache = removeVideoCache;\n }\n ipc.mainIPC.handleRenderer(IPC_GET_APP_METRICS, async () => {\n const metrics = app.getAppMetrics().map(i => {\n const webContents = findWebContentsByPid(i.pid || 0);\n console.log('webContents', webContents);\n return {\n ...i,\n webContents: webContents?.id || undefined,\n url: webContents?.url,\n }\n })\n return JSON.stringify(metrics);\n })\n ipc.mainIPC.handleRenderer(`__sdk_transcode_video__`, async (options:VideoDownloadOptions) => {\n const md5Options = md5(JSON.stringify(options.url));\n downloadVideo(options, {\n onDownloadProgress: (progress) => {\n ipc.mainIPC.invokeAllRenderer(`__sdk_transcode_video_callback_${md5Options}__`, {\n type: 'downloadProgress',\n data: progress\n });\n },\n onTranscodeProgress: (progress) => {\n ipc.mainIPC.invokeAllRenderer(`__sdk_transcode_video_callback_${md5Options}__`, {\n type: 'transcodeProgress',\n data: progress\n });\n },\n onComplete: (result) => {\n ipc.mainIPC.invokeAllRenderer(`__sdk_transcode_video_callback_${md5Options}__`, {\n type: 'complete',\n data: result\n });\n },\n });\n return true;\n })\n // @ts-ignore\n global['__GLBOAL_ELECTRON_SDK__'] = SDK;\n return SDK;\n}\n\n/** 后续再优化 */\nexport const getSDK = (): SDK_TYPE => {\n // @ts-ignore\n if (!global['__GLBOAL_ELECTRON_SDK__']) {\n throw '请在ready前先调用initialize'\n }\n // @ts-ignore\n return global['__GLBOAL_ELECTRON_SDK__'];\n}\n\n/**\n * 注册唤起\n */\nexport const registerProtocol = (() => {\n let isRegister = false;\n let launchOptions = ``;\n return (protocol: string) => {\n if (isRegister) {\n return;\n }\n isRegister = true;\n if (process.defaultApp) {\n if (process.argv.length >= 2) {\n app.setAsDefaultProtocolClient(protocol, process.execPath, [path.resolve(lodash.get(process, `argv[1]`, ''))]);\n }\n } else {\n app.setAsDefaultProtocolClient(protocol);\n }\n const handleUrl = (url: string) => {\n launchOptions = `${url ?? ''}`;\n ipc.mainIPC.invokeAllRenderer('__GLOBAL_CHANGE_LAUNCH_OPTIONS__', url)\n };\n\n function handleArgv(argv: string[]) {\n const prefix = `${protocol}:`;\n const offset = app?.isPackaged ? 1 : 2;\n const url = argv.find((arg, i) => i >= offset && arg.startsWith(prefix));\n if (url) handleUrl(url);\n }\n\n handleArgv(process.argv);\n\n app.on('second-instance', (event, argv) => {\n console.info(event)\n if (process.platform === 'win32') {\n // Windows\n handleArgv(argv);\n }\n });\n\n // macOS\n app.on('open-url', (event, urlStr) => {\n console.info(event)\n handleUrl(urlStr);\n });\n\n\n app.on(\"open-url\", (_event, url) => {\n handleUrl(url);\n });\n ipc.mainIPC.handleRenderer('__GLOBAL_GET_LAUNCH_OPTIONS__', async () => {\n return launchOptions;\n })\n ipc.mainIPC.handleRenderer('__GLOBAL_DEL_LAUNCH_OPTIONS__', async () => {\n launchOptions = ``;\n return launchOptions;\n })\n }\n})();\n\n/**\n * 注册协议\n * @param options\n * @returns\n */\nexport const serve = (options: ServeOptions) => {\n if (!options.length) {\n\t\tthrow new Error('请传入配置');\n\t}\n const stat = promisify(fs.stat);\n\n // See https://cs.chromium.org/chromium/src/net/base/net_error_list.h\n const FILE_NOT_FOUND = -6;\n\n const getPath: any = async (path_: string) => {\n try {\n const result = await stat(path_);\n\n if (result.isFile()) {\n return path_;\n }\n\n if (result.isDirectory()) {\n return getPath(path.join(path_, 'index.html'));\n }\n } catch (_) {}\n };\n\n const createHandler = (item: any) => {\n return async (request: any, callback: any) => {\n const indexPath = path.join(item.directory, 'index.html');\n const filePath = path.join(item.directory, decodeURIComponent(new URL(request.url).pathname));\n const resolvedPath = await getPath(filePath);\n const targetPath = resolvedPath || indexPath;\n const ext = path.extname(targetPath).toLowerCase();\n\n if (resolvedPath || !ext || ext === '.html' || ext === '.asar') {\n callback({\n path: resolvedPath || indexPath\n });\n } else {\n callback({error: FILE_NOT_FOUND});\n }\n }\n };\n\n\toptions = options.map((item) => {\n\t\treturn {\n\t\t\t...item,\n\t\t\tisCorsEnabled: !!item?.isCorsEnabled,\n\t\t\tscheme: item?.scheme || 'app',\n\t\t\tdirectory: path.resolve(electron.app.getAppPath(), item.directory),\n\t\t}\n\t})\n // 注册自定义协议的权限配置\n // 说明:\n // bypassCSP: 是否绕过内容安全策略(CSP),允许加载本地资源时不受 CSP 限制\n // standard: 使协议表现得像标准协议(如 http/https),支持标准 API\n // secure: 标记为安全协议,允许安全相关的操作\n // allowServiceWorkers: 允许该协议下注册 Service Worker\n // supportFetchAPI: 允许 fetch/XHR 等 API 访问该协议资源\n // corsEnabled: 是否启用跨域资源共享(CORS)\n // stream: 支持流式传输\n\telectron.protocol.registerSchemesAsPrivileged(options.map((item) => {\n\t\treturn {\n\t\t\tscheme: item.scheme,\n\t\t\tprivileges: {\n bypassCSP: true,\n\t\t\t\tstandard: true,\n\t\t\t\tsecure: true,\n\t\t\t\tallowServiceWorkers: true,\n\t\t\t\tsupportFetchAPI: true,\n\t\t\t\tcorsEnabled: item.isCorsEnabled ?? false,\n stream: true,\n\t\t\t}\n\t\t}\n\t}));\n\treturn new Promise((resolve, reject) => {\n\t\ttry {\n\t\t\telectron.app.on('ready', () => {\n\t\t\t\toptions.forEach((item) => {\n\t\t\t\t\tconst handler = createHandler(item);\n\t\t\t\t\tconst session = item.partition ? electron.session.fromPartition(item.partition) : electron.session.defaultSession;\n\t\t\t\t\tsession.protocol.registerFileProtocol(item.scheme, handler);\n\t\t\t\t})\n\t\t\t\tresolve(true)\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treject(error)\n\t\t}\n\t})\n}\n\n\n/**\n * @electron/remote/main enable方法\n * @param webContents\n * @returns\n */\nexport const enable = (webContents: WebContents) => remote.enable(webContents);\n\n/**\n * 获取随机UUID\n * @param key\n * @returns\n */\nexport function getRandomUUID(key: string = `${Date.now()}`): string {\n let webcrypto!: Crypto;\n let randomValue = Math?.random()?.toString()?.replace('0.', '');\n try {\n if (!webcrypto && !!window && window?.crypto) {\n webcrypto = window.crypto;\n }\n } catch (error) {console.log}\n try {\n const ar = webcrypto.getRandomValues(new Uint8Array(12));\n randomValue = `${md5(ar)}` || randomValue;\n } catch (error) {}\n const uuid = uuidv5(\n `${JSON.stringify(key)}_${Date.now()}_${randomValue}`,\n uuidv5.URL,\n );\n return uuid;\n}\n\n\n/**\n * 获取定制Session\n */\nexport const getCustomSession = windowManager.getCustomSession;\n\n/**\n * 获取SDK实例\n * @returns\n */\nexport default new Proxy({} as SDK_TYPE, {\n get(_target, prop) {\n if (!isInitialized) {\n throw new Error('请在ready前先调用initialize');\n }\n if (!prop) {\n // @ts-ignore\n return global['__GLBOAL_ELECTRON_SDK__'];\n }\n // @ts-ignore\n return global['__GLBOAL_ELECTRON_SDK__'][prop];\n },\n set() {\n return false;\n }\n});\n"],"names":["uuidv5"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/main/index.ts"],"sourcesContent":["import type { WebContents } from 'electron'\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport {promisify} from 'node:util';\nimport electron, { app, webContents } from 'electron';\nimport md5 from 'md5';\nimport { v5 as uuidv5 } from 'uuid';\nimport lodash from 'lodash';\nimport * as remote from '@electron/remote/main'\nimport * as ipc from '@lynker-desktop/electron-ipc/main'\nimport * as logs from '@lynker-desktop/electron-logs/main'\nimport * as windowManager from '@lynker-desktop/electron-window-manager/main'\nimport type { PreloadWebContentsConfig } from '@lynker-desktop/electron-window-manager/common'\nimport { IPC_GET_APP_METRICS, type ServeOptions, type VideoDownloadOptions, type VideoProgressCallback, type VideoDownloadResult, type CacheOptions } from '../common';\nimport { initializeStore } from './store';\nimport { initializeClipboard } from './clipboard';\nimport { ResourceCache, type ResourceCacheOptions } from './resource-cache';\nimport { downloadVideo, clearVideoCache, getVideoCacheStats, removeVideoCache, getFileUrl, setFFmpegPath } from './video-downloader';\nimport { DownloadOptions, getDownloader } from './downloader';\n\n// 重新导出 ResourceCache 相关类型和类,以便外部使用\nexport { ResourceCache, type ResourceCacheOptions };\n\nif (!remote.isInitialized()) {\n remote.initialize();\n}\n\nexport let isInitialized = false;\n\nexport interface SDK_TYPE {\n ipc: typeof ipc.mainIPC;\n windowManager: windowManager.WindowsManager,\n getPreload: () => string;\n downloadVideo: (options: VideoDownloadOptions, callbacks?: VideoProgressCallback) => Promise<VideoDownloadResult>;\n clearVideoCache: (outputDir: string) => void;\n getVideoCacheStats: (outputDir: string) => { size: number; entries: Array<{ fileName: string; filePath: string; fileSize: number; mtime: number }> };\n removeVideoCache: (options: VideoDownloadOptions) => boolean;\n}\n\n\nfunction findWebContentsByPid(pid: number): {id: number, url: string} | undefined {\n const all = webContents.getAllWebContents();\n\n const wc = all.find((wc) => {\n try {\n return wc.getOSProcessId() === pid;\n } catch (e) {\n return false;\n }\n });\n // @ts-ignore\n return wc ? {id: wc.id, url: wc.getURL()} : undefined;\n}\n\n\n/**\n * 初始化\n * @param protocol 唤起协议 例:dgz\n * @param preload 预加载js 必须传\n * @param loadingViewUrl 窗口管理默认loading页面\n * @returns\n */\nexport const initialize = (options: { protocol: string, preload: string, loadingViewUrl?: string, errorViewUrl?: string, preloadWebContentsConfig?: PreloadWebContentsConfig, webviewDomainWhiteList?: string[], serveOptions?: ServeOptions, resourceCacheOptions?: ResourceCacheOptions, ffmpegPath?: string }): SDK_TYPE => {\n let {protocol, preload, loadingViewUrl, serveOptions = [], errorViewUrl, preloadWebContentsConfig, webviewDomainWhiteList, resourceCacheOptions} = options;\n if (isInitialized) {\n // @ts-ignore\n return global['__GLBOAL_ELECTRON_SDK__'];\n }\n isInitialized = true;\n ipc.initialize();\n logs.initialize();\n initializeStore();\n initializeClipboard();\n if (options.ffmpegPath) {\n setFFmpegPath(options.ffmpegPath);\n }\n const wm = windowManager.initialize(preload, loadingViewUrl, errorViewUrl, preloadWebContentsConfig, webviewDomainWhiteList || []);\n if (protocol) {\n registerProtocol(protocol);\n }\n if (!serveOptions) {\n serveOptions = []\n }\n if (resourceCacheOptions) {\n if (resourceCacheOptions.cacheDir) {\n serveOptions.push({\n scheme: ResourceCache.scheme,\n directory: resourceCacheOptions.cacheDir,\n })\n }\n }\n if (serveOptions && serveOptions.length) {\n serve(serveOptions);\n }\n if (resourceCacheOptions) {\n app.on('ready', () => {\n new ResourceCache(electron.session.defaultSession, resourceCacheOptions);\n });\n }\n class SDK {\n static ipc = ipc.mainIPC;\n static windowManager = wm;\n static getPreload = () => preload;\n static downloader = getDownloader();\n static downloadVideo = downloadVideo;\n static clearVideoCache = clearVideoCache;\n static getVideoCacheStats = getVideoCacheStats;\n static removeVideoCache = removeVideoCache;\n }\n ipc.mainIPC.handleRenderer(IPC_GET_APP_METRICS, async () => {\n const metrics = app.getAppMetrics().map(i => {\n const webContents = findWebContentsByPid(i.pid || 0);\n console.log('webContents', webContents);\n return {\n ...i,\n webContents: webContents?.id || undefined,\n url: webContents?.url,\n }\n })\n return JSON.stringify(metrics);\n })\n ipc.mainIPC.handleRenderer(`__sdk_transcode_video__`, async (options:VideoDownloadOptions) => {\n const md5Options = md5(JSON.stringify(options.url));\n downloadVideo(options, {\n onDownloadProgress: (progress) => {\n ipc.mainIPC.invokeAllRenderer(`__sdk_transcode_video_callback_${md5Options}__`, {\n type: 'downloadProgress',\n data: progress\n });\n },\n onTranscodeProgress: (progress) => {\n ipc.mainIPC.invokeAllRenderer(`__sdk_transcode_video_callback_${md5Options}__`, {\n type: 'transcodeProgress',\n data: progress\n });\n },\n onComplete: (result) => {\n ipc.mainIPC.invokeAllRenderer(`__sdk_transcode_video_callback_${md5Options}__`, {\n type: 'complete',\n data: result\n });\n },\n });\n return true;\n })\n // @ts-ignore\n global['__GLBOAL_ELECTRON_SDK__'] = SDK;\n return SDK;\n}\n\n/** 后续再优化 */\nexport const getSDK = (): SDK_TYPE => {\n // @ts-ignore\n if (!global['__GLBOAL_ELECTRON_SDK__']) {\n throw '请在ready前先调用initialize'\n }\n // @ts-ignore\n return global['__GLBOAL_ELECTRON_SDK__'];\n}\n\n/**\n * 注册唤起\n */\nexport const registerProtocol = (() => {\n let isRegister = false;\n let launchOptions = ``;\n return (protocol: string) => {\n if (isRegister) {\n return;\n }\n isRegister = true;\n if (process.defaultApp) {\n if (process.argv.length >= 2) {\n app.setAsDefaultProtocolClient(protocol, process.execPath, [path.resolve(lodash.get(process, `argv[1]`, ''))]);\n }\n } else {\n app.setAsDefaultProtocolClient(protocol);\n }\n const handleUrl = (url: string) => {\n launchOptions = `${url ?? ''}`;\n ipc.mainIPC.invokeAllRenderer('__GLOBAL_CHANGE_LAUNCH_OPTIONS__', url)\n };\n\n function handleArgv(argv: string[]) {\n const prefix = `${protocol}:`;\n const offset = app?.isPackaged ? 1 : 2;\n const url = argv.find((arg, i) => i >= offset && arg.startsWith(prefix));\n if (url) handleUrl(url);\n }\n\n handleArgv(process.argv);\n\n app.on('second-instance', (event, argv) => {\n console.info(event)\n if (process.platform === 'win32') {\n // Windows\n handleArgv(argv);\n }\n });\n\n // macOS\n app.on('open-url', (event, urlStr) => {\n console.info(event)\n handleUrl(urlStr);\n });\n\n\n app.on(\"open-url\", (_event, url) => {\n handleUrl(url);\n });\n ipc.mainIPC.handleRenderer('__GLOBAL_GET_LAUNCH_OPTIONS__', async () => {\n return launchOptions;\n })\n ipc.mainIPC.handleRenderer('__GLOBAL_DEL_LAUNCH_OPTIONS__', async () => {\n launchOptions = ``;\n return launchOptions;\n })\n }\n})();\n\n/**\n * 注册协议\n * @param options\n * @returns\n */\nexport const serve = (options: ServeOptions) => {\n if (!options.length) {\n\t\tthrow new Error('请传入配置');\n\t}\n const stat = promisify(fs.stat);\n\n // See https://cs.chromium.org/chromium/src/net/base/net_error_list.h\n const FILE_NOT_FOUND = -6;\n\n const getPath: any = async (path_: string) => {\n try {\n const result = await stat(path_);\n\n if (result.isFile()) {\n return path_;\n }\n\n if (result.isDirectory()) {\n return getPath(path.join(path_, 'index.html'));\n }\n } catch (_) {}\n };\n\n const createHandler = (item: any) => {\n return async (request: any, callback: any) => {\n const indexPath = path.join(item.directory, 'index.html');\n const filePath = path.join(item.directory, decodeURIComponent(new URL(request.url).pathname));\n const resolvedPath = await getPath(filePath);\n const targetPath = resolvedPath || indexPath;\n const ext = path.extname(targetPath).toLowerCase();\n\n if (resolvedPath || !ext || ext === '.html' || ext === '.asar') {\n callback({\n path: resolvedPath || indexPath\n });\n } else {\n callback({error: FILE_NOT_FOUND});\n }\n }\n };\n\n\toptions = options.map((item) => {\n\t\treturn {\n\t\t\t...item,\n\t\t\tisCorsEnabled: !!item?.isCorsEnabled,\n\t\t\tscheme: item?.scheme || 'app',\n\t\t\tdirectory: path.resolve(electron.app.getAppPath(), item.directory),\n\t\t}\n\t})\n // 注册自定义协议的权限配置\n // 说明:\n // bypassCSP: 是否绕过内容安全策略(CSP),允许加载本地资源时不受 CSP 限制\n // standard: 使协议表现得像标准协议(如 http/https),支持标准 API\n // secure: 标记为安全协议,允许安全相关的操作\n // allowServiceWorkers: 允许该协议下注册 Service Worker\n // supportFetchAPI: 允许 fetch/XHR 等 API 访问该协议资源\n // corsEnabled: 是否启用跨域资源共享(CORS)\n // stream: 支持流式传输\n\telectron.protocol.registerSchemesAsPrivileged(options.map((item) => {\n\t\treturn {\n\t\t\tscheme: item.scheme,\n\t\t\tprivileges: {\n bypassCSP: true,\n\t\t\t\tstandard: true,\n\t\t\t\tsecure: true,\n\t\t\t\tallowServiceWorkers: true,\n\t\t\t\tsupportFetchAPI: true,\n\t\t\t\tcorsEnabled: item.isCorsEnabled ?? false,\n stream: true,\n\t\t\t}\n\t\t}\n\t}));\n\treturn new Promise((resolve, reject) => {\n\t\ttry {\n\t\t\telectron.app.on('ready', () => {\n\t\t\t\toptions.forEach((item) => {\n\t\t\t\t\tconst handler = createHandler(item);\n\t\t\t\t\tconst session = item.partition ? electron.session.fromPartition(item.partition) : electron.session.defaultSession;\n\t\t\t\t\tsession.protocol.registerFileProtocol(item.scheme, handler);\n\t\t\t\t})\n\t\t\t\tresolve(true)\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treject(error)\n\t\t}\n\t})\n}\n\n\n/**\n * @electron/remote/main enable方法\n * @param webContents\n * @returns\n */\nexport const enable = (webContents: WebContents) => remote.enable(webContents);\n\n/**\n * 获取随机UUID\n * @param key\n * @returns\n */\nexport function getRandomUUID(key: string = `${Date.now()}`): string {\n let webcrypto!: Crypto;\n let randomValue = Math?.random()?.toString()?.replace('0.', '');\n try {\n if (!webcrypto && !!window && window?.crypto) {\n webcrypto = window.crypto;\n }\n } catch (error) {console.log}\n try {\n const ar = webcrypto.getRandomValues(new Uint8Array(12));\n randomValue = `${md5(ar)}` || randomValue;\n } catch (error) {}\n const uuid = uuidv5(\n `${JSON.stringify(key)}_${Date.now()}_${randomValue}`,\n uuidv5.URL,\n );\n return uuid;\n}\n\n\n/**\n * 获取定制Session\n */\nexport const getCustomSession = windowManager.getCustomSession;\n\n/**\n * 获取SDK实例\n * @returns\n */\nexport default new Proxy({} as SDK_TYPE, {\n get(_target, prop) {\n if (!isInitialized) {\n throw new Error('请在ready前先调用initialize');\n }\n if (!prop) {\n // @ts-ignore\n return global['__GLBOAL_ELECTRON_SDK__'];\n }\n // @ts-ignore\n return global['__GLBOAL_ELECTRON_SDK__'][prop];\n },\n set() {\n return false;\n }\n});\n"],"names":["uuidv5"],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 下载进度回调函数类型
|
|
3
|
+
*/
|
|
4
|
+
export type DownloadProgressCallback = (progress: {
|
|
5
|
+
url: string;
|
|
6
|
+
downloaded: number;
|
|
7
|
+
total: number;
|
|
8
|
+
percentage: number;
|
|
9
|
+
speed: number;
|
|
10
|
+
}) => void;
|
|
11
|
+
/**
|
|
12
|
+
* 下载选项
|
|
13
|
+
*/
|
|
14
|
+
export interface DownloadOptions {
|
|
15
|
+
/** 唯一标识(IPC 调用时必需) */
|
|
16
|
+
id?: string;
|
|
17
|
+
/** 输出文件路径(可选,如果不提供则使用 URL 的文件名) */
|
|
18
|
+
outputPath?: string;
|
|
19
|
+
/** 输出目录(可选,如果不提供则使用当前目录) */
|
|
20
|
+
outputDir?: string;
|
|
21
|
+
/** 输出文件名(可选,如果不提供则从 URL 提取) */
|
|
22
|
+
fileName?: string;
|
|
23
|
+
/** 是否覆盖已存在的文件(默认 true) */
|
|
24
|
+
override?: boolean;
|
|
25
|
+
/** 是否弹窗选择保存路径(默认 false) */
|
|
26
|
+
showSaveDialog?: boolean;
|
|
27
|
+
/** 保存对话框的默认文件名(可选) */
|
|
28
|
+
defaultPath?: string;
|
|
29
|
+
/** 超时时间(毫秒,默认 10 分钟) */
|
|
30
|
+
timeout?: number;
|
|
31
|
+
/** 重试配置 */
|
|
32
|
+
retry?: {
|
|
33
|
+
maxRetries?: number;
|
|
34
|
+
delay?: number;
|
|
35
|
+
};
|
|
36
|
+
/** 下载进度回调函数(可选) */
|
|
37
|
+
onProgress?: DownloadProgressCallback;
|
|
38
|
+
/** 下载完成回调函数(可选) */
|
|
39
|
+
onComplete?: (filePath: string) => void;
|
|
40
|
+
/** 下载错误回调函数(可选) */
|
|
41
|
+
onError?: (error: Error) => void;
|
|
42
|
+
/** HTTP 请求选项 */
|
|
43
|
+
httpRequestOptions?: {
|
|
44
|
+
headers?: Record<string, string>;
|
|
45
|
+
followRedirect?: boolean;
|
|
46
|
+
maxRedirects?: number;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 下载结果
|
|
51
|
+
*/
|
|
52
|
+
export interface DownloadResult {
|
|
53
|
+
/** 是否成功 */
|
|
54
|
+
success: boolean;
|
|
55
|
+
/** 下载的文件路径 */
|
|
56
|
+
filePath?: string;
|
|
57
|
+
/** 错误信息 */
|
|
58
|
+
error?: string;
|
|
59
|
+
/** 文件大小(字节) */
|
|
60
|
+
size?: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 下载器类:提供文件下载功能
|
|
64
|
+
*/
|
|
65
|
+
declare class Downloader {
|
|
66
|
+
/** 正在下载的 URL 集合(避免重复下载) */
|
|
67
|
+
private _downloadingUrls;
|
|
68
|
+
constructor();
|
|
69
|
+
/**
|
|
70
|
+
* 下载文件
|
|
71
|
+
* @param url 要下载的 URL
|
|
72
|
+
* @param options 下载选项
|
|
73
|
+
* @param onProgress 进度回调函数(可选,优先级高于 options.onProgress)
|
|
74
|
+
* @returns Promise<DownloadResult> 下载结果
|
|
75
|
+
*/
|
|
76
|
+
download(url: string, options?: DownloadOptions, onProgress?: DownloadProgressCallback): Promise<DownloadResult>;
|
|
77
|
+
/**
|
|
78
|
+
* 取消下载
|
|
79
|
+
* @param url 要取消的 URL
|
|
80
|
+
* @returns 是否成功取消
|
|
81
|
+
*/
|
|
82
|
+
cancel(url: string): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* 取消所有正在进行的下载
|
|
85
|
+
*/
|
|
86
|
+
cancelAll(): void;
|
|
87
|
+
/**
|
|
88
|
+
* 检查指定 URL 是否正在下载
|
|
89
|
+
* @param url 要检查的 URL
|
|
90
|
+
* @returns 是否正在下载
|
|
91
|
+
*/
|
|
92
|
+
isDownloading(url: string): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* 获取正在下载的 URL 列表
|
|
95
|
+
* @returns 正在下载的 URL 数组
|
|
96
|
+
*/
|
|
97
|
+
getDownloadingUrls(): string[];
|
|
98
|
+
/**
|
|
99
|
+
* 显示保存对话框
|
|
100
|
+
* @param url 下载 URL
|
|
101
|
+
* @param options 下载选项
|
|
102
|
+
* @returns Promise<string | null> 用户选择的保存路径,如果取消则返回 null
|
|
103
|
+
*/
|
|
104
|
+
private _showSaveDialog;
|
|
105
|
+
/**
|
|
106
|
+
* 生成唯一的文件名(如果文件已存在,添加数字后缀)
|
|
107
|
+
* @param dir 目录路径
|
|
108
|
+
* @param fileName 原始文件名
|
|
109
|
+
* @returns 唯一的文件名
|
|
110
|
+
*/
|
|
111
|
+
private _generateUniqueFileName;
|
|
112
|
+
/**
|
|
113
|
+
* 解析输出路径
|
|
114
|
+
* @param url 下载 URL
|
|
115
|
+
* @param options 下载选项
|
|
116
|
+
* @returns 输出文件路径
|
|
117
|
+
*/
|
|
118
|
+
private _resolveOutputPath;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 获取默认下载器实例
|
|
122
|
+
* @returns Downloader 实例
|
|
123
|
+
*/
|
|
124
|
+
export declare function getDownloader(): Downloader;
|
|
125
|
+
/**
|
|
126
|
+
* 下载文件的便捷函数
|
|
127
|
+
* @param url 要下载的 URL
|
|
128
|
+
* @param options 下载选项
|
|
129
|
+
* @param onProgress 进度回调函数(可选)
|
|
130
|
+
* @returns Promise<DownloadResult> 下载结果
|
|
131
|
+
*/
|
|
132
|
+
export declare function downloadFile(url: string, options?: DownloadOptions, onProgress?: DownloadProgressCallback): Promise<DownloadResult>;
|
|
133
|
+
export {};
|
|
134
|
+
//# sourceMappingURL=downloader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"downloader.d.ts","sourceRoot":"","sources":["../src/main/downloader.ts"],"names":[],"mappings":"AAuBA;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,QAAQ,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf,KAAK,IAAI,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sBAAsB;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2BAA2B;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW;IACX,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,mBAAmB;IACnB,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,mBAAmB;IACnB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,mBAAmB;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,gBAAgB;IAChB,kBAAkB,CAAC,EAAE;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,cAAM,UAAU;IACd,2BAA2B;IAC3B,OAAO,CAAC,gBAAgB,CAAuC;;IAiC/D;;;;;;OAMG;IACU,QAAQ,CACnB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,eAAoB,EAC7B,UAAU,CAAC,EAAE,wBAAwB,GACpC,OAAO,CAAC,cAAc,CAAC;IA+J1B;;;;OAIG;IACI,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAUnC;;OAEG;IACI,SAAS,IAAI,IAAI;IAOxB;;;;OAIG;IACI,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI1C;;;OAGG;IACI,kBAAkB,IAAI,MAAM,EAAE;IAIrC;;;;;OAKG;YACW,eAAe;IAmE7B;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB;IAyB/B;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;CA8B3B;AAOD;;;GAGG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAK1C;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,eAAoB,EAC7B,UAAU,CAAC,EAAE,wBAAwB,GACpC,OAAO,CAAC,cAAc,CAAC,CAGzB"}
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const nodeDownloaderHelper = require('node-downloader-helper');
|
|
4
|
+
const electron = require('electron');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 最大重定向次数
|
|
8
|
+
*/
|
|
9
|
+
const MAX_REDIRECTS = 5;
|
|
10
|
+
/**
|
|
11
|
+
* 默认超时时间(毫秒):10 分钟
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_TIMEOUT = 600000;
|
|
14
|
+
/**
|
|
15
|
+
* 默认重试配置
|
|
16
|
+
*/
|
|
17
|
+
const DEFAULT_RETRY = {
|
|
18
|
+
maxRetries: 3,
|
|
19
|
+
delay: 1000
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* 下载器类:提供文件下载功能
|
|
23
|
+
*/
|
|
24
|
+
class Downloader {
|
|
25
|
+
constructor() {
|
|
26
|
+
/** 正在下载的 URL 集合(避免重复下载) */
|
|
27
|
+
this._downloadingUrls = new Map();
|
|
28
|
+
electron.ipcMain.handle(`core:download`, async (event, options) => {
|
|
29
|
+
try {
|
|
30
|
+
const { id, url } = options;
|
|
31
|
+
const sender = event.sender;
|
|
32
|
+
// 创建进度回调,通过 IPC 发送进度更新
|
|
33
|
+
const progressCallback = (progress) => {
|
|
34
|
+
try {
|
|
35
|
+
if (sender && typeof sender.send === 'function') {
|
|
36
|
+
sender.send(`core:download:progress`, {
|
|
37
|
+
id,
|
|
38
|
+
data: progress
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error('发送下载进度失败:', error);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const result = await this.download(url, options, progressCallback);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: error instanceof Error ? error.message : '未知错误'
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 下载文件
|
|
59
|
+
* @param url 要下载的 URL
|
|
60
|
+
* @param options 下载选项
|
|
61
|
+
* @param onProgress 进度回调函数(可选,优先级高于 options.onProgress)
|
|
62
|
+
* @returns Promise<DownloadResult> 下载结果
|
|
63
|
+
*/
|
|
64
|
+
async download(url, options = {}, onProgress) {
|
|
65
|
+
// 检查是否正在下载,避免重复下载
|
|
66
|
+
if (this._downloadingUrls.has(url)) {
|
|
67
|
+
return {
|
|
68
|
+
success: false,
|
|
69
|
+
error: `资源正在下载中: ${url}`
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// 合并进度回调:优先使用传入的 onProgress,其次使用 options.onProgress
|
|
73
|
+
const progressCallback = onProgress || options.onProgress;
|
|
74
|
+
// 如果需要显示保存对话框,先让用户选择保存路径
|
|
75
|
+
let outputPath;
|
|
76
|
+
if (options.showSaveDialog) {
|
|
77
|
+
try {
|
|
78
|
+
const savePath = await this._showSaveDialog(url, options);
|
|
79
|
+
if (!savePath) {
|
|
80
|
+
// 用户取消了保存对话框
|
|
81
|
+
return {
|
|
82
|
+
success: false,
|
|
83
|
+
error: '用户取消了保存操作'
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
outputPath = savePath;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: error instanceof Error ? error.message : '显示保存对话框失败'
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// 确定输出路径
|
|
97
|
+
outputPath = this._resolveOutputPath(url, options);
|
|
98
|
+
}
|
|
99
|
+
const downloadDir = path.dirname(outputPath);
|
|
100
|
+
const downloadFileName = path.basename(outputPath);
|
|
101
|
+
// 确保目录存在
|
|
102
|
+
if (!fs.existsSync(downloadDir)) {
|
|
103
|
+
fs.mkdirSync(downloadDir, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
// 如果文件已存在且不允许覆盖,直接返回
|
|
106
|
+
if (fs.existsSync(outputPath) && !(options.override ?? true)) {
|
|
107
|
+
const stats = fs.statSync(outputPath);
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
filePath: outputPath,
|
|
111
|
+
size: stats.size
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// 使用临时文件下载,避免下载中断时文件不完整
|
|
115
|
+
const tempFilePath = `${outputPath}.cache`;
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
// 创建下载器实例
|
|
118
|
+
const dl = new nodeDownloaderHelper.DownloaderHelper(url, downloadDir, {
|
|
119
|
+
fileName: downloadFileName + '.cache',
|
|
120
|
+
retry: {
|
|
121
|
+
maxRetries: options.retry?.maxRetries ?? DEFAULT_RETRY.maxRetries,
|
|
122
|
+
delay: options.retry?.delay ?? DEFAULT_RETRY.delay
|
|
123
|
+
},
|
|
124
|
+
timeout: options.timeout ?? DEFAULT_TIMEOUT,
|
|
125
|
+
override: true, // 临时文件总是覆盖
|
|
126
|
+
httpRequestOptions: {
|
|
127
|
+
followRedirect: options.httpRequestOptions?.followRedirect ?? true,
|
|
128
|
+
maxRedirects: options.httpRequestOptions?.maxRedirects ?? MAX_REDIRECTS,
|
|
129
|
+
headers: options.httpRequestOptions?.headers
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
// 记录正在下载的 URL
|
|
133
|
+
this._downloadingUrls.set(url, dl);
|
|
134
|
+
// 监听下载进度
|
|
135
|
+
if (progressCallback) {
|
|
136
|
+
dl.on('progress', (stats) => {
|
|
137
|
+
try {
|
|
138
|
+
progressCallback({
|
|
139
|
+
url,
|
|
140
|
+
downloaded: stats.downloaded || 0,
|
|
141
|
+
total: stats.total || 0,
|
|
142
|
+
percentage: stats.progress !== undefined ? Math.round(stats.progress) : -1,
|
|
143
|
+
speed: stats.speed || 0
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
// 忽略回调中的错误,避免影响下载
|
|
148
|
+
console.error('进度回调执行失败:', error);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// 监听下载完成
|
|
153
|
+
dl.on('end', () => {
|
|
154
|
+
this._downloadingUrls.delete(url);
|
|
155
|
+
// 将临时文件重命名为最终文件
|
|
156
|
+
fs.rename(tempFilePath, outputPath, (renameErr) => {
|
|
157
|
+
if (renameErr) {
|
|
158
|
+
const error = new Error(`文件重命名失败 from ${tempFilePath} to ${outputPath}: ${renameErr.message}`);
|
|
159
|
+
if (options.onError) {
|
|
160
|
+
options.onError(error);
|
|
161
|
+
}
|
|
162
|
+
reject(error);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
// 获取文件大小
|
|
166
|
+
const stats = fs.statSync(outputPath);
|
|
167
|
+
const result = {
|
|
168
|
+
success: true,
|
|
169
|
+
filePath: outputPath,
|
|
170
|
+
size: stats.size
|
|
171
|
+
};
|
|
172
|
+
if (options.onComplete) {
|
|
173
|
+
options.onComplete(outputPath);
|
|
174
|
+
}
|
|
175
|
+
resolve(result);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
// 统一的错误处理函数
|
|
180
|
+
const handleError = (error) => {
|
|
181
|
+
this._downloadingUrls.delete(url);
|
|
182
|
+
// 清理临时文件
|
|
183
|
+
fs.promises.unlink(tempFilePath).catch(() => {
|
|
184
|
+
// 忽略删除失败
|
|
185
|
+
});
|
|
186
|
+
if (options.onError) {
|
|
187
|
+
options.onError(error);
|
|
188
|
+
}
|
|
189
|
+
reject(error);
|
|
190
|
+
};
|
|
191
|
+
// 监听下载错误
|
|
192
|
+
dl.on('error', (err) => {
|
|
193
|
+
const error = err instanceof Error ? err : new Error(`下载失败: ${url}`);
|
|
194
|
+
handleError(error);
|
|
195
|
+
});
|
|
196
|
+
// 监听下载停止(取消)
|
|
197
|
+
dl.on('stop', () => {
|
|
198
|
+
const error = new Error(`下载已停止: ${url}`);
|
|
199
|
+
handleError(error);
|
|
200
|
+
});
|
|
201
|
+
// 开始下载
|
|
202
|
+
dl.start().catch((err) => {
|
|
203
|
+
const error = err instanceof Error ? err : new Error(`启动下载失败: ${url}`);
|
|
204
|
+
handleError(error);
|
|
205
|
+
});
|
|
206
|
+
}).catch((error) => {
|
|
207
|
+
return {
|
|
208
|
+
success: false,
|
|
209
|
+
error: error instanceof Error ? error.message : '未知错误'
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 取消下载
|
|
215
|
+
* @param url 要取消的 URL
|
|
216
|
+
* @returns 是否成功取消
|
|
217
|
+
*/
|
|
218
|
+
cancel(url) {
|
|
219
|
+
const dl = this._downloadingUrls.get(url);
|
|
220
|
+
if (dl) {
|
|
221
|
+
dl.stop();
|
|
222
|
+
this._downloadingUrls.delete(url);
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* 取消所有正在进行的下载
|
|
229
|
+
*/
|
|
230
|
+
cancelAll() {
|
|
231
|
+
for (const [url, dl] of this._downloadingUrls.entries()) {
|
|
232
|
+
dl.stop();
|
|
233
|
+
}
|
|
234
|
+
this._downloadingUrls.clear();
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* 检查指定 URL 是否正在下载
|
|
238
|
+
* @param url 要检查的 URL
|
|
239
|
+
* @returns 是否正在下载
|
|
240
|
+
*/
|
|
241
|
+
isDownloading(url) {
|
|
242
|
+
return this._downloadingUrls.has(url);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* 获取正在下载的 URL 列表
|
|
246
|
+
* @returns 正在下载的 URL 数组
|
|
247
|
+
*/
|
|
248
|
+
getDownloadingUrls() {
|
|
249
|
+
return Array.from(this._downloadingUrls.keys());
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* 显示保存对话框
|
|
253
|
+
* @param url 下载 URL
|
|
254
|
+
* @param options 下载选项
|
|
255
|
+
* @returns Promise<string | null> 用户选择的保存路径,如果取消则返回 null
|
|
256
|
+
*/
|
|
257
|
+
async _showSaveDialog(url, options) {
|
|
258
|
+
// 获取默认文件名
|
|
259
|
+
let defaultFileName;
|
|
260
|
+
if (options.defaultPath) {
|
|
261
|
+
defaultFileName = path.basename(options.defaultPath);
|
|
262
|
+
}
|
|
263
|
+
else if (options.fileName) {
|
|
264
|
+
defaultFileName = options.fileName;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
// 尝试从 URL 中提取文件名
|
|
268
|
+
try {
|
|
269
|
+
const urlObj = new URL(url);
|
|
270
|
+
defaultFileName = path.basename(urlObj.pathname) || 'download';
|
|
271
|
+
if (!path.extname(defaultFileName)) {
|
|
272
|
+
defaultFileName += '.bin';
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
defaultFileName = 'download.bin';
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// 获取默认目录
|
|
280
|
+
let defaultDir;
|
|
281
|
+
if (options.defaultPath) {
|
|
282
|
+
defaultDir = path.dirname(options.defaultPath);
|
|
283
|
+
}
|
|
284
|
+
else if (options.outputDir) {
|
|
285
|
+
defaultDir = path.resolve(options.outputDir);
|
|
286
|
+
}
|
|
287
|
+
// 如果指定了目录,检查文件是否存在并生成唯一文件名
|
|
288
|
+
if (defaultDir) {
|
|
289
|
+
defaultFileName = this._generateUniqueFileName(defaultDir, defaultFileName);
|
|
290
|
+
}
|
|
291
|
+
// 构建默认路径
|
|
292
|
+
const defaultPath = defaultDir ? path.join(defaultDir, defaultFileName) : defaultFileName;
|
|
293
|
+
// 获取当前活动的 BrowserWindow
|
|
294
|
+
const focusedWindow = electron.BrowserWindow.getFocusedWindow();
|
|
295
|
+
const allWindows = electron.BrowserWindow.getAllWindows();
|
|
296
|
+
const parentWindow = focusedWindow || (allWindows.length > 0 ? allWindows[0] : undefined);
|
|
297
|
+
// 显示保存对话框
|
|
298
|
+
const dialogOptions = {
|
|
299
|
+
defaultPath,
|
|
300
|
+
title: '保存文件',
|
|
301
|
+
buttonLabel: '保存',
|
|
302
|
+
filters: [
|
|
303
|
+
// 尝试从文件名推断文件类型
|
|
304
|
+
...(path.extname(defaultFileName) ? [{
|
|
305
|
+
name: '所有文件',
|
|
306
|
+
extensions: ['*']
|
|
307
|
+
}] : [])
|
|
308
|
+
]
|
|
309
|
+
};
|
|
310
|
+
// 根据是否有父窗口调用不同的方法
|
|
311
|
+
const result = parentWindow
|
|
312
|
+
? await electron.dialog.showSaveDialog(parentWindow, dialogOptions)
|
|
313
|
+
: await electron.dialog.showSaveDialog(dialogOptions);
|
|
314
|
+
if (result.canceled || !result.filePath) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
return result.filePath;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* 生成唯一的文件名(如果文件已存在,添加数字后缀)
|
|
321
|
+
* @param dir 目录路径
|
|
322
|
+
* @param fileName 原始文件名
|
|
323
|
+
* @returns 唯一的文件名
|
|
324
|
+
*/
|
|
325
|
+
_generateUniqueFileName(dir, fileName) {
|
|
326
|
+
// 分离文件名和扩展名
|
|
327
|
+
const ext = path.extname(fileName);
|
|
328
|
+
const nameWithoutExt = path.basename(fileName, ext);
|
|
329
|
+
// 检查原始文件名是否存在
|
|
330
|
+
const originalPath = path.join(dir, fileName);
|
|
331
|
+
if (!fs.existsSync(originalPath)) {
|
|
332
|
+
return fileName;
|
|
333
|
+
}
|
|
334
|
+
// 如果存在,尝试添加数字后缀
|
|
335
|
+
let counter = 1;
|
|
336
|
+
let newFileName;
|
|
337
|
+
let newPath;
|
|
338
|
+
do {
|
|
339
|
+
newFileName = `${nameWithoutExt}(${counter})${ext}`;
|
|
340
|
+
newPath = path.join(dir, newFileName);
|
|
341
|
+
counter++;
|
|
342
|
+
} while (fs.existsSync(newPath) && counter < 10000); // 防止无限循环
|
|
343
|
+
return newFileName;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* 解析输出路径
|
|
347
|
+
* @param url 下载 URL
|
|
348
|
+
* @param options 下载选项
|
|
349
|
+
* @returns 输出文件路径
|
|
350
|
+
*/
|
|
351
|
+
_resolveOutputPath(url, options) {
|
|
352
|
+
// 如果提供了完整路径,直接使用
|
|
353
|
+
if (options.outputPath) {
|
|
354
|
+
return path.resolve(options.outputPath);
|
|
355
|
+
}
|
|
356
|
+
// 确定输出目录
|
|
357
|
+
const outputDir = options.outputDir ? path.resolve(options.outputDir) : process.cwd();
|
|
358
|
+
// 确定文件名
|
|
359
|
+
let fileName;
|
|
360
|
+
if (options.fileName) {
|
|
361
|
+
fileName = options.fileName;
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
// 尝试从 URL 中提取文件名
|
|
365
|
+
try {
|
|
366
|
+
const urlObj = new URL(url);
|
|
367
|
+
fileName = path.basename(urlObj.pathname) || 'download';
|
|
368
|
+
// 如果没有扩展名,尝试从 Content-Type 或其他方式获取
|
|
369
|
+
if (!path.extname(fileName)) {
|
|
370
|
+
fileName += '.bin';
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
// 如果 URL 解析失败,使用默认文件名
|
|
375
|
+
fileName = 'download.bin';
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return path.join(outputDir, fileName);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* 默认下载器实例(单例)
|
|
383
|
+
*/
|
|
384
|
+
let defaultDownloader = null;
|
|
385
|
+
/**
|
|
386
|
+
* 获取默认下载器实例
|
|
387
|
+
* @returns Downloader 实例
|
|
388
|
+
*/
|
|
389
|
+
function getDownloader() {
|
|
390
|
+
if (!defaultDownloader) {
|
|
391
|
+
defaultDownloader = new Downloader();
|
|
392
|
+
}
|
|
393
|
+
return defaultDownloader;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
exports.getDownloader = getDownloader;
|
|
397
|
+
//# sourceMappingURL=downloader.js.map
|