@@ -16,19 +16,16 @@ import { loadNativeModule } from "../utils/native-loader";
1616type toolModule = typeof import ( "@native/tools" ) ;
1717const tools : toolModule = loadNativeModule ( "tools.node" , "tools" ) ;
1818
19- interface DownloadProgress {
20- percent : number ;
21- transferredBytes : number ;
22- totalBytes : number ;
23- }
24-
2519/**
2620 * 文件相关 IPC
2721 */
2822const initFileIpc = ( ) : void => {
2923 /** 本地音乐服务 */
3024 const localMusicService = new LocalMusicService ( ) ;
3125
26+ // Store active download tasks: ID -> DownloadTask instance
27+ const activeDownloads = new Map < number , any > ( ) ;
28+
3229 /**
3330 * 获取全局搜索配置
3431 * @param cwd 当前工作目录
@@ -510,6 +507,7 @@ const initFileIpc = (): void => {
510507 skipIfExist ?: boolean ;
511508 threadCount ?: number ;
512509 referer ?: string ;
510+ enableDownloadHttp2 ?: boolean ;
513511 } = {
514512 fileName : "未知文件名" ,
515513 fileType : "mp3" ,
@@ -533,6 +531,7 @@ const initFileIpc = (): void => {
533531 songData,
534532 skipIfExist,
535533 referer,
534+ enableDownloadHttp2,
536535 } = options ;
537536 // 规范化路径
538537 const downloadPath = resolve ( path ) ;
@@ -593,33 +592,46 @@ const initFileIpc = (): void => {
593592 }
594593
595594 const onProgress = ( ...args : any [ ] ) => {
596- // console.log("Received progress args:", args);
597- let progressJson : string | undefined ;
595+ let progressData : any ;
598596
599597 // Handle (err, value) or (value) signature
600- if ( args . length > 1 && args [ 0 ] === null && typeof args [ 1 ] === "string" ) {
601- progressJson = args [ 1 ] ;
602- } else if ( args . length > 0 && typeof args [ 0 ] === "string" ) {
603- progressJson = args [ 0 ] ;
598+ if ( args . length > 1 && args [ 0 ] === null ) {
599+ progressData = args [ 1 ] ;
600+ } else if ( args . length > 0 ) {
601+ progressData = args [ 0 ] ;
604602 }
605603
606604 try {
607- if ( ! progressJson ) return ;
608- const progress = JSON . parse ( progressJson ) as DownloadProgress ;
609- if ( ! progress ) return ;
605+ if ( ! progressData ) return ;
606+
607+ // Handle both object (new) and JSON string (legacy/fallback)
608+ if ( typeof progressData === "string" ) {
609+ try {
610+ progressData = JSON . parse ( progressData ) ;
611+ } catch ( e ) {
612+ console . error ( "Failed to parse progress json" , e ) ;
613+ return ;
614+ }
615+ }
616+
617+ if ( ! progressData || typeof progressData !== 'object' ) return ;
618+
619+ // Map snake_case (Rust) to camelCase (JS)
620+ // Rust struct: { percent, transferred_bytes, total_bytes }
621+ const percent = progressData . percent ;
622+ const transferredBytes = progressData . transferredBytes ?? progressData . transferred_bytes ?? 0 ;
623+ const totalBytes = progressData . totalBytes ?? progressData . total_bytes ?? 0 ;
610624
611625 win . webContents . send ( "download-progress" , {
612626 id : songData ?. id ,
613- percent : progress . percent ,
614- transferredBytes : progress . transferredBytes ,
615- totalBytes : progress . totalBytes ,
627+ percent : percent ,
628+ transferredBytes : transferredBytes ,
629+ totalBytes : totalBytes ,
616630 } ) ;
617631 } catch ( e ) {
618632 console . error (
619- "Failed to parse progress json " ,
633+ "Error processing progress callback " ,
620634 e ,
621- "Input:" ,
622- progressJson ,
623635 "Args:" ,
624636 args ,
625637 ) ;
@@ -635,15 +647,35 @@ const initFileIpc = (): void => {
635647 const threadCount =
636648 ( options . threadCount as number ) || ( store . get ( "downloadThreadCount" ) as number ) || 8 ;
637649
638- await tools . downloadFile (
639- songData ?. id || 0 ,
640- url ,
641- finalFilePath ,
642- metadata ,
643- threadCount ,
644- referer ,
645- onProgress ,
646- ) ;
650+ const enableHttp2 =
651+ enableDownloadHttp2 !== undefined
652+ ? enableDownloadHttp2
653+ : ( store . get ( "enableDownloadHttp2" , true ) as boolean ) ;
654+
655+ // Upgrade HTTP to HTTPS if HTTP2 is enabled (HTTP2 usually requires HTTPS)
656+ let finalUrl = url ;
657+ if ( enableHttp2 && finalUrl . startsWith ( "http://" ) ) {
658+ finalUrl = finalUrl . replace ( / ^ h t t p : \/ \/ / , "https://" ) ;
659+ ipcLog . info ( `🔒 Upgraded download URL to HTTPS for HTTP/2 support: ${ finalUrl } ` ) ;
660+ }
661+
662+ const task = new tools . DownloadTask ( ) ;
663+ const downloadId = songData ?. id || 0 ;
664+ activeDownloads . set ( downloadId , task ) ;
665+
666+ try {
667+ await task . download (
668+ finalUrl ,
669+ finalFilePath ,
670+ metadata ,
671+ threadCount ,
672+ referer ,
673+ onProgress ,
674+ enableHttp2 ,
675+ ) ;
676+ } finally {
677+ activeDownloads . delete ( downloadId ) ;
678+ }
647679
648680 // 创建同名歌词文件
649681 if ( lyric && saveMetaFile && downloadLyric ) {
@@ -667,8 +699,9 @@ const initFileIpc = (): void => {
667699
668700 // 取消下载
669701 ipcMain . handle ( "cancel-download" , async ( _ , songId : number ) => {
670- if ( tools ) {
671- tools . cancelDownload ( songId ) ;
702+ const task = activeDownloads . get ( songId ) ;
703+ if ( task ) {
704+ task . cancel ( ) ;
672705 return true ;
673706 }
674707 return false ;
0 commit comments