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/