Added add_batch endpoint.
This commit is contained in:
parent
b7bf9627a1
commit
83745c1f98
4 changed files with 78 additions and 20 deletions
57
app/main.py
57
app/main.py
|
|
@ -12,8 +12,6 @@ import logging
|
||||||
import caribou
|
import caribou
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
log = logging.getLogger('main')
|
|
||||||
|
|
||||||
|
|
||||||
class Main:
|
class Main:
|
||||||
config: Config = None
|
config: Config = None
|
||||||
|
|
@ -87,7 +85,6 @@ class Main:
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
log.info(f"Listening on {self.config.host}:{self.config.port}")
|
|
||||||
web.run_app(
|
web.run_app(
|
||||||
self.app,
|
self.app,
|
||||||
host=self.config.host,
|
host=self.config.host,
|
||||||
|
|
@ -118,7 +115,7 @@ class Main:
|
||||||
if ytdlp_config is None:
|
if ytdlp_config is None:
|
||||||
ytdlp_config = {}
|
ytdlp_config = {}
|
||||||
|
|
||||||
status = await self.dqueue.add(
|
status = await self.add(
|
||||||
url=url,
|
url=url,
|
||||||
quality=quality,
|
quality=quality,
|
||||||
format=format,
|
format=format,
|
||||||
|
|
@ -130,6 +127,33 @@ class Main:
|
||||||
|
|
||||||
return web.Response(text=self.serializer.encode(status))
|
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')
|
@self.routes.delete(self.config.url_prefix + 'delete')
|
||||||
async def delete(request: Request) -> Response:
|
async def delete(request: Request) -> Response:
|
||||||
post = await request.json()
|
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
|
'Could not find the frontend UI static assets. Please run `node_modules/.bin/ng build` inside the frontend folder.') from e
|
||||||
raise 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__':
|
if __name__ == '__main__':
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import coloredlogs
|
||||||
log = logging.getLogger('config')
|
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
config_path: str = '.'
|
config_path: str = '.'
|
||||||
|
|
@ -32,6 +30,8 @@ class Config:
|
||||||
|
|
||||||
base_path: str = ''
|
base_path: str = ''
|
||||||
|
|
||||||
|
logging_level: str = 'info'
|
||||||
|
|
||||||
_boolean_vars: tuple = ('keep_archive', 'ytdl_debug')
|
_boolean_vars: tuple = ('keep_archive', 'ytdl_debug')
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -59,7 +59,7 @@ class Config:
|
||||||
for key in re.findall(r'\{.*?\}', v):
|
for key in re.findall(r'\{.*?\}', v):
|
||||||
localKey: str = key[1:-1]
|
localKey: str = key[1:-1]
|
||||||
if localKey not in self.__dict__:
|
if localKey not in self.__dict__:
|
||||||
log.error(
|
logging.error(
|
||||||
f'Config variable "{k}" had non exisitng config reference "{key}"')
|
f'Config variable "{k}" had non exisitng config reference "{key}"')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
v = v.replace(key, getattr(self, localKey))
|
v = v.replace(key, getattr(self, localKey))
|
||||||
|
|
@ -68,27 +68,41 @@ class Config:
|
||||||
|
|
||||||
if k in self._boolean_vars:
|
if k in self._boolean_vars:
|
||||||
if str(v).lower() not in (True, False, 'true', 'false', 'on', 'off', '1', '0'):
|
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}".')
|
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'))
|
setattr(self, k, str(v).lower() in (True, 'true', 'on', '1'))
|
||||||
|
|
||||||
if not self.url_prefix.endswith('/'):
|
if not self.url_prefix.endswith('/'):
|
||||||
self.url_prefix += '/'
|
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):
|
if isinstance(self.ytdl_options, str):
|
||||||
try:
|
try:
|
||||||
self.ytdl_options = json.loads(self.ytdl_options)
|
self.ytdl_options = json.loads(self.ytdl_options)
|
||||||
assert isinstance(self.ytdl_options, dict)
|
assert isinstance(self.ytdl_options, dict)
|
||||||
except (json.decoder.JSONDecodeError, AssertionError) as e:
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
if self.ytdl_options_file:
|
if self.ytdl_options_file:
|
||||||
log.info(
|
logging.info(
|
||||||
f'Loading yt-dlp custom options from "{self.ytdl_options_file}"')
|
f'Loading yt-dlp custom options from "{self.ytdl_options_file}"')
|
||||||
if not os.path.exists(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}"')
|
f'"YTP_YTDL_OPTIONS_FILE" ENV points to non-existent file: "{self.ytdl_options_file}"')
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
|
@ -97,7 +111,8 @@ class Config:
|
||||||
assert isinstance(opts, dict)
|
assert isinstance(opts, dict)
|
||||||
self.ytdl_options.update(opts)
|
self.ytdl_options.update(opts)
|
||||||
except (json.decoder.JSONDecodeError, AssertionError) as e:
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
if self.keep_archive:
|
if self.keep_archive:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import json
|
import json
|
||||||
import logging
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from src.Utils import calcDownloadPath
|
from src.Utils import calcDownloadPath
|
||||||
from src.Config import Config
|
from src.Config import Config
|
||||||
|
|
@ -55,7 +54,6 @@ class DataStore:
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in cursor:
|
for row in cursor:
|
||||||
logging.debug(row)
|
|
||||||
data: dict = json.loads(row['data'])
|
data: dict = json.loads(row['data'])
|
||||||
key: str = data.pop('_id')
|
key: str = data.pop('_id')
|
||||||
item: ItemDTO = ItemDTO(**data)
|
item: ItemDTO = ItemDTO(**data)
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,6 @@ def get_opts(format: str, quality: str, ytdl_opts: dict) -> dict:
|
||||||
Returns:
|
Returns:
|
||||||
ytdl_opts: Extra options
|
ytdl_opts: Extra options
|
||||||
"""
|
"""
|
||||||
logging.debug(f"get_opts: {format} {quality} {ytdl_opts}")
|
|
||||||
opts = copy.deepcopy(ytdl_opts)
|
opts = copy.deepcopy(ytdl_opts)
|
||||||
if not opts:
|
if not opts:
|
||||||
opts: dict = {
|
opts: dict = {
|
||||||
|
|
@ -107,8 +106,6 @@ def get_opts(format: str, quality: str, ytdl_opts: dict) -> dict:
|
||||||
postprocessors.append(
|
postprocessors.append(
|
||||||
{"key": "FFmpegThumbnailsConvertor", "format": "jpg", "when": "before_dl"})
|
{"key": "FFmpegThumbnailsConvertor", "format": "jpg", "when": "before_dl"})
|
||||||
|
|
||||||
logging.debug(
|
|
||||||
f"Postprocessors: {opts['postprocessors']} + {postprocessors}")
|
|
||||||
opts["postprocessors"] = postprocessors + \
|
opts["postprocessors"] = postprocessors + \
|
||||||
(opts["postprocessors"] if "postprocessors" in opts else [])
|
(opts["postprocessors"] if "postprocessors" in opts else [])
|
||||||
return opts
|
return opts
|
||||||
|
|
@ -119,7 +116,6 @@ def getVideoInfo(url: str, ytdlp_opts: dict = None) -> (Any | dict[str, Any] | N
|
||||||
'quiet': True,
|
'quiet': True,
|
||||||
'no_color': True,
|
'no_color': True,
|
||||||
'extract_flat': True,
|
'extract_flat': True,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ytdlp_opts:
|
if ytdlp_opts:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue