Fix data model issues

This commit is contained in:
ronaldheft 2022-08-10 22:17:12 -04:00
parent e275aa1699
commit 446e54cb91
6 changed files with 65 additions and 69 deletions

View file

@ -45,7 +45,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Local library // Local library
Realm.registerRealmables(LocalLibraryItem.self) Realm.registerRealmables(LocalLibraryItem.self)
Realm.registerRealmables(LocalPodcastEpisode.self)
Realm.registerRealmables(LocalFile.self) Realm.registerRealmables(LocalFile.self)
Realm.registerRealmables(LocalMediaProgress.self) Realm.registerRealmables(LocalMediaProgress.self)

View file

@ -154,10 +154,8 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
let files = downloadItem.downloadItemParts.compactMap { part -> LocalFile? in let files = downloadItem.downloadItemParts.compactMap { part -> LocalFile? in
if part.filename == "cover.jpg" { if part.filename == "cover.jpg" {
coverFile = part.destinationUri coverFile = part.destinationUri
return nil
} else {
return LocalFile(libraryItem.id, part.filename!, part.mimeType()!, part.destinationUri!, fileSize: Int(part.destinationURL!.fileSize))
} }
return LocalFile(libraryItem.id, part.filename!, part.mimeType()!, part.destinationUri!, fileSize: Int(part.destinationURL!.fileSize))
} }
let localLibraryItem = LocalLibraryItem(libraryItem, localUrl: localDirectory, server: Store.serverConfig!, files: files, coverPath: coverFile) let localLibraryItem = LocalLibraryItem(libraryItem, localUrl: localDirectory, server: Store.serverConfig!, files: files, coverPath: coverFile)
@ -165,8 +163,8 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
statusNotification["localLibraryItem"] = try? localLibraryItem.asDictionary() statusNotification["localLibraryItem"] = try? localLibraryItem.asDictionary()
if let progress = libraryItem.userMediaProgress { if let progress = libraryItem.userMediaProgress {
// TODO: Handle podcast let episode = downloadItem.media?.episodes?.first(where: { $0.id == downloadItem.episodeId })
let localMediaProgress = LocalMediaProgress(localLibraryItem: localLibraryItem, episode: nil, progress: progress) let localMediaProgress = LocalMediaProgress(localLibraryItem: localLibraryItem, episode: episode, progress: progress)
Database.shared.saveLocalMediaProgress(localMediaProgress) Database.shared.saveLocalMediaProgress(localMediaProgress)
statusNotification["localMediaProgress"] = try? localMediaProgress.asDictionary() statusNotification["localMediaProgress"] = try? localMediaProgress.asDictionary()
} }

View file

@ -199,8 +199,7 @@ struct AudioTrack: Realmable, Codable {
var contentUrl: String? var contentUrl: String?
var mimeType: String var mimeType: String
var metadata: FileMetadata? var metadata: FileMetadata?
// var isLocal: Bool var localFileId: String?
// var localFileId: String?
// var audioProbeResult: AudioProbeResult? Needed for local playback // var audioProbeResult: AudioProbeResult? Needed for local playback
var serverIndex: Int? var serverIndex: Int?
@ -208,6 +207,13 @@ struct AudioTrack: Realmable, Codable {
duration = 0 duration = 0
mimeType = "" mimeType = ""
} }
mutating func setLocalInfo(filenameIdMap: [String: String], serverIndex: Int) {
if let localFileId = filenameIdMap[self.metadata?.filename ?? ""] {
self.localFileId = localFileId
self.serverIndex = serverIndex
}
}
} }
struct FileMetadata: Realmable, Codable { struct FileMetadata: Realmable, Codable {

View file

@ -121,6 +121,14 @@ extension DownloadItemPart {
} }
func mimeType() -> String? { func mimeType() -> String? {
audioTrack?.mimeType ?? episode?.audioTrack?.mimeType if let track = audioTrack {
return track.mimeType
} else if let podcastTrack = episode?.audioTrack {
return podcastTrack.mimeType
} else if serverPath?.hasSuffix("/cover") ?? false {
return "image/jpg"
} else {
return nil
}
} }
} }

View file

@ -11,12 +11,12 @@ import Unrealm
struct LocalLibraryItem: Realmable, Codable { struct LocalLibraryItem: Realmable, Codable {
var id: String = "local_\(UUID().uuidString)" var id: String = "local_\(UUID().uuidString)"
var basePath: String = "" var basePath: String = ""
dynamic var _contentUrl: String? var _contentUrl: String?
var isInvalid: Bool = false var isInvalid: Bool = false
var mediaType: String = "" var mediaType: String = ""
var media: MediaType? var media: MediaType?
var localFiles: [LocalFile] = [] var localFiles: [LocalFile] = []
dynamic var _coverContentUrl: String? var _coverContentUrl: String?
var isLocal: Bool = true var isLocal: Bool = true
var serverConnectionConfigId: String? var serverConnectionConfigId: String?
var serverAddress: String? var serverAddress: String?
@ -24,28 +24,18 @@ struct LocalLibraryItem: Realmable, Codable {
var libraryItemId: String? var libraryItemId: String?
var contentUrl: String? { var contentUrl: String? {
set(url) { if let path = _contentUrl {
_contentUrl = url return AbsDownloader.downloadsDirectory.appendingPathComponent(path).absoluteString
} } else {
get { return nil
if let path = _contentUrl {
return AbsDownloader.downloadsDirectory.appendingPathComponent(path).absoluteString
} else {
return nil
}
} }
} }
var coverContentUrl: String? { var coverContentUrl: String? {
set(url) { if let path = self._coverContentUrl {
_coverContentUrl = url return AbsDownloader.downloadsDirectory.appendingPathComponent(path).absoluteString
} } else {
get { return nil
if let path = self._coverContentUrl {
return AbsDownloader.downloadsDirectory.appendingPathComponent(path).absoluteString
} else {
return nil
}
} }
} }
@ -63,17 +53,15 @@ struct LocalLibraryItem: Realmable, Codable {
let values = try decoder.container(keyedBy: CodingKeys.self) let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id) id = try values.decode(String.self, forKey: .id)
basePath = try values.decode(String.self, forKey: .basePath) basePath = try values.decode(String.self, forKey: .basePath)
contentUrl = try values.decode(String.self, forKey: .contentUrl)
isInvalid = try values.decode(Bool.self, forKey: .isInvalid) isInvalid = try values.decode(Bool.self, forKey: .isInvalid)
mediaType = try values.decode(String.self, forKey: .mediaType) mediaType = try values.decode(String.self, forKey: .mediaType)
media = try values.decode(MediaType.self, forKey: .media) media = try values.decode(MediaType.self, forKey: .media)
localFiles = try values.decode([LocalFile].self, forKey: .localFiles) localFiles = try values.decode([LocalFile].self, forKey: .localFiles)
coverContentUrl = try values.decode(String.self, forKey: .coverContentUrl)
isLocal = try values.decode(Bool.self, forKey: .isLocal) isLocal = try values.decode(Bool.self, forKey: .isLocal)
serverConnectionConfigId = try values.decode(String.self, forKey: .serverConnectionConfigId) serverConnectionConfigId = try? values.decode(String.self, forKey: .serverConnectionConfigId)
serverAddress = try values.decode(String.self, forKey: .serverAddress) serverAddress = try? values.decode(String.self, forKey: .serverAddress)
serverUserId = try values.decode(String.self, forKey: .serverUserId) serverUserId = try? values.decode(String.self, forKey: .serverUserId)
libraryItemId = try values.decode(String.self, forKey: .libraryItemId) libraryItemId = try? values.decode(String.self, forKey: .libraryItemId)
} }
func encode(to encoder: Encoder) throws { func encode(to encoder: Encoder) throws {
@ -94,41 +82,23 @@ struct LocalLibraryItem: Realmable, Codable {
} }
} }
struct LocalPodcastEpisode: Realmable, Codable {
var id: String = UUID().uuidString
var index: Int = 0
var episode: String?
var episodeType: String?
var title: String = "Unknown"
var subtitle: String?
var desc: String?
var audioFile: AudioFile?
var audioTrack: AudioTrack?
var duration: Double = 0
var size: Int = 0
var serverEpisodeId: String?
static func primaryKey() -> String? {
return "id"
}
}
struct LocalFile: Realmable, Codable { struct LocalFile: Realmable, Codable {
var id: String = UUID().uuidString var id: String = UUID().uuidString
var filename: String? var filename: String?
var contentUrl: String = "" var _contentUrl: String = ""
var absolutePath: String {
return AbsDownloader.downloadsDirectory.appendingPathComponent(self.contentUrl).absoluteString
}
var mimeType: String? var mimeType: String?
var size: Int = 0 var size: Int = 0
var contentUrl: String {
return AbsDownloader.downloadsDirectory.appendingPathComponent(_contentUrl).absoluteString
}
static func primaryKey() -> String? { static func primaryKey() -> String? {
return "id" return "id"
} }
private enum CodingKeys : String, CodingKey { private enum CodingKeys : String, CodingKey {
case id, filename, contentUrl, absolutePath, mimeType, size case id, filename, contentUrl, mimeType, size
} }
init() {} init() {}
@ -137,8 +107,7 @@ struct LocalFile: Realmable, Codable {
let values = try decoder.container(keyedBy: CodingKeys.self) let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id) id = try values.decode(String.self, forKey: .id)
filename = try values.decode(String.self, forKey: .filename) filename = try values.decode(String.self, forKey: .filename)
contentUrl = try values.decode(String.self, forKey: .contentUrl) mimeType = try? values.decode(String.self, forKey: .mimeType)
mimeType = try values.decode(String.self, forKey: .mimeType)
size = try values.decode(Int.self, forKey: .size) size = try values.decode(Int.self, forKey: .size)
} }
@ -147,7 +116,6 @@ struct LocalFile: Realmable, Codable {
try container.encode(id, forKey: .id) try container.encode(id, forKey: .id)
try container.encode(filename, forKey: .filename) try container.encode(filename, forKey: .filename)
try container.encode(contentUrl, forKey: .contentUrl) try container.encode(contentUrl, forKey: .contentUrl)
try container.encode(absolutePath, forKey: .absolutePath)
try container.encode(mimeType, forKey: .mimeType) try container.encode(mimeType, forKey: .mimeType)
try container.encode(size, forKey: .size) try container.encode(size, forKey: .size)
} }

View file

@ -10,15 +10,32 @@ import Foundation
extension LocalLibraryItem { extension LocalLibraryItem {
init(_ item: LibraryItem, localUrl: String, server: ServerConnectionConfig, files: [LocalFile], coverPath: String?) { init(_ item: LibraryItem, localUrl: String, server: ServerConnectionConfig, files: [LocalFile], coverPath: String?) {
self.init() self.init()
self.contentUrl = localUrl self._contentUrl = localUrl
self.mediaType = item.mediaType self.mediaType = item.mediaType
self.media = item.media
self.localFiles = files self.localFiles = files
self.coverContentUrl = coverPath self._coverContentUrl = coverPath
self.libraryItemId = item.id self.libraryItemId = item.id
self.serverConnectionConfigId = server.id self.serverConnectionConfigId = server.id
self.serverAddress = server.address self.serverAddress = server.address
self.serverUserId = server.userId self.serverUserId = server.userId
// Link the audio tracks and files
var media = item.media
let fileIdByFilename = Dictionary(uniqueKeysWithValues: files.map { ($0.filename ?? "", $0.id) } )
if ( item.mediaType == "book" ) {
if let tracks = media.tracks {
for i in tracks.indices {
media.tracks?[i].setLocalInfo(filenameIdMap: fileIdByFilename, serverIndex: i)
}
}
} else if ( item.mediaType == "podcast" ) {
if let episodes = media.episodes {
for i in episodes.indices {
media.episodes?[i].audioTrack?.setLocalInfo(filenameIdMap: fileIdByFilename, serverIndex: 0)
}
}
}
self.media = media
} }
func getDuration() -> Double { func getDuration() -> Double {
@ -27,7 +44,7 @@ extension LocalLibraryItem {
return total return total
} }
func getPlaybackSession(episode: LocalPodcastEpisode?) -> PlaybackSession { func getPlaybackSession(episode: PodcastEpisode?) -> PlaybackSession {
let localEpisodeId = episode?.id let localEpisodeId = episode?.id
let sessionId = "play_local_\(UUID().uuidString)" let sessionId = "play_local_\(UUID().uuidString)"
@ -49,7 +66,7 @@ extension LocalLibraryItem {
id: sessionId, id: sessionId,
userId: self.serverUserId, userId: self.serverUserId,
libraryItemId: self.libraryItemId, libraryItemId: self.libraryItemId,
episodeId: episode?.serverEpisodeId, episodeId: episode?.id,
mediaType: self.mediaType, mediaType: self.mediaType,
chapters: [], chapters: [],
displayTitle: mediaMetadata?.title, displayTitle: mediaMetadata?.title,
@ -75,7 +92,7 @@ extension LocalFile {
self.id = "\(libraryItemId)_\(filename.toBase64())" self.id = "\(libraryItemId)_\(filename.toBase64())"
self.filename = filename self.filename = filename
self.mimeType = mimeType self.mimeType = mimeType
self.contentUrl = localUrl self._contentUrl = localUrl
self.size = fileSize self.size = fileSize
} }
@ -91,7 +108,7 @@ extension LocalFile {
} }
extension LocalMediaProgress { extension LocalMediaProgress {
init(localLibraryItem: LocalLibraryItem, episode: LocalPodcastEpisode?, progress: MediaProgress) { init(localLibraryItem: LocalLibraryItem, episode: PodcastEpisode?, progress: MediaProgress) {
self.id = localLibraryItem.id self.id = localLibraryItem.id
self.localLibraryItemId = localLibraryItem.id self.localLibraryItemId = localLibraryItem.id
self.libraryItemId = localLibraryItem.libraryItemId self.libraryItemId = localLibraryItem.libraryItemId