import logging import random import time from datetime import UTC, datetime from urllib.parse import urlparse import httpx from aiohttp import web from aiohttp.web import Request, Response from app.library.ag_utils import ag from app.library.cache import Cache from app.library.config import Config from app.library.router import route from app.library.Utils import validate_url LOG: logging.Logger = logging.getLogger(__name__) IS_REQUESTING_BACKGROUND: bool = False @route("GET", "api/thumbnail/", "get_thumbnail") async def get_thumbnail(request: Request, config: Config) -> Response: """ Get the thumbnail. Args: request (Request): The request object. config (Config): The configuration object. Returns: Response: The response object. """ url = request.query.get("url") if not url: return web.json_response(data={"error": "URL is required."}, status=web.HTTPForbidden.status_code) try: validate_url(url) except ValueError as e: return web.json_response(data={"error": str(e)}, status=web.HTTPForbidden.status_code) try: ytdlp_args = config.get_ytdlp_args() opts = { "proxy": ytdlp_args.get("proxy", None), "headers": { "User-Agent": ytdlp_args.get( "user_agent", request.headers.get("User-Agent", f"YTPTube/{config.app_version}") ), }, } async with httpx.AsyncClient(**opts) as client: LOG.debug(f"Fetching thumbnail from '{url}'.") response = await client.request(method="GET", url=url) return web.Response( body=response.content, headers={ "Content-Type": response.headers.get("Content-Type"), "Pragma": "public", "Access-Control-Allow-Origin": "*", "Cache-Control": f"public, max-age={time.time() + 31536000}", "Expires": time.strftime( "%a, %d %b %Y %H:%M:%S GMT", datetime.fromtimestamp(time.time() + 31536000, tz=UTC).timetuple(), ), }, ) except Exception as e: LOG.error(f"Error fetching thumbnail from '{url}'. '{e}'.") return web.json_response( data={"error": "failed to retrieve the thumbnail."}, status=web.HTTPInternalServerError.status_code ) @route("GET", "api/random/background/", "get_background") async def get_background(request: Request, config: Config, cache: Cache) -> Response: """ Get random background. Args: request (Request): The request object. config (Config): The configuration object. cache (Cache): The cache object. Returns: Response: The response object. """ global IS_REQUESTING_BACKGROUND # noqa: PLW0603 backend = None if IS_REQUESTING_BACKGROUND: return web.Response(status=web.HTTPTooManyRequests.status_code) try: IS_REQUESTING_BACKGROUND = True backend = random.choice(config.pictures_backends) # noqa: S311 CACHE_KEY_BING = "random_background_bing" CACHE_KEY = "random_background" if cache.has(CACHE_KEY) and not request.query.get("force", False): data = await cache.aget(CACHE_KEY) return web.Response( body=data.get("content"), headers={ "X-Cache": "HIT", "X-Cache-TTL": str(await cache.attl(CACHE_KEY)), "X-Image-Via": data.get("backend"), **data.get("headers"), }, ) ytdlp_args = config.get_ytdlp_args() opts = { "proxy": ytdlp_args.get("proxy", None), "headers": { "User-Agent": ytdlp_args.get("user_agent", f"YTPTube/{config.app_version}"), }, } async with httpx.AsyncClient(**opts) as client: if backend.startswith("https://www.bing.com/HPImageArchive.aspx"): if not cache.has(CACHE_KEY_BING): response = await client.request(method="GET", url=backend) if response.status_code != web.HTTPOk.status_code: return web.json_response( data={"error": "failed to retrieve the random background image."}, status=web.HTTPInternalServerError.status_code, ) img_url: str | None = ag(response.json(), "images.0.url") if not img_url: return web.json_response( data={"error": "failed to retrieve the random background image."}, status=web.HTTPInternalServerError.status_code, ) backend = f"https://www.bing.com{img_url}" await cache.aset(key=CACHE_KEY_BING, value=backend, ttl=3600 * 24) else: backend: str = await cache.aget(CACHE_KEY_BING) LOG.debug(f"Requesting random picture from '{backend!s}'.") response = await client.request(method="GET", url=backend, follow_redirects=True) if response.status_code != web.HTTPOk.status_code: return web.json_response( data={"error": "failed to retrieve the random background image."}, status=web.HTTPInternalServerError.status_code, ) data = { "content": response.content, "backend": urlparse(backend).netloc, "headers": { "Content-Type": response.headers.get("Content-Type", "image/jpeg"), "Content-Length": str(len(response.content)), }, } await cache.aset(key=CACHE_KEY, value=data, ttl=3600) LOG.debug(f"Random background image from '{backend!s}' cached.") return web.Response( body=data.get("content"), headers={ "X-Cache": "MISS", "X-Cache-TTL": "3600", "X-Image-Via": data.get("backend"), **data.get("headers"), }, ) except Exception as e: LOG.error(f"Failed to request random background image from '{backend!s}'.'. '{e!s}'.") return web.json_response( data={"error": "failed to retrieve the random background image."}, status=web.HTTPInternalServerError.status_code, ) finally: IS_REQUESTING_BACKGROUND = False