Semantic movie search over a Hotstar-style catalog using FastAPI + Qdrant + text embeddings.
- Scrapes movie/show pages (from sitemap discovery) into structured JSON.
- Enriches records with metadata (genre/cast/keywords/language/runtime/rating).
- Embeds records into vectors.
- Stores vectors in Qdrant.
- Serves a search API with lexical boosting on top of vector similarity.
- Provides a lightweight frontend to query results.
backend/main.py— FastAPI app (/search,/health,/docs)backend/scraper.py— async sitemap/content scraper (+ optional TMDB enrichment)backend/enrich_dataset.py— enrich/clean pipeline from CSV resourcesbackend/embedder.py— embedding generation (hf-inferenceor local sentence-transformers)backend/qdrant_db.py— ingest embedded JSON into Qdrantbackend/check_search_quality.py— smoke test for search relevancefrontend/— static HTML/CSS/JS search UIdata/— source and generated datasetsqdrant_storage/— local Qdrant persistence
- Python 3.10+
- A running Qdrant instance (for example
https://your-qdrant-host:6333) - Hugging Face token (
HF_TOKEN) forBAAI/bge-m3inference
Install dependencies:
pip install -r requirements.txtCreate .env in the project root:
HF_TOKEN=your_huggingface_token
HF_MODEL=BAAI/bge-m3
QDRANT_URL=https://your-qdrant-host:6333
QDRANT_COLLECTION=hotstar_catalog
LOG_LEVEL=INFO
# Optional
TMDB_API_KEY=your_tmdb_keyFrom project root:
uvicorn backend.main:app --host 0.0.0.0 --port 8010 --reloadCheck health:
curl https://your-api-domain/healthServe frontend/ with any static server (for example VS Code Live Server):
- Open your deployed frontend URL (for example
https://your-frontend-domain) - Set Backend URL to your deployed API URL (for example
https://your-api-domain)
curl "https://your-api-domain/search?q=al%20pacino&k=5"Run from project root.
python backend/scraper.py --output data/hotstar_scraped.json --verboseUseful options:
--discover-latest / --no-discover-latest--region in --region us(repeatable)--sitemap-hint MOVIE --sitemap-hint SHOWS(repeatable)--tmdb-api-key <key>--max-urls 500
python backend/enrich_dataset.py \
--input data/hotstar_scraped.json \
--output data/hotstar_quality_5000_final.json \
--movies-metadata data/movies_metadata.csv \
--keywords data/keywords.csv \
--credits data/credits.csv \
--target-count 5000 \
--verbosepython backend/embedder.py \
--input data/hotstar_quality_5000_final.json \
--output data/hotstar_embedded.json \
--backend hf-inference \
--hf-model BAAI/bge-m3 \
--batch-size 32 \
--verbosepython backend/qdrant_db.py \
--input data/hotstar_embedded.json \
--qdrant-url https://your-qdrant-host:6333 \
--collection hotstar_catalog \
--batch-size 256 \
--verboseReturns service status.
Query params:
q(required): search textk(optional, default5): number of resultstop_k(deprecated alias ofk)year(optional): filter by release yearlanguage(optional): filter by language code (en,hi,ta, ...)
Example:
curl "https://your-api-domain/search?q=crime%20thriller&k=5&language=en"Interactive docs: https://your-api-domain/docs
python backend/check_search_quality.py --base-url https://your-api-domain --top-k 5 --timeout 20Generated examples from live API responses:
To regenerate:
python backend/capture_search_shots.py- Ensure Qdrant collection vector size matches embedding model output (for
BAAI/bge-m3, expected size is 1024 in this project). - The backend requires
HF_TOKENat startup; missing token will fail app initialization. - CORS origins are controlled by
FRONTEND_ORIGINSinbackend/main.py.


