mirror of
https://github.com/alexta69/metube.git
synced 2025-04-05 13:17:37 +03:00
Backend: support "folder" POST param and add config options
This commit is contained in:
parent
fb2e6d71ef
commit
e28458a74f
2 changed files with 28 additions and 12 deletions
|
@ -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')
|
||||||
|
|
33
app/ytdl.py
33
app/ytdl.py
|
@ -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:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue