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 { useToast } from "@/hooks/use-toast";
import { useAppContext } from "@/providers/appContextProvider";
import { useDownloadActionStatesStore, useDownloadStatesStore } from "@/services/store";
import { formatBitrate, formatCodec, formatDurationString, formatFileSize, formatSecToTimeString, formatSpeed } from "@/utils";
import { AudioLines, Clock, CloudDownload, File, FileAudio2, FileQuestion, FileVideo2, FolderInput, ListVideo, Loader2, Music, PackageCheck, Pause, Play, 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";
export default function LibraryPage() {
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 { toast } = useToast();
const queryClient = useQueryClient();
const downloadStateDeleter = useDeleteDownloadState();
const incompleteDownloads = downloadStates.filter(state => state.download_status !== 'completed');
const completedDownloads = downloadStates.filter(state => state.download_status === 'completed');
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({
title: 'Opening file',
description: `Opening the file with ${app ? app : 'default app'}.`,
})
});
} catch (e) {
console.error(e);
toast({
title: 'Failed to open file',
description: 'An error occurred while trying to open the file.',
variant: "destructive"
})
}
} else {
toast({
title: 'File unavailable',
description: 'The file you are trying to open does not exist.',
variant: "destructive"
})
}
}
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({
title: 'Removed from downloads',
description: 'The download has been removed successfully.',
})
},
onError: (error) => {
console.error("Failed to delete download state:", error);
toast({
title: 'Failed to remove download',
description: 'An error occurred while trying to remove the download.',
variant: "destructive"
})
}
})
}
return (
{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.filetype && state.filetype === 'audio' && (
)}
{(!state.filetype) || (state.filetype && state.filetype !== 'video' && state.filetype !== 'audio' && state.filetype !== 'video+audio') && (
)}
{state.ext.toUpperCase()} {state.resolution ? `(${state.resolution})` : null}
)}
{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_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' ? (
) : (
)}
)
})
) : (
No Incomplete downloads!
)}
{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.filetype && state.filetype === 'audio' && (
)}
{(!state.filetype) || (state.filetype && state.filetype !== 'video' && state.filetype !== 'audio' && state.filetype !== 'video+audio') && (
)}
{state.ext?.toUpperCase()} {state.resolution ? `(${state.resolution})` : null}
{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
)}
Are you absolutely sure?
This action cannot be undone! it will permanently remove this from downloads.
{setIsDeleteFileChecked(state.download_id, !itemActionStates.isDeleteFileChecked)}} />
Cancel
removeFromDownloads(state, itemActionStates.isDeleteFileChecked).then(() => {
setIsDeleteFileChecked(state.download_id, false);
})
}>Remove
)
})
) : (
No Completed downloads!
)}
);
}