diff --git a/app/main.py b/app/main.py
index 98879cc..0678544 100644
--- a/app/main.py
+++ b/app/main.py
@@ -253,6 +253,7 @@ async def add(request):
subtitle_format = post.get('subtitle_format')
subtitle_language = post.get('subtitle_language')
subtitle_mode = post.get('subtitle_mode')
+ custom_filename = post.get('custom_filename', '')
if custom_name_prefix is None:
custom_name_prefix = ''
@@ -299,6 +300,7 @@ async def add(request):
subtitle_format,
subtitle_language,
subtitle_mode,
+ custom_filename=custom_filename,
)
return web.Response(text=serializer.encode(status))
diff --git a/app/ytdl.py b/app/ytdl.py
index bc84b74..6bc7cd2 100644
--- a/app/ytdl.py
+++ b/app/ytdl.py
@@ -142,6 +142,7 @@ class DownloadInfo:
subtitle_format="srt",
subtitle_language="en",
subtitle_mode="prefer_manual",
+ custom_filename='',
):
self.id = id if len(custom_name_prefix) == 0 else f'{custom_name_prefix}.{id}'
self.title = title if len(custom_name_prefix) == 0 else f'{custom_name_prefix}.{title}'
@@ -164,6 +165,7 @@ class DownloadInfo:
self.subtitle_language = subtitle_language
self.subtitle_mode = subtitle_mode
self.subtitle_files = []
+ self.custom_filename = custom_filename
class Download:
manager = None
@@ -624,6 +626,9 @@ class DownloadQueue:
for property, value in entry.items():
if property.startswith("channel"):
output = _outtmpl_substitute_field(output, property, value)
+ # Custom filename override: replaces entire output template
+ if getattr(dl, 'custom_filename', ''):
+ output = f'{dl.custom_filename}.%(ext)s'
ytdl_options = dict(self.config.YTDL_OPTIONS)
playlist_item_limit = getattr(dl, 'playlist_item_limit', 0)
if playlist_item_limit > 0:
@@ -652,6 +657,7 @@ class DownloadQueue:
subtitle_language,
subtitle_mode,
already,
+ custom_filename='',
_add_gen=None,
):
if not entry:
@@ -683,6 +689,7 @@ class DownloadQueue:
subtitle_language,
subtitle_mode,
already,
+ custom_filename,
_add_gen,
)
elif etype == 'playlist' or etype == 'channel':
@@ -722,7 +729,8 @@ class DownloadQueue:
subtitle_language,
subtitle_mode,
already,
- _add_gen,
+ custom_filename='',
+ _add_gen=_add_gen,
)
)
if any(res['status'] == 'error' for res in results):
@@ -751,6 +759,7 @@ class DownloadQueue:
subtitle_format,
subtitle_language,
subtitle_mode,
+ custom_filename=custom_filename,
)
await self.__add_download(dl, auto_start)
return {'status': 'ok'}
@@ -771,12 +780,13 @@ class DownloadQueue:
subtitle_language="en",
subtitle_mode="prefer_manual",
already=None,
+ custom_filename='',
_add_gen=None,
):
log.info(
f'adding {url}: {quality=} {format=} {already=} {folder=} {custom_name_prefix=} '
f'{playlist_item_limit=} {auto_start=} {split_by_chapters=} {chapter_template=} '
- f'{subtitle_format=} {subtitle_language=} {subtitle_mode=}'
+ f'{subtitle_format=} {subtitle_language=} {subtitle_mode=} {custom_filename=}'
)
if already is None:
_add_gen = self._add_generation
@@ -805,6 +815,7 @@ class DownloadQueue:
subtitle_language,
subtitle_mode,
already,
+ custom_filename,
_add_gen,
)
diff --git a/ui/src/app/app.html b/ui/src/app/app.html
index 42adcc9..eaa8385 100644
--- a/ui/src/app/app.html
+++ b/ui/src/app/app.html
@@ -211,6 +211,14 @@
ngbTooltip="Add a prefix to downloaded filenames">
+
Items Limit
diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts
index 26cca3a..26fffb0 100644
--- a/ui/src/app/app.ts
+++ b/ui/src/app/app.ts
@@ -45,6 +45,7 @@ export class App implements AfterViewInit, OnInit {
format: string;
folder!: string;
customNamePrefix!: string;
+ customFilename = '';
autoStart: boolean;
playlistItemLimit!: number;
splitByChapters: boolean;
@@ -405,6 +406,7 @@ export class App implements AfterViewInit, OnInit {
subtitleFormat?: string,
subtitleLanguage?: string,
subtitleMode?: string,
+ customFilename?: string,
) {
url = url ?? this.addUrl
quality = quality ?? this.quality
@@ -418,6 +420,7 @@ export class App implements AfterViewInit, OnInit {
subtitleFormat = subtitleFormat ?? this.subtitleFormat
subtitleLanguage = subtitleLanguage ?? this.subtitleLanguage
subtitleMode = subtitleMode ?? this.subtitleMode
+ customFilename = customFilename ?? this.customFilename
// Validate chapter template if chapter splitting is enabled
if (splitByChapters && !chapterTemplate.includes('%(section_number)')) {
@@ -425,14 +428,15 @@ export class App implements AfterViewInit, OnInit {
return;
}
- console.debug('Downloading: url=' + url + ' quality=' + quality + ' format=' + format + ' folder=' + folder + ' customNamePrefix=' + customNamePrefix + ' playlistItemLimit=' + playlistItemLimit + ' autoStart=' + autoStart + ' splitByChapters=' + splitByChapters + ' chapterTemplate=' + chapterTemplate + ' subtitleFormat=' + subtitleFormat + ' subtitleLanguage=' + subtitleLanguage + ' subtitleMode=' + subtitleMode);
+ console.debug('Downloading: url=' + url + ' quality=' + quality + ' format=' + format + ' folder=' + folder + ' customNamePrefix=' + customNamePrefix + ' playlistItemLimit=' + playlistItemLimit + ' autoStart=' + autoStart + ' splitByChapters=' + splitByChapters + ' chapterTemplate=' + chapterTemplate + ' subtitleFormat=' + subtitleFormat + ' subtitleLanguage=' + subtitleLanguage + ' subtitleMode=' + subtitleMode + ' customFilename=' + customFilename);
this.addInProgress = true;
this.cancelRequested = false;
- this.downloads.add(url, quality, format, folder, customNamePrefix, playlistItemLimit, autoStart, splitByChapters, chapterTemplate, subtitleFormat, subtitleLanguage, subtitleMode).subscribe((status: Status) => {
+ this.downloads.add(url, quality, format, folder, customNamePrefix, playlistItemLimit, autoStart, splitByChapters, chapterTemplate, subtitleFormat, subtitleLanguage, subtitleMode, customFilename).subscribe((status: Status) => {
if (status.status === 'error' && !this.cancelRequested) {
alert(`Error adding URL: ${status.msg}`);
} else if (status.status !== 'error') {
this.addUrl = '';
+ this.customFilename = '';
}
this.addInProgress = false;
this.cancelRequested = false;
@@ -466,6 +470,7 @@ export class App implements AfterViewInit, OnInit {
download.subtitle_format,
download.subtitle_language,
download.subtitle_mode,
+ download.custom_filename,
);
this.downloads.delById('done', [key]).subscribe();
}
@@ -613,7 +618,7 @@ export class App implements AfterViewInit, OnInit {
// Now pass the selected quality, format, folder, etc. to the add() method
this.downloads.add(url, this.quality, this.format, this.folder, this.customNamePrefix,
this.playlistItemLimit, this.autoStart, this.splitByChapters, this.chapterTemplate,
- this.subtitleFormat, this.subtitleLanguage, this.subtitleMode)
+ this.subtitleFormat, this.subtitleLanguage, this.subtitleMode, this.customFilename)
.subscribe({
next: (status: Status) => {
if (status.status === 'error') {
diff --git a/ui/src/app/interfaces/download.ts b/ui/src/app/interfaces/download.ts
index 6b41625..dbcce49 100644
--- a/ui/src/app/interfaces/download.ts
+++ b/ui/src/app/interfaces/download.ts
@@ -13,6 +13,7 @@ export interface Download {
subtitle_format?: string;
subtitle_language?: string;
subtitle_mode?: string;
+ custom_filename?: string;
status: string;
msg: string;
percent: number;
diff --git a/ui/src/app/services/downloads.service.ts b/ui/src/app/services/downloads.service.ts
index e94ca9b..0bd83d5 100644
--- a/ui/src/app/services/downloads.service.ts
+++ b/ui/src/app/services/downloads.service.ts
@@ -120,6 +120,7 @@ export class DownloadsService {
subtitleFormat: string,
subtitleLanguage: string,
subtitleMode: string,
+ customFilename: string = '',
) {
return this.http.post('add', {
url: url,
@@ -133,7 +134,8 @@ export class DownloadsService {
chapter_template: chapterTemplate,
subtitle_format: subtitleFormat,
subtitle_language: subtitleLanguage,
- subtitle_mode: subtitleMode
+ subtitle_mode: subtitleMode,
+ custom_filename: customFilename
}).pipe(
catchError(this.handleHTTPError)
);
@@ -183,6 +185,7 @@ export class DownloadsService {
const defaultSubtitleFormat = 'srt';
const defaultSubtitleLanguage = 'en';
const defaultSubtitleMode = 'prefer_manual';
+ const defaultCustomFilename = '';
return new Promise((resolve, reject) => {
this.add(
@@ -198,6 +201,7 @@ export class DownloadsService {
defaultSubtitleFormat,
defaultSubtitleLanguage,
defaultSubtitleMode,
+ defaultCustomFilename,
)
.subscribe({
next: (response) => resolve(response),