Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Dieses Skript komprimiert alle markierten .mp4-Dateien über das Kontextmenü per FFmpeg mit einem wählbaren CRF-Wert wahlweise als H.265 oder AV1. Der ursprüngliche Zeitstempel (Änderungsdatum) der Datei wird im Nachhinein neu gesetzt, und die Ausgabedateien erhalten angepasste Namen (_crfXX.mp4 bzw. mit AV1 _AV1_crfXX.mp4). Bei Videos, die von einer DJI Action Kamera oder der DJI Mimo App stammen, wird automatisch das Datum aus dem Dateinamen verwendet, unabhängig von dem Änderungsdatum. Zusätzlich wird die Zeitzone aus der Abfrage berücksichtigt.
- # Verwendung des Scripts:
- # Script als "video_compress.ps1" mit Kodierung "UTF-8-BOM" anlegen.
- # "ffmpeg-release-essentials.zip" hier herunterladen, entpacken und aus dem Ordner "bin" die ffmpeg.exe und ffprobe.exe in das Verzeichnis $ToolDir (Hier im Script im Bereich "PFAD HIER ANPASSEN" definiert) kopieren: https://www.gyan.dev/ffmpeg/builds/
- # "exiftool-XXX.zip" hier herunterladen, entpacken, in das Verzeichnis $ToolDir kopieren und die .exe-Datei umbenennen in "exiftool.exe": https://exiftool.org/
- # REG-Datei mit Endung ".reg" erstellen: https://pastebin.com/V4sn7Mqc
- # Pfade in der REG-Datei anpassen, wo "video_compress.ps1" liegt
- # Datei doppelklicken und alles bejahen
- # Version 1.0.4 - 24.01.2026 - @nurjns
- param (
- [string]$FilePath,
- [int]$CRF = 25,
- [string]$Codec = 'HEVC'
- )
- # PFAD HIER ANPASSEN
- $ToolDir = 'X:\PFAD\ZU\ASSETS'
- $FFmpeg = Join-Path $ToolDir 'ffmpeg.exe'
- $FFprobe = Join-Path $ToolDir 'ffprobe.exe'
- $ExifTool = Join-Path $ToolDir 'exiftool.exe'
- Add-Type -AssemblyName System.Windows.Forms
- Add-Type -AssemblyName System.Drawing
- # Mutex zur Instanz-Steuerung
- $Mutex = New-Object System.Threading.Mutex($false, 'VideoCompressor_nurjns_Mutex')
- $IsFirstInstance = $Mutex.WaitOne(10)
- if (-not $IsFirstInstance) {
- if (-not [string]::IsNullOrWhiteSpace($FilePath) -and $FilePath -match '\.mp4$') {
- $Connected = $false
- $Attempts = 0
- while (-not $Connected -and $Attempts -lt 5) {
- try {
- $Client = New-Object System.IO.Pipes.NamedPipeClientStream('.', 'VideoCompressorQueue', [System.IO.Pipes.PipeDirection]::Out)
- $Client.Connect(500)
- $Writer = New-Object System.IO.StreamWriter($Client)
- $Writer.WriteLine($FilePath)
- $Writer.Flush()
- $Client.Close()
- $Connected = $true
- } catch {
- $Attempts++
- Start-Sleep -Milliseconds 200
- }
- }
- }
- exit
- }
- $MissingTools = @()
- foreach ($Tool in @($FFmpeg, $FFprobe, $ExifTool)) {
- if (-not (Test-Path -LiteralPath $Tool)) {
- $MissingTools += $Tool
- }
- }
- if ($MissingTools.Count -gt 0) {
- $Message = "Folgende Tools wurden nicht gefunden:`n`n" + ($MissingTools -join "`n")
- [System.Windows.Forms.MessageBox]::Show($Message, 'Fehler: Tools fehlen', [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
- $Mutex.ReleaseMutex()
- exit
- }
- $Queue = [System.Collections.Generic.List[string]]::new()
- # Nur hinzufügen, wenn es eine MP4 ist
- if (-not [string]::IsNullOrWhiteSpace($FilePath) -and $FilePath -match '\.mp4$') {
- $Queue.Add($FilePath)
- }
- $Form = New-Object System.Windows.Forms.Form
- $Form.Text = 'Video Compressor - 1.0.4 by @nurjns'
- $Form.Size = New-Object System.Drawing.Size(450, 230)
- $Form.StartPosition = 'CenterScreen'
- $Form.FormBorderStyle = 'FixedDialog'
- $Form.MaximizeBox = $false
- $Form.TopMost = $false
- $LabelCurrent = New-Object System.Windows.Forms.Label
- $LabelCurrent.Location = New-Object System.Drawing.Point(15, 15)
- $LabelCurrent.Size = New-Object System.Drawing.Size(400, 45)
- $LabelCurrent.Text = 'Sammle Dateien...'
- $ProgressCurrent = New-Object System.Windows.Forms.ProgressBar
- $ProgressCurrent.Location = New-Object System.Drawing.Point(15, 65)
- $ProgressCurrent.Size = New-Object System.Drawing.Size(400, 25)
- $LabelTotal = New-Object System.Windows.Forms.Label
- $LabelTotal.Location = New-Object System.Drawing.Point(15, 110)
- $LabelTotal.Size = New-Object System.Drawing.Size(400, 20)
- $LabelTotal.Text = "Warte auf Start..."
- $ProgressTotal = New-Object System.Windows.Forms.ProgressBar
- $ProgressTotal.Location = New-Object System.Drawing.Point(15, 135)
- $ProgressTotal.Size = New-Object System.Drawing.Size(400, 20)
- $Form.Controls.AddRange(@($LabelCurrent, $ProgressCurrent, $LabelTotal, $ProgressTotal))
- function Get-ExifTag {
- param([string]$Tag, [string]$Path)
- $val = & $ExifTool -s3 -api 'QuickTimeUTC' $Tag $Path | Out-String
- return $val.Trim()
- }
- $Form.Add_Shown({
- $ProcessedCount = 0
- $OverallSuccess = $true
- while ($true) {
- $Server = New-Object System.IO.Pipes.NamedPipeServerStream('VideoCompressorQueue', [System.IO.Pipes.PipeDirection]::In, 1, [System.IO.Pipes.PipeTransmissionMode]::Byte, [System.IO.Pipes.PipeOptions]::Asynchronous)
- $AsyncResult = $Server.BeginWaitForConnection($null, $null)
- while (-not $AsyncResult.AsyncWaitHandle.WaitOne(200)) {
- if ($ProcessedCount -lt $Queue.Count) {
- $CurrentPath = $Queue[$ProcessedCount]
- $ProgressTotal.Maximum = $Queue.Count
- $ProgressTotal.Value = $ProcessedCount
- $LabelTotal.Text = "Gesamtfortschritt: Video $($ProcessedCount + 1) von $($Queue.Count)"
- $Result = Process-Video -Path $CurrentPath
- if ($Result -eq $false) { $OverallSuccess = $false }
- $ProcessedCount++
- $ProgressTotal.Value = $ProcessedCount
- $LabelTotal.Text = "Gesamtfortschritt: Video $ProcessedCount von $($Queue.Count)"
- } else {
- if (-not $AsyncResult.AsyncWaitHandle.WaitOne(1000)) {
- if ($ProcessedCount -gt 0 -and $OverallSuccess) {
- $LabelCurrent.Text = "Kodierung erfolgreich!"
- $ProgressCurrent.Value = $ProgressCurrent.Maximum
- [System.Windows.Forms.Application]::DoEvents()
- [console]::Beep(500, 200)
- Start-Sleep -Seconds 2
- }
- $Form.Close()
- return
- }
- }
- [System.Windows.Forms.Application]::DoEvents()
- }
- try {
- $Server.EndWaitForConnection($AsyncResult)
- $Reader = New-Object System.IO.StreamReader($Server)
- $NewPath = $Reader.ReadLine()
- # Auch hier beim Empfang prüfen
- if (-not [string]::IsNullOrWhiteSpace($NewPath) -and $NewPath -match '\.mp4$') {
- $Queue.Add($NewPath)
- }
- } catch {}
- $Server.Dispose()
- }
- })
- function Process-Video {
- param([string]$Path)
- # Sicherheitsprüfung auf MP4
- if (-not (Test-Path -LiteralPath $Path) -or $Path -notmatch '\.mp4$') { return $false }
- $File = Get-Item -LiteralPath $Path
- $BaseName = $File.BaseName
- if ($BaseName -match '(_crf\d+|_AV1_crf\d+)$') {
- $LabelCurrent.Text = "Überspringe: $($File.Name)`n(Bereits verarbeitet)"
- return $true
- }
- if ($Codec -eq 'AV1') {
- $Suffix = "_AV1_crf$CRF"
- $CpuUsed = if ($CRF -le 20) { 2 } elseif ($CRF -le 28) { 4 } elseif ($CRF -le 35) { 6 } else { 8 }
- $VArgs = "-c:v libaom-av1 -crf $CRF -cpu-used $CpuUsed -pix_fmt yuv420p"
- } else {
- $Suffix = "_crf$CRF"
- $VArgs = "-c:v libx265 -crf $CRF -preset slow -pix_fmt yuv420p -tag:v hvc1"
- }
- $OutFile = Join-Path $File.DirectoryName ($BaseName + $Suffix + '.mp4')
- if (Test-Path -LiteralPath $OutFile) {
- $LabelCurrent.Text = "Überspringe: Datei existiert bereits.`n$($File.Name)"
- return $true
- }
- $TotalFramesRaw = & $FFprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 "$Path"
- $TotalFrames = if ($TotalFramesRaw -match '^\d+$') { [int]$TotalFramesRaw } else { 0 }
- $ProgressCurrent.Style = if ($TotalFrames -gt 0) { 'Continuous' } else { 'Marquee' }
- if ($TotalFrames -gt 0) { $ProgressCurrent.Maximum = $TotalFrames; $ProgressCurrent.Value = 0 }
- $Offset = Get-ExifTag '-OffsetTimeOriginal' "$Path"
- if ([string]::IsNullOrWhiteSpace($Offset)) { $Offset = Get-ExifTag '-OffsetTime' "$Path" }
- if ([string]::IsNullOrWhiteSpace($Offset)) {
- $TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById('W. Europe Standard Time')
- $Offset = if ($TZ.IsDaylightSavingTime((Get-Date))) { '+02:00' } else { '+01:00' }
- }
- $Timestamp = ''
- if ($BaseName -match '^DJI_(\d{14})_') {
- $dt = $Matches[1]
- $Timestamp = $dt.Substring(0,4) + ':' + $dt.Substring(4,2) + ':' + $dt.Substring(6,2) + ' ' + $dt.Substring(8,2) + ':' + $dt.Substring(10,2) + ':' + $dt.Substring(12,2)
- } elseif ($BaseName -match 'dji_mimo_(\d{8})_(\d{6})') {
- $d = $Matches[1]; $t = $Matches[2]
- $Timestamp = $d.Substring(0,4) + ':' + $d.Substring(4,2) + ':' + $d.Substring(6,2) + ' ' + $t.Substring(0,2) + ':' + $t.Substring(2,2) + ':' + $t.Substring(4,2)
- } else {
- $Timestamp = Get-ExifTag '-CreateDate' "$Path"
- if ([string]::IsNullOrWhiteSpace($Timestamp) -or $Timestamp -eq '0000:00:00 00:00:00') {
- $Timestamp = $File.LastWriteTime.ToString('yyyy:MM:dd HH:mm:ss')
- }
- }
- $StartInfo = New-Object System.Diagnostics.ProcessStartInfo
- $StartInfo.FileName = $FFmpeg
- $StartInfo.Arguments = "-y -i `"$Path`" $VArgs -movflags +faststart -c:a aac -b:a 160k `"$OutFile`""
- $StartInfo.RedirectStandardError = $true
- $StartInfo.UseShellExecute = $false
- $StartInfo.CreateNoWindow = $true
- $Process = [System.Diagnostics.Process]::Start($StartInfo)
- while (-not $Process.HasExited) {
- $Line = $Process.StandardError.ReadLine()
- if ($Line -match 'frame=\s*(\d+)') {
- $CurrentFrame = [int]$Matches[1]
- if ($TotalFrames -gt 0 -and $CurrentFrame -le $TotalFrames) {
- $ProgressCurrent.Value = $CurrentFrame
- $Percent = [math]::Round(($CurrentFrame / $TotalFrames) * 100)
- $LabelCurrent.Text = "Datei: $($File.Name)`nEncoding: $Percent% ($CurrentFrame / $TotalFrames Frames)"
- }
- }
- [System.Windows.Forms.Application]::DoEvents()
- }
- if ($Process.ExitCode -eq 0) {
- $LabelCurrent.Text = "Schreibe Metadaten..."
- [System.Windows.Forms.Application]::DoEvents()
- $ExifArgs = @(
- '-overwrite_original',
- "-DateTimeOriginal=$Timestamp$Offset",
- "-OffsetTimeOriginal=$Offset",
- "-FileModifyDate=$Timestamp",
- $OutFile
- )
- & $ExifTool $ExifArgs | Out-Null
- return $true
- } else {
- [System.Windows.Forms.MessageBox]::Show('Fehler beim Encoding! ExitCode: ' + $Process.ExitCode, 'Fehler')
- [console]::Beep(200, 500)
- return $false
- }
- }
- $Form.ShowDialog()
- $Mutex.ReleaseMutex()
Advertisement