nurjns

Batch Video Cutter & Encoder

Jul 14th, 2025 (edited)
1,343
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Batch 11.47 KB | None | 0 0
  1. :: Dieses Skript verarbeitet alle .mp4-Dateien im aktuellen Verzeichnis, entfernt die angegebenen Sekunden vom Anfang und Ende und komprimiert sie optional per FFmpeg mit einem wählbaren CRF-Wert. Kodiert werden kann als H.265 und AV1.. Nur Videos mit ausreichender Länge (Videolänge + 3 > Schnittbereich) werden bearbeitet. Der ursprüngliche Zeitstempel (Änderungsdatum) der Datei wird im Nachhinein neu gesetzt, und die Ausgabedateien erhalten angepasste Namen (_cut.mp4 oder _crfXX.mp4).
  2. :: Für Videos von Action-Cams, die kein GPS besitzen und daher keine Zeitzone in den Metadaten speichern, erfolgt eine extra Abfrage zur Angabe einer Zeitzone. Häufig ist auch das Aufnahmedatum in den Metadaten falsch, während nur das Änderungsdatum der Datei die korrekte Zeit enthält. Um ein korrektes Aufnahmedatum in Galerie-Programmen (z. B. Google Fotos, Immich) zu erhalten, wird das Änderungsdatum als Aufnahmedatum in die Metadaten geschrieben. Wichtig: In der Kamera muss stets die lokale Uhrzeit des Aufnahmeortes eingestellt sein. Das Skript kann auch für andere Videos genutzt werden.
  3. :: 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 der Eingabe in der Script-Abfrage. Zusätzlich wird die Zeitzone aus der Abfrage berücksichtigt.
  4. ::
  5. :: Verwendung des Scripts:
  6. ::
  7. :: Script als z. B. "encode.bat" mit Kodierung "UTF-8" anlegen.
  8. :: "ffmpeg-release-essentials.zip" hier herunterladen, entpacken und aus dem Ordner "bin" die ffmpeg.exe und ffprobe.exe in das Verzeichnis kopieren: https://www.gyan.dev/ffmpeg/builds/
  9. :: "exiftool-XXX.zip" hier herunterladen, entpacken, in das Verzeichnis kopieren und die .exe-Datei umbenennen in "exiftool.exe": https://exiftool.org/
  10. :: Version 2.3.6-pub - 04.10.2025 - @nurjns
  11.  
  12. @echo off
  13. setlocal enabledelayedexpansion
  14.  
  15. :: Benutzerabfrage: Codec-Auswahl
  16. echo Waehle Codec:
  17. echo 1 - H.265 - Gute Kompression, schnellere Kodierung, breite Unterstuetzung (Standard)
  18. echo 2 - AV1 - Beste Kompression, aber sehr langsam, fuer neuere Geraete
  19. set /p CODECWAHL="Eingabe (1 oder 2): "
  20.  
  21. if not "%CODECWAHL%"=="1" if not "%CODECWAHL%"=="2" (
  22.     echo Ungueltige Eingabe. Standard: H.265 wird verwendet.
  23.     set CODECWAHL=1
  24. )
  25.  
  26. :: Benutzerabfrage: Start-Sekunden schneiden
  27. :ASK_CUT_START
  28. set CUT_START=
  29. set /p CUT_START="Wie viele Sekunden vom ANFANG sollen entfernt werden? "
  30. if "%CUT_START%"=="" set CUT_START=0
  31. if not "%CUT_START%"=="0" (
  32.     echo %CUT_START%| findstr /r "^[0-9][0-9]*$" >nul
  33.     if errorlevel 1 (
  34.         echo Ungueltige Eingabe bei Startzeit^^!
  35.         goto ASK_CUT_START
  36.     )
  37. )
  38.  
  39. :: Benutzerabfrage: End-Sekunden schneiden
  40. :ASK_CUT_END
  41. set CUT_END=
  42. set /p CUT_END="Wie viele Sekunden vom ENDE sollen entfernt werden? "
  43. if "%CUT_END%"=="" set CUT_END=0
  44. if not "%CUT_END%"=="0" (
  45.     echo %CUT_END%| findstr /r "^[0-9][0-9]*$" >nul
  46.     if errorlevel 1 (
  47.         echo Ungueltige Eingabe bei Endzeit^^!
  48.         goto ASK_CUT_END
  49.     )
  50. )
  51.  
  52. :: Benutzerabfrage: CRF-Wert
  53. :ASK_CRF
  54. set CRF_WERT=
  55. if "%CODECWAHL%"=="1" (
  56.     echo H.265 CRF-Wert ^(18=hoch, 25=normal, 30=niedrig, 35=sehr niedrig^) - Leer lassen = nur schneiden
  57. ) else (
  58.     echo AV1 CRF-Wert ^(20=hoch, 30=normal, 38=niedrig, 45=sehr niedrig^) - Leer lassen = nur schneiden
  59. )
  60. set /p CRF_WERT="Welcher CRF-Wert soll verwendet werden? "
  61.  
  62. if not "%CRF_WERT%"=="" (
  63.     echo %CRF_WERT%| findstr /r "^[0-9][0-9]*$" >nul
  64.     if errorlevel 1 (
  65.         echo Ungueltige Eingabe bei CRF-Wert^^!
  66.         goto ASK_CRF
  67.     )
  68. )
  69.  
  70. :: Benutzerabfrage: Zeitzone
  71. :: Automatisch Sommer-/Winterzeit erkennen für Deutschland
  72. for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "$tz = [System.TimeZoneInfo]::FindSystemTimeZoneById('W. Europe Standard Time'); $now = Get-Date; if($tz.IsDaylightSavingTime($now)) {'+02:00'} else {'+01:00'}"`) do set "SYSTEM_TZ=%%i"
  73.  
  74. set /p "TIMEZONE_NECESSARY=Muss Zeitzone in Datei geschrieben werden? Notwendig bei Videos ohne GPS-Daten (y/n): "
  75. if /i "!TIMEZONE_NECESSARY!"=="y" (
  76.     set /p "TIMEZONE=Zeitzone fuer Metadaten (Format: +/-XX:00, leer fuer Standard: !SYSTEM_TZ!): "
  77.     if "!TIMEZONE!"=="" (
  78.         echo Leere Eingabe. Standard: !SYSTEM_TZ! wird verwendet
  79.         set "TIMEZONE=!SYSTEM_TZ!"
  80.     )
  81. ) else (
  82.     set "TIMEZONE="
  83. )
  84.  
  85. :: Benutzerabfrage: Welches Datum als Aufnahmedatum verwenden?
  86. echo Welches Datum soll als Aufnahmedatum verwendet werden? Bei Videos von DJI und DJI Mimo wird automatisch das Datum aus dem Dateinamen verwendet. Bei Videos mit GPS-Daten aber angegebener Zeitzone wird Auswahl 1 empfohlen.
  87. echo 1 - Aenderungsdatum der Datei
  88. echo 2 - Aufnahmedatum aus Metadaten (Standard)
  89. set /p DATETOUSE="Eingabe (1 oder 2): "
  90.  
  91. if not "%DATETOUSE%"=="1" if not "%DATETOUSE%"=="2" (
  92.     echo Ungueltige Eingabe. Standard: "Aenderungsdatei der Datei" wird verwendet
  93.     set DATETOUSE=2
  94. )
  95.  
  96. :: Benutzerabfrage: Audio entfernen?
  97. set /p REMOVE_AUDIO="Ton entfernen? (y/n): "
  98. if /i "%REMOVE_AUDIO%"=="y" (
  99.     set "AUDIO_PARAM=-an"
  100.     echo Audio wird entfernt
  101. ) else (
  102.     set "AUDIO_PARAM=-c:a aac -b:a 160k"
  103. )
  104.  
  105. :: Schleife durch alle MP4-Dateien
  106. for %%F in (*.mp4) do (
  107.     :: Prüfen ob bereits verarbeitetes Video (enthält _crf, _cut oder _AV1)
  108.     set "IS_PROCESSED=0"
  109.     echo %%F | findstr /i "_crf" >nul && set "IS_PROCESSED=1"
  110.     echo %%F | findstr /i "_cut" >nul && set "IS_PROCESSED=1"
  111.     echo %%F | findstr /i "_AV1" >nul && set "IS_PROCESSED=1"
  112.    
  113.     if !IS_PROCESSED! == 1 (
  114.         echo [OK] Ignoriert: %%F - bereits verarbeitetes Video
  115.     ) else (
  116.         :: Prüfen ob die Datei ein gültiges Video ist
  117.         ffprobe -v error -select_streams v:0 -show_entries format=duration -of default=nokey=1:noprint_wrappers=1 "%%F" >nul 2>&1
  118.         if !errorlevel! NEQ 0 (
  119.             echo [FEHLER] Ignoriert: %%F - ungueltige oder beschaedigte Videodatei
  120.         ) else (
  121.             :: Prüfen ob bereits ein gerendertes Video mit EXAKT den gleichen Einstellungen existiert
  122.             set "BASENAME=%%~nF"
  123.             set "RENDERED_EXISTS=0"
  124.            
  125.             :: Prüfen ob ein Video ohne CRF (nur _cut) existiert und aktuell auch ohne CRF verarbeitet werden soll
  126.             if "%CRF_WERT%"=="" (
  127.                 if exist "!BASENAME!_cut.mp4" set "RENDERED_EXISTS=1"
  128.             ) else (
  129.                 :: CRF-Wert ist gesetzt - prüfen auf exakte Übereinstimmung
  130.                 if "%CODECWAHL%"=="1" (
  131.                     :: H.265 - prüfen auf exakte CRF-Übereinstimmung
  132.                     if exist "!BASENAME!_crf!CRF_WERT!.mp4" set "RENDERED_EXISTS=1"
  133.                 ) else (
  134.                     :: AV1 - prüfen auf exakte CRF-Übereinstimmung
  135.                     if exist "!BASENAME!_AV1_crf!CRF_WERT!.mp4" set "RENDERED_EXISTS=1"
  136.                 )
  137.             )
  138.            
  139.             if !RENDERED_EXISTS! == 1 (
  140.                 echo [OK] Ignoriert: %%F - bereits mit gleichen Einstellungen gerendert
  141.             ) else (
  142.                 echo Bearbeite: %%F
  143.                
  144.                 :: Prüfen ob DJI Action Video, falls ja, Datum aus Dateinamen verwenden
  145.                 echo %%~nF | findstr /r /i "^DJI_[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_" >nul
  146.                 if !errorlevel! == 0 (
  147.                     :: Dateiname ohne Extension
  148.                     set "FNAME=%%~nF"
  149.                     :: 14-stellige Zeitkette nach "DJI_"
  150.                     for /f "tokens=2 delims=_" %%A in ("!FNAME!") do (
  151.                         set "DATETIME=%%A"
  152.                     )
  153.                     set "YYYY=!DATETIME:~0,4!"
  154.                     set "MM=!DATETIME:~4,2!"
  155.                     set "DD=!DATETIME:~6,2!"
  156.                     set "hh=!DATETIME:~8,2!"
  157.                     set "nn=!DATETIME:~10,2!"
  158.                     set "ss=!DATETIME:~12,2!"
  159.                     :: Aufnahmedatum mit Offset festlegen
  160.                     set "TIMESTAMP=!YYYY!:!MM!:!DD! !hh!:!nn!:!ss!"
  161.                     echo [INFO] DJI Action Video erkannt, verwende Aufnahmedatum aus Dateinamen...
  162.                
  163.                 ) else (
  164.                     :: Prüfen ob DJI Mimo Datei, falls ja, Datum aus Dateinamen verwenden
  165.                     echo %%~nF | findstr /b /i "dji_mimo_" >nul
  166.                     if !errorlevel! == 0 (
  167.                         :: Dateiname ohne Extension
  168.                         set "FNAME=%%~nF"
  169.                         :: tokens=3,4: überspringe "dji_mimo"
  170.                         for /f "tokens=3,4 delims=_" %%A in ("!FNAME!") do (
  171.                             set "FILEDATE=%%A"
  172.                             set "FILETIME=%%B"
  173.                         )
  174.                         set "YYYY=!FILEDATE:~0,4!"
  175.                         set "MM=!FILEDATE:~4,2!"
  176.                         set "DD=!FILEDATE:~6,2!"
  177.                         set "hh=!FILETIME:~0,2!"
  178.                         set "nn=!FILETIME:~2,2!"
  179.                         set "ss=!FILETIME:~4,2!"
  180.                         :: Aufnahmedatum mit Offset festlegen
  181.                         set "TIMESTAMP=!YYYY!:!MM!:!DD! !hh!:!nn!:!ss!"
  182.                         echo [INFO] DJI Mimo Datei erkannt, verwende Aufnahmedatum aus Dateinamen...
  183.                     ) else (
  184.                         :: Datum entsprechend der Benutzerwahl auslesen
  185.                         if "!DATETOUSE!"=="2" (
  186.                             :: Aufnahmedatum aus Video-Metadaten (Create Date) verwenden, sofern vorhanden
  187.                             for /f "usebackq delims=" %%T in (`exiftool.exe -s3 -CreateDate "%%F"`) do (
  188.                                 set "TIMESTAMP=%%T"
  189.                             )
  190.                             if "%TIMESTAMP%"=="0000:00:00 00:00:00" set "TIMESTAMP="
  191.                             if not defined TIMESTAMP (
  192.                                 echo Warnung: Kein Aufnahmedatum gefunden fuer %%F, verwende Aenderungsdatum
  193.                                 for /f "usebackq delims=" %%T in (`powershell -NoLogo -NoProfile -Command "(Get-Item '%%F').LastWriteTime.ToString('yyyy:MM:dd HH:mm:ss')"`) do (
  194.                                     set "TIMESTAMP=%%T"
  195.                                 )
  196.                             )
  197.                         ) else (
  198.                             :: Änderungsdatum der Datei (Standard)
  199.                             for /f "usebackq delims=" %%T in (`powershell -NoLogo -NoProfile -Command "(Get-Item '%%F').LastWriteTime.ToString('yyyy:MM:dd HH:mm:ss')"`) do (
  200.                                 set "TIMESTAMP=%%T"
  201.                             )
  202.                         )
  203.                     )
  204.                 )
  205.                
  206.                 for /f "usebackq delims=" %%D in (`ffprobe -v error -select_streams v:0 -show_entries format^=duration -of default^=nokey^=1:noprint_wrappers^=1 "%%F"`) do (
  207.                     set "DURATION=%%D"
  208.                 )
  209.                 for /f "tokens=1 delims=." %%T in ("!DURATION!") do set /a INTDURATION=%%T
  210.                 set /a CUTS_TOTAL=!CUT_START!+!CUT_END!
  211.                 set /a VIDEO_PLUS_3=!INTDURATION!+3
  212.                 if !VIDEO_PLUS_3! GTR !CUTS_TOTAL! (
  213.                     set /a REMAINING=!INTDURATION!-!CUTS_TOTAL!
  214.                     if !REMAINING! GEQ 1 (
  215.                         set "OUTFILE=%%~nF"
  216.                         if "%CRF_WERT%"=="" (
  217.                             set "OUTFILE=!OUTFILE!_cut.mp4"
  218.                             ffmpeg -y -i "%%F" -ss !CUT_START! -t !REMAINING! -c copy !AUDIO_PARAM! "!OUTFILE!"
  219.                         ) else (
  220.                             if "%CODECWAHL%"=="1" (
  221.                                 set "OUTFILE=!OUTFILE!_crf!CRF_WERT!.mp4"
  222.                                 ffmpeg -y -ss !CUT_START! -i "%%F" -t !REMAINING! -c:v libx265 -crf !CRF_WERT! -preset slow -pix_fmt yuv420p -tag:v hvc1 -movflags +faststart !AUDIO_PARAM! "!OUTFILE!"
  223.                             ) else (
  224.                                 :: AV1 - CPU-Used dynamisch bestimmen
  225.                                 if !CRF_WERT! LEQ 20 (
  226.                                     set "CPU_USED=2"
  227.                                 ) else if !CRF_WERT! LEQ 28 (
  228.                                     set "CPU_USED=4"
  229.                                 ) else if !CRF_WERT! LEQ 35 (
  230.                                     set "CPU_USED=6"
  231.                                 ) else (
  232.                                     set "CPU_USED=8"
  233.                                 )
  234.                                 set "OUTFILE=!OUTFILE!_AV1_crf!CRF_WERT!.mp4"
  235.                                 ffmpeg -y -ss !CUT_START! -i "%%F" -t !REMAINING! -c:v libaom-av1 -crf !CRF_WERT! -cpu-used !CPU_USED! -pix_fmt yuv420p -movflags +faststart !AUDIO_PARAM! "!OUTFILE!"
  236.                             )
  237.                         )
  238.  
  239.                         :: ExifTool: UTC in MP4/QuickTime-Felder, lokale Zeit + Offset in EXIF
  240.                         if defined TIMEZONE (
  241.                             exiftool.exe -overwrite_original ^
  242.                                 "-DateTimeOriginal=!TIMESTAMP!!TIMEZONE!" ^
  243.                                 "-OffsetTimeOriginal=!TIMEZONE!" ^
  244.                                 "-FileModifyDate=!TIMESTAMP!" ^
  245.                                 "!OUTFILE!"
  246.                         ) else (
  247.                             exiftool.exe -overwrite_original -P -TagsFromFile "%%F" ^
  248.                                 "-DateTimeOriginal" ^
  249.                                 "-CreateDate" ^
  250.                                 "-ModifyDate" ^
  251.                                 "-FileModifyDate" ^
  252.                                 "-OffsetTimeOriginal" ^
  253.                                 "-OffsetTime" ^
  254.                                 "-OffsetTimeDigitized" ^
  255.                                 "!OUTFILE!"
  256.                         )
  257.          
  258.                         echo [OK] Verarbeitet: !OUTFILE!
  259.                     ) else (
  260.                         echo [OK] Uebersprungen: %%F - nur !REMAINING!s wuerden uebrig bleiben
  261.                     )
  262.                 ) else (
  263.                     echo [OK] Uebersprungen: %%F - !VIDEO_PLUS_3! nicht > !CUTS_TOTAL!
  264.                 )
  265.             )
  266.         )
  267.     )
  268. )
  269.  
  270. echo.
  271. powershell -c [console]::beep(500,200)
  272. echo Alle Videos wurden verarbeitet.
  273. pause
Advertisement