2021-07-18 22:32:46 +00:00
|
|
|
import AVKit
|
2021-10-05 20:20:09 +00:00
|
|
|
import Defaults
|
2021-07-18 22:32:46 +00:00
|
|
|
import Siesta
|
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
struct VideoPlayerView: View {
|
2021-11-08 16:29:35 +00:00
|
|
|
static let defaultAspectRatio = 16 / 9.0
|
2021-09-18 20:36:42 +00:00
|
|
|
static var defaultMinimumHeightLeft: Double {
|
2021-08-22 19:13:33 +00:00
|
|
|
#if os(macOS)
|
|
|
|
300
|
|
|
|
#else
|
|
|
|
200
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-11-03 23:00:17 +00:00
|
|
|
@State private var playerSize: CGSize = .zero
|
2021-12-19 23:36:12 +00:00
|
|
|
@State private var fullScreenDetails = false
|
2021-07-18 22:32:46 +00:00
|
|
|
|
2021-11-28 14:37:55 +00:00
|
|
|
@Environment(\.colorScheme) private var colorScheme
|
|
|
|
|
2021-08-22 19:13:33 +00:00
|
|
|
#if os(iOS)
|
2021-11-28 14:37:55 +00:00
|
|
|
@Environment(\.presentationMode) private var presentationMode
|
2021-10-05 20:20:09 +00:00
|
|
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
2021-08-22 19:13:33 +00:00
|
|
|
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
|
|
|
#endif
|
2021-08-16 22:46:18 +00:00
|
|
|
|
2021-12-24 19:20:05 +00:00
|
|
|
@EnvironmentObject<AccountsModel> private var accounts
|
2021-10-05 20:20:09 +00:00
|
|
|
@EnvironmentObject<PlayerModel> private var player
|
2021-09-25 08:18:22 +00:00
|
|
|
|
2021-10-05 20:20:09 +00:00
|
|
|
var body: some View {
|
2021-11-04 22:01:27 +00:00
|
|
|
#if os(macOS)
|
|
|
|
HSplitView {
|
|
|
|
content
|
|
|
|
}
|
2021-12-24 19:20:05 +00:00
|
|
|
.onOpenURL { OpenURLHandler(accounts: accounts, player: player).handle($0) }
|
2021-12-19 17:17:04 +00:00
|
|
|
.frame(minWidth: 950, minHeight: 700)
|
2021-11-04 22:01:27 +00:00
|
|
|
#else
|
|
|
|
GeometryReader { geometry in
|
2021-11-03 23:00:17 +00:00
|
|
|
HStack(spacing: 0) {
|
|
|
|
content
|
|
|
|
}
|
|
|
|
.onAppear {
|
|
|
|
self.playerSize = geometry.size
|
|
|
|
}
|
|
|
|
.onChange(of: geometry.size) { size in
|
|
|
|
self.playerSize = size
|
|
|
|
}
|
2021-11-04 22:01:27 +00:00
|
|
|
}
|
2021-12-17 19:53:36 +00:00
|
|
|
.navigationBarHidden(true)
|
2021-11-04 22:01:27 +00:00
|
|
|
#endif
|
2021-07-18 22:32:46 +00:00
|
|
|
}
|
|
|
|
|
2021-10-05 20:20:09 +00:00
|
|
|
var content: some View {
|
|
|
|
Group {
|
2021-10-28 17:14:55 +00:00
|
|
|
Group {
|
2021-10-05 20:20:09 +00:00
|
|
|
#if os(tvOS)
|
2021-10-28 17:14:55 +00:00
|
|
|
player.playerView
|
2021-10-05 20:20:09 +00:00
|
|
|
#else
|
|
|
|
GeometryReader { geometry in
|
|
|
|
VStack(spacing: 0) {
|
|
|
|
#if os(iOS)
|
|
|
|
if verticalSizeClass == .regular {
|
|
|
|
PlaybackBar()
|
|
|
|
}
|
|
|
|
#elseif os(macOS)
|
|
|
|
PlaybackBar()
|
|
|
|
#endif
|
2021-07-18 22:32:46 +00:00
|
|
|
|
2021-10-05 20:20:09 +00:00
|
|
|
if player.currentItem.isNil {
|
|
|
|
playerPlaceholder(geometry: geometry)
|
2021-12-19 17:17:04 +00:00
|
|
|
} else if player.playingInPictureInPicture {
|
|
|
|
pictureInPicturePlaceholder(geometry: geometry)
|
2021-10-05 20:20:09 +00:00
|
|
|
} else {
|
2021-12-19 17:17:04 +00:00
|
|
|
player.playerView
|
|
|
|
.modifier(
|
|
|
|
VideoPlayerSizeModifier(
|
|
|
|
geometry: geometry,
|
|
|
|
aspectRatio: player.controller?.aspectRatio
|
|
|
|
)
|
|
|
|
)
|
2021-08-22 19:13:33 +00:00
|
|
|
}
|
2021-10-05 20:20:09 +00:00
|
|
|
}
|
2021-08-22 19:13:33 +00:00
|
|
|
#if os(iOS)
|
2021-11-08 16:29:35 +00:00
|
|
|
.onSwipeGesture(
|
|
|
|
up: {
|
|
|
|
withAnimation {
|
2021-12-24 19:20:05 +00:00
|
|
|
fullScreenDetails = true
|
2021-11-08 16:29:35 +00:00
|
|
|
}
|
|
|
|
},
|
2021-12-24 19:20:05 +00:00
|
|
|
down: { player.hide() }
|
2021-11-08 16:29:35 +00:00
|
|
|
)
|
2021-10-05 20:20:09 +00:00
|
|
|
#endif
|
|
|
|
|
2021-11-28 14:37:55 +00:00
|
|
|
.background(Color.black)
|
2021-10-05 20:20:09 +00:00
|
|
|
|
|
|
|
Group {
|
|
|
|
#if os(iOS)
|
|
|
|
if verticalSizeClass == .regular {
|
2021-12-19 23:36:12 +00:00
|
|
|
VideoDetails(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreenDetails)
|
2021-10-05 20:20:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
2021-12-19 23:36:12 +00:00
|
|
|
VideoDetails(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreenDetails)
|
2021-10-05 20:20:09 +00:00
|
|
|
#endif
|
|
|
|
}
|
2021-11-28 14:37:55 +00:00
|
|
|
.background(colorScheme == .dark ? Color.black : Color.white)
|
2021-12-19 23:36:12 +00:00
|
|
|
.modifier(VideoDetailsPaddingModifier(geometry: geometry, aspectRatio: player.controller?.aspectRatio, fullScreen: fullScreenDetails))
|
2021-08-22 19:13:33 +00:00
|
|
|
}
|
2021-10-05 20:20:09 +00:00
|
|
|
#endif
|
|
|
|
}
|
2021-12-02 20:35:42 +00:00
|
|
|
.background(colorScheme == .dark ? Color.black : Color.white)
|
2021-10-05 20:20:09 +00:00
|
|
|
#if os(macOS)
|
2021-12-02 20:35:42 +00:00
|
|
|
.frame(minWidth: 650)
|
2021-10-05 20:20:09 +00:00
|
|
|
#endif
|
|
|
|
#if os(iOS)
|
|
|
|
if sidebarQueue {
|
2021-12-19 23:36:12 +00:00
|
|
|
PlayerQueueView(sidebarQueue: .constant(true), fullScreen: $fullScreenDetails)
|
2021-10-05 20:20:09 +00:00
|
|
|
.frame(maxWidth: 350)
|
2021-07-18 22:32:46 +00:00
|
|
|
}
|
2021-10-05 20:20:09 +00:00
|
|
|
#elseif os(macOS)
|
2021-11-03 23:00:17 +00:00
|
|
|
if Defaults[.playerSidebar] != .never {
|
2021-12-19 23:36:12 +00:00
|
|
|
PlayerQueueView(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreenDetails)
|
2021-12-02 19:22:55 +00:00
|
|
|
.frame(minWidth: 300)
|
2021-11-03 23:00:17 +00:00
|
|
|
}
|
2021-07-18 22:32:46 +00:00
|
|
|
#endif
|
|
|
|
}
|
2021-10-05 20:20:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func playerPlaceholder(geometry: GeometryProxy) -> some View {
|
|
|
|
HStack {
|
|
|
|
Spacer()
|
|
|
|
VStack {
|
|
|
|
Spacer()
|
|
|
|
VStack(spacing: 10) {
|
|
|
|
#if !os(tvOS)
|
|
|
|
Image(systemName: "ticket")
|
2021-10-19 21:27:04 +00:00
|
|
|
.font(.system(size: 120))
|
2021-10-05 20:20:09 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
Spacer()
|
|
|
|
}
|
|
|
|
.foregroundColor(.gray)
|
|
|
|
Spacer()
|
2021-07-18 22:32:46 +00:00
|
|
|
}
|
2021-10-05 20:20:09 +00:00
|
|
|
.contentShape(Rectangle())
|
|
|
|
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: geometry.size.width / VideoPlayerView.defaultAspectRatio)
|
2021-07-18 22:32:46 +00:00
|
|
|
}
|
2021-08-22 19:13:33 +00:00
|
|
|
|
2021-12-19 17:17:04 +00:00
|
|
|
func pictureInPicturePlaceholder(geometry: GeometryProxy) -> some View {
|
|
|
|
HStack {
|
|
|
|
Spacer()
|
|
|
|
VStack {
|
|
|
|
Spacer()
|
|
|
|
VStack(spacing: 10) {
|
|
|
|
#if !os(tvOS)
|
|
|
|
Image(systemName: "pip")
|
|
|
|
.font(.system(size: 120))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Text("Playing in Picture in Picture")
|
|
|
|
}
|
|
|
|
Spacer()
|
|
|
|
}
|
|
|
|
.foregroundColor(.gray)
|
|
|
|
Spacer()
|
|
|
|
}
|
|
|
|
.contextMenu {
|
|
|
|
Button {
|
|
|
|
player.closePiP()
|
|
|
|
} label: {
|
|
|
|
Label("Exit Picture in Picture", systemImage: "pip.exit")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.contentShape(Rectangle())
|
|
|
|
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: geometry.size.width / VideoPlayerView.defaultAspectRatio)
|
|
|
|
}
|
|
|
|
|
2021-11-03 23:00:17 +00:00
|
|
|
var sidebarQueue: Bool {
|
|
|
|
switch Defaults[.playerSidebar] {
|
|
|
|
case .never:
|
|
|
|
return false
|
|
|
|
case .always:
|
|
|
|
return true
|
|
|
|
case .whenFits:
|
|
|
|
return playerSize.width > 900
|
2021-10-05 20:20:09 +00:00
|
|
|
}
|
2021-11-03 23:00:17 +00:00
|
|
|
}
|
2021-10-05 20:20:09 +00:00
|
|
|
|
2021-11-03 23:00:17 +00:00
|
|
|
var sidebarQueueBinding: Binding<Bool> {
|
|
|
|
Binding(
|
|
|
|
get: { sidebarQueue },
|
|
|
|
set: { _ in }
|
|
|
|
)
|
|
|
|
}
|
2021-08-22 19:13:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct VideoPlayerView_Previews: PreviewProvider {
|
|
|
|
static var previews: some View {
|
2021-10-05 20:20:09 +00:00
|
|
|
VideoPlayerView()
|
|
|
|
.injectFixtureEnvironmentObjects()
|
2021-08-22 19:13:33 +00:00
|
|
|
}
|
2021-07-18 22:32:46 +00:00
|
|
|
}
|