Add skip intro / next episode at credits, via Emby's own detection #4

Merged
kyuno merged 2 commits from feature/intro-credits-skip into master 2026-09-16 07:19:00 +02:00
Owner

Summary

  • Checked the real Emby server before building anything: no plugin exists for this, and the embedded Chapters field is just auto-generated placeholders. What the server does have natively: a per-library EnableMarkerDetection option (now enabled for both TV libraries) backed by a built-in "Detect Episode Intros" task, confirmed via the server's own /openapi.json to add real IntroStart/IntroEnd/CreditsStart markers into that same Chapters array.
  • New GET /api/catalog/item/:id/markers (mirrors getPlaybackOptions) reads just those three marker types.
  • Player gets one new floating button, "Passer l'intro" - a sibling of #bottomBar so it stays reachable through the bar's auto-hide, host-only and cast-aware for free since it reuses the existing activeDriver/seek path.
  • End credits reuse the existing #nextBtn ("Épisode suivant") rather than duplicating it - the only gap closed is un-minimizing the bar once so it's actually visible when credits start.

Test plan

  • npm run typecheck / npm test / npm run build on both projects (125/125 server tests, 3 new for getPlaybackMarkers)
  • Button position checked directly (getBoundingClientRect against the real compiled CSS) at two viewport sizes - clear of #bottomBar, no overlap
  • Not verified: the detection task had just been enabled and hadn't finished analyzing any episode yet, so the real end-to-end "marker appears at the right second" behavior needs a check once it's had time to run

🤖 Generated with Claude Code

## Summary - Checked the real Emby server before building anything: no plugin exists for this, and the embedded `Chapters` field is just auto-generated placeholders. What the server *does* have natively: a per-library `EnableMarkerDetection` option (now enabled for both TV libraries) backed by a built-in "Detect Episode Intros" task, confirmed via the server's own `/openapi.json` to add real `IntroStart`/`IntroEnd`/`CreditsStart` markers into that same `Chapters` array. - New `GET /api/catalog/item/:id/markers` (mirrors `getPlaybackOptions`) reads just those three marker types. - Player gets one new floating button, "Passer l'intro" - a sibling of `#bottomBar` so it stays reachable through the bar's auto-hide, host-only and cast-aware for free since it reuses the existing `activeDriver`/seek path. - End credits reuse the existing `#nextBtn` ("Épisode suivant") rather than duplicating it - the only gap closed is un-minimizing the bar once so it's actually visible when credits start. ## Test plan - [x] `npm run typecheck` / `npm test` / `npm run build` on both projects (125/125 server tests, 3 new for `getPlaybackMarkers`) - [x] Button position checked directly (`getBoundingClientRect` against the real compiled CSS) at two viewport sizes - clear of `#bottomBar`, no overlap - [ ] Not verified: the detection task had just been enabled and hadn't finished analyzing any episode yet, so the real end-to-end "marker appears at the right second" behavior needs a check once it's had time to run 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kyuno added 1 commit 2026-09-15 23:16:55 +02:00
Add skip intro / next episode at credits, via Emby's own detection
Some checks failed
Publier l'image Docker / test (pull_request) Failing after 4s
Publier l'image Docker / publish (pull_request) Has been skipped
57bb297bff
Asked for directly. Checked the real server before building anything:
no plugin provides this (Cinema Intros is a different feature - pre-show
trailers, not per-episode skip points), and Emby's own Chapters field
turned out to be auto-generated placeholders every 5 minutes, not real
markers.

What the server does have natively, confirmed from its own
/openapi.json rather than assumed: a per-library EnableMarkerDetection
option backed by a built-in "Detect Episode Intros" scheduled task -
audio-fingerprint detection, the same idea as Jellyfin's separate Intro
Skipper plugin, already part of Emby itself. Enabling it (done directly
on the server for the two TV libraries) adds real entries to the same
Chapters array, distinguished by MarkerType: IntroStart/IntroEnd/
CreditsStart.

This made the feature almost entirely "read what Emby already
computed" - no fingerprinting, no new persistence, no background jobs
of this app's own. CatalogService.getPlaybackMarkers fetches
Fields=Chapters and picks out just those three marker types (nulls
when detection hasn't reached this item or isn't enabled for its
library - never an error), behind a new
GET /api/catalog/item/:id/markers route mirroring getPlaybackOptions.

The player gets one new floating button, #skipIntroBtn - an explicit
label, not an icon, matching this app's own convention for every
request-shaped control. It's a sibling of #bottomBar, not inside it,
so it stays reachable through the bar's own auto-hide/.minimal collapse.
Clicking it seeks to introEndTicks through the exact same
activeDriver/onDriverStateChanged path any other seek uses, so it's
automatically host-only and automatically cast-aware with no new
special-casing for either. End credits got no button of their own -
#nextBtn already does exactly "go to the next queued item" and was
already shown whenever hasNextQueued is true; the only gap closed here
is visibility, by un-minimizing the bar once (a one-shot flag, not a
fight against someone deliberately re-minimizing it) the moment
playback crosses creditsStartTicks.

Verified: full typecheck/test/build on both projects (125/125 server
tests, including new coverage for getPlaybackMarkers - real MarkerType
values mixed with auto-generated Chapter ones, and both "no Chapters"
and "no markers among the Chapters" resolving to nulls rather than
throwing). The button's on-screen position was checked directly
(getBoundingClientRect against the real compiled CSS) at two viewport
sizes - clear of #bottomBar with a 10px gap, not overlapping. Not
verified: the detection task had only just been enabled on the real
server and hadn't finished analyzing any episode yet, so the actual
end-to-end "a detected marker makes the button appear at the right
second" behavior needs a real check once it's had time to run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
kyuno added 1 commit 2026-09-16 07:18:45 +02:00
Merge remote-tracking branch 'origin/master' into feature/intro-credits-skip
Some checks failed
Publier l'image Docker / test (pull_request) Failing after 4s
Publier l'image Docker / publish (pull_request) Has been skipped
bc878c8d1c
# Conflicts:
#	README.md
#	docs/architecture.md
kyuno merged commit f680f72b1b into master 2026-09-16 07:19:00 +02:00
kyuno deleted branch feature/intro-credits-skip 2026-09-16 07:19:01 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: kyuno/watchparty#4
No description provided.