Caps-Server/Sources/App/CapServer.swift

153 lines
4.4 KiB
Swift
Raw Normal View History

2021-11-08 21:58:55 +01:00
import Foundation
import Vapor
final class CapServer {
// MARK: Paths
private let imageFolder: URL
2022-05-24 14:47:50 +02:00
/// The file where the database of caps is stored
private let dbFile: URL
2021-11-08 21:58:55 +01:00
private let fm = FileManager.default
// MARK: Caps
2022-05-24 14:47:50 +02:00
private var saveImmediatelly = true
private var caps = [Int: Cap]() {
didSet {
guard saveImmediatelly else {
return
}
try? saveCaps()
}
}
var nextClassifierVersion: Int {
caps.values.map { $0.classifierVersion }.max() ?? 1
}
2021-11-08 21:58:55 +01:00
init(in folder: URL) throws {
self.imageFolder = folder.appendingPathComponent("images")
2022-05-24 14:47:50 +02:00
self.dbFile = folder.appendingPathComponent("caps.json")
2021-11-08 21:58:55 +01:00
var isDirectory: ObjCBool = false
guard fm.fileExists(atPath: folder.path, isDirectory: &isDirectory),
2022-05-24 14:47:50 +02:00
isDirectory.boolValue else {
2021-11-08 21:58:55 +01:00
log("Public directory \(folder.path) is not a folder, or doesn't exist")
throw CapError.invalidConfiguration
}
2022-05-24 14:47:50 +02:00
try loadCaps()
try updateCounts()
2021-11-08 21:58:55 +01:00
}
2022-05-24 14:47:50 +02:00
private func loadCaps() throws {
let data = try Data(contentsOf: dbFile)
caps = try JSONDecoder().decode([Cap].self, from: data)
.reduce(into: [:]) { $0[$1.id] = $1 }
2021-11-08 21:58:55 +01:00
log("\(caps.count) caps loaded")
}
2022-05-24 14:47:50 +02:00
private func saveCaps() throws {
let data = try JSONEncoder().encode(caps.values.sorted())
try data.write(to: dbFile)
}
2021-11-08 21:58:55 +01:00
// MARK: Paths
func folder(of cap: Int) -> URL {
imageFolder.appendingPathComponent(String(format: "%04d", cap))
}
func file(of cap: Int, version: Int) -> URL {
folder(of: cap).appendingPathComponent(String(format: "%04d-%02d.jpg", cap, version))
}
// MARK: Counts
2022-05-24 14:47:50 +02:00
private func updateCounts() throws {
saveImmediatelly = false
caps = try caps.mapValues {
var cap = $0
cap.count = try count(of: $0.id)
return cap
}
saveImmediatelly = true
try? saveCaps()
}
2021-11-08 21:58:55 +01:00
func countImages(in folder: URL) throws -> Int {
2022-05-24 14:47:50 +02:00
try fm.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil)
.filter({ $0.pathExtension == "jpg" }).count
2021-11-08 21:58:55 +01:00
}
2022-05-24 14:47:50 +02:00
/**
Get the image count of a cap.
*/
2021-11-08 21:58:55 +01:00
func count(of cap: Int) throws -> Int {
let f = folder(of: cap)
guard fm.fileExists(atPath: f.path) else {
return 0
}
return try countImages(in: f)
}
// MARK: Names
2022-05-24 14:47:50 +02:00
func set(name: String, for capId: Int) throws {
guard var cap = caps[capId] else {
let url = server.folder(of: capId)
2021-11-08 21:58:55 +01:00
if !fm.fileExists(atPath: url.path) {
try fm.createDirectory(at: url, withIntermediateDirectories: false)
}
2022-05-24 14:47:50 +02:00
caps[capId] = Cap(id: capId, name: name, count: 0, mainImage: 0, classifierVersion: nextClassifierVersion)
log("Added cap \(capId)")
return
2021-11-08 21:58:55 +01:00
}
2022-05-24 14:47:50 +02:00
cap.name = name
caps[capId] = cap
log("Set name for cap \(capId)")
2021-11-08 21:58:55 +01:00
}
2022-05-24 14:47:50 +02:00
2021-11-08 21:58:55 +01:00
// MARK: Images
/**
Save a cap image to disk.
Automatically creates the image name with the current image count.
- Parameter data: The image data
- Parameter cap: The id of the cap.
- Returns: The new image count for the cap
2022-05-24 14:47:50 +02:00
- Throws: `CapError.unknownId`, if the cap doesn't exist. `CapError.dataInconsistency` if an image already exists for the current count.
2021-11-08 21:58:55 +01:00
*/
func save(image data: Data, for cap: Int) throws -> Int {
2022-05-24 14:47:50 +02:00
guard var count = caps[cap]?.count else {
throw CapError.unknownId
}
var id = 0
var f = file(of: cap, version: id)
while fm.fileExists(atPath: f.path) {
id += 1
f = file(of: cap, version: id)
2021-11-08 21:58:55 +01:00
}
try data.write(to: f)
2022-05-24 14:47:50 +02:00
count += 1
caps[cap]!.count = count
log("Added image \(id) for cap \(cap)")
return count
2021-11-08 21:58:55 +01:00
}
func switchMainImage(to version: Int, for cap: Int) throws {
let file2 = file(of: cap, version: version)
guard fm.fileExists(atPath: file2.path) else {
log("No image \(version) for cap \(cap)")
throw CapError.invalidFile
}
2022-05-24 14:47:50 +02:00
caps[cap]?.mainImage = version
2021-11-08 21:58:55 +01:00
log("Switched cap \(cap) to version \(version)")
}
}