Dodawanie daty do nazwy zdjęcia
Lubię robić zdjęcia. Jakiś czas temu stanąłem przed problemem uporządkowania archiwum zdjęć. Udało mi się to ogarnąć rozdzielając zdjęcia na odpowiedne katalogi. Problem natomiast się skomplikował w sytuacji, gdy zacząłem używać więcej niż jednego urządzenia do fotografowania. Uważam, że zdjęcia powinny być posortowane chronologicznie. Wpadłem zatem na pomysł, aby dopisywać datę i czas zrobienia zdjęcia, jako prefiks do każdej fotografii. Aby się nie napracować napisałem skrypt w powershellu. Jeżeli jesteś ciekawy jak on działa to zapraszam najserdeczniej.
Metadane
Aby móc automatycznie nazywać pliki, należy wyciągnąć informacje z metadanych. Ale czym one są? Każdy plik posiada informacje go opisujące. Mogą to być ogólne dane jak wielkość obrazu, data ostatniej modyfikacji, czy informacja o prawach autorskich. W przypadku plików muzycznych może to być informacja o wykonawcy, roku wydania, czy nazwie albumu. Natomiast w przypadku zdjęć może to być model aparatu, informacje o ekspozycji, ale i to co mnie najbardziej interesuje. Data wykonania zdjęcia.
Wyciąganie metadanych.
Skoro wiemy czym są metadane. Pozostaje zatem się do nich dobrać. Najbardziej uniwersalnym sposobem jest ręczne parsowanie nagłówka plików w celu poszukiwania metadanych. Gdyby wydajność i uniwersalność byłaby dla mnie, w tym przypadku, priorytetem to z pewnością poszedłbym w tę stronę. Jednak istnieje łatwiejsza opcja na początek. Mianowicie w windowsie istnieje biblioteka shell32.dll która pozwala na wydobycie metadanych z plików.
Naprawa metadanych
Skoro już mam wszystkie informacje o plikach to już powinno być z górki. Nic bardziej mylnego. O ile shell32.dll pozwolił mi na bardzo szybkie dobranie się do metadanych, to nie okazał się rozwiązaniem bez wad. Okazało się, że zwraca on dane zależne od ustawień regionalnych systemu operacyjnego. Co konkretnie mnie uderzyło? Już piszę.
- Nazwy wartości metadanych są tłumaczone, zatem pole z datą zrobienia zdjęcia w polskim windowsie to 'Data wykonania', natomiast w angielskim to 'Data taken'.
- Format daty również zależy od ustawień regionalnych. Aby odpowiednio sparsować datę należy wcześniej pobrać aktualne formaty.
- Jeżeli rozszerzenia znanych plików są ukryte, to również zostaną one pominięte.
- Dodatkowo data zawierała niewidoczne znaki, przez co była neparsowalna. Był to znak #8026, czyli Left-to-right[https://en.wikipedia.org/wiki/Left-to-right_mark].
Wszystkie te rzeczy zostały rozwiązane. Poniżej znajduje się kompletny skrypt, który u mnie działa :) i nazwy plików odpowiednio się zmieniają.
Zatem zapraszam do analizy kodu.
Kod
function Add-DataTakenToFilesName { param( [Parameter(Mandatory)] [string] $Path) $filesMetadata = Get-FileMetaData -folder $Path foreach($fileMetadata in $filesMetadata) { $dateTaken = Get-DataTaken -fileMetadata $fileMetadata if (-not $dateTaken) { continue; } $fileName = Get-FileName -fileMetadata $fileMetadata if($fileName.StartsWith($dateTaken) ) { continue } $filePath = Join-Path -Path $Path -ChildPath $fileName $newName = $dateTaken + ' - ' + $fileName $newPath = Join-Path -Path $Path -ChildPath $newName Rename-Item -Path $filePath -newName $newName } } function Get-FileMetaData { Param([string]$folder) $a = 0 $objShell = New-Object -ComObject Shell.Application $objFolder = $objShell.namespace($folder) foreach ($File in $objFolder.items()) { $FileMetaData = New-Object PSOBJECT for ($a ; $a -le 266; $a++) { if($objFolder.getDetailsOf($File, $a)) { $hash += @{$($objFolder.getDetailsOf($objFolder.items, $a)) = $($objFolder.getDetailsOf($File, $a)) } $FileMetaData | Add-Member $hash $hash.clear() } } $a=0 $FileMetaData } } function Get-DataTaken { param ($fileMetadata) $rawDate = $fileMetadata.'Data wykonania' $rawDate = $rawDate -replace '[^a-zA-Z0-9 :.]', '' #remove non date format characters $datePattern = [System.Threading.Thread]::CurrentThread.CurrentUICulture.DateTimeFormat.ShortDatePattern; $timePattern = [System.Threading.Thread]::CurrentThread.CurrentUICulture.DateTimeFormat.ShortTimePattern; $pattern = "$datePattern $timePattern" $culture = [System.Globalization.CultureInfo]::InvariantCulture $parsedDate = [DateTime]::ParseExact($rawDate, $pattern, $culture) $formatedDate = $parsedDate.ToString('yyyy.MM.dd HH-mm'); $formatedDate } function Get-FileName { param ($fileMetadata) $name = $fileMetadata.'Nazwa' $extenstion = Get-Extension -fileMetadata $fileMetadata if($name.EndsWith($extenstion) ) { $name } $name = $name + $extenstion $name } function Get-Extension { param ($fileMetadata) $rawExtension= $fileMetadata.'Rozszerzenie Pliku' $rawExtension }
Podoba się?
Takie podejście i ten skrypt bardzo uprościły mi proces segregacji i archiwizacji zdjęć. Mam nadzieję, że również dla Ciebie będzie przydatny. Jeżeli masz jakiekolwiek sugestie do skryptu to pisz. Chętnie podyskutuję i pomogę. Pozdrawiam serdecznie :).
1 Komentarz
Pawel · 2022-04-10 o 16:04
W funkcji Get-FileName brakuje elsa, chyba powinno byc
function Get-FileName {
param ($fileMetadata)
$name = $fileMetadata.’Nazwa'
$extenstion = Get-Extension -fileMetadata $fileMetadata
if($name.EndsWith($extenstion) )
dodatkowo warto by obsluzyc sytuacje gdy nie ma wartosci 'Data wykonania' np poprzez:
$rawDate = $fileMetadata.’Data wykonania'
if($null -eq $rawDate){
$rawDate = $fileMetadata.’Data modyfikacji'
}
{
$name
}
else {
$name = $name + $extenstion
$name
}
}