Rewrite downloader to use delegate and download item

This commit is contained in:
ronaldheft 2022-08-06 10:11:46 -04:00
parent 33041608f8
commit d5d65e244b
6 changed files with 222 additions and 123 deletions

View file

@ -145,6 +145,30 @@ class Database {
}
}
public func getDownloadItem(downloadItemId: String) -> DownloadItem? {
Database.realmQueue.sync {
instance.object(ofType: DownloadItem.self, forPrimaryKey: downloadItemId)
}
}
public func getDownloadItem(libraryItemId: String) -> DownloadItem? {
Database.realmQueue.sync {
instance.objects(DownloadItem.self).filter("libraryItemId == %@", libraryItemId).first
}
}
public func getDownloadItem(downloadItemPartId: String) -> DownloadItem? {
Database.realmQueue.sync {
instance.objects(DownloadItem.self).filter("SUBQUERY(downloadItemParts, $part, $part.id == %@) .@count > 0", downloadItemPartId).first
}
}
public func saveDownloadItem(_ downloadItem: DownloadItem) {
Database.realmQueue.sync {
try! instance.write { instance.add(downloadItem) }
}
}
public func getDeviceSettings() -> DeviceSettings {
return Database.realmQueue.sync {
return instance.objects(DeviceSettings.self).first ?? getDefaultDeviceSettings()

View file

@ -1,102 +0,0 @@
//
// LibraryItemDownloadSession.swift
// App
//
// Created by Ron Heft on 8/2/22.
//
import Foundation
enum LibraryItemDownloadError: String, Error {
case noTracks = "No tracks on library item"
case noMetadata = "No metadata for track, unable to download"
case failedDownload = "Failed to download item"
}
class LibraryItemDownloadSession {
let item: LibraryItem
private let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
init(_ item: LibraryItem) {
self.item = item
}
public func startDownload() async throws -> LocalLibraryItem {
guard let tracks = item.media.tracks else {
throw LibraryItemDownloadError.noTracks
}
return try await withThrowingTaskGroup(of: LocalFile.self, returning: LocalLibraryItem.self) { group in
for (position, track) in tracks.enumerated() {
group.addTask { try await self.startLibraryItemTrackDownload(item: self.item, position: position, track: track) }
}
var files = [LocalFile]()
for try await file in group {
files.append(file)
}
return LocalLibraryItem(self.item, localUrl: self.documentsDirectory, server: Store.serverConfig!, files: files)
}
}
private func startLibraryItemTrackDownload(item: LibraryItem, position: Int, track: AudioTrack) async throws -> LocalFile {
NSLog("TRACK \(track.contentUrl!)")
// If we don't name metadata, then we can't proceed
guard let filename = track.metadata?.filename else {
throw LibraryItemDownloadError.noMetadata
}
let serverUrl = urlForTrack(item: item, track: track)
let itemDirectory = createLibraryItemFileDirectory(item: item)
let localUrl = itemDirectory.appendingPathComponent("\(filename)")
try await downloadFile(serverUrl: serverUrl, localUrl: localUrl)
return LocalFile(item.id, filename, track.mimeType, localUrl)
}
private func createLibraryItemFileDirectory(item: LibraryItem) -> URL {
let itemDirectory = documentsDirectory.appendingPathComponent("\(item.id)")
NSLog("ITEM DIR \(itemDirectory)")
// Create library item directory
do {
try FileManager.default.createDirectory(at: itemDirectory, withIntermediateDirectories: true)
} catch {
NSLog("Failed to CREATE LI DIRECTORY \(error)")
}
return itemDirectory
}
private func urlForTrack(item: LibraryItem, track: AudioTrack) -> URL {
// filename needs to be encoded otherwise would just use contentUrl
let filenameEncoded = track.metadata?.filename.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
let urlstr = "\(Store.serverConfig!.address)/s/item/\(item.id)/\(filenameEncoded ?? "")?token=\(Store.serverConfig!.token)"
return URL(string: urlstr)!
}
private func downloadFile(serverUrl: URL, localUrl: URL) async throws {
return try await withCheckedThrowingContinuation { continuation in
let downloadTask = URLSession.shared.downloadTask(with: serverUrl) { urlOrNil, responseOrNil, errorOrNil in
guard let tempUrl = urlOrNil else {
continuation.resume(throwing: errorOrNil!)
return
}
do {
try FileManager.default.moveItem(at: tempUrl, to: localUrl)
continuation.resume()
} catch {
continuation.resume(throwing: error)
}
}
downloadTask.resume()
}
}
}