From 5bc774dd8317b0c718d6e8148256c0f9c3e1fa4e Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Thu, 18 Jun 2026 23:49:15 +0200 Subject: [PATCH] Fix missing storyboards when advancing to next queued video Queue items carry a pre-resolved stream, so play() takes the provided-stream shortcut and skips the full API fetch that populates storyboards. The follow-up loadOnlineStreams() call fetched the response containing them but dropped storyboards on the floor. Keep the storyboards in loadOnlineStreams() and apply them when none are set (preserving local storyboards for downloaded playback), and bail out if the current video changed during the fetch. --- Yattee/Services/Player/PlayerService.swift | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Yattee/Services/Player/PlayerService.swift b/Yattee/Services/Player/PlayerService.swift index ec93d437..b4d90476 100644 --- a/Yattee/Services/Player/PlayerService.swift +++ b/Yattee/Services/Player/PlayerService.swift @@ -1287,12 +1287,25 @@ final class PlayerService { defer { isLoadingOnlineStreams = false } do { - let (_, streams, captions) = try await fetchVideoStreamsAndCaptions(for: video) + let (_, streams, captions, storyboards) = try await fetchVideoStreamsAndCaptionsAndStoryboards(for: video) + + // Bail if the user moved on to another video while we were fetching + guard state.currentVideo?.id == video.id else { return } + // Combine downloaded streams with online streams (downloaded first) let combined = downloadedStreams + streams availableStreams = combined availableCaptions = captions + // Queue items carry a pre-resolved stream, so play() skipped the + // full fetch that populates storyboards — fill them in here. + // Don't clobber a local storyboard loaded for downloaded playback. + if state.storyboards.isEmpty, !storyboards.isEmpty { + Task { await StoryboardService.shared.clearCache() } + state.storyboards = storyboards + LoggingService.shared.logPlayer("Loaded \(storyboards.count) storyboards (deferred), preferred: \(state.preferredStoryboard?.width ?? 0)x\(state.preferredStoryboard?.height ?? 0)") + } + // The freshly fetched streams have new (signed) URLs that won't // match `state.currentStream`/`state.currentAudioStream` from the // queue's pre-resolved entry — repoint state to the equivalent