// // Swiftfin is subject to the terms of the Mozilla Public // License, v2.0. If a copy of the MPL was not distributed with this // file, you can obtain one at https://mozilla.org/MPL/2.0/. // // Copyright (c) 2025 Jellyfin & Jellyfin Contributors // import AVKit import Factory import JellyfinAPI import Logging import SwiftUI // TODO: remove struct NativeVideoPlayer: View { @Environment(\.presentationCoordinator) private var presentationCoordinator @InjectedObject(\.mediaPlayerManager) private var manager: MediaPlayerManager @LazyState private var proxy: AVMediaPlayerProxy @Router private var router init() { self._proxy = .init(wrappedValue: AVMediaPlayerProxy()) } var body: some View { ZStack { Color.black switch manager.state { case .playback: NativeVideoPlayerView(proxy: proxy) default: ProgressView() } } .onAppear { manager.proxy = proxy manager.start() } .preference(key: IsStatusBarHiddenKey.self, value: true) .backport .onChange(of: presentationCoordinator.isPresented) { _, isPresented in Container.shared.mediaPlayerManager.reset() guard !isPresented else { return } manager.stop() } .alert( L10n.error, isPresented: .constant(manager.error != nil) ) { Button(L10n.close, role: .cancel) { Container.shared.mediaPlayerManager.reset() router.dismiss() } } message: { // TODO: localize Text("Unable to load this item.") } } } extension NativeVideoPlayer { private struct NativeVideoPlayerView: UIViewControllerRepresentable { let proxy: AVMediaPlayerProxy func makeUIViewController(context: Context) -> UINativeVideoPlayerViewController { UINativeVideoPlayerViewController(proxy: proxy) } func updateUIViewController(_ uiViewController: UINativeVideoPlayerViewController, context: Context) {} } private class UINativeVideoPlayerViewController: AVPlayerViewController { private let proxy: AVMediaPlayerProxy init(proxy: AVMediaPlayerProxy) { self.proxy = proxy super.init(nibName: nil, bundle: nil) player = proxy.player player?.allowsExternalPlayback = true player?.appliesMediaSelectionCriteriaAutomatically = false allowsPictureInPicturePlayback = true #if !os(tvOS) updatesNowPlayingInfoCenter = false #endif } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } }