Luminate/Sources/LuminatePlayer/PlayerView.swift

110 lines
3.3 KiB
Swift

//
// PlayerView.swift
//
// Copyright 2026 Brendan Szymanski <hello@bscubed.dev>
//
// 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 <https://www.gnu.org/licenses/>.
//
// 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
)
)
}
}
}