67 lines
2.6 KiB
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
|
|
}
|
|
}
|