Skip to content

Instantly share code, notes, and snippets.

@tillig
Created March 29, 2017 00:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tillig/528a6eceaac68b71eae87d798e6dfa1b to your computer and use it in GitHub Desktop.
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
<#
.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