Backend: support "folder" POST param and add config options

This commit is contained in:
James Woglom 2022-08-29 18:25:29 -04:00
parent fb2e6d71ef
commit e28458a74f
2 changed files with 28 additions and 12 deletions

View file

@ -16,11 +16,13 @@ class Config:
_DEFAULTS = { _DEFAULTS = {
'DOWNLOAD_DIR': '.', 'DOWNLOAD_DIR': '.',
'AUDIO_DOWNLOAD_DIR': '%%DOWNLOAD_DIR', 'AUDIO_DOWNLOAD_DIR': '%%DOWNLOAD_DIR',
'CUSTOM_DIR': 'true',
'AUTO_CREATE_CUSTOM_DIR': 'false',
'STATE_DIR': '.', 'STATE_DIR': '.',
'URL_PREFIX': '', 'URL_PREFIX': '',
'OUTPUT_TEMPLATE': '%(title)s.%(ext)s', 'OUTPUT_TEMPLATE': '%(title)s.%(ext)s',
'OUTPUT_TEMPLATE_CHAPTER': '%(title)s - %(section_number)s %(section_title)s.%(ext)s', 'OUTPUT_TEMPLATE_CHAPTER': '%(title)s - %(section_number)s %(section_title)s.%(ext)s',
'YTDL_OPTIONS': '{}', 'YTDL_OPTIONS': '{}'
} }
def __init__(self): def __init__(self):
@ -80,7 +82,8 @@ async def add(request):
if not url or not quality: if not url or not quality:
raise web.HTTPBadRequest() raise web.HTTPBadRequest()
format = post.get('format') format = post.get('format')
status = await dqueue.add(url, quality, format) folder = post.get('folder')
status = await dqueue.add(url, quality, format, folder)
return web.Response(text=serializer.encode(status)) return web.Response(text=serializer.encode(status))
@routes.post(config.URL_PREFIX + 'delete') @routes.post(config.URL_PREFIX + 'delete')

View file

@ -27,10 +27,11 @@ class DownloadQueueNotifier:
raise NotImplementedError raise NotImplementedError
class DownloadInfo: class DownloadInfo:
def __init__(self, id, title, url, quality, format): def __init__(self, id, title, url, quality, format, folder):
self.id, self.title, self.url = id, title, url self.id, self.title, self.url = id, title, url
self.quality = quality self.quality = quality
self.format = format self.format = format
self.folder = folder
self.status = self.msg = self.percent = self.speed = self.eta = None self.status = self.msg = self.percent = self.speed = self.eta = None
self.filename = None self.filename = None
self.timestamp = time.time_ns() self.timestamp = time.time_ns()
@ -192,7 +193,7 @@ class DownloadQueue:
async def __import_queue(self): async def __import_queue(self):
for k, v in self.queue.saved_items(): for k, v in self.queue.saved_items():
await self.add(v.url, v.quality, v.format) await self.add(v.url, v.quality, v.format, folder=v.folder)
async def initialize(self): async def initialize(self):
self.event = asyncio.Event() self.event = asyncio.Event()
@ -207,7 +208,7 @@ class DownloadQueue:
**self.config.YTDL_OPTIONS, **self.config.YTDL_OPTIONS,
}).extract_info(url, download=False) }).extract_info(url, download=False)
async def __add_entry(self, entry, quality, format, already): async def __add_entry(self, entry, quality, format, already, folder=None):
etype = entry.get('_type') or 'video' etype = entry.get('_type') or 'video'
if etype == 'playlist': if etype == 'playlist':
entries = entry['entries'] entries = entry['entries']
@ -220,14 +221,26 @@ class DownloadQueue:
for property in ("id", "title", "uploader", "uploader_id"): for property in ("id", "title", "uploader", "uploader_id"):
if property in entry: if property in entry:
etr[f"playlist_{property}"] = entry[property] etr[f"playlist_{property}"] = entry[property]
results.append(await self.__add_entry(etr, quality, format, already)) results.append(await self.__add_entry(etr, quality, format, already, folder=folder))
if any(res['status'] == 'error' for res in results): 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': 'error', 'msg': ', '.join(res['msg'] for res in results if res['status'] == 'error' and 'msg' in res)}
return {'status': 'ok'} return {'status': 'ok'}
elif etype == 'video' or etype.startswith('url') and 'id' in entry and 'title' in entry: elif etype == 'video' or etype.startswith('url') and 'id' in entry and 'title' in entry:
if not self.queue.exists(entry['id']): if not self.queue.exists(entry['id']):
dl = DownloadInfo(entry['id'], entry['title'], entry.get('webpage_url') or entry['url'], quality, format) dl = DownloadInfo(entry['id'], entry['title'], entry.get('webpage_url') or entry['url'], quality, format, folder)
dldirectory = self.config.DOWNLOAD_DIR if (quality != 'audio' and format != 'mp3') else self.config.AUDIO_DOWNLOAD_DIR base_directory = self.config.DOWNLOAD_DIR if (quality != 'audio' and format != 'mp3') else self.config.AUDIO_DOWNLOAD_DIR
if folder:
if self.config.CUSTOM_DIR != 'true':
return {'status': 'error', 'msg': f'A folder for the download was specified but CUSTOM_DIR is not true in the configuration.'}
dldirectory = os.path.realpath(os.path.join(base_directory, folder))
if not dldirectory.startswith(base_directory):
return {'status': 'error', 'msg': f'Folder "{folder}" must resolve inside the base download directory "{base_directory}"'}
if not os.path.isdir(dldirectory):
if self.config.AUTO_CREATE_CUSTOM_DIR != 'true':
return {'status': 'error', 'msg': f'Folder "{folder}" for download does not exist, and AUTO_CREATE_CUSTOM_DIR is not true in the configuration.'}
os.makedirs(dldirectory, exist_ok=True)
else:
dldirectory = base_directory
output = self.config.OUTPUT_TEMPLATE output = self.config.OUTPUT_TEMPLATE
output_chapter = self.config.OUTPUT_TEMPLATE_CHAPTER output_chapter = self.config.OUTPUT_TEMPLATE_CHAPTER
for property, value in entry.items(): for property, value in entry.items():
@ -238,11 +251,11 @@ class DownloadQueue:
await self.notifier.added(dl) await self.notifier.added(dl)
return {'status': 'ok'} return {'status': 'ok'}
elif etype.startswith('url'): elif etype.startswith('url'):
return await self.add(entry['url'], quality, format, already) return await self.add(entry['url'], quality, format, already, folder=folder)
return {'status': 'error', 'msg': f'Unsupported resource "{etype}"'} return {'status': 'error', 'msg': f'Unsupported resource "{etype}"'}
async def add(self, url, quality, format, already=None): async def add(self, url, quality, format, already=None, folder=None):
log.info(f'adding {url}') log.info(f'adding {url}: {quality=} {format=} {already=} {folder=}')
already = set() if already is None else already already = set() if already is None else already
if url in already: if url in already:
log.info('recursion detected, skipping') log.info('recursion detected, skipping')
@ -253,7 +266,7 @@ class DownloadQueue:
entry = await asyncio.get_running_loop().run_in_executor(None, self.__extract_info, url) entry = await asyncio.get_running_loop().run_in_executor(None, self.__extract_info, url)
except yt_dlp.utils.YoutubeDLError as exc: except yt_dlp.utils.YoutubeDLError as exc:
return {'status': 'error', 'msg': str(exc)} return {'status': 'error', 'msg': str(exc)}
return await self.__add_entry(entry, quality, format, already) return await self.__add_entry(entry, quality, format, already, folder=folder)
async def cancel(self, ids): async def cancel(self, ids):
for id in ids: for id in ids: