More-Like-This recommendation engine for OTT/Media platforms using NLP
Project description
ottmlt — More-Like-This for OTT Platforms
NLP-powered "more like this" recommendations for streaming / media catalogs. Built by a Data Scientist who spent years improving recommendations at a major OTT platform.
What is it?
ottmlt (OTT More Like This) is a lightweight Python library that answers the question:
"A user just finished watching Inception. What should we show them next?"
It analyses your content catalog — titles, descriptions, genres, cast, directors — and finds the most semantically similar items using NLP techniques.
User watches: Inception (2010)
↓
ottmlt finds: The Dark Knight (2008) — same director, same genre
Interstellar (2014) — same director, sci-fi
The Prestige (2006) — same director, same lead
The Matrix (1999) — same genre, same themes
...
Why ottmlt?
| Feature | ottmlt |
|---|---|
| Zero GPU required | TF-IDF model runs on any machine |
| Semantic search | Optional embedding model (sentence-transformers) |
| OTT-aware | Designed for title/genre/cast/director metadata |
| Filtering | Restrict results by genre, language, type, etc. |
| Simple API | fit() → recommend() — that's it |
| PyPI installable | pip install ottmlt |
| Open source | MIT licensed |
Installation
# Minimal install (TF-IDF only — recommended for most use cases)
pip install ottmlt
# With semantic embedding support (requires ~90 MB model download)
pip install "ottmlt[embedding]"
Requirements: Python 3.8+, numpy, pandas, scikit-learn, scipy
Quick Start
import pandas as pd
from ottmlt import MoreLikeThis
from ottmlt.utils.data import load_sample_catalog
# 1. Load your catalog (must have an 'id' column + text fields)
catalog = load_sample_catalog() # bundled 50-title sample
# -- or --
catalog = pd.read_csv("my_catalog.csv") # your real OTT catalog
# 2. Create and fit the recommender
mlt = MoreLikeThis(
text_fields=["title", "description", "genre", "cast", "director"],
field_weights={"title": 3, "genre": 2, "director": 2}, # boost important fields
)
mlt.fit(catalog)
# 3. Get recommendations
recs = mlt.recommend("tt0468569", n=5) # The Dark Knight
print(recs[["id", "title", "genre", "similarity_score"]])
Output:
id title genre similarity_score
0 tt0372784 Batman Begins Action|Adventure 0.8123
1 tt1375666 Inception Action|Sci-Fi 0.6541
2 tt0482571 The Prestige Drama|Sci-Fi 0.6102
3 tt0816692 Interstellar Adventure|Sci-Fi 0.5873
4 tt0110912 Pulp Fiction Crime|Drama 0.3124
Catalog Format
Your catalog should be a pandas.DataFrame. The only required column is id.
Any text columns can be used for similarity.
| Column | Type | Description | Example |
|---|---|---|---|
id |
str/int | Unique item identifier | "tt0468569" |
title |
str | Content title | "The Dark Knight" |
description |
str | Synopsis or plot summary | "When the Joker..." |
genre |
str | Genre tags | "Action|Crime|Drama" |
cast |
str | Actor names | "Christian Bale|Heath Ledger" |
director |
str | Director name(s) | "Christopher Nolan" |
language |
str | Content language | "English" |
content_type |
str | Movie / Series / etc. | "Movie" |
Pipe-separated (|) or comma-separated values in text fields are handled automatically.
Models
1. TF-IDF (default)
The fastest model — no GPU, no large downloads. Excellent for catalogs where genre tags and director names are consistent.
mlt = MoreLikeThis(model="tfidf") # default
How it works:
- Combines your text fields into a weighted "soup" string per item
- Builds a TF-IDF matrix (rare tokens like director names get higher weight)
- Uses cosine similarity to find nearest neighbours
2. Embedding (semantic)
Better at finding thematically similar content, even when they share no keywords.
pip install "ottmlt[embedding]"
mlt = MoreLikeThis(model="embedding")
Uses all-MiniLM-L6-v2 (384-dim, sentence-transformers) by default.
Configurable via model_kwargs:
mlt = MoreLikeThis(
model="embedding",
model_name="all-mpnet-base-v2", # larger, slower, more accurate
batch_size=128,
)
3. Hybrid
Best of both worlds — blends TF-IDF keyword overlap with semantic similarity.
mlt = MoreLikeThis(
model="hybrid",
alpha=0.6, # 60% TF-IDF + 40% Embedding
)
alpha=1.0 → pure TF-IDF. alpha=0.0 → pure Embedding.
If sentence-transformers is not installed, automatically falls back to TF-IDF.
API Reference
MoreLikeThis
MoreLikeThis(
text_fields=["title", "description", "genre", "cast", "director"],
field_weights={"title": 3, "genre": 2, "director": 2},
model="tfidf", # "tfidf" | "embedding" | "hybrid"
id_col="id", # column used as item identifier
**model_kwargs # forwarded to the underlying model
)
.fit(catalog: pd.DataFrame) → self
Fit the recommender on your catalog. Must contain id_col column.
.recommend(item_id, n=10, filters=None) → pd.DataFrame
Return the top-N most similar items. Returns catalog rows + similarity_score.
# Basic
recs = mlt.recommend("tt0468569", n=10)
# With filters — only recommend English Drama films
recs = mlt.recommend(
"tt0468569",
n=10,
filters={"language": "English", "genre": "Drama"},
)
# filters support lists too
recs = mlt.recommend(
"tt0468569",
n=10,
filters={"language": ["English", "French"]},
)
.get_item(item_id) → pd.Series
Return the catalog row for a single item.
.catalog property
Access the fitted catalog as a DataFrame.
Preprocessor
from ottmlt.core.preprocessor import Preprocessor
prep = Preprocessor(
text_fields=["title", "genre"],
field_weights={"title": 3},
)
soups = prep.transform(catalog_df) # list of str, one per row
TFIDFRecommender (low-level)
from ottmlt.models.tfidf import TFIDFRecommender
model = TFIDFRecommender(
max_features=None, # vocabulary size limit (None = unlimited)
ngram_range=(1, 2), # unigrams + bigrams
min_df=1, # minimum document frequency
sublinear_tf=True, # log-scale TF
)
model.fit(list_of_soups)
results = model.get_similar(query_idx=0, candidate_indices=[1,2,3,...], top_n=10)
# Returns: [(idx, score), ...]
Advanced Usage
Large catalogs (50k+ titles)
For catalogs with 50,000+ titles, limit the vocabulary to keep memory low:
mlt = MoreLikeThis(
model="tfidf",
max_features=50_000, # forwarded to TFIDFRecommender
ngram_range=(1, 1), # unigrams only — faster
)
Pre-filtering candidates
Use filters to restrict the recommendation pool before similarity scoring.
This is useful for "more like this but in the same language":
recs = mlt.recommend(
seed_id,
n=10,
filters={"language": "Hindi", "content_type": "Series"},
)
Validating your catalog
from ottmlt.utils.data import validate_catalog
validate_catalog(
catalog,
text_fields=["title", "description", "genre"],
id_col="id",
)
# Raises ValueError on duplicate IDs or missing id_col
# Warns about missing text fields or high null rates
How to Publish Your Own Library to PyPI
This section is for contributors and anyone learning to package Python libraries.
Step 1 — Structure your project
ottmlt/
├── ottmlt/ ← your package code
│ └── __init__.py
├── tests/
├── pyproject.toml ← modern packaging config (PEP 517/518)
├── README.md
└── LICENSE
Step 2 — pyproject.toml (the modern way)
[build-system]
requires = ["setuptools>=61", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "your-package-name"
version = "0.1.0"
description = "One line about what it does"
readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.8"
dependencies = ["numpy", "pandas"]
[project.urls]
Homepage = "https://github.com/you/your-package"
Step 3 — Build distribution files
pip install build
python -m build
# Creates: dist/your_package-0.1.0-py3-none-any.whl
# dist/your_package-0.1.0.tar.gz
Step 4 — Test on TestPyPI first
pip install twine
twine upload --repository testpypi dist/*
# Anyone can now: pip install --index-url https://test.pypi.org/simple/ your-package
Step 5 — Publish to real PyPI
twine upload dist/*
# Register at https://pypi.org first and use API tokens (not passwords)
Step 6 — Automate with GitHub Actions
Create .github/workflows/publish.yml:
name: Publish to PyPI
on:
push:
tags: ["v*"] # triggers on git tag like v0.1.0
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.11" }
- run: pip install build twine
- run: python -m build
- run: twine upload dist/*
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
Tag a release → PyPI auto-updates:
git tag v0.1.1
git push origin v0.1.1
Project Structure
ottmlt/
├── ottmlt/
│ ├── __init__.py # Public API exports
│ ├── core/
│ │ ├── preprocessor.py # Text cleaning + field-weighted soup builder
│ │ ├── similarity.py # Efficient top-N cosine similarity (no N×N matrix)
│ │ └── recommender.py # MoreLikeThis — the high-level API
│ ├── models/
│ │ ├── tfidf.py # TF-IDF vectorizer-based model
│ │ ├── embedding.py # Sentence-Transformer based model
│ │ └── hybrid.py # Weighted blend of TF-IDF + Embedding
│ ├── utils/
│ │ └── data.py # load_sample_catalog(), validate_catalog()
│ └── data/
│ └── sample_catalog.csv # 50-title OTT sample dataset
├── tests/
│ ├── test_preprocessor.py
│ ├── test_models.py
│ └── test_recommender.py
├── pyproject.toml
├── LICENSE
└── README.md
Development
git clone https://github.com/yourusername/ottmlt.git
cd ottmlt
pip install -e ".[dev]"
pytest tests/ -v
Contributing
Pull requests are welcome! Please:
- Fork the repo and create a feature branch
- Add tests for any new functionality
- Ensure
pytest tests/passes - Submit a PR with a clear description
Roadmap
- Collaborative filtering support (user-item interactions)
- BM25 model option
- Incremental
partial_fit()for streaming catalog updates - REST API / FastAPI example server
- Readthedocs documentation
License
MIT — see LICENSE.
Built with love by a Data Scientist who spent years building recommendation systems for OTT platforms.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ottmlt-0.1.0.tar.gz.
File metadata
- Download URL: ottmlt-0.1.0.tar.gz
- Upload date:
- Size: 31.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
75a56fae5931d322433303516bf7ff8373e091d27863fad8ec39063fbdee906e
|
|
| MD5 |
87c427109a777184e915fb15560c4895
|
|
| BLAKE2b-256 |
ca171cefe37f0a8bde37ba80497b1ffb0c656709cac5c6b9f09a1aca52565d83
|
File details
Details for the file ottmlt-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ottmlt-0.1.0-py3-none-any.whl
- Upload date:
- Size: 27.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1fb15c8b445ec3c948eb7e7bb89a2a18e368bf66a320702a6cb048e282202c11
|
|
| MD5 |
1cb2e3d8e97fe8e05225a17d626d3bd1
|
|
| BLAKE2b-256 |
8e4e00791b6dd01ad31604fc8f1a104bc1156dbdccd1abce111e9dc08b87525b
|