from fastapi import APIRouter, HTTPException, Query from app.services.tvdb_auth_service import TvdbAuthService from app.services.tvdb_client import TvdbClient router = APIRouter() @router.get("/auth-status") def auth_status(): service = TvdbAuthService() return service.auth_status() @router.post("/login") def force_login(): service = TvdbAuthService() try: state = service.login_and_store_token() return { "status": "ok", "issued_at": state["issued_at"], "expires_at": state["expires_at"], "renew_after": state["renew_after"], } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/search") def search_series(q: str = Query(..., min_length=1)): """ Eenvoudige smoke test richting TVDB. Deze proxy gebruikt de backend token flow en filtert series uit het resultaat. """ client = TvdbClient() try: # In TVDB v4 wordt search via de API ondersteund; deze proxy is bewust dun. payload = client.get("/search", params={"query": q, "type": "series"}) items = payload.get("data", []) normalized = [] for item in items: name = item.get("name") or item.get("seriesName") or item.get("slug") year = item.get("year") normalized.append( { "id": item.get("tvdb_id") or item.get("id"), "name": name, "year": year, "display_name": f"{name} ({year})" if name and year else name, "raw": item, } ) return {"items": normalized} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) def _series_display_name(name: str | None, year: int | str | None) -> str | None: if name and year: return f"{name} ({year})" return name def _extract_episodes(payload: dict) -> list[dict]: data = payload.get("data") if isinstance(data, list): return data if isinstance(data, dict): episodes = data.get("episodes") if isinstance(episodes, list): return episodes return [] def _fetch_aired_episodes(client: TvdbClient, series_id: int) -> list[dict]: attempts = [ (f"/series/{series_id}/episodes/default", {"seasonType": "aired"}), (f"/series/{series_id}/episodes/official", None), ] errors = [] for path, params in attempts: try: payload = client.get(path, params=params) items = _extract_episodes(payload) if items: return items except Exception as exc: errors.append(f"{path}: {exc}") raise RuntimeError( "Unable to load aired episodes from TVDB; tried known episode endpoints" if not errors else f"Unable to load aired episodes from TVDB ({'; '.join(errors)})" ) @router.get("/series/{series_id}/episodes") def get_series_episodes( series_id: int, order_type: str = Query("aired", min_length=1), ): if order_type.lower() != "aired": raise HTTPException( status_code=400, detail="Unsupported order_type. Only 'aired' is supported in this MVP.", ) client = TvdbClient() try: series_payload = client.get(f"/series/{series_id}") series_data = series_payload.get("data", {}) if isinstance(series_payload, dict) else {} series_name = ( series_data.get("name") or series_data.get("seriesName") or series_data.get("slug") ) series_year = series_data.get("year") episodes = _fetch_aired_episodes(client, series_id) normalized = [] for item in episodes: season_number = ( item.get("seasonNumber") or item.get("airedSeason") or item.get("season") or 0 ) episode_number = ( item.get("number") or item.get("airedEpisodeNumber") or item.get("episodeNumber") or 0 ) title = item.get("name") or item.get("episodeName") or "" aired_on = item.get("aired") or item.get("firstAired") label = None try: season_num = int(season_number) episode_num = int(episode_number) label = f"S{season_num:02}E{episode_num:02} - {title}" if aired_on: label = f"{label} - {aired_on}" except (TypeError, ValueError): pass normalized.append( { "id": item.get("id"), "season_number": season_number, "episode_number": episode_number, "title": title, "aired": aired_on, "label": label, "raw": item, } ) return { "series": { "id": series_data.get("id") or series_id, "name": series_name, "year": series_year, "display_name": _series_display_name(series_name, series_year), }, "order_type": "aired", "items": normalized, } except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e))