Skip to content

fix(playback): restore Safari Dolby Vision Profile 8 remux - #613

Merged
Quick104 merged 13 commits into
mainfrom
t3code/fix-issue-609
Aug 12, 2026
Merged

fix(playback): restore Safari Dolby Vision Profile 8 remux#613
Quick104 merged 13 commits into
mainfrom
t3code/fix-issue-609

Conversation

@Quick104

@Quick104 Quick104 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Problem

The web capability probe only sent the legacy hdr boolean. Playback V3 validates HDR and Dolby Vision against structured hdr_details, so Safari could decode the issue's Dolby Vision Profile 8.1 source but the server rejected it as requiring video adaptation. With 4K transcoding disabled, that surfaced as the misleading terminal reason no_alternate_version.

Fixes #609.

Approach

  • Probe progressive HDR10 with an exact 2160p HEVC Main10 / Rec. 2020 / PQ / static-HDR Media Capabilities configuration.
  • Probe the exact progressive-remux Dolby Vision sample entries (dvhe.05.06 and dvhe.08.06) through HTMLMediaElement.canPlayType.
  • Advertise Dolby Vision only when the active output also reports high dynamic range.
  • Send structured HDR capabilities at the device, active-output, and per-delivery scopes required by playback V3.
  • Restrict media-element Dolby Vision and inferred HEVC evidence to progressive delivery; original and HLS keep only independently probed claims.
  • Persist FFprobe's exact Dolby Vision level and enforce a per-profile maximum level, with bounded source-property fallback for existing rows.
  • Retry terminal decisions and replan active sessions when output capabilities change, preserving the current file, position, quality, selected tracks, and paused state.
  • Let automatic-quality requests try a compatible alternate version before returning the HDR terminal.
  • Prefer hdr_transcode_unsupported over the 4K policy terminal when an HDR-incompatible client truly requires video encoding.
  • Cover both capability propagation and the planner precedence with regression tests.

The probe deliberately does not use MediaSource support: this route plays the server's progressive MP4 remux directly through a media element.

Runtime evidence

Validated against the shared development deployment with a real single-version 2160p HEVC Main10 Dolby Vision Profile 8 / BL compatibility ID 1 source:

  • Before: the current web payload returned adaptation_unavailable / no_alternate_version.
  • After structured Profile 8 attestation: the same file returned playable / server_remux_progressive / container_normalization.
  • In the browser, the deployed UI sent dolby_vision_profiles: [8] plus max Level 6, kept HDR10/HLG false, and cleared the DV claim from HLS. The real source returned 201 playable / server_remux_progressive and reached HTMLMediaElement.readyState = 4 with no media error. On exact PR head b0249e377630f60e4ccb8c6880053cabb3943a16, a fresh request with HEVC and Profile 8.1 advertised only for progressive delivery again returned playable / server_remux_progressive with native Dolby Vision preserved. A live output_change replan with the same capabilities kept the same progressive route and plan-attempt key. A second live Profile 7 source that initially selected its 1080p SDR alternate stayed on requested file 236941 once this exact HDR10 evidence was present, using server_remux_progressive with server_dv7_to_hdr10.
  • Negative control without structured HDR details now returned hdr_transcode_unsupported.
  • The temporary playback sessions were closed after verification.

Validation

  • go test ./internal/playback ./internal/scanner ./internal/catalogseed
  • go test ./internal/api/handlers
  • pnpm --dir web exec vitest run src/player/client-context-v3.test.ts src/player/hooks/useCodecDetection.test.ts — 18 tests passed
  • pnpm --dir web exec vitest run src/player/hooks/usePlaybackSession.test.ts src/player/hooks/useCodecDetection.test.ts src/player/client-context-v3.test.ts src/player/components/WatchPage.test.ts src/player/components/VideoPlayer.test.tsx — 63 tests passed
  • PATH="$DEV_CACHE_ROOT/bin:$PATH" golangci-lint run --new-from-merge-base="origin/main" ./... — 0 issues
  • pnpm --dir web run lint — 0 errors (151 inherited warnings)
  • pnpm --dir web run format:check
  • pnpm --dir web run build
  • make verify-local-paths

Full local gates also exposed existing baseline failures outside this change:

  • make lint: 300 inherited full-tree findings; the CI-equivalent changed-lines lint above is clean.
  • make test: two unchanged macOS-only Jellycompat process-lock tests fail because the current process identity token is empty. The focused playback and API handler suites pass; CI runs the Go suite on Linux.

Risk

The capability claim is conservative: exact Dolby Vision profiles are only published when both the media element and active HDR output attest support, are bounded to the probed level and Profile 8.1 base-layer shape, and are scoped away from original/HLS delivery. Browsers that do not recognize the sample entry continue to omit Dolby Vision and follow the existing adaptation path.

AI Disclosure

  • Tool(s): OpenAI Codex
  • Model(s): gpt-5.6-sol
  • Involvement: Fully AI-generated implementation, tests, runtime reproduction, browser verification, and PR text under maintainer direction.
  • Adversarial review: Performed before and after submission. Fourteen material findings were addressed: generic HDR no longer implies HDR10/HLG, progressive evidence is excluded from original/HLS, Dolby Vision levels are preserved and enforced end-to-end, the HEVC base claim is limited to its proven delivery, Profile 8 support is constrained to the probed 8.1 base-layer shape, output capability changes invalidate stale decisions without losing tracks or pause state, paused HLS no longer triggers a false startup timeout, and progressive HDR10 is claimed only after an exact Media Capabilities probe, and HDR terminals retain alternate-version fallback. All threads were answered and resolved after focused tests and changed-lines lint.

Summary by CodeRabbit

  • New Features

    • Improved HDR and Dolby Vision capability detection across web playback environments.
    • Added Dolby Vision profile, level, and compatibility details to playback decisions.
    • Added output-change replanning, playback-state tracking, and configurable autoplay behavior.
    • Preserved Dolby Vision metadata from media scanning through playback planning.
  • Bug Fixes

    • Improved fallback handling for incompatible HDR and restricted 4K sources.
    • Applied Dolby Vision profile, level, pixel-rate, width, and bitrate limits.
    • Prevented unnecessary Dolby Vision detection during SDR playback.
  • Tests

    • Added regression coverage for HDR, Dolby Vision, fallback playback, autoplay, and session replanning.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fba5ed62-ec1b-4ec6-8be2-6df9661d3696

📥 Commits

Reviewing files that changed from the base of the PR and between b0249e3 and 5694c1f.

📒 Files selected for processing (16)
  • docs/architecture/playback-protocol-v3.md
  • docs/design/schemas/playback-v3/v3/replan-request.schema.json
  • docs/design/schemas/playback-v3/v3/start-request.schema.json
  • internal/api/handlers/playback_v3.go
  • internal/api/handlers/playback_v3_test.go
  • internal/playback/contract/contract_test.go
  • internal/playback/plan_v3.go
  • internal/playback/protocol_v3.go
  • internal/playback/protocol_v3_test.go
  • web/src/player/client-context-v3.test.ts
  • web/src/player/client-context-v3.ts
  • web/src/player/hooks/useCodecDetection.test.ts
  • web/src/player/hooks/useCodecDetection.ts
  • web/src/player/hooks/usePlaybackSession.test.ts
  • web/src/player/hooks/usePlaybackSession.ts
  • web/src/player/protocol-v3.ts
📝 Walkthrough

Walkthrough

The change adds structured Dolby Vision profile-level capabilities, preserves Dolby Vision levels from media probing through playback planning, refreshes web sessions when HDR output changes, and enables alternate-file fallback for HDR incompatibility terminals.

Changes

HDR playback support

Layer / File(s) Summary
HDR contracts and media metadata
docs/design/schemas/playback-v3/v3/*.json, internal/playback/protocol_v3.go, web/src/player/protocol-v3.ts, internal/scanner/*, internal/models/media.go, internal/catalogseed/*
Schemas and protocol types support Dolby Vision profile levels, source levels, and output_change. Scanner and catalog conversion preserve Dolby Vision level metadata.
Browser HDR detection and propagation
web/src/player/hooks/useCodecDetection.*, web/src/player/client-context-v3.*
The browser detects Dolby Vision and HDR10 support, refreshes capabilities when HDR output changes, and propagates HDR details to delivery and playback contexts.
Source-aware playback validation
internal/playback/capabilities_v3.go, internal/playback/plan_v3.go, internal/playback/protocol_v3_test.go
Playback validates Dolby Vision profiles, levels, compatibility IDs, and source limits. HDR transcode availability is evaluated before the 4K restriction.
Fallback and capability-driven transport
internal/api/handlers/playback_v3.*, web/src/player/hooks/usePlaybackSession.*, web/src/player/components/{VideoPlayer,WatchPage}.*
HDR incompatibility terminals allow alternate-file selection. Sessions preserve transport state during output-change replans, retire refused sessions, and control autoplay during transport changes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MediaScanner
  participant PlaybackPlanner
  participant WebClient
  participant PlaybackSession
  MediaScanner->>PlaybackPlanner: Provide source Dolby Vision level
  WebClient->>PlaybackPlanner: Provide HDR profile-level capabilities
  PlaybackPlanner->>PlaybackPlanner: Validate source and client HDR limits
  PlaybackPlanner-->>PlaybackSession: Return plan or HDR terminal
  PlaybackSession->>PlaybackPlanner: Replan after HDR output change
  PlaybackPlanner-->>PlaybackSession: Return updated plan or refusal
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address issue #609 by restoring structured HDR and Dolby Vision capability handling and correcting incompatible terminal outcomes.
Out of Scope Changes check ✅ Passed The changes remain within the playback objectives, including output-capability replanning, autoplay handling, and HDR regression coverage.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: restoring Safari playback for Dolby Vision Profile 8 remuxes in Playback V3.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/fix-issue-609

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the v1 Silo v1 scope - auto-adds to the Silo v1 project label Aug 12, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8702bdfe5d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web/src/player/hooks/useCodecDetection.ts Outdated
Comment thread web/src/player/client-context-v3.ts
Comment thread web/src/player/hooks/useCodecDetection.ts
Comment thread web/src/player/hooks/useCodecDetection.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/design/schemas/playback-v3/v3/decision-response.schema.json`:
- Around line 764-766: Constrain the dolby_vision_level property in the response
schema to integers from 1 through 13 by adding minimum and maximum bounds, and
ensure the field is omitted when no valid level is available.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f2acdd00-fb5c-4913-8947-98eaaae0dffd

📥 Commits

Reviewing files that changed from the base of the PR and between 8702bdf and dae1c3c.

📒 Files selected for processing (20)
  • docs/design/schemas/playback-v3/v3/decision-response.schema.json
  • docs/design/schemas/playback-v3/v3/replan-request.schema.json
  • docs/design/schemas/playback-v3/v3/start-request.schema.json
  • internal/catalogseed/service.go
  • internal/catalogseed/service_test.go
  • internal/catalogseed/types.go
  • internal/models/media.go
  • internal/playback/capabilities_v3.go
  • internal/playback/plan_v3.go
  • internal/playback/protocol_v3.go
  • internal/playback/protocol_v3_test.go
  • internal/scanner/probe.go
  • internal/scanner/probe_video_range_test.go
  • internal/scanner/scanner.go
  • internal/scanner/types.go
  • web/src/player/client-context-v3.test.ts
  • web/src/player/client-context-v3.ts
  • web/src/player/hooks/useCodecDetection.test.ts
  • web/src/player/hooks/useCodecDetection.ts
  • web/src/player/protocol-v3.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • web/src/player/client-context-v3.test.ts
  • web/src/player/hooks/useCodecDetection.test.ts

Comment thread docs/design/schemas/playback-v3/v3/decision-response.schema.json

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dae1c3c280

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web/src/player/client-context-v3.ts
Comment thread web/src/player/hooks/useCodecDetection.ts
Comment thread web/src/player/hooks/useCodecDetection.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1bf8a9ac1f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web/src/player/hooks/useCodecDetection.ts Outdated
Comment thread internal/playback/plan_v3.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3ecac75f85

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web/src/player/hooks/useCodecDetection.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fea09a0537

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/playback/plan_v3.go
Comment thread web/src/player/hooks/usePlaybackSession.ts Outdated
Comment thread web/src/player/components/WatchPage.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea6fdf92bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web/src/player/hooks/usePlaybackSession.ts
Comment thread web/src/player/hooks/usePlaybackSession.ts Outdated
@coderabbitai coderabbitai Bot removed the v1 Silo v1 scope - auto-adds to the Silo v1 project label Aug 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/src/player/components/VideoPlayer.tsx`:
- Around line 1400-1403: Update the shouldAutoPlay-disabled branch in
VideoPlayer to mark hlsStartupGuardRef as complete before returning, preventing
paused HLS playback from triggering startup timeout recovery. Add a regression
test covering paused HLS with autoplay disabled and verifying no unnecessary
recovery replan occurs.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cc1a4b3c-6778-4b6d-bef8-7f32f5761393

📥 Commits

Reviewing files that changed from the base of the PR and between dae1c3c and ea6fdf9.

📒 Files selected for processing (20)
  • docs/design/schemas/playback-v3/v3/decision-response.schema.json
  • docs/design/schemas/playback-v3/v3/replan-request.schema.json
  • docs/design/schemas/playback-v3/v3/start-request.schema.json
  • internal/api/handlers/playback_v3.go
  • internal/api/handlers/playback_v3_test.go
  • internal/playback/capabilities_v3.go
  • internal/playback/plan_v3.go
  • internal/playback/protocol_v3.go
  • internal/playback/protocol_v3_test.go
  • web/src/player/client-context-v3.test.ts
  • web/src/player/client-context-v3.ts
  • web/src/player/components/VideoPlayer.test.tsx
  • web/src/player/components/VideoPlayer.tsx
  • web/src/player/components/WatchPage.test.ts
  • web/src/player/components/WatchPage.tsx
  • web/src/player/hooks/useCodecDetection.test.ts
  • web/src/player/hooks/useCodecDetection.ts
  • web/src/player/hooks/usePlaybackSession.test.ts
  • web/src/player/hooks/usePlaybackSession.ts
  • web/src/player/protocol-v3.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • internal/playback/capabilities_v3.go
  • internal/playback/protocol_v3_test.go
  • docs/design/schemas/playback-v3/v3/decision-response.schema.json
  • web/src/player/hooks/useCodecDetection.test.ts
  • docs/design/schemas/playback-v3/v3/start-request.schema.json
  • web/src/player/client-context-v3.test.ts
  • web/src/player/protocol-v3.ts

Comment thread web/src/player/components/VideoPlayer.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ce5a490b9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web/src/player/hooks/usePlaybackSession.ts Outdated
Comment thread web/src/player/hooks/usePlaybackSession.ts Outdated
Comment thread internal/api/handlers/playback_v3.go
Comment thread web/src/player/hooks/usePlaybackSession.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2bd376370c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web/src/player/hooks/usePlaybackSession.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b0249e3776

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web/src/player/hooks/useCodecDetection.ts Outdated
Comment thread web/src/player/hooks/usePlaybackSession.ts Outdated
Comment thread web/src/player/hooks/usePlaybackSession.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/playback/protocol_v3.go`:
- Around line 831-832: Reject non-zero FailureV3 values for intent-only
operations track_change, quality_change, and output_change in
ReplanRequestV3.Validate, while preserving the documented seek_reanchor
exception. Update docs/design/schemas/playback-v3/v3/replan-request.schema.json
to forbid failure for those operations, and extend
internal/playback/contract/contract_test.go with negative wire bodies for each
operation asserting both schema and runtime rejection.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7800a972-6ea4-4712-bc43-75cc1422550f

📥 Commits

Reviewing files that changed from the base of the PR and between ea6fdf9 and b0249e3.

📒 Files selected for processing (18)
  • cmd/playbackfixtures/main.go
  • docs/architecture/playback-protocol-v3.md
  • docs/design/schemas/playback-v3/v3/replan-request.schema.json
  • internal/api/handlers/playback_v3.go
  • internal/api/handlers/playback_v3_test.go
  • internal/playback/contract/contract_test.go
  • internal/playback/protocol_v3.go
  • internal/playback/protocol_v3_test.go
  • internal/playback/testdata/protocol_v3/conformance_matrix.json
  • web/src/player/client-context-v3.test.ts
  • web/src/player/client-context-v3.ts
  • web/src/player/components/VideoPlayer.test.tsx
  • web/src/player/components/VideoPlayer.tsx
  • web/src/player/hooks/useCodecDetection.test.ts
  • web/src/player/hooks/useCodecDetection.ts
  • web/src/player/hooks/usePlaybackSession.test.ts
  • web/src/player/hooks/usePlaybackSession.ts
  • web/src/player/protocol-v3.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • web/src/player/hooks/usePlaybackSession.test.ts
  • internal/api/handlers/playback_v3_test.go
  • internal/api/handlers/playback_v3.go
  • web/src/player/components/VideoPlayer.tsx
  • web/src/player/client-context-v3.ts
  • web/src/player/client-context-v3.test.ts

Comment thread internal/playback/protocol_v3.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 530ff695c9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/api/handlers/playback_v3.go
Comment thread web/src/player/hooks/usePlaybackSession.ts Outdated
Comment thread internal/playback/protocol_v3.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36632b2bb2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web/src/player/hooks/usePlaybackSession.ts Outdated
Comment thread web/src/player/hooks/useCodecDetection.ts
@cursor

cursor Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@Quick104
Quick104 merged commit 24c3fee into main Aug 12, 2026
29 of 30 checks passed
@github-project-automation github-project-automation Bot moved this to Done in Silo v1 Aug 12, 2026
@Quick104
Quick104 deleted the t3code/fix-issue-609 branch August 12, 2026 18:15

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5694c1f214

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +208 to +209
dolby_vision_profiles: dolbyVisionProfiles,
dolby_vision_profile_levels: dolbyVisionProfiles.map((profile) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate bounded Dolby Vision claims on planner support

When a newer cached web bundle reaches a pre-change backend during a rolling deployment, Go ignores the unknown dolby_vision_profile_levels field but still honors this legacy dolby_vision_profiles list; the base planner matches only the profile number. The exact Profile 8.1/Level 6 probe therefore becomes an unrestricted Profile 8 claim, allowing that backend to remux unverified Profile 8.2/8.4 or Level >6 sources. Fresh evidence beyond the resolved per-profile bounds is this cross-version interpretation; advertise planner support through a distinct capability before sending the legacy profile claim, or make the payload backward-safe.

AGENTS.md reference: AGENTS.md:L112-L119

Useful? React with 👍 / 👎.

Comment on lines +937 to +938
if (!serverFeaturesRef.current.includes(FEATURE_OUTPUT_CHANGE_V3)) {
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh playback when output replans are unsupported

When a current web bundle talks to a backend that predates output_change_v1, this return occurs after the refreshed capability key was marked active, so moving from HDR to SDR neither replans nor retries. Such a backend already understands the legacy Dolby Vision profile claim and can have selected a Dolby Vision route during the initial HDR start, leaving that incompatible route active indefinitely after the output changes. Use a compatible replacement-start fallback that preserves playback state instead of consuming the refresh without applying it.

AGENTS.md reference: AGENTS.md:L12-L14

Useful? React with 👍 / 👎.

Comment on lines +760 to +764
pendingReplanRef.current = {
options,
loadSequence: loadSequenceRef.current,
retireSessionOnRefusal,
resolve,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Carry output retirement into replacement queue entries

With an ordinary replan in flight, an HDR-to-SDR change can first queue an output_change with retireSessionOnRefusal=true; if a track, quality, or failure request arrives before dispatch, this assignment replaces that entry using the new call's default false. The first request may adopt a plan using the old output evidence, while the replacement request uses fresh SDR evidence and returns terminal without retiring the now-incompatible plan. Fresh evidence after the prior queued-retirement fix is this reverse ordering; preserve the existing retirement requirement when replacing the pending entry.

AGENTS.md reference: AGENTS.md:L12-L14

Useful? React with 👍 / 👎.

Quick104 added a commit that referenced this pull request Aug 13, 2026
…C in remuxes (#617)

* fix(playback): claim Safari DV/HDR10 decode evidence and preserve dvvC

Follow-up to #613 for #609: Safari 26 reports dynamic-range: standard even
on an XDR display, and answers canPlayType "probably" only for dvh1/hvc1
sample entries, never dvhe/hev1. The web probe gated every structured HDR
claim on that media query and probed only dvhe, so the planner still saw
empty hdr_details and terminated with hdr_transcode_unsupported.

- Run the Dolby Vision and HDR10 shape probes unconditionally; the
  dynamic-range query survives only as the best-effort hdr output boolean.
- Probe dvh1+dvhe per DV profile and hvc1+hev1 for the exact HDR10
  Media Capabilities shape; either definitive answer earns the claim.
- Tag preserved-DV remuxes dvh1 with -strict unofficial: FFmpeg omits the
  dvvC configuration record under either tag without it, so the previous
  dvhe output carried no DV signaling at all.
- Scale the screen-derived max_resolution by devicePixelRatio so 2x
  panels stop advertising 1080p.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(playback): align DV/HDR10 claims with the sample entries the remux emits

Review follow-ups on #617 (Codex P1s):

- Probe only dvh1: a browser answering "probably" solely for dvhe has
  given no evidence for the dvh1-tagged file the preserve remux delivers,
  so that answer no longer earns a Dolby Vision claim; such browsers keep
  the validated HDR10 fallback.
- Label the explicit v3 HDR10 strip output hvc1 so the file matches the
  hvc1 evidence the web probe accepts; legacy/auto strips keep hev1.
- Cover preserve tagging for profiles 5 and 8, and exercise the
  mode-to-argument mapping through StartRemuxWithDVMode instead of
  passing the tag flag directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(playback): name the subtitle when burn-in alone forces a refused transcode

Selecting a PGS/VOBSUB track on a client without a bitmap renderer forces
a burn-in transcode. When that transcode cannot run, the terminal blamed
the HDR pipeline (hdr_transcode_unsupported) or the 4K policy
(no_alternate_version) — problems that were not blocking playback, since
deselecting the subtitle restores the previous route.

- When the burn requirement is the sole trigger of the adaptation, the
  HDR and 4K-policy terminals emit subtitle_conversion_unsupported with
  a message naming the burn requirement and the actual blocker. A range
  the client genuinely cannot take keeps the HDR terminal.
- describePlanTerminal passes the server's subtitle message through
  instead of flattening every subtitle_* reason to one generic sentence.
- The web player toasts the refusal when it rolls the subtitle selection
  back; previously the only surface was the quality menu, which a user
  who just picked a subtitle has no reason to open.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(playback): require decode evidence for the exact hvc1 entry and gate the disabled-transcode subtitle terminal

Review follow-ups on #617:

- The HDR10 probe accepts only hvc1.2.4.L153.B0: the explicit v3 strip
  remux labels its output hvc1, so an hev1-only decodingInfo answer is
  evidence for bytes Silo never sends and earns no claim (Codex P1).
- The disabled-transcode branch blames the subtitle only when the burn
  requirement was the sole adaptation trigger; other causes keep
  transcoding_disabled. In practice a bitmap selection without transcode
  terminals in the subtitle policy before this branch, but the guard
  keeps the sole-cause invariant if that ordering ever changes
  (CodeRabbit / Codex P2).
- Protocol doc scopes the hvc1 labeling to the explicit v3 strip path;
  legacy/auto strips keep hev1 (CodeRabbit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Quick104 added a commit that referenced this pull request Aug 13, 2026
…ality

Narrow proxy selection by capability *before* selection rather than
rejecting a single round-robin pick afterwards. Abandoning the pool on one
mismatch meant a capable proxy with free capacity sat unused while
playback either ran ffmpeg on the API node or, with
playback.local_transcode_fallback disabled, was refused outright — the
exact api/proxy split this branch targets, during exactly the rolling
ffmpeg upgrade the capability check exists for. PlanSessionWith now
applies its eligibility predicate to the proxy on proxy-only plans (the
proxy is the executor there), mirroring how HLS filters transcode nodes,
and the planner grows ProxyNodeURLs to match TranscodeNodeURLs. Direct
play still skips the probe: it copies bytes and needs no recipe.

Every committed route now records transport locality, not just the
identity-proxy one. A session replanned from a proxy onto the integrated
transcoder previously kept a stale remote-transport mark, and the widened
idle grace it grants would hold that session's stream and transcode slots
for five minutes after the local stream disconnected without an explicit
stop. The remote HLS route sets it too — it also hands the client an
absolute proxy URL that never reaches this server.

The proxy's CORS config exposed no response headers, so cross-origin
JavaScript could send the If-Range/Range request headers it already allows
but never read the ETag, Accept-Ranges or Content-Range needed to build
them. direct_stream_resume_v1 silently degraded to a full restart whenever
the proxy was on a different origin than the web app, which is the normal
deployment.

Also regenerates internal/playback/testdata/protocol_v3 and the schema
fixtures, which were stale for output_change_v1 since #613/#617 and failed
CI on every branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Quick104 added a commit that referenced this pull request Aug 13, 2026
* fix(playback): route v3 direct play and remux through proxy nodes

Protocol v3 consulted the node planner only for the HLS deliveries, so
`original_http` and `server_remux_progressive` sessions returned an
API-local `/stream/{session_id}` URL and the API node served the bytes —
ServeDirectPlay for direct play, a locally spawned ffmpeg for the remux.
An operator running dedicated proxy nodes still saw all of that egress on
the API node.

The capability already existed: the proxy implements /stream/direct and
/stream/remux, and the Jellyfin-compat transport already plans a proxy for
exactly these two methods. Native v3 was the only surface skipping it, so
Jellyfin clients routed correctly on a deployment where Silo's own clients
did not. This wires the same shape into the v3 identity transport rather
than inventing a second selection path.

The proxy serves from the stream token alone, so the token now carries the
media path, the file's Dolby Vision profile (a P7 remux must strip the
dangling RPU) and the audio-only flag (which picks audio/mp4 over
video/mp4, the MIME the plan promised). RecipeCard models none of the
three; a missing claim would not fail loudly, it would serve a subtly
different stream than the plan promised.

Two related fixes:

- Proxy direct play served via http.ServeFile, which sets no strong ETag.
  direct_stream_resume_v1 depends on the ETag ServeDirectPlay sets before
  ServeContent, so routing direct play to a proxy without this would have
  silently broken resumable direct streams: If-Range never validates and a
  resumed range restarts at 200. The proxy now uses the same serve path.

- playback.local_transcode_fallback was only checked in the HLS branch, so
  a progressive remux that converts audio still spawned ffmpeg locally on
  an API-only node with the setting disabled. Identity deliveries now
  honor the gate too — direct play still falls back locally, since moving
  bytes is not transcode work and single-node deployments must keep
  working.

Falling back to the API-local path when no proxy is eligible preserves
single-node behavior, and a planner reservation is released whenever the
session does not actually reach a proxy.

Closes #619

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(playback): validate proxy recipes and keep proxy sessions alive

Addresses three P1 findings on the proxy-transport change.

Proxies do run ffmpeg — /stream/remux converts audio and strips Dolby
Vision RPUs — but they exposed no capability endpoint, so unlike the HLS
offload path nothing checked that the selected proxy could execute the
transformations a plan froze. A pool whose proxies carry a different
ffmpeg build (rolling upgrade, custom image) would fail at stream time: a
missing aac encoder 500s, a missing dovi_rpu filter is refused outright by
the remux itself. Proxies now serve /hw-capabilities in the same shape and
at the same path as a transcode node, and identity planning validates the
frozen recipe against the selected proxy, falling back to a node that can
do the work. A proxy that does not answer is treated as incapable rather
than assumed good: an older proxy predating the endpoint is exactly the
mismatched build the check exists to catch. Direct play copies bytes and
needs no recipe, so it skips the probe entirely.

meteredResponseWriter implemented neither Unwrap nor SetWriteDeadline, so
RollingDeadlineWriter could not install its stall deadline on any proxy
stream. With the standalone proxy running WriteTimeout 0 there was no
server-level guard behind it, so a client that stopped reading without
closing its connection would block a write forever, holding the session,
the file, the goroutine and the connection.

A proxy-served session never produces a transport request on the API node,
so activeTransportCount — what protects a local stream from the idle
reaper — stays zero and a heartbeat gap longer than the active grace would
reap a healthy stream, after which progress, stop and replan all fail with
session-not-found while bytes still flow. Sessions are now marked as
remotely transported, which widens their idle windows rather than granting
immunity: this manager has no absolute session lifetime, so unconditional
immunity would leak a session forever when a client disappears without
stopping. The mark is always set on commit, so a re-plan that moves a
session back onto the API clears a stale one.

Also adopts the exported transformation constants in the tests and covers
the effective-recipe bitrate branch, per review.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(playback): pick capable sibling proxies and refresh transport locality

Narrow proxy selection by capability *before* selection rather than
rejecting a single round-robin pick afterwards. Abandoning the pool on one
mismatch meant a capable proxy with free capacity sat unused while
playback either ran ffmpeg on the API node or, with
playback.local_transcode_fallback disabled, was refused outright — the
exact api/proxy split this branch targets, during exactly the rolling
ffmpeg upgrade the capability check exists for. PlanSessionWith now
applies its eligibility predicate to the proxy on proxy-only plans (the
proxy is the executor there), mirroring how HLS filters transcode nodes,
and the planner grows ProxyNodeURLs to match TranscodeNodeURLs. Direct
play still skips the probe: it copies bytes and needs no recipe.

Every committed route now records transport locality, not just the
identity-proxy one. A session replanned from a proxy onto the integrated
transcoder previously kept a stale remote-transport mark, and the widened
idle grace it grants would hold that session's stream and transcode slots
for five minutes after the local stream disconnected without an explicit
stop. The remote HLS route sets it too — it also hands the client an
absolute proxy URL that never reaches this server.

The proxy's CORS config exposed no response headers, so cross-origin
JavaScript could send the If-Range/Range request headers it already allows
but never read the ETag, Accept-Ranges or Content-Range needed to build
them. direct_stream_resume_v1 silently degraded to a full restart whenever
the proxy was on a different origin than the web app, which is the normal
deployment.

Also regenerates internal/playback/testdata/protocol_v3 and the schema
fixtures, which were stale for output_change_v1 since #613/#617 and failed
CI on every branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(playback): restore the alternate-version fallback for burn-in refusals

#617 renamed the terminal a burn-in-forced adaptation reports: when the
subtitle burn requirement is the sole trigger, an HDR source that cannot
be re-encoded now returns subtitle_conversion_unsupported instead of
hdr_transcode_unsupported, so the refusal names the thing the viewer can
actually act on.

terminalAllowsAlternateFileV3 was not updated to match, and it gates the
alternate-version retry on the old reason strings. That silently retired
the fallback for exactly the case its own comment describes — a bitmap
subtitle needing burn-in that an HDR source cannot support while an SDR
alternate can. Playback was refused outright instead of switching to the
version that can serve it.

Adds the new reason to the gate and covers it directly, so a future
rename of a refusal reason fails on the gate rather than only on the
end-to-end replan test.

Also drops debug instrumentation that was committed by mistake in
TestHandleReplanPlaybackV3BitmapSubtitleFallsBackFromHDRToSDRVersion; the
assertion is back to its original form and now passes on the merits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
atiabjobayer added a commit to atiabjobayer/silo-server that referenced this pull request Aug 13, 2026
Upstream Silo-Server#613 renamed the HDR burn-in terminal to
subtitle_conversion_unsupported but never added it to the alternate-version
fallback allowlist, so a bitmap subtitle selected against an HDR source
could no longer fall back to an SDR alternate version. Add the reason to
terminalAllowsAlternateFileV3 and pin the behavior with handler tests.

Also keep the branch's stale route-event no-op while preserving the 403 for
terminal-start claims on unknown attempts, and add web regression tests that
a manually selected SRT stays selected once the server adopts the replan.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[bug] Web playback v3 rejects previously playable Dolby Vision Profile 8.1 remux

1 participant