feat: added filename template option in settings

This commit is contained in:
2025-10-06 22:50:05 +05:30
parent 7193083b6b
commit e59a85d2b3
4 changed files with 75 additions and 5 deletions

View File

@@ -87,6 +87,7 @@ export default function App({ children }: { children: React.ReactNode }) {
const FORCE_INTERNET_PROTOCOL = useSettingsPageStatesStore(state => state.settings.force_internet_protocol);
const USE_CUSTOM_COMMANDS = useSettingsPageStatesStore(state => state.settings.use_custom_commands);
const CUSTOM_COMMANDS = useSettingsPageStatesStore(state => state.settings.custom_commands);
const FILENAME_TEMPLATE = useSettingsPageStatesStore(state => state.settings.filename_template);
const isErrored = useDownloaderPageStatesStore((state) => state.isErrored);
const isErrorExpected = useDownloaderPageStatesStore((state) => state.isErrorExpected);
@@ -124,7 +125,7 @@ export default function App({ children }: { children: React.ReactNode }) {
const fetchVideoMetadata = async (url: string, formatId?: string, playlistIndex?: string, selectedSubtitles?: string | null, resumeState?: DownloadState, downloadConfig?: DownloadConfiguration): Promise<RawVideoInfo | null> => {
try {
const args = [url, '--dump-single-json', '--no-warnings'];
if (formatId) args.push('-f', formatId);
if (formatId) args.push('--format', formatId);
if (selectedSubtitles) args.push('--embed-subs', '--sub-lang', selectedSubtitles);
if (playlistIndex) args.push('--playlist-items', playlistIndex);
if (PREFER_VIDEO_OVER_PLAYLIST && !playlistIndex) args.push('--no-playlist');
@@ -285,12 +286,12 @@ export default function App({ children }: { children: React.ReactNode }) {
'--paths',
`home:${downloadDirPath}`,
'--output',
`%(title)s_%(resolution|unknown)s[${downloadId}].%(ext)s`,
`${FILENAME_TEMPLATE}[${downloadId}].%(ext)s`,
'--windows-filenames',
'--restrict-filenames',
'--exec',
'after_move:echo Finalpath: {}',
'-f',
'--format',
selectedFormat,
'--no-mtime',
'--no-warnings',

View File

@@ -72,6 +72,10 @@ const addCustomCommandSchema = z.object({
args: z.string().min(1, { message: "Arguments are required" }),
});
const filenameTemplateShcema = z.object({
template: z.string().min(1, { message: "Filename Template is required" }),
});
export default function SettingsPage() {
const { setTheme } = useTheme();
@@ -118,6 +122,7 @@ export default function SettingsPage() {
const forceInternetProtocol = useSettingsPageStatesStore(state => state.settings.force_internet_protocol);
const useCustomCommands = useSettingsPageStatesStore(state => state.settings.use_custom_commands);
const customCommands = useSettingsPageStatesStore(state => state.settings.custom_commands);
const filenameTemplate = useSettingsPageStatesStore(state => state.settings.filename_template);
const websocketPort = useSettingsPageStatesStore(state => state.settings.websocket_port);
const isChangingWebSocketPort = useSettingsPageStatesStore(state => state.isChangingWebSocketPort);
@@ -297,6 +302,30 @@ export default function SettingsPage() {
}
}
const filenameTemplateForm = useForm<z.infer<typeof filenameTemplateShcema>>({
resolver: zodResolver(filenameTemplateShcema),
defaultValues: {
template: filenameTemplate,
},
mode: "onChange",
});
const watchedFilenameTemplate = filenameTemplateForm.watch("template");
const { errors: filenameTemplateFormErrors } = filenameTemplateForm.formState;
function handleFilenameTemplateSubmit(values: z.infer<typeof filenameTemplateShcema>) {
try {
saveSettingsKey('filename_template', values.template);
toast.success("Filename Template updated", {
description: `Filename Template changed to ${values.template}`,
});
} catch (error) {
console.error("Error changing filename template:", error);
toast.error("Failed to change filename template", {
description: "An error occurred while trying to change the filename template. Please try again.",
});
}
}
interface Config {
port: number;
}
@@ -372,7 +401,14 @@ export default function SettingsPage() {
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={
() => resetSettings()
() => {
resetSettings()
proxyUrlForm.reset();
rateLimitForm.reset();
addCustomCommandForm.reset();
filenameTemplateForm.reset();
websocketPortForm.reset();
}
}>Reset</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
@@ -629,6 +665,36 @@ export default function SettingsPage() {
</AlertDialog>
</div>
</div>
<div className="filename-template">
<h3 className="font-semibold">Filename Template</h3>
<p className="text-xs text-muted-foreground mb-3">Set the template for naming downloaded files (download id and file extension will be auto-appended at the end, changing template may cause paused downloads to re-start from begining)</p>
<Form {...filenameTemplateForm}>
<form onSubmit={filenameTemplateForm.handleSubmit(handleFilenameTemplateSubmit)} className="flex gap-4 w-full" autoComplete="off">
<FormField
control={filenameTemplateForm.control}
name="template"
render={({ field }) => (
<FormItem className="w-full">
<FormControl>
<Input
className="focus-visible:ring-0"
placeholder="Enter filename template"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
disabled={!watchedFilenameTemplate || watchedFilenameTemplate === filenameTemplate || Object.keys(filenameTemplateFormErrors).length > 0}
>
Save
</Button>
</form>
</Form>
</div>
</TabsContent>
<TabsContent key="formats" value="formats" className="flex flex-col gap-4 min-h-[350px]">
<div className="video-format">

View File

@@ -179,6 +179,7 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
force_internet_protocol: 'ipv4',
use_custom_commands: false,
custom_commands: [],
filename_template: '%(title)s_%(resolution|unknown)s',
// extension settings
websocket_port: 53511
},
@@ -239,6 +240,7 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
force_internet_protocol: 'ipv4',
use_custom_commands: false,
custom_commands: [],
filename_template: '%(title)s_%(resolution|unknown)s',
// extension settings
websocket_port: 53511
},

View File

@@ -43,6 +43,7 @@ export interface Settings {
force_internet_protocol: string;
use_custom_commands: boolean;
custom_commands: CustomCommand[];
filename_template: string;
// extension settings
websocket_port: number;
}
@@ -52,4 +53,4 @@ export interface DownloadConfiguration {
embed_metadata: boolean | null;
embed_thumbnail: boolean | null;
custom_command: string | null;
}
}