mirror of
https://github.com/yattee/yattee.git
synced 2025-12-16 13:08:14 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0264aaabe | ||
|
|
035f3503c4 | ||
|
|
e3ac11c172 | ||
|
|
7aed6ac0d9 | ||
|
|
457c0ce7b3 | ||
|
|
747baf3edd | ||
|
|
cd24a0322f | ||
|
|
d525a22215 | ||
|
|
322a550666 | ||
|
|
98fa0b98e5 | ||
|
|
5313e4ead0 | ||
|
|
fa7b897e76 |
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,11 +1,9 @@
|
|||||||
## Build 194
|
## Build 195
|
||||||
* Gestures: swipe up toggles fullscreen by @stonerl in https://github.com/yattee/yattee/pull/778
|
* iOS: Simplified fullscreen and orientation by @stonerl in https://github.com/yattee/yattee/pull/786
|
||||||
* don’t open video when dismissing context menu by @stonerl in https://github.com/yattee/yattee/pull/780
|
* macOS: only apply player shortcuts when window is active by @stonerl in https://github.com/yattee/yattee/pull/802
|
||||||
* mpv: remove video layer when entering background by @stonerl in https://github.com/yattee/yattee/pull/793
|
* player controls: add background opacity selection by @stonerl in https://github.com/yattee/yattee/pull/799
|
||||||
* hi-res invidious logos by @stonerl in https://github.com/yattee/yattee/pull/791
|
* add missing Shorts resolutions by @stonerl in https://github.com/yattee/yattee/pull/797
|
||||||
* enable -O3 by @stonerl in https://github.com/yattee/yattee/pull/794
|
* use -O1 on macOS by @stonerl in https://github.com/yattee/yattee/pull/801
|
||||||
* Better audio ducking by @stonerl in https://github.com/yattee/yattee/pull/779
|
|
||||||
* fix picture in picture by @stonerl in https://github.com/yattee/yattee/pull/789
|
|
||||||
|
|
||||||
## Previous builds
|
## Previous builds
|
||||||
* Add skip, play/pause, and fullscreen shortcuts to macOS player (by @rickykresslein)
|
* Add skip, play/pause, and fullscreen shortcuts to macOS player (by @rickykresslein)
|
||||||
@@ -24,6 +22,13 @@
|
|||||||
* Add import export of missing settings
|
* Add import export of missing settings
|
||||||
* macOS: Fix settings windows layout
|
* macOS: Fix settings windows layout
|
||||||
* Fix seek OSD layout on tvOS, revert OSD position
|
* Fix seek OSD layout on tvOS, revert OSD position
|
||||||
|
* Gestures: swipe up toggles fullscreen by @stonerl in https://github.com/yattee/yattee/pull/778
|
||||||
|
* don’t open video when dismissing context menu by @stonerl in https://github.com/yattee/yattee/pull/780
|
||||||
|
* mpv: remove video layer when entering background by @stonerl in https://github.com/yattee/yattee/pull/793
|
||||||
|
* hi-res invidious logos by @stonerl in https://github.com/yattee/yattee/pull/791
|
||||||
|
* enable -O3 by @stonerl in https://github.com/yattee/yattee/pull/794
|
||||||
|
* Better audio ducking by @stonerl in https://github.com/yattee/yattee/pull/779
|
||||||
|
* fix picture in picture by @stonerl in https://github.com/yattee/yattee/pull/789
|
||||||
* Invidious: propper HTTP basic auth support by @stonerl in https://github.com/yattee/yattee/pull/762
|
* Invidious: propper HTTP basic auth support by @stonerl in https://github.com/yattee/yattee/pull/762
|
||||||
* Apply correct orientation by @stonerl in https://github.com/yattee/yattee/pull/770
|
* Apply correct orientation by @stonerl in https://github.com/yattee/yattee/pull/770
|
||||||
* Circular Invidious logo by @stonerl in https://github.com/yattee/yattee/pull/769
|
* Circular Invidious logo by @stonerl in https://github.com/yattee/yattee/pull/769
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ final class ConstrolsSettingsGroupExporter: SettingsGroupExporter {
|
|||||||
"seekGestureSpeed": Defaults[.seekGestureSpeed],
|
"seekGestureSpeed": Defaults[.seekGestureSpeed],
|
||||||
"playerControlsLayout": Defaults[.playerControlsLayout].rawValue,
|
"playerControlsLayout": Defaults[.playerControlsLayout].rawValue,
|
||||||
"fullScreenPlayerControlsLayout": Defaults[.fullScreenPlayerControlsLayout].rawValue,
|
"fullScreenPlayerControlsLayout": Defaults[.fullScreenPlayerControlsLayout].rawValue,
|
||||||
|
"playerControlsBackgroundOpacity": Defaults[.playerControlsBackgroundOpacity],
|
||||||
"systemControlsCommands": Defaults[.systemControlsCommands].rawValue,
|
"systemControlsCommands": Defaults[.systemControlsCommands].rawValue,
|
||||||
"buttonBackwardSeekDuration": Defaults[.buttonBackwardSeekDuration],
|
"buttonBackwardSeekDuration": Defaults[.buttonBackwardSeekDuration],
|
||||||
"buttonForwardSeekDuration": Defaults[.buttonForwardSeekDuration],
|
"buttonForwardSeekDuration": Defaults[.buttonForwardSeekDuration],
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ final class PlayerSettingsGroupExporter: SettingsGroupExporter {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
export["honorSystemOrientationLock"].bool = Defaults[.honorSystemOrientationLock]
|
export["isOrientationLocked"].bool = Defaults[.isOrientationLocked]
|
||||||
export["enterFullscreenInLandscape"].bool = Defaults[.enterFullscreenInLandscape]
|
export["enterFullscreenInLandscape"].bool = Defaults[.enterFullscreenInLandscape]
|
||||||
export["rotateToLandscapeOnEnterFullScreen"].string = Defaults[.rotateToLandscapeOnEnterFullScreen].rawValue
|
export["rotateToLandscapeOnEnterFullScreen"].string = Defaults[.rotateToLandscapeOnEnterFullScreen].rawValue
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ struct ConstrolsSettingsGroupImporter {
|
|||||||
Defaults[.fullScreenPlayerControlsLayout] = fullScreenPlayerControlsLayout
|
Defaults[.fullScreenPlayerControlsLayout] = fullScreenPlayerControlsLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let playerControlsBackgroundOpacity = json["playerControlsBackgroundOpacity"].double {
|
||||||
|
Defaults[.playerControlsBackgroundOpacity] = playerControlsBackgroundOpacity
|
||||||
|
}
|
||||||
|
|
||||||
if let systemControlsCommandsString = json["systemControlsCommands"].string,
|
if let systemControlsCommandsString = json["systemControlsCommands"].string,
|
||||||
let systemControlsCommands = SystemControlsCommands(rawValue: systemControlsCommandsString)
|
let systemControlsCommands = SystemControlsCommands(rawValue: systemControlsCommandsString)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ struct PlayerSettingsGroupImporter {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if let honorSystemOrientationLock = json["honorSystemOrientationLock"].bool {
|
if let isOrientationLocked = json["isOrientationLocked"].bool {
|
||||||
Defaults[.honorSystemOrientationLock] = honorSystemOrientationLock
|
Defaults[.isOrientationLocked] = isOrientationLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
if let enterFullscreenInLandscape = json["enterFullscreenInLandscape"].bool {
|
if let enterFullscreenInLandscape = json["enterFullscreenInLandscape"].bool {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ final class MPVClient: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var logger = Logger(label: "mpv-client")
|
private var logger = Logger(label: "mpv-client")
|
||||||
|
private var needsDrawingCooldown = false
|
||||||
|
private var needsDrawingWorkItem: DispatchWorkItem?
|
||||||
|
|
||||||
var mpv: OpaquePointer!
|
var mpv: OpaquePointer!
|
||||||
var mpvGL: OpaquePointer!
|
var mpvGL: OpaquePointer!
|
||||||
@@ -389,10 +391,30 @@ final class MPVClient: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setNeedsDrawing(_ needsDrawing: Bool) {
|
func setNeedsDrawing(_ needsDrawing: Bool) {
|
||||||
|
// Check if we are currently in a cooldown period
|
||||||
|
guard !needsDrawingCooldown else {
|
||||||
|
logger.info("Not drawing, cooldown in progress")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
logger.info("needs drawing: \(needsDrawing)")
|
logger.info("needs drawing: \(needsDrawing)")
|
||||||
|
|
||||||
|
// Set the cooldown flag to true and cancel any existing work item
|
||||||
|
needsDrawingCooldown = true
|
||||||
|
needsDrawingWorkItem?.cancel()
|
||||||
|
|
||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
glView?.needsDrawing = needsDrawing
|
glView?.needsDrawing = needsDrawing
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Create a new DispatchWorkItem to reset the cooldown flag after 0.1 seconds
|
||||||
|
let workItem = DispatchWorkItem { [weak self] in
|
||||||
|
self?.needsDrawingCooldown = false
|
||||||
|
}
|
||||||
|
needsDrawingWorkItem = workItem
|
||||||
|
|
||||||
|
// Schedule the cooldown reset after 0.1 seconds
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: workItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func command(
|
func command(
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ final class PlayerModel: ObservableObject {
|
|||||||
@Published var presentingPlayer = false { didSet { handlePresentationChange() } }
|
@Published var presentingPlayer = false { didSet { handlePresentationChange() } }
|
||||||
@Published var activeBackend = PlayerBackendType.mpv
|
@Published var activeBackend = PlayerBackendType.mpv
|
||||||
@Published var forceBackendOnPlay: PlayerBackendType?
|
@Published var forceBackendOnPlay: PlayerBackendType?
|
||||||
@Published var wasFullscreen = false
|
|
||||||
|
|
||||||
var avPlayerBackend = AVPlayerBackend()
|
var avPlayerBackend = AVPlayerBackend()
|
||||||
var mpvBackend = MPVBackend()
|
var mpvBackend = MPVBackend()
|
||||||
@@ -131,6 +130,12 @@ final class PlayerModel: ObservableObject {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Published var lockedOrientation: UIInterfaceOrientationMask?
|
@Published var lockedOrientation: UIInterfaceOrientationMask?
|
||||||
|
@Published var isOrientationLocked: Bool {
|
||||||
|
didSet {
|
||||||
|
Defaults[.isOrientationLocked] = isOrientationLocked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
|
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -201,6 +206,16 @@ final class PlayerModel: ObservableObject {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
#if os(iOS)
|
||||||
|
isOrientationLocked = Defaults[.isOrientationLocked]
|
||||||
|
|
||||||
|
if isOrientationLocked, Defaults[.lockPortraitWhenBrowsing] {
|
||||||
|
lockedOrientation = UIInterfaceOrientationMask.portrait
|
||||||
|
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||||
|
} else if isOrientationLocked {
|
||||||
|
lockOrientationAction()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
mpvBackend.controller = mpvController
|
mpvBackend.controller = mpvController
|
||||||
mpvBackend.client = mpvController.client
|
mpvBackend.client = mpvController.client
|
||||||
@@ -517,7 +532,10 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func handlePresentationChange() {
|
private func handlePresentationChange() {
|
||||||
backend.setNeedsDrawing(presentingPlayer)
|
#if !os(iOS)
|
||||||
|
// TODO: Check whether this is neede on tvOS and macOS
|
||||||
|
backend.setNeedsDrawing(presentingPlayer)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if presentingPlayer, activeBackend == .appleAVPlayer, avPlayerUsesSystemControls, Constants.isIPhone {
|
if presentingPlayer, activeBackend == .appleAVPlayer, avPlayerUsesSystemControls, Constants.isIPhone {
|
||||||
@@ -551,8 +569,6 @@ final class PlayerModel: ObservableObject {
|
|||||||
} else {
|
} else {
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
Orientation.lockOrientation(.allButUpsideDown)
|
||||||
}
|
}
|
||||||
|
|
||||||
OrientationModel.shared.stopOrientationUpdates()
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -659,32 +675,37 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func closeCurrentItem(finished: Bool = false) {
|
func closeCurrentItem(finished: Bool = false) {
|
||||||
pause()
|
guard !closing else { return }
|
||||||
videoBeingOpened = nil
|
|
||||||
advancing = false
|
|
||||||
forceBackendOnPlay = nil
|
|
||||||
|
|
||||||
closing = true
|
closing = true
|
||||||
controls.presentingControls = false
|
|
||||||
|
|
||||||
self.prepareCurrentItemForHistory(finished: finished)
|
if playingFullScreen { exitFullScreen() }
|
||||||
|
|
||||||
self.hide()
|
Delay.by(0.3) { [weak self] in
|
||||||
|
|
||||||
Delay.by(0.8) { [weak self] in
|
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
self.closePiP()
|
pause()
|
||||||
|
videoBeingOpened = nil
|
||||||
|
advancing = false
|
||||||
|
forceBackendOnPlay = nil
|
||||||
|
|
||||||
withAnimation {
|
controls.presentingControls = false
|
||||||
self.currentItem = nil
|
|
||||||
|
self.prepareCurrentItemForHistory(finished: finished)
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
Delay.by(0.7) { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
if playingInPictureInPicture { self.closePiP() }
|
||||||
|
|
||||||
|
withAnimation {
|
||||||
|
self.currentItem = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateNowPlayingInfo()
|
||||||
|
self.backend.closeItem()
|
||||||
|
self.aspectRatio = VideoPlayerView.defaultAspectRatio
|
||||||
|
self.resetAutoplay()
|
||||||
|
self.closing = false
|
||||||
}
|
}
|
||||||
self.updateNowPlayingInfo()
|
|
||||||
|
|
||||||
self.backend.closeItem()
|
|
||||||
self.aspectRatio = VideoPlayerView.defaultAspectRatio
|
|
||||||
self.resetAutoplay()
|
|
||||||
self.closing = false
|
|
||||||
self.playingFullScreen = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,7 +794,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toggleFullScreenAction() {
|
func toggleFullScreenAction() {
|
||||||
toggleFullscreen(playingFullScreen, showControls: false)
|
toggleFullscreen(playingFullScreen, showControls: false, initiatedByButton: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func togglePiPAction() {
|
func togglePiPAction() {
|
||||||
@@ -786,20 +807,21 @@ final class PlayerModel: ObservableObject {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
var lockOrientationImage: String {
|
var lockOrientationImage: String {
|
||||||
lockedOrientation.isNil ? "lock.rotation.open" : "lock.rotation"
|
isOrientationLocked ? "lock.rotation" : "lock.rotation.open"
|
||||||
}
|
}
|
||||||
|
|
||||||
func lockOrientationAction() {
|
func lockOrientationAction() {
|
||||||
if lockedOrientation.isNil {
|
// This makes toggling orientation lock more robust
|
||||||
|
if lockedOrientation.isNil || !isOrientationLocked {
|
||||||
|
isOrientationLocked = true
|
||||||
let orientationMask = OrientationTracker.shared.currentInterfaceOrientationMask
|
let orientationMask = OrientationTracker.shared.currentInterfaceOrientationMask
|
||||||
lockedOrientation = orientationMask
|
lockedOrientation = orientationMask
|
||||||
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
||||||
Orientation.lockOrientation(orientationMask, andRotateTo: .landscapeLeft)
|
Orientation.lockOrientation(orientationMask, andRotateTo: playingFullScreen ? nil : orientation)
|
||||||
// iOS 16 workaround
|
|
||||||
Orientation.lockOrientation(orientationMask, andRotateTo: orientation)
|
|
||||||
} else {
|
} else {
|
||||||
|
isOrientationLocked = false
|
||||||
lockedOrientation = nil
|
lockedOrientation = nil
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
|
Orientation.lockOrientation(.allButUpsideDown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -985,7 +1007,14 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
func handleEnterForeground() {
|
func handleEnterForeground() {
|
||||||
setNeedsDrawing(presentingPlayer)
|
#if os(iOS)
|
||||||
|
OrientationTracker.shared.startDeviceOrientationTracking()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if os(tvOS)
|
||||||
|
// TODO: Not sure if this is realy needed on tvOS, maybe it can be removed.
|
||||||
|
setNeedsDrawing(presentingPlayer)
|
||||||
|
#endif
|
||||||
|
|
||||||
if !musicMode, activeBackend == .mpv {
|
if !musicMode, activeBackend == .mpv {
|
||||||
mpvBackend.addVideoTrackFromStream()
|
mpvBackend.addVideoTrackFromStream()
|
||||||
@@ -995,17 +1024,6 @@ final class PlayerModel: ObservableObject {
|
|||||||
avPlayerBackend.bindPlayerToLayer()
|
avPlayerBackend.bindPlayerToLayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
|
||||||
if wasFullscreen {
|
|
||||||
wasFullscreen = false
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
Delay.by(0.3) {
|
|
||||||
self?.enterFullScreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
guard closePiPAndOpenPlayerOnEnteringForeground, playingInPictureInPicture else {
|
guard closePiPAndOpenPlayerOnEnteringForeground, playingInPictureInPicture else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1018,6 +1036,10 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleEnterBackground() {
|
func handleEnterBackground() {
|
||||||
|
#if os(iOS)
|
||||||
|
OrientationTracker.shared.stopDeviceOrientationTracking()
|
||||||
|
#endif
|
||||||
|
|
||||||
if Defaults[.pauseOnEnteringBackground], !playingInPictureInPicture, !musicMode {
|
if Defaults[.pauseOnEnteringBackground], !playingInPictureInPicture, !musicMode {
|
||||||
pause()
|
pause()
|
||||||
} else if !playingInPictureInPicture, activeBackend == .appleAVPlayer {
|
} else if !playingInPictureInPicture, activeBackend == .appleAVPlayer {
|
||||||
@@ -1025,15 +1047,6 @@ final class PlayerModel: ObservableObject {
|
|||||||
} else if activeBackend == .mpv, !musicMode {
|
} else if activeBackend == .mpv, !musicMode {
|
||||||
mpvBackend.setVideoToNo()
|
mpvBackend.setVideoToNo()
|
||||||
}
|
}
|
||||||
#if os(iOS)
|
|
||||||
guard playingFullScreen else { return }
|
|
||||||
wasFullscreen = playingFullScreen
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
Delay.by(0.3) {
|
|
||||||
self?.exitFullScreen(showControls: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1124,7 +1137,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
task.resume()
|
task.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleFullscreen(_ isFullScreen: Bool, showControls: Bool = true) {
|
func toggleFullscreen(_ isFullScreen: Bool, showControls: Bool = true, initiatedByButton: Bool = false) {
|
||||||
controls.presentingControls = showControls && isFullScreen
|
controls.presentingControls = showControls && isFullScreen
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
@@ -1139,15 +1152,13 @@ final class PlayerModel: ObservableObject {
|
|||||||
avPlayerBackend.controller.enterFullScreen(animated: true)
|
avPlayerBackend.controller.enterFullScreen(animated: true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard rotateToLandscapeOnEnterFullScreen.isRotating else { return }
|
let lockOrientation = rotateToLandscapeOnEnterFullScreen.interfaceOrientation
|
||||||
if currentVideoIsLandscape {
|
if currentVideoIsLandscape {
|
||||||
let delay = activeBackend == .appleAVPlayer && avPlayerUsesSystemControls ? 0.8 : 0
|
if initiatedByButton {
|
||||||
// not sure why but first rotation call is ignore so doing rotate to same orientation first
|
Orientation.lockOrientation(self.isOrientationLocked ? (lockOrientation == .landscapeRight ? .landscapeRight : .landscapeLeft) : .landscape)
|
||||||
Delay.by(delay) {
|
|
||||||
let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape ? OrientationTracker.shared.currentInterfaceOrientation : self.rotateToLandscapeOnEnterFullScreen.interaceOrientation
|
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
|
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: orientation)
|
|
||||||
}
|
}
|
||||||
|
let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape ? OrientationTracker.shared.currentInterfaceOrientation : self.rotateToLandscapeOnEnterFullScreen.interfaceOrientation
|
||||||
|
Orientation.lockOrientation(self.isOrientationLocked ? (lockOrientation == .landscapeRight ? .landscapeRight : .landscapeLeft) : .landscape, andRotateTo: orientation)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if activeBackend == .appleAVPlayer, avPlayerUsesSystemControls {
|
if activeBackend == .appleAVPlayer, avPlayerUsesSystemControls {
|
||||||
@@ -1155,10 +1166,12 @@ final class PlayerModel: ObservableObject {
|
|||||||
avPlayerBackend.controller.dismiss(animated: true)
|
avPlayerBackend.controller.dismiss(animated: true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let rotationOrientation = Constants.isIPhone ? UIInterfaceOrientation.portrait : nil
|
if Defaults[.lockPortraitWhenBrowsing] {
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: rotationOrientation)
|
lockedOrientation = UIInterfaceOrientationMask.portrait
|
||||||
|
}
|
||||||
|
let rotationOrientation = Defaults[.lockPortraitWhenBrowsing] ? UIInterfaceOrientation.portrait : nil
|
||||||
|
Orientation.lockOrientation(Defaults[.lockPortraitWhenBrowsing] ? .portrait : .allButUpsideDown, andRotateTo: rotationOrientation)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1286,7 +1299,10 @@ final class PlayerModel: ObservableObject {
|
|||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
private func assignKeyPressMonitor() {
|
private func assignKeyPressMonitor() {
|
||||||
keyPressMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { keyEvent -> NSEvent? in
|
keyPressMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] keyEvent -> NSEvent? in
|
||||||
|
// Check if the player window is the key window
|
||||||
|
guard let self, let window = Windows.playerWindow, window.isKeyWindow else { return keyEvent }
|
||||||
|
|
||||||
switch keyEvent.keyCode {
|
switch keyEvent.keyCode {
|
||||||
case 124:
|
case 124:
|
||||||
if !self.liveStreamInAVPlayer {
|
if !self.liveStreamInAVPlayer {
|
||||||
|
|||||||
@@ -139,10 +139,14 @@ class Stream: Equatable, Hashable, Identifiable {
|
|||||||
|
|
||||||
case sd428p30
|
case sd428p30
|
||||||
case sd428p25
|
case sd428p25
|
||||||
|
case sd426p30
|
||||||
|
case sd426p25
|
||||||
case sd360p30
|
case sd360p30
|
||||||
case sd360p25
|
case sd360p25
|
||||||
case sd320p30
|
case sd320p30
|
||||||
case sd320p25
|
case sd320p25
|
||||||
|
case sd256p30
|
||||||
|
case sd256p25
|
||||||
case sd240p30
|
case sd240p30
|
||||||
case sd240p25
|
case sd240p25
|
||||||
case sd214p30
|
case sd214p30
|
||||||
@@ -253,7 +257,7 @@ class Stream: Equatable, Hashable, Identifiable {
|
|||||||
case .sd480p30, .sd480p25:
|
case .sd480p30, .sd480p25:
|
||||||
return 2_500_000 // 2.5 Mbit/s
|
return 2_500_000 // 2.5 Mbit/s
|
||||||
|
|
||||||
case .sd428p30, .sd428p25:
|
case .sd428p30, .sd428p25, .sd426p30, .sd426p25:
|
||||||
return 2_000_000 // 2 Mbit/s
|
return 2_000_000 // 2 Mbit/s
|
||||||
|
|
||||||
case .sd360p30, .sd360p25:
|
case .sd360p30, .sd360p25:
|
||||||
@@ -262,7 +266,7 @@ class Stream: Equatable, Hashable, Identifiable {
|
|||||||
case .sd320p30, .sd320p25:
|
case .sd320p30, .sd320p25:
|
||||||
return 1_200_000 // 1.2 Mbit/s
|
return 1_200_000 // 1.2 Mbit/s
|
||||||
|
|
||||||
case .sd240p30, .sd240p25:
|
case .sd256p30, .sd256p25, .sd240p30, .sd240p25:
|
||||||
return 1_000_000 // 1 Mbit/s
|
return 1_000_000 // 1 Mbit/s
|
||||||
|
|
||||||
case .sd214p30, .sd214p25:
|
case .sd214p30, .sd214p25:
|
||||||
|
|||||||
@@ -93,12 +93,9 @@ extension Defaults.Keys {
|
|||||||
static let enableReturnYouTubeDislike = Key<Bool>("enableReturnYouTubeDislike", default: false)
|
static let enableReturnYouTubeDislike = Key<Bool>("enableReturnYouTubeDislike", default: false)
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
static let isOrientationLocked = Key<Bool>("isOrientationLocked", default: Constants.isIPhone)
|
||||||
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: Constants.isIPhone)
|
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: Constants.isIPhone)
|
||||||
static let rotateToLandscapeOnEnterFullScreen = Key<FullScreenRotationSetting>(
|
static let rotateToLandscapeOnEnterFullScreen = Key<FullScreenRotationSetting>("rotateToLandscapeOnEnterFullScreen", default: .landscapeRight)
|
||||||
"rotateToLandscapeOnEnterFullScreen",
|
|
||||||
default: Constants.isIPhone ? .landscapeRight : .disabled
|
|
||||||
)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static let closePiPOnNavigation = Key<Bool>("closePiPOnNavigation", default: false)
|
static let closePiPOnNavigation = Key<Bool>("closePiPOnNavigation", default: false)
|
||||||
@@ -134,6 +131,7 @@ extension Defaults.Keys {
|
|||||||
|
|
||||||
static let playerControlsLayout = Key<PlayerControlsLayout>("playerControlsLayout", default: playerControlsLayoutDefault)
|
static let playerControlsLayout = Key<PlayerControlsLayout>("playerControlsLayout", default: playerControlsLayoutDefault)
|
||||||
static let fullScreenPlayerControlsLayout = Key<PlayerControlsLayout>("fullScreenPlayerControlsLayout", default: fullScreenPlayerControlsLayoutDefault)
|
static let fullScreenPlayerControlsLayout = Key<PlayerControlsLayout>("fullScreenPlayerControlsLayout", default: fullScreenPlayerControlsLayoutDefault)
|
||||||
|
static let playerControlsBackgroundOpacity = Key<Double>("playerControlsBackgroundOpacity", default: 0.2)
|
||||||
|
|
||||||
static let systemControlsCommands = Key<SystemControlsCommands>("systemControlsCommands", default: .restartAndAdvanceToNext)
|
static let systemControlsCommands = Key<SystemControlsCommands>("systemControlsCommands", default: .restartAndAdvanceToNext)
|
||||||
|
|
||||||
@@ -612,26 +610,19 @@ enum PlayerTapGestureAction: String, CaseIterable, Defaults.Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum FullScreenRotationSetting: String, CaseIterable, Defaults.Serializable {
|
enum FullScreenRotationSetting: String, CaseIterable, Defaults.Serializable {
|
||||||
case disabled
|
|
||||||
case landscapeLeft
|
case landscapeLeft
|
||||||
case landscapeRight
|
case landscapeRight
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
var interaceOrientation: UIInterfaceOrientation {
|
var interfaceOrientation: UIInterfaceOrientation {
|
||||||
switch self {
|
switch self {
|
||||||
case .landscapeLeft:
|
case .landscapeLeft:
|
||||||
return .landscapeLeft
|
return .landscapeLeft
|
||||||
case .landscapeRight:
|
case .landscapeRight:
|
||||||
return .landscapeRight
|
return .landscapeRight
|
||||||
default:
|
|
||||||
return .portrait
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var isRotating: Bool {
|
|
||||||
self != .disabled
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WidgetSettings: Defaults.Serializable {
|
struct WidgetSettings: Defaults.Serializable {
|
||||||
|
|||||||
@@ -17,12 +17,11 @@ import SwiftUI
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
func playerViewController(_: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator) {
|
func playerViewController(_: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator) {
|
||||||
guard rotateToLandscapeOnEnterFullScreen.isRotating else { return }
|
|
||||||
if PlayerModel.shared.currentVideoIsLandscape {
|
if PlayerModel.shared.currentVideoIsLandscape {
|
||||||
let delay = PlayerModel.shared.activeBackend == .appleAVPlayer && avPlayerUsesSystemControls ? 0.8 : 0
|
let delay = PlayerModel.shared.activeBackend == .appleAVPlayer && avPlayerUsesSystemControls ? 0.8 : 0
|
||||||
// not sure why but first rotation call is ignore so doing rotate to same orientation first
|
// not sure why but first rotation call is ignore so doing rotate to same orientation first
|
||||||
Delay.by(delay) {
|
Delay.by(delay) {
|
||||||
let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape ? OrientationTracker.shared.currentInterfaceOrientation : self.rotateToLandscapeOnEnterFullScreen.interaceOrientation
|
let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape ? OrientationTracker.shared.currentInterfaceOrientation : self.rotateToLandscapeOnEnterFullScreen.interfaceOrientation
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: orientation)
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: orientation)
|
||||||
}
|
}
|
||||||
@@ -37,8 +36,6 @@ import SwiftUI
|
|||||||
}
|
}
|
||||||
if !context.isCancelled {
|
if !context.isCancelled {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
self.player.lockedOrientation = nil
|
|
||||||
|
|
||||||
if Constants.isIPhone {
|
if Constants.isIPhone {
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ struct PlayerControls: View {
|
|||||||
|
|
||||||
@Default(.playerControlsLayout) private var regularPlayerControlsLayout
|
@Default(.playerControlsLayout) private var regularPlayerControlsLayout
|
||||||
@Default(.fullScreenPlayerControlsLayout) private var fullScreenPlayerControlsLayout
|
@Default(.fullScreenPlayerControlsLayout) private var fullScreenPlayerControlsLayout
|
||||||
|
@Default(.playerControlsBackgroundOpacity) private var playerControlsBackgroundOpacity
|
||||||
@Default(.buttonBackwardSeekDuration) private var buttonBackwardSeekDuration
|
@Default(.buttonBackwardSeekDuration) private var buttonBackwardSeekDuration
|
||||||
@Default(.buttonForwardSeekDuration) private var buttonForwardSeekDuration
|
@Default(.buttonForwardSeekDuration) private var buttonForwardSeekDuration
|
||||||
|
|
||||||
@@ -270,6 +271,9 @@ struct PlayerControls: View {
|
|||||||
}
|
}
|
||||||
} else if player.videoForDisplay == nil {
|
} else if player.videoForDisplay == nil {
|
||||||
Color.black
|
Color.black
|
||||||
|
} else if model.presentingControls {
|
||||||
|
Color.black.opacity(playerControlsBackgroundOpacity)
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -389,7 +393,7 @@ struct PlayerControls: View {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
private var lockOrientationButton: some View {
|
private var lockOrientationButton: some View {
|
||||||
button("Lock Rotation", systemImage: player.lockOrientationImage, active: !player.lockedOrientation.isNil, action: player.lockOrientationAction)
|
button("Lock Rotation", systemImage: player.lockOrientationImage, active: player.isOrientationLocked, action: player.lockOrientationAction)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -64,11 +64,7 @@ extension VideoPlayerView {
|
|||||||
|
|
||||||
// Toggle fullscreen on upward drag only when not disabled
|
// Toggle fullscreen on upward drag only when not disabled
|
||||||
if verticalDrag < -50 {
|
if verticalDrag < -50 {
|
||||||
if player.playingFullScreen {
|
player.toggleFullScreenAction()
|
||||||
player.exitFullScreen(showControls: false)
|
|
||||||
} else {
|
|
||||||
player.enterFullScreen()
|
|
||||||
}
|
|
||||||
disableGestureTemporarily()
|
disableGestureTemporarily()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ struct VideoActions: View {
|
|||||||
actionButton("PiP", systemImage: player.pipImage, active: player.playingInPictureInPicture, action: player.togglePiPAction)
|
actionButton("PiP", systemImage: player.pipImage, active: player.playingInPictureInPicture, action: player.togglePiPAction)
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
case .lockOrientation:
|
case .lockOrientation:
|
||||||
actionButton("Lock", systemImage: player.lockOrientationImage, active: player.lockedOrientation != nil, action: player.lockOrientationAction)
|
actionButton("Lock", systemImage: player.lockOrientationImage, active: player.isOrientationLocked, action: player.lockOrientationAction)
|
||||||
#endif
|
#endif
|
||||||
case .restart:
|
case .restart:
|
||||||
actionButton("Replay", systemImage: "backward.end.fill", action: player.replayAction)
|
actionButton("Replay", systemImage: "backward.end.fill", action: player.replayAction)
|
||||||
|
|||||||
@@ -111,9 +111,6 @@ struct VideoPlayerView: View {
|
|||||||
.onChange(of: geometry.size) { _ in
|
.onChange(of: geometry.size) { _ in
|
||||||
self.playerSize = geometry.size
|
self.playerSize = geometry.size
|
||||||
}
|
}
|
||||||
.onChange(of: fullScreenDetails) { value in
|
|
||||||
player.backend.setNeedsDrawing(!value)
|
|
||||||
}
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.onChange(of: player.presentingPlayer) { newValue in
|
.onChange(of: player.presentingPlayer) { newValue in
|
||||||
if newValue {
|
if newValue {
|
||||||
@@ -127,19 +124,6 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
viewDragOffset = 0
|
viewDragOffset = 0
|
||||||
|
|
||||||
Delay.by(0.2) {
|
|
||||||
orientationModel.configureOrientationUpdatesBasedOnAccelerometer()
|
|
||||||
|
|
||||||
if let orientationMask = player.lockedOrientation {
|
|
||||||
Orientation.lockOrientation(
|
|
||||||
orientationMask,
|
|
||||||
andRotateTo: orientationMask == .landscapeLeft ? .landscapeLeft : orientationMask == .landscapeRight ? .landscapeRight : .portrait
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.onAnimationCompleted(for: viewDragOffset) {
|
.onAnimationCompleted(for: viewDragOffset) {
|
||||||
guard !dragGestureState else { return }
|
guard !dragGestureState else { return }
|
||||||
@@ -313,11 +297,14 @@ struct VideoPlayerView: View {
|
|||||||
playerSize: player.playerSize,
|
playerSize: player.playerSize,
|
||||||
fullScreen: fullScreenDetails
|
fullScreen: fullScreenDetails
|
||||||
))
|
))
|
||||||
|
#if os(macOS)
|
||||||
|
// TODO: Check whether this is needed on macOS.
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
if player.presentingPlayer {
|
if player.presentingPlayer {
|
||||||
player.setNeedsDrawing(true)
|
player.setNeedsDrawing(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
.id(player.currentVideo?.cacheKey)
|
.id(player.currentVideo?.cacheKey)
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ struct BrowsingSettings: View {
|
|||||||
@Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges
|
@Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges
|
||||||
@Default(.keepChannelsWithUnwatchedFeedOnTop) private var keepChannelsWithUnwatchedFeedOnTop
|
@Default(.keepChannelsWithUnwatchedFeedOnTop) private var keepChannelsWithUnwatchedFeedOnTop
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
||||||
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
|
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
|
||||||
@Default(.showDocuments) private var showDocuments
|
@Default(.showDocuments) private var showDocuments
|
||||||
#endif
|
#endif
|
||||||
@@ -161,14 +162,18 @@ struct BrowsingSettings: View {
|
|||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
Toggle("Show Documents", isOn: $showDocuments)
|
Toggle("Show Documents", isOn: $showDocuments)
|
||||||
|
|
||||||
Toggle("Lock portrait mode", isOn: $lockPortraitWhenBrowsing)
|
if Constants.isIPad {
|
||||||
.onChange(of: lockPortraitWhenBrowsing) { lock in
|
Toggle("Lock portrait mode", isOn: $lockPortraitWhenBrowsing)
|
||||||
if lock {
|
.onChange(of: lockPortraitWhenBrowsing) { lock in
|
||||||
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
if lock {
|
||||||
} else {
|
enterFullscreenInLandscape = true
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||||
|
} else {
|
||||||
|
enterFullscreenInLandscape = false
|
||||||
|
Orientation.lockOrientation(.allButUpsideDown)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if !accounts.isEmpty {
|
if !accounts.isEmpty {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ struct PlayerControlsSettings: View {
|
|||||||
@Default(.playerControlsAdvanceToNextEnabled) private var playerControlsAdvanceToNextEnabled
|
@Default(.playerControlsAdvanceToNextEnabled) private var playerControlsAdvanceToNextEnabled
|
||||||
@Default(.playerControlsPlaybackModeEnabled) private var playerControlsPlaybackModeEnabled
|
@Default(.playerControlsPlaybackModeEnabled) private var playerControlsPlaybackModeEnabled
|
||||||
@Default(.playerControlsMusicModeEnabled) private var playerControlsMusicModeEnabled
|
@Default(.playerControlsMusicModeEnabled) private var playerControlsMusicModeEnabled
|
||||||
|
@Default(.playerControlsBackgroundOpacity) private var playerControlsBackgroundOpacity
|
||||||
|
|
||||||
private var player = PlayerModel.shared
|
private var player = PlayerModel.shared
|
||||||
|
|
||||||
@@ -76,6 +77,8 @@ struct PlayerControlsSettings: View {
|
|||||||
playerControlsLayoutPicker
|
playerControlsLayoutPicker
|
||||||
SettingsHeader(text: "Fullscreen size".localized(), secondary: true)
|
SettingsHeader(text: "Fullscreen size".localized(), secondary: true)
|
||||||
fullScreenPlayerControlsLayoutPicker
|
fullScreenPlayerControlsLayoutPicker
|
||||||
|
SettingsHeader(text: "Background opacity".localized(), secondary: true)
|
||||||
|
playerControlsBackgroundOpacityPicker
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -202,6 +205,15 @@ struct PlayerControlsSettings: View {
|
|||||||
.modifier(SettingsPickerModifier())
|
.modifier(SettingsPickerModifier())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var playerControlsBackgroundOpacityPicker: some View {
|
||||||
|
Picker("Background opacity", selection: $playerControlsBackgroundOpacity) {
|
||||||
|
ForEach(Array(stride(from: 0.0, through: 1.0, by: 0.1)), id: \.self) { value in
|
||||||
|
Text("\(Int(value * 100))%").tag(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modifier(SettingsPickerModifier())
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder private var seekingSection: some View {
|
@ViewBuilder private var seekingSection: some View {
|
||||||
seekingDurationSetting("System controls", $systemControlsSeekDuration)
|
seekingDurationSetting("System controls", $systemControlsSeekDuration)
|
||||||
.foregroundColor(systemControlsCommands == .restartAndAdvanceToNext ? .secondary : .primary)
|
.foregroundColor(systemControlsCommands == .restartAndAdvanceToNext ? .secondary : .primary)
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ struct PlayerSettings: View {
|
|||||||
@Default(.pauseOnHidingPlayer) private var pauseOnHidingPlayer
|
@Default(.pauseOnHidingPlayer) private var pauseOnHidingPlayer
|
||||||
@Default(.closeVideoOnEOF) private var closeVideoOnEOF
|
@Default(.closeVideoOnEOF) private var closeVideoOnEOF
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
|
|
||||||
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
||||||
|
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
|
||||||
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
|
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
|
||||||
#endif
|
#endif
|
||||||
@Default(.closePiPOnNavigation) private var closePiPOnNavigation
|
@Default(.closePiPOnNavigation) private var closePiPOnNavigation
|
||||||
@@ -87,7 +87,7 @@ struct PlayerSettings: View {
|
|||||||
}
|
}
|
||||||
pauseOnHidingPlayerToggle
|
pauseOnHidingPlayerToggle
|
||||||
closeVideoOnEOFToggle
|
closeVideoOnEOFToggle
|
||||||
#if !os(tvOS)
|
#if os(macOS)
|
||||||
exitFullscreenOnEOFToggle
|
exitFullscreenOnEOFToggle
|
||||||
#endif
|
#endif
|
||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
@@ -202,11 +202,12 @@ struct PlayerSettings: View {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
Section(header: SettingsHeader(text: "Orientation".localized())) {
|
Section(header: SettingsHeader(text: "Fullscreen".localized())) {
|
||||||
if idiom == .pad {
|
if Constants.isIPad {
|
||||||
enterFullscreenInLandscapeToggle
|
enterFullscreenInLandscapeToggle
|
||||||
}
|
}
|
||||||
honorSystemOrientationLockToggle
|
|
||||||
|
exitFullscreenOnEOFToggle
|
||||||
rotateToLandscapeOnEnterFullScreenPicker
|
rotateToLandscapeOnEnterFullScreenPicker
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -318,20 +319,15 @@ struct PlayerSettings: View {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
private var honorSystemOrientationLockToggle: some View {
|
|
||||||
Toggle("Honor orientation lock", isOn: $honorSystemOrientationLock)
|
|
||||||
.disabled(!enterFullscreenInLandscape)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var enterFullscreenInLandscapeToggle: some View {
|
private var enterFullscreenInLandscapeToggle: some View {
|
||||||
Toggle("Enter fullscreen in landscape", isOn: $enterFullscreenInLandscape)
|
Toggle("Enter fullscreen in landscape orientation", isOn: $enterFullscreenInLandscape)
|
||||||
|
.disabled(lockPortraitWhenBrowsing)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var rotateToLandscapeOnEnterFullScreenPicker: some View {
|
private var rotateToLandscapeOnEnterFullScreenPicker: some View {
|
||||||
Picker("Rotate when entering fullscreen on landscape video", selection: $rotateToLandscapeOnEnterFullScreen) {
|
Picker("Default orientation", selection: $rotateToLandscapeOnEnterFullScreen) {
|
||||||
Text("Landscape left").tag(FullScreenRotationSetting.landscapeLeft)
|
Text("Landscape left").tag(FullScreenRotationSetting.landscapeLeft)
|
||||||
Text("Landscape right").tag(FullScreenRotationSetting.landscapeRight)
|
Text("Landscape right").tag(FullScreenRotationSetting.landscapeRight)
|
||||||
Text("No rotation").tag(FullScreenRotationSetting.disabled)
|
|
||||||
}
|
}
|
||||||
.modifier(SettingsPickerModifier())
|
.modifier(SettingsPickerModifier())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,9 +204,14 @@ struct YatteeApp: App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
if Defaults[.lockPortraitWhenBrowsing] {
|
if Defaults[.lockPortraitWhenBrowsing] {
|
||||||
Orientation.lockOrientation(.all, andRotateTo: .portrait)
|
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||||
|
} else {
|
||||||
|
let rotationOrientation =
|
||||||
|
OrientationTracker.shared.currentDeviceOrientation.rawValue == 4 ? UIInterfaceOrientation.landscapeRight :
|
||||||
|
(OrientationTracker.shared.currentDeviceOrientation.rawValue == 3 ? UIInterfaceOrientation.landscapeLeft : UIInterfaceOrientation.portrait)
|
||||||
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: rotationOrientation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -225,6 +230,17 @@ struct YatteeApp: App {
|
|||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
self.migrateQualityProfiles()
|
self.migrateQualityProfiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
self.migrateRotateToLandscapeOnEnterFullScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
self.migrateLockPortraitWhenBrowsing()
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +269,22 @@ struct YatteeApp: App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
func migrateRotateToLandscapeOnEnterFullScreen() {
|
||||||
|
if Defaults[.rotateToLandscapeOnEnterFullScreen] != .landscapeRight || Defaults[.rotateToLandscapeOnEnterFullScreen] != .landscapeLeft {
|
||||||
|
Defaults[.rotateToLandscapeOnEnterFullScreen] = .landscapeRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func migrateLockPortraitWhenBrowsing() {
|
||||||
|
if Constants.isIPhone {
|
||||||
|
Defaults[.lockPortraitWhenBrowsing] = true
|
||||||
|
} else if Constants.isIPad, Defaults[.lockPortraitWhenBrowsing] {
|
||||||
|
Defaults[.enterFullscreenInLandscape] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
var navigationStyle: NavigationStyle {
|
var navigationStyle: NavigationStyle {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
return horizontalSizeClass == .compact ? .tab : .sidebar
|
return horizontalSizeClass == .compact ? .tab : .sidebar
|
||||||
|
|||||||
@@ -4103,7 +4103,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "Open in Yattee/Open in Yattee.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "Open in Yattee/Open in Yattee.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = "Open in Yattee/Info.plist";
|
INFOPLIST_FILE = "Open in Yattee/Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Open in Yattee";
|
INFOPLIST_KEY_CFBundleDisplayName = "Open in Yattee";
|
||||||
@@ -4134,7 +4134,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = "Open in Yattee/Info.plist";
|
INFOPLIST_FILE = "Open in Yattee/Info.plist";
|
||||||
@@ -4165,7 +4165,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
@@ -4185,7 +4185,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
@@ -4349,7 +4349,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "iOS/Yattee (iOS).entitlements";
|
CODE_SIGN_ENTITLEMENTS = "iOS/Yattee (iOS).entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
"DEBUG=1",
|
"DEBUG=1",
|
||||||
@@ -4366,7 +4366,9 @@
|
|||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
||||||
INFOPLIST_KEY_UIStatusBarHidden = NO;
|
INFOPLIST_KEY_UIStatusBarHidden = NO;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UIStatusBarStyle = "";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -4401,7 +4403,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = "GLES_SILENCE_DEPRECATION=1";
|
GCC_PREPROCESSOR_DEFINITIONS = "GLES_SILENCE_DEPRECATION=1";
|
||||||
@@ -4415,7 +4417,9 @@
|
|||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
||||||
INFOPLIST_KEY_UIStatusBarHidden = NO;
|
INFOPLIST_KEY_UIStatusBarHidden = NO;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UIStatusBarStyle = "";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -4453,7 +4457,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -4492,13 +4496,14 @@
|
|||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = 78Z5H3M6RJ;
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = 78Z5H3M6RJ;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
ENABLE_USER_SELECTED_FILES = readonly;
|
ENABLE_USER_SELECTED_FILES = readonly;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = macOS/Info.plist;
|
INFOPLIST_FILE = macOS/Info.plist;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.video";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.video";
|
||||||
@@ -4526,7 +4531,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -4549,7 +4554,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -4574,7 +4579,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -4598,7 +4603,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -4624,7 +4629,7 @@
|
|||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -4664,7 +4669,7 @@
|
|||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "iPhone Distribution";
|
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "iPhone Distribution";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
"DEVELOPMENT_TEAM[sdk=appletvos*]" = 78Z5H3M6RJ;
|
"DEVELOPMENT_TEAM[sdk=appletvos*]" = 78Z5H3M6RJ;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -4704,7 +4709,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -4727,7 +4732,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 194;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
import Defaults
|
||||||
import Foundation
|
import Foundation
|
||||||
import Logging
|
import Logging
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
final class AppDelegate: UIResponder, UIApplicationDelegate {
|
final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
var orientationLock = UIInterfaceOrientationMask.all
|
var orientationLock = UIInterfaceOrientationMask.allButUpsideDown
|
||||||
|
|
||||||
private var logger = Logger(label: "stream.yattee.app.delegalate")
|
private var logger = Logger(label: "stream.yattee.app.delegate")
|
||||||
private(set) static var instance: AppDelegate!
|
private(set) static var instance: AppDelegate!
|
||||||
|
|
||||||
func application(_: UIApplication, supportedInterfaceOrientationsFor _: UIWindow?) -> UIInterfaceOrientationMask {
|
func application(_: UIApplication, supportedInterfaceOrientationsFor _: UIWindow?) -> UIInterfaceOrientationMask {
|
||||||
orientationLock
|
return orientationLock
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // swiftlint:disable:this discouraged_optional_collection
|
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // swiftlint:disable:this discouraged_optional_collection
|
||||||
@@ -19,6 +20,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
UIViewController.swizzleHomeIndicatorProperty()
|
UIViewController.swizzleHomeIndicatorProperty()
|
||||||
OrientationTracker.shared.startDeviceOrientationTracking()
|
OrientationTracker.shared.startDeviceOrientationTracking()
|
||||||
|
OrientationModel.shared.startOrientationUpdates()
|
||||||
|
|
||||||
// Configure the audio session for playback
|
// Configure the audio session for playback
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import Defaults
|
import Defaults
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Logging
|
||||||
import Repeat
|
import Repeat
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
final class OrientationModel {
|
final class OrientationModel {
|
||||||
static var shared = OrientationModel()
|
static var shared = OrientationModel()
|
||||||
|
let logger = Logger(label: "stream.yattee.orientation.model")
|
||||||
|
|
||||||
var orientation = UIInterfaceOrientation.portrait
|
var orientation = UIInterfaceOrientation.portrait
|
||||||
var lastOrientation: UIInterfaceOrientation?
|
var lastOrientation: UIInterfaceOrientation?
|
||||||
@@ -13,79 +15,69 @@ final class OrientationModel {
|
|||||||
|
|
||||||
private var player = PlayerModel.shared
|
private var player = PlayerModel.shared
|
||||||
|
|
||||||
func configureOrientationUpdatesBasedOnAccelerometer() {
|
func startOrientationUpdates() {
|
||||||
let currentOrientation = OrientationTracker.shared.currentInterfaceOrientation
|
// Ensure the orientation observer is active
|
||||||
if currentOrientation.isLandscape,
|
|
||||||
Defaults[.enterFullscreenInLandscape],
|
|
||||||
!Defaults[.honorSystemOrientationLock],
|
|
||||||
!player.playingFullScreen,
|
|
||||||
!player.currentItem.isNil,
|
|
||||||
player.lockedOrientation.isNil || player.lockedOrientation!.contains(.landscape),
|
|
||||||
!player.playingInPictureInPicture,
|
|
||||||
player.presentingPlayer
|
|
||||||
{
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.player.controls.presentingControls = false
|
|
||||||
self.player.enterFullScreen(showControls: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
player.onPresentPlayer.append {
|
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: currentOrientation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
orientationObserver = NotificationCenter.default.addObserver(
|
orientationObserver = NotificationCenter.default.addObserver(
|
||||||
forName: OrientationTracker.deviceOrientationChangedNotification,
|
forName: OrientationTracker.deviceOrientationChangedNotification,
|
||||||
object: nil,
|
object: nil,
|
||||||
queue: .main
|
queue: .main
|
||||||
) { _ in
|
) { _ in
|
||||||
guard !Defaults[.honorSystemOrientationLock],
|
self.logger.info("Notification received: Device orientation changed.")
|
||||||
self.player.presentingPlayer,
|
|
||||||
!self.player.playingInPictureInPicture,
|
// We only allow .portrait and are not showing the player
|
||||||
self.player.lockedOrientation.isNil
|
guard (!self.player.presentingPlayer && !Defaults[.lockPortraitWhenBrowsing]) || self.player.presentingPlayer
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
||||||
|
self.logger.info("Current interface orientation: \(orientation)")
|
||||||
|
|
||||||
guard self.lastOrientation != orientation else {
|
// Always update lastOrientation to keep track of the latest state
|
||||||
|
if self.lastOrientation != orientation {
|
||||||
|
self.lastOrientation = orientation
|
||||||
|
self.logger.info("Orientation changed to: \(orientation)")
|
||||||
|
} else {
|
||||||
|
self.logger.info("Orientation has not changed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only take action if the player is active and presenting
|
||||||
|
guard (!self.player.isOrientationLocked && !self.player.playingInPictureInPicture) || (!Defaults[.lockPortraitWhenBrowsing] && !self.player.presentingPlayer) || (!Defaults[.lockPortraitWhenBrowsing] && self.player.presentingPlayer && !self.player.isOrientationLocked)
|
||||||
|
else {
|
||||||
|
self.logger.info("Only updating orientation without actions.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.lastOrientation = orientation
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
guard Defaults[.enterFullscreenInLandscape],
|
|
||||||
self.player.presentingPlayer
|
|
||||||
else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.orientationDebouncer.callback = {
|
self.orientationDebouncer.callback = {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if orientation.isLandscape {
|
if orientation.isLandscape {
|
||||||
self.player.controls.presentingControls = false
|
if Defaults[.enterFullscreenInLandscape], self.player.presentingPlayer {
|
||||||
self.player.enterFullScreen(showControls: false)
|
self.logger.info("Entering fullscreen because orientation is landscape.")
|
||||||
|
self.player.controls.presentingControls = false
|
||||||
|
self.player.enterFullScreen(showControls: false)
|
||||||
|
}
|
||||||
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
|
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
|
||||||
} else {
|
} else {
|
||||||
self.player.exitFullScreen(showControls: false)
|
self.logger.info("Exiting fullscreen because orientation is portrait.")
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
if self.player.playingFullScreen {
|
||||||
|
self.player.exitFullScreen(showControls: false)
|
||||||
|
}
|
||||||
|
if Defaults[.lockPortraitWhenBrowsing] {
|
||||||
|
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||||
|
} else {
|
||||||
|
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.orientationDebouncer.call()
|
self.orientationDebouncer.call()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopOrientationUpdates() {
|
|
||||||
guard let observer = orientationObserver else { return }
|
|
||||||
NotificationCenter.default.removeObserver(observer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation? = nil) {
|
func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation? = nil) {
|
||||||
|
logger.info("Locking orientation to: \(orientation), rotating to: \(String(describing: rotateOrientation)).")
|
||||||
if let rotateOrientation {
|
if let rotateOrientation {
|
||||||
self.orientation = rotateOrientation
|
self.orientation = rotateOrientation
|
||||||
lastOrientation = rotateOrientation
|
lastOrientation = rotateOrientation
|
||||||
|
|||||||
Reference in New Issue
Block a user