mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-07-21 11:14:38 +02:00
Working local file playback
This commit is contained in:
parent
65fcc45bc6
commit
9477860bca
6 changed files with 74 additions and 24 deletions
|
@ -45,11 +45,19 @@ public class AbsAudioPlayer: CAPPlugin {
|
|||
let isLocalItem = libraryItemId?.starts(with: "local_") ?? false
|
||||
if (isLocalItem) {
|
||||
let item = Database.shared.getLocalLibraryItem(localLibraryItemId: libraryItemId!)
|
||||
// TODO: Logic required for podcasts here
|
||||
let playbackSession = item?.getPlaybackSession(episode: nil)
|
||||
let episode = item?.getPodcastEpisode(episodeId: episodeId)
|
||||
let playbackSession = item?.getPlaybackSession(episode: episode)
|
||||
sendPrepareMetadataEvent(itemId: libraryItemId!, playWhenReady: playWhenReady)
|
||||
do {
|
||||
self.sendPlaybackSession(session: try playbackSession.asDictionary())
|
||||
call.resolve(try playbackSession.asDictionary())
|
||||
} catch(let exception) {
|
||||
NSLog("failed to convert session to json")
|
||||
debugPrint(exception)
|
||||
call.resolve([:])
|
||||
}
|
||||
PlayerHandler.startPlayback(session: playbackSession!, playWhenReady: playWhenReady, playbackRate: playbackRate)
|
||||
self.sendMetadata()
|
||||
call.resolve()
|
||||
} else { // Playing from the server
|
||||
sendPrepareMetadataEvent(itemId: libraryItemId!, playWhenReady: playWhenReady)
|
||||
ApiClient.startPlaybackSession(libraryItemId: libraryItemId!, episodeId: episodeId, forceTranscode: false) { session in
|
||||
|
@ -62,12 +70,12 @@ public class AbsAudioPlayer: CAPPlugin {
|
|||
call.resolve([:])
|
||||
}
|
||||
|
||||
|
||||
PlayerHandler.startPlayback(session: session, playWhenReady: playWhenReady, playbackRate: playbackRate)
|
||||
self.sendMetadata()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func closePlayback(_ call: CAPPluginCall) {
|
||||
NSLog("Close playback")
|
||||
|
||||
|
|
|
@ -90,6 +90,8 @@ struct Metadata: Realmable, Codable {
|
|||
var seriesName: String?
|
||||
var feedUrl: String?
|
||||
|
||||
var authorDisplayName: String { self.authorName ?? "Unknown" }
|
||||
|
||||
init() {
|
||||
title = "Unknown"
|
||||
genres = []
|
||||
|
@ -251,6 +253,11 @@ struct AudioTrack: Realmable, Codable {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getLocalFile() -> LocalFile? {
|
||||
guard let localFileId = self.localFileId else { return nil }
|
||||
return Database.shared.getLocalFile(localFileId: localFileId)
|
||||
}
|
||||
}
|
||||
|
||||
struct FileMetadata: Realmable, Codable {
|
||||
|
|
|
@ -59,6 +59,12 @@ extension LocalLibraryItem {
|
|||
return total
|
||||
}
|
||||
|
||||
func getPodcastEpisode(episodeId: String?) -> PodcastEpisode? {
|
||||
guard self.isPodcast else { return nil }
|
||||
guard let episodes = self.media?.episodes else { return nil }
|
||||
return episodes.first(where: { $0.id == episodeId })
|
||||
}
|
||||
|
||||
func getPlaybackSession(episode: PodcastEpisode?) -> PlaybackSession {
|
||||
let localEpisodeId = episode?.id
|
||||
let sessionId = "play_local_\(UUID().uuidString)"
|
||||
|
@ -67,13 +73,13 @@ extension LocalLibraryItem {
|
|||
let mediaProgressId = (localEpisodeId != nil) ? "\(self.id)-\(localEpisodeId!)" : self.id
|
||||
let mediaProgress = Database.shared.getLocalMediaProgress(localMediaProgressId: mediaProgressId)
|
||||
|
||||
// TODO: Clean up add mediaType methods for displayTitle and displayAuthor
|
||||
let mediaMetadata = self.media?.metadata
|
||||
let audioTracks = self.media?.tracks
|
||||
let authorName = mediaMetadata?.authorName
|
||||
let chapters = self.media?.chapters
|
||||
var audioTracks = self.media?.tracks
|
||||
let authorName = mediaMetadata?.authorDisplayName
|
||||
|
||||
if let episode = episode {
|
||||
// TODO: Implement podcast
|
||||
if let episode = episode, let track = episode.audioTrack {
|
||||
audioTracks = [track]
|
||||
}
|
||||
|
||||
let dateNow = Date().timeIntervalSince1970
|
||||
|
@ -81,18 +87,18 @@ extension LocalLibraryItem {
|
|||
id: sessionId,
|
||||
userId: self.serverUserId,
|
||||
libraryItemId: self.libraryItemId,
|
||||
episodeId: episode?.id,
|
||||
episodeId: episode?.serverEpisodeId,
|
||||
mediaType: self.mediaType,
|
||||
chapters: [],
|
||||
chapters: chapters ?? [],
|
||||
displayTitle: mediaMetadata?.title,
|
||||
displayAuthor: authorName,
|
||||
coverPath: nil,
|
||||
coverPath: self.coverContentUrl,
|
||||
duration: self.getDuration(),
|
||||
playMethod: 3,
|
||||
playMethod: PlayMethod.local.rawValue,
|
||||
startedAt: dateNow,
|
||||
updatedAt: 0,
|
||||
timeListening: 0.0,
|
||||
audioTracks: [],
|
||||
audioTracks: audioTracks ?? [],
|
||||
currentTime: mediaProgress?.currentTime ?? 0.0,
|
||||
libraryItem: nil,
|
||||
serverConnectionConfigId: self.serverConnectionConfigId,
|
||||
|
|
|
@ -286,6 +286,17 @@ class AudioPlayer: NSObject {
|
|||
let urlstr = "\(Store.serverConfig!.address)/s/item/\(itemId)/\(filenameEncoded ?? "")?token=\(Store.serverConfig!.token)"
|
||||
let url = URL(string: urlstr)!
|
||||
return AVURLAsset(url: url)
|
||||
} else if (playbackSession.playMethod == PlayMethod.local.rawValue) {
|
||||
guard let localFile = track.getLocalFile() else {
|
||||
// Worst case we can stream the file
|
||||
NSLog("Unable to play local file. Resulting to streaming \(track.localFileId ?? "Unknown")")
|
||||
let filename = track.metadata?.filename ?? ""
|
||||
let filenameEncoded = filename.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
|
||||
let urlstr = "\(Store.serverConfig!.address)/s/item/\(itemId)/\(filenameEncoded ?? "")?token=\(Store.serverConfig!.token)"
|
||||
let url = URL(string: urlstr)!
|
||||
return AVURLAsset(url: url)
|
||||
}
|
||||
return AVURLAsset(url: localFile.contentPath)
|
||||
} else { // HLS Transcode
|
||||
let headers: [String: String] = [
|
||||
"Authorization": "Bearer \(Store.serverConfig!.token)"
|
||||
|
|
|
@ -131,6 +131,11 @@ class Database {
|
|||
}
|
||||
}
|
||||
|
||||
public func getLocalFile(localFileId: String) -> LocalFile? {
|
||||
let realm = try! Realm()
|
||||
return realm.object(ofType: LocalFile.self, forPrimaryKey: localFileId)
|
||||
}
|
||||
|
||||
public func getDownloadItem(downloadItemId: String) -> DownloadItem? {
|
||||
let realm = try! Realm()
|
||||
return realm.object(ofType: DownloadItem.self, forPrimaryKey: downloadItemId)
|
||||
|
|
|
@ -15,6 +15,10 @@ struct NowPlayingMetadata {
|
|||
var title: String
|
||||
var author: String?
|
||||
var series: String?
|
||||
var coverUrl: URL? {
|
||||
guard let url = URL(string: "\(Store.serverConfig!.address)/api/items/\(itemId)/cover?token=\(Store.serverConfig!.token)") else { return nil }
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
class NowPlayingInfo {
|
||||
|
@ -30,9 +34,17 @@ class NowPlayingInfo {
|
|||
public func setSessionMetadata(metadata: NowPlayingMetadata) {
|
||||
setMetadata(artwork: nil, metadata: metadata)
|
||||
|
||||
guard let url = URL(string: "\(Store.serverConfig!.address)/api/items/\(metadata.itemId)/cover?token=\(Store.serverConfig!.token)") else {
|
||||
return
|
||||
let isLocalItem = metadata.itemId.starts(with: "local_")
|
||||
if isLocalItem {
|
||||
guard let artworkUrl = metadata.artworkUrl else { return }
|
||||
let coverImage = UIImage(contentsOfFile: artworkUrl)
|
||||
guard let coverImage = coverImage else { return }
|
||||
let artwork = MPMediaItemArtwork(boundsSize: coverImage.size) { _ -> UIImage in
|
||||
return coverImage
|
||||
}
|
||||
self.setMetadata(artwork: artwork, metadata: metadata)
|
||||
} else {
|
||||
guard let url = metadata.coverUrl else { return }
|
||||
ApiClient.getData(from: url) { [self] image in
|
||||
guard let downloadedImage = image else {
|
||||
return
|
||||
|
@ -44,6 +56,7 @@ class NowPlayingInfo {
|
|||
self.setMetadata(artwork: artwork, metadata: metadata)
|
||||
}
|
||||
}
|
||||
}
|
||||
public func update(duration: Double, currentTime: Double, rate: Float) {
|
||||
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = duration
|
||||
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentTime
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue