Improve cache settings view

This commit is contained in:
Christoph Hagen
2023-10-24 15:44:13 +02:00
parent d5edf360fb
commit f192e1c29d
6 changed files with 110 additions and 74 deletions

View File

@ -91,8 +91,12 @@ final class Database: ObservableObject {
pendingImageUploadStorage = newValue.map { cap, images in
"\(cap)-\(images.map { "\($0)" }.joined(separator: ":"))"
}.joined(separator: ";")
pendingImageUploadCount = newValue.values.reduce(0) { $0 + $1.count }
}
}
@Published
private(set) var pendingImageUploadCount = 0
private var uploadTimer: Timer?
@ -569,28 +573,10 @@ final class Database: ObservableObject {
changedCaps.contains(cap) || imageUploads[cap] != nil
}
var pendingImageUploadCount: Int {
imageUploads.values.reduce(0) { $0 + $1.count }
}
private func capId(from url: URL) -> Int? {
Int(url.lastPathComponent.components(separatedBy: "-").first!)
}
private func imageId(from url: URL) -> CapImage? {
let parts = url.deletingPathExtension().lastPathComponent.components(separatedBy: "-")
guard parts.count == 2 else {
log("File \(url.lastPathComponent) is not a cap image")
return nil
}
guard let capId = Int(parts.first!),
let version = Int(parts.last!) else {
log("File \(url.lastPathComponent) is not a cap image")
return nil
}
return .init(cap: capId, version: version)
}
private func uploadAllImages() async {
guard hasServerAuthentication else {
log("No server authentication to upload to server")
@ -980,36 +966,10 @@ final class Database: ObservableObject {
var classifierSize: Int {
localClassifierUrl.fileSize
}
private var cachedImages: [URL] {
do {
return try fm.contentsOfDirectory(at: images.folder, includingPropertiesForKeys: nil)
} catch {
log("Failed to get cached images: \(error)")
return []
}
}
func clearImageCache() {
let allImages = cachedImages
let unnecessaryImages = allImages
.filter {
guard let id = imageId(from: $0) else {
return true
}
guard let cap = caps[id.cap] else {
return true
}
return cap.mainImage != id.version
}
log("Deleting \(unnecessaryImages.count) of \(allImages.count) cached images")
for cachedImage in unnecessaryImages {
do {
try fm.removeItem(at: cachedImage)
} catch {
log("Failed to delete cached image \(cachedImage.lastPathComponent): \(error)")
}
}
let imagesToKeep = caps.values.map { $0.image }
images.clearImageCache(keeping: Set(imagesToKeep))
}
}

View File

@ -1,7 +1,7 @@
import Foundation
import UIKit
final class ImageCache {
final class ImageCache: ObservableObject {
let folder: URL
@ -16,6 +16,9 @@ final class ImageCache {
private let thumbnailQuality: CGFloat = 0.7
private let imageQuality: CGFloat = 0.3
@Published
private(set) var imageCount = 0
init(folder: URL, server: URL, thumbnailSize: CGFloat) throws {
self.folder = folder
@ -25,6 +28,7 @@ final class ImageCache {
if !fm.fileExists(atPath: folder.path) {
try fm.createDirectory(at: folder, withIntermediateDirectories: true)
}
self.imageCount = countLocalImages()
}
private func localImageUrl(_ image: CapImage) -> URL {
@ -92,12 +96,6 @@ final class ImageCache {
return UIImage(at: localUrl)
}
@discardableResult
func removeImage(_ image: CapImage) -> Bool {
let localUrl = localImageUrl(image)
return removePossibleFile(localUrl)
}
@discardableResult
func refreshImage(_ image: CapImage) async -> Bool {
guard let downloadedImageUrl = await loadRemoteImage(image) else {
@ -122,6 +120,9 @@ final class ImageCache {
for url in files {
do {
try fm.removeItem(at: url)
DispatchQueue.main.async {
self.imageCount -= 1
}
} catch {
isSuccessful = false
log("Failed to remove image \(url.lastPathComponent) from cache: \(error)")
@ -157,14 +158,20 @@ final class ImageCache {
private func saveImage(_ image: CapImage, at tempUrl: URL) -> Bool {
let localUrl = localImageUrl(image)
let isOverwrite = exists(localUrl)
guard removePossibleFile(localUrl) else {
return false
}
do {
try fm.moveItem(at: tempUrl, to: localUrl)
if !isOverwrite {
DispatchQueue.main.async {
self.imageCount += 1
}
}
return true
} catch {
print("failed to save image \(localUrl.lastPathComponent): \(error)")
print("Failed to save image \(localUrl.lastPathComponent): \(error)")
return false
}
}
@ -250,12 +257,64 @@ final class ImageCache {
return false
}
let localUrl = localThumbnailUrl(cap: cap)
let isOverwrite = exists(localUrl)
do {
try data.write(to: localUrl)
if !isOverwrite {
DispatchQueue.main.async {
self.imageCount += 1
}
}
return true
} catch {
print("Failed to save thumbnail \(cap): \(error)")
return false
}
}
private func countLocalImages() -> Int {
cachedImages.count
}
private var cachedImages: [URL] {
do {
return try fm.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil)
} catch {
log("Failed to get cached images: \(error)")
return []
}
}
private func imageId(from url: URL) -> CapImage? {
let parts = url.deletingPathExtension().lastPathComponent.components(separatedBy: "-")
guard parts.count == 2 else {
log("File \(url.lastPathComponent) is not a cap image")
return nil
}
guard let capId = Int(parts.first!),
let version = Int(parts.last!) else {
log("File \(url.lastPathComponent) is not a cap image")
return nil
}
return .init(cap: capId, version: version)
}
func clearImageCache(keeping: Set<CapImage>) {
let allImages = cachedImages
for url in allImages {
if let id = imageId(from: url), keeping.contains(id) {
continue // Skip image
}
do {
try fm.removeItem(at: url)
} catch {
log("Failed to delete cached image \(url.lastPathComponent): \(error)")
}
}
let newCount = countLocalImages()
log("Deleted \(allImages.count - newCount) of \(allImages.count) cached images, leaving \(newCount)")
DispatchQueue.main.async {
self.imageCount = newCount
}
}
}