Files
UnHided/mediaflow_proxy/routes/extractor.py
UrloMythus 7785e8c604 new version
2026-01-11 14:29:22 +01:00

97 lines
4.4 KiB
Python

import logging
from typing import Annotated
from fastapi import APIRouter, Query, HTTPException, Request, Depends, BackgroundTasks
from fastapi.responses import RedirectResponse
from mediaflow_proxy.extractors.base import ExtractorError
from mediaflow_proxy.extractors.factory import ExtractorFactory
from mediaflow_proxy.schemas import ExtractorURLParams
from mediaflow_proxy.utils.cache_utils import get_cached_extractor_result, set_cache_extractor_result
from mediaflow_proxy.utils.http_utils import (
DownloadError,
encode_mediaflow_proxy_url,
get_original_scheme,
ProxyRequestHeaders,
get_proxy_headers,
)
from mediaflow_proxy.utils.base64_utils import process_potential_base64_url
extractor_router = APIRouter()
logger = logging.getLogger(__name__)
async def refresh_extractor_cache(cache_key: str, extractor_params: ExtractorURLParams, proxy_headers: ProxyRequestHeaders):
"""Asynchronously refreshes the extractor cache in the background."""
try:
logger.info(f"Background cache refresh started for key: {cache_key}")
extractor = ExtractorFactory.get_extractor(extractor_params.host, proxy_headers.request)
response = await extractor.extract(extractor_params.destination, **extractor_params.extra_params)
await set_cache_extractor_result(cache_key, response)
logger.info(f"Background cache refresh completed for key: {cache_key}")
except Exception as e:
logger.error(f"Background cache refresh failed for key {cache_key}: {e}")
@extractor_router.head("/video")
@extractor_router.get("/video")
async def extract_url(
extractor_params: Annotated[ExtractorURLParams, Query()],
request: Request,
background_tasks: BackgroundTasks,
proxy_headers: Annotated[ProxyRequestHeaders, Depends(get_proxy_headers)],
):
"""Extract clean links from various video hosting services."""
try:
# Process potential base64 encoded destination URL
processed_destination = process_potential_base64_url(extractor_params.destination)
extractor_params.destination = processed_destination
cache_key = f"{extractor_params.host}_{extractor_params.model_dump_json()}"
response = await get_cached_extractor_result(cache_key)
if response:
logger.info(f"Serving from cache for key: {cache_key}")
# Schedule a background task to refresh the cache without blocking the user
background_tasks.add_task(refresh_extractor_cache, cache_key, extractor_params, proxy_headers)
else:
logger.info(f"Cache miss for key: {cache_key}. Fetching fresh data.")
extractor = ExtractorFactory.get_extractor(extractor_params.host, proxy_headers.request)
response = await extractor.extract(extractor_params.destination, **extractor_params.extra_params)
await set_cache_extractor_result(cache_key, response)
# Ensure the latest request headers are used, even with cached data
if "request_headers" not in response:
response["request_headers"] = {}
response["request_headers"].update(proxy_headers.request)
response["mediaflow_proxy_url"] = str(
request.url_for(response.pop("mediaflow_endpoint")).replace(scheme=get_original_scheme(request))
)
response["query_params"] = response.get("query_params", {})
# Add API password to query params
response["query_params"]["api_password"] = request.query_params.get("api_password")
if "max_res" in request.query_params:
response["query_params"]["max_res"] = request.query_params.get("max_res")
if "no_proxy" in request.query_params:
response["query_params"]["no_proxy"] = request.query_params.get("no_proxy")
if extractor_params.redirect_stream:
stream_url = encode_mediaflow_proxy_url(
**response,
response_headers=proxy_headers.response,
)
return RedirectResponse(url=stream_url, status_code=302)
return response
except DownloadError as e:
logger.error(f"Extraction failed: {str(e)}")
raise HTTPException(status_code=e.status_code, detail=str(e))
except ExtractorError as e:
logger.error(f"Extraction failed: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.exception(f"Extraction failed: {str(e)}")
raise HTTPException(status_code=500, detail=f"Extraction failed: {str(e)}")