1- import { app , BrowserWindow , dialog , ipcMain , shell } from "electron" ;
1+ import { type DownloadItem , app , BrowserWindow , dialog , ipcMain , shell } from "electron" ;
22import { basename , dirname , extname , isAbsolute , join , relative , resolve } from "path" ;
33import { access , mkdir , readdir , readFile , stat , unlink , writeFile } from "fs/promises" ;
44import { parseFile } from "music-metadata" ;
@@ -12,6 +12,9 @@ import { useStore } from "../store";
1212import FastGlob from "fast-glob" ;
1313import pLimit from "p-limit" ;
1414
15+ // 下载项
16+ const downloadItems = new Map < number , DownloadItem > ( ) ;
17+
1518/**
1619 * 文件相关 IPC
1720 */
@@ -119,10 +122,10 @@ const initFileIpc = (): void => {
119122 "aac" ,
120123 "webm" ,
121124 "m4a" ,
122- "mp4" ,
123125 "ogg" ,
124126 "aiff" ,
125127 "aif" ,
128+ "aifc" ,
126129 ] ;
127130 // 查找指定目录下的所有音乐文件
128131 const musicFiles = await FastGlob ( `**/*.{${ musicExtensions . join ( "," ) } }` , globOpt ( filePath ) ) ;
@@ -467,7 +470,7 @@ const initFileIpc = (): void => {
467470 fileType : "mp3" ,
468471 path : app . getPath ( "downloads" ) ,
469472 } ,
470- ) : Promise < { status : "success" | "skipped" | "error" ; message ?: string } > => {
473+ ) : Promise < { status : "success" | "skipped" | "error" | "cancelled" ; message ?: string } > => {
471474 try {
472475 // 获取窗口
473476 const win = BrowserWindow . fromWebContents ( event . sender ) ;
@@ -505,16 +508,57 @@ const initFileIpc = (): void => {
505508 }
506509 }
507510
511+ // 尝试删除可能存在的临时文件
512+ const tempPath = join ( downloadPath , `${ fileName } .${ fileType } .tmp` ) ;
513+ try {
514+ await unlink ( tempPath ) ;
515+ } catch {
516+ // 忽略错误
517+ }
518+
508519 // 下载文件
509- const songDownload = await download ( win , url , {
510- directory : downloadPath ,
511- filename : `${ fileName } .${ fileType } ` ,
512- showProgressBar : false ,
513- onProgress : ( progress ) => {
514- win . webContents . send ( "download-progress" , { ...progress , id : songData ?. id } ) ;
515- } ,
516- } ) ;
520+ let songDownload : DownloadItem ;
521+ try {
522+ songDownload = await download ( win , url , {
523+ directory : downloadPath ,
524+ filename : `${ fileName } .${ fileType } ` ,
525+ showProgressBar : false ,
526+ onProgress : ( progress ) => {
527+ win . webContents . send ( "download-progress" , { ...progress , id : songData ?. id } ) ;
528+ } ,
529+ onStarted : ( item ) => {
530+ if ( songData ?. id ) {
531+ downloadItems . set ( songData . id , item ) ;
532+ }
533+ } ,
534+ } ) ;
535+ } catch ( error : unknown ) {
536+ if ( error instanceof Error && error . message === "The download was cancelled" ) {
537+ return { status : "cancelled" , message : "下载已取消" } ;
538+ }
539+ throw error ;
540+ } finally {
541+ if ( songData ?. id ) {
542+ downloadItems . delete ( songData . id ) ;
543+ }
544+ }
545+
517546 if ( ! downloadMeta || ! songData ?. cover ) return { status : "success" } ;
547+
548+ // 验证文件是否存在
549+ const savedPath = songDownload . getSavePath ( ) ;
550+ try {
551+ await access ( savedPath ) ;
552+ } catch ( e ) {
553+ // 等待一小段时间再次检查(解决某些情况下文件系统延迟)
554+ await new Promise ( ( resolve ) => setTimeout ( resolve , 500 ) ) ;
555+ try {
556+ await access ( savedPath ) ;
557+ } catch {
558+ throw new Error ( `File not found at ${ savedPath } ` ) ;
559+ }
560+ }
561+
518562 // 下载封面
519563 const coverUrl = songData ?. coverSize ?. l || songData . cover ;
520564 const coverDownload = await download ( win , coverUrl , {
@@ -523,7 +567,7 @@ const initFileIpc = (): void => {
523567 showProgressBar : false ,
524568 } ) ;
525569 // 读取歌曲文件
526- let songFile = File . createFromPath ( songDownload . getSavePath ( ) ) ;
570+ let songFile = File . createFromPath ( savedPath ) ;
527571 // 清除原有标签,防止脏数据(如模拟播放下载时的乱码歌词)
528572 songFile . removeTags ( TagTypes . AllTags ) ;
529573 songFile . save ( ) ;
@@ -565,6 +609,17 @@ const initFileIpc = (): void => {
565609 } ,
566610 ) ;
567611
612+ // 取消下载
613+ ipcMain . handle ( "cancel-download" , async ( _ , songId : number ) => {
614+ const item = downloadItems . get ( songId ) ;
615+ if ( item ) {
616+ item . cancel ( ) ;
617+ downloadItems . delete ( songId ) ;
618+ return true ;
619+ }
620+ return false ;
621+ } ) ;
622+
568623 // 检查是否是相同的路径(规范化后比较)
569624 ipcMain . handle ( "check-if-same-path" , ( _ , localFilesPath : string [ ] , selectedDir : string ) => {
570625 const resolvedSelectedDir = resolve ( selectedDir ) ;
0 commit comments