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 (
{state.channel ? state.channel : 'unknown'} {state.host ? `• ${state.host}` : 'unknown'}
No Completed Downloads
You have not completed any downloads yet. Complete downloading something to see here :)
No Incomplete Downloads
You have all caught up! Sit back and relax or just spin up a new download to see here :)