import asyncio import json import logging from datetime import datetime, timedelta, timezone from typing import List, Optional import dropbox from modules.dropbox.client import dbx from fastapi import HTTPException # Logger logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) # cache = { audible_path: {"url": ..., "expiry": ...} } audible_art_cache: dict[str, dict] = {} AUDIBLE_ART_CACHE_TTL = timedelta(hours=3, minutes=30) async def get_audible_art_url(art_path: str): """ Returns a temporary Dropbox download URL for an audible art file. Uses in-memory caching to avoid regenerating links too frequently. """ if not art_path: raise HTTPException(status_code=400, detail="art_path is required") # Normalize path (ensure leading slash) dropbox_path = ( art_path if art_path.startswith("/") else f"/{art_path}" ) now = datetime.now(timezone.utc) # 1️⃣ Check cache cached = audible_art_cache.get(dropbox_path) if cached and cached["expiry"] > now: return {"art_url": cached["url"]} # 2️⃣ Generate fresh Dropbox temp link try: temp_link = dbx.files_get_temporary_link(dropbox_path).link except dropbox.exceptions.ApiError: raise HTTPException(status_code=404, detail="Audible art not found") # 3️⃣ Cache it audible_art_cache[dropbox_path] = { "url": temp_link, "expiry": now + AUDIBLE_ART_CACHE_TTL, } return {"art_url": temp_link} async def cleanup_audible_art_cache(interval_seconds: int = 600): while True: now = datetime.now(timezone.utc) expired = [ k for k, v in audible_art_cache.items() if v["expiry"] <= now ] for k in expired: del audible_art_cache[k] await asyncio.sleep(interval_seconds)