mirror of
https://github.com/yattee/yattee.git
synced 2024-12-14 14:20:32 +05:30
tvOS fixes
This commit is contained in:
parent
011ca5dac2
commit
b9dfdeb23f
@ -276,6 +276,13 @@ final class MPVBackend: PlayerBackend {
|
|||||||
startClientUpdates()
|
startClientUpdates()
|
||||||
onFileLoaded = nil
|
onFileLoaded = nil
|
||||||
|
|
||||||
|
case MPV_EVENT_PLAYBACK_RESTART:
|
||||||
|
isLoadingVideo = false
|
||||||
|
|
||||||
|
onFileLoaded?()
|
||||||
|
startClientUpdates()
|
||||||
|
onFileLoaded = nil
|
||||||
|
|
||||||
case MPV_EVENT_UNPAUSE:
|
case MPV_EVENT_UNPAUSE:
|
||||||
isLoadingVideo = false
|
isLoadingVideo = false
|
||||||
|
|
||||||
|
@ -10,6 +10,14 @@ struct PlayerControls: View {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||||
|
#elseif os(tvOS)
|
||||||
|
enum Field: Hashable {
|
||||||
|
case play
|
||||||
|
case backward
|
||||||
|
case forward
|
||||||
|
}
|
||||||
|
|
||||||
|
@FocusState private var focusedField: Field?
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
init(player: PlayerModel) {
|
init(player: PlayerModel) {
|
||||||
@ -57,14 +65,27 @@ struct PlayerControls: View {
|
|||||||
}
|
}
|
||||||
.opacity(model.presentingControls ? 1 : 0)
|
.opacity(model.presentingControls ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
#if os(tvOS)
|
||||||
|
.onChange(of: model.presentingControls) { _ in
|
||||||
|
if model.presentingControls {
|
||||||
|
focusedField = .play
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: focusedField) { _ in
|
||||||
|
model.resetTimer()
|
||||||
|
}
|
||||||
|
#else
|
||||||
.background(controlsBackground)
|
.background(controlsBackground)
|
||||||
|
#endif
|
||||||
.environment(\.colorScheme, .dark)
|
.environment(\.colorScheme, .dark)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
var controlsBackground: some View {
|
var controlsBackground: some View {
|
||||||
PlayerGestures()
|
PlayerGestures()
|
||||||
.background(Color.black.opacity(model.presentingControls ? 0.5 : 0))
|
.background(Color.black.opacity(model.presentingControls ? 0.5 : 0))
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
var timeline: some View {
|
var timeline: some View {
|
||||||
TimelineView(duration: durationBinding, current: currentTimeBinding, cornerRadius: 0)
|
TimelineView(duration: durationBinding, current: currentTimeBinding, cornerRadius: 0)
|
||||||
@ -93,12 +114,17 @@ struct PlayerControls: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
ToggleBackendButton()
|
ToggleBackendButton()
|
||||||
Text("•")
|
Text("•")
|
||||||
|
|
||||||
StreamControl()
|
StreamControl()
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
.frame(maxWidth: 160)
|
.frame(maxWidth: 160)
|
||||||
#endif
|
#endif
|
||||||
|
#else
|
||||||
|
Text(player.stream?.description ?? "")
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(.primary)
|
||||||
.padding(.trailing, 4)
|
.padding(.trailing, 4)
|
||||||
@ -111,7 +137,9 @@ struct PlayerControls: View {
|
|||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "chevron.down.circle.fill")
|
Image(systemName: "chevron.down.circle.fill")
|
||||||
}
|
}
|
||||||
|
#if !os(tvOS)
|
||||||
.keyboardShortcut(.cancelAction)
|
.keyboardShortcut(.cancelAction)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var playbackStatus: String {
|
private var playbackStatus: String {
|
||||||
@ -146,7 +174,9 @@ struct PlayerControls: View {
|
|||||||
|
|
||||||
var buttonsBar: some View {
|
var buttonsBar: some View {
|
||||||
HStack {
|
HStack {
|
||||||
|
#if !os(tvOS)
|
||||||
fullscreenButton
|
fullscreenButton
|
||||||
|
#endif
|
||||||
Spacer()
|
Spacer()
|
||||||
// button("Music Mode", systemImage: "music.note")
|
// button("Music Mode", systemImage: "music.note")
|
||||||
}
|
}
|
||||||
@ -159,15 +189,26 @@ struct PlayerControls: View {
|
|||||||
) {
|
) {
|
||||||
model.toggleFullscreen(fullScreenLayout)
|
model.toggleFullscreen(fullScreenLayout)
|
||||||
}
|
}
|
||||||
|
#if !os(tvOS)
|
||||||
.keyboardShortcut(fullScreenLayout ? .cancelAction : .defaultAction)
|
.keyboardShortcut(fullScreenLayout ? .cancelAction : .defaultAction)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var mediumButtonsBar: some View {
|
var mediumButtonsBar: some View {
|
||||||
HStack {
|
HStack {
|
||||||
|
#if !os(tvOS)
|
||||||
button("Seek Backward", systemImage: "gobackward.10", size: 50, cornerRadius: 10) {
|
button("Seek Backward", systemImage: "gobackward.10", size: 50, cornerRadius: 10) {
|
||||||
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
|
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(tvOS)
|
||||||
|
.focused($focusedField, equals: .backward)
|
||||||
|
#else
|
||||||
.keyboardShortcut("k")
|
.keyboardShortcut("k")
|
||||||
|
.keyboardShortcut(.leftArrow)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
@ -179,15 +220,27 @@ struct PlayerControls: View {
|
|||||||
) {
|
) {
|
||||||
player.backend.togglePlay()
|
player.backend.togglePlay()
|
||||||
}
|
}
|
||||||
|
#if os(tvOS)
|
||||||
|
.focused($focusedField, equals: .play)
|
||||||
|
#else
|
||||||
.keyboardShortcut("p")
|
.keyboardShortcut("p")
|
||||||
|
.keyboardShortcut(.space)
|
||||||
|
#endif
|
||||||
.disabled(model.isLoadingVideo)
|
.disabled(model.isLoadingVideo)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
button("Seek Forward", systemImage: "goforward.10", size: 50, cornerRadius: 10) {
|
button("Seek Forward", systemImage: "goforward.10", size: 50, cornerRadius: 10) {
|
||||||
player.backend.seek(relative: .secondsInDefaultTimescale(10))
|
player.backend.seek(relative: .secondsInDefaultTimescale(10))
|
||||||
}
|
}
|
||||||
|
#if os(tvOS)
|
||||||
|
.focused($focusedField, equals: .forward)
|
||||||
|
#else
|
||||||
.keyboardShortcut("l")
|
.keyboardShortcut("l")
|
||||||
|
.keyboardShortcut(.rightArrow)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.font(.system(size: 30))
|
.font(.system(size: 30))
|
||||||
.padding(.horizontal, 4)
|
.padding(.horizontal, 4)
|
||||||
@ -234,7 +287,7 @@ struct PlayerControls: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var fullScreenLayout: Bool {
|
var fullScreenLayout: Bool {
|
||||||
#if !os(macOS)
|
#if os(iOS)
|
||||||
model.playingFullscreen || verticalSizeClass == .compact
|
model.playingFullscreen || verticalSizeClass == .compact
|
||||||
#else
|
#else
|
||||||
model.playingFullscreen
|
model.playingFullscreen
|
||||||
|
@ -47,6 +47,7 @@ struct TimelineView: View {
|
|||||||
|
|
||||||
.frame(maxHeight: height * 2)
|
.frame(maxHeight: height * 2)
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
.gesture(
|
.gesture(
|
||||||
DragGesture(minimumDistance: 0)
|
DragGesture(minimumDistance: 0)
|
||||||
.onChanged { value in
|
.onChanged { value in
|
||||||
@ -79,6 +80,7 @@ struct TimelineView: View {
|
|||||||
controls.resetTimer()
|
controls.resetTimer()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
#endif
|
||||||
|
|
||||||
ZStack {
|
ZStack {
|
||||||
RoundedRectangle(cornerRadius: cornerRadius)
|
RoundedRectangle(cornerRadius: cornerRadius)
|
||||||
@ -100,11 +102,13 @@ struct TimelineView: View {
|
|||||||
self.size = size
|
self.size = size
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
#if !os(tvOS)
|
||||||
.gesture(DragGesture(minimumDistance: 0).onEnded { value in
|
.gesture(DragGesture(minimumDistance: 0).onEnded { value in
|
||||||
let target = (value.location.x / size.width) * units
|
let target = (value.location.x / size.width) * units
|
||||||
current = target
|
current = target
|
||||||
player.backend.seek(to: target)
|
player.backend.seek(to: target)
|
||||||
})
|
})
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var projectedValue: Double {
|
var projectedValue: Double {
|
||||||
|
@ -89,7 +89,7 @@ struct VideoDetails: View {
|
|||||||
if fullScreen {
|
if fullScreen {
|
||||||
fullScreen = false
|
fullScreen = false
|
||||||
} else {
|
} else {
|
||||||
self.presentationMode.wrappedValue.dismiss()
|
self.player.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,8 +93,26 @@ struct VideoPlayerView: View {
|
|||||||
Group {
|
Group {
|
||||||
Group {
|
Group {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
player.playerView
|
playerView
|
||||||
.ignoresSafeArea(.all, edges: .all)
|
.ignoresSafeArea(.all, edges: .all)
|
||||||
|
.onMoveCommand { direction in
|
||||||
|
if direction == .left {
|
||||||
|
playerControls.resetTimer()
|
||||||
|
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
|
||||||
|
}
|
||||||
|
if direction == .right {
|
||||||
|
playerControls.resetTimer()
|
||||||
|
player.backend.seek(relative: .secondsInDefaultTimescale(10))
|
||||||
|
}
|
||||||
|
if direction == .up {
|
||||||
|
playerControls.show()
|
||||||
|
playerControls.resetTimer()
|
||||||
|
}
|
||||||
|
if direction == .down {
|
||||||
|
playerControls.show()
|
||||||
|
playerControls.resetTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@ -103,30 +121,8 @@ struct VideoPlayerView: View {
|
|||||||
} else if player.playingInPictureInPicture {
|
} else if player.playingInPictureInPicture {
|
||||||
pictureInPicturePlaceholder(geometry: geometry)
|
pictureInPicturePlaceholder(geometry: geometry)
|
||||||
} else {
|
} else {
|
||||||
ZStack(alignment: .top) {
|
playerView
|
||||||
switch player.activeBackend {
|
#if !os(tvOS)
|
||||||
case .mpv:
|
|
||||||
player.mpvPlayerView
|
|
||||||
.overlay(GeometryReader { proxy in
|
|
||||||
Color.clear
|
|
||||||
.onAppear {
|
|
||||||
player.playerSize = proxy.size
|
|
||||||
// TODO: move to backend method
|
|
||||||
player.mpvBackend.client?.setSize(proxy.size.width, proxy.size.height)
|
|
||||||
}
|
|
||||||
.onChange(of: proxy.size) { _ in
|
|
||||||
player.playerSize = proxy.size
|
|
||||||
player.mpvBackend.client?.setSize(proxy.size.width, proxy.size.height)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
case .appleAVPlayer:
|
|
||||||
player.avPlayerView
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerGestures()
|
|
||||||
|
|
||||||
PlayerControls(player: player)
|
|
||||||
}
|
|
||||||
.modifier(
|
.modifier(
|
||||||
VideoPlayerSizeModifier(
|
VideoPlayerSizeModifier(
|
||||||
geometry: geometry,
|
geometry: geometry,
|
||||||
@ -134,6 +130,7 @@ struct VideoPlayerView: View {
|
|||||||
fullScreen: playerControls.playingFullscreen
|
fullScreen: playerControls.playingFullscreen
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: fullScreenLayout ? .infinity : nil, maxHeight: fullScreenLayout ? .infinity : nil)
|
.frame(maxWidth: fullScreenLayout ? .infinity : nil, maxHeight: fullScreenLayout ? .infinity : nil)
|
||||||
@ -165,6 +162,7 @@ struct VideoPlayerView: View {
|
|||||||
|
|
||||||
.background(Color.black)
|
.background(Color.black)
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
if !playerControls.playingFullscreen {
|
if !playerControls.playingFullscreen {
|
||||||
Group {
|
Group {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@ -183,6 +181,7 @@ struct VideoPlayerView: View {
|
|||||||
fullScreen: fullScreenDetails
|
fullScreen: fullScreenDetails
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -205,14 +204,40 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoresSafeArea(.all, edges: fullScreenLayout ? .vertical : Edge.Set())
|
.ignoresSafeArea(.all, edges: fullScreenLayout ? .vertical : Edge.Set())
|
||||||
#if !os(macOS)
|
#if os(iOS)
|
||||||
.statusBar(hidden: playerControls.playingFullscreen)
|
.statusBar(hidden: playerControls.playingFullscreen)
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var playerView: some View {
|
||||||
|
ZStack(alignment: .top) {
|
||||||
|
switch player.activeBackend {
|
||||||
|
case .mpv:
|
||||||
|
player.mpvPlayerView
|
||||||
|
.overlay(GeometryReader { proxy in
|
||||||
|
Color.clear
|
||||||
|
.onAppear {
|
||||||
|
player.playerSize = proxy.size
|
||||||
|
}
|
||||||
|
.onChange(of: proxy.size) { _ in
|
||||||
|
player.playerSize = proxy.size
|
||||||
|
}
|
||||||
|
})
|
||||||
|
case .appleAVPlayer:
|
||||||
|
player.avPlayerView
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
|
PlayerGestures()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PlayerControls(player: player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var fullScreenLayout: Bool {
|
var fullScreenLayout: Bool {
|
||||||
#if !os(macOS)
|
#if os(iOS)
|
||||||
playerControls.playingFullscreen || verticalSizeClass == .compact
|
playerControls.playingFullscreen || verticalSizeClass == .compact
|
||||||
#else
|
#else
|
||||||
playerControls.playingFullscreen
|
playerControls.playingFullscreen
|
||||||
|
@ -212,7 +212,6 @@
|
|||||||
373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; };
|
373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; };
|
||||||
373CFAF02697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; };
|
373CFAF02697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; };
|
||||||
373CFAF12697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; };
|
373CFAF12697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; };
|
||||||
3740457227E91A4C00DC8A64 /* StreamControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3795593527B08538007FF8F4 /* StreamControl.swift */; };
|
|
||||||
374108D1272B11B2006C5CC8 /* PictureInPictureDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374108D0272B11B2006C5CC8 /* PictureInPictureDelegate.swift */; };
|
374108D1272B11B2006C5CC8 /* PictureInPictureDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374108D0272B11B2006C5CC8 /* PictureInPictureDelegate.swift */; };
|
||||||
3741A32C27E7EFFD00D266D1 /* PlayerControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37030FFE27B04DCC00ECDDAA /* PlayerControls.swift */; };
|
3741A32C27E7EFFD00D266D1 /* PlayerControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37030FFE27B04DCC00ECDDAA /* PlayerControls.swift */; };
|
||||||
3743B86927216D3600261544 /* ChannelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743B86727216D3600261544 /* ChannelCell.swift */; };
|
3743B86927216D3600261544 /* ChannelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743B86727216D3600261544 /* ChannelCell.swift */; };
|
||||||
@ -2942,7 +2941,6 @@
|
|||||||
3743CA50270EFE3400E4D32B /* PlayerQueueRow.swift in Sources */,
|
3743CA50270EFE3400E4D32B /* PlayerQueueRow.swift in Sources */,
|
||||||
376BE50827347B57009AD608 /* SettingsHeader.swift in Sources */,
|
376BE50827347B57009AD608 /* SettingsHeader.swift in Sources */,
|
||||||
37A9966026D6F9B9006E3224 /* FavoritesView.swift in Sources */,
|
37A9966026D6F9B9006E3224 /* FavoritesView.swift in Sources */,
|
||||||
3740457227E91A4C00DC8A64 /* StreamControl.swift in Sources */,
|
|
||||||
37001565271B1F250049C794 /* AccountsModel.swift in Sources */,
|
37001565271B1F250049C794 /* AccountsModel.swift in Sources */,
|
||||||
3751B4B427836902000B7DF4 /* SearchPage.swift in Sources */,
|
3751B4B427836902000B7DF4 /* SearchPage.swift in Sources */,
|
||||||
374C0541272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
|
374C0541272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
|
||||||
|
Loading…
Reference in New Issue
Block a user