EurenikZ

Google Fotos Takeout ExifTool Metadata Script

Jul 16th, 2025 (edited)
188
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#
  2. Dieses Script verarbeitet Google Fotos Takeout-Exporte, ordnet die passenden Metadaten aus den JSON-Dateien den Bild- und Videodateien zu, schreibt diese Metadaten in die Dateien und passt das Dateizeitstempel an. .jfif-Dateien werden automatisch in .jpg umbenannt. Alle Aktivitäten werden in eine "log.txt" geloggt.
  3. OPTIONAL: In Schritt 3 werden die Originaldateien gelöscht, wenn eine bearbeitete Variante existiert. Ist das nicht gewünscht, diesen Part einfach rauslöschen.
  4. "exiftool-XXX.zip" hier herunterladen, entpacken, in das Verzeichnis kopieren und die EXE umbenennen in "exiftool.exe": https://exiftool.org/
  5. Version 1.0.3 - 19.07.2025
  6. #>
  7.  
  8. $exiftoolPath = Join-Path $PSScriptRoot 'exiftool.exe'
  9. $logPath = Join-Path $PSScriptRoot 'log.txt'
  10. "`n--- Lauf gestartet: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ---`n" | Out-File $logPath -Encoding UTF8
  11.  
  12. # 1. .jfif zu .jpg umbenennen
  13. Get-ChildItem -Recurse -Filter *.jfif | ForEach-Object {
  14.     try {
  15.         $newPath = $_.FullName -replace '\.jfif$', '.jpg'
  16.         Rename-Item $_.FullName $newPath
  17.         Add-Content $logPath "Umbenannt: $($_.FullName) zu $newPath"
  18.     } catch {
  19.         Add-Content $logPath "FEHLER beim Umbenennen: $($_.FullName) - $_"
  20.     }
  21. }
  22.  
  23. # 2. Umlaute ersetzen
  24. $extensionstmp = @('*.jpg', '*.jpeg', '*.png', '*.mp4', '*.mov', '*.heic', '*.json')
  25.  
  26. # Alle Dateien rekursiv finden
  27. $files = Get-ChildItem -Path . -Include $extensionstmp -Recurse -File
  28.  
  29. foreach ($file in $files) {
  30.     $originalName = $file.BaseName
  31.     $extension = $file.Extension
  32.     $directory = $file.Directory.FullName
  33.    
  34.     Write-Host "Prüfe: $($file.Name)"
  35.    
  36.     # Umlaute ersetzen
  37.     $newName = $originalName.Replace("ä", "ae").Replace("ö", "oe").Replace("ü", "ue").Replace("Ä", "Ae").Replace("Ö", "Oe").Replace("Ü", "Ue").Replace("ß", "ss")
  38.    
  39.     # Prüfen ob Änderung notwendig
  40.     if ($originalName -ne $newName) {
  41.         $newFullName = Join-Path $directory ($newName + $extension)
  42.        
  43.         if (Test-Path $newFullName) {
  44.             Write-Host "WARNUNG: Zieldatei existiert bereits - übersprungen" -ForegroundColor Yellow
  45.         } else {
  46.             try {
  47.                 Write-Host "Benenne um: '$($file.Name)' zu '$($newName + $extension)'"
  48.                 Rename-Item -Path $file.FullName -NewName ($newName + $extension)
  49.             } catch {
  50.                 Write-Host "FEHLER: $($_.Exception.Message)" -ForegroundColor Red
  51.             }
  52.         }
  53.     } else {
  54.         Write-Host "Keine Änderung erforderlich" -ForegroundColor Gray
  55.     }
  56.     Write-Host ""
  57. }
  58.  
  59. # 3. Dateien erfassen
  60. $extensions = @('*.jpg', '*.jpeg', '*.png', '*.mp4', '*.mov', '*.heic')
  61. $files = Get-ChildItem -Recurse -File -Include $extensions
  62.  
  63. # 4. Originale löschen, wenn bearbeitete Variante existiert (mit Suffix -bear*)
  64. $grouped = $files | Group-Object { $_.Name -replace '-bear\w*(?=\.\w+$)', '' }
  65.  
  66. foreach ($group in $grouped) {
  67.     $original = $group.Group | Where-Object { $_.Name -notmatch '-bear\w*(?=\.\w+$)' }
  68.     $edited = $group.Group | Where-Object { $_.Name -match '-bear\w*(?=\.\w+$)' }
  69.  
  70.     if ($original.Count -gt 0 -and $edited.Count -gt 0) {
  71.         foreach ($file in $original) {
  72.             try {
  73.                 Remove-Item $file.FullName -Force
  74.                 Add-Content $logPath "Original geloescht: $($file.FullName)"
  75.             } catch {
  76.                 Add-Content $logPath "FEHLER beim Loeschen: $($file.FullName) - $_"
  77.             }
  78.         }
  79.     }
  80. }
  81.  
  82. # 5. Metadaten schreiben und NTFS-Zeit setzen (mit Endungskorrektur)
  83. $files = Get-ChildItem -Recurse -File -Include $extensions
  84.  
  85. # 5a. Falsche Dateiendungen korrigieren (z.B. PNG mit JPEG-Inhalt)
  86. foreach ($file in $files) {
  87.     try {
  88.         $magic = Get-Content -Path $file.FullName -Encoding Byte -TotalCount 4
  89.         $isJpeg = ($magic[0] -eq 0xFF -and $magic[1] -eq 0xD8 -and $magic[2] -eq 0xFF)
  90.         $wrongExt = $file.Extension -ieq '.png'
  91.  
  92.         if ($isJpeg -and $wrongExt) {
  93.             $newPath = [System.IO.Path]::ChangeExtension($file.FullName, '.jpg')
  94.             Rename-Item $file.FullName $newPath -Force
  95.             Add-Content $logPath "Falsche Endung korrigiert: $($file.FullName) zu $newPath"
  96.         }
  97.     } catch {
  98.         Add-Content $logPath "FEHLER bei Endungskorrektur: $($file.FullName) - $_"
  99.     }
  100. }
  101.  
  102. # 5b. Nach Endungskorrekturen erneut Dateiliste holen
  103. $files = Get-ChildItem -Recurse -File -Include $extensions
  104.  
  105. foreach ($file in $files) {
  106.     Write-Host "Bearbeite Datei: $($file.Name)"
  107.  
  108.     # Basisname bereinigen: ohne -bear*, (Zahl) und evtl. abschließendem _
  109.     $basenameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
  110.     $basenameClean = $basenameWithoutExt -replace '-bear\w*$', '' -replace '\(\d+\)$', '' -replace '_$'
  111.     $base = [regex]::Escape($basenameClean)
  112.  
  113.     # JSON-Datei suchen mit tolerantem Match (inkl. *.supplemental-metadata(45).json)
  114.     $jsonPath = Get-ChildItem -Path $file.DirectoryName -Filter "*.json" -File | Where-Object {
  115.         $jsonName = $_.Name
  116.         $jsonNameNoExt = [System.IO.Path]::GetFileNameWithoutExtension($jsonName)
  117.        
  118.         # Klammern und evtl. abschließendes "_" entfernen, aber rest beibehalten
  119.         $jsonNameClean = $jsonNameNoExt -replace '\(\d+\)$', '' -replace '_$'
  120.  
  121.         # Match wenn Dateiname mit dem bereinigten Basisnamen beginnt und eine "sup*" JSON ist
  122.         $jsonNameClean -eq $basenameClean -or (
  123.             $jsonNameClean -match "^$base" -and $jsonName -match '\.sup(p|pl|plemental)?.*\.json$'
  124.         )
  125.     } | Select-Object -ExpandProperty FullName -First 1
  126.  
  127.     if (-not $jsonPath) {
  128.         Add-Content $logPath "FEHLER: Keine JSON gefunden: $($file.FullName)"
  129.         continue
  130.     }
  131.  
  132.     # JSON laden
  133.     try {
  134.         $json = Get-Content $jsonPath -Raw | ConvertFrom-Json
  135.         $description = $json.description
  136.         $timestamp = [double]::Parse($json.photoTakenTime.timestamp)
  137.         $dateTaken = [DateTimeOffset]::FromUnixTimeSeconds($timestamp).ToLocalTime().ToString('yyyy:MM:dd HH:mm:ss')
  138.     } catch {
  139.         Add-Content $logPath "FEHLER beim Parsen: $jsonPath - $_"
  140.         continue
  141.     }
  142.  
  143.     # ExifTool & NTFS-Zeit setzen
  144.     try {
  145.         & $exiftoolPath `
  146.             -overwrite_original `
  147.             -Description="$description" `
  148.             -EXIF:DateTimeOriginal="$dateTaken" `
  149.             -QuickTime:CreateDate="$dateTaken" `
  150.             -QuickTime:ModifyDate="$dateTaken" `
  151.             -XMP:CreateDate="$dateTaken" `
  152.             -XMP:ModifyDate="$dateTaken" `
  153.             -XMP:DateTimeOriginal="$dateTaken" `
  154.             "$($file.FullName)" | Out-Null
  155.  
  156.         if ($dateTaken) {
  157.             $escapedPath = $file.FullName.Replace("'", "''")
  158.             $cmd = "powershell -NoLogo -NoProfile -Command ""(Get-Item '$escapedPath').LastWriteTimeUtc = [datetime]::ParseExact('$dateTaken', 'yyyy:MM:dd HH:mm:ss', `$null)"""
  159.             Start-Process -FilePath 'cmd.exe' -ArgumentList "/c $cmd" -NoNewWindow -Wait
  160.             Add-Content $logPath "Metadaten & NTFS-Zeit geschrieben: $($file.FullName)"
  161.         } else {
  162.             Add-Content $logPath "FEHLER: Kein gültiges Datum für NTFS-Zeit bei: $($file.FullName)"
  163.         }
  164.     } catch {
  165.         Add-Content $logPath "FEHLER bei Metadaten/NTFS-Zeit: $($file.FullName) - $_"
  166.     }
  167. }
  168.  
Advertisement
Add Comment
Please, Sign In to add comment