diff --git a/app/main.py b/app/main.py index 6fc46c6..d59b7c1 100644 --- a/app/main.py +++ b/app/main.py @@ -94,7 +94,10 @@ async def add(request): raise web.HTTPBadRequest() format = post.get('format') folder = post.get('folder') - status = await dqueue.add(url, quality, format, folder) + custom_name_prefix = post.get('custom_name_prefix') + if custom_name_prefix is None: + custom_name_prefix = '' + status = await dqueue.add(url, quality, format, folder, custom_name_prefix) return web.Response(text=serializer.encode(status)) @routes.post(config.URL_PREFIX + 'delete') diff --git a/app/ytdl.py b/app/ytdl.py index c001a16..0088131 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -28,13 +28,14 @@ class DownloadQueueNotifier: raise NotImplementedError class DownloadInfo: - def __init__(self, id, title, url, quality, format, folder): - self.id, self.title, self.url = id, title, url + def __init__(self, id, title, url, quality, format, folder, custom_name_prefix): + 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}' + self.url = url self.quality = quality self.format = format self.folder = folder self.status = self.msg = self.percent = self.speed = self.eta = None - self.filename = None self.timestamp = time.time_ns() class Download: @@ -213,7 +214,7 @@ class DownloadQueue: **self.config.YTDL_OPTIONS, }).extract_info(url, download=False) - async def __add_entry(self, entry, quality, format, folder, already): + async def __add_entry(self, entry, quality, format, folder, custom_name_prefix, already): etype = entry.get('_type') or 'video' if etype == 'playlist': entries = entry['entries'] @@ -226,13 +227,13 @@ class DownloadQueue: for property in ("id", "title", "uploader", "uploader_id"): if property in entry: etr[f"playlist_{property}"] = entry[property] - results.append(await self.__add_entry(etr, quality, format, folder, already)) + results.append(await self.__add_entry(etr, quality, format, folder, custom_name_prefix, already)) if any(res['status'] == 'error' for res in results): return {'status': 'error', 'msg': ', '.join(res['msg'] for res in results if res['status'] == 'error' and 'msg' in res)} return {'status': 'ok'} elif etype == 'video' or etype.startswith('url') and 'id' in entry and 'title' in entry: if not self.queue.exists(entry['id']): - dl = DownloadInfo(entry['id'], entry['title'], entry.get('webpage_url') or entry['url'], quality, format, folder) + dl = DownloadInfo(entry['id'], entry['title'], entry.get('webpage_url') or entry['url'], quality, format, folder, custom_name_prefix) # Keep consistent with frontend base_directory = self.config.DOWNLOAD_DIR if (quality != 'audio' and format not in AUDIO_FORMATS) else self.config.AUDIO_DOWNLOAD_DIR if folder: @@ -248,7 +249,7 @@ class DownloadQueue: os.makedirs(dldirectory, exist_ok=True) else: dldirectory = base_directory - output = self.config.OUTPUT_TEMPLATE + output = self.config.OUTPUT_TEMPLATE if len(custom_name_prefix) == 0 else f'{custom_name_prefix}.{self.config.OUTPUT_TEMPLATE}' output_chapter = self.config.OUTPUT_TEMPLATE_CHAPTER for property, value in entry.items(): if property.startswith("playlist"): @@ -258,11 +259,11 @@ class DownloadQueue: await self.notifier.added(dl) return {'status': 'ok'} elif etype.startswith('url'): - return await self.add(entry['url'], quality, format, folder, already) + return await self.add(entry['url'], quality, format, folder, custom_name_prefix, already) return {'status': 'error', 'msg': f'Unsupported resource "{etype}"'} - async def add(self, url, quality, format, folder, already=None): - log.info(f'adding {url}: {quality=} {format=} {already=} {folder=}') + async def add(self, url, quality, format, folder, custom_name_prefix, already=None): + log.info(f'adding {url}: {quality=} {format=} {already=} {folder=} {custom_name_prefix=}') already = set() if already is None else already if url in already: log.info('recursion detected, skipping') @@ -273,7 +274,7 @@ class DownloadQueue: entry = await asyncio.get_running_loop().run_in_executor(None, self.__extract_info, url) except yt_dlp.utils.YoutubeDLError as exc: return {'status': 'error', 'msg': str(exc)} - return await self.__add_entry(entry, quality, format, folder, already) + return await self.__add_entry(entry, quality, format, folder, custom_name_prefix, already) async def cancel(self, ids): for id in ids: diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index 4807064..e23df39 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -56,9 +56,15 @@ Advanced options diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 5dbcd5b..7c7dee0 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -20,6 +20,7 @@ export class AppComponent implements AfterViewInit { quality: string; format: string; folder: string; + customNamePrefix: string; addInProgress = false; darkMode: boolean; customDirs$: Observable; @@ -154,15 +155,16 @@ export class AppComponent implements AfterViewInit { this.quality = exists ? this.quality : 'best' } - addDownload(url?: string, quality?: string, format?: string, folder?: string) { + addDownload(url?: string, quality?: string, format?: string, folder?: string, customNamePrefix?: string) { url = url ?? this.addUrl quality = quality ?? this.quality format = format ?? this.format folder = folder ?? this.folder + customNamePrefix = customNamePrefix ?? this.customNamePrefix - console.debug('Downloading: url='+url+' quality='+quality+' format='+format+' folder='+folder); + console.debug('Downloading: url='+url+' quality='+quality+' format='+format+' folder='+folder+' customNamePrefix='+customNamePrefix); this.addInProgress = true; - this.downloads.add(url, quality, format, folder).subscribe((status: Status) => { + this.downloads.add(url, quality, format, folder, customNamePrefix).subscribe((status: Status) => { if (status.status === 'error') { alert(`Error adding URL: ${status.msg}`); } else { @@ -172,8 +174,8 @@ export class AppComponent implements AfterViewInit { }); } - retryDownload(key: string, url: string, quality: string, format: string, folder: string) { - this.addDownload(url, quality, format, folder); + retryDownload(key: string, url: string, quality: string, format: string, folder: string, customNamePrefix: string) { + this.addDownload(url, quality, format, folder, customNamePrefix); this.downloads.delById('done', [key]).subscribe(); } diff --git a/ui/src/app/downloads.service.ts b/ui/src/app/downloads.service.ts index e1a134d..b4a6108 100644 --- a/ui/src/app/downloads.service.ts +++ b/ui/src/app/downloads.service.ts @@ -97,8 +97,8 @@ export class DownloadsService { return of({status: 'error', msg: msg}) } - public add(url: string, quality: string, format: string, folder: string) { - return this.http.post('add', {url: url, quality: quality, format: format, folder: folder}).pipe( + public add(url: string, quality: string, format: string, folder: string, customNamePrefix: string) { + return this.http.post('add', {url: url, quality: quality, format: format, folder: folder, custom_name_prefix: customNamePrefix}).pipe( catchError(this.handleHTTPError) ); }