ytptube/app/routes/api/docs.py
2026-06-14 18:35:03 +03:00

115 lines
3.8 KiB
Python

import time
from datetime import UTC, datetime
from aiohttp import web
from aiohttp.web import Request, Response
from app.features.ytdlp.ytdlp_opts import YTDLPOpts
from app.library.cache import Cache
from app.library.config import Config
from app.library.httpx_client import Globals, build_request_headers, get_async_client, resolve_curl_transport
from app.library.log import get_logger
from app.library.router import add_route, route
LOG = get_logger()
STATIC_FILES = ["README.md", "FAQ.md", "API.md", "sc_short.jpg", "sc_simple.jpg"]
EXT_TO_MIME: dict = {
".md": "text/markdown",
".png": "image/png",
".jpg": "image/jpeg",
}
@route("GET", "api/docs/{file}", name="get_doc")
async def get_doc(request: Request, config: Config, cache: Cache) -> Response:
"""
Get the thumbnail.
Args:
request (Request): The request object.
config (Config): The configuration object.
cache (Cache): The cache object.
Returns:
Response: The response object.
"""
if not (file := request.path):
return web.json_response(
data={
"error": "Doc file is is required.",
"matcher": request.match_info,
},
status=web.HTTPForbidden.status_code,
)
file = file.removeprefix("/api/docs/") if file.startswith("/api/docs/") else file.removeprefix("/")
if file not in STATIC_FILES:
return web.json_response(
data={
"error": "Doc file not found.",
"file": file,
"st": STATIC_FILES,
},
status=web.HTTPNotFound.status_code,
)
cache_key = f"doc:{file}"
if dct := cache.get(cache_key):
LOG.debug("Serving doc '%s' from cache.", file, extra={"doc_file": file, "cache_key": cache_key})
return web.Response(**dct)
url = f"https://raw.githubusercontent.com/arabcoders/ytptube/refs/heads/dev/{file}"
try:
ytdlp_args: dict = YTDLPOpts.get_instance().preset(name=config.default_preset).get_all()
use_curl = resolve_curl_transport()
request_headers = build_request_headers(
user_agent=request.headers.get("User-Agent", ytdlp_args.get("user_agent", Globals.get_random_agent())),
use_curl=use_curl,
)
proxy = ytdlp_args.get("proxy")
client = get_async_client(proxy=proxy, use_curl=use_curl)
LOG.debug("Fetching doc '%s' from '%s'.", file, url, extra={"route": "docs.get", "doc_file": file, "url": url})
response = await client.request(
method="GET",
url=url,
follow_redirects=True,
headers=request_headers,
)
dct = {
"body": response.content,
"headers": {
"Content-Type": EXT_TO_MIME.get(file[file.rfind(".") :], "text/plain"),
"Pragma": "public",
"Access-Control-Allow-Origin": "*",
"Cache-Control": f"public, max-age={time.time() + 3600}",
"Expires": time.strftime(
"%a, %d %b %Y %H:%M:%S GMT",
datetime.fromtimestamp(time.time() + 3600, tz=UTC).timetuple(),
),
},
}
cache.set(cache_key, dct, ttl=3600)
return web.Response(body=dct["body"], headers=dct["headers"])
except Exception:
LOG.exception(
"Failed to request doc '%s' from '%s'.",
file,
url,
extra={"route": "docs.get", "doc_file": file, "url": url},
)
return web.json_response(data={"error": "Failed to get doc."}, status=web.HTTPInternalServerError.status_code)
for file in STATIC_FILES:
add_route(
method="GET",
path=f"{file}",
handler=get_doc,
name=f"get_{file.replace('.', '_')}",
)