-
-
Save davidbeechey/f68c9d4fca5f3b7b6036703824d6e907 to your computer and use it in GitHub Desktop.
A PowerShell script to organize photos by date taken
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
Param( | |
[string]$source, | |
[string]$dest, | |
[string]$format = "yyyy/yyyy-MM/yyyy-MM-dd" | |
) | |
$shell = New-Object -ComObject Shell.Application | |
function Get-File-Date { | |
[CmdletBinding()] | |
Param ( | |
$object | |
) | |
$dir = $shell.NameSpace( $object.Directory.FullName ) | |
$file = $dir.ParseName( $object.Name ) | |
# First see if we have Date Taken, which is at index 12 | |
$date = Get-Date-Property-Value $dir $file 12 | |
if ($null -eq $date) { | |
# If we don't have Date Taken, then find the oldest date from all date properties | |
0..287 | ForEach-Object { | |
$name = $dir.GetDetailsof($dir.items, $_) | |
if ( $name -match '(date)|(created)') { | |
# Only get value if date field because the GetDetailsOf call is expensive | |
$tmp = Get-Date-Property-Value $dir $file $_ | |
if ( ($null -ne $tmp) -and (($null -eq $date) -or ($tmp -lt $date))) { | |
$date = $tmp | |
} | |
} | |
} | |
} | |
return $date | |
} | |
function Get-Date-Property-Value { | |
[CmdletBinding()] | |
Param ( | |
$dir, | |
$file, | |
$index | |
) | |
$value = ($dir.GetDetailsof($file, $index) -replace "`u{200e}") -replace "`u{200f}" | |
if ($value -and $value -ne '' -and $value -match '../../.... ..:..') { | |
return [DateTime]::ParseExact($value, 'dd/MM/yyyy HH:mm', $null) | |
} | |
return $null | |
} | |
Get-ChildItem -Attributes !Directory $source -Recurse | | |
Foreach-Object { | |
Write-Host "Processing $_" | |
$date = Get-File-Date $_ | |
if ($date) { | |
$destinationFolder = Get-Date -Date $date -Format $format | |
$destinationPath = Join-Path -Path $dest -ChildPath $destinationFolder | |
# If the file is already in the right place, then skip it | |
$newFullName = Join-Path -Path $destinationPath -ChildPath $_.Name | |
if ($_.FullName -eq $newFullName) { | |
Write-Host "Skipping: Source file and destination files are at the same location. $_" | |
return | |
} | |
# See if the destination file (hard link) exists and skip if it does | |
if (Test-Path $newFullName) { | |
Write-Host "Skipping: Destination file already exists. $_" | |
return | |
} | |
Write-Host "Creating hard link of $_ in $newFullName" | |
# Create the destination directory if it doesn't exist | |
if (!(Test-Path $destinationPath)) { | |
New-Item -ItemType Directory -Force -Path $destinationPath | |
} | |
$path = ($destinationPath + "\" + $_.Name) | |
$target = ($_.DirectoryName + "\" + $_.Name) | |
# Creates a hard link https://learn.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions#hard-links | |
New-Item -ItemType HardLink -Path $path -Target $target | out-null | |
# OR: move the files instead of creating a hard link | |
# robocopy $_.DirectoryName $destinationPath $_.Name /mov | out-null | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Changes from the original gist:
out-null
for cleaner output.