Skip to content

Instantly share code, notes, and snippets.

@jtmoon79
Last active July 8, 2022 22:59
Show Gist options
  • Save jtmoon79/eea39c3f9b3d37a169286da8f7788512 to your computer and use it in GitHub Desktop.
Save jtmoon79/eea39c3f9b3d37a169286da8f7788512 to your computer and use it in GitHub Desktop.
set filesystem datetime of jpg and mp4 files
#!powershell
<#
.SYNOPSIS
Use exiftool to set file system datetimes values to those found within embedded media
tags of .mp4 and .jpg files.
.DESCRIPTION
Use exiftool (https://exiftool.org/) to set file system datetime attributes
FileModifyDate, FileAccessDate, FileCreateDate.
The various datetime values can be seen with command:
exiftool.exe -time:all -groupHeadings -duplicates file.mp4
.PARAMETER Path
File system path to search for .jpg files and .mp4 files.
.PARAMETER Help
Print the help message and return.
.LINK
https://gist.github.com/jtmoon79/eea39c3f9b3d37a169286da8f7788512
.NOTES
Author: James Thomas Moon
Date: 2022
#>
[Cmdletbinding()]
Param (
[string] $Path,
[switch] $help
)
New-Variable -Name SCRIPT_NAME -Value "set-datetime-of-pics-vids.ps1" -Option ReadOnly -Force
if ($help) {
Get-Help "${PSScriptRoot}\${SCRIPT_NAME}" -Full
Return
}
# save current values, restore at script end
$erroractionpreference_ = $ErrorActionPreference
$ErrorActionPreference = "Stop"
#Set-PSDebug -Trace 1
function PrintDates
{
Param(
[Parameter(Mandatory=$true)][String]$file
)
Write-Verbose "$exiftool -time:all -groupHeadings -short -duplicates '$file'"
& "$exiftool" -time:all -groupHeadings -short -duplicates $file
}
# functions Process*
# set the file system file attributes (`File:FileAccessDate` is typically not writable)
#
# XXX: `File:FileAccessDate` sometimes ends up matching the last changed datetime value,
# i.e. a change to `File:FileModifyDate` has a side-affect of setting `File:FileAccessDate`
# to the same value.
#
# BUG: `File:FileCreateDate` often fails to change, will `ForceCreateDate` help?
# most likely failure is due to the file being on a Unix Samba share, and
# the Samba share settings disallowing this change due to some combination
# of factors
function ForceCreateDate {
Param(
[Parameter(Mandatory=$true)][String]$file
)
$date_lastwritetime = (Get-Item $file).LastWriteTime
(Get-Item $file).CreationTime = $date_lastwritetime
}
function ProcessJPG
{
Param(
[Parameter(Mandatory=$true)][String]$file
)
Write-Verbose "$exiftool '$file'"
# first use `EXIF:DateTimeOriginal` which is sometimes available and sometimes correct
# then use `EXIF:CreateDate` which is sometimes avilable and often correct
# hopefully the most appropriate date is used
#
# XXX: the camera picture file embedded tagging varies tremendously; this works for my
# set of .jpg files
& "$exiftool" -verbose -verbose -extractEmbedded `
'-EXIF:DateTimeOriginal>File:FileCreateDate' `
'-EXIF:CreateDate>File:FileCreateDate' `
'-EXIF:DateTimeOriginal>File:FileModifyDate' `
'-EXIF:CreateDate>File:FileModifyDate' `
$file
}
function ProcessMP4
{
Param(
[Parameter(Mandatory=$true)][String]$file
)
Write-Verbose "$exiftool '$file'"
# set the file system file attributes (`File:FileAccessDate` is typically not writable)
& "$exiftool" -verbose -verbose -extractEmbedded `
'-Quicktime:CreateDate>File:FileCreateDate' `
'-Quicktime:CreateDate>File:FileModifyDate' `
$file
}
function ProcessAVI
{
Param(
[Parameter(Mandatory=$true)][String]$file
)
Write-Verbose "$exiftool '$file'"
# set the file system file attributes (`File:FileAccessDate` is typically not writable)
& "$exiftool" -verbose -verbose -extractEmbedded `
'-RIFF:DateTimeOriginal>File:FileCreateDate' `
'-RIFF:DateTimeOriginal>File:FileModifyDate' `
$file
}
try {
# XXX: fallback path is hardcoded to my location
$exiftool = "$HOME\Apps\exiftool-12.42\exiftool.exe"
if ($null -ne (Get-Command "exiftool.exe" -ErrorAction SilentlyContinue)) {
$exiftool = (
Get-Command "exiftool.exe" -CommandType Application -TotalCount 1 | ForEach-Object {
"$_"
}
)
}
# verify exiftool can run
& "$exiftool" -ver
$compare = [System.StringComparison]::CurrentCultureIgnoreCase
# user passed single .jpg file
if ((Test-Path -Path $Path -PathType Leaf) -AND (
$Path.EndsWith(".jpg", $compare) -OR $Path.EndsWith(".jpeg", $compare)
)
) {
# see datetime attributes before
PrintDates $Path
ProcessJPG $Path
# see datetime attributes after
PrintDates $Path
# user passed single .mp4 file
} elseif ((Test-Path -Path $Path -PathType Leaf) -AND $Path.EndsWith(".mp4", $compare)) {
# see datetime attributes before
PrintDates $Path
ProcessMP4 $Path
# see datetime attributes after
PrintDates $Path
# user passed single .avi file
} elseif ((Test-Path -Path $Path -PathType Leaf) -AND $Path.EndsWith(".avi", $compare)) {
# see datetime attributes before
PrintDates $Path
ProcessAVI $Path
# see datetime attributes after
PrintDates $Path
# user passed directory
} elseif (Test-Path -Path $Path -PathType Container) {
Get-ChildItem $Path -Recurse -File -Filter "*.jpg" `
| Sort-Object -Property FullName `
| ForEach-Object { ProcessJPG $_ }
Get-ChildItem $Path -Recurse -File -Filter "*.jpeg" `
| Sort-Object -Property FullName `
| ForEach-Object { ProcessJPG $_ }
Get-ChildItem $Path -Recurse -File -Filter "*.mp4" `
| Sort-Object -Property FullName `
| ForEach-Object { ProcessMP4 $_ }
Get-ChildItem $Path -Recurse -File -Filter "*.avi" `
| Sort-Object -Property FullName `
| ForEach-Object { ProcessAVI $_ }
# user passed unhandled file
} else {
Write-Error "Unhandled file $Path"
}
} catch {
# set `$ErrorActionPreference` *before* calling `Write-Error`
$ErrorActionPreference = "Continue"
Write-Error $_.ScriptStackTrace
Write-Error -Message $_.Exception.Message
} finally {
# restore to original shell value
$ErrorActionPreference = $erroractionpreference_
# presumably was already 0
#Set-PSDebug -Trace 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment