from fastapi import APIRouter, Query from database import Movie, NasSource, Season, Series, get_session from metadata import _imdb_suggest router = APIRouter(prefix="/api/search", tags=["search"]) _IMDB_QID = {"movie": "movie", "series": "tvSeries"} def _imdb_ids_for_query(q: str, kind: str) -> list[str]: """Search IMDB with the user query and return matching IMDB IDs.""" items = _imdb_suggest(q) qid = _IMDB_QID.get(kind) return [it["id"] for it in items if it.get("qid") == qid and it.get("id")] def _movie_matches(session, pattern: str, q: str) -> list: """Return movies matching by title in DB, OR via IMDB cross-reference.""" # Direct title match in DB by_title = session.query(Movie).filter(Movie.title.ilike(pattern)).all() found_ids = {m.id for m in by_title} # Cross-language: search IMDB and look up matching IMDB IDs in our DB imdb_ids = _imdb_ids_for_query(q, "movie") if imdb_ids: cross = session.query(Movie).filter( Movie.imdb_id.in_(imdb_ids), Movie.id.notin_(found_ids) if found_ids else Movie.id.isnot(None), ).all() by_title = by_title + cross return sorted(by_title, key=lambda m: m.title)[:50] def _series_matches(session, pattern: str, q: str) -> list: """Return series matching by title in DB, OR via IMDB cross-reference.""" by_title = session.query(Series).filter(Series.title.ilike(pattern)).all() found_ids = {s.id for s in by_title} imdb_ids = _imdb_ids_for_query(q, "series") if imdb_ids: cross = session.query(Series).filter( Series.imdb_id.in_(imdb_ids), Series.id.notin_(found_ids) if found_ids else Series.id.isnot(None), ).all() by_title = by_title + cross return sorted(by_title, key=lambda s: s.title)[:50] @router.get("") def search( q: str = Query(..., min_length=1), type: str = Query("all", pattern="^(all|movie|series)$"), ): session = get_session() try: pattern = f"%{q}%" results = {"movies": [], "series": []} if type in ("all", "movie"): for m in _movie_matches(session, pattern, q): nas = session.query(NasSource).get(m.nas_id) results["movies"].append({ "id": m.id, "title": m.title, "year": m.year, "quality": m.quality, "codec": m.codec, "nas_name": nas.name if nas else None, "file_path": m.file_path, "file_size": m.file_size, "poster_url": m.poster_url, "imdb_id": m.imdb_id, }) if type in ("all", "series"): for s in _series_matches(session, pattern, q): seasons_data = [] for season in sorted(s.seasons, key=lambda x: (x.season_number, x.nas_id)): nas = session.query(NasSource).get(season.nas_id) seasons_data.append({ "season_number": season.season_number, "episodes_count": len(season.episodes), "nas_name": nas.name if nas else None, "nas_id": season.nas_id, }) results["series"].append({ "id": s.id, "title": s.title, "year": s.year, "seasons_count": len(s.seasons), "seasons": seasons_data, "poster_url": s.poster_url, "imdb_id": s.imdb_id, }) total = len(results["movies"]) + len(results["series"]) return {"query": q, "results": results, "total": total} finally: session.close()