Detect credit sequences in video files by analysing per-frame features — entropy, histogram peaks, and text-detection density.
The heuristic is reverse-engineered from Plex Media Scanner's sub_292050:
a 5-phase pipeline that filters candidate frames, clusters them into
continuous runs, scores segments, merges nearby candidates, and applies
duration/score gates. The resulting segment boundaries match what Plex
calls CreditMarker entries.
- Python ≥ 3.10
pydantic≥ 2.0 (data models)- Optional —
opencv-python, a TensorFlow model (model_v1.pb), andffmpegfor direct-video mode
credit-detect does not ship, download, or vendor ffmpeg. It will
use whichever ffmpeg it finds in this order:
- Explicit
--ffmpeg-pathargument $PATHviashutil.which("ffmpeg")- Plex bundled —
/usr/lib/plexmediaserver/Plex Transcoder
If none of these resolve, the tool prints an actionable error and exits.
Install ffmpeg via your system package manager
(apt install ffmpeg, dnf install ffmpeg, brew install ffmpeg,
winget install ffmpeg) or pass --ffmpeg-path to point at an
existing binary.
The DNN model is not distributed with this project. The network
weights are derived from Plex Media Scanner's sub_292050 binary and
their licensing is unclear; redistributing the file would be a
potential IP issue. The architecture is a standard EAST-style text
detector (feature_fusion/Conv_7/Sigmoid output, 80×80 score map,
sigmoid activations), so a model trained on the same task is a drop-in
replacement.
You have three options:
| Option | Effort | Notes |
|---|---|---|
| Extract from Plex (recommended) | ~30 min | Run Plex Media Scanner once, locate the embedded .pb (see below). |
| Train a replacement | hours | Train EAST or CRAFT on the ICDAR-2015 + a custom credits dataset. |
Use --csv mode only |
none | Skip DNN detection entirely; feed pre-extracted features. |
- Install Plex Media Server on any machine (it does not need to scan your library).
- Locate
Plex TranscoderorPlex Media Scanner:- Linux:
/usr/lib/plexmediaserver/ - macOS:
/Applications/Plex Media Server.app/Contents/MacOS/ - Windows:
C:\Program Files\Plex Media Server\
- Linux:
- Extract
model_v1.pbfrom the binary:# (a) Sanity check — confirms the model layer is embedded. # Output of "1" (or any positive number) means the model is in # there; "0" means you have the wrong binary. strings "/usr/lib/plexmediaserver/Plex Media Scanner" \ | grep -c "feature_fusion/Conv_7/Sigmoid" # (b) Actual extraction — produces a directory of embedded files # including model_v1.pb (typically the largest). Without -e # binwalk just lists what's there. binwalk -e "/usr/lib/plexmediaserver/Plex Media Scanner" # Look for the extracted file, e.g.: # ./_Plex Media Scanner.extracted/model_v1.pb (binwalk default) # or use binwalk's -D flag to extract a single filetype: binwalk -e -D 'protobuf.*model' "/usr/lib/plexmediaserver/Plex Media Scanner"
- Point the plugin or CLI at the extracted file via
--model/ModelPathsetting.
Legal note: This extraction step is the user's responsibility. We don't distribute the model and we don't assert any rights over it. If you can't or won't extract it, use the
--csvworkflow — the detection heuristic works identically on the 9-column feature format without any model.
# Analyse from pre-extracted CSV (9-column format, no ffmpeg/model needed)
python credit_detect.py --csv thumbnail_data.csv --output result.json
# Analyse from video directly (requires opencv-python + model + ffmpeg)
python credit_detect.py --video input.mp4 --model model_v1.pb
# Explicit ffmpeg path
python credit_detect.py --video input.mp4 --model model_v1.pb --ffmpeg-path /usr/bin/ffmpeg
# Install and run as a command
pip install .
credit-detect --csv thumbnail_data.csvThe 9-column CSV matches what Plex's FeatureManager exports:
| Column | Type | Description |
|---|---|---|
index |
int | Frame counter (1-based) |
pts |
int | Raw presentation timestamp |
ptsTimeMs |
int | PTS in milliseconds |
log |
float | showinfo log field (unused) |
entropy |
float | Frame entropy |
histogramPeakRatio |
float | Peak bin / total pixels |
numTextDetections |
int | DNN cells ≥ 0.999 confidence |
textXCenter |
float | Average text detection X / 80 |
textYCenter |
float | Average text detection Y / 80 |
{
"MediaContainer": {
"size": 1,
"CreditMarker": [
{
"start_frame": 120,
"end_frame": 450,
"start_pts_ms": 240000,
"end_pts_ms": 900000,
"duration_sec": 660.0,
"score": 0.85,
"avg_entropy": 0.15,
"avg_peak_ratio": 0.3,
"num_frames": 331
}
]
}
}The pipeline in CreditDetector.detect():
- Candidate filter — frames with low entropy (
≤ 0.2) or strong text detection (≥ 9 cells) - Run detection — cluster consecutive candidates where centre position
shifts
≤ 0.01and index gap≤ 2 - Segment scoring — average entropy, peak ratio, and text density into a combined score (halved when entropy ratio exceeds 0.6)
- Merging — join nearby segments (gap
≤ 4always,== 5conditional) - Acceptance — keep segments ≥ 60 s with score ≥ 0.62, or short segments ≥ 3.5 s with score ≥ 0.3
AGPL-3.0-or-later © the credit-detect authors.