Skip to content

fix(menus,titles): fall back to source image for menu backgrounds, fix audio track compatibility#74

Merged
ScottMorris merged 9 commits into
mainfrom
fix/menu-background-fallback-and-audio-track-compat
Jun 24, 2026
Merged

fix(menus,titles): fall back to source image for menu backgrounds, fix audio track compatibility#74
ScottMorris merged 9 commits into
mainfrom
fix/menu-background-fallback-and-audio-track-compat

Conversation

@ScottMorris

@ScottMorris ScottMorris commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Menu canvas background images rendered nothing when no thumbnail cache existed yet, or the cached read failed. `BackgroundImage` now falls back to the original asset file via the `asset://` protocol (`convertFileSrc`), and only shows a placeholder if that also fails. Requires enabling Tauri's asset protocol and adding `tauri-plugin-persisted-scope` so arbitrary asset source paths can be read through it.
  • Audio track mapping in the Titles editor always defaulted every source stream's output target to AC3 re-encode regardless of actual codec compatibility. It now reads `asset.compatibilityDetail` to pick a 1:1 output target for already-compatible codecs (ac3/dts/mp2/pcm) and only forces AC3 re-encode for incompatible ones. Also adds a per-track remove button, an 8-track DVD limit warning, and a compact source-stream summary line.
  • Menu background images were being needlessly letterboxed on anamorphic 16:9 DVD output: the background scale filter compared the source image against the raw 720x480 pixel box (1.5:1) instead of the true anamorphic display aspect (16:9 ≈ 1.78:1 via the 32:27 sample-aspect-ratio DVD uses), so a genuinely 16:9 image got pillarboxed/letterboxed even though it should fill the frame exactly. Confirmed and fixed against a real rendered DVD (ffprobe/cropdetect on the encoded menu VOB).
  • Per-track audio bitrate override: new `bitrate_bps: Option` field on `AudioTrackMapping` lets each audio track select an explicit output bitrate or leave it at the codec default. Selecting a bitrate forces `copyMode` to `'re-encode'`. LPCM always ignores the override (its rate is derived from channel count/sample depth).
  • Audio track controls layout: split into two equal-width grid rows (output target + copy mode on row 1; channel layout + bitrate + language on row 2) so the remove button is always in the same vertical position regardless of track count.
  • Source stream bitrate is now shown in the per-track summary line (e.g. `EAC3 · 7.1 · 48kHz · 640kbps`) so users know what they're re-encoding from.
  • The "Copy" option in the copy-mode select is disabled for streams whose codec is not DVD-compatible (EAC3, AAC, etc.), preventing a configuration that ffmpeg would reject at build time.
  • Bitrate options are filtered per output codec: AC3 shows 192–640 kbps, MP2 shows 192–384 kbps, DTS shows 768 kbps and 1.5 Mbps. Switching codec clears any override that isn't valid for the new codec.
  • Channel layout and bitrate selects now show descriptive auto labels — "Auto Channels (5.1)" and "Auto Bitrate (448 kbps)" — so users understand what the default will produce without having to know codec defaults.
  • Hover-only delete buttons (titleset, title, audio track, subtitle track) removed from the tab order (`tabIndex={-1}`) to prevent accidental deletion via Tab+Enter.

Closes #57. Closes #71.

Test plan

  • `tsc --noEmit`
  • `pnpm test:js` (233/233 passing)
  • `pnpm format:check`
  • `cargo fmt --check`
  • `cargo clippy --all-targets -- -D warnings`
  • `cargo test -p tauri-plugin-spindle-project` (212/212 passing)
  • Manual verification of menu background fallback against a real project (confirmed via app logs: `Background thumbnail load failed, falling back to source`)
  • Menu-letterbox fix verified against a real rendered DVD (ffprobe/cropdetect on the encoded menu VOB, before/after)
  • Audio bitrate UI verified via Tauri MCP driver (bitrate select renders; select→re-encode promotion; LPCM disables+clears)
  • Audio bitrate UI verified with real EAC3 project (Elio): source bitrate in summary, Copy disabled for EAC3 streams, per-codec options, descriptive Auto labels — all confirmed via live app

@ScottMorris ScottMorris added frontend Frontend UI, styling, and webview behaviour backend Rust backend, commands, and native runtime behaviour titles Title structure, ordering, and playback mapping work menus Menu layout, navigation, and button behaviour labels Jun 23, 2026
@ScottMorris

Copy link
Copy Markdown
Contributor Author

@codex review

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

Spindle JS tests

233 tests  +5   233 ✅ +5   5s ⏱️ ±0s
 24 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 7df227b. ± Comparison against base commit 35c4f5c.

♻️ This comment has been updated with latest results.

@ScottMorris ScottMorris marked this pull request as draft June 23, 2026 15:48
@ScottMorris ScottMorris added the bug Something isn't working label Jun 23, 2026
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

Spindle Rust tests

221 tests  +7   221 ✅ +7   2s ⏱️ ±0s
  2 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 7df227b. ± Comparison against base commit 35c4f5c.

♻️ This comment has been updated with latest results.

@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: e187a4e3ce

ℹ️ 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 +555 to +556
const compatible = audioCompatByStream.get(as_.index) ?? false;
const outputTarget = compatible ? (AUDIO_CODEC_TARGETS[as_.codec] ?? 'AC3') : 'AC3';

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 Fall back when compatibility details are absent

When an asset was saved before compatibilityDetail existed (the model defaults the field to null on parse), audioCompatByStream is empty even though audioStreams still has codec data. In that context selecting the asset marks every stream incompatible, so even an existing AC3 track is remapped to AC3 re-encode instead of copy, regressing existing projects; please fall back to the codec table or re-inspect before defaulting to false.

Useful? React with 👍 / 👎.

Comment on lines +1128 to +1131
mp2: 'MP2',
pcm_s16le: 'LPCM',
pcm_s16be: 'LPCM',
};

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 Map compatible LPCM sources to LPCM

For assets whose inspector reports codec value lpcm, the backend marks that value DVD-compatible, but this table has no lpcm entry. Because the compatible-codec fallback is AC3, those LPCM streams are created as copy-mode tracks with an AC3 output target/bitrate estimate, and toggling re-encode will silently transcode them to AC3 rather than preserving LPCM; add lpcm: 'LPCM' here.

Useful? React with 👍 / 👎.

@ScottMorris ScottMorris force-pushed the fix/menu-background-fallback-and-audio-track-compat branch from e187a4e to 7237ad1 Compare June 24, 2026 02:59
@ScottMorris

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@ScottMorris

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@ScottMorris ScottMorris force-pushed the fix/menu-background-fallback-and-audio-track-compat branch from b6f6953 to fdb13b9 Compare June 24, 2026 13:43
…x audio track compatibility

Menu canvas backgrounds rendered nothing when no thumbnail cache existed yet
or the cached thumbnail failed to load. `BackgroundImage` now falls back to
the original asset file via the `asset://` protocol (`convertFileSrc`), with
a placeholder shown only if that also fails. Requires enabling the asset
protocol and `tauri-plugin-persisted-scope` so arbitrary asset source paths
can be read through it.

Audio track mapping in the Titles editor always defaulted every source
stream's output target to AC3 regardless of actual codec compatibility. It
now reads `asset.compatibilityDetail` to pick a 1:1 output target for
already-compatible codecs (ac3/dts/mp2/pcm) and only forces a re-encode to
AC3 for incompatible ones. Also adds a per-track remove button, an 8-track
DVD limit warning, and a compact source-stream summary line.

Closes #57, Closes #71
… output

DVD-Video menus encode at a 720x480 (NTSC) raster but display
anamorphically at 16:9 via a non-square sample-aspect-ratio (32:27).
`build_ffmpeg_menu_command`'s background-image/video scaling used
ffmpeg's `force_original_aspect_ratio=decrease`, which compares the
source's aspect against the *raw pixel* box (720x480 = 1.5:1) instead
of the *true display* aspect (16:9 ≈ 1.78:1). A genuinely 16:9
background image got needlessly letterboxed as a result — confirmed
against a real rendered DVD via `ffprobe`/`cropdetect`, which showed
~62px bars baked into the encoded menu VOB despite the background
being a 3840x2160 16:9 source.

- Extracted the display-aspect-aware fit math already used correctly
  for title-video transcoding (`build_dvd_scale_filter`) into a shared
  `dvd_active_dimensions()` helper in `ffmpeg.rs`.
- `menu.rs`'s background scaling (both still-image and motion-video
  paths) now computes `source_dar` from the background asset's probed
  `video_streams` and uses `dvd_active_dimensions` against the menu's
  true target display aspect, falling back to "no padding" when the
  asset has no probed dimensions.
- Added two regression tests: a 16:9 background on a 16:9 menu now
  fills the frame with zero padding (the actual bug), and a 4:3
  background on a 16:9 menu still pillarboxes correctly (proving the
  fix didn't just remove padding outright).

Separately investigated a title-video letterboxing report from the
same session: confirmed via a temporary diagnostic test that current
`build_ffmpeg_transcode_command` already produces zero padding for a
16:9-source/16:9-target title (`scale=720:480,pad=720:480:0:0`), so
the bars observed in that on-disk VOB were from a stale prior build,
not a live bug — no code change needed there.

Verified via:
- `cargo test -p tauri-plugin-spindle-project` (202 passed, up from 199)
- `cargo clippy -p tauri-plugin-spindle-project --all-targets` (clean)
- `cargo fmt -p tauri-plugin-spindle-project -- --check` (clean)
- Independent code review (no @codex review — Codex usage limits exhausted)
@ScottMorris ScottMorris force-pushed the fix/menu-background-fallback-and-audio-track-compat branch from fdb13b9 to 6283de2 Compare June 24, 2026 13:51
@ScottMorris ScottMorris marked this pull request as ready for review June 24, 2026 13:54
ScottMorris and others added 5 commits June 24, 2026 10:38
Adds an optional `bitrate_bps: Option<u32>` field to
`AudioTrackMapping` so each audio track in the Titles editor can have
its output bitrate overridden independently of codec defaults.

**Model / backend (`tauri-plugin-spindle-project`)**

- `models/title.rs`: new `bitrate_bps: Option<u32>` field with
  `#[serde(default)]` for backward compat with existing project files.
- `build/capacity.rs`: `estimate_title_audio_bitrate_bps` uses the
  override when present; LPCM always ignores it (its rate is derived
  from channel count/sample depth, not requestable).
- `build/ffmpeg.rs`: AC3, MP2, and DTS arms pass the override as the
  `-b:a` flag; LPCM gets no `-b:a` at all.
- All 5 `AudioTrackMapping` construction sites updated with
  `bitrate_bps: None`.

**Frontend (`apps/spindle`)**

- `TitlesPage.tsx`: new bitrate `<select>` after the channel-layout
  control — options from 192 kbps to 768 kbps plus "Auto (codec
  default)". Selecting a bitrate forces `copyMode` to `'re-encode'`
  (you can't change bitrate on a copied stream). Disabled for LPCM
  with an explanatory tooltip. Switching output target to LPCM or
  copy mode to `'copy'` clears the override.
- Default builder initialises `bitrateBps: null` on new mappings.
- 3 test-fixture construction sites updated with `bitrateBps: null`.

**Tests**

- `capacity.rs`: `reencode_audio_bitrate_override_replaces_the_codec_default`,
  `lpcm_ignores_a_bitrate_override_since_its_rate_is_derived_not_requestable`
- `ffmpeg.rs`: `ffmpeg_uses_bitrate_override_when_set`,
  `ffmpeg_falls_back_to_codec_default_bitrate_when_no_override_is_set`,
  `ffmpeg_ignores_bitrate_override_for_lpcm`
- `titles.test.tsx`: 5-test `describe('audio track bitrate control')`
  block covering defaults, select→re-encode promotion, copy-clears,
  LPCM-disabled, and LPCM-switch-clears-override.

Verified end-to-end via Tauri MCP driver: bitrate select renders
with "Auto (codec default)" selected; choosing 448 kbps sets
`bitrateBps: 448000` and flips `copyMode` to `'re-encode'`; switching
to LPCM clears `bitrateBps` and disables the select.

Closes #71.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3tSFK3P9vGwbNNtb4AYQG
Replace the single `flex-wrap` controls row (which split 4+1 at the
panel width) with two explicit CSS grid rows:

- Row 1 (`1fr 1fr`): output target + copy mode
- Row 2 (`1fr 1fr 1fr`): channel layout + bitrate + language

All cells are equal-width within their row, so the column positions
are consistent across every track. With a fixed row count per track,
the × remove button always lands at the same vertical position
regardless of how many tracks are open.

Also gives `titles__track-lang` proper select-matching styles (border,
bg, padding) so it fits flush in the grid alongside the selects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3tSFK3P9vGwbNNtb4AYQG
…y for incompatible streams

Source bitrate from file metadata:
- `audioSourceSummary` now includes the source stream's bitrate when
  known (`bitrateBps` from `AudioStreamInfo`), formatted as e.g.
  `448kbps`. Gives users a clear reference point when choosing an
  output bitrate override.

Incompatible-stream guard:
- New `isCopyCompatible` helper checks `asset.compatibilityDetail
  .audioStreams[].codec.compatible` for the mapped stream index.
  Defaults to `true` when no compatibility detail is available.
- The "Copy" option in the copy-mode select is `disabled` when the
  source stream's codec is not DVD-compatible (e.g. AAC, Opus, FLAC),
  preventing users from accidentally stream-copying a format DVD
  players won't understand. A tooltip explains why.
- Compatible streams (AC3, DTS, MP2, LPCM) are unaffected — both
  Copy and Re-encode remain available.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3tSFK3P9vGwbNNtb4AYQG
Replace the flat hardcoded bitrate list with a per-codec table
(`AUDIO_BITRATE_OPTIONS`) so the select only shows rates that are
actually valid for the chosen output target:

- AC3: 192 / 256 / 320 / 384 / 448 / 640 kbps (DVD-Video max 640)
- MP2: 192 / 256 / 320 / 384 kbps (MPEG-1 Layer II max 384)
- DTS: 768 kbps / 1.5 Mbps — DVD-Video allows both standard (~768)
  and high-bitrate (~1536) DTS; 1.5 Mbps was previously missing
- LPCM: no options (rate is derived from channel count × depth)

Switching output target now also clears `bitrateBps` when the
current override isn't in the new codec's valid list, preventing
a stale e.g. 768 kbps value from silently carrying over to AC3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3tSFK3P9vGwbNNtb4AYQG
Replace generic "Auto (source)" / "Auto (codec default)" with labels
that surface the actual values at a glance:

- Channel layout: "Auto Channels (5.1)" / "Auto Channels (stereo)"
  — shows the source channel count when asset metadata is known,
  or plain "Auto Channels" when no source is selected yet.

- Bitrate: "Auto Bitrate (448 kbps)" for AC3, "Auto Bitrate
  (768 kbps)" for DTS, "Auto Bitrate (384 kbps)" for MP2 — driven
  by a new `CODEC_DEFAULT_BPS` map that mirrors the hard-coded
  defaults in `ffmpeg.rs`. LPCM shows plain "Auto Bitrate" since
  its rate is derived from channel count × sample depth, not a
  fixed default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3tSFK3P9vGwbNNtb4AYQG
@ScottMorris

Copy link
Copy Markdown
Contributor Author

@codex review

All four `titles__row-remove` buttons (titleset, title, audio track,
subtitle track) are opacity-0 until hover — invisible to sighted users
not hovering, but previously reachable by Tab. Tab-Tab-Enter from a
track label would silently delete the track.

`tabIndex={-1}` keeps them mouse-accessible while removing them from
the sequential focus order. Tab flow per audio track is now:
label → codec → copy mode → channels → bitrate → lang

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3tSFK3P9vGwbNNtb4AYQG
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3tSFK3P9vGwbNNtb4AYQG
@ScottMorris ScottMorris merged commit 775680c into main Jun 24, 2026
8 checks passed
@ScottMorris ScottMorris deleted the fix/menu-background-fallback-and-audio-track-compat branch June 24, 2026 18:42
ScottMorris added a commit that referenced this pull request Jun 24, 2026
## Summary

`.titles__track-lang` was defined twice in `TitlesPage.css`: a dead
full-width/`text-sm` block (overridden immediately by the block below
it) followed by the intended 48px/mono/`text-xs` block. Consolidate into
one block and move the `:focus` rule to follow it.

No visual change — the second block was already winning the cascade.

Caught during review of #74.

## Test plan
- [x] `pnpm format:check` passes
- [x] Visual spot-check: lang input still renders as a narrow mono code
field
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Rust backend, commands, and native runtime behaviour bug Something isn't working frontend Frontend UI, styling, and webview behaviour menus Menu layout, navigation, and button behaviour titles Title structure, ordering, and playback mapping work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No user control over audio track bitrate in Titles editor Image background does not render on menu canvas

1 participant