feat: extend segment skip offers to ten seconds

This commit is contained in:
2026-10-22 13:58:42 +02:00
parent 3d949154a2
commit 458b2c9544
5 changed files with 9 additions and 9 deletions
+5 -5
View File
@@ -107,11 +107,11 @@ struct ResolvedPlaybackCheck {
}
model.supplyMetadataForCheck(metadata, episode: 1)
require(model.segmentToSkip == opening, "late metadata offers opening at acknowledged paused position")
clock.now = 102.999
require(model.segmentToSkip == opening, "opening remains available before three seconds")
clock.now = 103
require(model.segmentToSkip == nil && !model.skipSegment(), "offer expires at three seconds while paused")
clock.now = 104
clock.now = 109.999
require(model.segmentToSkip == opening, "opening remains available before ten seconds")
clock.now = 110
require(model.segmentToSkip == nil && !model.skipSegment(), "offer expires at ten seconds while paused")
clock.now = 111
try await Task.sleep(for: .milliseconds(100))
require(model.segmentToSkip == nil, "polling does not reoffer ignored opening")
require(model.seekRelative(2.25 - model.state.position), "enter ending")
+1 -1
View File
@@ -24,7 +24,7 @@ To run without installing, use `make run`. The built `.build/terebi.app` is self
- **Local files:** use **⌘O**, drag files onto the player, or choose Finder's **Open With**. Multiple files form a queue in natural filename order, with automatic advance. Use **View → Show Queue** to play, reorder or remove items; **+** or dropping files onto the sidebar appends them. The queue lasts only for the session; opening other media replaces it.
- **Streams:** use **Open Stream…** (**⇧⌘O**) or drop a supported link. YouTube supports individual videos, not playlists or channels. Miruro links must use `https://barelystarted.miruro.tv/watch/<opaque-id>/<slug>?ep=<episode>` with an explicit positive episode number; arbitrary streaming sites are not supported.
- **Seeking:** click or drag the progress track or thumb to seek. The thumb centers on the cursor, clamped to the track endpoints.
- **Miruro metadata:** titles and opening/ending intervals load independently of playback when available. Small rounded gaps mark interval boundaries in the slider. Inside a segment, **Skip Opening** or **Skip Ending** appears for three seconds; clicking seeks to its end. Ignored offers stay dismissed until the media is closed or replaced. Missing/invalid metadata leaves playback usable, and timings may differ between cuts. Nothing skips automatically.
- **Miruro metadata:** titles and opening/ending intervals load independently of playback when available. Small rounded gaps mark interval boundaries in the slider. Inside a segment, **Skip Opening** or **Skip Ending** appears for up to ten seconds or until the segment ends; clicking seeks to its end. Ignored offers stay dismissed until the media is closed or replaced. Missing/invalid metadata leaves playback usable, and timings may differ between cuts. Nothing skips automatically.
- **Resume:** videos resume their saved position; finished videos restart from the beginning. Live and audio-only media do not save progress.
- **Compact mode:** press **P** for a floating player; double-click it to restore. Drag noninteractive areas to move the window. Use the titlebar pin to keep the normal window on top.
- **Quality:** YouTube defaults to SDR up to 1080p. Use **gear → Quality** for other available resolutions or automatic quality.
+1 -1
View File
@@ -84,7 +84,7 @@ final class PlayerModel: ObservableObject {
return
}
if offeredSegments.insert(interval.kind).inserted {
skipOffer = (interval, skipClock() + 3)
skipOffer = (interval, skipClock() + 10)
} else if let skipOffer, skipClock() >= skipOffer.deadline || skipOffer.interval != interval {
self.skipOffer = nil
}
+1 -1
View File
@@ -22,7 +22,7 @@
- audited SDK 27.0 / Swift 6.4 with the macOS 15 minimum retained; migrated Cast discovery to NWBrowser and production activation to activate(). Real Bonjour identity/name/update/order/deduplication/removal/restart and routable-endpoint checks, loopback Cast transitions, focused startup-error alerts and a cooperating-launcher production smoke for cold start/CLI reuse/document handoff/keyboard playback passed. The existing full CLI snapshot-harness timeout remains; macOS 15 runtime and physical-Cast verification remain unverified. Observation, glass and the existing native bridges were left unchanged.
- [x] fix the readme. it's currently bloated with irrelevant implementation detail and other such nonsense. it should do the job of a readme only, nothing more.
- [x] add a skip button when current playback is inside either an opening or an ending segment.
- Skip Opening/Ending uses validated metadata, seeks to the segment end and disappears after three seconds even while paused; ignored segments stay dismissed until close/media replacement. Native HLS, exact-EOF, controlled-clock dismissal and loopback Cast checks passed. Button placement and physical-Cast verification remain unverified; rapid play/seek probes encountered Core Audio startup errors on Bluetooth output.
- Skip Opening/Ending uses validated metadata, seeks to the segment end and disappears after ten seconds (including while paused) or on leaving the segment; ignored segments stay dismissed until close/media replacement. Native HLS, exact-EOF, controlled-clock dismissal and loopback Cast checks passed. Button placement and physical-Cast verification remain unverified; rapid play/seek probes encountered Core Audio startup errors on Bluetooth output.
- [ ] improve: subtitles.
- decrease padding on captions
- decrease font size of captions by 10 percent
+1 -1
View File
@@ -31,7 +31,7 @@ The native resolver requests watch-route data and safely follows bounded same-or
Titles come from returned series/selected-episode metadata. Only `op`/`mixed-op` and `ed`/`mixed-ed` become opening/ending bands. Bands retain fractional seconds, require finite positive player-reported duration, and reject nonfinite timestamps, originally reversed/zero-length intervals and wrong-episode timings. Display intervals are clipped to `[0, duration]`; intervals with no positive-length overlap are hidden. Raw timestamps remain unchanged, and duration changes recompute clipping without shifting or scaling. Optional provenance/votes/reference-length checks remain in force; a finite positive reference length differing from actual duration by more than 5% rejects the interval, and missing fields are not invented. Highest-vote eligible opening and ending win, with response order breaking ties. Overlaps are not merged.
The controls mark interval boundaries with small rounded gaps in the slider track, without visible labels or boxes. Buffer rendering shares the segmented track; accessibility descriptions retain opening/ending names and times. While acknowledged, seekable video playback is inside a band, a **Skip Opening** or **Skip Ending** button offers a seek to its end for three seconds of monotonic time, including while paused. Each kind is offered once until close or media-identity replacement; pointer movement and seeking back do not reoffer ignored segments. Pending seeks and slider previews cannot expose a stale offer. The same action routes to local or Cast playback and retains playback intent. No automatic skipping is performed. Bounds do not prove synchronization across cuts. Metadata tasks are bound to identity and lifecycle generation; replacement clears intervals and rejects stale results, including A → B → A.
The controls mark interval boundaries with small rounded gaps in the slider track, without visible labels or boxes. Buffer rendering shares the segmented track; accessibility descriptions retain opening/ending names and times. While acknowledged, seekable video playback is inside a band, a **Skip Opening** or **Skip Ending** button offers a seek to its end for up to ten seconds of monotonic time, including while paused, or until playback leaves the band. Each kind is offered once until close or media-identity replacement; pointer movement and seeking back do not reoffer ignored segments. Pending seeks and slider previews cannot expose a stale offer. The same action routes to local or Cast playback and retains playback intent. No automatic skipping is performed. Bounds do not prove synchronization across cuts. Metadata tasks are bound to identity and lifecycle generation; replacement clears intervals and rejects stale results, including A → B → A.
### External resolved-source compatibility