// // PlayerView.swift // // Copyright 2026 Brendan Szymanski // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // SPDX-License-Identifier: GPL-3.0-or-later // import Adwaita import Foundation import LuminateCore public struct PlayerView: View { public var item: Components.Schemas.BaseItemDto public var client: JellyfinClient public var userId: String public var mediaSourceId: String public var playSessionId: String public var streamURL: URL @State private var isPlaying = true @State private var position: Double = 0 @State private var duration: Double = 0 @State private var showControls = true public var onClose: () -> Void public init( item: Components.Schemas.BaseItemDto, client: JellyfinClient, userId: String, mediaSourceId: String, playSessionId: String, streamURL: URL, onClose: @escaping () -> Void ) { self.item = item self.client = client self.userId = userId self.mediaSourceId = mediaSourceId self.playSessionId = playSessionId self.streamURL = streamURL self.onClose = onClose } public var view: Body { VStack { VideoPlayerWidget( url: streamURL.absoluteString, isPlaying: $isPlaying, position: $position, duration: $duration ) .hexpand(true) .vexpand(true) if showControls { PlayerControls( isPlaying: $isPlaying, position: $position, duration: $duration, onTogglePlay: { isPlaying.toggle() }, onSeekBack: { position = max(0, position - 10) }, onSeekForward: { position = min(duration, position + 10) }, onFullscreen: {}, onClose: { stopPlayback() onClose() } ) } } } private func startPlayback() { Task { try? await client.reportPlaybackStart( info: .init( itemId: item.id, mediaSourceId: mediaSourceId, playSessionId: playSessionId ) ) } } private func stopPlayback() { Task { try? await client.reportPlaybackStopped( info: .init( itemId: item.id, mediaSourceId: mediaSourceId, positionTicks: Int64(position * 10_000_000), playSessionId: playSessionId ) ) } } }