Playback rate menu

This commit is contained in:
Arkadiusz Fal 2021-11-02 18:24:59 +01:00
parent 320207e439
commit 0091af683f
3 changed files with 70 additions and 6 deletions

View File

@ -11,6 +11,7 @@ import SwiftUI
import SwiftyJSON
final class PlayerModel: ObservableObject {
static let availableRates: [Float] = [0.5, 0.67, 0.8, 1, 1.25, 1.5, 2]
let logger = Logger(label: "net.arekf.Pearvidious.ps")
private(set) var player = AVPlayer()
@ -23,7 +24,7 @@ final class PlayerModel: ObservableObject {
@Published var presentingPlayer = false
@Published var stream: Stream?
@Published var currentRate: Float?
@Published var currentRate: Float = 1.0 { didSet { player.rate = currentRate } }
@Published var availableStreams = [Stream]() { didSet { rebuildTVMenu() } }
@Published var streamSelection: Stream? { didSet { rebuildTVMenu() } }
@ -418,6 +419,10 @@ final class PlayerModel: ObservableObject {
self.objectWillChange.send()
}
if player.timeControlStatus == .playing, player.rate != self.currentRate {
player.rate = self.currentRate
}
#if os(macOS)
if player.timeControlStatus == .playing {
ScreenSaverManager.shared.disable(reason: "Yattee is playing video")
@ -469,4 +474,12 @@ final class PlayerModel: ObservableObject {
currentArtwork = MPMediaItemArtwork(boundsSize: image!.size) { _ in image! }
}
func rateLabel(_ rate: Float) -> String {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 2
return "\(formatter.string(from: NSNumber(value: rate))!)×"
}
}

View File

@ -42,12 +42,33 @@ extension PlayerModel {
self.restoreLastSkippedSegment()
}
}
private var rateMenu: UIMenu {
UIMenu(title: "Playback rate", image: UIImage(systemName: rateMenuSystemImage), children: rateMenuActions)
}
private var rateMenuSystemImage: String {
[0.0, 1.0].contains(currentRate) ? "speedometer" : (currentRate < 1.0 ? "tortoise.fill" : "hare.fill")
}
private var rateMenuActions: [UIAction] {
PlayerModel.availableRates.map { rate in
let image = currentRate == Float(rate) ? UIImage(systemName: "checkmark") : nil
return UIAction(title: rateLabel(rate), image: image) { _ in
DispatchQueue.main.async {
self.currentRate = rate
}
}
}
}
#endif
func rebuildTVMenu() {
#if os(tvOS)
avPlayerViewController?.transportBarCustomMenuItems = [
restoreLastSkippedSegmentAction,
rateMenu,
streamsMenu
].compactMap { $0 }
#endif

View File

@ -14,9 +14,15 @@ struct PlaybackBar: View {
closeButton
if player.currentItem != nil {
Text(playbackStatus)
.foregroundColor(.gray)
.font(.caption2)
HStack {
Text(playbackStatus)
Text("")
rateMenu
}
.font(.caption2)
.foregroundColor(.gray)
Spacer()
@ -44,7 +50,6 @@ struct PlaybackBar: View {
.frame(maxWidth: 180)
#endif
}
.environment(\.colorScheme, .dark)
.transaction { t in t.animation = .none }
.foregroundColor(.gray)
.font(.caption2)
@ -52,6 +57,7 @@ struct PlaybackBar: View {
Spacer()
}
}
.environment(\.colorScheme, .dark)
.frame(minWidth: 0, maxWidth: .infinity)
.padding(4)
.background(.black)
@ -82,7 +88,8 @@ struct PlaybackBar: View {
return "loading..."
}
let remainingSeconds = player.currentVideo!.length - player.time!.seconds
let videoLengthAtRate = player.currentVideo!.length / Double(player.currentRate)
let remainingSeconds = videoLengthAtRate - player.time!.seconds
if remainingSeconds < 60 {
return "less than a minute"
@ -94,6 +101,29 @@ struct PlaybackBar: View {
return "ends at \(timeFinishAtString)"
}
private var rateMenu: some View {
#if os(macOS)
ratePicker
.labelsHidden()
.frame(maxWidth: 70)
#else
Menu {
ratePicker
} label: {
Text(player.rateLabel(player.currentRate))
}
#endif
}
private var ratePicker: some View {
Picker("", selection: $player.currentRate) {
ForEach(PlayerModel.availableRates, id: \.self) { rate in
Text(player.rateLabel(rate)).tag(rate)
}
}
}
private var restoreLastSkippedSegmentButton: some View {
HStack(spacing: 4) {
Button {