jellyflood/Swiftfin/Views/AdminDashboardView/ActiveSessions/ActiveSessionsView/Components/ActiveSessionProgressSectio...

107 lines
3.7 KiB
Swift

//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2025 Jellyfin & Jellyfin Contributors
//
import Defaults
import JellyfinAPI
import SwiftUI
extension ActiveSessionsView {
struct ProgressSection: View {
@Default(.accentColor)
private var accentColor
private let item: BaseItemDto
private let playState: PlayerStateInfo
private let transcodingInfo: TranscodingInfo?
private let showTranscodeReason: Bool
private var playbackPercentage: Double {
clamp(Double(playState.positionTicks ?? 0) / Double(item.runTimeTicks ?? 1), min: 0, max: 1)
}
private var transcodingPercentage: Double? {
guard let c = transcodingInfo?.completionPercentage else { return nil }
return clamp(c / 100.0, min: 0, max: 1)
}
init(item: BaseItemDto, playState: PlayerStateInfo, transcodingInfo: TranscodingInfo?, showTranscodeReason: Bool = false) {
self.item = item
self.playState = playState
self.transcodingInfo = transcodingInfo
self.showTranscodeReason = showTranscodeReason
}
@ViewBuilder
private var playbackInformation: some View {
HStack(alignment: .top) {
FlowLayout(
alignment: .leading,
direction: .down,
spacing: 4,
lineSpacing: 4,
minRowLength: 1
) {
if playState.isPaused ?? false {
Image(systemName: "pause.fill")
.transition(.opacity.combined(with: .scale).animation(.bouncy))
.padding(.trailing, 8)
} else {
Image(systemName: "play.fill")
.transition(.opacity.combined(with: .scale).animation(.bouncy))
.padding(.trailing, 8)
}
if let playMethod = playState.playMethod,
let transcodeReasons = transcodingInfo?.transcodeReasons,
playMethod == .transcode
{
if showTranscodeReason {
let transcodeIcons = Set(transcodeReasons.map(\.systemImage)).sorted()
ForEach(transcodeIcons, id: \.self) { icon in
Image(systemName: icon)
.foregroundStyle(.secondary)
.symbolRenderingMode(.monochrome)
}
}
Text(playMethod)
.foregroundStyle(.secondary)
}
}
Spacer()
HStack(spacing: 2) {
Text(playState.position ?? .zero, format: .runtime)
Text("/")
Text(item.runtime ?? .zero, format: .runtime)
}
.monospacedDigit()
.fixedSize(horizontal: true, vertical: true)
}
.font(.subheadline)
}
var body: some View {
VStack {
ProgressView(value: playbackPercentage)
.progressViewStyle(.playback.secondaryProgress(transcodingPercentage))
.frame(height: 5)
.foregroundStyle(.primary, .secondary, .orange)
playbackInformation
}
}
}
}