import DropTwoCore import Foundation /// Ein Element in der Dateiliste der Oberfläche, mit seinem aktuellen /// Verarbeitungsstatus. Rein UI-seitig — die eigentliche Logik steckt in /// `ConversionPipeline`/`JobQueue` in DropTwoCore. struct DropItem: Identifiable { let id: UUID let sourceURL: URL var status: Status /// Ausschnitt für diese Datei, in der Review-Ansicht eingestellt. var cropPlan = CropPlan() /// Nutzer hat bei fehlendem Format im Namen "4x5 anhängen" gewählt. var appendFormatWhenMissing = false enum Status { case waiting case running(progress: Double) case skipped(SkipReason) case rejected(RejectionReason) case succeeded(croppedDestination: URL) case failed(message: String) /// Nutzer hat sich bei einer Overwrite-Rückfrage bewusst fürs /// Überspringen entschieden — kein Fehler, aber sichtbar im Verlauf. case skippedByUser(reason: String) /// Fehlerliste statt stiller Abbrüche: alles außer "läuft"/"fertig"/ /// "bewusst vom Nutzer übersprungen" braucht sichtbare Aufmerksamkeit. var needsAttention: Bool { switch self { case .skipped, .rejected, .failed: return true case .waiting, .running, .succeeded, .skippedByUser: return false } } } init(sourceURL: URL) { self.id = UUID() self.sourceURL = sourceURL self.status = .waiting } var fileName: String { sourceURL.lastPathComponent } /// Was der Dateiname vor dem Lauf schon verrät — so sieht man Probleme /// beim Reinziehen, nicht erst nach dem Start. var nameOutcome: FileNameParseResult.Outcome { FileNameParser.parse(fileName).outcome } /// true = im Namen steht kein Format, und der Nutzer hat noch nicht /// entschieden, "4x5" anzuhängen. var isMissingFormat: Bool { guard !appendFormatWhenMissing, case .rejected(.formatSegmentMissing) = nameOutcome else { return false } return true } /// Name der 4:5-Fassung, wie er entstehen wird. nil = wird so nicht verarbeitet. var plannedTargetName: String? { let root = URL(fileURLWithPath: "/", isDirectory: true) return ConversionPipeline.previewDestination( sourceURL: sourceURL, outputRootDirectory: root, appendFormatWhenMissing: appendFormatWhenMissing )?.lastPathComponent } }