feat: add intro and credits detection with Plex marker support#191
feat: add intro and credits detection with Plex marker support#191aweigold wants to merge 5 commits into
Conversation
Code Review: PR #191 — Intro & Credits DetectionThanks for the PR. Architecture is clear, the DB safety layer is strong, and shipping both features off by default with an Experimental label is the right call. Below: what should change and why, grouped by severity. Critical1. Fingerprint store attached via
2. Intro detection compares only the first episode to all others
UX / logging / observability3. Detection is hard to “see” during a job BIF work has FFmpeg %, ETA, and worker updates. Detection runs after BIF (or on BIF skip) with little or no user-visible phase.
4. Intro pass 2 can sit at “100%” while still working Pass 2 runs after the main queue; the job may look complete while CPU is still busy comparing seasons.
5. Job outcome / dashboard doesn’t reflect detection Outcome tooltips and summaries are built around
6. Most useful messages are Many paths log skips and “no match” at debug, so default logs look empty.
Significant7. No server-side validation of numeric settings UI has min/max; API can still send bad values.
8. Duplicate Marker check and show/season metadata can use one fetch.
9. Credits heuristic false positives “Earliest black+silence after 75%” can match scene transitions, not credits.
Minor10. 11. 12. Fingerprint compare: Pure Python may be slow on huge seasons — acceptable for v1; optimize if needed. What’s already strong
Disclaimer: This review was generated with LLM assistance from a user-provided review guide; it is not a substitute for maintainer review and real Plex/client testing. |
|
Good notes, will incorporate next time I'm at the keyboard. |
b62d4bc to
4a88294
Compare
Critical: - Thread fingerprint_store explicitly through worker pipeline instead of monkey-patching config._fingerprint_store (stevezau#1) - Multi-reference intro comparison: compare multiple episode pairs instead of only episode[0] vs all others (stevezau#2) UX/Logging: - Promote detection log messages from debug to info: credits scan start, no credits found, marker exists, fpcalc not available (stevezau#3, stevezau#6) - Intro pass 2 progress visibility: emit progress callback during season comparison so dashboard doesn't look stuck at 100% (stevezau#4) - Add detection_stats tracking for credits_written/skipped counters (stevezau#5) Significant: - Server-side validation: clamp numeric detection settings to valid ranges in job_runner.py (stevezau#7) - Deduplicate plex.fetchItem call in intro fingerprinting (stevezau#8) - Skip credits markers with confidence < 0.5 to reduce false positives from silence-only detection (stevezau#9) Minor: - Deterministic ORDER BY id in get_marker_tag_id query (stevezau#10) - Use apiGet for loadPlexDbStatus consistency (stevezau#11) - Add complexity comment on fingerprint comparison (stevezau#12) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review Feedback AddressedAll 12 items from the review have been addressed in commit CriticalItem 1 — Item 2 — Multi-reference intro comparison. UX / Logging / ObservabilityItems 3 + 6 — Log levels promoted to info. Key detection outcomes now log at Item 4 — Intro pass 2 progress visibility. After the main dispatch completes, a Item 5 — Detection counters. Added SignificantItem 7 — Server-side validation. All numeric detection settings are now clamped to valid ranges in Item 8 — Duplicate Item 9 — Credits false positive reduction. Added a confidence threshold: markers with confidence < 0.5 are now skipped. This filters out silence-only detections (confidence 0.4) which are most prone to false positives from scene transitions. Black+silence combined (0.9) and black-only (0.6) are still accepted. MinorItem 10 — Deterministic Item 11 — Item 12 — Fingerprint comparison complexity noted. Added a docstring comment noting the O(max_offset × overlap_length) complexity and that numpy/Cython optimization is available if needed for very large seasons. All 1193 tests pass. No |
I did a quick test. The credit marker didn't work on this one, it was way to early. It did however, show up in Plex so that's a win. I think we might need a debug tool like the BIF viewer also. |
|
Any luck @aweigold ^? |
…zau#157) Adds automatic detection of intro and credits segments in media files, writing "Skip Intro" / "Skip Credits" markers directly to the Plex SQLite database. Both features are disabled by default and marked Experimental in the Settings UI. Credits detection uses FFmpeg blackdetect + silencedetect filters on the last portion of each video. Intro detection uses two-pass chromaprint audio fingerprinting: pass 1 fingerprints each episode in parallel, pass 2 compares fingerprints within each season to find the recurring intro theme. Writes only to the `taggings` table (no FTS triggers). Never inserts into the `tags` table. Uses PRAGMA busy_timeout = 5000, single INSERT + COMMIT per call, immediate connection close. Docker-on-Windows detected via failed fcntl.flock and warned in the UI. The Plex REST marker API was tested and confirmed to return 400 for intro/credits types (only bookmarks work), matching all community tools that use direct SQLite writes. Adds libchromaprint-tools to the Docker image for fpcalc. Refs stevezau#157 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical: - Thread fingerprint_store explicitly through worker pipeline instead of monkey-patching config._fingerprint_store (stevezau#1) - Multi-reference intro comparison: compare multiple episode pairs instead of only episode[0] vs all others (stevezau#2) UX/Logging: - Promote detection log messages from debug to info: credits scan start, no credits found, marker exists, fpcalc not available (stevezau#3, stevezau#6) - Intro pass 2 progress visibility: emit progress callback during season comparison so dashboard doesn't look stuck at 100% (stevezau#4) - Add detection_stats tracking for credits_written/skipped counters (stevezau#5) Significant: - Server-side validation: clamp numeric detection settings to valid ranges in job_runner.py (stevezau#7) - Deduplicate plex.fetchItem call in intro fingerprinting (stevezau#8) - Skip credits markers with confidence < 0.5 to reduce false positives from silence-only detection (stevezau#9) Minor: - Deterministic ORDER BY id in get_marker_tag_id query (stevezau#10) - Use apiGet for loadPlexDbStatus consistency (stevezau#11) - Add complexity comment on fingerprint comparison (stevezau#12) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Credits detection false positive fix: - Require 2+ black frames in a cluster (within 60s window) instead of accepting a single black frame, which triggered on scene transitions (e.g. Superman II flagged 12 min early from a scene-transition black frame) - Confidence levels: cluster+silence=0.9, single+silence=0.8, cluster-only=0.6, silence-only=0.4 (below 0.5 write threshold) Detection debug viewer (Tools → Detection Debug): - Enter a Plex item ID to run credits analysis without writing markers - Visual timeline showing black frames, silence regions, scan window, detected credits segment, and existing Plex markers - Tables with raw detection data (timestamps, durations) - Shows whether the result would pass the confidence threshold - Parameters used for the analysis - New API endpoint: POST /api/detection/analyze Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0cf4e01 to
4771427
Compare
Addressing the Superman II false positive + Debug viewerTwo changes pushed based on your test feedback: 1. Credits heuristic tightenedThe Superman II false positive was caused by
2. Detection Debug viewer addedNew page at Tools → Detection Debug that lets you analyze any media item without writing markers:
This makes it easy to troubleshoot why detection did or didn't work on a specific file, and to tune parameters before running on the full library. Would be great if you could re-test Superman II with the debug viewer to verify the false positive is gone. |
Adds two label-gated workflows so reviewers can docker pull a working image of any PR without waiting for a dev/main merge. Triggered by applying the `build-docker` label; uses pull_request_target so it works on cross-repo fork PRs (e.g. #191). Publishes to GHCR under a single `pr-<N>` tag that is overwritten in place on each new commit to the PR, and is automatically deleted when the PR closes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR Docker image readydocker pull ghcr.io/stevezau/plex_generate_vid_previews:pr-191
|
|
@aweigold do you mind merging dev into this PR? lots of changes, want to keep it in sync. |
# Conflicts: # Dockerfile # plex_generate_previews/web/routes/api_settings.py # plex_generate_previews/web/templates/settings.html # plex_generate_previews/worker.py
|
Intro detection is not working. What logs do you need to troubleshoot that? |
|
Hey @aweigold are you still planning to work on this one? |
|
I just watched the latest episode of NCIS: Sydney and the credit detection was about 10 min too early |
|
@aweigold what logs do you need to debug the intro detection not working? |
|
@aweigold are you still planning to work on this PR? It's gone stale. |
|
I switched back to the latest version for now. I noticed that thumbnails were not generated when using the Sonarr/Radarr webhooks, so I switched to latest and to Plex webhooks to see if it would generate them now when new media is added. I saw that the webhook was sent and received, and ppg logs that the file has been processed but there were no thumbnails. I cleared all the logs now and will test on latest image |
|
if you hit issues on latest, please open a new issue |
I will do |
|
@XenoUniv3rse i'll prob look at it at some point but it's a significant effort so not sure when. |
|
I fully understand that. Thanks for all your hard work!!!! |
|
Closing in favour of #241 — a fresh design (and eventually implementation) for multi-server intro/credits detection branched from latest This PR's approach ( Thanks @aweigold for the original work — it shaped the new design. |
…x Tier 1/2
Background agent ran the proposed algorithms against the user's actual
/data library (4856 TV dirs, 9753 movie dirs). Concrete findings either
confirmed, refined, or REFUTED spec claims.
REFUTED:
- Tier 2.5 subtitle-keyword recap detection — 0/30 hits on real files.
"Previously on" is a graphical title card on streaming releases, NOT
in subtitle streams. Mr. Robot S01E06's 90s recap montage has
subtitled dialogue but no keyword chyron in SRT. Reason no existing
tool ships this approach: it doesn't work.
→ DELETED Tier 2.5 from cascade. Recap handling routes through
Tier 0 chapter names (verified working — Dead Boy Detectives
S01E05 has "Recap" chapter), Tier 1 TheIntroDB's recap segments,
Tier 4 OCR (optional, chyron-region keyword scan), or manual
Inspector edits.
REFINED:
- Tier 0 hit rate: spec said 20% TV / 16% movies; measured 40% TV /
23% movies. Spec was conservative by 2× on TV. Updated.
- Tier 1 hit rate: spec said ~57% TV; measured 5% on user's
heterogeneous library (1/20 random IMDb-keyed lookups). Popular-show
cross-check confirmed 70% on mainstream — the 57% figure is a
popularity-biased number. Added tmdb_popularity_min setting + popularity
gate before query to avoid wasted rate-limit quota.
- Tier 2 chromaprint: validated working (Hart to Hart S01 = 102/231
pair matches; theme region 0-78s identified consistently across
pairs). One Piece anime = 10/10 pairs, OP 0-110s — anime myth
busted. BUT discovered release-source variance: cross-encoder
pairs (WEBDL-GRAVE vs Bluray-COLL3CTiF) match poorly.
→ ADDED release-source grouping to Pass 2: extract release group
from filename, run pairwise within-source first (near-100%),
then cross-source with relaxed thresholds.
CONFIRMED:
- Tier 3 adaptive blackdetect: 5 random movies, all converged in
7 probes (1.5-6.3s elapsed, median 4s). PR #191's naive linear
scan was 131s on equivalent 4K HDR. ~30x speedup verified.
- TheIntroDB v2 API + response shape matches spec exactly. null
semantics ("from beginning" / "to end") confirmed.
UNCONFIRMED:
- Tier 5 Emby: local Emby (4.9.3.0) doesn't have MediaPreviewBridge
or Segment_Reporting installed; can't live-verify Phase C writes.
Surfaced as Setup Health requirement.
New §11.5 section captures all the empirical evidence with citations
to test artifacts at /tmp/intro_validation/. Future implementers can
re-run the tests against new content to verify accuracy stays high.
Spec is now 1488 lines (was 1374). PR #241 commit count: 11.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Summary
Implements intro and credits detection as described in #157. Both features are disabled by default and marked Experimental in the Settings UI, so they can be merged into builds for opt-in testing without affecting existing functionality.
What's included
Credits Detection
blackdetect+silencedetectIntro Detection (two-pass)
fpcalc(chromaprint)Plex Database Safety
taggingstable (no FTS triggers)tagstable (has custom ICU tokenizer FTS4 triggers — only SELECT for tag_id lookup)PRAGMA busy_timeout = 5000, single INSERT + COMMIT, immediate closefcntl.flocktest → warning shown in Settings UIWhy SQLite, not REST API
The Plex REST marker API (
POST /library/metadata/{id}/marker) was tested against a live Plex instance:type=bookmark→ 200 OK (works)type=credits→ 400 Bad Requesttype=intro→ 400 Bad RequestAll community tools (MarkerEditorForPlex, plex-credits-detect) confirm this limitation and use direct SQLite writes.
New files
plex_generate_previews/plex_db.py— Safe Plex SQLite access layer (20 tests)plex_generate_previews/credits_detection.py— FFmpeg blackdetect + silencedetect (17 tests)plex_generate_previews/intro_detection.py— Chromaprint fingerprinting + comparison (21 tests)Modified files
config.py— 10 new config fields (all default to disabled/off)media_processing.py— Detection hooks inprocess_item()after BIF generationprocessing.py— Intro pass 2 orchestration +_process_intro_fingerprints()api_settings.py— New settings exposed via APIapi_system.py— New/system/plex-db-statusendpoint for Windows detectionjob_runner.py— Bridge settings to configsettings.html— Credits Detection + Intro Detection UI cards with Experimental badges and Docker-on-Windows warningDockerfile— Addedlibchromaprint-toolsforfpcalcREADME.md,docs/reference.md,docs/guides.md— DocumentationTest plan
ruff checkandruff formatpassfpcalcverified in Docker image (v1.5.1)Refs #157
🤖 Generated with Claude Code