Reputation: 1585
I'm trying convert a bunch of pictures and videos, but when I convert it to a new format I obviously lose the properties of the original file. I'd like to be able to read the "Date taken" property from the old file and update it on the new one using powershell.
Upvotes: 20
Views: 23991
Reputation: 6527
This PowerShell function, Get-DateTaken
, retrieves the date when a file was taken or as fallback created. It uses the Windows Shell COM object to access file properties that aren't directly accessible through .NET's System.IO classes.
This function is more versatile than using System.Drawing
for a couple of reasons:
It can be used with any type of file, not just image files. System.Drawing
is primarily used for working with images, so it may not be able to retrieve the "Date taken" property for other types of files.
function Get-DateTaken {
[OutputType([datetime])]
param (
[Parameter(Mandatory = $true)]
[System.IO.FileInfo]
$file
)
# $DateFormat = 'dd.MM.yyyy HH:mm'
$DateTakenWinApi = 12
$DateCreatedWinApi = 4
if ($null -eq $Shell) {
$Shell = New-Object -ComObject shell.application
}
$dir = $Shell.Namespace($_.DirectoryName)
$DateTakenString = $dir.GetDetailsOf($dir.ParseName($_.Name), $DateTakenWinApi)
if ($DateTakenString -eq '') {
$DateTakenString = $dir.GetDetailsOf($dir.ParseName($_.Name), $DateCreatedWinApi)
}
# sanitze string
$DateTakenString = $DateTakenString -replace '[^0-9\.\:\ \/]', ''
# parse to DateTime
$DateTaken = Get-Date $DateTakenString # -Format $DateFormat
$DateTaken
}
With synopsis: https://gist.github.com/dhcgn/90c6677f2d6c5c70c123b050b9f9a310
Upvotes: 0
Reputation: 11
This works for me, thanks to the above help and others.
try{
Get-ChildItem C:\YourFolder\Path | Where-Object {$_.extension -eq '.jpg'} |
ForEach-Object {
$path = $_.FullName
Add-Type -AssemblyName System.Drawing
$bitmap = New-Object System.Drawing.Bitmap($path)
$propertyItem = $bitmap.GetPropertyItem(36867)
$bytes = $propertyItem.Value
$string = [System.Text.Encoding]::ASCII.GetString($bytes)
$dateTime = [DateTime]::ParseExact($string,"yyyy:MM:dd HH:mm:ss`0",$Null)
$bitmap.Dispose()
$_.LastWriteTime = $dateTime
$_.CreationTime = $dateTime
}}
finally
{
}
Upvotes: 1
Reputation: 2397
To read and write the "date taken" property of an image, use the following code (building on the answer of @EBGreen):
try
{
$path = "C:\PATH\TO\SomePic.jpg"
$pathModified = "C:\PATH\TO\SomePic_MODIFIED.jpg"
Add-Type -AssemblyName System.Drawing
$bitmap = New-Object System.Drawing.Bitmap($path)
$propertyItem = $bitmap.GetPropertyItem(36867)
$bytes = $propertyItem.Value
$string = [System.Text.Encoding]::ASCII.GetString($bytes)
$dateTime = [DateTime]::ParseExact($string,"yyyy:MM:dd HH:mm:ss`0",$Null)
$dateTimeModified = $dateTime.AddDays(1) # Set new date here
$stringModified = $dateTimeModified.ToString("yyyy:MM:dd HH:mm:ss`0",$Null)
$bytesModified = [System.Text.Encoding]::ASCII.GetBytes($stringModified)
$propertyItem.Value = $bytesModified
$bitmap.SetPropertyItem($propertyItem)
$bitmap.Save($pathModified)
}
finally
{
$bitmap.Dispose()
}
Upvotes: 0
Reputation: 37720
I can't test it right now (don't have any images with XIF data laying around, but I think this should work:
[reflection.assembly]::LoadWithPartialName("System.Drawing")
$pic = New-Object System.Drawing.Bitmap('C:\PATH\TO\SomePic.jpg')
$bitearr = $pic.GetPropertyItem(36867).Value
$string = [System.Text.Encoding]::ASCII.GetString($bitearr)
$DateTime = [datetime]::ParseExact($string,"yyyy:MM:dd HH:mm:ss`0",$Null)
$DateTime
Upvotes: 17
Reputation: 13432
In general, you can access any extended property for a file shown in explorer through the shell GetDetailsOf
method. Here's a short example, adapted from another answer:
$file = Get-Item IMG_0386.jpg
$shellObject = New-Object -ComObject Shell.Application
$directoryObject = $shellObject.NameSpace( $file.Directory.FullName )
$fileObject = $directoryObject.ParseName( $file.Name )
$property = 'Date taken'
for(
$index = 5;
$directoryObject.GetDetailsOf( $directoryObject.Items, $index ) -ne $property;
++$index ) { }
$value = $directoryObject.GetDetailsOf( $fileObject, $index )
However, according to the comments on another question, there is no general-purpose mechanism for setting these properties. The System.Drawing.Bitmap
class that EBGreen mentioned will work for images, but I'm afraid I also do not know of a .NET option for video files.
Upvotes: 7