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