first commit
This commit is contained in:
commit
6e9fc6e849
466
organisephotos.ps1
Normal file
466
organisephotos.ps1
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user