From 4ff210cdde243f38dcc3a8440e0576177ae6f97f Mon Sep 17 00:00:00 2001 From: Brendan Szymanski Date: Wed, 24 Jun 2026 00:28:46 -0400 Subject: [PATCH] Wire Play button in MovieDetailView --- Sources/Luminate/Luminate.swift | 2 +- .../LuminateUI/Components/EpisodeList.swift | 34 +++++++++++++++++-- .../Components/MovieDetailView.swift | 26 ++++++++++++-- .../LuminateUI/Components/TVShowView.swift | 4 ++- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/Sources/Luminate/Luminate.swift b/Sources/Luminate/Luminate.swift index 74626c4..585d55c 100644 --- a/Sources/Luminate/Luminate.swift +++ b/Sources/Luminate/Luminate.swift @@ -137,7 +137,7 @@ struct ContentView: View { } .navigationTitle(page.description) case .movieDetail(let item): - MovieDetailView(for: item) + MovieDetailView(for: item, navigation: $stack) .topToolbar { ToolbarView() } diff --git a/Sources/LuminateUI/Components/EpisodeList.swift b/Sources/LuminateUI/Components/EpisodeList.swift index e52232a..d19080d 100644 --- a/Sources/LuminateUI/Components/EpisodeList.swift +++ b/Sources/LuminateUI/Components/EpisodeList.swift @@ -30,6 +30,7 @@ struct EpisodeList: View { var seasonId: String var client: JellyfinClient var userId: String + @Binding var navigation: NavigationStack @State private var episodes: [BaseItemDto] = [] var view: Body { @@ -37,7 +38,8 @@ struct EpisodeList: View { ForEach(episodes) { episode in EpisodeRow( episode: episode, - client: client + client: client, + navigation: $navigation ) } } @@ -62,10 +64,18 @@ struct EpisodeRow: View { var episode: BaseItemDto var client: JellyfinClient + @Binding var navigation: NavigationStack @Injected(\.imageService) var imageService @Injected(\.viewUpdateScheduler) var viewUpdateScheduler + @Injected(\.userId) var userId @State private var imageData: Data? + init(episode: BaseItemDto, client: JellyfinClient, navigation: Binding>) { + self.episode = episode + self.client = client + self._navigation = navigation + } + var view: Body { HStack { if let data = imageData { @@ -92,7 +102,27 @@ struct EpisodeRow: View { } .hexpand(true) Button(icon: .default(icon: .mediaPlaybackStart)) { - + Task { + guard let itemId = episode.id else { return } + guard let info = try? await client.getPlaybackInfo( + itemId: itemId, userId: userId + ) else { return } + guard let mediaSource = info.mediaSources?.first, + let playSessionId = info.playSessionId else { return } + guard let streamURL = await client.streamURL( + itemId: itemId, + mediaSourceId: mediaSource.id ?? "", + playSessionId: playSessionId + ) else { return } + let state = PlayerState( + itemId: itemId, + mediaSourceId: mediaSource.id ?? "", + playSessionId: playSessionId, + streamURL: streamURL, + serverBase: await client.serverURL.absoluteString + ) + navigation.push(.player(state)) + } } .flat() } diff --git a/Sources/LuminateUI/Components/MovieDetailView.swift b/Sources/LuminateUI/Components/MovieDetailView.swift index f86a5ad..05f45e6 100644 --- a/Sources/LuminateUI/Components/MovieDetailView.swift +++ b/Sources/LuminateUI/Components/MovieDetailView.swift @@ -27,6 +27,7 @@ import LuminateDI public struct MovieDetailView: View { var item: BaseItemDto + @Binding var navigation: NavigationStack @Injected(\.client) private var client @Injected(\.userId) private var userId @Injected(\.imageService) private var imageService @@ -35,8 +36,9 @@ public struct MovieDetailView: View { @State private var similarItems: [BaseItemDto] = [] @State private var backdropData: Data? - public init(for item: BaseItemDto) { + public init(for item: BaseItemDto, navigation: Binding>) { self.item = item + self._navigation = navigation _isFavorite = .init(wrappedValue: item.userData?.value1.isFavorite ?? false) _isPlayed = .init(wrappedValue: item.userData?.value1.played ?? false) } @@ -71,7 +73,27 @@ public struct MovieDetailView: View { .halign(.start) HStack { Button("Play", icon: .default(icon: .mediaPlaybackStart)) { - + Task { + guard let itemId = item.id else { return } + guard let info = try? await client.getPlaybackInfo( + itemId: itemId, userId: userId + ) else { return } + guard let mediaSource = info.mediaSources?.first, + let playSessionId = info.playSessionId else { return } + guard let streamURL = await client.streamURL( + itemId: itemId, + mediaSourceId: mediaSource.id ?? "", + playSessionId: playSessionId + ) else { return } + let state = PlayerState( + itemId: itemId, + mediaSourceId: mediaSource.id ?? "", + playSessionId: playSessionId, + streamURL: streamURL, + serverBase: await client.serverURL.absoluteString + ) + navigation.push(.player(state)) + } } .suggested() Button(icon: .default(icon: .bookmarkNew)) { diff --git a/Sources/LuminateUI/Components/TVShowView.swift b/Sources/LuminateUI/Components/TVShowView.swift index 6b37a5b..1b2e3e9 100644 --- a/Sources/LuminateUI/Components/TVShowView.swift +++ b/Sources/LuminateUI/Components/TVShowView.swift @@ -29,6 +29,7 @@ struct TVShowView: View { var item: BaseItemDto var client: JellyfinClient var userId: String + @Binding var navigation: NavigationStack @Injected(\.imageService) var imageService @State private var seasons: [BaseItemDto] = [] @State private var selectedSeasonId: String? @@ -89,7 +90,8 @@ struct TVShowView: View { seriesId: item.id ?? "", seasonId: seasonId, client: client, - userId: userId + userId: userId, + navigation: $navigation ) } }