diff --git a/src/app/api/offline-download/route.ts b/src/app/api/offline-download/route.ts index 6609951..3e670a5 100644 --- a/src/app/api/offline-download/route.ts +++ b/src/app/api/offline-download/route.ts @@ -304,6 +304,21 @@ export async function DELETE(request: NextRequest) { const downloader = getDownloader(); + // 如果任务正在下载,先标记为取消状态,等待下载停止 + const downloadPromise = activeDownloads.get(taskId); + if (downloadPromise) { + // 将任务状态设置为 error,这样下载器会停止下载 + task.status = 'error'; + task.errorMessage = '任务已被删除'; + tasks.set(taskId, task); + + // 从活跃下载列表中移除 + activeDownloads.delete(taskId); + + // 等待一小段时间,让下载操作有机会停止 + await new Promise(resolve => setTimeout(resolve, 500)); + } + // 删除文件 await downloader.deleteTask(task); @@ -311,12 +326,6 @@ export async function DELETE(request: NextRequest) { tasks.delete(taskId); saveTasks(); // 持久化任务 - // 如果正在下载,等待下载完成后再删除 - const downloadPromise = activeDownloads.get(taskId); - if (downloadPromise) { - activeDownloads.delete(taskId); - } - return NextResponse.json({ message: '任务已删除' }); } catch (error) { console.error('删除任务失败:', error); diff --git a/src/contexts/DownloadContext.tsx b/src/contexts/DownloadContext.tsx index 50c4c33..73e84eb 100644 --- a/src/contexts/DownloadContext.tsx +++ b/src/contexts/DownloadContext.tsx @@ -21,7 +21,7 @@ interface DownloadContextType { ) => Promise; startTask: (taskId: string) => void; pauseTask: (taskId: string) => void; - cancelTask: (taskId: string) => void; + cancelTask: (taskId: string) => Promise; retryFailedSegments: (taskId: string) => void; getProgress: (taskId: string) => number; downloadingCount: number; @@ -509,8 +509,8 @@ export function DownloadProvider({ children }: { children: React.ReactNode }) { startNextPendingTask(downloader); }, [downloader, startNextPendingTask]); - const cancelTask = useCallback((taskId: string) => { - downloader.cancelTask(taskId); + const cancelTask = useCallback(async (taskId: string) => { + await downloader.cancelTask(taskId); setTasks(downloader.getAllTasks()); // 保存任务状态到 IndexedDB(删除被取消的任务) saveTasks(downloader.getAllTasks()); diff --git a/src/lib/m3u8-downloader.ts b/src/lib/m3u8-downloader.ts index fc1de0a..f965f43 100644 --- a/src/lib/m3u8-downloader.ts +++ b/src/lib/m3u8-downloader.ts @@ -241,11 +241,17 @@ export class M3U8Downloader { /** * 取消任务 */ - cancelTask(taskId: string): void { + async cancelTask(taskId: string): Promise { const task = this.tasks.get(taskId); if (!task) return; this.abortRequests(task); + + // 如果是 filesystem 模式且任务未完成,删除已下载的文件 + if (task.downloadMode === 'filesystem' && task.status !== 'done' && task.filesystemDirHandle) { + await this.deleteFilesystemTask(task); + } + this.tasks.delete(taskId); if (this.currentTask?.id === taskId) { @@ -253,6 +259,30 @@ export class M3U8Downloader { } } + /** + * 删除 filesystem 模式下的任务文件 + */ + private async deleteFilesystemTask(task: M3U8DownloadTask): Promise { + if (!task.filesystemDirHandle || !task.source || !task.videoId || task.episodeIndex === undefined) { + return; + } + + try { + // 获取目标目录 + const sourceDirHandle = await task.filesystemDirHandle.getDirectoryHandle(task.source, { create: false }); + const videoIdDirHandle = await sourceDirHandle.getDirectoryHandle(task.videoId, { create: false }); + + // 删除 ep{n} 目录 + const epDirName = `ep${task.episodeIndex + 1}`; + await videoIdDirHandle.removeEntry(epDirName, { recursive: true }); + + console.log(`已删除未完成的下载文件: ${task.source}/${task.videoId}/${epDirName}`); + } catch (error) { + // 如果目录不存在或删除失败,忽略错误 + console.warn('删除文件失败(可能目录不存在):', error); + } + } + /** * 获取任务信息 */