Skip to main content

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.

PyPI version Python 3.8+ License: MIT Tests


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:

  1. Combines your text fields into a weighted "soup" string per item
  2. Builds a TF-IDF matrix (rare tokens like director names get higher weight)
  3. 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:

  1. Fork the repo and create a feature branch
  2. Add tests for any new functionality
  3. Ensure pytest tests/ passes
  4. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ottmlt-0.1.0.tar.gz (31.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ottmlt-0.1.0-py3-none-any.whl (27.2 kB view details)

Uploaded Python 3

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

Hashes for ottmlt-0.1.0.tar.gz
Algorithm Hash digest
SHA256 75a56fae5931d322433303516bf7ff8373e091d27863fad8ec39063fbdee906e
MD5 87c427109a777184e915fb15560c4895
BLAKE2b-256 ca171cefe37f0a8bde37ba80497b1ffb0c656709cac5c6b9f09a1aca52565d83

See more details on using hashes here.

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

Hashes for ottmlt-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1fb15c8b445ec3c948eb7e7bb89a2a18e368bf66a320702a6cb048e282202c11
MD5 1cb2e3d8e97fe8e05225a17d626d3bd1
BLAKE2b-256 8e4e00791b6dd01ad31604fc8f1a104bc1156dbdccd1abce111e9dc08b87525b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page