Skip to content

Instantly share code, notes, and snippets.

@davidbeechey
Forked from jongio/PhotoOrganizer.ps1
Last active June 25, 2023 17:13
Show Gist options
  • Save davidbeechey/f68c9d4fca5f3b7b6036703824d6e907 to your computer and use it in GitHub Desktop.
Save davidbeechey/f68c9d4fca5f3b7b6036703824d6e907 to your computer and use it in GitHub Desktop.
A PowerShell script to organize photos by date taken
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
}
}
@davidbeechey
Copy link
Author

Changes from the original gist:

  • Creates a Hard Link rather than moving the files.
  • Skips files that already exist in the destination, rather than renaming them.
  • Changed to UK date format,
  • Piped output to out-null for cleaner output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment