From 1d4c401103354661002b5ac03b491ee5865cb1e9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Jul 2026 17:05:09 +0000 Subject: [PATCH] Store the stem model in Application Support instead of Caches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caches is OS-evictable, so the 158 MB HT-Demucs model could silently vanish and re-download at play time — a delay and a network/memory spike stacked exactly on playback start (the jetsam RCA's ~50 s window matches a silent re-download). Application Support persists; excluded from backup; existing Caches copies are migrated instead of re-downloaded. --- .../Sources/Ingest/StemModelStore.swift | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Packages/ContinuityKit/Sources/Ingest/StemModelStore.swift b/Packages/ContinuityKit/Sources/Ingest/StemModelStore.swift index c529848..208dfdc 100644 --- a/Packages/ContinuityKit/Sources/Ingest/StemModelStore.swift +++ b/Packages/ContinuityKit/Sources/Ingest/StemModelStore.swift @@ -8,18 +8,37 @@ enum StemModelStore { static let fileName = "htdemucs_ft_vocals_fp16weights.onnx" static var directory: URL { - let dir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] + // Application Support, not Caches: the OS evicts Caches under storage pressure, and a + // silently re-downloaded 158 MB model at play time is both a delay and a memory/network + // spike stacked exactly on playback start (see the jetsam RCA). Excluded from backup — + // large and re-derivable. + var dir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] .appendingPathComponent("StemModel", isDirectory: true) try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + var values = URLResourceValues() + values.isExcludedFromBackup = true + try? dir.setResourceValues(values) return dir } + /// Pre-move location (Caches). Checked once so existing installs don't re-download. + private static var legacyURL: URL { + FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] + .appendingPathComponent("StemModel", isDirectory: true) + .appendingPathComponent(fileName) + } + static var localURL: URL { directory.appendingPathComponent(fileName) } static var isDownloaded: Bool { FileManager.default.fileExists(atPath: localURL.path) } /// Returns the local model URL, downloading it first if needed. static func ensureModel() async throws -> URL { if isDownloaded { return localURL } + // Migrate a surviving Caches copy instead of re-downloading it. + if FileManager.default.fileExists(atPath: legacyURL.path), + (try? FileManager.default.moveItem(at: legacyURL, to: localURL)) != nil { + return localURL + } let (temp, response) = try await URLSession.shared.download(from: remoteURL) defer { try? FileManager.default.removeItem(at: temp) } if let http = response as? HTTPURLResponse, !(200...299).contains(http.statusCode) {