mirror of
https://github.com/neosubhamoy/neodlp.git
synced 2026-02-04 14:12:22 +05:30
fix: errored playlist downloads falsely showing up as completed
This commit is contained in:
@@ -107,7 +107,7 @@ export default function App({ children }: { children: React.ReactNode }) {
|
|||||||
appWindow.setFocus();
|
appWindow.setFocus();
|
||||||
navigate('/');
|
navigate('/');
|
||||||
if (event.payload.url) {
|
if (event.payload.url) {
|
||||||
LOG.info('NEODLP', `Received download request from neodlp browser extension for URL: ${event.payload.url}`);
|
LOG.info('NEODLP', `Received search request from neodlp browser extension for URL: ${event.payload.url}`);
|
||||||
const { setRequestedUrl, setAutoSubmitSearch } = useCurrentVideoMetadataStore.getState();
|
const { setRequestedUrl, setAutoSubmitSearch } = useCurrentVideoMetadataStore.getState();
|
||||||
setRequestedUrl(event.payload.url);
|
setRequestedUrl(event.payload.url);
|
||||||
setAutoSubmitSearch(true);
|
setAutoSubmitSearch(true);
|
||||||
@@ -333,8 +333,11 @@ export default function App({ children }: { children: React.ReactNode }) {
|
|||||||
|
|
||||||
processedUnexpectedErrors.forEach((downloadId) => {
|
processedUnexpectedErrors.forEach((downloadId) => {
|
||||||
const downloadState = globalDownloadStates.find(d => d.download_id === downloadId);
|
const downloadState = globalDownloadStates.find(d => d.download_id === downloadId);
|
||||||
|
const isPlaylist = downloadState?.playlist_id !== null && downloadState?.playlist_indices !== null;
|
||||||
|
const isMultiplePlaylistItems = isPlaylist && downloadState?.playlist_indices && downloadState?.playlist_indices.includes(',');
|
||||||
|
|
||||||
toast.error("Download Failed", {
|
toast.error("Download Failed", {
|
||||||
description: `The download for "${downloadState?.title}" failed because yt-dlp exited unexpectedly. Please try again later.`,
|
description: `The download for ${isMultiplePlaylistItems ? 'playlist ' : ''}"${isMultiplePlaylistItems ? downloadState?.playlist_title : downloadState?.title}" failed because yt-dlp exited unexpectedly. Please try again later.`,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -42,12 +42,12 @@ export default function Navbar() {
|
|||||||
<p>Logs</p>
|
<p>Logs</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<DialogContent className="sm:max-w-[600px]">
|
<DialogContent className="sm:max-w-150">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Log Viewer</DialogTitle>
|
<DialogTitle>Log Viewer</DialogTitle>
|
||||||
<DialogDescription>Monitor real-time app session logs (latest on top)</DialogDescription>
|
<DialogDescription>Monitor real-time app session logs (latest on top)</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="flex flex-col gap-2 p-2 max-h-[300px] overflow-y-scroll overflow-x-hidden bg-muted">
|
<div className="flex flex-col gap-2 p-2 max-h-75 overflow-y-scroll overflow-x-hidden bg-muted">
|
||||||
{logs.length === 0 ? (
|
{logs.length === 0 ? (
|
||||||
<p className="text-sm text-muted-foreground">NO LOGS TO SHOW!</p>
|
<p className="text-sm text-muted-foreground">NO LOGS TO SHOW!</p>
|
||||||
) : (
|
) : (
|
||||||
@@ -61,7 +61,7 @@ export default function Navbar() {
|
|||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="destructive"
|
||||||
disabled={logs.length === 0}
|
disabled={logs.length === 0}
|
||||||
onClick={() => logger.clearLogs()}
|
onClick={() => logger.clearLogs()}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -63,12 +63,12 @@ function DownloadConfigDialog({ selectedFormatFileType }: DownloadConfigDialogPr
|
|||||||
<p>Configurations</p>
|
<p>Configurations</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<DialogContent className="sm:max-w-[450px]">
|
<DialogContent className="sm:max-w-112.5">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Configurations</DialogTitle>
|
<DialogTitle>Configurations</DialogTitle>
|
||||||
<DialogDescription>Tweak this download's configurations</DialogDescription>
|
<DialogDescription>Tweak this download's configurations</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="flex flex-col gap-2 max-h-[300px] overflow-y-scroll overflow-x-hidden no-scrollbar">
|
<div className="flex flex-col gap-2 max-h-75 overflow-y-scroll overflow-x-hidden no-scrollbar">
|
||||||
<Tabs
|
<Tabs
|
||||||
className=""
|
className=""
|
||||||
value={activeDownloadConfigurationTab}
|
value={activeDownloadConfigurationTab}
|
||||||
|
|||||||
@@ -61,11 +61,11 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
|
|||||||
|
|
||||||
const removeFromDownloads = async (downloadState: DownloadState, delete_file: boolean) => {
|
const removeFromDownloads = async (downloadState: DownloadState, delete_file: boolean) => {
|
||||||
if (delete_file && downloadState.filepath) {
|
if (delete_file && downloadState.filepath) {
|
||||||
const isMutilplePlaylistItems = downloadState.playlist_id !== null &&
|
const isMultiplePlaylistItems = downloadState.playlist_id !== null &&
|
||||||
downloadState.playlist_indices !== null &&
|
downloadState.playlist_indices !== null &&
|
||||||
downloadState.playlist_indices.includes(',');
|
downloadState.playlist_indices.includes(',');
|
||||||
|
|
||||||
if (isMutilplePlaylistItems) {
|
if (isMultiplePlaylistItems) {
|
||||||
const dirPath = await dirname(downloadState.filepath);
|
const dirPath = await dirname(downloadState.filepath);
|
||||||
try {
|
try {
|
||||||
if (await fs.exists(dirPath)) {
|
if (await fs.exists(dirPath)) {
|
||||||
@@ -95,11 +95,11 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
|
|||||||
queryClient.invalidateQueries({ queryKey: ['download-states'] });
|
queryClient.invalidateQueries({ queryKey: ['download-states'] });
|
||||||
if (delete_file && downloadState.filepath) {
|
if (delete_file && downloadState.filepath) {
|
||||||
toast.success("Deleted from downloads", {
|
toast.success("Deleted from downloads", {
|
||||||
description: `The download for ${isMutilplePlaylistItems ? 'playlist ' : ''}"${isMutilplePlaylistItems ? downloadState.playlist_title : downloadState.title}" has been deleted successfully.`,
|
description: `The download for ${isMultiplePlaylistItems ? 'playlist ' : ''}"${isMultiplePlaylistItems ? downloadState.playlist_title : downloadState.title}" has been deleted successfully.`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast.success("Removed from downloads", {
|
toast.success("Removed from downloads", {
|
||||||
description: `The download for ${isMutilplePlaylistItems ? 'playlist ' : ''}"${isMutilplePlaylistItems ? downloadState.playlist_title : downloadState.title}" has been removed successfully.`,
|
description: `The download for ${isMultiplePlaylistItems ? 'playlist ' : ''}"${isMultiplePlaylistItems ? downloadState.playlist_title : downloadState.title}" has been removed successfully.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -107,11 +107,11 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
|
|||||||
console.error("Failed to delete download state:", error);
|
console.error("Failed to delete download state:", error);
|
||||||
if (delete_file && downloadState.filepath) {
|
if (delete_file && downloadState.filepath) {
|
||||||
toast.error("Failed to delete download", {
|
toast.error("Failed to delete download", {
|
||||||
description: `An error occurred while trying to delete the download for ${isMutilplePlaylistItems ? 'playlist ' : ''}"${isMutilplePlaylistItems ? downloadState.playlist_title : downloadState.title}".`,
|
description: `An error occurred while trying to delete the download for ${isMultiplePlaylistItems ? 'playlist ' : ''}"${isMultiplePlaylistItems ? downloadState.playlist_title : downloadState.title}".`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast.error("Failed to remove download", {
|
toast.error("Failed to remove download", {
|
||||||
description: `An error occurred while trying to remove the download for ${isMutilplePlaylistItems ? 'playlist ' : ''}"${isMutilplePlaylistItems ? downloadState.playlist_title : downloadState.title}".`,
|
description: `An error occurred while trying to remove the download for ${isMultiplePlaylistItems ? 'playlist ' : ''}"${isMultiplePlaylistItems ? downloadState.playlist_title : downloadState.title}".`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,12 +144,12 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isPlaylist = state.playlist_id !== null && state.playlist_indices !== null;
|
const isPlaylist = state.playlist_id !== null && state.playlist_indices !== null;
|
||||||
const isMutilplePlaylistItems = isPlaylist && state.playlist_indices && state.playlist_indices.includes(',');
|
const isMultiplePlaylistItems = isPlaylist && state.playlist_indices && state.playlist_indices.includes(',');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 border border-border rounded-lg flex gap-4" key={state.download_id}>
|
<div className="p-4 border border-border rounded-lg flex gap-4" key={state.download_id}>
|
||||||
<div className="w-[30%] flex flex-col justify-between gap-2">
|
<div className="w-[30%] flex flex-col justify-between gap-2">
|
||||||
{isMutilplePlaylistItems ? (
|
{isMultiplePlaylistItems ? (
|
||||||
<div className="w-full relative flex items-center justify-center mt-2">
|
<div className="w-full relative flex items-center justify-center mt-2">
|
||||||
<AspectRatio ratio={16 / 9} className="w-full rounded-lg overflow-hidden border border-border mb-2 z-20">
|
<AspectRatio ratio={16 / 9} className="w-full rounded-lg overflow-hidden border border-border mb-2 z-20">
|
||||||
<ProxyImage src={state.thumbnail || ""} alt="thumbnail" className="" />
|
<ProxyImage src={state.thumbnail || ""} alt="thumbnail" className="" />
|
||||||
@@ -166,7 +166,7 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
|
|||||||
<ProxyImage src={state.thumbnail || ""} alt="thumbnail" className="" />
|
<ProxyImage src={state.thumbnail || ""} alt="thumbnail" className="" />
|
||||||
</AspectRatio>
|
</AspectRatio>
|
||||||
)}
|
)}
|
||||||
{isMutilplePlaylistItems ? (
|
{isMultiplePlaylistItems ? (
|
||||||
<span className="w-full flex items-center justify-center text-xs border border-border py-1 px-2 rounded">
|
<span className="w-full flex items-center justify-center text-xs border border-border py-1 px-2 rounded">
|
||||||
<ListVideo className="w-4 h-4 mr-2 stroke-primary" /> Playlist ({state.playlist_indices?.split(',').length})
|
<ListVideo className="w-4 h-4 mr-2 stroke-primary" /> Playlist ({state.playlist_indices?.split(',').length})
|
||||||
</span>
|
</span>
|
||||||
@@ -187,11 +187,11 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-col justify-between gap-2">
|
<div className="w-full flex flex-col justify-between gap-2">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<h4 className="">{isMutilplePlaylistItems ? state.playlist_title : state.title}</h4>
|
<h4 className="">{isMultiplePlaylistItems ? state.playlist_title : state.title}</h4>
|
||||||
<p className="text-xs text-muted-foreground">{isMutilplePlaylistItems ? state.playlist_channel ?? 'unknown' : state.channel ?? 'unknown'} {state.host ? <><span className="text-primary">•</span> {state.host}</> : 'unknown'}</p>
|
<p className="text-xs text-muted-foreground">{isMultiplePlaylistItems ? state.playlist_channel ?? 'unknown' : state.channel ?? 'unknown'} {state.host ? <><span className="text-primary">•</span> {state.host}</> : 'unknown'}</p>
|
||||||
<div className="flex items-center mt-1">
|
<div className="flex items-center mt-1">
|
||||||
<span className="text-xs text-muted-foreground flex items-center pr-3">
|
<span className="text-xs text-muted-foreground flex items-center pr-3">
|
||||||
{isMutilplePlaylistItems ? (
|
{isMultiplePlaylistItems ? (
|
||||||
<><ListVideo className="w-4 h-4 mr-2"/> {state.playlist_n_entries ?? 'unknown'}</>
|
<><ListVideo className="w-4 h-4 mr-2"/> {state.playlist_n_entries ?? 'unknown'}</>
|
||||||
) : (
|
) : (
|
||||||
<><Clock className="w-4 h-4 mr-2"/> {state.duration_string ? formatDurationString(state.duration_string) : 'unknown'}</>
|
<><Clock className="w-4 h-4 mr-2"/> {state.duration_string ? formatDurationString(state.duration_string) : 'unknown'}</>
|
||||||
@@ -224,7 +224,7 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden xl:flex items-center mt-1 gap-2 flex-wrap text-xs">
|
<div className="hidden xl:flex items-center mt-1 gap-2 flex-wrap text-xs">
|
||||||
{state.playlist_id && state.playlist_indices && !isMutilplePlaylistItems && (
|
{state.playlist_id && state.playlist_indices && !isMultiplePlaylistItems && (
|
||||||
<span
|
<span
|
||||||
className="border border-border py-1 px-2 rounded flex items-center cursor-pointer"
|
className="border border-border py-1 px-2 rounded flex items-center cursor-pointer"
|
||||||
title={`${state.playlist_title ?? 'UNKNOWN PLAYLIST'}` + ' by ' + `${state.playlist_channel ?? 'UNKNOWN CHANNEL'}`}
|
title={`${state.playlist_title ?? 'UNKNOWN PLAYLIST'}` + ' by ' + `${state.playlist_channel ?? 'UNKNOWN CHANNEL'}`}
|
||||||
@@ -232,13 +232,13 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
|
|||||||
<ListVideo className="w-4 h-4 mr-2" /> Playlist ({state.playlist_indices} of {state.playlist_n_entries})
|
<ListVideo className="w-4 h-4 mr-2" /> Playlist ({state.playlist_indices} of {state.playlist_n_entries})
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{state.vcodec && !isMutilplePlaylistItems && (
|
{state.vcodec && !isMultiplePlaylistItems && (
|
||||||
<span className="border border-border py-1 px-2 rounded">{formatCodec(state.vcodec)}</span>
|
<span className="border border-border py-1 px-2 rounded">{formatCodec(state.vcodec)}</span>
|
||||||
)}
|
)}
|
||||||
{state.acodec && !isMutilplePlaylistItems && (
|
{state.acodec && !isMultiplePlaylistItems && (
|
||||||
<span className="border border-border py-1 px-2 rounded">{formatCodec(state.acodec)}</span>
|
<span className="border border-border py-1 px-2 rounded">{formatCodec(state.acodec)}</span>
|
||||||
)}
|
)}
|
||||||
{state.dynamic_range && state.dynamic_range !== 'SDR' && !isMutilplePlaylistItems && (
|
{state.dynamic_range && state.dynamic_range !== 'SDR' && !isMultiplePlaylistItems && (
|
||||||
<span className="border border-border py-1 px-2 rounded">{state.dynamic_range}</span>
|
<span className="border border-border py-1 px-2 rounded">{state.dynamic_range}</span>
|
||||||
)}
|
)}
|
||||||
{state.subtitle_id && (
|
{state.subtitle_id && (
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ export function IncompleteDownload({ state }: IncompleteDownloadProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isPlaylist = state.playlist_id !== null && state.playlist_indices !== null;
|
const isPlaylist = state.playlist_id !== null && state.playlist_indices !== null;
|
||||||
const isMutilplePlaylistItems = isPlaylist && state.playlist_indices && state.playlist_indices.includes(',');
|
const isMultiplePlaylistItems = isPlaylist && state.playlist_indices && state.playlist_indices.includes(',');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 border border-border rounded-lg flex gap-4" key={state.download_id}>
|
<div className="p-4 border border-border rounded-lg flex gap-4" key={state.download_id}>
|
||||||
<div className="w-[30%] flex flex-col justify-between gap-2">
|
<div className="w-[30%] flex flex-col justify-between gap-2">
|
||||||
{isMutilplePlaylistItems ? (
|
{isMultiplePlaylistItems ? (
|
||||||
<div className="w-full relative flex items-center justify-center mt-2">
|
<div className="w-full relative flex items-center justify-center mt-2">
|
||||||
<AspectRatio ratio={16 / 9} className="w-full rounded-lg overflow-hidden border border-border mb-2 z-20">
|
<AspectRatio ratio={16 / 9} className="w-full rounded-lg overflow-hidden border border-border mb-2 z-20">
|
||||||
<ProxyImage src={state.thumbnail || ""} alt="thumbnail" className="" />
|
<ProxyImage src={state.thumbnail || ""} alt="thumbnail" className="" />
|
||||||
@@ -60,7 +60,7 @@ export function IncompleteDownload({ state }: IncompleteDownloadProps) {
|
|||||||
<ProxyImage src={state.thumbnail || ""} alt="thumbnail" className="" />
|
<ProxyImage src={state.thumbnail || ""} alt="thumbnail" className="" />
|
||||||
</AspectRatio>
|
</AspectRatio>
|
||||||
)}
|
)}
|
||||||
{isMutilplePlaylistItems ? (
|
{isMultiplePlaylistItems ? (
|
||||||
<span className="w-full flex items-center justify-center text-xs border border-border py-1 px-2 rounded">
|
<span className="w-full flex items-center justify-center text-xs border border-border py-1 px-2 rounded">
|
||||||
<ListVideo className="w-4 h-4 mr-2 stroke-primary" /> Playlist ({state.playlist_indices?.split(',').length})
|
<ListVideo className="w-4 h-4 mr-2 stroke-primary" /> Playlist ({state.playlist_indices?.split(',').length})
|
||||||
</span>
|
</span>
|
||||||
@@ -89,13 +89,13 @@ export function IncompleteDownload({ state }: IncompleteDownloadProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-col justify-between">
|
<div className="w-full flex flex-col justify-between">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<h4>{isMutilplePlaylistItems ? state.playlist_title : state.title}</h4>
|
<h4>{isMultiplePlaylistItems ? state.playlist_title : state.title}</h4>
|
||||||
{((state.download_status === 'starting') || (state.download_status === 'downloading' && state.status === 'finished')) && (
|
{((state.download_status === 'starting') || (state.download_status === 'downloading' && state.status === 'finished')) && (
|
||||||
<IndeterminateProgress indeterminate={true} className="w-full" />
|
<IndeterminateProgress indeterminate={true} className="w-full" />
|
||||||
)}
|
)}
|
||||||
{(state.download_status === 'downloading' || state.download_status === 'paused' || state.download_status === 'errored') && state.progress && state.status !== 'finished' && (
|
{(state.download_status === 'downloading' || state.download_status === 'paused' || state.download_status === 'errored') && state.progress && state.status !== 'finished' && (
|
||||||
<div className="w-full flex items-center gap-2">
|
<div className="w-full flex items-center gap-2">
|
||||||
{isMutilplePlaylistItems && state.item ? (
|
{isMultiplePlaylistItems && state.item ? (
|
||||||
<span className="text-sm text-nowrap">({state.item})</span>
|
<span className="text-sm text-nowrap">({state.item})</span>
|
||||||
) : null}
|
) : null}
|
||||||
<span className="text-sm text-nowrap">{state.progress}%</span>
|
<span className="text-sm text-nowrap">{state.progress}%</span>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ function AppGeneralSettings() {
|
|||||||
<p className="text-xs text-muted-foreground mb-3">Set maximum number of allowed parallel downloads</p>
|
<p className="text-xs text-muted-foreground mb-3">Set maximum number of allowed parallel downloads</p>
|
||||||
<Slider
|
<Slider
|
||||||
id="max-parallel-downloads"
|
id="max-parallel-downloads"
|
||||||
className="w-[350px] mb-2"
|
className="w-87.5 mb-2"
|
||||||
value={[maxParallelDownloads]}
|
value={[maxParallelDownloads]}
|
||||||
min={1}
|
min={1}
|
||||||
max={5}
|
max={5}
|
||||||
@@ -114,7 +114,7 @@ function AppGeneralSettings() {
|
|||||||
<p className="text-xs text-muted-foreground mb-3">Set maximum number of retries for a download before giving up</p>
|
<p className="text-xs text-muted-foreground mb-3">Set maximum number of retries for a download before giving up</p>
|
||||||
<Slider
|
<Slider
|
||||||
id="max-retries"
|
id="max-retries"
|
||||||
className="w-[350px] mb-2"
|
className="w-87.5 mb-2"
|
||||||
value={[maxRetries]}
|
value={[maxRetries]}
|
||||||
min={1}
|
min={1}
|
||||||
max={100}
|
max={100}
|
||||||
@@ -769,7 +769,7 @@ function AppCookiesSettings() {
|
|||||||
onValueChange={(value) => saveSettingsKey('cookies_browser', value)}
|
onValueChange={(value) => saveSettingsKey('cookies_browser', value)}
|
||||||
disabled={importCookiesFrom !== "browser" || !useCookies || useCustomCommands}
|
disabled={importCookiesFrom !== "browser" || !useCookies || useCustomCommands}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-[230px] ring-0 focus:ring-0">
|
<SelectTrigger className="w-57.5 ring-0 focus:ring-0">
|
||||||
<SelectValue placeholder="Select browser to import cookies" />
|
<SelectValue placeholder="Select browser to import cookies" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -1408,7 +1408,7 @@ function AppInfoSettings() {
|
|||||||
<Package className="size-4" /> Dependencies
|
<Package className="size-4" /> Dependencies
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[600px]">
|
<DialogContent className="sm:max-w-150">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Dependencies</DialogTitle>
|
<DialogTitle>Dependencies</DialogTitle>
|
||||||
<DialogDescription>Major dependencies of NeoDLP</DialogDescription>
|
<DialogDescription>Major dependencies of NeoDLP</DialogDescription>
|
||||||
@@ -1505,7 +1505,7 @@ export function ApplicationSettings() {
|
|||||||
value={ytDlpUpdateChannel}
|
value={ytDlpUpdateChannel}
|
||||||
onValueChange={(value) => saveSettingsKey('ytdlp_update_channel', value)}
|
onValueChange={(value) => saveSettingsKey('ytdlp_update_channel', value)}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-[150px] ring-0 focus:ring-0">
|
<SelectTrigger className="w-37.5 ring-0 focus:ring-0">
|
||||||
<SelectValue placeholder="Select update channel" />
|
<SelectValue placeholder="Select update channel" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -1553,7 +1553,7 @@ export function ApplicationSettings() {
|
|||||||
</TabsList>
|
</TabsList>
|
||||||
<div className="min-h-full flex flex-col w-full border-l border-border pl-4">
|
<div className="min-h-full flex flex-col w-full border-l border-border pl-4">
|
||||||
{tabsList.map((tab) => (
|
{tabsList.map((tab) => (
|
||||||
<TabsContent key={tab.key} value={tab.key} className={clsx("flex flex-col gap-4 min-h-[435px]", tab.key === "info" ? "max-w-[80%]" : "max-w-[70%]")}>
|
<TabsContent key={tab.key} value={tab.key} className={clsx("flex flex-col gap-4 min-h-108.75", tab.key === "info" ? "max-w-[80%]" : "max-w-[70%]")}>
|
||||||
{tab.component}
|
{tab.component}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ export function ExtensionSettings() {
|
|||||||
</TabsList>
|
</TabsList>
|
||||||
<div className="min-h-full flex flex-col w-full border-l border-border pl-4">
|
<div className="min-h-full flex flex-col w-full border-l border-border pl-4">
|
||||||
{tabsList.map((tab) => (
|
{tabsList.map((tab) => (
|
||||||
<TabsContent key={tab.key} value={tab.key} className={clsx("flex flex-col gap-4 min-h-[150px]", tab.key === "install" ? "max-w-[90%]" : "max-w-[70%]")}>
|
<TabsContent key={tab.key} value={tab.key} className={clsx("flex flex-col gap-4 min-h-37.5", tab.key === "install" ? "max-w-[90%]" : "max-w-[70%]")}>
|
||||||
{tab.component}
|
{tab.component}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { config } from "@/config";
|
import { config } from "@/config";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, useSidebar } from "@/components/ui/sidebar";
|
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, useSidebar } from "@/components/ui/sidebar";
|
||||||
import { CircleArrowUp, Download, Settings, SquarePlay, } from "lucide-react";
|
import { CircleArrowUp } from "lucide-react";
|
||||||
import { isActive as isActiveSidebarItem } from "@/utils";
|
import { isActive as isActiveSidebarItem } from "@/utils";
|
||||||
import { RoutesObj } from "@/types/route";
|
import { RoutesObj } from "@/types/route";
|
||||||
|
import { AllRoutes } from "@/routes";
|
||||||
import { useDownloadStatesStore, useSettingsPageStatesStore } from "@/services/store";
|
import { useDownloadStatesStore, useSettingsPageStatesStore } from "@/services/store";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -36,24 +37,12 @@ export function AppSidebar() {
|
|||||||
const [isNativeLinuxApp, setIsNativeLinuxApp] = useState(false);
|
const [isNativeLinuxApp, setIsNativeLinuxApp] = useState(false);
|
||||||
|
|
||||||
const topItems: Array<RoutesObj> = [
|
const topItems: Array<RoutesObj> = [
|
||||||
{
|
AllRoutes[0], // Downloader
|
||||||
title: "Downloader",
|
AllRoutes[1], // Library
|
||||||
url: "/",
|
|
||||||
icon: Download,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Library",
|
|
||||||
url: "/library",
|
|
||||||
icon: SquarePlay,
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const bottomItems: Array<RoutesObj> = [
|
const bottomItems: Array<RoutesObj> = [
|
||||||
{
|
AllRoutes[2], // Settings
|
||||||
title: "Settings",
|
|
||||||
url: "/settings",
|
|
||||||
icon: Settings,
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { ulid } from "ulid";
|
|||||||
import { sendNotification } from '@tauri-apps/plugin-notification';
|
import { sendNotification } from '@tauri-apps/plugin-notification';
|
||||||
import { FetchVideoMetadataParams, StartDownloadParams } from "@/providers/appContextProvider";
|
import { FetchVideoMetadataParams, StartDownloadParams } from "@/providers/appContextProvider";
|
||||||
import { useDebouncedCallback } from '@tanstack/react-pacer/debouncer';
|
import { useDebouncedCallback } from '@tanstack/react-pacer/debouncer';
|
||||||
|
import { fetchDownloadStateById } from "@/services/database";
|
||||||
|
|
||||||
export default function useDownloader() {
|
export default function useDownloader() {
|
||||||
const globalDownloadStates = useDownloadStatesStore((state) => state.downloadStates);
|
const globalDownloadStates = useDownloadStatesStore((state) => state.downloadStates);
|
||||||
@@ -564,6 +565,9 @@ export default function useDownloader() {
|
|||||||
downloadFilePath = line.replace('Finalpath: ', '').trim().replace(/^"|"$/g, '');
|
downloadFilePath = line.replace('Finalpath: ', '').trim().replace(/^"|"$/g, '');
|
||||||
const downloadedFileExt = downloadFilePath.split('.').pop();
|
const downloadedFileExt = downloadFilePath.split('.').pop();
|
||||||
|
|
||||||
|
const state = await fetchDownloadStateById(downloadId);
|
||||||
|
const isLastPlaylistItem = state?.item && Number(state?.item.split('/')[0]) === Number(state?.item.split('/')[1]);
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
downloadFilePathUpdater.mutate({ download_id: downloadId, filepath: downloadFilePath as string, ext: downloadedFileExt as string }, {
|
downloadFilePathUpdater.mutate({ download_id: downloadId, filepath: downloadFilePath as string, ext: downloadedFileExt as string }, {
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
@@ -574,13 +578,10 @@ export default function useDownloader() {
|
|||||||
console.error("Failed to update download filepath:", error);
|
console.error("Failed to update download filepath:", error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPlaylist && line.startsWith('[download] Finished downloading playlist:')) {
|
if (isLastPlaylistItem) {
|
||||||
// Update completion status after a short delay to ensure database states are propagated correctly
|
console.log(`Playlist download completed with ID: ${downloadId}, updating status...`);
|
||||||
console.log(`Playlist download completed with ID: ${downloadId}, updating status after 2s delay...`);
|
|
||||||
setTimeout(async () => {
|
|
||||||
LOG.info('NEODLP', `yt-dlp download completed with id: ${downloadId}`);
|
LOG.info('NEODLP', `yt-dlp download completed with id: ${downloadId}`);
|
||||||
downloadStatusUpdater.mutate({ download_id: downloadId, download_status: 'completed' }, {
|
downloadStatusUpdater.mutate({ download_id: downloadId, download_status: 'completed' }, {
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
@@ -592,16 +593,17 @@ export default function useDownloader() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
toast.success(`${isMultiplePlaylistItems ? 'Playlist ' : ''}Download Completed`, {
|
toast.success("Download Completed", {
|
||||||
description: `The download for ${isMultiplePlaylistItems ? 'playlist ' : ''}"${isMultiplePlaylistItems ? videoMetadata.playlist_title : videoMetadata.title}" has completed successfully.`,
|
description: `The download for ${isMultiplePlaylistItems ? 'playlist ' : ''}"${isMultiplePlaylistItems ? videoMetadata.playlist_title : videoMetadata.title}" has completed successfully.`,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ENABLE_NOTIFICATIONS && DOWNLOAD_COMPLETION_NOTIFICATION) {
|
if (ENABLE_NOTIFICATIONS && DOWNLOAD_COMPLETION_NOTIFICATION) {
|
||||||
sendNotification({
|
sendNotification({
|
||||||
title: `${isMultiplePlaylistItems ? 'Playlist ' : ''}Download Completed`,
|
title: "Download Completed",
|
||||||
body: `The download for ${isMultiplePlaylistItems ? 'playlist ' : ''}"${isMultiplePlaylistItems ? videoMetadata.playlist_title : videoMetadata.title}" has completed successfully.`,
|
body: `The download for ${isMultiplePlaylistItems ? 'playlist ' : ''}"${isMultiplePlaylistItems ? videoMetadata.playlist_title : videoMetadata.title}" has completed successfully.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ export default function DownloaderPage() {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="focus-visible:ring-0"
|
className="focus-visible:ring-0"
|
||||||
placeholder="Enter Video URL to Search"
|
placeholder="Enter Video/Playlist URL to Download"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
Reference in New Issue
Block a user