Skip to content

Add an audio-track selector for multi-audio videos - #28

Merged
Yoddikko merged 1 commit into
mainfrom
feature/audio-language-selector
Jul 29, 2026
Merged

Yoddikko merged 1 commit into
mainfrom
feature/audio-language-selector

Conversation

@Yoddikko

@Yoddikko Yoddikko commented Jul 29, 2026 •

Copy link
Copy Markdown
Owner

User description

Closes #27

Multi-audio (dubbed) videos now get an audio-language menu next to the captions control — and an "Audio" entry in the overflow menu when the player bar is narrow. Single-audio videos show nothing new.

How it works

Mirrors the existing captions plumbing end to end:

  • YouTubeAudioTrack model + three protocol methods on the playback controller
  • WebView JS reads getAvailableAudioTracks()/getAudioTrack() and switches by index into the player's live track array, so the opaque player track objects never cross the JS bridge — switching is exact
  • YouTubePlayerService exposes audioTracks/activeAudioTrackId, refreshed alongside captions and quality
  • The control is hidden entirely for single-audio videos

Known ceiling

Display names are heuristic (opaque track object → player-response audioTrack.displayName → generic "Audio N"); switching is exact regardless. Marked with a ponytail: comment pointing at the dump-and-map fix if names come out wrong on a real multi-audio video.

🤖 Generated with Claude Code


CodeAnt-AI Description

Let viewers switch between available audio languages while watching multi-audio videos

What Changed

  • Adds an Audio menu showing the available dubbed-language tracks
  • Places the control beside captions on wide player bars and in the overflow menu on narrow bars
  • Marks the active audio track and switches playback when a different track is selected
  • Hides the Audio control for videos with only one audio track
  • Clears and reloads audio-track choices when changing videos

Impact

✅ Audio-language switching during playback
✅ Audio controls shown only when applicable
✅ Clear active-track indication

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Closes #27

Multi-audio (dubbed) videos now get an audio-language menu next to the
captions control (and an "Audio" entry in the overflow menu on narrow
bars). It mirrors the existing captions plumbing:

- YouTubeAudioTrack model + protocol methods on the playback controller
- WebView JS reads getAvailableAudioTracks()/getAudioTrack() and switches
  by index into the player's live track array, so the opaque player track
  objects never cross the JS bridge (switching is exact)
- YouTubePlayerService exposes audioTracks/activeAudioTrackId, refreshed
  alongside captions and quality
- The control is hidden entirely for single-audio videos

Display names are heuristic (opaque object → player-response
audioTrack.displayName → "Audio N"); switching is exact regardless.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 93f5dac Jul 29, 2026 · 10:23 10:26

@codeant-ai

codeant-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@Yoddikko
Yoddikko merged commit dabf181 into main Jul 29, 2026
1 of 8 checks passed
@Yoddikko
Yoddikko deleted the feature/audio-language-selector branch July 29, 2026 10:23
@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Jul 29, 2026
Comment on lines +37 to +39
func availableAudioTracks() async -> [YouTubeAudioTrack]
func currentAudioTrackId() async -> String?
func setAudioTrack(id: String)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The test target's MockYouTubeWatchPlaybackController conforms directly to this protocol but does not implement these three newly required methods, so adding the requirements causes the test target to fail to compile until the mock is updated or default protocol implementations are provided. [api mismatch]

Severity Level: Critical 🚨
- ❌ Test target fails compilation for every build.
- ❌ All service tests are blocked by protocol nonconformance.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** Sources/Kaset/Services/Player/YouTubePlayerService.swift
**Line:** 37:39
**Comment:**
	*Api Mismatch: The test target's `MockYouTubeWatchPlaybackController` conforms directly to this protocol but does not implement these three newly required methods, so adding the requirements causes the test target to fail to compile until the mock is updated or default protocol implementations are provided.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +1262 to +1263
self.audioTracks = await self.playbackController.availableAudioTracks()
self.activeAudioTrackId = await self.playbackController.currentAudioTrackId()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: These awaits can suspend while the user navigates to another video. The only video-id guard runs before the quality, caption-state, and audio queries, so an old refresh can resume afterward and overwrite the new video's audioTracks and activeAudioTrackId state. Recheck currentVideo?.videoId == videoId immediately before applying these results, or associate the refresh with a generation token. [stale reference]

Severity Level: Critical 🚨
- ⚠️ New video can display previous audio tracks.
- ⚠️ Audio checkmark can identify the wrong active track.
- ⚠️ Quality and caption state can also be overwritten by the same race.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** Sources/Kaset/Services/Player/YouTubePlayerService.swift
**Line:** 1262:1263
**Comment:**
	*Stale Reference: These awaits can suspend while the user navigates to another video. The only video-id guard runs before the quality, caption-state, and audio queries, so an old refresh can resume afterward and overwrite the new video's `audioTracks` and `activeAudioTrackId` state. Recheck `currentVideo?.videoId == videoId` immediately before applying these results, or associate the refresh with a generation token.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +1325 to +1329
/// Selects an alternate audio track (dubbed language) by id.
func selectAudioTrack(id: String) {
self.activeAudioTrackId = id
self.playbackController.setAudioTrack(id: id)
HapticService.toggle()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The service marks the requested track active before the JavaScript switch succeeds, while setAudioTrack has no completion result and swallows all player errors. If the player disappears during navigation or rejects the switch, the checkmark remains on the requested track even though playback still uses the previous track. Update the state only after confirming the switch, or refresh the active track after the command completes. [logic error]

Severity Level: Major ⚠️
- ⚠️ Audio menu checkmark can disagree with actual playback.
- ⚠️ Navigation or player errors can leave stale selection state.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** Sources/Kaset/Services/Player/YouTubePlayerService.swift
**Line:** 1325:1329
**Comment:**
	*Logic Error: The service marks the requested track active before the JavaScript switch succeeds, while `setAudioTrack` has no completion result and swallows all player errors. If the player disappears during navigation or rejects the switch, the checkmark remains on the requested track even though playback still uses the previous track. Update the state only after confirming the switch, or refresh the active track after the command completes.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +1373 to +1378
const resp = playerResponse();
const fmts = (resp && resp.streamingData && resp.streamingData.adaptiveFormats) || [];
fmts.forEach(function(f) {
const dn = f && f.audioTrack && f.audioTrack.displayName;
if (dn && !seenName[dn]) { seenName[dn] = true; responseNames.push(dn); }
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: responseNames is deduplicated across adaptive formats and then indexed against the live audio-track array. Adaptive formats can contain several formats for one language or use an order different from the player track array, so the menu can display one language while selecting a different track. Map each live track using its own identifying metadata rather than assuming these arrays share the same order. [logic error]

Severity Level: Major ⚠️
- ⚠️ Multi-audio menu can show incorrect language labels.
- ⚠️ Users may select tracks based on misleading names.
- ⚠️ Playback switching remains exact but discoverability is impaired.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** Sources/Kaset/Views/YouTube/YouTubeWatchWebView+Scripts.swift
**Line:** 1373:1378
**Comment:**
	*Logic Error: `responseNames` is deduplicated across adaptive formats and then indexed against the live audio-track array. Adaptive formats can contain several formats for one language or use an order different from the player track array, so the menu can display one language while selecting a different track. Map each live track using its own identifying metadata rather than assuming these arrays share the same order.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audio language button

1 participant