mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
AVPlayer background music mode
This commit is contained in:
parent
b5f8a0fba2
commit
48e616b301
@ -588,6 +588,42 @@ final class AVPlayerBackend: PlayerBackend {
|
||||
controlsUpdates = false
|
||||
}
|
||||
|
||||
func startMusicMode() {
|
||||
if model.playingInPictureInPicture {
|
||||
closePiP()
|
||||
}
|
||||
|
||||
playerLayer.player = nil
|
||||
|
||||
toggleVisualTracksEnabled(false)
|
||||
}
|
||||
|
||||
func stopMusicMode() {
|
||||
playerLayer.player = avPlayer
|
||||
|
||||
toggleVisualTracksEnabled(true)
|
||||
}
|
||||
|
||||
func toggleVisualTracksEnabled(_ value: Bool) {
|
||||
if let item = avPlayer.currentItem {
|
||||
for playerItemTrack in item.tracks {
|
||||
if let assetTrack = playerItemTrack.assetTrack,
|
||||
assetTrack.hasMediaCharacteristic(AVMediaCharacteristic.visual)
|
||||
{
|
||||
playerItemTrack.isEnabled = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func didChangeTo() {
|
||||
if model.musicMode {
|
||||
startMusicMode()
|
||||
} else {
|
||||
stopMusicMode()
|
||||
}
|
||||
}
|
||||
|
||||
func setNeedsDrawing(_: Bool) {}
|
||||
func setSize(_: Double, _: Double) {}
|
||||
func setNeedsNetworkStateUpdates(_: Bool) {}
|
||||
|
@ -512,4 +512,36 @@ final class MPVBackend: PlayerBackend {
|
||||
networkStateTimer.pause()
|
||||
}
|
||||
}
|
||||
|
||||
func startMusicMode() {
|
||||
setVideoToNo()
|
||||
}
|
||||
|
||||
func stopMusicMode() {
|
||||
addVideoTrackFromStream()
|
||||
setVideoToAuto()
|
||||
|
||||
controls.resetTimer()
|
||||
}
|
||||
|
||||
func addVideoTrackFromStream() {
|
||||
if let videoTrackURL = model.stream?.videoAsset?.url,
|
||||
tracks < 2
|
||||
{
|
||||
logger.info("adding video track")
|
||||
addVideoTrack(videoTrackURL)
|
||||
}
|
||||
|
||||
setVideoToAuto()
|
||||
}
|
||||
|
||||
func didChangeTo() {
|
||||
setNeedsDrawing(model.presentingPlayer)
|
||||
|
||||
if model.musicMode {
|
||||
startMusicMode()
|
||||
} else {
|
||||
stopMusicMode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,10 +47,15 @@ protocol PlayerBackend {
|
||||
|
||||
func closePiP()
|
||||
|
||||
func startMusicMode()
|
||||
func stopMusicMode()
|
||||
|
||||
func updateControls()
|
||||
func startControlsUpdates()
|
||||
func stopControlsUpdates()
|
||||
|
||||
func didChangeTo()
|
||||
|
||||
func setNeedsNetworkStateUpdates(_ needsUpdates: Bool)
|
||||
|
||||
func setNeedsDrawing(_ needsDrawing: Bool)
|
||||
|
@ -38,14 +38,18 @@ final class PlayerControlsModel: ObservableObject {
|
||||
|
||||
func handlePresentationChange() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
guard let self = self,
|
||||
let player = self.player else { return }
|
||||
if self.presentingControls {
|
||||
self.player?.backend.startControlsUpdates()
|
||||
player.backend.startControlsUpdates()
|
||||
self.resetTimer()
|
||||
} else {
|
||||
self.player?.backend.stopControlsUpdates()
|
||||
self.timer?.invalidate()
|
||||
self.timer = nil
|
||||
if !player.musicMode {
|
||||
player.backend.stopControlsUpdates()
|
||||
self.removeTimer()
|
||||
} else {
|
||||
self.presentingControls = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,6 +136,8 @@ final class PlayerControlsModel: ObservableObject {
|
||||
}
|
||||
|
||||
func startPiP(startImmediately: Bool = true) {
|
||||
player?.avPlayerBackend.startPictureInPictureOnPlay = true
|
||||
|
||||
#if !os(macOS)
|
||||
player.exitFullScreen()
|
||||
#endif
|
||||
@ -143,7 +149,6 @@ final class PlayerControlsModel: ObservableObject {
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak player] in
|
||||
player?.avPlayerBackend.startPictureInPictureOnPlay = true
|
||||
if startImmediately {
|
||||
player?.pipController?.startPictureInPicture()
|
||||
}
|
||||
|
@ -226,9 +226,11 @@ final class PlayerModel: ObservableObject {
|
||||
|
||||
navigation.hideKeyboard()
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
withAnimation(.linear(duration: 0.25)) {
|
||||
self?.presentingPlayer = true
|
||||
if !presentingPlayer {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
withAnimation(.linear(duration: 0.25)) {
|
||||
self?.presentingPlayer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,14 +342,30 @@ final class PlayerModel: ObservableObject {
|
||||
func play(_ video: Video, at time: CMTime? = nil, showingPlayer: Bool = true) {
|
||||
pause()
|
||||
|
||||
var changeBackendHandler: (() -> Void)?
|
||||
|
||||
if let backend = qualityProfile?.backend ?? QualityProfilesModel.shared.automaticProfile?.backend,
|
||||
activeBackend != backend,
|
||||
backend == .appleAVPlayer || !avPlayerBackend.startPictureInPictureOnPlay
|
||||
{
|
||||
changeBackendHandler = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.changeActiveBackend(from: self.activeBackend, to: backend)
|
||||
}
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
if !playingInPictureInPicture, showingPlayer {
|
||||
onPresentPlayer = { [weak self] in self?.playNow(video, at: time) }
|
||||
onPresentPlayer = { [weak self] in
|
||||
changeBackendHandler?()
|
||||
self?.playNow(video, at: time)
|
||||
}
|
||||
show()
|
||||
return
|
||||
}
|
||||
#endif
|
||||
|
||||
changeBackendHandler?()
|
||||
playNow(video, at: time)
|
||||
|
||||
guard !playingInPictureInPicture else {
|
||||
@ -493,16 +511,12 @@ final class PlayerModel: ObservableObject {
|
||||
Defaults[.activeBackend] = to
|
||||
self.activeBackend = to
|
||||
|
||||
self.backend.didChangeTo()
|
||||
|
||||
guard var stream = stream else {
|
||||
return
|
||||
}
|
||||
|
||||
if to == .mpv {
|
||||
addVideoTrackFromStream()
|
||||
} else {
|
||||
musicMode = false
|
||||
}
|
||||
|
||||
let fromBackend: PlayerBackend = from == .appleAVPlayer ? avPlayerBackend : mpvBackend
|
||||
let toBackend: PlayerBackend = to == .appleAVPlayer ? avPlayerBackend : mpvBackend
|
||||
|
||||
@ -534,7 +548,6 @@ final class PlayerModel: ObservableObject {
|
||||
return
|
||||
}
|
||||
self.upgradeToStream(stream, force: true)
|
||||
self.setNeedsDrawing(self.presentingPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -756,6 +769,7 @@ final class PlayerModel: ObservableObject {
|
||||
#else
|
||||
func handleEnterForeground() {
|
||||
setNeedsDrawing(presentingPlayer)
|
||||
avPlayerBackend.playerLayer.player = avPlayerBackend.avPlayer
|
||||
|
||||
guard closePiPAndOpenPlayerOnEnteringForeground, playingInPictureInPicture else {
|
||||
return
|
||||
@ -766,10 +780,10 @@ final class PlayerModel: ObservableObject {
|
||||
}
|
||||
|
||||
func handleEnterBackground() {
|
||||
setNeedsDrawing(false)
|
||||
|
||||
if Defaults[.pauseOnEnteringBackground], !playingInPictureInPicture, !musicMode {
|
||||
pause()
|
||||
} else if !playingInPictureInPicture {
|
||||
avPlayerBackend.playerLayer.player = nil
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -872,34 +886,17 @@ final class PlayerModel: ObservableObject {
|
||||
musicMode.toggle()
|
||||
|
||||
if musicMode {
|
||||
if playingInPictureInPicture {
|
||||
avPlayerBackend.pause()
|
||||
closePiP()
|
||||
}
|
||||
changeActiveBackend(from: .appleAVPlayer, to: .mpv)
|
||||
controls.presentingControls = true
|
||||
controls.removeTimer()
|
||||
mpvBackend.setVideoToNo()
|
||||
|
||||
backend.startMusicMode()
|
||||
} else {
|
||||
addVideoTrackFromStream()
|
||||
mpvBackend.setVideoToAuto()
|
||||
backend.stopMusicMode()
|
||||
|
||||
controls.resetTimer()
|
||||
}
|
||||
}
|
||||
|
||||
func addVideoTrackFromStream() {
|
||||
if let videoTrackURL = stream?.videoAsset?.url,
|
||||
mpvBackend.tracks < 2
|
||||
{
|
||||
logger.info("adding video track")
|
||||
|
||||
mpvBackend.addVideoTrack(videoTrackURL)
|
||||
}
|
||||
|
||||
mpvBackend.setVideoToAuto()
|
||||
}
|
||||
|
||||
func updateAspectRatio() {
|
||||
#if !os(tvOS)
|
||||
guard aspectRatio != backend.aspectRatio else { return }
|
||||
|
@ -90,7 +90,7 @@ struct VideoPlayerView: View {
|
||||
Spacer()
|
||||
#endif
|
||||
}
|
||||
#if os(macOS)
|
||||
#if !os(tvOS)
|
||||
.frame(width: player.playerSize.width)
|
||||
#endif
|
||||
|
||||
@ -103,6 +103,11 @@ struct VideoPlayerView: View {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if player.musicMode {
|
||||
player.backend.startControlsUpdates()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var videoPlayer: some View {
|
||||
@ -435,10 +440,14 @@ struct VideoPlayerView: View {
|
||||
guard player.presentingPlayer,
|
||||
!playerControls.presentingControlsOverlay else { return }
|
||||
|
||||
if playerControls.presentingControls {
|
||||
if playerControls.presentingControls, !player.musicMode {
|
||||
playerControls.presentingControls = false
|
||||
}
|
||||
|
||||
if player.musicMode {
|
||||
player.backend.stopControlsUpdates()
|
||||
}
|
||||
|
||||
let drag = value.translation.height
|
||||
|
||||
guard drag > 0 else { return }
|
||||
@ -479,6 +488,10 @@ struct VideoPlayerView: View {
|
||||
}
|
||||
player.backend.setNeedsDrawing(true)
|
||||
player.show()
|
||||
|
||||
if player.musicMode {
|
||||
player.backend.startControlsUpdates()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user