1
1
mirror of https://github.com/neosubhamoy/neodlp.git synced 2026-02-04 17:42:22 +05:30

(feat): added speed rate limit option in settings, improved download error handling and improved remove from library dialog

This commit is contained in:
2025-07-14 13:39:59 +05:30
Verified
parent 21524d2b29
commit 097839d919
6 changed files with 201 additions and 34 deletions

View File

@@ -6,7 +6,7 @@ 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, useLibraryPageStatesStore } from "@/services/store";
import { useDownloadActionStatesStore, useDownloaderPageStatesStore, 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, Square, Trash2, Video, X } from "lucide-react";
import { invoke } from "@tauri-apps/api/core";
@@ -33,6 +33,8 @@ export default function LibraryPage() {
const setIsCancelingDownload = useDownloadActionStatesStore(state => state.setIsCancelingDownload);
const setIsDeleteFileChecked = useDownloadActionStatesStore(state => state.setIsDeleteFileChecked);
const setIsErrorExpected = useDownloaderPageStatesStore((state) => state.setIsErrorExpected);
const { pauseDownload, resumeDownload, cancelDownload } = useAppContext()
const { toast } = useToast();
@@ -106,6 +108,7 @@ export default function LibraryPage() {
const stopOngoingDownloads = async () => {
if (ongoingDownloads.length > 0) {
setIsErrorExpected(true); // Set error expected to true to handle UI state
try {
await invoke('pause_ongoing_downloads').then(() => {
queryClient.invalidateQueries({ queryKey: ['download-states'] });
@@ -275,9 +278,9 @@ export default function LibraryPage() {
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogTitle>Remove from library?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone! it will permanently remove this from downloads.
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.
</AlertDialogDescription>
<div className="flex items-center space-x-2">
<Checkbox id="delete-file" checked={itemActionStates.isDeleteFileChecked} onCheckedChange={() => {setIsDeleteFileChecked(state.download_id, !itemActionStates.isDeleteFileChecked)}} />

View File

@@ -26,6 +26,7 @@ import { SlidingButton } from "@/components/custom/slidingButton";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import * as fs from "@tauri-apps/plugin-fs";
import { join } from "@tauri-apps/api/path";
import { formatSpeed } from "@/utils";
const websocketPortSchema = z.object({
port: z.coerce.number({
@@ -42,6 +43,17 @@ const proxyUrlSchema = z.object({
url: z.string().min(1, { message: "Proxy URL is required" }).url({ message: "Invalid URL format" })
});
const rateLimitSchema = z.object({
rate_limit: z.coerce.number({
required_error: "Rate Limit is required",
invalid_type_error: "Rate Limit must be a valid number",
}).min(1024, {
message: "Rate Limit must be at least 1024 bytes/s (1 KB/s)"
}).max(104857600, {
message: "Rate Limit must be at most 104857600 bytes/s (100 MB/s)"
}),
});
export default function SettingsPage() {
const { toast } = useToast();
const { setTheme } = useTheme();
@@ -65,6 +77,8 @@ export default function SettingsPage() {
const strictDownloadabilityCheck = useSettingsPageStatesStore(state => state.settings.strict_downloadablity_check);
const useProxy = useSettingsPageStatesStore(state => state.settings.use_proxy);
const proxyUrl = useSettingsPageStatesStore(state => state.settings.proxy_url);
const useRateLimit = useSettingsPageStatesStore(state => state.settings.use_rate_limit);
const rateLimit = useSettingsPageStatesStore(state => state.settings.rate_limit);
const videoFormat = useSettingsPageStatesStore(state => state.settings.video_format);
const audioFormat = useSettingsPageStatesStore(state => state.settings.audio_format);
const alwaysReencodeVideo = useSettingsPageStatesStore(state => state.settings.always_reencode_video);
@@ -169,6 +183,33 @@ export default function SettingsPage() {
}
}
const rateLimitForm = useForm<z.infer<typeof rateLimitSchema>>({
resolver: zodResolver(rateLimitSchema),
defaultValues: {
rate_limit: rateLimit,
},
mode: "onChange",
});
const watchedRateLimit = rateLimitForm.watch("rate_limit");
const { errors: rateLimitFormErrors } = rateLimitForm.formState;
function handleRateLimitSubmit(values: z.infer<typeof rateLimitSchema>) {
try {
saveSettingsKey('rate_limit', values.rate_limit);
toast({
title: "Rate Limit updated",
description: `Rate Limit changed to ${values.rate_limit} bytes/s`,
});
} catch (error) {
console.error("Error changing rate limit:", error);
toast({
title: "Failed to change rate limit",
description: "Please try again.",
variant: "destructive",
});
}
}
interface Config {
port: number;
}
@@ -577,36 +618,74 @@ export default function SettingsPage() {
/>
<Label htmlFor="use-proxy">Use Proxy</Label>
</div>
<div className="flex items-center gap-4">
<Form {...proxyUrlForm}>
<form onSubmit={proxyUrlForm.handleSubmit(handleProxyUrlSubmit)} className="flex gap-4 w-full" autoComplete="off">
<FormField
control={proxyUrlForm.control}
name="url"
disabled={!useProxy}
render={({ field }) => (
<FormItem className="w-full">
<FormControl>
<Input
className="focus-visible:ring-0"
placeholder="Enter proxy URL"
{...field}
/>
</FormControl>
<Label htmlFor="url" className="text-xs text-muted-foreground">(Configured: {proxyUrl ? 'Yes' : 'No'}, Status: {useProxy ? 'Enabled' : 'Disabled'})</Label>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
disabled={!watchedProxyUrl || watchedProxyUrl === proxyUrl || Object.keys(proxyUrlFormErrors).length > 0 || !useProxy}
>
Save
</Button>
</form>
</Form>
<Form {...proxyUrlForm}>
<form onSubmit={proxyUrlForm.handleSubmit(handleProxyUrlSubmit)} className="flex gap-4 w-full" autoComplete="off">
<FormField
control={proxyUrlForm.control}
name="url"
disabled={!useProxy}
render={({ field }) => (
<FormItem className="w-full">
<FormControl>
<Input
className="focus-visible:ring-0"
placeholder="Enter proxy URL"
{...field}
/>
</FormControl>
<Label htmlFor="url" className="text-xs text-muted-foreground">(Configured: {proxyUrl ? 'Yes' : 'No'}, Status: {useProxy ? 'Enabled' : 'Disabled'})</Label>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
disabled={!watchedProxyUrl || watchedProxyUrl === proxyUrl || Object.keys(proxyUrlFormErrors).length > 0 || !useProxy}
>
Save
</Button>
</form>
</Form>
</div>
<div className="rate-limit">
<h3 className="font-semibold">Rate Limit</h3>
<p className="text-xs text-muted-foreground mb-3">Limit download speed to prevent network congestion. Rate limit is applied per-download basis (not in the whole app)</p>
<div className="flex items-center space-x-2 mb-4">
<Switch
id="use-rate-limit"
checked={useRateLimit}
onCheckedChange={(checked) => saveSettingsKey('use_rate_limit', checked)}
/>
<Label htmlFor="use-rate-limit">Use Rate Limit</Label>
</div>
<Form {...rateLimitForm}>
<form onSubmit={rateLimitForm.handleSubmit(handleRateLimitSubmit)} className="flex gap-4 w-full" autoComplete="off">
<FormField
control={rateLimitForm.control}
name="rate_limit"
disabled={!useRateLimit}
render={({ field }) => (
<FormItem className="w-full">
<FormControl>
<Input
className="focus-visible:ring-0"
placeholder="Enter rate limit in bytes/s"
{...field}
/>
</FormControl>
<Label htmlFor="rate_limit" className="text-xs text-muted-foreground">(Configured: {rateLimit ? `${rateLimit} = ${formatSpeed(rateLimit)}` : 'No'}, Status: {useRateLimit ? 'Enabled' : 'Disabled'}) (Default: 1048576, Range: 1024-104857600)</Label>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
disabled={!watchedRateLimit || Number(watchedRateLimit) === rateLimit || Object.keys(rateLimitFormErrors).length > 0 || !useRateLimit}
>
Save
</Button>
</form>
</Form>
</div>
</TabsContent>
</div>