Working local file playback

This commit is contained in:
ronaldheft 2022-08-11 18:29:55 -04:00
parent 65fcc45bc6
commit 9477860bca
6 changed files with 74 additions and 24 deletions

View file

@ -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")

View file

@ -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 {

View file

@ -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,

View file

@ -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)"

View file

@ -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)

View file

@ -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