mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-06-29 00:10:09 +02:00
feat: now playing chapter track
This commit is contained in:
parent
793f0c05f7
commit
2b1667e532
11 changed files with 91 additions and 11 deletions
|
@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
// Override point for customization after application launch.
|
// Override point for customization after application launch.
|
||||||
|
|
||||||
let configuration = Realm.Configuration(
|
let configuration = Realm.Configuration(
|
||||||
schemaVersion: 15,
|
schemaVersion: 16,
|
||||||
migrationBlock: { [weak self] migration, oldSchemaVersion in
|
migrationBlock: { [weak self] migration, oldSchemaVersion in
|
||||||
if (oldSchemaVersion < 1) {
|
if (oldSchemaVersion < 1) {
|
||||||
self?.logger.log("Realm schema version was \(oldSchemaVersion)")
|
self?.logger.log("Realm schema version was \(oldSchemaVersion)")
|
||||||
|
@ -48,6 +48,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
newObject?["languageCode"] = "en-us"
|
newObject?["languageCode"] = "en-us"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (oldSchemaVersion < 16) {
|
||||||
|
self?.logger.log("Realm schema version was \(oldSchemaVersion)... Adding chapterTrack setting")
|
||||||
|
migration.enumerateObjects(ofType: PlayerSettings.className()) { oldObject, newObject in
|
||||||
|
newObject?["chapterTrack"] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Realm.Configuration.defaultConfiguration = configuration
|
Realm.Configuration.defaultConfiguration = configuration
|
||||||
|
|
|
@ -15,6 +15,7 @@ CAP_PLUGIN(AbsAudioPlayer, "AbsAudioPlayer",
|
||||||
CAP_PLUGIN_METHOD(closePlayback, CAPPluginReturnPromise);
|
CAP_PLUGIN_METHOD(closePlayback, CAPPluginReturnPromise);
|
||||||
|
|
||||||
CAP_PLUGIN_METHOD(setPlaybackSpeed, CAPPluginReturnPromise);
|
CAP_PLUGIN_METHOD(setPlaybackSpeed, CAPPluginReturnPromise);
|
||||||
|
CAP_PLUGIN_METHOD(setChapterTrack, CAPPluginReturnPromise);
|
||||||
|
|
||||||
CAP_PLUGIN_METHOD(playPlayer, CAPPluginReturnPromise);
|
CAP_PLUGIN_METHOD(playPlayer, CAPPluginReturnPromise);
|
||||||
CAP_PLUGIN_METHOD(pausePlayer, CAPPluginReturnPromise);
|
CAP_PLUGIN_METHOD(pausePlayer, CAPPluginReturnPromise);
|
||||||
|
|
|
@ -120,6 +120,17 @@ public class AbsAudioPlayer: CAPPlugin {
|
||||||
call.resolve()
|
call.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func setChapterTrack(_ call: CAPPluginCall) {
|
||||||
|
let chapterTrack = call.getBool("enabled", true)
|
||||||
|
logger.log(String(chapterTrack))
|
||||||
|
let settings = PlayerSettings.main()
|
||||||
|
try? settings.update {
|
||||||
|
settings.chapterTrack = chapterTrack
|
||||||
|
}
|
||||||
|
PlayerHandler.setChapterTrack()
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
@objc func playPlayer(_ call: CAPPluginCall) {
|
@objc func playPlayer(_ call: CAPPluginCall) {
|
||||||
PlayerHandler.paused = false
|
PlayerHandler.paused = false
|
||||||
call.resolve()
|
call.resolve()
|
||||||
|
|
|
@ -160,3 +160,11 @@ class PlaybackSession: Object, Codable, Deletable {
|
||||||
try container.encode(localMediaProgressId, forKey: .localMediaProgressId)
|
try container.encode(localMediaProgressId, forKey: .localMediaProgressId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension PlaybackSession {
|
||||||
|
func getCurrentChapter() -> Chapter? {
|
||||||
|
return chapters.first { chapter in
|
||||||
|
chapter.start <= self.currentTime && chapter.end > self.currentTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ class PlayerSettings: Object {
|
||||||
// The webapp has a persisted setting for playback speed, but it's not always available to the native code
|
// The webapp has a persisted setting for playback speed, but it's not always available to the native code
|
||||||
// Lets track it natively as well, so we never have a situation where the UI and native player are out of sync
|
// Lets track it natively as well, so we never have a situation where the UI and native player are out of sync
|
||||||
@Persisted var playbackRate: Float = 1.0
|
@Persisted var playbackRate: Float = 1.0
|
||||||
|
@Persisted var chapterTrack: Bool = true
|
||||||
|
|
||||||
|
|
||||||
// Singleton pattern for Realm objects
|
// Singleton pattern for Realm objects
|
||||||
static func main() -> PlayerSettings {
|
static func main() -> PlayerSettings {
|
||||||
|
|
|
@ -39,3 +39,13 @@ class Chapter: EmbeddedObject, Codable {
|
||||||
try container.encode(title, forKey: .title)
|
try container.encode(title, forKey: .title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Chapter {
|
||||||
|
func getRelativeChapterCurrentTime(sessionCurrentTime: Double) -> Double {
|
||||||
|
return sessionCurrentTime - self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRelativeChapterEndTime() -> Double {
|
||||||
|
return self.end - self.start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -224,6 +224,8 @@ class AudioPlayer: NSObject {
|
||||||
// Update the UI
|
// Update the UI
|
||||||
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil)
|
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil)
|
||||||
}
|
}
|
||||||
|
// Update the now playing and chapter info
|
||||||
|
self.updateNowPlaying()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -443,6 +445,10 @@ class AudioPlayer: NSObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func setChapterTrack() {
|
||||||
|
self.updateNowPlaying()
|
||||||
|
}
|
||||||
|
|
||||||
public func getCurrentTime() -> Double? {
|
public func getCurrentTime() -> Double? {
|
||||||
guard let playbackSession = self.getPlaybackSession() else { return nil }
|
guard let playbackSession = self.getPlaybackSession() else { return nil }
|
||||||
let currentTrackTime = self.audioPlayer.currentTime().seconds
|
let currentTrackTime = self.audioPlayer.currentTime().seconds
|
||||||
|
@ -657,7 +663,16 @@ class AudioPlayer: NSObject {
|
||||||
}
|
}
|
||||||
private func updateNowPlaying() {
|
private func updateNowPlaying() {
|
||||||
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil)
|
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil)
|
||||||
if let duration = self.getDuration(), let currentTime = self.getCurrentTime() {
|
if let session = self.getPlaybackSession(), let currentChapter = session.getCurrentChapter(), PlayerSettings.main().chapterTrack {
|
||||||
|
NowPlayingInfo.shared.update(
|
||||||
|
duration: currentChapter.getRelativeChapterEndTime(),
|
||||||
|
currentTime: currentChapter.getRelativeChapterCurrentTime(sessionCurrentTime: session.currentTime),
|
||||||
|
rate: rate,
|
||||||
|
chapterName: currentChapter.title,
|
||||||
|
chapterNumber: (session.chapters.firstIndex(of: currentChapter) ?? 0) + 1,
|
||||||
|
chapterCount: session.chapters.count
|
||||||
|
)
|
||||||
|
} else if let duration = self.getDuration(), let currentTime = self.getCurrentTime() {
|
||||||
NowPlayingInfo.shared.update(duration: duration, currentTime: currentTime, rate: rate)
|
NowPlayingInfo.shared.update(duration: duration, currentTime: currentTime, rate: rate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,10 @@ class PlayerHandler {
|
||||||
self.player?.setPlaybackRate(speed)
|
self.player?.setPlaybackRate(speed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func setChapterTrack() {
|
||||||
|
self.player?.setChapterTrack()
|
||||||
|
}
|
||||||
|
|
||||||
public static func getSleepTimeRemaining() -> Double? {
|
public static func getSleepTimeRemaining() -> Double? {
|
||||||
return self.player?.getSleepTimeRemaining()
|
return self.player?.getSleepTimeRemaining()
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ class NowPlayingInfo {
|
||||||
self.setMetadata(artwork: artwork, metadata: metadata)
|
self.setMetadata(artwork: artwork, metadata: metadata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public func update(duration: Double, currentTime: Double, rate: Float) {
|
public func update(duration: Double, currentTime: Double, rate: Float, chapterName: String? = nil, chapterNumber: Int? = nil, chapterCount: Int? = nil) {
|
||||||
// Update on the main to prevent access collisions
|
// Update on the main to prevent access collisions
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
if let self = self {
|
if let self = self {
|
||||||
|
@ -62,6 +62,18 @@ class NowPlayingInfo {
|
||||||
self.nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = rate
|
self.nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = rate
|
||||||
self.nowPlayingInfo[MPNowPlayingInfoPropertyDefaultPlaybackRate] = 1.0
|
self.nowPlayingInfo[MPNowPlayingInfoPropertyDefaultPlaybackRate] = 1.0
|
||||||
|
|
||||||
|
|
||||||
|
if let chapterName = chapterName, let chapterNumber = chapterNumber, let chapterCount = chapterCount {
|
||||||
|
self.nowPlayingInfo[MPMediaItemPropertyTitle] = chapterName
|
||||||
|
self.nowPlayingInfo[MPNowPlayingInfoPropertyChapterNumber] = chapterNumber
|
||||||
|
self.nowPlayingInfo[MPNowPlayingInfoPropertyChapterCount] = chapterCount
|
||||||
|
} else {
|
||||||
|
// Set the title back to the book title
|
||||||
|
self.nowPlayingInfo[MPMediaItemPropertyTitle] = self.nowPlayingInfo[MPMediaItemPropertyAlbumTitle]
|
||||||
|
self.nowPlayingInfo[MPNowPlayingInfoPropertyChapterNumber] = nil
|
||||||
|
self.nowPlayingInfo[MPNowPlayingInfoPropertyChapterCount] = nil
|
||||||
|
}
|
||||||
|
|
||||||
MPNowPlayingInfoCenter.default().nowPlayingInfo = self.nowPlayingInfo
|
MPNowPlayingInfoCenter.default().nowPlayingInfo = self.nowPlayingInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +101,7 @@ class NowPlayingInfo {
|
||||||
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyTitle] = metadata!.title
|
nowPlayingInfo[MPMediaItemPropertyTitle] = metadata!.title
|
||||||
nowPlayingInfo[MPMediaItemPropertyArtist] = metadata!.author ?? "unknown"
|
nowPlayingInfo[MPMediaItemPropertyArtist] = metadata!.author ?? "unknown"
|
||||||
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = metadata!.series
|
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = metadata!.title
|
||||||
}
|
}
|
||||||
|
|
||||||
private func shouldFetchCover(id: String) -> Bool {
|
private func shouldFetchCover(id: String) -> Bool {
|
||||||
|
|
|
@ -141,6 +141,11 @@ class AbsAudioPlayerWeb extends WebPlugin {
|
||||||
if (this.player) this.player.playbackRate = value
|
if (this.player) this.player.playbackRate = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PluginMethod
|
||||||
|
setChapterTrack({ enabled }) {
|
||||||
|
this.useChapterTrack = enabled
|
||||||
|
}
|
||||||
|
|
||||||
// PluginMethod
|
// PluginMethod
|
||||||
async getCurrentTime() {
|
async getCurrentTime() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { Preferences } from '@capacitor/preferences'
|
import { Preferences } from '@capacitor/preferences'
|
||||||
|
import { AbsAudioPlayer } from '@/plugins/capacitor'
|
||||||
|
|
||||||
|
|
||||||
class LocalStorage {
|
class LocalStorage {
|
||||||
constructor(vuexStore) {
|
constructor(vuexStore) {
|
||||||
|
@ -45,6 +47,9 @@ class LocalStorage {
|
||||||
async setUseChapterTrack(useChapterTrack) {
|
async setUseChapterTrack(useChapterTrack) {
|
||||||
try {
|
try {
|
||||||
await Preferences.set({ key: 'useChapterTrack', value: useChapterTrack ? '1' : '0' })
|
await Preferences.set({ key: 'useChapterTrack', value: useChapterTrack ? '1' : '0' })
|
||||||
|
console.log("ooooooo")
|
||||||
|
console.log(useChapterTrack)
|
||||||
|
AbsAudioPlayer.setChapterTrack({ enabled: useChapterTrack })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[LocalStorage] Failed to set use chapter track', error)
|
console.error('[LocalStorage] Failed to set use chapter track', error)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue