-
-
Save dkittell/746e69967a7f4d0f0c52 to your computer and use it in GitHub Desktop.
<# | |
.SYNOPSIS | |
Renames pictures. | |
.DESCRIPTION | |
The Rename-Pictures cmdlet to rename pictures to a format where the file creation time is first | |
in the name in this format: . The idea is that | |
.PARAMETER Path | |
Specifies the path to the folder where image files are located. Default is current location (Get-Location). | |
.EXAMPLE | |
PS C:\> Rename-Pictures | |
Description: | |
Renames all the pictures in folder you are in. | |
.EXAMPLE | |
PS C:\> Rename-Pictures -Path C:\Folder\Pics\ | |
Description: | |
Renames all the pictures in the given folder path. | |
.NOTES | |
Author: Magnus Ringkjøb | |
E-mail: [email protected] | |
Image Dimensions Added By David Kittell - Kittell.net | |
#> | |
Param( | |
[string]$Path | |
) | |
if ([string]::IsNullOrWhiteSpace($Path)) { | |
$Path = (Get-Location) | |
} | |
$BackupFileName = '_backupdata.csv' | |
$ErrorFileName = '_errors.csv' | |
[reflection.assembly]::LoadFile("C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Drawing.dll") | |
$Script:ErrorLogMsg = $Null | |
$Script:CorrectPath = $Null | |
$Path | |
$ImgsFound = (dir $Path -Include ('*.jpeg', '*.png', '*.gif', '*.jpg', '*.bmp', '*.png') -Recurse | Select-Object -Property FullName, Name, BaseName, Extension) | |
# If any file was found | |
# Array that takes in the old- and the new filename. This is used for saving a backup to .csv | |
$BackupData = @() | |
# Loops through the images found | |
foreach ($Img in $ImgsFound) { | |
# Gets image data | |
$ImgData = New-Object System.Drawing.Bitmap($Img.FullName) | |
$ImgDimensions = $ImgData.Width.ToString() + $("x") + $ImgData.Height.ToString() | |
try { | |
# Gets 'Date Taken' in bytes | |
[byte[]]$ImgBytes = $ImgData.GetPropertyItem(36867).Value | |
} | |
catch [System.Exception], [System.IO.IOException] { | |
[string]$ErrorMessage = ( | |
(Get-Date).ToString('yyyyMMdd HH:mm:ss') + "`tERROR`tDid not change name for " + $Img.Name + ". Reason: " + $Error | |
) | |
$Script:ErrorLogMsg += $ErrorMessage + "`r`n" | |
Write-Host -ForegroundColor Red -Object $ErrorMessage | |
# Clears any error messages | |
$Error.Clear() | |
# No reason to continue. Move on to the next file | |
continue | |
} | |
# Gets the date and time from bytes | |
[string]$dateString = [System.Text.Encoding]::ASCII.GetString($ImgBytes) | |
# Formats the date to the desired format | |
[string]$dateTaken = [datetime]::ParseExact($dateString, "yyyy:MM:dd HH:mm:ss`0", $Null).ToString('yyyy-MM-dd_HH.mm.ss.ms') | |
# The new file name for the image | |
# [string]$NewFileName = $dateTaken + '-' + $Img.Name | |
[string]$NewFileName = $dateTaken + "_" + $ImgDimensions + [System.IO.Path]::GetExtension($Img.Name) | |
$ImgData.Dispose() | |
try { | |
Rename-Item -NewName $NewFileName -Path $Img.FullName -ErrorAction Stop | |
Write-Host -Object ("Renamed " + $Img.Name + " to " + $NewFileName) | |
} | |
catch { | |
[string]$ErrorMessage = ( | |
(Get-Date).ToString('yyyyMMdd HH:mm:ss') + "`tERROR`tDid not change name for " + $Img.Name + ". Reason: " + $Error | |
) | |
$Script:ErrorLogMsg += $ErrorMessage + "`r`n" | |
Write-Host -ForegroundColor Red -Object $ErrorMessage | |
# Clears any previous error messages | |
$Error.Clear() | |
# No reason to continue. Move on to the next file | |
continue | |
} | |
# Collect data to be added to the backup file | |
$BUData = New-Object -TypeName System.Object | |
$BUData | Add-Member -MemberType NoteProperty -Name "OldName" -Value $Img.Name | |
$BUData | Add-Member -MemberType NoteProperty -Name "NewName" -Value $NewFileName | |
# Add data to backup collection | |
$BackupData += $BUData | |
try { | |
$BackupData | Export-Csv -NoTypeInformation -Path "$Path\\$BackupFileName" | |
} | |
catch [System.Exception] { | |
[string]$ErrorMessage = "((Get-Date).ToString('yyyyMMdd HH:mm:ss'))`tERROR`tCould not create $Path $BackupFileName Reason: " + $Error | |
$Script:ErrorLogMsg += $ErrorMessage + "`r`n" | |
# Clears any error messages | |
$Error.Clear() | |
} | |
} | |
# If there was a problem during the run: | |
# Print to file, and let user know | |
if ($Script:ErrorLogMsg -ne $Null) { | |
$ErrorLogMsg | Export-Csv -NoTypeInformation -Path "$Path\\$ErrorFileName" | |
Write-Host -ForegroundColor Red -Object ( | |
"Errors were found. Please check " + $Path + "_errors.log" | |
) | |
} | |
Thanks again. Did a fork with my addition.
I have just tried to use this script and run into a couple of issues (is Win 11 supported?)
Get-ChildItem : Cannot retrieve the dynamic parameters for the cmdlet. Cannot process argument because
the value of argument "path" is not valid. Change the value of the "path" argument and run the operation
again.
- When calling it like file.ps1 -Path mypath it does not work
- When I call it with file.ps1 . to process the local files, it works with manay exceptions, all similar to these
20230924 22:00:35 ERROR Did not change name for 010.jpg. Reason: Exception calling "GetPropertyItem" with "1" argument(s): "Property cannot be found." Cannot retrieve the dynamic parameters for the cmdlet. Cannot process argument because the value of argument "path" is not valid. Change the value of the "path" argument and run the operation again. Cannot find path 'C:\DATOS\VILLA_LEYVA\-Path' because it does not exist. File C:\DATOS\VILLA_LEYVA\timeLine.ps1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
20230924 22:00:36 ERROR Did not change name for 160.jpg. Reason: Exception calling "GetPropertyItem" with "1" argument(s): "Property cannot be found."
20230924 22:00:36 ERROR Did not change name for 165.jpg. Reason: Exception calling "GetPropertyItem" with "1" argument(s): "Property cannot be found."
20230924 22:00:36 ERROR Did not change name for 170.jpg. Reason: Exception calling "GetPropertyItem" with "1" argument(s): "Property cannot be found."
Haven't had any problems on my end with Windows 11 but it is likely needing some tweaks as this was made before 11
Window 11 not working now
`PS D:\NewFolder\iphone-backup> powershell -ExecutionPolicy Bypass -File .\PictureRename.ps1
GAC Version Location
True v4.0.30319 C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\Syste...
Get-ChildItem : Cannot retrieve the dynamic parameters for the cmdlet. Cannot process argument because the value of
argument "path" is not valid. Change the value of the "path" argument and run the operation again.
At D:\NewFolder\iphone-backup\PictureRename.ps1:53 char:26
- ... ImgsFound = Get-ChildItem ($Path + '\*') -Include *.jpeg, *.png, *.g ...
-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
- FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.GetChildItemCommand`
I can look at in a few hours
Should be minimal updates
Finally had a moment to get back to this.
The script now works in Windows 11. The main issue was that since Windows 11 the concatenation of path with the file names process changed I had to change the code to accommodate the needed change
Thanks for that
No problem
how would I be able to get this to work for MP4s as well in the same folder? I tried adding that in the ImgsFound to include '*.mp4' but did not work
Typically video files in general do not have the needed EXIF information.
Appreciate the quick response. Do you have any suggestions on how I could do this with photos and videos inside? Seems powershell can't handle it if I want the date created
ah it looks like videos use "media created" and not "date taken"
Even that "media created" can be misleading depending on the device used.
If you look at the date created value you can get from get-content that may help.
I had a script that would pull that and rename the videos but similar to this powershell script when Windows 11 came out the process changed just enough that it doesn't work.
Get-ChildItem -Path video.mp4 | select Name,CreationTime
If you do a loop within the directory of your video files this could work.
By all means try out each field and see what works best for you
The $ImgData.Dispose()
happens outside of the error handler, which performs continue if there is an error. This puts a lock on the files. Should perhaps be refactored to dispose before continue on next file.
Thanks for this sample, I'm working on an updated script that includes the ability to read LastWriteTime
if there are no EXIF data available.
The
$ImgData.Dispose()
happens outside of the error handler, which performs continue if there is an error. This puts a lock on the files. Should perhaps be refactored to dispose before continue on next file.Thanks for this sample, I'm working on an updated script that includes the ability to read
LastWriteTime
if there are no EXIF data available.
Good catch, I mostly use a bash/shell version of this script now but would love to see your update.
Hi! Just wanted to say thanks for keeping my script alive and mentioning me as the original author 😀
Hi! Just wanted to say thanks for keeping my script alive and mentioning me as the original author 😀
Thank you for doing the initial work, it has been a great script.
No worries here, glad you got the code you needed :)