2026-07-20 Plex Credits Detection Only Scans the Last 10 Percent
Context
No “Skip Credits” button on recently watched One Piece episodes (S22E12-E14, absolute 1097-1099). Plex is a native macOS install on the Mac, not part of the media-stack compose set (see Homelab Services Architecture), so nothing in the repo was implicated.
What Happened
Marker coverage across the 70 episodes in the library was patchy: 51/70 had intro markers, 47/70 had some credits marker, but only 24/70 had a usable one. Roughly 28 of the “credits” markers were 1-3 second junk glued to the final frame (final=1), which render no button.
The first real signal came from the Plex DB (com.plexapp.plugins.library.db), in media_parts.extra_data: a pv:creditsDetectionErrorCount field. Correlation with marker presence was perfect across all 70 episodes — errCount 0 meant a marker existed (47), errCount 4-5 meant none (23). No exceptions. Plex’s detector had crashed, not missed, and gives up permanently after ~4 failures. Server logs confirmed it:
ERROR - [CreditsDetectionManager] BufferingLineReader: failed to read line (error: -1)
ERROR - [CreditsDetectionManager/Response::fetch/MarkerResponse] incomplete marker attributes
Reproducing the detector by hand revealed the actual cause. The credits job shells out to Plex Transcoder and samples thumbnails from a fixed offset:
Plex Transcoder -skip_frame noref -ss 1288 -i <file> -vf "fps=0.5,scale=320:320,showinfo"
-ss 1288 on a 1431.7s file is 89.96% in. Plex only looks at the final ~10% of runtime for credits. These files declare their own chapters, and the ED sits at 1216-1306s — so Plex sees the last 18 seconds of a 90-second sequence and nothing else.
Testing that against chapter data across the season: where the ED falls entirely inside the scan window, detection succeeded 28/31; where it’s clipped, 13/33. One Piece is unusually exposed because ~2 minutes of content (a post-ED scene plus next-episode preview) follow the credits, pushing them out of the region Plex bothers to examine.
Why It Was Notable
Three things worth remembering.
The first fix attempt was wrong and silently wrong. PUT /library/metadata/{id}/analyze on 46 episodes returned 200 for every one and changed exactly zero markers, because analyze doesn’t reset the error counter. Had I not snapshotted markers beforehand and diffed, I’d have reported success.
I also over-fit an intermediate theory. Marker quality flipped cleanly at audio-encode boundaries (AAC vs E-AC-3, and a runtime change at E38), which looked causal and wasn’t — the codec correlation was real but incidental. The blocks mattered because the release changed where the ED sits, not what it was encoded with.
Third, strings on the binaries was the fastest route to a definitive negative. Enumerating all 181 server prefs plus grepping both Plex Media Server and Plex Media Scanner proved no scan-window tunable exists anywhere. That search also surfaced an undocumented endpoint the docs never mention:
PUT /library/metadata/{id}/credits?force=1&manual=1
This is the correct way to force credits re-detection — unlike /analyze it bypasses the “Already up to date” skip and genuinely re-runs the detector. Testing it confirmed the diagnosis by failing identically (“has failed too many times, we will not retry again”), which is the proof that no amount of forcing or configuring fixes a geometric problem.
Resolution / Lessons Learned
Configuration cannot fix this. Plex exposes only GenerateCreditsMarkerBehavior, enableCreditsMarkerGeneration, MarkerSource, and the butler task — all already at their most permissive values. The 90% window is hardcoded with no knob.
That leaves third-party tooling, and the survey mattered:
- MarkerEditorForPlex (maintained, May 2026) — GUI bulk-add with chapter-reference expressions (
=Ch(Credits)S). Writes real markers, so the native button works. - PlexAutoSkip (maintained, Sep 2025) — daemon that auto-skips on chapter name (
"tags": ["c:credits"]). Touches no database at all, but seeks automatically rather than showing a button. - Casvt/Plex-scripts
intro_marker_editor.py(maintained, Sep 2025) — CLI marker writer, the automatable option. - plex-credits-detect — the most-recommended tool for this online, and abandoned since 2022. Popularity is not maintenance.
Chose the batch-correction route: derive markers from each file’s own Credits chapter (23 episodes needed it, all with clean ~90s sequences at ~20:16-21:46) and write them via the Casvt CLI. Applied and verified — all 23 present at the chapter times, stamped pv:version=5, which also stops Plex’s v4 detector regenerating over them.
Two things surfaced during verification. Plex applies a ±2s safety inset to credits markers when serving them over the API (DB 1216000-1306000 becomes API 1218000-1304000); this is universal, confirmed against Plex’s own native marker on E11 (1330712-1358712 → 1332712-1356712), not something the write introduced.
More interesting: Plex’s marker on E11 — one of the episodes it “succeeded” on — sits at 1330-1358s, which is inside the Scene 5 post-credits chapter, not the Credits chapter at 1216-1306. It didn’t just fail on 23 episodes; on some of the other 47 it marked the wrong content, exactly what a detector confined to the last 10% would do.
Auditing every marker against chapter data narrowed that down usefully. Of Plex’s 44 checkable markers: 16 correct, 4 partial, 24 misplaced. But 21 of the 24 are final=1 junk glued to the last frame — clutter that never fires during content. Only three were genuinely harmful (E6, E11, E18), each landing 90-135s past the real ED inside the post-credits scene. Those three were replaced with chapter-derived markers; the 21 junk ones were left alone, since deleting them buys no behavioural change. Final state: 0 harmful markers, 42 aligned, 6 episodes unverifiable because their files carry no Credits chapter.
The generalisation “if one was wrong, they’re all suspect” was wrong and worth noticing — the failure was concentrated exactly where the model predicted it would be (ED outside the scan window), not spread randomly.
The durable lesson is that the ground truth was sitting in the file the whole time. VARYG ships correct, named chapter markers; Plex parsed them into pv:chapters and stored them in the same DB row as the empty marker field, then failed to detect what it had already been told. Reaching for the source data beat trying to make the detector work.
Related
Homelab Services Architecture, Media Stack IaC Declarative Config Evaluation