110 lines
3.1 KiB
Swift
110 lines
3.1 KiB
Swift
//
|
|
// Swiftfin is subject to the terms of the Mozilla Public
|
|
// License, v2.0. If a copy of the MPL was not distributed with this
|
|
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
//
|
|
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
|
//
|
|
|
|
import Factory
|
|
import Files
|
|
import Foundation
|
|
import JellyfinAPI
|
|
|
|
extension Container {
|
|
var downloadManager: Factory<DownloadManager> { self { DownloadManager() }.shared }
|
|
}
|
|
|
|
class DownloadManager: ObservableObject {
|
|
|
|
@Injected(\.logService)
|
|
private var logger
|
|
|
|
@Published
|
|
private(set) var downloads: [DownloadTask] = []
|
|
|
|
fileprivate init() {
|
|
|
|
createDownloadDirectory()
|
|
}
|
|
|
|
private func createDownloadDirectory() {
|
|
|
|
try? FileManager.default.createDirectory(
|
|
at: URL.downloads,
|
|
withIntermediateDirectories: true
|
|
)
|
|
}
|
|
|
|
func clearTmp() {
|
|
do {
|
|
try Folder(path: URL.tmp.path).files.delete()
|
|
|
|
logger.trace("Cleared tmp directory")
|
|
} catch {
|
|
logger.error("Unable to clear tmp directory: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
func download(task: DownloadTask) {
|
|
guard !downloads.contains(where: { $0.item == task.item }) else { return }
|
|
|
|
downloads.append(task)
|
|
|
|
task.download()
|
|
}
|
|
|
|
func task(for item: BaseItemDto) -> DownloadTask? {
|
|
if let currentlyDownloading = downloads.first(where: { $0.item == item }) {
|
|
return currentlyDownloading
|
|
} else {
|
|
var isDir: ObjCBool = true
|
|
guard let downloadFolder = item.downloadFolder else { return nil }
|
|
guard FileManager.default.fileExists(atPath: downloadFolder.path, isDirectory: &isDir) else { return nil }
|
|
|
|
return parseDownloadItem(with: item.id!)
|
|
}
|
|
}
|
|
|
|
func cancel(task: DownloadTask) {
|
|
guard downloads.contains(where: { $0.item == task.item }) else { return }
|
|
|
|
task.cancel()
|
|
|
|
remove(task: task)
|
|
}
|
|
|
|
func remove(task: DownloadTask) {
|
|
downloads.removeAll(where: { $0.item == task.item })
|
|
}
|
|
|
|
func downloadedItems() -> [DownloadTask] {
|
|
do {
|
|
let downloadContents = try FileManager.default.contentsOfDirectory(atPath: URL.downloads.path)
|
|
return downloadContents.compactMap(parseDownloadItem(with:))
|
|
} catch {
|
|
logger.error("Error retrieving all downloads: \(error.localizedDescription)")
|
|
|
|
return []
|
|
}
|
|
}
|
|
|
|
private func parseDownloadItem(with id: String) -> DownloadTask? {
|
|
|
|
let itemMetadataFile = URL.downloads
|
|
.appendingPathComponent(id)
|
|
.appendingPathComponent("Metadata")
|
|
.appendingPathComponent("Item.json")
|
|
|
|
guard let itemMetadataData = FileManager.default.contents(atPath: itemMetadataFile.path) else { return nil }
|
|
|
|
let jsonDecoder = JSONDecoder()
|
|
|
|
guard let offlineItem = try? jsonDecoder.decode(BaseItemDto.self, from: itemMetadataData) else { return nil }
|
|
|
|
let task = DownloadTask(item: offlineItem)
|
|
task.state = .complete
|
|
return task
|
|
}
|
|
}
|