Compress videofiles from context menu in Windows 11 using ffmpeg - cmd

I'd looking for an easy way to just right click on a folder in Windows and be able to loop through video files and compress them with ffmpeg with prepared command settings.
Been able to prepare the reg file to import but I think I'm missing something important here (working directory reference I think). How should I pass the working dir path inside a command like that for this mechanism to work?
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\Shell\Compress_videos]
#="Compress videos"
[HKEY_CLASSES_ROOT\Directory\Shell\Compress_videos\command]
#="cmd /c for \"%E\" in (.mp4, .mkv, .mov, .avi) do forfiles /m *\"%E\" /c \"cmd /c C:\\Windows\\System32\\ffmpeg.exe -hide_banner -y -hwaccel cuda -i #path -c:v hevc_nvenc -b:v 8000k -c:a copy #fname_compressed.mp4\""

This will loop through and do like you want. (Warning: I didn't test it.)
cmd /c for %E in (.mp4, .mkv, .mov, .avi) do forfiles /m *%E /c "cmd /c ffmpeg -i \"#file\" -c:v libx264 -crf 20 -c:a aac -b:a 128k \"#fname.mp4\""

Related

How to loop through my text file and call the name of picture

I need to take all audio files from a directory, and export them along with one of multiple pictures listed in a text file.
It's working to take all the audio files one by one, but I cannot make it have a different picture for each video. I have same picture all the time.
I don't know how to make the loop working for the pictures files.
Will be nice if someone can tell me how I can group pictures in order to export as a slideshow.
Thank you.
In .txt file my pictures are like this :
1.jpg
2.jpg
3.jpg
And my code looks like this:
#echo off
set "sourcedir=E:\test slideshow imagini"
set "outputdir=E:\test slideshow imagini\1"
PUSHD "%sourcedir%"
for /F "tokens=*" %%A in (poze3.txt) do (
echo %%A
:: pictures names
for %%F in (*.wav *.mp3) DO ffmpeg -loop 1 -i %%A -i "%%F" -c:v libx264 -tune stillimage -c:a aac -b:a 192k -pix_fmt yuv420p -shortest -vf scale=1920:1080 "%outputdir%\%%F.mp4"
POPD
)
You need a trick to read the next line of a file with each run of the loop:
#echo off
setlocal enabledelayedexpansion
set "sourcedir=E:\test slideshow imagini"
set "outputdir=E:\test slideshow imagini\1"
PUSHD "%sourcedir%"
<"poze3.txt" (
for %%F in (*.wav *.mp3) DO (
set /p pic=
ffmpeg -loop 1 -i !pic! -i "%%F" -c:v libx264 -tune stillimage -c:a aac -b:a 192k -pix_fmt yuv420p -shortest -vf scale=1920:1080 "%outputdir%\%%F.mp4"
)
)
(untested, but should work).
Btw: DO NOT use :: as comment. Use REM instead.
I know, :: is used often and behaves well outside of code blocks, but tends to hiccup when used within code blocks, leading to unexpected behaviour which is hard to troubleshoot.
I removed the popd in the loop. Repeatedly popd's without matching push's don't make sense. You do only one pushd before the loop, so the matching popd should be after the loop, where you can omit it, as the script ends anyway.

ffmpeg overwrites input files when called in a forfiles loop

I need to join several mp4 and wav file pairs using ffmpeg.
When I run the command specifying the names of the files, it works well:
.\ffmpeg.exe -i file01.mp4 -i file01.wav -c:v copy -c:a aac -b:a 96k file01_new.mp4
But when I integrate this call in a forfiles loop, the source mp4 file is overwritten and the output mp4 file contains only the audio:
forfiles /M "*.mp4" /C ".\ffmpeg.exe -i #FNAME.mp4 -i #FNAME.wav -c:v copy -c:a aac -b:a 96k #FNAME_new.mp4"
I don't really understand what happens before the MP4 and WAV files are joined. Why is the source MP4 being overwritten?
How do I write this script to make it work? Thank you for your support.
Let me recommend to use for rather than forfiles, because the latter is quite slow and it expands its #-variables with surrounding quotes, which could be problematic:
rem // Create sub-directory for resulting files, so resulting files cannot become reprocessed:
2> nul md "result"
rem // Loop through `*.mp4` files:
for %%I in ("*.mp4") do (
rem /* Process current file, together with the related `*.wav` file,
rem and write the result into the new sub-directory: */
ffmpeg.exe -i "%%~I" -i "%%~nI.wav" -c:v copy -c:a aac -b:a 96k "result\%%~I"
)

How to execute ffmpeg from with Batch For Loop

FOR %%A IN (%*) DO ffmpeg -y -i %%A -c:v prores_ks -profile:v 3 -c:a pcm_s16le "%%~dpA%%~nA.mov"
This batch file is intended to take multiple files which are dragged and dropped onto it, and transcode them with ffmpeg. The command results in a whole bunch of prinouts that are similar to the command, but it does not execute. I have also tried using start cmd.exe /c, but that causes numerous windows to open which necessitates a computer reboot.
Edit: squashman has made it clear that this solution would not work anyway (Thanks!). However, I have edited the script to act on only a single file at once and it still does not execute as intended and causes a looping printout.
ffmpeg -y -i %%1 -c:v prores_ks -profile:v 3 -c:a pcm_s16le "%%~dp1%%~n1.mov"
How can I modify this script to execute ffmpeg properly?
Edit 2: Resolved by dragging and dropping a folder onto the batch script.
cd /d %1
md output
FOR /F %%A IN ('dir /B %1') do ffmpeg -y -i %%A -c:v prores_ks -profile:v 3 -c:a pcm_s16le "output/%%~nA.mov"
pause
Expect all .mov files reside in current directory. if not, prepend path into both of "*.mov" and "%%~A.mov".
FOR /F "tokens=1,2 delims=." %%A IN ('dir /B *.mov') do ffmpeg -y -i %%A -c:v prores_ks -profile:v 3 -c:a pcm_s16le "%%~A.mov"
another way is to use PPX http://www.pirosa.co.uk/demo/wxargs/wxargs.html and speed-up whole process by utilizing all CPU cores
dir /b *.%required_file_extension% |ppx2 -P 4 -L 1 ffmpeg -y -i %%A -c:v prores_ks -profile:v 3 -c:a pcm_s16le "{}.mov"
Worth also considering using a build system like SCons.
I've coded up something similar here to transcode a directory's worth of video files all at once from the command-line:
https://github.com/nuket/Shrinkr#shrinkr-batch-transcode-scons-edition
Benefit of a build system is it tracks which source files have changed, so you can interrupt and re-run the batch transcode and it won't repeat already-completed work.

Parallel processing with Pipe command

I'd like to parallel process the command that downloads a live stream.
So if it has 4 parts and the PARTS variable contains the number 4, it should open 4 new cmd windows and process the individual part.
After reading a lot about parallel processing I came to the following solution:
set /p URL=Enter video URL:
set /p NAME=Enter video name:
set /p PARTS=Enter Number of Parts:
for /l %%x in (1, 1, %PARTS%) do (
start cmd /C "C:\Program Files (x86)\Streamlink\bin\streamlink.exe" -O "%URL%/%%x" best | ffmpeg -y -i pipe:0 -c:v copy -c:a copy -absf aac_adtstoasc "%NAME%p%%x.mp4"
)
There seems to be an issue with the | command though since this script would open windows that close right after start and the output of the piped command ffmpeg -y -i pipe:0 -c:v copy -c:a copy -absf aac_adtstoasc "%NAME%p%%x.mp4 would show in the initial cmd window that executed the script.
How can I change it so the whole command gets executed in the new window?
To not let the pipe process the output of the start command, you need to escape it:
start "" cmd /C "C:\Program Files (x86)\Streamlink\bin\streamlink.exe" -O "%URL%/%%x" best ^| ffmpeg -y -i pipe:0 -c:v copy -c:a copy -absf aac_adtstoasc "%NAME%p%%x.mp4"
Since quotation is not modified this way, the used path and all variable parts still appear quoted to the calling cmd instance too, so no more additional escaping is required, unless these strings may contain quotation marks on their own, in which case I strongly recommend delayed expansion:
setlocal EnableDelayedExpansion
rem // some other code...
set /P URL="Enter video URL: "
set /P NAME="Enter video name: "
rem // some other code...
start "" cmd /C "C:\Program Files (x86)\Streamlink\bin\streamlink.exe" -O "!URL!/%%x" best ^| ffmpeg -y -i pipe:0 -c:v copy -c:a copy -absf aac_adtstoasc "!NAME!p%%x.mp4"
rem // some other code...
endlocal
The "" behind the start command should be stated to provide a window title; otherwise an error could occur as the first quoted item was taken as the title rather than as part of the command line.
The above line still could cause problems, since the cmd instance executing the actual commands receives the already expanded values rather than the variable. So you might even need to do this:
rem // Supposing delayed expansion is disabled in the hosting `cmd` instance:
start "" cmd /C /V "C:\Program Files (x86)\Streamlink\bin\streamlink.exe" -O "!URL!/%%x" best ^| ffmpeg -y -i pipe:0 -c:v copy -c:a copy -absf aac_adtstoasc "!NAME!p%%x.mp4"
setlocal EnableDelayedExpansion
rem // Supposing delayed expansion is enabled in the hosting `cmd` instance:
start "" cmd /C /V "C:\Program Files (x86)\Streamlink\bin\streamlink.exe" -O "^!URL^!/%%x" best ^| ffmpeg -y -i pipe:0 -c:v copy -c:a copy -absf aac_adtstoasc "^!NAME^!p%%x.mp4"
endlocal
Note that the pipe | creates two more cmd instances one for either side, implicitly.

Convert .MOD video files to .mp4 but maintain original file date

Windows 10,
ffmpeg
I'm very new to ffmpeg so I can't figure this out. I'm trying to use a command to copy then convert all .MOD video files in a directory to .mp4 files and keep the original date that the .MOD file was created. I don't understand how to map the current file in the loop to the map_metadata option. This command works but doesn't maintain the metadata (taken from this post)
FOR /F "tokens=*" %G IN ('dir /b *.MOD') DO ffmpeg -i "%G" -acodec copy "%~nG.mp4"
I've tried including the map in the above command but get various errors, mostly "invalid input file index: 1". The commands below will copy and convert but the new .mp4 files don't have the original file date so I must be using map_metadata incorrectly:
FOR /F "tokens=*" %G IN ('dir /b *.MOD') DO ffmpeg -i "%G" -acodec copy -map_metadata 0 "%~nG.mp4"
FOR /F "tokens=*" %G IN ('dir /b *.MOD') DO ffmpeg -i "%G" -map_metadata 0 -acodec copy "%~nG.mp4"
Any suggestions? Thanks
UPDATE
I got this with Powershell! (thanks to this post)
$oldvids = Get-ChildItem *.MOD -Recurse
foreach ($oldvid in $oldvids) {
$newvid = [io.path]::ChangeExtension($oldvid.LastWriteTime.toString("MMMddyyyy_HHmmss"), '.mp4')
ffmpeg.exe -i $oldvid.FullName -c:v libx264 -crf 18 -c:a aac -q:a 100 $newvid
}
input: MOV01E.MOD (Created date 4/1/2012 10:10 AM)
output: Apr012012_101005.mp4
ANOTHER UPDATE
The above command kind of works but I just realized all of the files are output to the root directory. I changed the command a bit but I'm not sure what's going on:
Get-ChildItem *.MOD -recurse | % {
$newvid = [io.path]::ChangeExtension($_.LastWriteTime.toString("MMMddyyyy_HHmmss"), '.mp4')
ffmpeg.exe -i $_.FullName -c:v libx264 -crf 50 -c:a aac -q:a 100 $newvid
}
I just needed to change the output file to include the directory path
Get-ChildItem *.MOD -recurse | % {
$newvid = [io.path]::ChangeExtension($_.LastWriteTime.toString("MMMddyyyy_HHmmss"), '.mp4')
ffmpeg.exe -i $_.FullName -c:v libx264 -crf 20 -c:a aac -q:a 100 ($_.DirectoryName + "\" + $newvid)
}

Resources