advplyr.audiobookshelf-app/ios/App/Shared/models/LocalLibraryExtensions.swift

470 lines
16 KiB
Swift
Raw Normal View History

2022-07-16 21:46:49 -04:00
//
// LocalLibraryExtensions.swift
// App
//
// Created by Ron Heft on 7/16/22.
//
import Foundation
extension LocalLibraryItem {
enum CodingKeys: CodingKey {
case id
case basePath
case absolutePath
case contentUrl
case isInvalid
case mediaType
case media
case localFiles
case coverContentUrl
case coverAbsolutePath
case isLocal
case serverConnectionConfigId
case serverAddress
case serverUserId
case libraryItemId
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(basePath, forKey: .basePath)
try container.encode(absolutePath, forKey: .absolutePath)
try container.encode(contentUrl, forKey: .contentUrl)
try container.encode(isInvalid, forKey: .isInvalid)
try container.encode(mediaType, forKey: .mediaType)
2022-07-17 21:50:19 -04:00
try container.encode(media, forKey: .media)
try container.encode(localFiles, forKey: .localFiles)
try container.encode(coverContentUrl, forKey: .coverContentUrl)
try container.encode(coverAbsolutePath, forKey: .coverAbsolutePath)
try container.encode(isLocal, forKey: .isLocal)
try container.encode(serverConnectionConfigId, forKey: .serverConnectionConfigId)
try container.encode(serverAddress, forKey: .serverAddress)
try container.encode(serverUserId, forKey: .serverUserId)
try container.encode(libraryItemId, forKey: .libraryItemId)
2022-07-16 21:46:49 -04:00
}
convenience init(_ item: LibraryItem, localUrl: URL, server: ServerConnectionConfig, files: [LocalFile]) {
self.init()
self.contentUrl = localUrl.absoluteString
self.mediaType = item.mediaType
2022-07-30 17:21:50 -04:00
self.media = LocalMediaType(item.media, coverPath: "", files: files)
2022-07-16 21:46:49 -04:00
self.localFiles.append(objectsIn: files)
// TODO: self.coverContentURL
// TODO: self.converAbsolutePath
self.libraryItemId = item.id
self.serverConnectionConfigId = server.id
self.serverAddress = server.address
self.serverUserId = server.userId
}
2022-07-31 13:33:36 -04:00
func getDuration() -> Double {
var total = 0.0
self.media?.tracks.forEach { track in total += track.duration }
return total
}
func getPlaybackSession(episode: LocalPodcastEpisode?) -> PlaybackSession {
let localEpisodeId = episode?.id
let sessionId = "play_local_\(UUID().uuidString)"
// Get current progress from local media
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
if let episode = episode {
// TODO: Implement podcast
}
let dateNow = Date().timeIntervalSince1970
return PlaybackSession(
id: sessionId,
userId: self.serverUserId,
libraryItemId: self.libraryItemId,
episodeId: episode?.serverEpisodeId,
mediaType: self.mediaType,
chapters: [],
displayTitle: mediaMetadata?.title,
displayAuthor: authorName,
coverPath: nil,
duration: self.getDuration(),
playMethod: 3,
startedAt: dateNow,
updatedAt: 0,
timeListening: 0.0,
audioTracks: [],
currentTime: mediaProgress?.currentTime ?? 0.0,
libraryItem: nil,
serverConnectionConfigId: self.serverConnectionConfigId,
serverAddress: self.serverAddress
)
}
2022-07-16 21:46:49 -04:00
}
2022-07-17 21:50:19 -04:00
extension LocalMediaType {
enum CodingKeys: CodingKey {
case libraryItemId
case metadata
case coverPath
case tags
case audioFiles
case chapters
case tracks
case size
case duration
case episodes
case autoDownloadEpisodes
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(libraryItemId, forKey: .libraryItemId)
try container.encode(metadata, forKey: .metadata)
try container.encode(coverPath, forKey: .coverPath)
try container.encode(tags, forKey: .tags)
try container.encode(audioFiles, forKey: .audioFiles)
try container.encode(chapters, forKey: .chapters)
try container.encode(tracks, forKey: .tracks)
try container.encode(size, forKey: .size)
try container.encode(duration, forKey: .duration)
try container.encode(episodes, forKey: .episodes)
try container.encode(autoDownloadEpisodes, forKey: .autoDownloadEpisodes)
}
2022-07-30 17:21:50 -04:00
convenience init(_ mediaType: MediaType, coverPath: String, files: [LocalFile]) {
2022-07-17 21:50:19 -04:00
self.init()
self.libraryItemId = mediaType.libraryItemId
self.metadata = LocalMetadata(mediaType.metadata)
2022-07-30 17:21:50 -04:00
self.coverPath = coverPath
2022-07-17 21:50:19 -04:00
self.tags.append(objectsIn: mediaType.tags ?? [])
self.audioFiles.append(objectsIn: mediaType.audioFiles!.enumerated().map() {
i, audioFile -> LocalAudioFile in LocalAudioFile(audioFile)
})
self.chapters.append(objectsIn: mediaType.chapters!.enumerated().map() {
i, chapter -> LocalChapter in LocalChapter(chapter)
})
self.tracks.append(objectsIn: mediaType.tracks!.enumerated().map() {
2022-07-30 17:21:50 -04:00
i, track in LocalAudioTrack(track, libraryItemId: self.libraryItemId ?? "", filename: files[i].filename ?? "")
2022-07-17 21:50:19 -04:00
})
self.size = mediaType.size
self.duration = mediaType.duration
// TODO: self.episodes
2022-07-30 17:21:50 -04:00
// TODO: Handle podcast auto downloads
2022-07-17 21:50:19 -04:00
self.autoDownloadEpisodes = mediaType.autoDownloadEpisodes
}
}
extension LocalMetadata {
enum CodingKeys: CodingKey {
case title
case subtitle
case authors
case narrators
case genres
case publishedYear
case publishedDate
case publisher
case desc
case isbn
case asin
case language
case explicit
case authorName
case authorNameLF
case narratorName
case seriesName
case feedUrl
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(title, forKey: .title)
try container.encode(subtitle, forKey: .subtitle)
try container.encode(authors, forKey: .authors)
try container.encode(narrators, forKey: .narrators)
try container.encode(genres, forKey: .genres)
try container.encode(publishedYear, forKey: .publishedYear)
try container.encode(publishedDate, forKey: .publishedDate)
try container.encode(publisher, forKey: .publisher)
try container.encode(desc, forKey: .desc)
try container.encode(isbn, forKey: .isbn)
try container.encode(asin, forKey: .asin)
try container.encode(language, forKey: .language)
try container.encode(explicit, forKey: .explicit)
try container.encode(authorName, forKey: .authorName)
try container.encode(authorNameLF, forKey: .authorNameLF)
try container.encode(narratorName, forKey: .narratorName)
try container.encode(seriesName, forKey: .seriesName)
try container.encode(feedUrl, forKey: .feedUrl)
}
convenience init(_ metadata: Metadata) {
self.init()
self.title = metadata.title
self.subtitle = metadata.subtitle
self.narrators.append(objectsIn: metadata.narrators ?? [])
self.genres.append(objectsIn: metadata.genres)
self.publishedYear = metadata.publishedYear
self.publishedDate = metadata.publishedDate
self.publisher = metadata.publisher
self.desc = metadata.description
self.isbn = metadata.isbn
self.asin = metadata.asin
self.language = metadata.language
self.explicit = metadata.explicit
self.authorName = metadata.authorName
self.authorNameLF = metadata.authorNameLF
self.narratorName = metadata.narratorName
self.seriesName = metadata.seriesName
self.feedUrl = metadata.feedUrl
}
}
extension LocalPodcastEpisode {
enum CodingKeys: CodingKey {
case id
case index
case episode
case episodeType
case title
case subtitle
case desc
case audioFile
case audioTrack
case duration
case size
case serverEpisodeId
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(index, forKey: .index)
try container.encode(episode, forKey: .episode)
try container.encode(episodeType, forKey: .episodeType)
try container.encode(title, forKey: .title)
try container.encode(subtitle, forKey: .subtitle)
try container.encode(desc, forKey: .desc)
try container.encode(audioFile, forKey: .audioFile)
try container.encode(audioTrack, forKey: .audioTrack)
try container.encode(duration, forKey: .duration)
try container.encode(size, forKey: .size)
try container.encode(serverEpisodeId, forKey: .serverEpisodeId)
}
}
extension LocalAudioFile {
enum CodingKeys: CodingKey {
case index
case ino
case metadata
}
convenience init(_ audioFile: AudioFile) {
self.init()
self.index = audioFile.index
self.ino = audioFile.ino
2022-07-30 17:21:50 -04:00
// self.metadata
2022-07-17 21:50:19 -04:00
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(index, forKey: .index)
try container.encode(ino, forKey: .ino)
try container.encode(metadata, forKey: .metadata)
}
}
extension LocalAuthor {
enum CodingKeys: CodingKey {
case id
case name
case coverPath
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(name, forKey: .name)
try container.encode(coverPath, forKey: .coverPath)
}
2022-07-30 17:21:50 -04:00
convenience init(_ author: Author) {
self.init()
self.id = author.id
self.name = author.name
// self.coverPath
}
2022-07-17 21:50:19 -04:00
}
extension LocalChapter {
enum CodingKeys: CodingKey {
case id
case start
case end
case title
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(start, forKey: .start)
try container.encode(end, forKey: .end)
try container.encode(title, forKey: .title)
}
convenience init(_ chapter: Chapter) {
self.init()
self.id = chapter.id
self.start = chapter.start
self.end = chapter.end
self.title = chapter.title
}
}
extension LocalAudioTrack {
enum CodingKeys: CodingKey {
case index
case startOffset
case duration
case title
case contentUrl
case mimeType
case metadata
case isLocal
case localFileId
case serverIndex
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(index, forKey: .index)
try container.encode(startOffset, forKey: .startOffset)
try container.encode(duration, forKey: .duration)
try container.encode(title, forKey: .title)
try container.encode(contentUrl, forKey: .contentUrl)
try container.encode(mimeType, forKey: .mimeType)
try container.encode(metadata, forKey: .metadata)
try container.encode(isLocal, forKey: .isLocal)
try container.encode(localFileId, forKey: .localFileId)
try container.encode(serverIndex, forKey: .serverIndex)
}
2022-07-30 17:21:50 -04:00
convenience init(_ track: AudioTrack, libraryItemId: String, filename: String) {
2022-07-17 21:50:19 -04:00
self.init()
self.index = track.index
self.startOffset = track.startOffset
self.duration = track.duration
self.title = track.title
2022-07-30 17:21:50 -04:00
self.contentUrl = "" // TODO: Different URL
2022-07-17 21:50:19 -04:00
self.mimeType = track.mimeType
// TODO: self.metadata
2022-07-30 17:21:50 -04:00
self.localFileId = "\(libraryItemId)_\(filename.toBase64())"
2022-07-17 21:50:19 -04:00
self.serverIndex = track.serverIndex
}
}
extension LocalFileMetadata {
enum CodingKeys: CodingKey {
case filename
case ext
case path
case relPath
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(filename, forKey: .filename)
try container.encode(ext, forKey: .ext)
try container.encode(path, forKey: .path)
try container.encode(relPath, forKey: .relPath)
}
2022-07-30 17:21:50 -04:00
/* TODO: Can we skip this object? */
2022-07-17 21:50:19 -04:00
}
extension LocalFile {
enum CodingKeys: CodingKey {
case id
case filename
case contentUrl
case absolutePath
case mimeType
case size
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(filename, forKey: .filename)
try container.encode(contentUrl, forKey: .contentUrl)
try container.encode(absolutePath, forKey: .absolutePath)
try container.encode(mimeType, forKey: .mimeType)
try container.encode(size, forKey: .size)
}
2022-07-30 17:21:50 -04:00
convenience init(_ libraryItemId: String, _ filename: String, _ mimeType: String, _ localUrl: URL) {
2022-07-17 21:50:19 -04:00
self.init()
2022-07-30 17:21:50 -04:00
self.id = "\(libraryItemId)_\(filename.toBase64())"
2022-07-17 21:50:19 -04:00
self.filename = filename
self.contentUrl = localUrl.absoluteString
self.absolutePath = localUrl.path
self.size = localUrl.fileSize
}
func isAudioFile() -> Bool {
switch self.mimeType {
2022-07-30 18:25:20 -04:00
case "application/octet-stream",
"video/mp4":
return true
default:
return self.mimeType?.starts(with: "audio") ?? false
}
2022-07-17 21:50:19 -04:00
}
}
extension LocalMediaProgress {
enum CodingKeys: CodingKey {
case id
case localLibraryItemId
case localEpisodeId
case duration
case progress
case currentTime
case isFinished
case lastUpdate
case startedAt
case finishedAt
case serverConnectionConfigId
case serverAddress
case serverUserId
case libraryItemId
case episodeId
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(localLibraryItemId, forKey: .localLibraryItemId)
try container.encode(localEpisodeId, forKey: .localEpisodeId)
try container.encode(duration, forKey: .duration)
try container.encode(progress, forKey: .progress)
try container.encode(currentTime, forKey: .currentTime)
try container.encode(isFinished, forKey: .isFinished)
try container.encode(lastUpdate, forKey: .lastUpdate)
try container.encode(startedAt, forKey: .startedAt)
try container.encode(finishedAt, forKey: .finishedAt)
try container.encode(serverConnectionConfigId, forKey: .serverConnectionConfigId)
try container.encode(serverAddress, forKey: .serverAddress)
try container.encode(serverUserId, forKey: .serverUserId)
try container.encode(libraryItemId, forKey: .libraryItemId)
try container.encode(episodeId, forKey: .episodeId)
}
}