@ -8,8 +8,12 @@ import Foundation
final class ConversionCoordinator : ObservableObject {
final class ConversionCoordinator : ObservableObject {
@ Published private ( set ) var items : [ DropItem ] = [ ]
@ Published private ( set ) var items : [ DropItem ] = [ ]
@ Published private ( set ) var isRunning = false
@ Published private ( set ) var isRunning = false
// / N i c h t - l e e r , s o l a n g e e i n e O v e r w r i t e - R ü c k f r a g e a u s s t e h t — d i e V i e w z e i g t
// / d a n n e i n e n A l e r t u n d r u f t ` r e s o l v e C o n f l i c t s ` / ` c a n c e l C o n f l i c t R e s o l u t i o n ` .
@ Published private ( set ) var pendingConflicts : [ OverwriteConflict ] = [ ]
private var runningTask : Task < Void , Never > ?
private var runningTask : Task < Void , Never > ?
private var pendingTargetFolder : URL ?
var overallProgress : Double {
var overallProgress : Double {
guard ! items . isEmpty else { return 0 }
guard ! items . isEmpty else { return 0 }
@ -19,7 +23,7 @@ final class ConversionCoordinator: ObservableObject {
return partial
return partial
case . running ( let progress ) :
case . running ( let progress ) :
return partial + progress
return partial + progress
case . skipped , . rejected , . succeeded , . failed :
case . skipped , . rejected , . succeeded , . failed , . skippedByUser :
return partial + 1
return partial + 1
}
}
}
}
@ -44,19 +48,69 @@ final class ConversionCoordinator: ObservableObject {
items . removeAll ( )
items . removeAll ( )
}
}
// / P r ü f t v o r d e m e i g e n t l i c h e n L a u f , o b Z i e l d a t e i e n s c h o n e x i s t i e r e n , u n d
// / s t e l l t b e i K o n f l i k t e n e r s t e i n e R ü c k f r a g e s t a t t s i e m i t t e n i m B a t c h a l s
// / F e h l e r a u f f l i e g e n z u l a s s e n ( R E A D M E : " ü b e r s c h r e i b e n n u r n a c h R ü c k f r a g e " ) .
func start ( targetFolder : URL ) {
func start ( targetFolder : URL ) {
guard ! isRunning , ! items . isEmpty else { return }
guard ! isRunning , ! items . isEmpty else { return }
isRunning = true
for index in items . indices {
let conflicts = items . compactMap { item -> OverwriteConflict ? in
guard let destination = ConversionPipeline . previewDestination ( sourceURL : item . sourceURL , outputRootDirectory : targetFolder ) ,
FileManager . default . fileExists ( atPath : destination . path ) else {
return nil
}
return OverwriteConflict ( itemID : item . id , fileName : item . fileName , destination : destination )
}
guard conflicts . isEmpty else {
pendingTargetFolder = targetFolder
pendingConflicts = conflicts
return
}
runBatch ( targetFolder : targetFolder , overwrite : false , skipping : [ ] )
}
// / A n t w o r t a u f d i e O v e r w r i t e - R ü c k f r a g e : ` o v e r w r i t e ` g i l t f ü r d e n g a n z e n
// / B a t c h ( b e w u s s t k e i n e D a t e i - f ü r - D a t e i - A u s w a h l f ü r s M V P ) .
func resolveConflicts ( overwrite : Bool ) {
guard let targetFolder = pendingTargetFolder else { return }
let skipIDs = overwrite ? Set < UUID > ( ) : Set ( pendingConflicts . map ( \ . itemID ) )
for id in skipIDs {
guard let index = items . firstIndex ( where : { $0 . id = = id } ) else { continue }
items [ index ] . status = . skippedByUser ( reason : String ( localized : " Zieldatei existiert bereits. " ) )
}
pendingConflicts = [ ]
pendingTargetFolder = nil
runBatch ( targetFolder : targetFolder , overwrite : overwrite , skipping : skipIDs )
}
func cancelConflictResolution ( ) {
pendingConflicts = [ ]
pendingTargetFolder = nil
}
private func runBatch ( targetFolder : URL , overwrite : Bool , skipping skipIDs : Set < UUID > ) {
let jobs = items
. filter { ! skipIDs . contains ( $0 . id ) }
. map { JobQueue . Job ( id : $0 . id , sourceURL : $0 . sourceURL ) }
guard ! jobs . isEmpty else { return }
isRunning = true
for index in items . indices where ! skipIDs . contains ( items [ index ] . id ) {
items [ index ] . status = . waiting
items [ index ] . status = . waiting
}
}
let jobs = items . map { JobQueue . Job ( id : $0 . id , sourceURL : $0 . sourceURL ) }
var options = ConversionPipeline . Options ( )
options . cropOptions . overwrite = overwrite
runningTask = Task {
runningTask = Task {
let stream = JobQueue . run (
let stream = JobQueue . run (
jobs : jobs ,
jobs : jobs ,
outputRootDirectory : targetFolder ,
outputRootDirectory : targetFolder ,
options : options ,
progress : { [ weak self ] job , value in
progress : { [ weak self ] job , value in
Task { @ MainActor in
Task { @ MainActor in
self ? . updateProgress ( jobID : job . id , value : value )
self ? . updateProgress ( jobID : job . id , value : value )