import { IndeterminateProgress } from "@/components/custom/indeterminateProgress"; import { ProxyImage } from "@/components/custom/proxyImage"; import { AspectRatio } from "@/components/ui/aspect-ratio"; import { Button } from "@/components/ui/button"; import { Progress } from "@/components/ui/progress"; import { Separator } from "@/components/ui/separator"; import { toast } from "sonner"; import { useAppContext } from "@/providers/appContextProvider"; import { useCurrentVideoMetadataStore, useDownloadActionStatesStore, useDownloadStatesStore, useLibraryPageStatesStore } from "@/services/store"; import { formatBitrate, formatCodec, formatDurationString, formatFileSize, formatSecToTimeString, formatSpeed } from "@/utils"; import { AudioLines, Clock, File, FileAudio2, FileQuestion, FileVideo2, FolderInput, ListVideo, Loader2, Music, Pause, Play, Search, Square, Trash2, Video, X } from "lucide-react"; import { invoke } from "@tauri-apps/api/core"; import * as fs from "@tauri-apps/plugin-fs"; import { DownloadState } from "@/types/download"; import { useQueryClient } from "@tanstack/react-query"; import { useDeleteDownloadState } from "@/services/mutations"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; import Heading from "@/components/heading"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Badge } from "@/components/ui/badge"; import { useNavigate } from "react-router-dom"; import { useLogger } from "@/helpers/use-logger"; export default function LibraryPage() { const activeTab = useLibraryPageStatesStore(state => state.activeTab); const setActiveTab = useLibraryPageStatesStore(state => state.setActiveTab); const downloadStates = useDownloadStatesStore(state => state.downloadStates); const downloadActions = useDownloadActionStatesStore(state => state.downloadActions); const setIsResumingDownload = useDownloadActionStatesStore(state => state.setIsResumingDownload); const setIsPausingDownload = useDownloadActionStatesStore(state => state.setIsPausingDownload); const setIsCancelingDownload = useDownloadActionStatesStore(state => state.setIsCancelingDownload); const setIsDeleteFileChecked = useDownloadActionStatesStore(state => state.setIsDeleteFileChecked); const { pauseDownload, resumeDownload, cancelDownload } = useAppContext() const queryClient = useQueryClient(); const downloadStateDeleter = useDeleteDownloadState(); const navigate = useNavigate(); const LOG = useLogger(); const incompleteDownloads = downloadStates.filter(state => state.download_status !== 'completed'); const completedDownloads = downloadStates.filter(state => state.download_status === 'completed') .sort((a, b) => { // Latest updated first const dateA = a.updated_at ? new Date(a.updated_at).getTime() : 0; const dateB = b.updated_at ? new Date(b.updated_at).getTime() : 0; return dateB - dateA; }); const ongoingDownloads = downloadStates.filter(state => ['starting', 'downloading', 'queued'].includes(state.download_status) ); const openFile = async (filePath: string | null, app: string | null) => { if (filePath && await fs.exists(filePath)) { try { await invoke('open_file_with_app', { filePath: filePath, appName: app }).then(() => { toast.info(`${app === 'explorer' ? 'Revealing' : 'Opening'} file`, { description: `${app === 'explorer' ? 'Revealing' : 'Opening'} the file ${app === 'explorer' ? 'in' : 'with'} ${app ? app : 'default app'}.`, }) }); } catch (e) { console.error(e); toast.error(`Failed to ${app === 'explorer' ? 'reveal' : 'open'} file`, { description: `An error occurred while trying to ${app === 'explorer' ? 'reveal' : 'open'} the file.`, }) } } else { toast.info("File unavailable", { description: `The file you are trying to ${app === 'explorer' ? 'reveal' : 'open'} does not exist.`, }) } } const removeFromDownloads = async (downloadState: DownloadState, delete_file: boolean) => { if (delete_file && downloadState.filepath) { try { if (await fs.exists(downloadState.filepath)) { await fs.remove(downloadState.filepath); } else { console.error(`File not found: ${downloadState.filepath}`); } } catch (e) { console.error(e); } } downloadStateDeleter.mutate(downloadState.download_id, { onSuccess: (data) => { console.log("Download State deleted successfully:", data); queryClient.invalidateQueries({ queryKey: ['download-states'] }); toast.success("Removed from downloads", { description: "The download has been removed successfully.", }); }, onError: (error) => { console.error("Failed to delete download state:", error); toast.error("Failed to remove download", { description: "An error occurred while trying to remove the download.", }); } }) } const stopOngoingDownloads = async () => { if (ongoingDownloads.length > 0) { for (const state of ongoingDownloads) { setIsPausingDownload(state.download_id, true); try { await pauseDownload(state); } catch (e) { console.error(e); toast.error("Failed to stop download", { description: `An error occurred while trying to stop the download for ${state.title}.`, }); } finally { setIsPausingDownload(state.download_id, false); } } if (ongoingDownloads.length === 0) { toast.success("Stopped ongoing downloads", { description: "All ongoing downloads have been stopped successfully.", }); } } else { toast.info("No ongoing downloads", { description: "There are no ongoing downloads to stop.", }); } } const handleSearch = async (url: string, isPlaylist: boolean) => { try { LOG.info('NEODLP', `Received search request from library for URL: ${url}`); navigate('/'); const { setRequestedUrl, setAutoSubmitSearch } = useCurrentVideoMetadataStore.getState(); setRequestedUrl(url); setAutoSubmitSearch(true); toast.info(`Initiating ${isPlaylist ? 'Playlist' : 'Video'} Search`, { description: `Initiating search for the selected ${isPlaylist ? 'playlist' : 'video'}.`, }); } catch (e) { console.error(e); toast.error("Failed to initiate search", { description: "An error occurred while trying to initiate the search.", }); } } return (
Completed {completedDownloads.length > 0 && (`(${completedDownloads.length})`)} Incomplete {(incompleteDownloads.length > 0 && ongoingDownloads.length <= 0) && (`(${incompleteDownloads.length})`)} {ongoingDownloads.length > 0 && ({ongoingDownloads.length})} Stop all ongoing downloads? Are you sure you want to stop all ongoing downloads? This will pause all downloads including the download queue. Cancel stopOngoingDownloads()} >Stop
{completedDownloads.length > 0 ? ( completedDownloads.map((state) => { const itemActionStates = downloadActions[state.download_id] || { isResuming: false, isPausing: false, isCanceling: false, isDeleteFileChecked: false, }; return (
{state.filetype && (state.filetype === 'video' || state.filetype === 'video+audio') && (

{state.title}

{state.channel ? state.channel : 'unknown'} {state.host ? `• ${state.host}` : 'unknown'}

{state.duration_string ? formatDurationString(state.duration_string) : 'unknown'} {state.filetype && (state.filetype === 'video' || state.filetype === 'video+audio') && ( )} {state.filetype && state.filetype === 'audio' && ( )} {(!state.filetype) || (state.filetype && state.filetype !== 'video' && state.filetype !== 'audio' && state.filetype !== 'video+audio') && ( )} {state.filesize ? formatFileSize(state.filesize) : 'unknown'} {state.vbr && state.abr ? ( formatBitrate(state.vbr + state.abr) ) : state.vbr ? ( formatBitrate(state.vbr) ) : state.abr ? ( formatBitrate(state.abr) ) : ( 'unknown' )}
{state.playlist_id && state.playlist_index && ( Playlist ({state.playlist_index} of {state.playlist_n_entries}) )} {state.vcodec && ( {formatCodec(state.vcodec)} )} {state.acodec && ( {formatCodec(state.acodec)} )} {state.dynamic_range && state.dynamic_range !== 'SDR' && ( {state.dynamic_range} )} {state.subtitle_id && ( ESUB )}
Remove from library? Are you sure you want to remove this download from the library? You can also delete the downloaded file by cheking the box below. This action cannot be undone.
{setIsDeleteFileChecked(state.download_id, !itemActionStates.isDeleteFileChecked)}} />
Cancel removeFromDownloads(state, itemActionStates.isDeleteFileChecked).then(() => { setIsDeleteFileChecked(state.download_id, false); }) }>Remove
) }) ) : (

Nothing!

No Completed Downloads

You have not completed any downloads yet. Complete downloading something to see here :)

)}
{incompleteDownloads.length > 0 ? ( incompleteDownloads.map((state) => { const itemActionStates = downloadActions[state.download_id] || { isResuming: false, isPausing: false, isCanceling: false, isDeleteFileChecked: false, }; return (
{state.ext && ( {state.filetype && (state.filetype === 'video' || state.filetype === 'video+audio') && ( )}

{state.title}

{((state.download_status === 'starting') || (state.download_status === 'downloading' && state.status === 'finished')) && ( )} {(state.download_status === 'downloading' || state.download_status === 'paused') && state.progress && state.status !== 'finished' && (
{state.progress}% { state.downloaded && state.total ? `(${formatFileSize(state.downloaded)} / ${formatFileSize(state.total)})` : null }
)}
{ state.download_status && ( `${state.download_status === 'downloading' && state.status === 'finished' ? 'Processing' : state.download_status.charAt(0).toUpperCase() + state.download_status.slice(1)} ${state.download_id ? `• ID: ${state.download_id.toUpperCase()}` : ""} ${state.download_status === 'downloading' && state.status !== 'finished' && state.speed ? `• Speed: ${formatSpeed(state.speed)}` : ""} ${state.download_status === 'downloading' && state.eta ? `• ETA: ${formatSecToTimeString(state.eta)}` : ""}` )}
{state.download_status === 'paused' ? ( ) : ( )}
) }) ) : (

Nothing!

No Incomplete Downloads

You have all caught up! Sit back and relax or just spin up a new download to see here :)

)}
); }