Luminate/Sources/LuminateCore/ImageService.swift

73 lines
2.6 KiB
Swift

//
// ImageService.swift
//
// Copyright 2026 Brendan Szymanski <hello@bscubed.dev>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
public actor ImageService {
private let cacheDir: URL
private let memoryCache = NSCache<NSString, NSData>()
public init(cacheDir: URL? = nil) {
let defaultCache = FileManager.default.urls(
for: .cachesDirectory, in: .userDomainMask
).first!.appendingPathComponent("luminate/images")
self.cacheDir = cacheDir ?? defaultCache
try? FileManager.default.createDirectory(
at: self.cacheDir, withIntermediateDirectories: true)
}
public func loadImage(url: URL) async throws -> Data {
let key = url.absoluteString as NSString
if let cached = memoryCache.object(forKey: key) {
return cached as Data
}
let diskKey = url.absoluteString.data(using: .utf8)!.base64EncodedString()
.replacingOccurrences(of: "/", with: "_")
let diskURL = cacheDir.appendingPathComponent(diskKey)
if let data = try? Data(contentsOf: diskURL) {
memoryCache.setObject(data as NSData, forKey: key)
return data
}
let (data, _) = try await URLSession.shared.data(from: url)
let nsData = data as NSData
memoryCache.setObject(nsData, forKey: key)
try? data.write(to: diskURL)
return data
}
public func prefetch(urls: [URL]) async {
await withTaskGroup(of: Void.self) { group in
for url in urls {
group.addTask { _ = try? await self.loadImage(url: url) }
}
}
}
public func clearCache() {
memoryCache.removeAllObjects()
try? FileManager.default.removeItem(at: cacheDir)
try? FileManager.default.createDirectory(at: cacheDir, withIntermediateDirectories: true)
}
}