mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-28 22:08:47 +02:00
fix: seek pauses playback
This commit is contained in:
parent
40b2ff5a97
commit
cbd74d08e1
6 changed files with 49 additions and 47 deletions
|
@ -783,8 +783,6 @@ export default {
|
||||||
|
|
||||||
console.log('received metadata update', data)
|
console.log('received metadata update', data)
|
||||||
|
|
||||||
if (data.currentRate && data.currentRate > 0) this.playbackSpeed = data.currentRate
|
|
||||||
|
|
||||||
this.timeupdate()
|
this.timeupdate()
|
||||||
},
|
},
|
||||||
// When a playback session is started the native android/ios will send the session
|
// When a playback session is started the native android/ios will send the session
|
||||||
|
|
|
@ -159,7 +159,7 @@ public class AbsAudioPlayer: CAPPlugin {
|
||||||
|
|
||||||
@objc func sendMetadata() {
|
@objc func sendMetadata() {
|
||||||
self.notifyListeners("onPlayingUpdate", data: [ "value": !PlayerHandler.paused ])
|
self.notifyListeners("onPlayingUpdate", data: [ "value": !PlayerHandler.paused ])
|
||||||
if let metadata = PlayerHandler.getMetdata() {
|
if let metadata = try? PlayerHandler.getMetdata()?.asDictionary() {
|
||||||
self.notifyListeners("onMetadata", data: metadata)
|
self.notifyListeners("onMetadata", data: metadata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class PlaybackMetadata: Codable {
|
struct PlaybackMetadata: Codable {
|
||||||
var duration: Double = 0
|
let duration: Double
|
||||||
var currentTime: Double = 0
|
let currentTime: Double
|
||||||
var playerState: PlayerState = PlayerState.IDLE
|
let playerState: PlayerState
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum PlayerState: Codable {
|
enum PlayerState: String, Codable {
|
||||||
case IDLE
|
case idle = "IDLE"
|
||||||
case BUFFERING
|
case buffering = "BUFFERING"
|
||||||
case READY
|
case ready = "READY"
|
||||||
case ENDED
|
case ended = "ENDED"
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,20 +10,25 @@ import AVFoundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import MediaPlayer
|
import MediaPlayer
|
||||||
|
|
||||||
enum PlayMethod:Int {
|
enum PlayMethod: Int {
|
||||||
case directplay = 0
|
case directplay = 0
|
||||||
case directstream = 1
|
case directstream = 1
|
||||||
case transcode = 2
|
case transcode = 2
|
||||||
case local = 3
|
case local = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PlayerStatus: Int {
|
||||||
|
case uninitialized = -1
|
||||||
|
case paused = 0
|
||||||
|
case playing = 1
|
||||||
|
}
|
||||||
|
|
||||||
class AudioPlayer: NSObject {
|
class AudioPlayer: NSObject {
|
||||||
internal let queue = DispatchQueue(label: "ABSAudioPlayerQueue")
|
internal let queue = DispatchQueue(label: "ABSAudioPlayerQueue")
|
||||||
internal let logger = AppLogger(category: "AudioPlayer")
|
internal let logger = AppLogger(category: "AudioPlayer")
|
||||||
|
|
||||||
// enums and @objc are not compatible
|
private var status: PlayerStatus
|
||||||
@objc dynamic var status: Int
|
internal var rate: Float
|
||||||
@objc dynamic var rate: Float
|
|
||||||
|
|
||||||
private var tmpRate: Float = 1.0
|
private var tmpRate: Float = 1.0
|
||||||
|
|
||||||
|
@ -59,7 +64,7 @@ class AudioPlayer: NSObject {
|
||||||
self.audioPlayer = AVQueuePlayer()
|
self.audioPlayer = AVQueuePlayer()
|
||||||
self.audioPlayer.automaticallyWaitsToMinimizeStalling = false
|
self.audioPlayer.automaticallyWaitsToMinimizeStalling = false
|
||||||
self.sessionId = sessionId
|
self.sessionId = sessionId
|
||||||
self.status = -1
|
self.status = .uninitialized
|
||||||
self.rate = 0.0
|
self.rate = 0.0
|
||||||
self.tmpRate = playbackRate
|
self.tmpRate = playbackRate
|
||||||
|
|
||||||
|
@ -141,7 +146,7 @@ class AudioPlayer: NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isInitialized() -> Bool {
|
public func isInitialized() -> Bool {
|
||||||
return self.status != -1
|
return self.status != .uninitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getPlaybackSession() -> PlaybackSession? {
|
public func getPlaybackSession() -> PlaybackSession? {
|
||||||
|
@ -254,7 +259,7 @@ class AudioPlayer: NSObject {
|
||||||
logger.log("queueStatusObserver: Current Item Ready to play. PlayWhenReady: \(self.playWhenReady)")
|
logger.log("queueStatusObserver: Current Item Ready to play. PlayWhenReady: \(self.playWhenReady)")
|
||||||
|
|
||||||
// Seek the player before initializing, so a currentTime of 0 does not appear in MediaProgress / session
|
// Seek the player before initializing, so a currentTime of 0 does not appear in MediaProgress / session
|
||||||
let firstReady = self.status < 0
|
let firstReady = self.status == .uninitialized
|
||||||
if firstReady && !self.playWhenReady {
|
if firstReady && !self.playWhenReady {
|
||||||
// Seek is async, and if we call this when also pressing play, we will get weird jumps in the scrub bar depending on timing
|
// Seek is async, and if we call this when also pressing play, we will get weird jumps in the scrub bar depending on timing
|
||||||
// Seeking to the correct position happens during play()
|
// Seeking to the correct position happens during play()
|
||||||
|
@ -267,7 +272,7 @@ class AudioPlayer: NSObject {
|
||||||
self.play(allowSeekBack: false, isInitializing: true)
|
self.play(allowSeekBack: false, isInitializing: true)
|
||||||
} else {
|
} else {
|
||||||
// Mark the player as ready
|
// Mark the player as ready
|
||||||
self.status = 0
|
self.status = .paused
|
||||||
}
|
}
|
||||||
} else if (playerItem.status == .failed) {
|
} else if (playerItem.status == .failed) {
|
||||||
logger.error("queueStatusObserver: FAILED \(playerItem.error?.localizedDescription ?? "")")
|
logger.error("queueStatusObserver: FAILED \(playerItem.error?.localizedDescription ?? "")")
|
||||||
|
@ -327,7 +332,7 @@ class AudioPlayer: NSObject {
|
||||||
self.audioPlayer.play()
|
self.audioPlayer.play()
|
||||||
self.audioPlayer.rate = self.tmpRate
|
self.audioPlayer.rate = self.tmpRate
|
||||||
}
|
}
|
||||||
self.status = 1
|
self.status = .playing
|
||||||
|
|
||||||
// Update the progress
|
// Update the progress
|
||||||
self.updateNowPlaying()
|
self.updateNowPlaying()
|
||||||
|
@ -351,19 +356,15 @@ class AudioPlayer: NSObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.status = 0
|
|
||||||
|
|
||||||
|
self.status = .paused
|
||||||
updateNowPlaying()
|
updateNowPlaying()
|
||||||
|
|
||||||
self.startPausedTimer()
|
self.startPausedTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func seek(_ to: Double, from: String) {
|
public func seek(_ to: Double, from: String) {
|
||||||
let continuePlaying = rate > 0.0
|
logger.log("SEEK: Seek to \(to) from \(from)")
|
||||||
|
|
||||||
self.pause()
|
|
||||||
|
|
||||||
logger.log("SEEK: Seek to \(to) from \(from) and continuePlaying(\(continuePlaying)")
|
|
||||||
|
|
||||||
guard let playbackSession = self.getPlaybackSession() else { return }
|
guard let playbackSession = self.getPlaybackSession() else { return }
|
||||||
|
|
||||||
|
@ -383,8 +384,6 @@ class AudioPlayer: NSObject {
|
||||||
playbackSession.currentTime = to
|
playbackSession.currentTime = to
|
||||||
}
|
}
|
||||||
|
|
||||||
self.playWhenReady = continuePlaying // Only playWhenReady if already playing
|
|
||||||
self.status = -1
|
|
||||||
let playerItems = self.allPlayerItems[indexOfSeek..<self.allPlayerItems.count]
|
let playerItems = self.allPlayerItems[indexOfSeek..<self.allPlayerItems.count]
|
||||||
|
|
||||||
DispatchQueue.runOnMainQueue {
|
DispatchQueue.runOnMainQueue {
|
||||||
|
@ -402,17 +401,13 @@ class AudioPlayer: NSObject {
|
||||||
|
|
||||||
DispatchQueue.runOnMainQueue {
|
DispatchQueue.runOnMainQueue {
|
||||||
self.audioPlayer.seek(to: CMTime(seconds: seekTime, preferredTimescale: 1000)) { [weak self] completed in
|
self.audioPlayer.seek(to: CMTime(seconds: seekTime, preferredTimescale: 1000)) { [weak self] completed in
|
||||||
self?.logger.log("SEEK: Completion handler called and continuePlaying(\(continuePlaying)")
|
self?.logger.log("SEEK: Completion handler called")
|
||||||
guard completed else {
|
guard completed else {
|
||||||
self?.logger.log("SEEK: WARNING: seeking not completed (to \(seekTime)")
|
self?.logger.log("SEEK: WARNING: seeking not completed (to \(seekTime)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
if continuePlaying {
|
|
||||||
self.resumePlayback()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.updateNowPlaying()
|
self.updateNowPlaying()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -463,7 +458,17 @@ class AudioPlayer: NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isPlaying() -> Bool {
|
public func isPlaying() -> Bool {
|
||||||
return self.status > 0
|
return self.status == .playing
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getPlayerState() -> PlayerState {
|
||||||
|
// Other PlayerStates doesn't translate to player status
|
||||||
|
switch status {
|
||||||
|
case .uninitialized:
|
||||||
|
return PlayerState.buffering
|
||||||
|
default:
|
||||||
|
return PlayerState.ready
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
|
@ -118,16 +118,15 @@ class PlayerHandler {
|
||||||
player.seek(amount, from: "handler")
|
player.seek(amount, from: "handler")
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func getMetdata() -> [String: Any]? {
|
public static func getMetdata() -> PlaybackMetadata? {
|
||||||
guard let player = player else { return nil }
|
guard let player = player else { return nil }
|
||||||
guard player.isInitialized() else { return nil }
|
guard player.isInitialized() else { return nil }
|
||||||
|
|
||||||
return [
|
return PlaybackMetadata(
|
||||||
"duration": player.getDuration(),
|
duration: player.getDuration() ?? 0,
|
||||||
"currentTime": player.getCurrentTime(),
|
currentTime: player.getCurrentTime() ?? 0,
|
||||||
"playerState": !paused,
|
playerState: player.getPlayerState()
|
||||||
"currentRate": player.rate,
|
)
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Helper logic
|
// MARK: - Helper logic
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue