mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
parent
b53b5eac56
commit
f67b1d4feb
@ -318,14 +318,14 @@ final class MPVClient: ObservableObject {
|
|||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
let model = self.backend.model
|
let model = self.backend.model
|
||||||
|
let aspectRatio = self.aspectRatio > 0 && self.aspectRatio < VideoPlayerView.defaultAspectRatio ? self.aspectRatio : VideoPlayerView.defaultAspectRatio
|
||||||
|
let height = [model.playerSize.height, model.playerSize.width / aspectRatio].min()!
|
||||||
|
var insets = 0.0
|
||||||
|
#if os(iOS)
|
||||||
|
insets = OrientationTracker.shared.currentInterfaceOrientation.isPortrait ? SafeAreaModel.shared.safeArea.bottom : 0
|
||||||
|
#endif
|
||||||
|
let offsetY = max(0, model.playingFullScreen ? ((model.playerSize.height / 2.0) - ((height + insets) / 2)) : 0)
|
||||||
UIView.animate(withDuration: 0.2, animations: {
|
UIView.animate(withDuration: 0.2, animations: {
|
||||||
let aspectRatio = self.aspectRatio > 0 && self.aspectRatio < VideoPlayerView.defaultAspectRatio ? self.aspectRatio : VideoPlayerView.defaultAspectRatio
|
|
||||||
let height = [model.playerSize.height, model.playerSize.width / aspectRatio].min()!
|
|
||||||
var insets = 0.0
|
|
||||||
#if os(iOS)
|
|
||||||
insets = OrientationTracker.shared.currentInterfaceOrientation.isPortrait ? SafeArea.insets.bottom : 0
|
|
||||||
#endif
|
|
||||||
let offsetY = model.playingFullScreen ? ((model.playerSize.height / 2.0) - ((height + insets) / 2)) : 0
|
|
||||||
self.glView?.frame = CGRect(x: 0, y: offsetY, width: roundedWidth, height: height)
|
self.glView?.frame = CGRect(x: 0, y: offsetY, width: roundedWidth, height: height)
|
||||||
}) { completion in
|
}) { completion in
|
||||||
if completion {
|
if completion {
|
||||||
|
@ -129,6 +129,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Published var lockedOrientation: UIInterfaceOrientationMask?
|
@Published var lockedOrientation: UIInterfaceOrientationMask?
|
||||||
@Default(.rotateToPortraitOnExitFullScreen) private var rotateToPortraitOnExitFullScreen
|
@Default(.rotateToPortraitOnExitFullScreen) private var rotateToPortraitOnExitFullScreen
|
||||||
|
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var accounts: AccountsModel { .shared }
|
var accounts: AccountsModel { .shared }
|
||||||
@ -981,24 +982,21 @@ final class PlayerModel: ObservableObject {
|
|||||||
Windows.player.toggleFullScreen()
|
Windows.player.toggleFullScreen()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
playingFullScreen = !isFullScreen
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if !playingFullScreen {
|
if playingFullScreen {
|
||||||
playingFullScreen = true
|
guard rotateToLandscapeOnEnterFullScreen.isRotating else { return }
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
if currentVideoIsLandscape {
|
||||||
|
// not sure why but first rotation call is ignore so doing rotate to same orientation first
|
||||||
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
|
||||||
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: rotateToLandscapeOnEnterFullScreen.interaceOrientation)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let rotationOrientation = rotateToPortraitOnExitFullScreen ? UIInterfaceOrientation.portrait : nil
|
let rotationOrientation = rotateToPortraitOnExitFullScreen ? UIInterfaceOrientation.portrait : nil
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: rotationOrientation)
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: rotationOrientation)
|
||||||
// TODO: rework to move view before rotating
|
|
||||||
if SafeArea.insets.left > 0 {
|
|
||||||
Delay.by(0.15) {
|
|
||||||
self.playingFullScreen = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.playingFullScreen = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
playingFullScreen = !isFullScreen
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1036,6 +1034,12 @@ final class PlayerModel: ObservableObject {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var currentVideoIsLandscape: Bool {
|
||||||
|
guard currentVideo != nil else { return false }
|
||||||
|
|
||||||
|
return aspectRatio > 1
|
||||||
|
}
|
||||||
|
|
||||||
var formattedSize: String {
|
var formattedSize: String {
|
||||||
guard let videoWidth = backend?.videoWidth, let videoHeight = backend?.videoHeight else { return "unknown" }
|
guard let videoWidth = backend?.videoWidth, let videoHeight = backend?.videoHeight else { return "unknown" }
|
||||||
return "\(String(format: "%.2f", videoWidth))\u{d7}\(String(format: "%.2f", videoHeight))"
|
return "\(String(format: "%.2f", videoWidth))\u{d7}\(String(format: "%.2f", videoHeight))"
|
||||||
|
@ -189,6 +189,10 @@ extension Defaults.Keys {
|
|||||||
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
||||||
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone)
|
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone)
|
||||||
static let rotateToPortraitOnExitFullScreen = Key<Bool>("rotateToPortraitOnExitFullScreen", default: UIDevice.current.userInterfaceIdiom == .phone)
|
static let rotateToPortraitOnExitFullScreen = Key<Bool>("rotateToPortraitOnExitFullScreen", default: UIDevice.current.userInterfaceIdiom == .phone)
|
||||||
|
static let rotateToLandscapeOnEnterFullScreen = Key<FullScreenRotationSetting>(
|
||||||
|
"rotateToLandscapeOnEnterFullScreen",
|
||||||
|
default: UIDevice.current.userInterfaceIdiom == .phone ? .landscapeRight : .disabled
|
||||||
|
)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static let showMPVPlaybackStats = Key<Bool>("showMPVPlaybackStats", default: false)
|
static let showMPVPlaybackStats = Key<Bool>("showMPVPlaybackStats", default: false)
|
||||||
@ -416,3 +420,24 @@ enum PlayerTapGestureAction: String, CaseIterable, Defaults.Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum FullScreenRotationSetting: String, CaseIterable, Defaults.Serializable {
|
||||||
|
case disabled
|
||||||
|
case landscapeLeft
|
||||||
|
case landscapeRight
|
||||||
|
|
||||||
|
var interaceOrientation: UIInterfaceOrientation {
|
||||||
|
switch self {
|
||||||
|
case .landscapeLeft:
|
||||||
|
return .landscapeLeft
|
||||||
|
case .landscapeRight:
|
||||||
|
return .landscapeRight
|
||||||
|
default:
|
||||||
|
return .portrait
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isRotating: Bool {
|
||||||
|
self != .disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,13 +18,23 @@ struct ContentView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if Constants.isIPhone {
|
GeometryReader { proxy in
|
||||||
AppTabNavigation()
|
Group {
|
||||||
} else {
|
if Constants.isIPhone {
|
||||||
if horizontalSizeClass == .compact {
|
AppTabNavigation()
|
||||||
AppTabNavigation()
|
} else {
|
||||||
} else {
|
if horizontalSizeClass == .compact {
|
||||||
AppSidebarNavigation()
|
AppTabNavigation()
|
||||||
|
} else {
|
||||||
|
AppSidebarNavigation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
SafeAreaModel.shared.safeArea = proxy.safeAreaInsets
|
||||||
|
}
|
||||||
|
.onChange(of: proxy.safeAreaInsets) { newValue in
|
||||||
|
SafeAreaModel.shared.safeArea = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
|
@ -5,7 +5,7 @@ import SwiftUI
|
|||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
struct AppleAVPlayerView: UIViewRepresentable {
|
struct AppleAVPlayerView: UIViewRepresentable {
|
||||||
func makeUIView(context _: Context) -> some UIView {
|
func makeUIView(context _: Context) -> some UIView {
|
||||||
PlayerLayerView(frame: .zero)
|
PlayerLayerView()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIView(_: UIViewType, context _: Context) {}
|
func updateUIView(_: UIViewType, context _: Context) {}
|
||||||
|
@ -13,6 +13,7 @@ struct PlayerControls: View {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||||
|
@ObservedObject private var safeAreaModel = SafeAreaModel.shared
|
||||||
#elseif os(tvOS)
|
#elseif os(tvOS)
|
||||||
enum Field: Hashable {
|
enum Field: Hashable {
|
||||||
case seekOSD
|
case seekOSD
|
||||||
@ -229,7 +230,7 @@ struct PlayerControls: View {
|
|||||||
guard player.playerSize.height.isFinite else { return 200 }
|
guard player.playerSize.height.isFinite else { return 200 }
|
||||||
var inset = 0.0
|
var inset = 0.0
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
inset = SafeArea.insets.bottom
|
inset = safeAreaModel.safeArea.bottom
|
||||||
#endif
|
#endif
|
||||||
return [player.playerSize.height - inset, 500].min()!
|
return [player.playerSize.height - inset, 500].min()!
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ struct PlayerBackendView: View {
|
|||||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||||
#endif
|
#endif
|
||||||
@ObservedObject private var player = PlayerModel.shared
|
@ObservedObject private var player = PlayerModel.shared
|
||||||
|
@ObservedObject private var safeAreaModel = SafeAreaModel.shared
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack(alignment: .top) {
|
ZStack(alignment: .top) {
|
||||||
@ -55,19 +56,17 @@ struct PlayerBackendView: View {
|
|||||||
guard player.playingFullScreen else { return 0 }
|
guard player.playingFullScreen else { return 0 }
|
||||||
|
|
||||||
if UIDevice.current.userInterfaceIdiom != .pad {
|
if UIDevice.current.userInterfaceIdiom != .pad {
|
||||||
return verticalSizeClass == .compact ? SafeArea.insets.top : 0
|
return verticalSizeClass == .compact ? safeAreaModel.safeArea.top : 0
|
||||||
} else {
|
} else {
|
||||||
return SafeArea.insets.top.isZero ? SafeArea.insets.bottom : SafeArea.insets.top
|
return safeAreaModel.safeArea.top.isZero ? safeAreaModel.safeArea.bottom : safeAreaModel.safeArea.top
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var controlsBottomPadding: Double {
|
var controlsBottomPadding: Double {
|
||||||
guard player.playingFullScreen else { return 0 }
|
|
||||||
|
|
||||||
if UIDevice.current.userInterfaceIdiom != .pad {
|
if UIDevice.current.userInterfaceIdiom != .pad {
|
||||||
return player.playingFullScreen && verticalSizeClass == .compact ? SafeArea.insets.bottom : 0
|
return player.playingFullScreen || verticalSizeClass == .compact ? safeAreaModel.safeArea.bottom : 0
|
||||||
} else {
|
} else {
|
||||||
return player.playingFullScreen ? SafeArea.insets.bottom : 0
|
return player.playingFullScreen ? safeAreaModel.safeArea.bottom : 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -37,10 +37,8 @@ struct VideoPlayerView: View {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||||
|
@ObservedObject private var safeAreaModel = SafeAreaModel.shared
|
||||||
@State internal var orientation = UIInterfaceOrientation.portrait
|
private var orientationModel = OrientationModel.shared
|
||||||
@State internal var lastOrientation: UIInterfaceOrientation?
|
|
||||||
@State internal var orientationDebouncer = Debouncer(.milliseconds(300))
|
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
var hoverThrottle = Throttle(interval: 0.5)
|
var hoverThrottle = Throttle(interval: 0.5)
|
||||||
var mouseLocation: CGPoint { NSEvent.mouseLocation }
|
var mouseLocation: CGPoint { NSEvent.mouseLocation }
|
||||||
@ -52,7 +50,6 @@ struct VideoPlayerView: View {
|
|||||||
@State internal var isHorizontalDrag = false
|
@State internal var isHorizontalDrag = false
|
||||||
@State internal var isVerticalDrag = false
|
@State internal var isVerticalDrag = false
|
||||||
@State internal var viewDragOffset = Self.hiddenOffset
|
@State internal var viewDragOffset = Self.hiddenOffset
|
||||||
@State internal var orientationObserver: Any?
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ObservedObject internal var player = PlayerModel.shared
|
@ObservedObject internal var player = PlayerModel.shared
|
||||||
@ -101,13 +98,12 @@ struct VideoPlayerView: View {
|
|||||||
return GeometryReader { geometry in
|
return GeometryReader { geometry in
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
content
|
content
|
||||||
|
.ignoresSafeArea(.all, edges: .bottom)
|
||||||
|
.frame(height: playerHeight.isNil ? nil : Double(playerHeight!))
|
||||||
.onAppear {
|
.onAppear {
|
||||||
playerSize = geometry.size
|
playerSize = geometry.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if os(iOS)
|
|
||||||
.padding(.bottom, fullScreenPlayer ? 0.0001 : geometry.safeAreaInsets.bottom)
|
|
||||||
#endif
|
|
||||||
.onChange(of: geometry.size) { _ in
|
.onChange(of: geometry.size) { _ in
|
||||||
self.playerSize = geometry.size
|
self.playerSize = geometry.size
|
||||||
}
|
}
|
||||||
@ -115,8 +111,6 @@ struct VideoPlayerView: View {
|
|||||||
player.backend.setNeedsDrawing(!value)
|
player.backend.setNeedsDrawing(!value)
|
||||||
}
|
}
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.frame(width: playerWidth.isNil ? nil : Double(playerWidth!), height: playerHeight.isNil ? nil : Double(playerHeight!))
|
|
||||||
.ignoresSafeArea(.all, edges: .bottom)
|
|
||||||
.onChange(of: player.presentingPlayer) { newValue in
|
.onChange(of: player.presentingPlayer) { newValue in
|
||||||
if newValue {
|
if newValue {
|
||||||
viewDragOffset = 0
|
viewDragOffset = 0
|
||||||
@ -131,7 +125,7 @@ struct VideoPlayerView: View {
|
|||||||
viewDragOffset = 0
|
viewDragOffset = 0
|
||||||
|
|
||||||
Delay.by(0.2) {
|
Delay.by(0.2) {
|
||||||
configureOrientationUpdatesBasedOnAccelerometer()
|
orientationModel.configureOrientationUpdatesBasedOnAccelerometer()
|
||||||
|
|
||||||
if let orientationMask = player.lockedOrientation {
|
if let orientationMask = player.lockedOrientation {
|
||||||
Orientation.lockOrientation(
|
Orientation.lockOrientation(
|
||||||
@ -149,7 +143,7 @@ struct VideoPlayerView: View {
|
|||||||
} else {
|
} else {
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
Orientation.lockOrientation(.allButUpsideDown)
|
||||||
}
|
}
|
||||||
stopOrientationUpdates()
|
orientationModel.stopOrientationUpdates()
|
||||||
player.controls.hideOverlays()
|
player.controls.hideOverlays()
|
||||||
}
|
}
|
||||||
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
|
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
|
||||||
@ -258,13 +252,10 @@ struct VideoPlayerView: View {
|
|||||||
dragGestureState && !isHorizontalDrag ? dragGestureOffset.height : viewDragOffset
|
dragGestureState && !isHorizontalDrag ? dragGestureOffset.height : viewDragOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
var playerWidth: Double? {
|
|
||||||
fullScreenPlayer ? (UIScreen.main.bounds.size.width - SafeArea.insets.left - SafeArea.insets.right) : nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var playerHeight: Double? {
|
var playerHeight: Double? {
|
||||||
let lockedPortrait = player.lockedOrientation?.contains(.portrait) ?? false
|
let lockedPortrait = player.lockedOrientation?.contains(.portrait) ?? false
|
||||||
return fullScreenPlayer ? UIScreen.main.bounds.size.height - (OrientationTracker.shared.currentInterfaceOrientation.isPortrait || lockedPortrait ? (SafeArea.insets.top + SafeArea.insets.bottom) : 0) : nil
|
let isPortrait = OrientationTracker.shared.currentInterfaceOrientation.isPortrait || lockedPortrait
|
||||||
|
return fullScreenPlayer ? UIScreen.main.bounds.size.height - (isPortrait ? safeAreaModel.safeArea.top + safeAreaModel.safeArea.bottom : 0) : nil
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -282,22 +273,20 @@ struct VideoPlayerView: View {
|
|||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
#else
|
#else
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
ZStack {
|
player.playerBackendView
|
||||||
player.playerBackendView
|
|
||||||
}
|
.modifier(
|
||||||
.modifier(
|
VideoPlayerSizeModifier(
|
||||||
VideoPlayerSizeModifier(
|
geometry: geometry,
|
||||||
geometry: geometry,
|
aspectRatio: player.aspectRatio,
|
||||||
aspectRatio: player.aspectRatio,
|
fullScreen: fullScreenPlayer
|
||||||
fullScreen: fullScreenPlayer
|
)
|
||||||
)
|
)
|
||||||
)
|
.onHover { hovering in
|
||||||
.frame(maxWidth: fullScreenPlayer ? .infinity : nil, maxHeight: fullScreenPlayer ? .infinity : nil)
|
hoveringPlayer = hovering
|
||||||
.onHover { hovering in
|
hovering ? player.controls.show() : player.controls.hide()
|
||||||
hoveringPlayer = hovering
|
}
|
||||||
hovering ? player.controls.show() : player.controls.hide()
|
.gesture(player.controls.presentingOverlays ? nil : playerDragGesture)
|
||||||
}
|
|
||||||
.gesture(player.controls.presentingOverlays ? nil : playerDragGesture)
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
.onAppear(perform: {
|
.onAppear(perform: {
|
||||||
NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
|
NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
|
||||||
@ -338,6 +327,7 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
.background(BackgroundBlackView().edgesIgnoringSafeArea(.all))
|
||||||
.background(((colorScheme == .dark || fullScreenPlayer) ? Color.black : Color.white).edgesIgnoringSafeArea(.all))
|
.background(((colorScheme == .dark || fullScreenPlayer) ? Color.black : Color.white).edgesIgnoringSafeArea(.all))
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
.frame(minWidth: 650)
|
.frame(minWidth: 650)
|
||||||
@ -489,3 +479,16 @@ struct VideoPlayerView_Previews: PreviewProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BackgroundBlackView: UIViewRepresentable {
|
||||||
|
func makeUIView(context _: Context) -> UIView {
|
||||||
|
let view = UIView()
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
view.superview?.superview?.backgroundColor = .black
|
||||||
|
view.superview?.superview?.layer.removeAllAnimations()
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIView(_: UIView, context _: Context) {}
|
||||||
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
struct SafeArea {
|
|
||||||
static var insets: UIEdgeInsets {
|
|
||||||
let keyWindow = scene?.windows.first { $0.isKeyWindow }
|
|
||||||
|
|
||||||
return keyWindow?.safeAreaInsets ?? .init()
|
|
||||||
}
|
|
||||||
|
|
||||||
static var verticalInset: Double {
|
|
||||||
insets.top + insets.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
static var horizontalInsets: Double {
|
|
||||||
insets.left + insets.right
|
|
||||||
}
|
|
||||||
|
|
||||||
static var scene: UIWindowScene? {
|
|
||||||
UIApplication.shared.connectedScenes
|
|
||||||
.filter { $0.activationState == .foregroundActive }
|
|
||||||
.compactMap { $0 as? UIWindowScene }
|
|
||||||
.first
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,6 +17,7 @@ struct PlayerSettings: View {
|
|||||||
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
|
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
|
||||||
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
||||||
@Default(.rotateToPortraitOnExitFullScreen) private var rotateToPortraitOnExitFullScreen
|
@Default(.rotateToPortraitOnExitFullScreen) private var rotateToPortraitOnExitFullScreen
|
||||||
|
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
|
||||||
#endif
|
#endif
|
||||||
@Default(.closePiPOnNavigation) private var closePiPOnNavigation
|
@Default(.closePiPOnNavigation) private var closePiPOnNavigation
|
||||||
@Default(.closePiPOnOpeningPlayer) private var closePiPOnOpeningPlayer
|
@Default(.closePiPOnOpeningPlayer) private var closePiPOnOpeningPlayer
|
||||||
@ -118,8 +119,9 @@ struct PlayerSettings: View {
|
|||||||
if idiom == .pad {
|
if idiom == .pad {
|
||||||
enterFullscreenInLandscapeToggle
|
enterFullscreenInLandscapeToggle
|
||||||
}
|
}
|
||||||
rotateToPortraitOnExitFullScreenToggle
|
|
||||||
honorSystemOrientationLockToggle
|
honorSystemOrientationLockToggle
|
||||||
|
rotateToPortraitOnExitFullScreenToggle
|
||||||
|
rotateToLandscapeOnEnterFullScreenPicker
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -209,6 +211,15 @@ struct PlayerSettings: View {
|
|||||||
private var rotateToPortraitOnExitFullScreenToggle: some View {
|
private var rotateToPortraitOnExitFullScreenToggle: some View {
|
||||||
Toggle("Rotate to portrait when exiting fullscreen", isOn: $rotateToPortraitOnExitFullScreen)
|
Toggle("Rotate to portrait when exiting fullscreen", isOn: $rotateToPortraitOnExitFullScreen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var rotateToLandscapeOnEnterFullScreenPicker: some View {
|
||||||
|
Picker("Rotate when entering fullscreen on landscape video", selection: $rotateToLandscapeOnEnterFullScreen) {
|
||||||
|
Text("Landscape left").tag(FullScreenRotationSetting.landscapeRight)
|
||||||
|
Text("Landscape right").tag(FullScreenRotationSetting.landscapeLeft)
|
||||||
|
Text("No rotation").tag(FullScreenRotationSetting.disabled)
|
||||||
|
}
|
||||||
|
.modifier(SettingsPickerModifier())
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private var closePiPOnNavigationToggle: some View {
|
private var closePiPOnNavigationToggle: some View {
|
||||||
|
@ -253,7 +253,6 @@
|
|||||||
37319F0627103F94004ECCD0 /* PlayerQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37319F0427103F94004ECCD0 /* PlayerQueue.swift */; };
|
37319F0627103F94004ECCD0 /* PlayerQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37319F0427103F94004ECCD0 /* PlayerQueue.swift */; };
|
||||||
37319F0727103F94004ECCD0 /* PlayerQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37319F0427103F94004ECCD0 /* PlayerQueue.swift */; };
|
37319F0727103F94004ECCD0 /* PlayerQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37319F0427103F94004ECCD0 /* PlayerQueue.swift */; };
|
||||||
3732BFD028B83763009F3F4D /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 3732BFCF28B83763009F3F4D /* KeychainAccess */; };
|
3732BFD028B83763009F3F4D /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 3732BFCF28B83763009F3F4D /* KeychainAccess */; };
|
||||||
3732C9FD28C012E600E7DCAF /* SafeArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37ECED55289FE166002BC2C9 /* SafeArea.swift */; };
|
|
||||||
3735C4E729A2D3D70051D251 /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 3735C4E629A2D3D70051D251 /* Logging */; };
|
3735C4E729A2D3D70051D251 /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 3735C4E629A2D3D70051D251 /* Logging */; };
|
||||||
3736A1FE286BB72300C9E5EE /* libavdevice.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3736A1EF286BB72300C9E5EE /* libavdevice.xcframework */; };
|
3736A1FE286BB72300C9E5EE /* libavdevice.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3736A1EF286BB72300C9E5EE /* libavdevice.xcframework */; };
|
||||||
3736A1FF286BB72300C9E5EE /* libavdevice.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3736A1EF286BB72300C9E5EE /* libavdevice.xcframework */; };
|
3736A1FF286BB72300C9E5EE /* libavdevice.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3736A1EF286BB72300C9E5EE /* libavdevice.xcframework */; };
|
||||||
@ -375,7 +374,6 @@
|
|||||||
374D11E72943C56300CB4350 /* Cache in Frameworks */ = {isa = PBXBuildFile; productRef = 374D11E62943C56300CB4350 /* Cache */; };
|
374D11E72943C56300CB4350 /* Cache in Frameworks */ = {isa = PBXBuildFile; productRef = 374D11E62943C56300CB4350 /* Cache */; };
|
||||||
374DE88028BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374DE87F28BB896C0062BBF2 /* PlayerDragGesture.swift */; };
|
374DE88028BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374DE87F28BB896C0062BBF2 /* PlayerDragGesture.swift */; };
|
||||||
374DE88128BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374DE87F28BB896C0062BBF2 /* PlayerDragGesture.swift */; };
|
374DE88128BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374DE87F28BB896C0062BBF2 /* PlayerDragGesture.swift */; };
|
||||||
374DE88328BB8A280062BBF2 /* PlayerOrientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374DE88228BB8A280062BBF2 /* PlayerOrientation.swift */; };
|
|
||||||
375168D62700FAFF008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
375168D62700FAFF008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
||||||
375168D72700FDB8008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
375168D72700FDB8008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
||||||
375168D82700FDB9008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
375168D82700FDB9008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
||||||
@ -885,6 +883,8 @@
|
|||||||
37D9BA0629507F69002586BD /* PlayerControlsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D9BA0529507F69002586BD /* PlayerControlsSettings.swift */; };
|
37D9BA0629507F69002586BD /* PlayerControlsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D9BA0529507F69002586BD /* PlayerControlsSettings.swift */; };
|
||||||
37D9BA0729507F69002586BD /* PlayerControlsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D9BA0529507F69002586BD /* PlayerControlsSettings.swift */; };
|
37D9BA0729507F69002586BD /* PlayerControlsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D9BA0529507F69002586BD /* PlayerControlsSettings.swift */; };
|
||||||
37D9BA0829507F69002586BD /* PlayerControlsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D9BA0529507F69002586BD /* PlayerControlsSettings.swift */; };
|
37D9BA0829507F69002586BD /* PlayerControlsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D9BA0529507F69002586BD /* PlayerControlsSettings.swift */; };
|
||||||
|
37DCD3112A18E8150059A470 /* OrientationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCD3102A18E8150059A470 /* OrientationModel.swift */; };
|
||||||
|
37DCD3152A18F7630059A470 /* SafeAreaModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCD3142A18F7630059A470 /* SafeAreaModel.swift */; };
|
||||||
37DD87C7271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
|
37DD87C7271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
|
||||||
37DD87C8271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
|
37DD87C8271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
|
||||||
37DD87C9271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
|
37DD87C9271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
|
||||||
@ -941,7 +941,6 @@
|
|||||||
37EBD8CA27AF26C200F1C24B /* MPVBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EBD8C927AF26C200F1C24B /* MPVBackend.swift */; };
|
37EBD8CA27AF26C200F1C24B /* MPVBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EBD8C927AF26C200F1C24B /* MPVBackend.swift */; };
|
||||||
37EBD8CB27AF26C200F1C24B /* MPVBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EBD8C927AF26C200F1C24B /* MPVBackend.swift */; };
|
37EBD8CB27AF26C200F1C24B /* MPVBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EBD8C927AF26C200F1C24B /* MPVBackend.swift */; };
|
||||||
37EBD8CC27AF26C200F1C24B /* MPVBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EBD8C927AF26C200F1C24B /* MPVBackend.swift */; };
|
37EBD8CC27AF26C200F1C24B /* MPVBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EBD8C927AF26C200F1C24B /* MPVBackend.swift */; };
|
||||||
37ECED56289FE166002BC2C9 /* SafeArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37ECED55289FE166002BC2C9 /* SafeArea.swift */; };
|
|
||||||
37EE6DC528A305AD00BFD632 /* Reachability in Frameworks */ = {isa = PBXBuildFile; productRef = 37EE6DC428A305AD00BFD632 /* Reachability */; };
|
37EE6DC528A305AD00BFD632 /* Reachability in Frameworks */ = {isa = PBXBuildFile; productRef = 37EE6DC428A305AD00BFD632 /* Reachability */; };
|
||||||
37EF5C222739D37B00B03725 /* MenuModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EF5C212739D37B00B03725 /* MenuModel.swift */; };
|
37EF5C222739D37B00B03725 /* MenuModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EF5C212739D37B00B03725 /* MenuModel.swift */; };
|
||||||
37EF5C232739D37B00B03725 /* MenuModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EF5C212739D37B00B03725 /* MenuModel.swift */; };
|
37EF5C232739D37B00B03725 /* MenuModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EF5C212739D37B00B03725 /* MenuModel.swift */; };
|
||||||
@ -1258,7 +1257,6 @@
|
|||||||
374C0542272496E4009BDDBE /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = macOS/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
|
374C0542272496E4009BDDBE /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = macOS/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
|
||||||
374C0544272496FD009BDDBE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
374C0544272496FD009BDDBE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
374DE87F28BB896C0062BBF2 /* PlayerDragGesture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerDragGesture.swift; sourceTree = "<group>"; };
|
374DE87F28BB896C0062BBF2 /* PlayerDragGesture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerDragGesture.swift; sourceTree = "<group>"; };
|
||||||
374DE88228BB8A280062BBF2 /* PlayerOrientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerOrientation.swift; sourceTree = "<group>"; };
|
|
||||||
375168D52700FAFF008F96A6 /* Debounce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debounce.swift; sourceTree = "<group>"; };
|
375168D52700FAFF008F96A6 /* Debounce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debounce.swift; sourceTree = "<group>"; };
|
||||||
3751B4B127836902000B7DF4 /* SearchPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchPage.swift; sourceTree = "<group>"; };
|
3751B4B127836902000B7DF4 /* SearchPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchPage.swift; sourceTree = "<group>"; };
|
||||||
3751BA7D27E63F1D007B1A60 /* MPVOGLView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPVOGLView.swift; sourceTree = "<group>"; };
|
3751BA7D27E63F1D007B1A60 /* MPVOGLView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPVOGLView.swift; sourceTree = "<group>"; };
|
||||||
@ -1458,6 +1456,8 @@
|
|||||||
37D836BB294927E700005E5E /* ChannelsCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCacheModel.swift; sourceTree = "<group>"; };
|
37D836BB294927E700005E5E /* ChannelsCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCacheModel.swift; sourceTree = "<group>"; };
|
||||||
37D9169A27388A81002B1BAA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
37D9169A27388A81002B1BAA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
37D9BA0529507F69002586BD /* PlayerControlsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsSettings.swift; sourceTree = "<group>"; };
|
37D9BA0529507F69002586BD /* PlayerControlsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsSettings.swift; sourceTree = "<group>"; };
|
||||||
|
37DCD3102A18E8150059A470 /* OrientationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrientationModel.swift; sourceTree = "<group>"; };
|
||||||
|
37DCD3142A18F7630059A470 /* SafeAreaModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafeAreaModel.swift; sourceTree = "<group>"; };
|
||||||
37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerStreams.swift; sourceTree = "<group>"; };
|
37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerStreams.swift; sourceTree = "<group>"; };
|
||||||
37DD9DA22785BBC900539416 /* NoCommentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoCommentsView.swift; sourceTree = "<group>"; };
|
37DD9DA22785BBC900539416 /* NoCommentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoCommentsView.swift; sourceTree = "<group>"; };
|
||||||
37DD9DAF2785D58D00539416 /* RefreshControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshControl.swift; sourceTree = "<group>"; };
|
37DD9DAF2785D58D00539416 /* RefreshControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshControl.swift; sourceTree = "<group>"; };
|
||||||
@ -1485,7 +1485,6 @@
|
|||||||
37EBD8C327AF0DA800F1C24B /* PlayerBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerBackend.swift; sourceTree = "<group>"; };
|
37EBD8C327AF0DA800F1C24B /* PlayerBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerBackend.swift; sourceTree = "<group>"; };
|
||||||
37EBD8C527AF26B300F1C24B /* AVPlayerBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerBackend.swift; sourceTree = "<group>"; };
|
37EBD8C527AF26B300F1C24B /* AVPlayerBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerBackend.swift; sourceTree = "<group>"; };
|
||||||
37EBD8C927AF26C200F1C24B /* MPVBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPVBackend.swift; sourceTree = "<group>"; };
|
37EBD8C927AF26C200F1C24B /* MPVBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPVBackend.swift; sourceTree = "<group>"; };
|
||||||
37ECED55289FE166002BC2C9 /* SafeArea.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafeArea.swift; sourceTree = "<group>"; };
|
|
||||||
37EF5C212739D37B00B03725 /* MenuModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuModel.swift; sourceTree = "<group>"; };
|
37EF5C212739D37B00B03725 /* MenuModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuModel.swift; sourceTree = "<group>"; };
|
||||||
37EF9A75275BEB8E0043B585 /* CommentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentView.swift; sourceTree = "<group>"; };
|
37EF9A75275BEB8E0043B585 /* CommentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentView.swift; sourceTree = "<group>"; };
|
||||||
37EFAC0728C138CD00ED9B89 /* ControlsOverlayModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsOverlayModel.swift; sourceTree = "<group>"; };
|
37EFAC0728C138CD00ED9B89 /* ControlsOverlayModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsOverlayModel.swift; sourceTree = "<group>"; };
|
||||||
@ -1812,7 +1811,6 @@
|
|||||||
374DE87F28BB896C0062BBF2 /* PlayerDragGesture.swift */,
|
374DE87F28BB896C0062BBF2 /* PlayerDragGesture.swift */,
|
||||||
3703100127B0713600ECDDAA /* PlayerGestures.swift */,
|
3703100127B0713600ECDDAA /* PlayerGestures.swift */,
|
||||||
373031F22838388A000CFD59 /* PlayerLayerView.swift */,
|
373031F22838388A000CFD59 /* PlayerLayerView.swift */,
|
||||||
374DE88228BB8A280062BBF2 /* PlayerOrientation.swift */,
|
|
||||||
3743CA4D270EFE3400E4D32B /* PlayerQueueRow.swift */,
|
3743CA4D270EFE3400E4D32B /* PlayerQueueRow.swift */,
|
||||||
373197D82732015300EF734F /* RelatedView.swift */,
|
373197D82732015300EF734F /* RelatedView.swift */,
|
||||||
3795593527B08538007FF8F4 /* StreamControl.swift */,
|
3795593527B08538007FF8F4 /* StreamControl.swift */,
|
||||||
@ -2180,6 +2178,8 @@
|
|||||||
3749BF9227ADA142000480FF /* BridgingHeader.h */,
|
3749BF9227ADA142000480FF /* BridgingHeader.h */,
|
||||||
37992DC726CC50BC003D4C27 /* Info.plist */,
|
37992DC726CC50BC003D4C27 /* Info.plist */,
|
||||||
3782430A291E5AFA005DEC1C /* Yattee (iOS).entitlements */,
|
3782430A291E5AFA005DEC1C /* Yattee (iOS).entitlements */,
|
||||||
|
37DCD3102A18E8150059A470 /* OrientationModel.swift */,
|
||||||
|
37DCD3142A18F7630059A470 /* SafeAreaModel.swift */,
|
||||||
);
|
);
|
||||||
path = iOS;
|
path = iOS;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -2298,7 +2298,6 @@
|
|||||||
3729037D2739E47400EA99F6 /* MenuCommands.swift */,
|
3729037D2739E47400EA99F6 /* MenuCommands.swift */,
|
||||||
37B7958F2771DAE0001CF27B /* OpenURLHandler.swift */,
|
37B7958F2771DAE0001CF27B /* OpenURLHandler.swift */,
|
||||||
374924EC2921669B0017D862 /* PreferenceKeys.swift */,
|
374924EC2921669B0017D862 /* PreferenceKeys.swift */,
|
||||||
37ECED55289FE166002BC2C9 /* SafeArea.swift */,
|
|
||||||
3700155E271B12DD0049C794 /* SiestaConfiguration.swift */,
|
3700155E271B12DD0049C794 /* SiestaConfiguration.swift */,
|
||||||
37FFC43F272734C3009FFD26 /* Throttle.swift */,
|
37FFC43F272734C3009FFD26 /* Throttle.swift */,
|
||||||
378FFBC328660172009E3FBE /* URLParser.swift */,
|
378FFBC328660172009E3FBE /* URLParser.swift */,
|
||||||
@ -3057,7 +3056,6 @@
|
|||||||
374710052755291C00CE0F87 /* SearchTextField.swift in Sources */,
|
374710052755291C00CE0F87 /* SearchTextField.swift in Sources */,
|
||||||
37494EA529200B14000DF176 /* DocumentsView.swift in Sources */,
|
37494EA529200B14000DF176 /* DocumentsView.swift in Sources */,
|
||||||
374DE88028BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */,
|
374DE88028BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */,
|
||||||
37ECED56289FE166002BC2C9 /* SafeArea.swift in Sources */,
|
|
||||||
37CEE4BD2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
37CEE4BD2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
||||||
37C2211D27ADA33300305B41 /* MPVViewController.swift in Sources */,
|
37C2211D27ADA33300305B41 /* MPVViewController.swift in Sources */,
|
||||||
37A362BE29537AAA00BDF328 /* PlaybackSettings.swift in Sources */,
|
37A362BE29537AAA00BDF328 /* PlaybackSettings.swift in Sources */,
|
||||||
@ -3094,7 +3092,6 @@
|
|||||||
377FF88F291A99580028EB0B /* HistoryView.swift in Sources */,
|
377FF88F291A99580028EB0B /* HistoryView.swift in Sources */,
|
||||||
3782B94F27553A6700990149 /* SearchSuggestions.swift in Sources */,
|
3782B94F27553A6700990149 /* SearchSuggestions.swift in Sources */,
|
||||||
378E50FF26FE8EEE00F49626 /* AccountViewButton.swift in Sources */,
|
378E50FF26FE8EEE00F49626 /* AccountViewButton.swift in Sources */,
|
||||||
374DE88328BB8A280062BBF2 /* PlayerOrientation.swift in Sources */,
|
|
||||||
374924F029216C630017D862 /* VideoActions.swift in Sources */,
|
374924F029216C630017D862 /* VideoActions.swift in Sources */,
|
||||||
37169AA62729E2CC0011DE61 /* AccountsBridge.swift in Sources */,
|
37169AA62729E2CC0011DE61 /* AccountsBridge.swift in Sources */,
|
||||||
37C7A1DA267CACF50010EAD6 /* TrendingCountry.swift in Sources */,
|
37C7A1DA267CACF50010EAD6 /* TrendingCountry.swift in Sources */,
|
||||||
@ -3176,6 +3173,7 @@
|
|||||||
37FD77002932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */,
|
37FD77002932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */,
|
||||||
373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
||||||
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||||
|
37DCD3112A18E8150059A470 /* OrientationModel.swift in Sources */,
|
||||||
3782B9522755667600990149 /* String+Format.swift in Sources */,
|
3782B9522755667600990149 /* String+Format.swift in Sources */,
|
||||||
37F9619F27BD90BB00058149 /* PlayerBackendType.swift in Sources */,
|
37F9619F27BD90BB00058149 /* PlayerBackendType.swift in Sources */,
|
||||||
373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
|
373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
|
||||||
@ -3246,6 +3244,7 @@
|
|||||||
37A9965A26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
|
37A9965A26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
|
||||||
37EBD8CA27AF26C200F1C24B /* MPVBackend.swift in Sources */,
|
37EBD8CA27AF26C200F1C24B /* MPVBackend.swift in Sources */,
|
||||||
37635FE4291EA6CF00C11E79 /* AccentButton.swift in Sources */,
|
37635FE4291EA6CF00C11E79 /* AccentButton.swift in Sources */,
|
||||||
|
37DCD3152A18F7630059A470 /* SafeAreaModel.swift in Sources */,
|
||||||
37D526DE2720AC4400ED2F5E /* VideosAPI.swift in Sources */,
|
37D526DE2720AC4400ED2F5E /* VideosAPI.swift in Sources */,
|
||||||
37484C2526FC83E000287258 /* InstanceForm.swift in Sources */,
|
37484C2526FC83E000287258 /* InstanceForm.swift in Sources */,
|
||||||
37DD9DBD2785D60300539416 /* ScrollViewMatcher.swift in Sources */,
|
37DD9DBD2785D60300539416 /* ScrollViewMatcher.swift in Sources */,
|
||||||
@ -3699,7 +3698,6 @@
|
|||||||
376B2E0926F920D600B1D64D /* SignInRequiredView.swift in Sources */,
|
376B2E0926F920D600B1D64D /* SignInRequiredView.swift in Sources */,
|
||||||
378FFBC628660172009E3FBE /* URLParser.swift in Sources */,
|
378FFBC628660172009E3FBE /* URLParser.swift in Sources */,
|
||||||
37141671267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
37141671267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||||
3732C9FD28C012E600E7DCAF /* SafeArea.swift in Sources */,
|
|
||||||
37A2B348294723850050933E /* CacheModel.swift in Sources */,
|
37A2B348294723850050933E /* CacheModel.swift in Sources */,
|
||||||
37C3A24727235DA70087A57A /* ChannelPlaylist.swift in Sources */,
|
37C3A24727235DA70087A57A /* ChannelPlaylist.swift in Sources */,
|
||||||
3788AC2926F6840700F6BAA9 /* FavoriteItemView.swift in Sources */,
|
3788AC2926F6840700F6BAA9 /* FavoriteItemView.swift in Sources */,
|
||||||
|
@ -31,7 +31,7 @@ struct Orientation {
|
|||||||
logger.info("rotating to \(orientationString)")
|
logger.info("rotating to \(orientationString)")
|
||||||
|
|
||||||
if #available(iOS 16, *) {
|
if #available(iOS 16, *) {
|
||||||
guard let windowScene = SafeArea.scene else { return }
|
guard let windowScene = Self.scene else { return }
|
||||||
let rotateOrientationMask = rotateOrientation == .portrait ? UIInterfaceOrientationMask.portrait :
|
let rotateOrientationMask = rotateOrientation == .portrait ? UIInterfaceOrientationMask.portrait :
|
||||||
rotateOrientation == .landscapeLeft ? .landscapeLeft :
|
rotateOrientation == .landscapeLeft ? .landscapeLeft :
|
||||||
rotateOrientation == .landscapeRight ? .landscapeRight :
|
rotateOrientation == .landscapeRight ? .landscapeRight :
|
||||||
@ -46,4 +46,11 @@ struct Orientation {
|
|||||||
|
|
||||||
UINavigationController.attemptRotationToDeviceOrientation()
|
UINavigationController.attemptRotationToDeviceOrientation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static var scene: UIWindowScene? {
|
||||||
|
UIApplication.shared.connectedScenes
|
||||||
|
.filter { $0.activationState == .foregroundActive }
|
||||||
|
.compactMap { $0 as? UIWindowScene }
|
||||||
|
.first
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
import Defaults
|
import Defaults
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Repeat
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
extension VideoPlayerView {
|
final class OrientationModel {
|
||||||
|
static var shared = OrientationModel()
|
||||||
|
|
||||||
|
var orientation = UIInterfaceOrientation.portrait
|
||||||
|
var lastOrientation: UIInterfaceOrientation?
|
||||||
|
var orientationDebouncer = Debouncer(.milliseconds(300))
|
||||||
|
internal var orientationObserver: Any?
|
||||||
|
|
||||||
|
private var player = PlayerModel.shared
|
||||||
|
|
||||||
func configureOrientationUpdatesBasedOnAccelerometer() {
|
func configureOrientationUpdatesBasedOnAccelerometer() {
|
||||||
let currentOrientation = OrientationTracker.shared.currentInterfaceOrientation
|
let currentOrientation = OrientationTracker.shared.currentInterfaceOrientation
|
||||||
if currentOrientation.isLandscape,
|
if currentOrientation.isLandscape,
|
||||||
@ -15,8 +25,8 @@ extension VideoPlayerView {
|
|||||||
player.presentingPlayer
|
player.presentingPlayer
|
||||||
{
|
{
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
player.controls.presentingControls = false
|
self.player.controls.presentingControls = false
|
||||||
player.enterFullScreen(showControls: false)
|
self.player.enterFullScreen(showControls: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
player.onPresentPlayer.append {
|
player.onPresentPlayer.append {
|
||||||
@ -30,42 +40,42 @@ extension VideoPlayerView {
|
|||||||
queue: .main
|
queue: .main
|
||||||
) { _ in
|
) { _ in
|
||||||
guard !Defaults[.honorSystemOrientationLock],
|
guard !Defaults[.honorSystemOrientationLock],
|
||||||
player.presentingPlayer,
|
self.player.presentingPlayer,
|
||||||
!player.playingInPictureInPicture,
|
!self.player.playingInPictureInPicture,
|
||||||
player.lockedOrientation.isNil
|
self.player.lockedOrientation.isNil
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
||||||
|
|
||||||
guard lastOrientation != orientation else {
|
guard self.lastOrientation != orientation else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lastOrientation = orientation
|
self.lastOrientation = orientation
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
guard Defaults[.enterFullscreenInLandscape],
|
guard Defaults[.enterFullscreenInLandscape],
|
||||||
player.presentingPlayer
|
self.player.presentingPlayer
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
orientationDebouncer.callback = {
|
self.orientationDebouncer.callback = {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if orientation.isLandscape {
|
if orientation.isLandscape {
|
||||||
player.controls.presentingControls = false
|
self.player.controls.presentingControls = false
|
||||||
player.enterFullScreen(showControls: false)
|
self.player.enterFullScreen(showControls: false)
|
||||||
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
|
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
|
||||||
} else {
|
} else {
|
||||||
player.exitFullScreen(showControls: false)
|
self.player.exitFullScreen(showControls: false)
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
orientationDebouncer.call()
|
self.orientationDebouncer.call()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,4 +84,12 @@ extension VideoPlayerView {
|
|||||||
guard let observer = orientationObserver else { return }
|
guard let observer = orientationObserver else { return }
|
||||||
NotificationCenter.default.removeObserver(observer)
|
NotificationCenter.default.removeObserver(observer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation? = nil) {
|
||||||
|
if let rotateOrientation {
|
||||||
|
self.orientation = rotateOrientation
|
||||||
|
lastOrientation = rotateOrientation
|
||||||
|
}
|
||||||
|
Orientation.lockOrientation(orientation, andRotateTo: rotateOrientation)
|
||||||
|
}
|
||||||
}
|
}
|
7
iOS/SafeAreaModel.swift
Normal file
7
iOS/SafeAreaModel.swift
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
final class SafeAreaModel: ObservableObject {
|
||||||
|
static var shared = SafeAreaModel()
|
||||||
|
@Published var safeArea = EdgeInsets()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user