A 2K 593 7 C:\>
mcgeek.de
mcgeek.de
--:--
mcgeek.de
mcgeek.de wird gestartet …

McTweak – HotFix & Solution Center

HandBrake Batch-Encoder mit Peak-Normalisierung

15.11.2026 · Medien / Audio / Video · E4

Problem

Eure Filmsammlung frisst immer mehr Platz, und beim Schauen greift man ständig zur Fernbedienung, weil ein Film flüsterleise ist und der nächste plötzlich brüllt?

Lösung

Hinweis: Führe Registry-Dateien und Skripte nur aus, wenn du verstanden hast, was sie tun. Prüfe den Code vor dem Ausführen. Die Nutzung erfolgt auf eigene Gefahr.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
chcp 65001 >nul
:: ============================================================
::  HANDBRAKE BATCH-ENCODER + PEAK-NORMALISIERUNG
::  Konvertiert alle MKV/MP4 (inkl. Unterordner) mit einem HandBrake-
::  Preset nach MKV. Der Audio-Peak wird vorher mit ffmpeg gemessen
::  und als Gain an HandBrake uebergeben (Audio nur 1x encodiert).
:: ============================================================

:: ======================== KONFIGURATION =========================

:: --- Programme ---
set "HANDBRAKE=C:\Pfad\zu\HandBrake CLI\HandBrakeCLI.exe"
set "FFMPEG=C:\Pfad\zuffmpeg\ffmpeg.exe"

:: --- HandBrake-Preset ---
::   exportierte JSON aus HandBrake (Preset-Name wird automatisch gelesen)
set "PRESET_DATEI=C:\Pfad\zu\PRESET.json"

:: --- Video-Encoder (H.265 10-Bit) - Reihenfolge ---
::   1 = zuerst versuchen, 2 = danach usw.   0 = nie verwenden
::   Genutzt wird nur, was der PC wirklich hat. Faellt ein Encoder aus,
::   wird automatisch der naechste in der Reihenfolge versucht.
set "PRIO_NVENC=1"  & rem NVIDIA-Grafikkarte
set "PRIO_QSV=2"    & rem Intel QuickSync (iGPU/Arc)
set "PRIO_VCE=3"    & rem AMD Radeon (Grafikkarte/iGPU)
set "PRIO_CPU=4"    & rem Prozessor (x265) - beste Qualitaet, aber langsam

:: --- Video-Qualitaet ---
::   1 = feste Bitrate    -> planbare Groesse (2100 kbit/s + Ton = ca. 1 GB/Stunde)
::   2 = feste Qualitaet  -> gleichmaessiges Bild, Groesse schwankt je nach Film
set "MODUS=1"
set "BITRATE=2100"

::   Nur fuer Modus 2 - Qualitaetsstufe je Encoder (kleiner = besser und groesser)
set "Q_NVENC=26"
set "Q_QSV=28"
set "Q_VCE=26"
set "Q_CPU=23"

:: --- Ordner ---
::   QUELLORDNER : leer = Ordner, in dem dieses Skript liegt
::   ZIELORDNER  : leer = Ausgabe neben der Quelle (siehe "Original")
::                 gesetzt = Ausgabe dort als "name.mkv",
::                 die Unterordner-Struktur wird nachgebaut
set "QUELLORDNER="
set "ZIELORDNER="

:: --- Original ---
::   0 = behalten (neue Datei "name.export.mkv" daneben)
::   1 = ersetzen (Achtung: Original wird endgueltig geloescht!)
::   Gilt nur ohne ZIELORDNER - mit Zielordner bleibt das Original immer erhalten.
set "ERSETZEN=0"

:: --- Audio-Normalisierung ---
::   ZIEL_PEAK : gewuenschter Spitzenpegel in dB
::   MIN_GAIN  : kleinere Abweichungen (dB) werden ignoriert
set "ZIEL_PEAK=-1.0"
set "MIN_GAIN=0.1"

:: ================== AB HIER NICHTS MEHR AENDERN ==================

set "LOG=%~dp0handbrake.log"
for %%V in (HANDBRAKE FFMPEG PRESET_DATEI) do call :CheckFile %%V || (pause & exit /b 1)
for /f "usebackq delims=" %%P in (`powershell -NoP -C "(Get-Content -LiteralPath $env:PRESET_DATEI -Raw -Encoding UTF8 | ConvertFrom-Json).PresetList[0].PresetName"`) do set "PRESET_NAME=%%P"
if not defined PRESET_NAME echo [FEHLER] Preset-Name in "%PRESET_DATEI%" nicht lesbar.& pause & exit /b 1
if not "%MODUS%"=="1" if not "%MODUS%"=="2" echo [FEHLER] MODUS muss 1 (Bitrate) oder 2 (Qualitaet) sein.& pause & exit /b 1
if "%MODUS%"=="1" (set "MODUSTEXT=feste Bitrate %BITRATE% kbit/s") else set "MODUSTEXT=feste Qualitaet"
:: Vorhandene Encoder ermitteln und nach PRIO_* sortieren
set "ENCLIST="
for /f "usebackq delims=" %%E in (`powershell -NoP -C "$n = (Get-CimInstance Win32_VideoController).Name -join ';'; $hw = @{nvenc='NVIDIA'; qsv='Intel'; vce='AMD|Radeon'}; $l = foreach ($k in 'nvenc','qsv','vce','cpu') { $p = 0; [int]::TryParse((Get-Item -Path ('env:PRIO_' + $k) -ErrorAction SilentlyContinue).Value, [ref]$p) | Out-Null; if ($p -gt 0 -and ($k -eq 'cpu' -or $n -match $hw[$k])) { [pscustomobject]@{k=$k; p=$p} } }; ($l | Sort-Object p | ForEach-Object { $_.k }) -join ' '"`) do set "ENCLIST=%%E"
if not defined ENCLIST echo [FEHLER] Kein Encoder verfuegbar - PRIO_* pruefen (mind. einer ^> 0, z.B. PRIO_CPU).& pause & exit /b 1
if not defined QUELLORDNER set "QUELLORDNER=%~dp0"
call :NormDir QUELLORDNER
if not exist "%QUELLORDNER%" echo [FEHLER] Quellordner nicht gefunden: "%QUELLORDNER%"& pause & exit /b 1
for /f %%L in ('powershell -NoP -C "$env:QUELLORDNER.Length"') do set "QLEN=%%L"
if defined ZIELORDNER call :NormDir ZIELORDNER
if defined ZIELORDNER call set "PREFIX=%%ZIELORDNER:~0,%QLEN%%%"
if defined ZIELORDNER if /i "%PREFIX%"=="%QUELLORDNER%" echo [FEHLER] Zielordner darf nicht im Quellordner liegen.& pause & exit /b 1

echo === Encoding gestartet ===
echo   Preset: "%PRESET_NAME%"
echo   Encoder: %ENCLIST%  (Reihenfolge, bester zuerst)
echo   Modus : %MODUSTEXT%
echo   Quelle: "%QUELLORDNER%"
if defined ZIELORDNER (echo   Ziel  : "%ZIELORDNER%") else echo   Ziel  : neben der Quelle
echo   Log   : "%LOG%"
for /r "%QUELLORDNER%" %%F in (*.mkv *.mp4) do call :Process "%%F"
echo. & echo === Alle Dateien verarbeitet === & pause & exit /b 0


:Process
:: Eigene Ausgabe- und Temp-Dateien ueberspringen
for %%N in ("%~n1") do if /i "%%~xN"==".export" goto :eof
for %%N in ("%~n1") do if /i "%%~xN"==".__temp__" goto :eof
set "INFILE=%~f1" & set "SRCDIR=%~dp1" & set "MAXVOL=" & set "GAIN=0.00" & set "DELSRC=" & set "HB="

:: Ausgabeort je nach Modus bestimmen
if defined ZIELORDNER goto :ModeTarget
if "%ERSETZEN%"=="1" goto :ModeReplace
set "OUTDIR=%SRCDIR%" & set "FINAL=%~dpn1.export.mkv"
goto :Check
:ModeTarget
call set "REL=%%SRCDIR:~%QLEN%%%"
set "OUTDIR=%ZIELORDNER%%REL%" & set "FINAL=%ZIELORDNER%%REL%%~n1.mkv"
goto :Check
:ModeReplace
set "OUTDIR=%SRCDIR%" & set "FINAL=%~dpn1.mkv"
if /i not "%~x1"==".mkv" set "DELSRC=1"

:Check
set "TMPFILE=%OUTDIR%%~n1.__temp__.mkv"
echo. & echo Datei: "%INFILE%"
if exist "%FINAL%" if /i not "%FINAL%"=="%INFILE%" call :Msg "[INFO] Ziel existiert bereits" & goto :eof
:: Beim Ersetzen von MKVs: bereits mit HandBrake encodierte Dateien nicht erneut encodieren
if /i "%FINAL%"=="%INFILE%" for /f "usebackq delims=" %%H in (`powershell -NoP -C "if ((& $env:FFMPEG -hide_banner -i $env:INFILE 2>&1 | Out-String) -match 'encoder\s*:\s*HandBrake') { 'HB' }"`) do set "HB=%%H"
if defined HB call :Msg "[INFO] Bereits mit HandBrake encodiert" & goto :eof
if not exist "%OUTDIR%" mkdir "%OUTDIR%" 2>nul

:: 1) Peak messen - erste deutsche Spur (wie im Preset), sonst erste Spur
echo [1/2] Messe Lautstaerke...
for /f "usebackq delims=" %%A in (`powershell -NoP -C "foreach ($m in '0:a:m:language:ger','0:a:m:language:deu','0:a:0') { $o = & $env:FFMPEG -hide_banner -nostats -i $env:INFILE -map $m -af volumedetect -f null - 2>&1 | Out-String; if ($o -match 'max_volume:\s*(-inf|-?[\d.]+)\s*dB') { $Matches[1]; break } }"`) do set "MAXVOL=%%A"
if not defined MAXVOL echo   [INFO] Peak nicht messbar - Encoding ohne Gain. & goto :Encode
if /i "%MAXVOL%"=="-inf" echo   [INFO] Audiospur stumm - Encoding ohne Gain. & goto :Encode
:: Gain berechnen (Punkt statt Komma, HandBrake erlaubt -20..+20 dB)
for /f "usebackq delims=" %%G in (`powershell -NoP -C "$c = [cultureinfo]::InvariantCulture; $g = [math]::Round([double]::Parse($env:ZIEL_PEAK,$c) - [double]::Parse($env:MAXVOL,$c), 2); if ([math]::Abs($g) -lt [double]::Parse($env:MIN_GAIN,$c)) { $g = 0 }; $g = [math]::Max(-20, [math]::Min(20, $g)); $g.ToString('0.00',$c)"`) do set "GAIN=%%G"
echo   Peak: %MAXVOL% dB  /  Gain: %GAIN% dB

:Encode
:: 2) Bester verfuegbarer Encoder zuerst, bei Fehler der naechste (zuletzt CPU)
echo [%date% %time%] "%INFILE%" Peak=%MAXVOL% Gain=%GAIN% >>"%LOG%"
set "USED="
for %%E in (%ENCLIST%) do if not defined USED call :TryEnc %%E
if not defined USED del "%TMPFILE%" 2>nul & call :Msg "[FEHLER] Encoding mit allen Encodern fehlgeschlagen, siehe Log" & goto :eof
move /y "%TMPFILE%" "%FINAL%" >nul || (call :Msg "[FEHLER] Verschieben fehlgeschlagen, Temp-Datei bleibt" & goto :eof)
if defined DELSRC del "%INFILE%"
echo   OK (%USED%) - "%FINAL%"
goto :eof


:TryEnc
:: Einen Encoder versuchen, bei Erfolg USED setzen
echo [2/2] Encodiere mit %~1...
call :RunHB %~1
if errorlevel 1 (echo   [INFO] %~1 fehlgeschlagen.& echo [%date% %time%] %~1 fehlgeschlagen >>"%LOG%"& goto :eof)
set "USED=%~1"
goto :eof

:RunHB
:: HandBrake mit passendem Encoder starten (Rueckgabe 1 = fehlgeschlagen)
if /i "%~1"=="nvenc" set "HBARGS=-e nvenc_h265_10bit --encoder-preset slow -x spatial-aq=1:temporal-aq=1:aq-strength=8:rc-lookahead=32" & set "QV=%Q_NVENC%"
if /i "%~1"=="qsv"   set "HBARGS=-e qsv_h265_10bit --encoder-preset quality" & set "QV=%Q_QSV%"
if /i "%~1"=="vce"   set "HBARGS=-e vce_h265_10bit --encoder-preset quality" & set "QV=%Q_VCE%"
if /i "%~1"=="cpu"   set "HBARGS=-e x265_10bit --encoder-preset medium -x aq-mode=3" & set "QV=%Q_CPU%"
if "%MODUS%"=="1" (set "RATE=-b %BITRATE%") else set "RATE=-q %QV%"
del "%TMPFILE%" 2>nul
"%HANDBRAKE%" --preset-import-file "%PRESET_DATEI%" -Z "%PRESET_NAME%" %HBARGS% %RATE% --gain %GAIN% -i "%INFILE%" -o "%TMPFILE%" 2>>"%LOG%"
if errorlevel 1 exit /b 1
if not exist "%TMPFILE%" exit /b 1
for %%S in ("%TMPFILE%") do if %%~zS LSS 1024 exit /b 1
exit /b 0

:CheckFile
:: Pfad pruefen; versehentliche Anfuehrungszeichen werden entfernt
call set "_F=%%%1%%"
if not defined _F echo [FEHLER] %1 ist leer - bitte oben in der Konfiguration eintragen.& exit /b 1
set "_F=%_F:"=%"
set "%1=%_F%"
if not exist "%_F%" echo [FEHLER] %1 nicht gefunden: "%_F%"& exit /b 1
exit /b 0

:NormDir
:: Ordnerpfad vervollstaendigen und mit "\" abschliessen
call set "_D=%%%1%%"
set "_D=%_D:"=%"
for %%P in ("%_D%") do set "_D=%%~fP"
if not "%_D:~-1%"=="\" set "_D=%_D%\"
set "%1=%_D%"
goto :eof

:Msg
echo   %~1 - uebersprungen.
goto :eof

PRESET.json für Handbrake / CLI

{
  "PresetList": [
    {
      "AlignAVStart": false,
      "AudioCopyMask": [
        "copy:aac"
      ],
      "AudioEncoderFallback": "av_aac",
      "AudioLanguageList": [
        "deu"
      ],
      "AudioList": [
        {
          "AudioBitrate": 384,
          "AudioCompressionLevel": 0,
          "AudioEncoder": "av_aac",
          "AudioMixdown": "5point1",
          "AudioNormalizeMixLevel": false,
          "AudioSamplerate": "auto",
          "AudioTrackQualityEnable": false,
          "AudioTrackQuality": -1,
          "AudioTrackGainSlider": 0,
          "AudioTrackDRCSlider": 0
        }
      ],
      "AudioSecondaryEncoderMode": true,
      "AudioTrackSelectionBehavior": "first",
      "AudioTrackNamePassthru": true,
      "AudioAutomaticNamingBehavior": "none",
      "ChapterMarkers": false,
      "ChildrenArray": [],
      "Default": false,
      "FileFormat": "av_mkv",
      "Folder": false,
      "FolderOpen": false,
      "Optimize": false,
      "Mp4iPodCompatible": false,
      "PictureCropMode": 2,
      "PictureBottomCrop": 0,
      "PictureLeftCrop": 0,
      "PictureRightCrop": 0,
      "PictureTopCrop": 0,
      "PictureDARWidth": 1920,
      "PictureDeblockPreset": "off",
      "PictureDeblockTune": "medium",
      "PictureDeblockCustom": "strength=strong:thresh=20:blocksize=8",
      "PictureDeinterlaceFilter": "off",
      "PictureCombDetectPreset": "off",
      "PictureCombDetectCustom": "",
      "PictureDenoiseCustom": "",
      "PictureDenoiseFilter": "hqdn3d",
      "PictureDenoisePreset": "light",
      "PictureSharpenCustom": "",
      "PictureSharpenFilter": "unsharp",
      "PictureSharpenPreset": "medium",
      "PictureSharpenTune": "medium",
      "PictureDetelecine": "off",
      "PictureDetelecineCustom": "",
      "PictureColorspacePreset": "off",
      "PictureColorspaceCustom": "",
      "PictureChromaSmoothPreset": "off",
      "PictureChromaSmoothTune": "none",
      "PictureChromaSmoothCustom": "",
      "PictureItuPAR": false,
      "PictureKeepRatio": true,
      "PicturePAR": "off",
      "PicturePARWidth": 1,
      "PicturePARHeight": 1,
      "PictureWidth": 1920,
      "PictureHeight": 1080,
      "PictureUseMaximumSize": true,
      "PictureAllowUpscaling": true,
      "PictureForceHeight": 0,
      "PictureForceWidth": 0,
      "PicturePadMode": "none",
      "PicturePadTop": 0,
      "PicturePadBottom": 0,
      "PicturePadLeft": 0,
      "PicturePadRight": 0,
      "PicturePadColor": "black",
      "PresetName": "EXPORT",
      "Type": 1,
      "SubtitleAddCC": false,
      "SubtitleAddForeignAudioSearch": false,
      "SubtitleAddForeignAudioSubtitle": false,
      "SubtitleBurnBehavior": "none",
      "SubtitleBurnBDSub": false,
      "SubtitleBurnDVDSub": false,
      "SubtitleLanguageList": [],
      "SubtitleTrackSelectionBehavior": "none",
      "SubtitleTrackNamePassthru": true,
      "VideoAvgBitrate": 2100,
      "VideoColorRange": "limited",
      "VideoColorMatrixCode": 0,
      "VideoEncoder": "nvenc_h265_10bit",
      "VideoFramerateMode": "cfr",
      "VideoGrayScale": false,
      "VideoScaler": "swscale",
      "VideoPreset": "slow",
      "VideoTune": "",
      "VideoProfile": "auto",
      "VideoLevel": "auto",
      "VideoOptionExtra": "",
      "VideoQualityType": 2,
      "VideoQualitySlider": 26,
      "VideoMultiPass": false,
      "VideoTurboMultiPass": false,
      "VideoPasshtruHDRDynamicMetadata": "all",
      "x264UseAdvancedOptions": false,
      "PresetDisabled": false,
      "MetadataPassthru": false
    }
  ],
  "VersionMajor": 72,
  "VersionMicro": 0,
  "VersionMinor": 0
}

Beschreibung

Konvertiert alle MKV/MP4 (inkl. Unterordner) mit einem HandBrake-Preset nach MKV.
Der Audio-Peak wird vorher mit ffmpeg gemessen und als Gain an HandBrake übergeben.

- Automatische Wahl zwischen NVIDIA, Intel, AMD und CPU, Reihenfolge einstellbar, mit Fallback
- Wahlweise feste Bitrate (planbare Dateigröße) oder feste Qualität (gleichmäßiges Bild)
- Originale behalten oder ersetzen, optional Ausgabe in einen Zielordner
- Bereits fertige Dateien werden übersprungen, Abbrüche hinterlassen keine Reste
- Log-Datei für alle Vorgänge

Benötigt:
HandbrakeCLI: https://handbrake.fr/downloads2.php
FFmpeg: https://www.gyan.dev/ffmpeg/builds/

mcgeek.de – Startseite