Added add_batch endpoint.

This commit is contained in:
ArabCoders 2023-11-25 14:33:56 +03:00
parent b7bf9627a1
commit 83745c1f98
4 changed files with 78 additions and 20 deletions

View file

@ -12,8 +12,6 @@ import logging
import caribou
import sqlite3
log = logging.getLogger('main')
class Main:
config: Config = None
@ -87,7 +85,6 @@ class Main:
self.start()
def start(self):
log.info(f"Listening on {self.config.host}:{self.config.port}")
web.run_app(
self.app,
host=self.config.host,
@ -118,7 +115,7 @@ class Main:
if ytdlp_config is None:
ytdlp_config = {}
status = await self.dqueue.add(
status = await self.add(
url=url,
quality=quality,
format=format,
@ -130,6 +127,33 @@ class Main:
return web.Response(text=self.serializer.encode(status))
@self.routes.post(self.config.url_prefix + 'add_batch')
async def add_batch(request: Request) -> Response:
status = {}
post = await request.json()
if not isinstance(post, list):
raise web.HTTPBadRequest()
for item in post:
if not isinstance(item, dict):
raise web.HTTPBadRequest(
'Invalid request body expecting list with dicts.')
if not item.get('url'):
raise web.HTTPBadRequest(reason='url is required')
status[item.get('url')] = await self.add(
url=item.get('url'),
quality=item.get('quality'),
format=item.get('format'),
folder=item.get('folder'),
ytdlp_cookies=item.get('ytdlp_cookies'),
ytdlp_config=item.get('ytdlp_config'),
output_template=item.get('output_template')
)
return web.Response(text=self.serializer.encode(status))
@self.routes.delete(self.config.url_prefix + 'delete')
async def delete(request: Request) -> Response:
post = await request.json()
@ -208,6 +232,31 @@ class Main:
'Could not find the frontend UI static assets. Please run `node_modules/.bin/ng build` inside the frontend folder.') from e
raise e
async def add(
self,
url: str,
quality: str,
format: str,
folder: str,
ytdlp_cookies: str,
ytdlp_config: dict,
output_template: str
) -> dict[str, str]:
if ytdlp_config is None:
ytdlp_config = {}
status = await self.dqueue.add(
url=url,
quality=quality,
format=format,
folder=folder,
ytdlp_cookies=ytdlp_cookies,
ytdlp_config=ytdlp_config,
output_template=output_template
)
return status
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)

View file

@ -3,9 +3,7 @@ import logging
import os
import re
import sys
log = logging.getLogger('config')
import coloredlogs
class Config:
config_path: str = '.'
@ -32,6 +30,8 @@ class Config:
base_path: str = ''
logging_level: str = 'info'
_boolean_vars: tuple = ('keep_archive', 'ytdl_debug')
def __init__(self):
@ -59,7 +59,7 @@ class Config:
for key in re.findall(r'\{.*?\}', v):
localKey: str = key[1:-1]
if localKey not in self.__dict__:
log.error(
logging.error(
f'Config variable "{k}" had non exisitng config reference "{key}"')
sys.exit(1)
v = v.replace(key, getattr(self, localKey))
@ -68,27 +68,41 @@ class Config:
if k in self._boolean_vars:
if str(v).lower() not in (True, False, 'true', 'false', 'on', 'off', '1', '0'):
log.error(
raise ValueError(
f'Config variable "{k}" is set to a non-boolean value "{v}".')
sys.exit(1)
setattr(self, k, str(v).lower() in (True, 'true', 'on', '1'))
if not self.url_prefix.endswith('/'):
self.url_prefix += '/'
numeric_level = getattr(
logging, self.logging_level.upper(), None)
if not isinstance(numeric_level, int):
raise ValueError(f"Invalid log level: {self.logging_level}")
logging.basicConfig(
force=True,
level=numeric_level,
format="%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s"
)
coloredlogs.install()
if isinstance(self.ytdl_options, str):
try:
self.ytdl_options = json.loads(self.ytdl_options)
assert isinstance(self.ytdl_options, dict)
except (json.decoder.JSONDecodeError, AssertionError) as e:
log.error(f'JSON error in "YTP_YTDL_OPTIONS": {e}')
logging.error(f'JSON error in "YTP_YTDL_OPTIONS": {e}')
sys.exit(1)
if self.ytdl_options_file:
log.info(
logging.info(
f'Loading yt-dlp custom options from "{self.ytdl_options_file}"')
if not os.path.exists(self.ytdl_options_file):
log.error(
logging.error(
f'"YTP_YTDL_OPTIONS_FILE" ENV points to non-existent file: "{self.ytdl_options_file}"')
else:
try:
@ -97,7 +111,8 @@ class Config:
assert isinstance(opts, dict)
self.ytdl_options.update(opts)
except (json.decoder.JSONDecodeError, AssertionError) as e:
log.error(f'JSON error in "{self.ytdl_options_file}": {e}')
logging.error(
f'JSON error in "{self.ytdl_options_file}": {e}')
sys.exit(1)
if self.keep_archive:

View file

@ -1,6 +1,5 @@
from collections import OrderedDict
import json
import logging
import sqlite3
from src.Utils import calcDownloadPath
from src.Config import Config
@ -55,7 +54,6 @@ class DataStore:
)
for row in cursor:
logging.debug(row)
data: dict = json.loads(row['data'])
key: str = data.pop('_id')
item: ItemDTO = ItemDTO(**data)

View file

@ -77,7 +77,6 @@ def get_opts(format: str, quality: str, ytdl_opts: dict) -> dict:
Returns:
ytdl_opts: Extra options
"""
logging.debug(f"get_opts: {format} {quality} {ytdl_opts}")
opts = copy.deepcopy(ytdl_opts)
if not opts:
opts: dict = {
@ -107,8 +106,6 @@ def get_opts(format: str, quality: str, ytdl_opts: dict) -> dict:
postprocessors.append(
{"key": "FFmpegThumbnailsConvertor", "format": "jpg", "when": "before_dl"})
logging.debug(
f"Postprocessors: {opts['postprocessors']} + {postprocessors}")
opts["postprocessors"] = postprocessors + \
(opts["postprocessors"] if "postprocessors" in opts else [])
return opts
@ -119,7 +116,6 @@ def getVideoInfo(url: str, ytdlp_opts: dict = None) -> (Any | dict[str, Any] | N
'quiet': True,
'no_color': True,
'extract_flat': True,
}
if ytdlp_opts: