Files
terebi/docs/miruro-playback-handoff.md
T

21 KiB
Raw Blame History

Handoff: Miruro playback in Terebi

Status

Terebi now supports resolved Miruro HTTP(S) sources with per-source headers, external WebVTT and stable episode progress. Direct ssub / sun playback was exercised with a freshly resolved source: native video/audio advanced, default English subtitles loaded, and the model's cue text matched expected subtitles after forward and backward acknowledged seeks. The user also reported that visible playback appears to work; this is not a comprehensive visual synchronization sign-off.

Tubular launches native episode URLs. Terebi resolves fresh direct sources and independently acquires optional descriptive titles and opening/ending metadata. Metadata never gates source playback: missing or invalid enrichment retains a generic title and no bands.

Ownership

  • Tubular: catalog/detail browsing, actual returned episode selection, collections, private reporting sessions, cleanup and watched-state decisions.
  • Terebi: episode-URL validation, fresh source resolution, headers/subtitles, titles, optional timeline bands, stable-identity resume and progress reporting.

Native source resolution selects direct ssub / sun. Other providers, iframe embedding, cookies, impersonation and protection-bypass machinery are not supported.

Launch contract

terebi --new-instance \
  --url 'https://barelystarted.miruro.tv/watch/OPAQUE_ID/episode?ep=1' \
  --progress-file '/private/session/progress.jsonl'

The slug episode is deliberately non-identifying. Use the returned episode number, not the absolute episode, array index or an external catalog ID. Slug/title changes never change resume or progress identity.

Create a private reporting-session directory (0700) and supply a nonexistent progress-file destination. Terebi exclusively creates the report (0600), rejecting existing destinations and symlinks. CLI success means macOS accepted the launch, not that playback succeeded. Tubular consumes reporting independently of launcher lifetime; launcher exit, a final record or Tubular shutdown does not establish that the player stopped. Preserve retained sessions and restart/partial-line recovery.

Ordered stream queues

terebi --new-instance --queue-file '/absolute/private/session/queue.json'

The manifest is bounded to 8 MiB, version 1, and 1–4096 entries in playback order:

{
  "version": 1,
  "items": [
    {"url": "https://www.youtube.com/watch?v=jNQXAC9IVRw", "progress_file": "/absolute/private/youtube-session/progress.jsonl"},
    {"url": "https://barelystarted.miruro.tv/watch/OPAQUE_ID/episode?ep=1", "progress_file": "/absolute/private/episode-session/progress.jsonl"}
  ]
}

Use canonical YouTube video pages or supported Miruro episode pages, never expiring source addresses. Stable identities must be unique even across alternate YouTube forms or different Miruro slugs. Reports must have distinct absolute nonexistent destinations, each under a canonical, nonsymlink, user-owned 0700 directory. Tubular retains each item's existing identity registration/session and stores the manifest in the first session. The CLI validates the complete document before launching; the GUI validates it again before opening any media. Invalid queues fail rather than falling back to a single stream. Queue launch requires --new-instance, accepts --no-config, and cannot combine with --url, --source-file or --progress-file.

The supplied order is preserved (local-file batches still use natural filename order). Existing sidebar reorder/remove/play and previous/next navigation apply. Natural end advances without clearing the queue. Sources resolve only when selected, resolve afresh on revisits, and canceled/stale results cannot replace a newer selection. Labels start with descriptive page information and update from resolved titles/optional metadata.

Each report is immutably bound to its stable identity in the native decoder. Switching entries drains/closes the previous writer before beginning the next; revisits append only after verifying the original private report inode, owner, permissions and link count. An unrelated or replaced file is never reopened as an owned session, and an unregistered identity is never written to the previously selected report. Only the active item owns a running writer/open report descriptor. A reporting failure is shown separately without rerouting facts or changing acknowledged playback/completion semantics. Single-stream --progress-file remains unchanged.

Runnable queue checks: make check-cli check-queue check-reporting check-resolved. The existing check-resolved local HTTP/HLS/VTT harness also drives a real native PlayerModel through a mixed YouTube/Miruro queue with injected fixture resolution, stale-result cancellation, manual navigation, natural-end advancement, fresh revisits/resume and isolated v1/v2 reports. Run TEREBI_CHECK_STREAM_QUEUE_ONLY=1 make check-startup for focused actual CLI→LaunchServices→GUI queue delivery, native decode, manual previous/next, natural-end advancement and per-item report revisits using the existing bundled-extractor/local-media fixture. These checks require no browser or live streaming service. Full make check-startup includes this queue scenario too.

Current local verification: make check-cli check-queue check-reporting check-resolved all passed. The packaged queue GUI smoke did not complete because native state snapshots stalled; no complete GUI pass is claimed. Temporary tracing was removed, and further UI testing is deferred to the user at their request. Tubular's handoff document records its terminal/capture and Rust checks separately.

Optional native metadata

The native resolver requests watch-route data and safely follows bounded same-origin, same-ID, same-episode canonicalization envelopes, including HTTP-200 redirects. It accepts JSON and newline-delimited text/sveltekit-data, decoding only the needed response-local tables and episode promise. Bodies and traversal are bounded; unrelated source promises are not used for playback.

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 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

--source-file PATH --new-instance remains available for callers supplying already-resolved sources; it is not Tubular's current launch path and does not acquire URL metadata. Keep private source files unchanged and present for their player session. Their version-1 contract below is unchanged.

Source document, version 1

Example only: replace CDN URLs with freshly resolved resources, preserving complete addresses and tokens.

{
  "version": 1,
  "url": "https://cdn.example/master.m3u8?token=fresh",
  "headers": {
    "Origin": "https://ultracloud.cc",
    "Referer": "https://ultracloud.cc/"
  },
  "identity": {
    "provider": "miruro",
    "media_id": "OERe_a0YS4h-DlsBDOeXwX7HOO6316tw",
    "episode": 1
  },
  "title": "The Apothecary Diaries — episode 1",
  "subtitles": [{
    "url": "https://cdn.example/english.jpeg?token=fresh",
    "format": "vtt",
    "language": "en",
    "label": "English",
    "default": true,
    "headers": {
      "Origin": "https://ultracloud.cc",
      "Referer": "https://ultracloud.cc/"
    }
  }]
}

All shown fields except title are required. headers: {} and subtitles: [] are valid; at most one subtitle may be default. Only vtt is supported for external subtitles in this pass.

Limits: source document 1 MiB; 32 subtitle tracks; 64 headers per resource; Miruro media ID up to 512 ASCII letters/digits/-/_; episode integer 1–2147483647. Invalid URLs, HTTP header names, case-insensitive duplicate header names and control characters are rejected with sanitized errors. Header values are not trimmed or rewritten.

External source projection

External source-document producers must preserve selected direct stream URLs and complete server headers, use Miruro's opaque catalog ID and returned episode number for identity, and carry explicit per-subtitle headers. Subtitle headers do not inherit stream headers. The verified Sun path supplies server headers to its VTT resources; do not generalize this to other providers.

identity.provider is the catalog namespace miruro, not the stream provider sun. Titles, slugs and expiring CDN URLs are not identity. Tubular no longer projects source documents.

Propagation and subtitle behavior

  • Stream headers reach the master, nested variants, media segments, AES-128 keys and fMP4 init resources. Relative references use native HLS resolution. Existing query tokens are preserved; Terebi does not invent query inheritance or provider-specific URL rewrites.
  • Resolved media requests must stay on the initial stream's origin: scheme, host and effective port. All media redirects, including same-origin redirects, are rejected before a second request. This conservative policy prevents FFmpeg from forwarding supplied private headers to another destination. Cross-origin HLS resources are not supported in this contract.
  • Sun serves MPEG-TS segments as .jpg / image/jpeg. Resolved playback probes actual media content instead of enforcing filename-extension matching; it does not change the URLs. HTTP(S)-only, origin and redirect restrictions remain in force.
  • External VTT classification uses explicit format and content, not filename extension or MIME type. .jpeg subtitle URLs work. Labels/languages appear in the captions menu; the declared default is selected, and the user can choose Off or another track.
  • Cues follow acknowledged player position, including seeking backwards. Zero-duration cues are permanently inactive rather than invalidating the track. Rendering is plain text; WebVTT CSS, positioning and typographic styling are not applied.
  • Subtitle HTTP requests use their own headers. Same-origin redirects retain those headers; cross-origin redirects are rejected. Responses are capped at 4 MiB, with 15-second request and 20-second resource timeouts.
  • Media errors remain visible with the operation/native error. Subtitle failures appear separately beside the captions menu, including when other controls are hidden. FFmpeg diagnostics redact private details; source/subtitle errors do not interpolate URLs or header values.
  • Casting uses the same episode identity and progress schema; receiver-confirmed positions replace paused local-decoder facts. The Mac relays media/VTT without exposing source URLs or credentials to the receiver. See Chromecast controls, transport limits and verification; physical receiver playback/captions remain unverified.

YouTube's existing launch, version-1 progress and transport behavior remain supported.

Progress contract and Tubular change

YouTube records remain version 1 with canonical media_url. Miruro records are version 2, with structured episode identity and no CDN address:

{"version":2,"event":"progress","media_identity":{"provider":"miruro","media_id":"OERe_a0YS4h-DlsBDOeXwX7HOO6316tw","episode":1},"position_seconds":123.5,"duration_seconds":1500.0,"completion_eligible":true}

event is progress, ended or closed. Position is finite, nonnegative and acknowledged by playback, not a pending seek/slider target. Duration is positive and finite or null. Completion eligibility remains false for live/non-seekable media, unknown duration and audio-only sources.

Reporting includes ready state after restoration, approximately five-second playback intervals, acknowledged pause/seek transitions, the first eligible 95% crossing, native end and final close/quit snapshots. Launch success and window closure do not establish completion. Consumers must tolerate duplicates, partial trailing lines and abrupt termination; a closed record is not guaranteed.

Tubular integration must retain canonical YouTube/version-1 validation, accept version-2 validation against the episode identity expected for that launched session, and key Miruro watched state by (miruro, opaque media ID, episode). Never use stream URL, token, slug or selected playback provider as identity.

Retain the current completion decision for matching identities:

completion_eligible
&& duration_seconds > 0
&& position_seconds / duration_seconds >= 0.95

Handle null duration as ineligible. This measures position, not viewing coverage; seeking near the end can satisfy it. Terebi also uses stable episode identity for its own resume bookmarks.

Verification evidence

Scenario Observed result
Explicit fresh direct Sun native smoke Video/audio decode and advancing playback; default English VTT; expected cue text after forward/backward acknowledged seeks
Live Sun audio continuity Dragon Ball episode 1: actual Core Audio PCM diverged from FFmpeg after about 0.6 seconds before the fix; after tolerating millisecond-scale timestamp jitter, all 960,008 stereo frames compared across twenty seconds and multiple HLS segments matched exactly
Audio regression Deterministic AAC/MPEG-TS timestamp-jitter fixture failed before the shared continuity fix and matched FFmpeg exactly afterward; existing authored-gap, resampling, gain and stepped-resume checks passed
Local resolved playback model Independent stream/subtitle headers, nested playlist tokens, video/audio playback, cue timing and stable v2 progress
Local native network checks Encrypted MPEG-TS and fMP4; image-suffixed resources; headers on master/variant/segment/key/init; seeks/frame steps; redirect/foreign-origin isolation; diagnostic redaction; existing YouTube redirects
Actual CLI-to-GUI handoff Two independent Miruro episode windows; authenticated encrypted HLS; resource-specific default VTT; v2 progress and quit draining
Full make check-startup rerun Passed, including held-key suppression, YouTube progress/resume/quality/replacement/end and independent launches
Other exercised checks CLI, external subtitles, progress persistence, JSONL reporting, YouTube resolver and system playback passed
Visible-app observation User reported playback appears to work; comprehensive visual subtitle/seek synchronization sign-off remains limited

Earlier startup runs failed held-Space and YouTube end-transition assertions. The user reported possible manual interaction. The full rerun passed without playback-code changes; causality of the earlier failures was not established. No full aggregate make check pass is claimed.

Streaming-input verification (2026-10-19): native URLSession resolved a live Miruro episode with nine VTT tracks; native URL-to-playback exercised video/audio, pause, default captions and forward/backward acknowledged subtitle seeks. Offline stream parsing/projection/security, CLI/config, resolved playback and YouTube resolver/adaptive checks passed. The actual source-file CLI launch emitted version-2 progress and ended records, but the startup check timed out waiting for GUI state snapshots; this run does not establish the complete two-window handoff scenario. The live probe's original synchronous wait blocked async resolution; yielding the actor fixed the probe without playback-code changes.

Reproduction commands from Terebi

make check-cli check-subtitles check-resolved check-network check-progress check-reporting
make check-youtube check-media
make check-startup

For only the actual Miruro launch scenario:

TEREBI_CHECK_RESOLVED_ONLY=1 make check-startup

Default checks use local fixtures and do not contact Miruro. Optional metadata protocol/security/normalization checks:

make check-miruro-metadata
make .build/resolved-playback-check
.build/resolved-playback-check --metadata-lifecycle

Explicit live native URL smoke (contacts Miruro and media/subtitle resources):

.build/miruro-metadata-check --live 'https://barelystarted.miruro.tv/watch/OERe_a0YS4h-DlsBDOeXwX7HOO6316tw/episode?ep=1'
.build/resolved-playback-check 'https://barelystarted.miruro.tv/watch/OERe_a0YS4h-DlsBDOeXwX7HOO6316tw/episode?ep=1'

URL smoke expects a fixture episode with available title, bands and default subtitles. It checks native decode and acknowledged subtitle/seek state, not visual appearance or perceived A/V synchronization. Tubular's catalog probe does not run Terebi or retrieve sources.

Metadata implementation verification (2026-10-22): protocol/security/bounds checks, lifecycle replacement/A-B-A stale results/duration changes, stream resolver and CLI checks passed. The live placeholder resolved the descriptive title “The Apothecary Diaries Season 3 — Episode 1: Locusts,” opening 63.313–153.237 seconds and ending 1335.252–1425.384 seconds; native video/audio advanced, measured-duration bands appeared in model state, and default VTT plus forward/backward acknowledged cue seeks passed. FFmpeg emitted redacted diagnostics during this successful run; no visual synchronization or visible-band sign-off is claimed.

Controls alignment correction: removed the obsolete label-height reservation after switching to gapped segments. A native offscreen SwiftUI/AppKit layout smoke reproduced a 9-point top-row shift when intervals appeared; after restoring the standard 14-point slider frame, volume and timeline geometry match exactly with and without intervals. Production build passed; visible confirmation remains a human check.

Segment-skip verification (2026-10-22): local HLS/VTT playback exercised late metadata, the controlled-clock three-second boundary while paused, persistent dismissal, pending-seek suppression, opening/ending seeks, exact media-end seeking, close/reopen reset and playing/paused intent. Exact-end HLS seeks initially failed with Operation not permitted; the shared native seek path now finishes network inputs at EOF instead of asking the demuxer to seek beyond its final packet. Network playback regression checks, metadata checks, production build and TLS loopback Cast segment skipping/restoration passed. Rapid play-then-seek probes encountered Core Audio startup errors on the active Bluetooth output; the settled-playback smoke waits for actual advancing playback and passed. Visible button placement, rapid audio transitions and physical-TV behavior remain unverified.

Remaining limits

  • Confirm title readability and gapped slider segments in normal/compact controls, buffer/playhead visibility and subtitle alignment in the visible app using Tubular's real launch path. Offscreen native rasterization at compact/normal widths confirmed all four interval-boundary gaps remain clear even with full buffer coverage.
  • Tubular collection launches, retained-report recovery and watched decisions remain its acceptance responsibility; this implementation changes no collection or progress schema.
  • Media redirects/cross-origin HLS resources remain unsupported. Native URL launches resolve fresh; externally supplied source documents must be refreshed by their producer. Automatic expired-source refresh is not implemented.
  • Other providers' proxies/HLS rewrites, iframe providers, downloads, thumbnails, automatic skipping, embedded captions and additional external-subtitle formats remain out of scope.

The implementation and this handoff are local; no remote publication or Tubular issue/comment update was performed.