tvos begin final work

This commit is contained in:
Ethan Pippin 2022-01-02 21:43:00 -07:00
parent 5b451ceaaa
commit 47249c2edd
8 changed files with 111 additions and 132 deletions

View File

@ -12,9 +12,6 @@ import Foundation
protocol PlayerOverlayDelegate {
func didSelectClose()
func didSelectGoogleCast()
func didSelectAirplay()
func didSelectSubtitles()
func didSelectMenu()
func didDeselectMenu()
@ -30,8 +27,6 @@ protocol PlayerOverlayDelegate {
func didSelectAudioStream(index: Int)
func didSelectSubtitleStream(index: Int)
func didSelectPreviousItem()
func didSelectNextItem()
func didFocusOnButton()
func didSelectPlayPreviousItem()
func didSelectPlayNextItem()
}

View File

@ -27,7 +27,7 @@ class VLCPlayerViewController: UIViewController {
private var vlcMediaPlayer = VLCMediaPlayer()
private var lastPlayerTicks: Int64 = 0
private var lastProgressReportTicks: Int64 = 0
private var viewModelReactCancellables = Set<AnyCancellable>()
private var viewModelListeners = Set<AnyCancellable>()
private var overlayDismissTimer: Timer?
private var currentPlayerTicks: Int64 {
@ -42,14 +42,6 @@ class VLCPlayerViewController: UIViewController {
return currentOverlayContentHostingController?.view.alpha ?? 0 > 0
}
private var jumpForwardLength: VideoPlayerJumpLength {
return Defaults[.videoPlayerJumpForward]
}
private var jumpBackwardLength: VideoPlayerJumpLength {
return Defaults[.videoPlayerJumpBackward]
}
private lazy var videoContentView = makeVideoContentView()
private lazy var jumpBackwardOverlayView = makeJumpBackwardOverlayView()
private lazy var jumpForwardOverlayView = makeJumpForwardOverlayView()
@ -170,7 +162,7 @@ class VLCPlayerViewController: UIViewController {
private func makeJumpBackwardOverlayView() -> UIImageView {
let symbolConfig = UIImage.SymbolConfiguration(pointSize: 72)
let forwardSymbolImage = UIImage(systemName: jumpBackwardLength.backwardImageLabel, withConfiguration: symbolConfig)
let forwardSymbolImage = UIImage(systemName: viewModel.jumpBackwardLength.backwardImageLabel, withConfiguration: symbolConfig)
let imageView = UIImageView(image: forwardSymbolImage)
imageView.translatesAutoresizingMaskIntoConstraints = false
@ -179,7 +171,7 @@ class VLCPlayerViewController: UIViewController {
private func makeJumpForwardOverlayView() -> UIImageView {
let symbolConfig = UIImage.SymbolConfiguration(pointSize: 72)
let forwardSymbolImage = UIImage(systemName: jumpForwardLength.forwardImageLabel, withConfiguration: symbolConfig)
let forwardSymbolImage = UIImage(systemName: viewModel.jumpForwardLength.forwardImageLabel, withConfiguration: symbolConfig)
let imageView = UIImageView(image: forwardSymbolImage)
imageView.translatesAutoresizingMaskIntoConstraints = false
@ -262,7 +254,6 @@ class VLCPlayerViewController: UIViewController {
currentOverlayHostingController.view.removeFromSuperview()
currentOverlayHostingController.removeFromParent()
// self.currentOverlayHostingController = nil
}
}
@ -346,7 +337,7 @@ extension VLCPlayerViewController {
// Stop current media if there is one
if vlcMediaPlayer.media != nil {
viewModelReactCancellables.forEach({ $0.cancel() })
viewModelListeners.forEach({ $0.cancel() })
vlcMediaPlayer.stop()
viewModel.sendStopReport()
@ -368,7 +359,7 @@ extension VLCPlayerViewController {
newViewModel.getAdjacentEpisodes()
newViewModel.playerOverlayDelegate = self
let startPercentage = viewModel.item.userData?.playedPercentage ?? 0
let startPercentage = newViewModel.item.userData?.playedPercentage ?? 0
if startPercentage > 0 {
newViewModel.sliderPercentage = startPercentage / 100
@ -393,7 +384,7 @@ extension VLCPlayerViewController {
private func setupViewModelListeners(viewModel: VideoPlayerViewModel) {
viewModel.$playbackSpeed.sink { newSpeed in
self.vlcMediaPlayer.rate = Float(newSpeed.rawValue)
}.store(in: &viewModelReactCancellables)
}.store(in: &viewModelListeners)
viewModel.$sliderIsScrubbing.sink { sliderIsScrubbing in
if sliderIsScrubbing {
@ -401,15 +392,19 @@ extension VLCPlayerViewController {
} else {
self.didEndScrubbing()
}
}.store(in: &viewModelReactCancellables)
}.store(in: &viewModelListeners)
viewModel.$selectedAudioStreamIndex.sink { newAudioStreamIndex in
self.didSelectAudioStream(index: newAudioStreamIndex)
}.store(in: &viewModelReactCancellables)
}.store(in: &viewModelListeners)
viewModel.$selectedSubtitleStreamIndex.sink { newSubtitleStreamIndex in
self.didSelectSubtitleStream(index: newSubtitleStreamIndex)
}.store(in: &viewModelReactCancellables)
}.store(in: &viewModelListeners)
viewModel.$subtitlesEnabled.sink { newSubtitlesEnabled in
self.didToggleSubtitles(newValue: newSubtitlesEnabled)
}.store(in: &viewModelListeners)
}
func setMediaPlayerTimeAtCurrentSlider() {
@ -554,8 +549,8 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
viewModel.playerState = vlcMediaPlayer.state
if vlcMediaPlayer.state == VLCMediaPlayerState.ended {
if viewModel.autoPlayNextItem && viewModel.shouldShowAutoPlayNextItem && viewModel.nextItemVideoPlayerViewModel != nil {
didSelectNextItem()
if viewModel.autoplayEnabled && viewModel.nextItemVideoPlayerViewModel != nil {
didSelectPlayNextItem()
} else {
didSelectClose()
}
@ -565,9 +560,8 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
// MARK: mediaPlayerTimeChanged
func mediaPlayerTimeChanged(_ aNotification: Notification!) {
guard !viewModel.sliderIsScrubbing else {
lastPlayerTicks = currentPlayerTicks
return
if !viewModel.sliderIsScrubbing {
viewModel.sliderPercentage = Double(vlcMediaPlayer.position)
}
viewModel.sliderPercentage = Double(vlcMediaPlayer.position)
@ -581,6 +575,9 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
// If needing to fix subtitle streams during playback
if vlcMediaPlayer.currentVideoSubTitleIndex != viewModel.selectedSubtitleStreamIndex && viewModel.subtitlesEnabled {
didSelectSubtitleStream(index: viewModel.selectedSubtitleStreamIndex)
}
if vlcMediaPlayer.currentAudioTrackIndex != viewModel.selectedAudioStreamIndex {
didSelectAudioStream(index: viewModel.selectedAudioStreamIndex)
}
@ -606,12 +603,11 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
lastProgressReportTicks = currentPlayerTicks
}
/// Do not call when setting to index -1
func didSelectSubtitleStream(index: Int) {
if viewModel.subtitlesEnabled {
vlcMediaPlayer.currentVideoSubTitleIndex = Int32(index)
} else {
vlcMediaPlayer.currentVideoSubTitleIndex = -1
}
viewModel.subtitlesEnabled = true
vlcMediaPlayer.currentVideoSubTitleIndex = Int32(index)
viewModel.sendProgressReport()
@ -626,19 +622,8 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
dismiss(animated: true, completion: nil)
}
func didSelectGoogleCast() {
print("didSelectCast")
}
func didSelectAirplay() {
print("didSelectAirplay")
}
func didSelectSubtitles() {
viewModel.subtitlesEnabled = !viewModel.subtitlesEnabled
if viewModel.subtitlesEnabled {
func didToggleSubtitles(newValue: Bool) {
if newValue {
vlcMediaPlayer.currentVideoSubTitleIndex = Int32(viewModel.selectedSubtitleStreamIndex)
} else {
vlcMediaPlayer.currentVideoSubTitleIndex = -1
@ -648,38 +633,41 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
// TODO: Implement properly in overlays
func didSelectMenu() {
stopOverlayDismissTimer()
hideOverlay()
showOverlayContent()
}
// TODO: Implement properly in overlays
func didDeselectMenu() {
restartOverlayDismissTimer()
}
func didSelectBackward() {
flashJumpBackwardOverlay()
vlcMediaPlayer.jumpBackward(jumpBackwardLength.rawValue)
vlcMediaPlayer.jumpBackward(viewModel.jumpBackwardLength.rawValue)
restartOverlayDismissTimer()
if displayingOverlay {
restartOverlayDismissTimer()
}
viewModel.sendProgressReport()
self.lastProgressReportTicks = currentPlayerTicks
lastProgressReportTicks = currentPlayerTicks
}
func didSelectForward() {
flashJumpFowardOverlay()
vlcMediaPlayer.jumpForward(jumpForwardLength.rawValue)
vlcMediaPlayer.jumpForward(viewModel.jumpForwardLength.rawValue)
restartOverlayDismissTimer()
if displayingOverlay {
restartOverlayDismissTimer()
}
viewModel.sendProgressReport()
self.lastProgressReportTicks = currentPlayerTicks
lastProgressReportTicks = currentPlayerTicks
}
func didSelectMain() {
@ -691,8 +679,7 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
case .playing:
viewModel.sendPauseReport(paused: true)
vlcMediaPlayer.pause()
showOverlay()
restartOverlayDismissTimer(interval: 10)
restartOverlayDismissTimer(interval: 5)
case .paused:
viewModel.sendPauseReport(paused: false)
vlcMediaPlayer.play()
@ -718,20 +705,20 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
viewModel.sendProgressReport()
self.lastProgressReportTicks = currentPlayerTicks
lastProgressReportTicks = currentPlayerTicks
}
func didSelectPreviousItem() {
setupMediaPlayer(newViewModel: viewModel.previousItemVideoPlayerViewModel!)
startPlayback()
func didSelectPlayPreviousItem() {
if let previousItemVideoPlayerViewModel = viewModel.previousItemVideoPlayerViewModel {
setupMediaPlayer(newViewModel: previousItemVideoPlayerViewModel)
startPlayback()
}
}
func didSelectNextItem() {
setupMediaPlayer(newViewModel: viewModel.nextItemVideoPlayerViewModel!)
startPlayback()
}
func didFocusOnButton() {
restartOverlayDismissTimer(interval: 8)
func didSelectPlayNextItem() {
if let nextItemVideoPlayerViewModel = viewModel.nextItemVideoPlayerViewModel {
setupMediaPlayer(newViewModel: nextItemVideoPlayerViewModel)
startPlayback()
}
}
}

View File

@ -12,15 +12,16 @@ import SwiftUI
struct SmallMediaStreamSelectionView: View {
@State var selectedItem: MediaStream?
@Binding var selectedItem: MediaStream?
private let title: String
private var items: [MediaStream]
private var selectedAction: (MediaStream) -> Void
init(items: [MediaStream], selectedItem: MediaStream? = nil, selectedAction: @escaping (MediaStream) -> Void) {
self.items = items
self.selectedItem = selectedItem
self.selectedAction = selectedAction
}
// init(items: [MediaStream], selectedItem: MediaStream?, selectedAction: @escaping (MediaStream) -> Void) {
// self.items = items
// self.selectedItem = selectedItem
// self.selectedAction = selectedAction
// }
var body: some View {
ZStack(alignment: .bottom) {
@ -35,7 +36,7 @@ struct SmallMediaStreamSelectionView: View {
Spacer()
HStack {
Text("Subtitles")
Text(title)
.font(.title3)
Spacer()
}
@ -44,7 +45,7 @@ struct SmallMediaStreamSelectionView: View {
HStack {
ForEach(items, id: \.self) { item in
Button {
// self.selectedItem = item
self.selectedAction(item)
} label: {
if item == selectedItem {
Label(item.displayTitle ?? "No Title", systemImage: "checkmark")

View File

@ -59,31 +59,30 @@ struct tvOSOverlayContentView: View {
}
struct tvOSOverlayContentView_Previews: PreviewProvider {
static let videoPlayerViewModel = VideoPlayerViewModel(item: BaseItemDto(),
title: "Glorious Purpose",
subtitle: "Loki - S1E1",
streamURL: URL(string: "www.apple.com")!,
hlsURL: URL(string: "www.apple.com")!,
response: PlaybackInfoResponse(),
audioStreams: [MediaStream(displayTitle: "English", index: -1)],
subtitleStreams: [MediaStream(displayTitle: "None", index: -1)],
selectedAudioStreamIndex: -1,
selectedSubtitleStreamIndex: -1,
subtitlesEnabled: true,
autoplayEnabled: false,
overlayType: .compact,
shouldShowPlayPreviousItem: true,
shouldShowPlayNextItem: true,
shouldShowAutoPlayNextItem: true)
static var previews: some View {
ZStack {
Color.red
.ignoresSafeArea()
tvOSOverlayContentView(viewModel: VideoPlayerViewModel(item: BaseItemDto(runTimeTicks: 720 * 10_000_000),
title: "Glorious Purpose",
subtitle: "Loki - S1E1",
streamURL: URL(string: "www.apple.com")!,
hlsURL: URL(string: "www.apple.com")!,
response: PlaybackInfoResponse(),
audioStreams: [MediaStream(displayTitle: "English", index: -1)],
subtitleStreams: [MediaStream(displayTitle: "None", index: -1)],
defaultAudioStreamIndex: -1,
defaultSubtitleStreamIndex: -1,
playerState: .error,
shouldShowGoogleCast: false,
shouldShowAirplay: false,
subtitlesEnabled: true,
sliderPercentage: 0.432,
selectedAudioStreamIndex: -1,
selectedSubtitleStreamIndex: -1,
showAdjacentItems: true,
shouldShowAutoPlayNextItem: true,
autoPlayNextItem: true))
tvOSOverlayContentView(viewModel: videoPlayerViewModel)
}
}
}

View File

@ -55,16 +55,19 @@ struct tvOSVLCOverlay: View {
Spacer()
if viewModel.showAdjacentItems {
if viewModel.shouldShowPlayPreviousItem {
SFSymbolButton(systemName: "chevron.left.circle", action: {
viewModel.playerOverlayDelegate?.didSelectPreviousItem()
viewModel.playerOverlayDelegate?.didSelectPlayPreviousItem()
})
.frame(maxWidth: 30, maxHeight: 30)
.disabled(viewModel.previousItemVideoPlayerViewModel == nil)
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
}
if viewModel.shouldShowPlayNextItem {
SFSymbolButton(systemName: "chevron.right.circle", action: {
viewModel.playerOverlayDelegate?.didSelectNextItem()
viewModel.playerOverlayDelegate?.didSelectPlayNextItem()
})
.frame(maxWidth: 30, maxHeight: 30)
.disabled(viewModel.nextItemVideoPlayerViewModel == nil)
@ -74,12 +77,12 @@ struct tvOSVLCOverlay: View {
if !viewModel.subtitleStreams.isEmpty {
if viewModel.subtitlesEnabled {
SFSymbolButton(systemName: "captions.bubble.fill") {
viewModel.playerOverlayDelegate?.didSelectSubtitles()
viewModel.subtitlesEnabled.toggle()
}
.frame(maxWidth: 30, maxHeight: 30)
} else {
SFSymbolButton(systemName: "captions.bubble") {
viewModel.playerOverlayDelegate?.didSelectSubtitles()
viewModel.subtitlesEnabled.toggle()
}
.frame(maxWidth: 30, maxHeight: 30)
}
@ -121,32 +124,30 @@ struct tvOSVLCOverlay: View {
}
struct tvOSVLCOverlay_Previews: PreviewProvider {
static let videoPlayerViewModel = VideoPlayerViewModel(item: BaseItemDto(),
title: "Glorious Purpose",
subtitle: "Loki - S1E1",
streamURL: URL(string: "www.apple.com")!,
hlsURL: URL(string: "www.apple.com")!,
response: PlaybackInfoResponse(),
audioStreams: [MediaStream(displayTitle: "English", index: -1)],
subtitleStreams: [MediaStream(displayTitle: "None", index: -1)],
selectedAudioStreamIndex: -1,
selectedSubtitleStreamIndex: -1,
subtitlesEnabled: true,
autoplayEnabled: false,
overlayType: .compact,
shouldShowPlayPreviousItem: true,
shouldShowPlayNextItem: true,
shouldShowAutoPlayNextItem: true)
static var previews: some View {
ZStack {
Color.red
.ignoresSafeArea()
tvOSVLCOverlay(viewModel: VideoPlayerViewModel(item: BaseItemDto(runTimeTicks: 720 * 10_000_000),
title: "Glorious Purpose",
subtitle: "Loki - S1E1",
streamURL: URL(string: "www.apple.com")!,
hlsURL: URL(string: "www.apple.com")!,
response: PlaybackInfoResponse(),
audioStreams: [MediaStream(displayTitle: "English", index: -1)],
subtitleStreams: [MediaStream(displayTitle: "None", index: -1)],
defaultAudioStreamIndex: -1,
defaultSubtitleStreamIndex: -1,
playerState: .error,
shouldShowGoogleCast: false,
shouldShowAirplay: false,
subtitlesEnabled: true,
sliderPercentage: 0.432,
selectedAudioStreamIndex: -1,
selectedSubtitleStreamIndex: -1,
showAdjacentItems: true,
shouldShowAutoPlayNextItem: true,
autoPlayNextItem: true))
tvOSVLCOverlay(viewModel: videoPlayerViewModel)
}
.previewInterfaceOrientation(.landscapeLeft)
}
}

View File

@ -95,7 +95,7 @@ struct VLCPlayerOverlayView: View {
if viewModel.autoplayEnabled {
Image(systemName: "play.circle.fill")
} else {
Image(systemName: "play.circle")
Image(systemName: "stop.circle")
}
}
}

View File

@ -266,7 +266,7 @@ class VLCPlayerViewController: UIViewController {
view.addSubview(newJumpForwardImageView)
NSLayoutConstraint.activate([
newJumpForwardImageView.leftAnchor.constraint(equalTo: view.rightAnchor, constant: -150),
newJumpForwardImageView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -150),
newJumpForwardImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])

View File

@ -19,7 +19,6 @@ final class VideoPlayerCoordinator: NavigationCoordinatable {
@Root var start = makeStart
@Default(.nativeVideoPlayer) var nativeVideoPlayer
let viewModel: VideoPlayerViewModel
init(viewModel: VideoPlayerViewModel) {
@ -27,9 +26,6 @@ final class VideoPlayerCoordinator: NavigationCoordinatable {
}
@ViewBuilder func makeStart() -> some View {
// NativePlayerView(viewModel: viewModel)
// .navigationBarHidden(true)
// .ignoresSafeArea()
VLCPlayerView(viewModel: viewModel)
.navigationBarHidden(true)
.ignoresSafeArea()