YouTube-style autoplay with an up-next countdown card - #26
Conversation
A new "Autoplay" toggle (default off) in the related header. When on, a finished video no longer stops: it queues the next suggested video behind a YouTube-style countdown card over the surface — "Up Next" + the next title, a 5s ring you can click to play now, or a Cancel button. On timeout or click it advances and starts playing automatically. Mixes/playlists are unaffected (they advance through their own queue via page drift). - SettingsManager.youtubeAutoplayEnabled (default off, persisted) - YouTubePlayerService: on a natural finish, start a 5s autoplay countdown to the first up-next (or a freshly fetched related video); confirm/cancel; the countdown is cleared by play/stop/advance so explicit playback overrides it - YouTubeAutoplayCountdownOverlay: reusable card hosted by both the inline watch view and the floating window - Toggle in the related header, matching YouTube's up-next autoplay switch - "Play now" localized (it: "Riproduci ora"); reuses "Up Next"/"Cancel" - Tests: off queues nothing, on queues a countdown (no instant switch), confirm advances now, cancel clears Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🤖 CodeAnt AI — Review Status
|
🤖 CodeAnt AI — Review Status
|
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
| Text("Related", comment: "Related videos section header") | ||
| .font(.title3.bold()) | ||
| Spacer(minLength: 12) | ||
| self.autoplayToggle |
There was a problem hiding this comment.
Suggestion: The autoplay switch is added to relatedHeader, but that header is also rendered for mix and playlist playback. Enabling this global setting while viewing a mix exposes the autoplay path to the mix queue, despite the feature contract that mixes and playlists should advance through their own page-drift queue. Hide or disable this control when mixVideos is non-empty. [api mismatch]
Severity Level: Major ⚠️
- ❌ Mix playback receives the unrelated Autoplay countdown.
- ❌ Mix tracks can advance through `confirmAutoplayNow()`.
- ⚠️ The control is shown whenever mix data is loaded.Steps of Reproduction ✅
1. Open a watch page whose loaded data contains mix videos.
`YouTubeWatchView.relatedColumn` still renders `relatedHeader` at
`Sources/Kaset/Views/YouTube/YouTubeWatchView.swift:1255-1260`, and `relatedHeader` always
renders `self.autoplayToggle` at `YouTubeWatchView.swift:1273-1279`.
2. Enable that toggle. The setting is global and is persisted by
`SettingsManager.youtubeAutoplayEnabled` at
`Sources/Kaset/Services/SettingsManager.swift:576-580`.
3. For the mix page, `upNextQueue` returns the mix videos mapped with mix context at
`Sources/Kaset/Views/YouTube/YouTubeWatchView.swift:1239-1242`; `startOrAdoptPlayback()`
supplies that queue to `YouTubePlayerService.setUpNext()` at
`YouTubeWatchView.swift:413-416`.
4. Let the current mix video finish. `handleVideoEnded()` calls `autoplayNextIfEnabled()`
at `Sources/Kaset/Services/Player/YouTubePlayerService.swift:2003-2005`, which takes
`self.upNext.first` at `YouTubePlayerService.swift:2021-2024` and starts the autoplay
countdown.
5. The countdown overlay then offers confirmation and advances through `advance(to:)` at
`Sources/Kaset/Services/Player/YouTubePlayerService.swift:2061-2064`, so the mix advances
through the new autoplay path rather than remaining exclusively under its intended
page-drift queue.(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/YouTubeWatchView.swift
**Line:** 1278:1278
**Comment:**
*Api Mismatch: The autoplay switch is added to `relatedHeader`, but that header is also rendered for mix and playlist playback. Enabling this global setting while viewing a mix exposes the autoplay path to the mix queue, despite the feature contract that mixes and playlists should advance through their own page-drift queue. Hide or disable this control when `mixVideos` is non-empty.
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| private var autoplayToggle: some View { | ||
| Toggle(isOn: Binding( | ||
| get: { self.settings.youtubeAutoplayEnabled }, | ||
| set: { self.settings.youtubeAutoplayEnabled = $0 } |
There was a problem hiding this comment.
Suggestion: Turning the toggle off only updates SettingsManager; it does not cancel an already-running countdown. The countdown task does not re-check this setting before calling confirmAutoplayNow(), so autoplay can still advance after the UI reports that autoplay is disabled. Cancel the pending countdown when the value changes to false, or make the service observe the setting before advancing. [logic error]
Severity Level: Major ⚠️
- ❌ Disabling Autoplay cannot stop an active countdown.
- ❌ The next suggested video starts against current user preference.
- ⚠️ The issue requires changing the setting during five seconds.Steps of Reproduction ✅
1. Open a YouTube watch page and enable the Autoplay switch;
`YouTubeWatchView.autoplayToggle` writes only `settings.youtubeAutoplayEnabled` at
`Sources/Kaset/Views/YouTube/YouTubeWatchView.swift:1286-1288`.
2. Let the current video finish through
`YouTubeWatchWebView.Coordinator.handleVideoEnded()` at
`Sources/Kaset/Views/YouTube/YouTubeWatchWebView+Coordinator.swift:191-210`;
`YouTubePlayerService.handleVideoEnded()` then calls `autoplayNextIfEnabled()` at
`Sources/Kaset/Services/Player/YouTubePlayerService.swift:2003-2005`.
3. With a related candidate available, `startAutoplayCountdown(to:)` creates the
five-second countdown task at
`Sources/Kaset/Services/Player/YouTubePlayerService.swift:2017-2053`.
4. Turn the Autoplay switch off before the five seconds elapse. The setter at
`YouTubeWatchView.swift:1288` does not call `cancelAutoplay()`, and the countdown task
only checks the pending video at `YouTubePlayerService.swift:2049`; it does not re-check
the setting.
5. When the countdown reaches zero, `YouTubePlayerService.swift:2051-2052` calls
`confirmAutoplayNow()`, which advances to the queued video at
`YouTubePlayerService.swift:2061-2064` despite the UI now showing Autoplay disabled.(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/YouTubeWatchView.swift
**Line:** 1288:1288
**Comment:**
*Logic Error: Turning the toggle off only updates `SettingsManager`; it does not cancel an already-running countdown. The countdown task does not re-check this setting before calling `confirmAutoplayNow()`, so autoplay can still advance after the UI reports that autoplay is disabled. Cancel the pending countdown when the value changes to false, or make the service observe the setting before advancing.
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| guard SettingsManager.shared.youtubeAutoplayEnabled, | ||
| let current = self.currentVideo | ||
| else { return } | ||
|
|
There was a problem hiding this comment.
Suggestion: The autoplay setting is checked only when creating a countdown. Turning the setting off afterward does not cancel the existing countdown task, and that task never rechecks the setting before confirming playback, so the queued video can still start even though autoplay is now disabled. Invalidate or clear the countdown when the setting changes, or check the current setting before confirming. [logic error]
Severity Level: Major ⚠️
- ❌ Disabling Autoplay does not prevent queued playback.
- ⚠️ Related-header setting changes are ignored by active countdowns.
- ⚠️ Users can be unexpectedly navigated to another video.Steps of Reproduction ✅
1. Enable the Autoplay toggle implemented by `YouTubeWatchView.autoplayToggle` at
`Sources/Kaset/Views/YouTube/YouTubeWatchView.swift:1286-1289`.
2. Finish a video with an available `upNext` candidate; `autoplayNextIfEnabled()` at
`Sources/Kaset/Services/Player/YouTubePlayerService.swift:2016-2023` passes the setting
guard and starts the five-second task at lines 2038-2056.
3. Before the five-second task reaches zero, disable the toggle.
`SettingsManager.youtubeAutoplayEnabled` at
`Sources/Kaset/Services/SettingsManager.swift:576-580` only persists the value and does
not clear the service's countdown.
4. Allow the task to finish. Its zero-countdown branch at
`YouTubePlayerService.swift:2049-2053` calls `confirmAutoplayNow()` without rechecking the
setting, and lines 2061-2065 advance to the queued video even though autoplay is disabled.(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:** 2017:2020
**Comment:**
*Logic Error: The autoplay setting is checked only when creating a countdown. Turning the setting off afterward does not cancel the existing countdown task, and that task never rechecks the setting before confirming playback, so the queued video can still start even though autoplay is now disabled. Invalidate or clear the countdown when the setting changes, or check the current setting before confirming.
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| Task { @MainActor in | ||
| guard let client = self.youtubeClient, | ||
| let next = try? await client.getWatchNext(videoId: expectedVideoId, playlistId: nil) | ||
| .related.first(where: { !$0.isShort }), | ||
| SettingsManager.shared.youtubeAutoplayEnabled, | ||
| self.currentVideo?.videoId == expectedVideoId, | ||
| self.autoplayPendingVideo == nil | ||
| else { return } | ||
| self.startAutoplayCountdown(to: next) | ||
| } |
There was a problem hiding this comment.
Suggestion: The fallback getWatchNext task is not tied to a playback-intent generation or cancelled by cancelAutoplay(). If the user cancels while this request is in flight, autoplayPendingVideo is nil, so a late successful response passes the final guard and recreates the countdown. Capture a generation before starting the request and require it to remain current before calling startAutoplayCountdown. [race condition]
Severity Level: Major ⚠️
- ❌ Cancelled autoplay can reappear after delayed network responses.
- ⚠️ Floating-window finishes use this fallback request path.
- ⚠️ Users may see an unexpected later video transition.Steps of Reproduction ✅
1. Enable autoplay and finish a YouTube video through `handleVideoEnded(videoId:)` at
`Sources/Kaset/Services/Player/YouTubePlayerService.swift:1960-2007` when `upNext` is
empty, causing the fallback task at line 2026 to call `getWatchNext`.
2. Keep `getWatchNext` suspended in the client while the fallback request is in flight; no
task handle is stored, so `cancelAutoplay()` at lines 2069-2071 cannot cancel this
request.
3. Invoke the countdown card's cancel action, which calls `cancelAutoplay()` and clears
`autoplayPendingVideo` at lines 2073-2078.
4. Resume the suspended request and return a related video. The guards at lines 2027-2033
still pass because autoplay remains enabled, the current video is unchanged, and
`autoplayPendingVideo` is now nil; line 2034 recreates the countdown despite the user's
cancellation.(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:** 2026:2035
**Comment:**
*Race Condition: The fallback `getWatchNext` task is not tied to a playback-intent generation or cancelled by `cancelAutoplay()`. If the user cancels while this request is in flight, `autoplayPendingVideo` is nil, so a late successful response passes the final guard and recreates the countdown. Capture a generation before starting the request and require it to remain current before calling `startAutoplayCountdown`.
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| self.autoplayCountdownTask = Task { [weak self] in | ||
| while true { | ||
| do { | ||
| try await Task.sleep(for: .seconds(1)) | ||
| } catch { | ||
| return | ||
| } | ||
| guard let self, self.autoplayPendingVideo?.videoId == next.videoId else { return } | ||
| self.autoplayCountdownRemaining -= 1 | ||
| if self.autoplayCountdownRemaining <= 0 { | ||
| self.confirmAutoplayNow() | ||
| return | ||
| } |
There was a problem hiding this comment.
Suggestion: The countdown task remains active when the user invokes pause or resume after the video has finished. Resuming the current video therefore does not dismiss the pending autoplay transition, and the task can still call confirmAutoplayNow() seconds later and replace the video the user resumed. Clear or invalidate the countdown on explicit pause/resume intent, consistent with the existing playback-intent generation handling. [stale reference]
Severity Level: Major ⚠️
- ❌ Resuming after a finish can unexpectedly replace the current video.
- ⚠️ Explicit pause/resume leaves stale autoplay state active.
- ⚠️ Playback controls conflict with countdown navigation.Steps of Reproduction ✅
1. Enable autoplay, finish a video, and leave the resulting countdown active. The task is
created at `Sources/Kaset/Services/Player/YouTubePlayerService.swift:2042-2056`.
2. Invoke the service's explicit `pause()` at `YouTubePlayerService.swift:909-913`; it
begins a playback intent and pauses the controller, but does not call
`clearAutoplayCountdown()`.
3. Invoke `resume()` at `YouTubePlayerService.swift:843-847`. `performResume()` resumes
the current video at lines 849-880, while the countdown task remains active.
4. Wait for the task's remaining seconds to expire. The task at lines 2049-2053 calls
`confirmAutoplayNow()`, and lines 2061-2065 advance from the video the user resumed to the
queued video.(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:** 2042:2054
**Comment:**
*Stale Reference: The countdown task remains active when the user invokes pause or resume after the video has finished. Resuming the current video therefore does not dismiss the pending autoplay transition, and the task can still call `confirmAutoplayNow()` seconds later and replace the video the user resumed. Clear or invalidate the countdown on explicit pause/resume intent, consistent with the existing playback-intent generation handling.
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
User description
Adds a YouTube-style Autoplay toggle (default off) to the related header. When on, a finished video queues the next suggested video behind a countdown card over the surface — Up Next + the next title, a 5s ring you can click to play now, or Cancel. On timeout/click it advances and starts playing automatically. Mixes/playlists are unaffected (their own queue advances via page drift).
SettingsManager.youtubeAutoplayEnabled(default off, persisted)YouTubePlayerService, cleared by play/stop/advanceYouTubeAutoplayCountdownOverlayhosted by both the inline watch view and the floating windowNote:
AppLocalizationTestsstays red exactly as on main (fork-only keys pending Crowdin propagation); the catalog↔lproj parity test passes.🤖 Generated with Claude Code
CodeAnt-AI Description
Add optional YouTube-style autoplay for suggested videos
What Changed
Impact
✅ User-controlled autoplay✅ Clearer next-video choice✅ Fewer unexpected video switches💡 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:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
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:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
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.