Created
March 29, 2017 00:08
-
-
Save tillig/528a6eceaac68b71eae87d798e6dfa1b to your computer and use it in GitHub Desktop.
Calculate which files from a camera source have been backed up and which need to be filed, then sort all files appropriately
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.Synopsis | |
Sorts picture files from camera sources into appropriate | |
folders for filing. | |
.DESCRIPTION | |
Compares the set of pictures in the camera source folder | |
and the backup folder, then merges/sorts the contents into | |
a third location based on how they need to be filed. | |
Items get sorted into one of four groups: | |
- To File: Items only in camera or items in camera and backup. | |
- Only In Backup: Items only in backup. | |
- Duplicate Camera Items: Items that appear multiple times in the camera folder. | |
- Backups To Remove After Filing: Items that were in the backup folder where originals are ready to file. | |
.EXAMPLE | |
.\Sort-PictureFiles.ps1 -CameraFolder .\camera -BackupFolder .\backup -DestinationFolder .\destination | |
#> | |
[CmdletBinding()] | |
Param( | |
[Parameter( | |
Mandatory=$True, | |
HelpMessage="The folder where all the pictures and videos from cameras are located. All subfolders will be considered picture sources. THIS FOLDER WILL BE EMPTIED AFTER SORTING.")] | |
[ValidateNotNullOrEmpty()] | |
[string] | |
$CameraFolder, | |
[Parameter( | |
Mandatory=$True, | |
HelpMessage="The folder where pictures are automatically backed up. All subfolders will be considered picture sources. THIS FOLDER WILL BE EMPTIED AFTER SORTING.")] | |
[ValidateNotNullOrEmpty()] | |
[string] | |
$BackupFolder, | |
[Parameter( | |
Mandatory=$True, | |
HelpMessage="The folder where sorted pictures and videos should be placed. All camera and backup pictures will be sorted into folders in here.")] | |
[ValidateNotNullOrEmpty()] | |
[string] | |
$DestinationFolder, | |
[Parameter( | |
Mandatory=$False, | |
HelpMessage="The folder where sorted pictures and videos should be placed. All camera and backup pictures will be sorted into folders in here.")] | |
[ValidateNotNullOrEmpty()] | |
[string] | |
$ExifTool = "exiftool.exe" | |
) | |
Begin | |
{ | |
If (-Not (Test-Path $CameraFolder -PathType Container)) | |
{ | |
throw "The camera folder $CameraFolder does not exist." | |
} | |
If (-Not (Test-Path $BackupFolder -PathType Container)) | |
{ | |
throw "The backup folder $BackupFolder does not exist." | |
} | |
If (-Not (Test-Path $DestinationFolder -PathType Container)) | |
{ | |
throw "The sort destination folder $DestinationFolder does not exist." | |
} | |
if ((Get-Command $ExifTool -ErrorAction SilentlyContinue) -eq $NULL) | |
{ | |
Write-Host "Unable to find exiftool.exe in your path." | |
} | |
$toFileFolder = New-Item -Path "$DestinationFolder" -Name "To File" -ItemType Directory -Force | |
$onlyInBackupFolder = New-Item -Path "$DestinationFolder" -Name "Only In Backup" -ItemType Directory -Force | |
$duplicateFolder = New-Item -Path "$DestinationFolder" -Name "Duplicate Camera Items" -ItemType Directory -Force | |
$removeAfterFiling = New-Item -Path "$DestinationFolder" -Name "Backups To Remove After Filing" -ItemType Directory -Force | |
function SafeMove() | |
{ | |
Param($SourceFile, $DestinationFolder) | |
Write-Verbose "Moving $SourceFile" | |
$name = [System.IO.Path]::GetFileNameWithoutExtension($SourceFile) | |
$ext = [System.IO.Path]::GetExtension($SourceFile) | |
if(-Not (Test-Path (Join-Path -Path $DestinationFolder -ChildPath "$name$ext"))) | |
{ | |
Move-Item -Force -Path $SourceFile -Destination $DestinationFolder | |
return; | |
} | |
$count = 0 | |
while(Test-Path (Join-Path -Path $DestinationFolder -ChildPath "$name-$count$ext")) | |
{ | |
$count++ | |
} | |
Move-Item -Force -Path $SourceFile -Destination (Join-Path -Path $DestinationFolder -ChildPath "$name-$count$ext") | |
} | |
} | |
Process | |
{ | |
# Get pairs of hash/path for camera folder. | |
$cameraHashes = Get-ChildItem -Path $CameraFolder -Recurse -File | Sort-Object -Property FullName | Get-FileHash | |
Write-Verbose "Camera file hashes:" | |
$cameraHashes | Write-Verbose | |
$cameraMoved = @() | |
for($i = 0; $i -lt $cameraHashes.length; $i++) | |
{ | |
$current = $cameraHashes[$i]; | |
if($cameraMoved -contains $current.Hash) | |
{ | |
# It's a duplicate in the camera folder (e.g., one person mailed a copy of a photo to another). | |
SafeMove $current.Path $duplicateFolder.FullName | |
} | |
else | |
{ | |
# It's a one-of-a-kind in the camera folder - file it! | |
SafeMove $current.Path $toFileFolder.FullName | |
$cameraMoved += $current.Hash | |
} | |
} | |
# Get pairs of hash/path for backup folder. | |
$backupHashes = Get-ChildItem -Path $BackupFolder -Recurse -File | Sort-Object -Property FullName | Get-FileHash | |
Write-Verbose "Backup file hashes:" | |
$backupHashes | Write-Verbose | |
$backupsMoved = @() | |
for($i = 0; $i -lt $backupHashes.length; $i++) | |
{ | |
$current = $backupHashes[$i]; | |
if($backupsMoved -contains $current.Hash) | |
{ | |
# It's a duplicate backup - assume it can be removed after filing. | |
SafeMove $current.Path $removeAfterFiling.FullName | |
continue; | |
} | |
$found = $cameraHashes | Where-Object { $_.Hash -eq $current.Hash } | |
if ($found -ne $NULL) | |
{ | |
# It's a backup of an item found in the camera folder - assume it can be removed after filing. | |
SafeMove $current.Path $removeAfterFiling.FullName | |
$backupsMoved += $current.Hash | |
continue; | |
} | |
# It wasn't a duplicate backup and it wasn't in the camera folder - possible recovery? | |
SafeMove $current.Path $onlyInBackupFolder.FullName | |
$backupsMoved += $current.Hash | |
} | |
foreach($location in ($toFileFolder.FullName, $onlyInBackupFolder.FullName, $duplicateFolder.FullName, $removeAfterFiling.FullName)) | |
{ | |
Push-Location $location | |
& $ExifTool "-FileName<DateTimeOriginal" -d "%Y%m%d_%H%M%S%%-c.%%e" *.jpg | |
Pop-Location | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment