Files
vorleser/Vorleser/Services/ModelDownloadManager.swift

67 lines
2.6 KiB
Swift

import Foundation
@MainActor
final class ModelDownloadManager: ObservableObject {
enum DownloadState: Equatable {
case idle
case downloading(String)
case completed
case failed(String)
}
@Published private(set) var state: DownloadState = .idle
@Published private(set) var progress: Double = 0
func downloadAll(manifest: ModelManifest) async {
state = .downloading("Preparing download")
progress = 0
let allAssets = manifest.assets + manifest.voices + [manifest.tokenizer]
let total = Double(allAssets.count)
var completed = 0.0
do {
for asset in allAssets {
state = .downloading("Downloading \(asset.name)")
_ = try await download(asset: asset)
completed += 1
progress = completed / total
}
state = .completed
} catch {
state = .failed(error.localizedDescription)
}
}
func localURL(for asset: ModelManifest.Asset) throws -> URL {
let destination = try Self.modelsDirectory().appendingPathComponent(asset.localFolderName, isDirectory: true)
return destination.appendingPathComponent(asset.remoteURL.lastPathComponent)
}
func download(asset: ModelManifest.Asset) async throws -> URL {
let destination = try Self.modelsDirectory().appendingPathComponent(asset.localFolderName, isDirectory: true)
try FileManager.default.createDirectory(at: destination, withIntermediateDirectories: true)
let (tempURL, response) = try await URLSession.shared.download(from: asset.remoteURL)
guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {
throw URLError(.badServerResponse)
}
let finalURL = destination.appendingPathComponent(asset.remoteURL.lastPathComponent)
if FileManager.default.fileExists(atPath: finalURL.path) {
try FileManager.default.removeItem(at: finalURL)
}
try FileManager.default.moveItem(at: tempURL, to: finalURL)
return finalURL
}
static func modelsDirectory() throws -> URL {
let base = try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let folder = base.appendingPathComponent("Models", isDirectory: true)
if !FileManager.default.fileExists(atPath: folder.path) {
try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
}
return folder
}
}