import logging import uuid from collections import OrderedDict from aiohttp import web from aiohttp.web import Request, Response from app.library.cache import Cache from app.library.conditions import Condition, Conditions from app.library.config import Config from app.library.encoder import Encoder from app.library.Events import EventBus, Events from app.library.router import route from app.library.Utils import extract_info, init_class, validate_uuid from app.library.YTDLPOpts import YTDLPOpts LOG: logging.Logger = logging.getLogger(__name__) @route("GET", "api/conditions/", "conditions_list") async def conditions_list(encoder: Encoder) -> Response: """ Get the conditions Args: encoder (Encoder): The encoder instance. Returns: Response: The response object. """ return web.json_response( data=Conditions.get_instance().get_all(), status=web.HTTPOk.status_code, dumps=encoder.encode, ) @route("PUT", "api/conditions/", "conditions_add") async def conditions_add(request: Request, encoder: Encoder, notify: EventBus) -> Response: """ Save Conditions. Args: request (Request): The request object. encoder (Encoder): The encoder instance. notify (EventBus): The event bus instance. Returns: Response: The response object """ data = await request.json() if not isinstance(data, list): return web.json_response( {"error": "Invalid request body expecting list with dicts."}, status=web.HTTPBadRequest.status_code, ) items: list = [] cls = Conditions.get_instance() for item in data: if not isinstance(item, dict): return web.json_response( {"error": "Invalid request body expecting list with dicts."}, status=web.HTTPBadRequest.status_code, ) if not item.get("name"): return web.json_response( {"error": "name is required.", "data": item}, status=web.HTTPBadRequest.status_code ) if not item.get("filter"): return web.json_response( {"error": "filter is required.", "data": item}, status=web.HTTPBadRequest.status_code ) if not item.get("cli"): return web.json_response( {"error": "command options for yt-dlp is required.", "data": item}, status=web.HTTPBadRequest.status_code, ) if not item.get("id", None) or not validate_uuid(item.get("id"), version=4): item["id"] = str(uuid.uuid4()) try: cls.validate(item) except ValueError as e: return web.json_response( {"error": f"Failed to validate condition '{item.get('name')}'. '{e!s}'"}, status=web.HTTPBadRequest.status_code, ) items.append(init_class(Condition, item)) try: items = cls.save(items=items).load().get_all() except Exception as e: LOG.exception(e) return web.json_response( {"error": "Failed to save conditions.", "message": str(e)}, status=web.HTTPInternalServerError.status_code, ) await notify.emit(Events.CONDITIONS_UPDATE, data=items) return web.json_response(data=items, status=web.HTTPOk.status_code, dumps=encoder.encode) @route("POST", "api/conditions/test/", "conditions_test") async def conditions_test(request: Request, encoder: Encoder, cache: Cache, config: Config) -> Response: """ Test condition against URL. Args: request (Request): The request object. encoder (Encoder): The encoder instance. cache (Cache): The cache instance. config (Config): The configuration instance. Returns: Response: The response object """ params = await request.json() if not isinstance(params, dict): return web.json_response( {"error": "Invalid request body expecting dict."}, status=web.HTTPBadRequest.status_code, ) url: str | None = params.get("url") if not url: return web.json_response({"error": "url is required."}, status=web.HTTPBadRequest.status_code) cond: str | None = params.get("condition") if not cond: return web.json_response({"error": "condition is required."}, status=web.HTTPBadRequest.status_code) try: preset: str = params.get("preset", config.default_preset) key: str = cache.hash(url + str(preset)) if not cache.has(key): opts = {} if ytdlp_proxy := config.get_ytdlp_args().get("proxy", None): opts["proxy"] = ytdlp_proxy ytdlp_opts = YTDLPOpts.get_instance().preset(name=preset).add(opts).get_all() data = extract_info( config=ytdlp_opts, url=url, debug=False, no_archive=True, follow_redirect=True, sanitize_info=True, ) if not data: return web.json_response( data={"error": f"Failed to extract info from '{url!s}'."}, status=web.HTTPBadRequest.status_code, ) cache.set(key=key, value=data, ttl=600) else: data = cache.get(key) except Exception as e: LOG.exception(e) return web.json_response( data={"error": f"Failed to extract video info. '{e!s}'"}, status=web.HTTPInternalServerError.status_code, ) try: from yt_dlp.utils import match_str status = match_str(cond, data) except Exception as e: LOG.exception(e) return web.json_response( data={"error": str(e)}, status=web.HTTPBadRequest.status_code, ) return web.json_response( data={ "status": status, "condition": cond, "data": OrderedDict(sorted(data.items(), key=lambda item: len(str(item[1])))), }, status=web.HTTPOk.status_code, dumps=encoder.encode, )