diff --git a/src/main/scala/net/wiringbits/myphototimeline/CommandAppHelper.scala b/src/main/scala/net/wiringbits/myphototimeline/CommandAppHelper.scala index 172625f..e48a818 100644 --- a/src/main/scala/net/wiringbits/myphototimeline/CommandAppHelper.scala +++ b/src/main/scala/net/wiringbits/myphototimeline/CommandAppHelper.scala @@ -63,11 +63,8 @@ object CommandAppHelper { } def run(source: os.Path, output: os.Path, dryRun: Boolean): Unit = { - new FileOrganizerTask().run( - inputRoot = source, - outputBaseRoot = output, - dryRun = dryRun - ) + val args = FileOrganizerTask.Arguments(inputRoot = source, outputBaseRoot = output, dryRun = dryRun) + new FileOrganizerTask().run(args) } def findPotentialDate(sourceFile: os.Path): Set[String] = { diff --git a/src/main/scala/net/wiringbits/myphototimeline/FileOrganizerTask.scala b/src/main/scala/net/wiringbits/myphototimeline/FileOrganizerTask.scala index 430b904..8ac10b9 100644 --- a/src/main/scala/net/wiringbits/myphototimeline/FileOrganizerTask.scala +++ b/src/main/scala/net/wiringbits/myphototimeline/FileOrganizerTask.scala @@ -1,14 +1,29 @@ package net.wiringbits.myphototimeline +object FileOrganizerTask { + case class Arguments(inputRoot: os.Path, outputBaseRoot: os.Path, dryRun: Boolean) { + val outputRoot: os.Path = outputBaseRoot / "organized" + val duplicatedRoot: os.Path = outputBaseRoot / "duplicated" + val invalidRoot: os.Path = outputBaseRoot / "invalid" + + val dataDirectories: List[os.Path] = List( + inputRoot, + outputRoot, + duplicatedRoot, + invalidRoot + ) + } +} + class FileOrganizerTask { - def run(inputRoot: os.Path, outputBaseRoot: os.Path, dryRun: Boolean): Unit = { - val outputRoot = outputBaseRoot / "organized" - val duplicatedRoot = outputBaseRoot / "duplicated" - val invalidRoot = outputBaseRoot / "invalid" + import FileOrganizerTask._ + + def run(args: Arguments): Unit = { + validate(args) println("Loading already processed files, it may take some minutes, be patient") - val (processedFiles, invalidProcessedFiles) = FileOrganizerService.load(outputRoot)(trackProgress) + val (processedFiles, invalidProcessedFiles) = FileOrganizerService.load(args.outputRoot)(trackProgress) println(s"Already processed files loaded: ${processedFiles.size}") if (invalidProcessedFiles.nonEmpty) { println( @@ -17,7 +32,7 @@ class FileOrganizerTask { } println("Loading files to process, it may take some minutes, be patient") - val (filesToProcess, invalidFilesToProcess) = FileOrganizerService.load(inputRoot)(trackProgress) + val (filesToProcess, invalidFilesToProcess) = FileOrganizerService.load(args.inputRoot)(trackProgress) println(s"Files to process loaded: ${filesToProcess.size}") if (invalidFilesToProcess.nonEmpty) { println( @@ -53,39 +68,39 @@ class FileOrganizerTask { println(s"- New unique files to organize: ${newUnique.size}") println() - if (dryRun) { + if (args.dryRun) { println("Files not affected because dry-run is enabled") } else { // Move duplicated files - println(s"Moving duplicated files to: $duplicatedRoot") + println(s"Moving duplicated files to: ${args.duplicatedRoot}") newDuplicated.zipWithIndex.foreach { case (file, index) => trackProgress(current = index, total = newDuplicated.size) - FileOrganizerService.safeMove(destinationDirectory = duplicatedRoot, sourceFile = file.source) + FileOrganizerService.safeMove(destinationDirectory = args.duplicatedRoot, sourceFile = file.source) } // Move files without metadata - println(s"Moving invalid files to: $invalidRoot") + println(s"Moving invalid files to: ${args.invalidRoot}") invalidFilesToProcess.zipWithIndex.foreach { case (file, index) => trackProgress(current = index, total = invalidFilesToProcess.size) - FileOrganizerService.safeMove(destinationDirectory = invalidRoot, sourceFile = file) + FileOrganizerService.safeMove(destinationDirectory = args.invalidRoot, sourceFile = file) } - println(s"Organizing unique files to: $outputRoot") + println(s"Organizing unique files to: ${args.outputRoot}") newUnique.zipWithIndex.foreach { case (file, index) => trackProgress(current = index, total = newDuplicated.size) FileOrganizerService.organizeByDate( - destinationDirectory = outputRoot, + destinationDirectory = args.outputRoot, sourceFile = file.source, createdOn = file.createdOn ) } println("Cleaning up empty directories") - FileOrganizerService.cleanEmptyDirectories(inputRoot) - FileOrganizerService.cleanEmptyDirectories(outputRoot) + FileOrganizerService.cleanEmptyDirectories(args.inputRoot) + FileOrganizerService.cleanEmptyDirectories(args.outputRoot) } println("Done") @@ -103,4 +118,39 @@ class FileOrganizerTask { } } } + + private def exit(msg: String): Unit = { + println(s"FATAL: $msg") + sys.exit(1) + } + + private def validateDirectory(path: os.Path): Unit = { + try { + lazy val exists = os.exists(path) + lazy val isDir = os.isDir(path) + if (isDir) { + () + } else if (!exists) { + os.makeDir.all(path) + } else { + exit(s"$path is not a directory, or it can't be created") + } + } catch { + case ex: Throwable => + ex.printStackTrace() + exit(s"$path is not a directory, or it can't be created") + } + } + + private def validate(args: Arguments): Unit = { + args.dataDirectories.foreach(validateDirectory) + + if (args.outputRoot.toString().startsWith(args.inputRoot.toString())) { + exit("The output directory can't be inside the input directory") + } + + if (args.inputRoot.toString().startsWith(args.outputRoot.toString())) { + exit("The input directory can't be inside the output directory") + } + } }