first commit

This commit is contained in:
Chris Stuurman 2025-01-06 22:35:02 +02:00
commit 6e9fc6e849
2 changed files with 467 additions and 0 deletions

1
README.md Normal file
View File

@ -0,0 +1 @@


466
organisephotos.ps1 Normal file
View File

@ -0,0 +1,466 @@
############### GLOBAL ##############
$LogPath = "C:/Temp/OrgPhonePhotos.log"
############### MAIN ###############
# main function
#
$Main =
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $SourceFolder,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $DestFolder,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $FolderForDuplicates,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $LogFile
)
$header = "$("-"*80)`n`n$(Get-Date -UFormat ""%A %Y-%m-%d %T"")`n"
Write-Output $header | Tee-Object -FilePath $LogFile -Append
$LogPath = (Get-Item $LogFile).FullName
validatedest $SourceFolder $DestFolder $FolderForDuplicates
cleanfolder $SourceFolder
remove-emptyfolders $SourceFolder
$medialist=[System.Collections.Generic.List[object]]::new()
$Files_jpg = Get-ChildItem -Path $SourceFolder -File -Filter "*.jpg" -Recurse
$Files_raw = Get-ChildItem -Path $SourceFolder -File -Filter "*.cr?" -Recurse
ForEach ($File in $Files_jpg) {
$medialist.Add($File)
}
ForEach ($File in $Files_raw) {
$medialist.Add($File)
}
foreach ($mediafile in $medialist) {
$xfoutput = @(C:\Tools\exiftool\exiftool.exe $mediafile.FullName)
$sourcefilefullname = $mediafile.FullName
# Write-Output $sourcefilefullname | Tee-Object -FilePath $LogPath -Append
$exiflist = @{}
foreach ($tagvalpair in $xfoutput) {
$arr=@($tagvalpair -split ":", 2)
$key=$arr[0] -replace ' ' -replace '/','_'
$value=$arr[1].Trim()
$exiflist[$key] = $value
}
$CreateDate = cv2datetime $exiflist['CreateDate']
$Date_TimeOriginal = cv2datetime $exiflist['Date_TimeOriginal']
$FileCreationDate_Time = cv2datetime $exiflist['FileCreationDate_Time']
$FileModificationDate_Time = cv2datetime $exiflist['FileModificationDate_Time']
$FileAccessDate_Time = cv2datetime $exiflist['FileAccessDate_Time']
$skip = $false
# $time_arr = ""
if ($null -eq $CreateDate) {
if ($null -eq $Date_TimeOriginal) {
$filenamedate = validatedate $mediafile.Name
if ($null -eq $filenamedate) {
$earliest = getearliest $FileCreationDate_Time $FileModificationDate_Time $FileAccessDate_Time
$hint_time = $earliest.ToString('yyyy-MM-dd HH:mm:ss')
Write-Output "Skipped ""$sourcefilefullname"" : File has no usable ExifData! Earliest file time is $hint_time" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor DarkRed
$skip = $true
}
else {
Write-Output """$sourcefilefullname"" : File has no usable ExifData. Using date in filename." | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Yellow
$datestr = $filenamedate.ToString('yyyyMMdd')
}
}
else {
$datestr = $Date_TimeOriginal.ToString('yyyyMMdd')
# $time_arr = @($Date_TimeOriginal -split " ", 2)
# $datestr = $time_arr[0] -replace ":"
# if (($null -eq $datestr) -or ($datestr -eq "")) {
# Write-Output "Skipped ""$sourcefilefullname"" : File has no usable ExifData!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor DarkRed
# $skip = $true
# }
}
}
else {
$datestr = $CreateDate.ToString('yyyyMMdd')
# $time_arr = @($CreateDate -split " ", 2)
# $datestr = $time_arr[0] -replace ":"
# if (($null -eq $datestr) -or ($datestr -eq "")) {
# $time_arr = @($Date_TimeOriginal -split " ", 2)
# $datestr = $time_arr[0] -replace ":"
# if (($null -eq $datestr) -or ($datestr -eq "")) {
# Write-Output "Skipped ""$sourcefilefullname"" : File has no usable ExifData!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor DarkRed
# $skip = $true
# }
# }
}
$sourcefiledir = "$($mediafile.Directory)"
#Write-Output $time_arr
if (-not($skip)) {
#Write-Output "$($mediafile.Name) $datestr"
movetosubfolder "$($mediafile.Name)" "$datestr" "$sourcefiledir" "$DestFolder" "$FolderForDuplicates"
remove-emptyfolder "$sourcefiledir"
}
}
}
Function validatedate {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false)]
[string] $datetimestr
)
if (($null -eq $datetimestr) -or ($datetimestr -eq "")) {
return $null
}
if ($datetimestr.Length -le 13) {
return $null
}
$datestart = 0
$stryear = $datetimestr.substring($datestart,4)
if ($stryear -eq "IMG-") {
$datestart = 4
$stryear = $datetimestr.substring($datestart,4)
if ($datetimestr.Length -le 17) {
return $null
}
}
$strmonth = $datetimestr.substring($datestart + 4,2)
$strday = $datetimestr.substring($datestart + 6,2)
$year = 0
$month = 0
$day = 0
if (-not [int]::TryParse($stryear, [ref] $year)) {
return $null
}
if (-not [int]::TryParse($strmonth, [ref] $month)) {
return $null
}
if (-not [int]::TryParse($strday, [ref] $day)) {
return $null
}
if ($year -lt 1 -or $month -lt 1 -or $day -lt 1) {
return $null
}
try {
$datetime =[DateTime]"$stryear-$strmonth-$strday"
}
catch {
return $null
}
$now = Get-Date
if ($datetime -gt $now) {
return $null
}
return $datetime
}
Function getearliest {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false)]
[string] $datetimestr1,
[Parameter(Mandatory=$false)]
[string] $datetimestr2,
[Parameter(Mandatory=$false)]
[string] $datetimestr3
)
$result = [long]::MaxValue
if (-Not(($null -eq $datetimestr1) -or ($datetimestr1 -eq ""))) {
$datetime1 = [DateTime]$datetimestr1
$time = $datetime1.Ticks
if ($time -lt $result) {
$result = $time
}
}
if (-Not(($null -eq $datetimestr2) -or ($datetimestr2 -eq ""))) {
$datetime2 = [DateTime]$datetimestr2
$time = $datetime2.Ticks
if ($time -lt $result) {
$result = $time
}
}
if (-Not(($null -eq $datetimestr3) -or ($datetimestr3 -eq ""))) {
$datetime3 = [DateTime]$datetimestr3
$time = $datetime3.Ticks
if ($time -lt $result) {
$result = $time
}
}
if ($result -eq [long]::MaxValue) {
return $null
}
return [DateTime]$result
}
Function cv2datetime {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false)]
[string] $datetimestr
)
if (($null -eq $datetimestr) -or ($datetimestr -eq "")) {
return $null
}
$time_arr = @($datetimestr -split " ", 2)
$datestr = $time_arr[0] -replace ":"
$datestr = $datestr.Trim()
if (($null -eq $datestr) -or ($datestr -eq "")) {
$datestr = ""
}
else {
$datestr = $time_arr[0] -replace ":","-"
}
$timestr = $time_arr[1] -replace ":"
$timestr = $timestr.Trim()
if (($null -eq $timestr) -or ($timestr -eq "")) {
$timestr = ""
}
else {
$timestr = $time_arr[1]
}
$result = "$datestr $timestr".Trim()
if ($result -eq "") {
return $null
}
return [DateTime]$result
}
Function cleanfolder {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $folder
)
$filelist = Get-ChildItem -Path $SourceFolder -File -Filter "ZbThumbnail.info" -Recurse
foreach ($file in $filelist) {
deletefile "$($file.FullName)"
}
$filelist = Get-ChildItem -Path $SourceFolder -File -Filter "Thumbs.db" -Recurse
foreach ($file in $filelist) {
deletefile "$($file.FullName)"
}
}
Function deletefile {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $filepath
)
if(Test-Path -Path $filepath -PathType Leaf) {
Remove-Item $filepath
Write-Output "$filepath deleted!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Magenta
}
}
Function remove-emptyfolders {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $folder
)
$folderlist = Get-ChildItem -Path $folder -Directory -Force -Recurse | Sort-Object FullName -Descending
foreach ($item in $folderlist) {
$folderdetail = $item.GetFileSystemInfos() | Measure-Object
If ($folderdetail.Count -eq 0) {
deletefolder "$($item.FullName)"
}
}
}
Function remove-emptyfolder {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $folder
)
$count = (Get-ChildItem -Path $folder -Force | Measure-Object).Count
If ($count -eq 0) {
deletefolder "$folder"
}
}
Function deletefolder {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $folderpath
)
if(Test-Path -Path $folderpath -PathType Container) {
Remove-Item $folderpath
Write-Output "$folderpath deleted!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor DarkMagenta
}
}
Function validatedest {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $sourcefolder,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $destfolder,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $duplicatesfolder
)
if(-Not(Test-Path $sourcefolder)) {
Write-Output "Fatal: Source folder ""$sourcefolder"" does not exist!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
if(-Not(Test-Path $destfolder)) {
Write-Output "Fatal: Destination folder ""$destfolder"" does not exist!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
if(-Not(Test-Path $duplicatesfolder)) {
Write-Output "Fatal: Duplicates folder ""$duplicatesfolder"" does not exist!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
do {
$temp = $sourcefolder
$sourcefolder = $temp.TrimEnd('\').TrimEnd('/')
} while ($temp -ne $sourcefolder)
do {
$temp = $destfolder
$destfolder = $temp.TrimEnd('\').TrimEnd('/')
} while ($temp -ne $destfolder)
do {
$temp = $duplicatesfolder
$duplicatesfolder = $temp.TrimEnd('\').TrimEnd('/')
} while ($temp -ne $duplicatesfolder)
$sourcedir = Get-Item $sourcefolder
$destdir = Get-Item $destfolder
$duplicatesdir = Get-Item $duplicatesfolder
if ($sourcedir.FullName -eq $destdir.FullName) {
Write-Output "Fatal: Destination folder must not be the same as the source folder!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
if ($sourcedir.FullName -eq $duplicatesdir.FullName) {
Write-Output "Fatal: Duplicates folder must not be the same as the source folder!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
if ($duplicatesdir.FullName -eq $destdir.FullName) {
Write-Output "Fatal: Destination folder must not be the same as the duplicates folder!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
$folderlist = @(Get-ChildItem -Path $sourcedir -Directory -Recurse)
ForEach ($folder in $folderlist) {
if ($folder.FullName -eq $destdir.FullName) {
Write-Output "Fatal: Destination folder must not be a subfolder of the source folder!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
if ($folder.FullName -eq $duplicatesdir.FullName) {
Write-Output "Fatal: Duplicates folder must not be a subfolder of the source folder!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
}
$folderlist = @(Get-ChildItem -Path $destdir -Directory -Recurse)
ForEach ($folder in $folderlist) {
if ($folder.FullName -eq $sourcedir.FullName) {
Write-Output "Fatal: Source folder must not be a subfolder of the destination folder!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
if ($folder.FullName -eq $duplicatesdir.FullName) {
Write-Output "Fatal: Duplicates folder must not be a subfolder of the destination folder!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
}
$folderlist = @(Get-ChildItem -Path $duplicatesdir -Directory -Recurse)
ForEach ($folder in $folderlist) {
if ($folder.FullName -eq $sourcedir.FullName) {
Write-Output "Fatal: Source folder must not be a subfolder of the duplicates folder!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
if ($folder.FullName -eq $destdir.FullName) {
Write-Output "Fatal: Destination folder must not be a subfolder of the duplicates folder!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Red
exit 1
}
}
}
Function movetosubfolder
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $mediafile,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $datestr,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $sourcefolder,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $destfolder,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $duplicates
)
$sourceitem="$sourcefolder/$mediafile"
# 4 = length of extension + minimum length of filename = 8+4+1
if ($mediafile.Length -le 4) {
Write-Output "Skipped ""$sourceitem"" : File name too short!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor DarkYellow
}
else {
$stryear=$datestr.substring(0,4)
$strmonth=$datestr.substring(4,2)
$strday=$datestr.substring(6,2)
$year=[int]$stryear
$month=[int]$strmonth
$day=[int]$strday
if ($year -lt 1 -or $month -lt 1 -or $day -lt 1) {
Write-Output "Skipped ""$sourceitem"" : File ExifData has invalid date!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor DarkYellow
}
else {
$mediadatestr = "$stryear-$strmonth-$strday"
$mediadate = [DateTime]"$mediadatestr"
$mediasubfolder = "$stryear`_$strmonth`_$strday"
$destdir="$destfolder\$mediasubfolder"
$dupldir="$duplicates\$mediasubfolder"
$todaystr=Get-Date -UFormat "%Y-%m-%d"
$todaydate = [DateTime]"$todaystr"
if ($mediadate -gt $todaydate) {
Write-Output "Skipped ""$sourceitem"" : File ExifData has future date!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor DarkYellow
}
else {
$destitem="$destdir/$mediafile"
$duplitem="$dupldir/$mediafile"
if (-Not(Test-Path -Path "$destdir")) {
Write-Output "$destdir" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Green
New-Item -ItemType "Directory" -Path "$destdir"
}
if(Test-Path $destitem) {
if(Test-Path $duplitem) {
Write-Output "Skipped ""$sourceitem"" : File exists in duplicates subfolder ""$mediasubfolder""!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Yellow
}
else {
if (-Not(Test-Path -Path "$dupldir")) {
Write-Output "$dupldir" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Green
New-Item -ItemType "Directory" -Path "$dupldir"
}
Move-Item -Path "$sourceitem" -Destination "$dupldir/"
Write-Output """$sourceitem"" moved to duplicates subfolder ""$mediasubfolder""!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor DarkYellow
}
}
else {
Move-Item -Path "$sourceitem" -Destination "$destdir/"
Write-Output """$sourceitem"" moved to destination subfolder ""$mediasubfolder""!" | Tee-Object -FilePath $LogPath -Append | Write-Host -ForegroundColor Green
}
}
}
}
}
###################################
# we call main proc from here
& $Main @Args