mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
Playback state improvements
This commit is contained in:
parent
f80b61f9c7
commit
151121aa31
@ -1,2 +1 @@
|
|||||||
5
|
5
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ parent_config: https://raw.githubusercontent.com/sindresorhus/swiftlint-config/m
|
|||||||
disabled_rules:
|
disabled_rules:
|
||||||
- identifier_name
|
- identifier_name
|
||||||
- opening_brace
|
- opening_brace
|
||||||
|
- number_separator
|
||||||
- multiline_arguments
|
- multiline_arguments
|
||||||
|
|
||||||
excluded:
|
excluded:
|
||||||
|
@ -22,4 +22,9 @@ final class PlaybackState: ObservableObject {
|
|||||||
|
|
||||||
return size.width / size.height
|
return size.width / size.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
stream = nil
|
||||||
|
time = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,14 +19,14 @@ final class PlayerState: ObservableObject {
|
|||||||
private(set) var currentRate: Float = 0.0
|
private(set) var currentRate: Float = 0.0
|
||||||
static let availableRates: [Double] = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]
|
static let availableRates: [Double] = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]
|
||||||
|
|
||||||
var playbackState: PlaybackState?
|
var playbackState: PlaybackState
|
||||||
var timeObserver: Any?
|
var timeObserver: Any?
|
||||||
|
|
||||||
let maxResolution: Stream.Resolution?
|
let maxResolution: Stream.Resolution?
|
||||||
|
|
||||||
var playingOutsideViewController = false
|
var playingOutsideViewController = false
|
||||||
|
|
||||||
init(_ video: Video? = nil, playbackState: PlaybackState? = nil, maxResolution: Stream.Resolution? = nil) {
|
init(_ video: Video? = nil, playbackState: PlaybackState, maxResolution: Stream.Resolution? = nil) {
|
||||||
self.video = video
|
self.video = video
|
||||||
self.playbackState = playbackState
|
self.playbackState = playbackState
|
||||||
self.maxResolution = maxResolution
|
self.maxResolution = maxResolution
|
||||||
@ -41,6 +41,8 @@ final class PlayerState: ObservableObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playbackState.reset()
|
||||||
|
|
||||||
loadExtendedVideoDetails(video) { video in
|
loadExtendedVideoDetails(video) { video in
|
||||||
self.video = video
|
self.video = video
|
||||||
self.playVideo(video)
|
self.playVideo(video)
|
||||||
@ -98,12 +100,16 @@ final class PlayerState: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func playStream(_ stream: Stream) {
|
fileprivate func playStream(_ stream: Stream) {
|
||||||
|
guard player != nil else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
logger.warning("loading \(stream.description) to player")
|
logger.warning("loading \(stream.description) to player")
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.saveTime()
|
self.saveTime()
|
||||||
self.player?.replaceCurrentItem(with: self.playerItemWithMetadata(for: stream))
|
self.player?.replaceCurrentItem(with: self.playerItemWithMetadata(for: stream))
|
||||||
self.playbackState?.stream = stream
|
self.playbackState.stream = stream
|
||||||
if self.timeObserver == nil {
|
if self.timeObserver == nil {
|
||||||
self.addTimeObserver()
|
self.addTimeObserver()
|
||||||
}
|
}
|
||||||
@ -259,7 +265,7 @@ final class PlayerState: ObservableObject {
|
|||||||
self.player.rate = self.currentRate
|
self.player.rate = self.currentRate
|
||||||
}
|
}
|
||||||
|
|
||||||
self.playbackState?.time = self.player.currentTime()
|
self.playbackState.time = self.player.currentTime()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@StateObject private var navigationState = NavigationState()
|
@StateObject private var navigationState = NavigationState()
|
||||||
|
@StateObject private var playbackState = PlaybackState()
|
||||||
@StateObject private var searchState = SearchState()
|
@StateObject private var searchState = SearchState()
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@ -37,6 +38,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
.environmentObject(navigationState)
|
.environmentObject(navigationState)
|
||||||
|
.environmentObject(playbackState)
|
||||||
.environmentObject(searchState)
|
.environmentObject(searchState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,25 +2,32 @@ import Foundation
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct PlaybackBar: View {
|
struct PlaybackBar: View {
|
||||||
@Environment(\.dismiss) private var dismiss
|
|
||||||
|
|
||||||
@ObservedObject var playbackState: PlaybackState
|
|
||||||
let video: Video
|
let video: Video
|
||||||
|
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
@EnvironmentObject private var playbackState: PlaybackState
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
closeButton
|
closeButton
|
||||||
.frame(minWidth: 0, maxWidth: 60, alignment: .leading)
|
.frame(width: 60, alignment: .leading)
|
||||||
|
|
||||||
Text(playbackFinishAtString)
|
Text(playbackFinishAtString)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
.font(.caption2)
|
.font(.caption2)
|
||||||
.frame(minWidth: 0, maxWidth: .infinity)
|
.frame(minWidth: 60, maxWidth: .infinity)
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
if playbackState.stream != nil {
|
||||||
Text(currentStreamString)
|
Text(currentStreamString)
|
||||||
|
} else {
|
||||||
|
Image(systemName: "bolt.horizontal.fill")
|
||||||
|
}
|
||||||
|
}
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
.font(.caption2)
|
.font(.caption2)
|
||||||
.frame(minWidth: 0, maxWidth: 60, alignment: .trailing)
|
.frame(width: 60, alignment: .trailing)
|
||||||
|
.fixedSize(horizontal: true, vertical: true)
|
||||||
}
|
}
|
||||||
.padding(4)
|
.padding(4)
|
||||||
.background(.black)
|
.background(.black)
|
||||||
@ -37,6 +44,10 @@ struct PlaybackBar: View {
|
|||||||
|
|
||||||
let remainingSeconds = video.length - playbackState.time!.seconds
|
let remainingSeconds = video.length - playbackState.time!.seconds
|
||||||
|
|
||||||
|
if remainingSeconds < 60 {
|
||||||
|
return "less than a minute"
|
||||||
|
}
|
||||||
|
|
||||||
let timeFinishAt = Date.now.addingTimeInterval(remainingSeconds)
|
let timeFinishAt = Date.now.addingTimeInterval(remainingSeconds)
|
||||||
let timeFinishAtString = timeFinishAt.formatted(date: .omitted, time: .shortened)
|
let timeFinishAtString = timeFinishAt.formatted(date: .omitted, time: .shortened)
|
||||||
|
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct Player: UIViewControllerRepresentable {
|
struct Player: UIViewControllerRepresentable {
|
||||||
@ObservedObject var playbackState: PlaybackState
|
@EnvironmentObject<PlaybackState> private var playbackState
|
||||||
|
|
||||||
var video: Video?
|
var video: Video?
|
||||||
|
|
||||||
func makeUIViewController(context _: Context) -> PlayerViewController {
|
func makeUIViewController(context _: Context) -> PlayerViewController {
|
||||||
let controller = PlayerViewController()
|
let controller = PlayerViewController()
|
||||||
|
|
||||||
controller.playbackState = playbackState
|
|
||||||
controller.video = video
|
controller.video = video
|
||||||
|
controller.playbackState = playbackState
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,7 @@ final class PlayerViewController: UIViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadPlayer() {
|
func loadPlayer() {
|
||||||
playerState = PlayerState()
|
playerState = PlayerState(playbackState: playbackState)
|
||||||
playerState.playbackState = playbackState
|
|
||||||
|
|
||||||
guard !playerLoaded else {
|
guard !playerLoaded else {
|
||||||
return
|
return
|
||||||
|
@ -13,11 +13,10 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EnvironmentObject<NavigationState> private var navigationState
|
@EnvironmentObject<NavigationState> private var navigationState
|
||||||
|
@EnvironmentObject<PlaybackState> private var playbackState
|
||||||
|
|
||||||
@ObservedObject private var store = Store<Video>()
|
@ObservedObject private var store = Store<Video>()
|
||||||
|
|
||||||
@ObservedObject private var playbackState = PlaybackState()
|
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||||
#endif
|
#endif
|
||||||
@ -36,19 +35,21 @@ struct VideoPlayerView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
Player(playbackState: playbackState, video: video)
|
Player(video: video)
|
||||||
|
.environmentObject(playbackState)
|
||||||
#else
|
#else
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if verticalSizeClass == .regular {
|
if verticalSizeClass == .regular {
|
||||||
PlaybackBar(playbackState: playbackState, video: video)
|
PlaybackBar(video: video)
|
||||||
}
|
}
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
PlaybackBar(playbackState: playbackState, video: video)
|
PlaybackBar(video: video)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Player(playbackState: playbackState, video: video)
|
Player(video: video)
|
||||||
|
.environmentObject(playbackState)
|
||||||
.modifier(VideoPlayerSizeModifier(geometry: geometry, aspectRatio: playbackState.aspectRatio))
|
.modifier(VideoPlayerSizeModifier(geometry: geometry, aspectRatio: playbackState.aspectRatio))
|
||||||
}
|
}
|
||||||
.background(.black)
|
.background(.black)
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct Player: NSViewControllerRepresentable {
|
struct Player: NSViewControllerRepresentable {
|
||||||
@ObservedObject var playbackState: PlaybackState
|
@EnvironmentObject<PlaybackState> private var playbackState
|
||||||
|
|
||||||
var video: Video!
|
var video: Video!
|
||||||
|
|
||||||
func makeNSViewController(context _: Context) -> PlayerViewController {
|
func makeNSViewController(context _: Context) -> PlayerViewController {
|
||||||
let controller = PlayerViewController()
|
let controller = PlayerViewController()
|
||||||
|
|
||||||
controller.playbackState = playbackState
|
|
||||||
controller.video = video
|
controller.video = video
|
||||||
|
controller.playbackState = playbackState
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct TVNavigationView: View {
|
struct TVNavigationView: View {
|
||||||
@EnvironmentObject<NavigationState> private var navigationState
|
@EnvironmentObject<NavigationState> private var navigationState
|
||||||
|
@EnvironmentObject<PlaybackState> private var playbackState
|
||||||
|
|
||||||
@State private var showingOptions = false
|
@State private var showingOptions = false
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ struct TVNavigationView: View {
|
|||||||
.fullScreenCover(isPresented: $navigationState.showingVideo) {
|
.fullScreenCover(isPresented: $navigationState.showingVideo) {
|
||||||
if let video = navigationState.video {
|
if let video = navigationState.video {
|
||||||
VideoPlayerView(video)
|
VideoPlayerView(video)
|
||||||
|
.environmentObject(playbackState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onPlayPauseCommand { showingOptions.toggle() }
|
.onPlayPauseCommand { showingOptions.toggle() }
|
||||||
|
Loading…
Reference in New Issue
Block a user