'use client'; import { Check, Trash2, X } from 'lucide-react'; import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; import { downloadDB, CompletedTask } from '@/lib/download-db'; import { ConfirmDialog } from './ConfirmDialog'; interface DownloadManagementPanelProps { isOpen: boolean; onClose: () => void; } export function DownloadManagementPanel({ isOpen, onClose }: DownloadManagementPanelProps) { const [completedTasks, setCompletedTasks] = useState([]); const [selectedIds, setSelectedIds] = useState>(new Set()); const [isDeleting, setIsDeleting] = useState(false); const [mounted, setMounted] = useState(false); const [showConfirmDialog, setShowConfirmDialog] = useState(false); useEffect(() => { setMounted(true); }, []); useEffect(() => { if (isOpen) { loadCompletedTasks(); } }, [isOpen]); const loadCompletedTasks = async () => { try { const tasks = await downloadDB.getCompletedTasks(); setCompletedTasks(tasks); } catch (error) { console.error('加载已完成任务失败:', error); } }; const handleSelectAll = () => { if (selectedIds.size === completedTasks.length) { setSelectedIds(new Set()); } else { setSelectedIds(new Set(completedTasks.map(t => t.id))); } }; const handleToggleSelect = (id: string) => { const newSet = new Set(selectedIds); if (newSet.has(id)) { newSet.delete(id); } else { newSet.add(id); } setSelectedIds(newSet); }; const handleDelete = async () => { if (selectedIds.size === 0) return; setShowConfirmDialog(true); }; const handleConfirmDelete = async () => { setShowConfirmDialog(false); setIsDeleting(true); try { // 获取要删除的任务 const tasksToDelete = completedTasks.filter(t => selectedIds.has(t.id)); // 删除文件系统中的文件 for (const task of tasksToDelete) { if (task.downloadMode === 'filesystem') { try { // 从 IndexedDB 读取目录句柄 const dbName = 'MoonTVPlus'; const storeName = 'dirHandles'; const dirHandle = await new Promise((resolve) => { const request = indexedDB.open(dbName, 2); // 使用版本 2 request.onsuccess = (event) => { const db = (event.target as IDBOpenDBRequest).result; if (!db.objectStoreNames.contains(storeName)) { db.close(); resolve(undefined); return; } const transaction = db.transaction([storeName], 'readonly'); const store = transaction.objectStore(storeName); const getRequest = store.get('downloadDir'); getRequest.onsuccess = () => { const handle = getRequest.result as FileSystemDirectoryHandle | undefined; db.close(); resolve(handle); }; getRequest.onerror = () => { db.close(); resolve(undefined); }; }; request.onerror = () => { resolve(undefined); }; }); if (dirHandle) { // 请求写权限 const permission = await (dirHandle as any).requestPermission({ mode: 'readwrite' }); if (permission !== 'granted') { console.error('未获得写权限,无法删除文件'); continue; } // 删除目录 try { const sourceDirHandle = await dirHandle.getDirectoryHandle(task.source, { create: false }); const videoIdDirHandle = await sourceDirHandle.getDirectoryHandle(task.videoId, { create: false }); await videoIdDirHandle.removeEntry(`ep${task.episodeIndex + 1}`, { recursive: true }); console.log('已删除文件:', task.source, task.videoId, `ep${task.episodeIndex + 1}`); } catch (deleteError) { console.error('删除目录失败:', deleteError); // 如果目录不存在,也算成功 if ((deleteError as Error).name !== 'NotFoundError') { throw deleteError; } } } } catch (error) { console.error('删除文件失败:', task.title, error); } } } // 从数据库删除记录 await downloadDB.deleteCompletedTasks(Array.from(selectedIds)); await loadCompletedTasks(); setSelectedIds(new Set()); } catch (error) { console.error('删除任务失败:', error); alert('删除失败,请重试'); } finally { setIsDeleting(false); } }; const formatDate = (timestamp: number) => { const date = new Date(timestamp); return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', }); }; const formatFileSize = (bytes?: number) => { if (!bytes) return '未知'; if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'; if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(2) + ' MB'; return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB'; }; if (!mounted || !isOpen) return null; return ( <> {createPortal(
{/* Header */}

下载文件管理

{/* Toolbar */}
已选择 {selectedIds.size} / {completedTasks.length}
{/* Content */}
{completedTasks.length === 0 ? (
暂无下载记录
) : (
{completedTasks.map((task) => (
handleToggleSelect(task.id)} >
{selectedIds.has(task.id) && ( )}

{task.title}

{task.videoTitle && (

{task.videoTitle}

)} {task.episodeTitle && (

{task.episodeTitle}

)}
{formatDate(task.completedAt)}
{task.fileSize && (
{formatFileSize(task.fileSize)}
)}
来源: {task.source} 第 {task.episodeIndex + 1} 集 {task.downloadMode === 'filesystem' ? 'File System API' : '浏览器下载'}
))}
)}
, document.body )} setShowConfirmDialog(false)} /> ); }