Have you ever downloaded a punch of images, MOV, and MP4 from iCloud to find that Windows displays their Creation Dates as the same value of your download time. It’s difficult to group photos in such views. Although Explorer does have the feature to display ‘Date taken’ attribute for image files by enabling certain ‘columns,’ such equivalent labeling for videos are hard to locate. You’re in luck, I’ve written a PoSH function to address this very issue by adding date stamps onto file names for easy scanning.
# renamePhotosByDateTaken.ps1
# Version: 0.0.2
# Author: KimConnect.com
$mediaDirectory='\\tsclient\Desktop\iCloud Photos'
function renameMediaFilesByAddingDates{
param(
[string]$mediaDirectory,
[string]$regexPhotoFile='(JPG|PNG|GIF|BMP|TIFF|JPEG)+$',
[string]$regexVideoFile='(MOV|MP4|AVI|MPG|MPEG|WMV|QT|FLV|SWF)+$'
)
Function useCultureOnScript{
param(
[System.Globalization.CultureInfo]$culture = 'en-US',
[ScriptBlock]$script
)
$originalCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture
trap{
[System.Threading.Thread]::CurrentThread.CurrentCulture=$originalCulture
}
[System.Threading.Thread]::CurrentThread.CurrentCulture=$culture
Invoke-Command $script
[System.Threading.Thread]::CurrentThread.CurrentCulture=$originalCulture
}
function getMediaDateTaken($filePath,$propertyIndex){
if(!$propertyIndex){
$propertyIndex=switch -regex ($filePath){
'(JPG|PNG|GIF|BMP|TIFF|JPEG)+$' {12}
'(MOV|MP4|AVI|MPG|MPEG|WMV|QT|FLV|SWF)+$' {208} # Assuming Windows 10
}
}
try{
$shell = New-Object -COMObject Shell.Application
$shellFolder = $shell.Namespace($(split-path $filePath))
$shellFile = $shellFolder.ParseName($(split-path $filePath -leaf))
# How to view all attributes: 0..287|%{'{0}: {1}' -f $_,$shellFolder.GetDetailsOf($shellFile,$_)}
$dateTakenUnicode=$shellFolder.GetDetailsOf($shellFile, $propertyIndex)
[datetime]$dateTaken=$dateTakenUnicode -replace '[^\d^\:^\w^\/^\s]'
return $dateTaken
}catch{
write-host $_
return $false
}
}
# This function reveals the index number of properties by names
function getFileAttributes ([string[]]$filePath){
$shell = New-Object -COMObject Shell.Application
$shellFolder = $shell.Namespace($(split-path $filePath))
$shellFile = $shellFolder.ParseName($(split-path $filePath -leaf))
$attributes = @()
0..287 | ForEach-Object {
$propertyName = $shellFolder.GetDetailsOf($shellFolder, $_)
$propertyValue = $shellFolder.GetDetailsOf($shellFile, $_)
#write-host "$propertyName`: $propertyValue"
if($propertyName -ne '' -and $null -ne $propertyName){
# Avoid this error:
#Item has already been added. Key in dictionary: '' Key being added: ''
#At line:5 char:13
#+ $attributes+=@{$propertyName=$propertyValue}
#+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# + CategoryInfo : OperationStopped: (:) [], ArgumentException
# + FullyQualifiedErrorId : System.ArgumentException
$attributes+=[pscustomobject]@{
index=$_
name=$propertyName
value=$propertyValue
}
}
}
$shell=$null
return $attributes
}
if (test-path "$mediaDirectory\Thumbs.db:encryptable" -ea SilentlyContinue){
# remove-item -literalPath "$mediaDirectory\Thumbs.db:encryptable"
write-warning "Retry this program after this file is deleted: $targetFile"
return $false
}
#$regexIllegalCharsInName='(\<|\>|:|"|/|\?|\*|\\)'
$regexMediaFile=($regexPhotoFile -replace '[)+$]') +'|'+ ($regexVideoFile -replace '\(')
$allMediaFiles=Get-ChildItem $mediaDirectory -Recurse|?{!$_.PSisContainer -and $_.FullName -match $regexMediaFile}
<# Note on an file name issue in media directories as Windows autogens a file named 'Thumbs.db:encryptable' that is an invalid file name
Error:
Get-ChildItem : The given path's format is not supported.
At line:1 char:11
+ $allFiles=Get-ChildItem $mediaDirectory -ea SilentlyContinue
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-ChildItem], NotSupportedException
+ FullyQualifiedErrorId : System.NotSupportedException,Microsoft.PowerShell.Commands.GetChildItemCommand
Solution:
Remove any file name that has illegal characters, specifically 'Thumbs.db:encryptable'
#>
if ($allMediaFiles){
$fileAttributes=getFileAttributes $allFiles[0].FullName # generate this variable once to speed up runtime
[hashtable]$mediaFiles=@{}
write-host "Retrieving the creation date of each media file..."
foreach ($item in $allMediaFiles){
$filePath=$item.FullName
$fileName=$item.Name
$propertyIndex=switch -regex ($filePath){
$regexPhotoFile {($fileAttributes|?{$_.Name -eq 'Date taken'}).index }
$regexVideoFile {($fileAttributes|?{$_.Name -eq 'Media created'}).index} # Assuming Windows 10
}
$dateCreated=.{
if($filePath -match $regexMediaFile){
$currentCulture=([System.Threading.Thread]::CurrentThread).CurrentCulture.Name
return $(useCultureOnScript $currentCulture {getMediaDateTaken $filePath $propertyIndex})
}else{
#return $item.CreationTime
return $false
}
}
if($dateCreated){
$mediaFiles.add($filePath,$dateCreated)
write-host "$fileName`: $dateCreated"
}else{
write-host "$fileName`: SKIPPED!"
}
}
#$maxFileNameLength=255 # Assuming legacy Windows 7 compatibility
$maxPathNameLength=260
$count=0
foreach ($file in $mediaFiles.GetEnumerator()){
try{
$filePath=$file.Key
$creationDateStamp=($file.Value).tostring("MM-dd-yyyy-HHmm")
$maxCharFeasible=$maxPathNameLength-$filePath.length
$newFilePath=if($maxCharFeasible -ge 16){
$filePath.Insert($filePath.LastIndexOf('.'),'-'+$creationDateStamp)
}elseif($maxCharFeasible -ge 11){
$filePath.Insert($filePath.LastIndexOf('.'),'-'+($file.Value).tostring('MM-dd-yyyy'))
}elseif($maxCharFeasible -ge 8){
$filePath.Insert($filePath.LastIndexOf('.'),'-'+($file.Value).tostring('MM-yyyy'))
}elseif($maxCharFeasible -ge 5){
$filePath.Insert($filePath.LastIndexOf('.'),'-'+($file.Value).tostring('yyyy'))
}else{
$filePath
}
# Confirm the first 3 files
if($count++ -lt 3){
write-host "`r`nRenaming $filePath to`r`n$newFilePath ?`r`n--- Ctrl+C to cancel ---"
pause
}else{
write-host "$newFilePath"
}
rename-item $filePath $newFilePath -force
}catch{
Write-Host $_
}
}
write-host "Done."
return $true
}else{
write-host "Unable to retrieve list of media files from directory $mediaDirectory"
return $false
}
}
renameMediaFilesByAddingDates $mediaDirectory
# renamePhotosByDateTaken.ps1
# Version: 0.0.1
# Author: KimConnect.com
$mediaDirectory='\\tsclient\Desktop\iCloud Photos'
function renameMediaFilesByAddingDates{
param(
[string]$mediaDirectory,
[string]$regexPhotoFile='(JPG|PNG|GIF|BMP|TIFF|JPEG)+$',
[string]$regexVideoFile='(MOV|MP4|AVI|MPG|MPEG|WMV|QT|FLV|SWF)+$'
)
function getMediaDateTaken($filePath,$propertyIndex){
if(!$propertyIndex){
$propertyIndex=switch -regex ($filePath){
'(JPG|PNG|GIF|BMP|TIFF|JPEG)+$' {12}
'(MOV|MP4|AVI|MPG|MPEG|WMV|QT|FLV|SWF)+$' {208} # Assuming Windows 10
}
}
try{
$shell = New-Object -COMObject Shell.Application
$shellFolder = $shell.Namespace($(split-path $filePath))
$shellFile = $shellFolder.ParseName($(split-path $filePath -leaf))
# How to view all attributes: 0..287|%{'{0}: {1}' -f $_,$shellFolder.GetDetailsOf($shellFile,$_)}
$dateTakenUnicode=$shellFolder.GetDetailsOf($shellFile, $propertyIndex)
[datetime]$dateTaken=$dateTakenUnicode -replace '[^\d^\:^\w^\/^\s]'
return $dateTaken
}catch{
write-host $_
return $false
}
}
# This function reveals the index number of properties by names
function getFileAttributes ([string[]]$filePath){
$shell = New-Object -COMObject Shell.Application
$shellFolder = $shell.Namespace($(split-path $filePath))
$shellFile = $shellFolder.ParseName($(split-path $filePath -leaf))
$attributes = @()
0..287 | ForEach-Object {
$propertyName = $shellFolder.GetDetailsOf($shellFolder, $_)
$propertyValue = $shellFolder.GetDetailsOf($shellFile, $_)
#write-host "$propertyName`: $propertyValue"
if($propertyName -ne '' -and $null -ne $propertyName){
# Avoid this error:
#Item has already been added. Key in dictionary: '' Key being added: ''
#At line:5 char:13
#+ $attributes+=@{$propertyName=$propertyValue}
#+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# + CategoryInfo : OperationStopped: (:) [], ArgumentException
# + FullyQualifiedErrorId : System.ArgumentException
$attributes+=[pscustomobject]@{
index=$_
name=$propertyName
value=$propertyValue
}
}
}
$shell=$null
return $attributes
}
if (test-path "$mediaDirectory\Thumbs.db:encryptable" -ea SilentlyContinue){
# remove-item -literalPath "$mediaDirectory\Thumbs.db:encryptable"
write-warning "Retry this program after this file is deleted: $targetFile"
return $false
}
#$regexIllegalCharsInName='(\<|\>|:|"|/|\?|\*|\\)'
$regexMediaFile=($regexPhotoFile -replace '[)+$]') +'|'+ ($regexVideoFile -replace '\(')
$allMediaFiles=Get-ChildItem $mediaDirectory -Recurse|?{!$_.PSisContainer -and $_.FullName -match $regexMediaFile}
<# Note on an file name issue in media directories as Windows autogens a file named 'Thumbs.db:encryptable' that is an invalid file name
Error:
Get-ChildItem : The given path's format is not supported.
At line:1 char:11
+ $allFiles=Get-ChildItem $mediaDirectory -ea SilentlyContinue
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-ChildItem], NotSupportedException
+ FullyQualifiedErrorId : System.NotSupportedException,Microsoft.PowerShell.Commands.GetChildItemCommand
Solution:
Remove any file name that has illegal characters, specifically 'Thumbs.db:encryptable'
#>
if ($allMediaFiles){
$fileAttributes=getFileAttributes $allFiles[0].FullName # generate this variable once to speed up runtime
[hashtable]$mediaFiles=@{}
write-host "Retrieving the creation date of each media file..."
foreach ($item in $allMediaFiles){
$filePath=$item.FullName
$fileName=$item.Name
$propertyIndex=switch -regex ($filePath){
$regexPhotoFile {($fileAttributes|?{$_.Name -eq 'Date taken'}).index }
$regexVideoFile {($fileAttributes|?{$_.Name -eq 'Media created'}).index} # Assuming Windows 10
}
$dateCreated=.{
if($filePath -match $regexMediaFile){
return getMediaDateTaken $filePath $propertyIndex
}else{
#return $item.CreationTime
return $false
}
}
if($dateCreated){
$mediaFiles.add($filePath,$dateCreated)
write-host "$fileName`: $dateCreated"
}else{
write-host "$fileName`: SKIPPED!"
}
}
#$maxFileNameLength=255 # Assuming legacy Windows 7 compatibility
$maxPathNameLength=260
$count=0
foreach ($file in $mediaFiles.GetEnumerator()){
try{
$filePath=$file.Key
$creationDateStamp=($file.Value).tostring("MM-dd-yyyy-HHmm")
$maxCharFeasible=$maxPathNameLength-$filePath.length
$newFilePath=if($maxCharFeasible -ge 16){
$filePath.Insert($filePath.LastIndexOf('.'),'-'+$creationDateStamp)
}elseif($maxCharFeasible -ge 11){
$filePath.Insert($filePath.LastIndexOf('.'),'-'+($file.Value).tostring('MM-dd-yyyy'))
}elseif($maxCharFeasible -ge 8){
$filePath.Insert($filePath.LastIndexOf('.'),'-'+($file.Value).tostring('MM-yyyy'))
}elseif($maxCharFeasible -ge 5){
$filePath.Insert($filePath.LastIndexOf('.'),'-'+($file.Value).tostring('yyyy'))
}else{
$filePath
}
# Confirm the first 3 files
if($count++ -lt 3){
write-host "`r`nRenaming $filePath to`r`n$newFilePath ?`r`n--- Ctrl+C to cancel ---"
pause
}else{
write-host "$newFilePath"
}
rename-item $filePath $newFilePath -force
}catch{
Write-Host $_
}
}
write-host "Done."
return $true
}else{
write-host "Unable to retrieve list of media files from directory $mediaDirectory"
return $false
}
}
renameMediaFilesByAddingDates $mediaDirectory
<#
Sample Output:
------------------------------
Renaming \\tsclient\Desktop\iCloud Photos\IMG_0379.JPEG to
\\tsclient\Desktop\iCloud Photos\IMG_0379-11-23-2019-2010.JPEG?
--- Ctrl+C to cancel ---
Press Enter to continue...:
Renaming \\tsclient\Desktop\iCloud Photos\IMG_0570.JPEG to
\\tsclient\Desktop\iCloud Photos\IMG_0570-01-01-2020-1535.JPEG?
--- Ctrl+C to cancel ---
Press Enter to continue...:
Renaming \\tsclient\Desktop\iCloud Photos\IMG_0392.JPEG to
\\tsclient\Desktop\iCloud Photos\IMG_0392-11-23-2019-2012.JPEG?
--- Ctrl+C to cancel ---
Press Enter to continue...:
--- Truncated for brevity ---
Done.
#>
Categories:
Jeroen
I get the following error:
Cannot convert value “1472022 14:54” to type “System.DateTime”. Error: “De tekenreeks is niet als geldige DateTime herkend.”
(String is not recognized as a valid DateTime)
Using Win10.
Do you have any idea?
kimconnect
I’ve modified the script to account for cultures other than ‘en-US’. It’s in “Version 0.02” – please test and let me know whether this would work on your system.
Rob
I have exactly the same error
kimconnect
Updated in version 0.02. Sorry, I don’t have a machine nor sample images with different cultures to test. I can only hope that the revised version would work for you.