So I made a batch script originally with the ability to relatively precision trim a video into chapters without having to run by keyframes, but the code looks horrible and I can't get it to loop through all mp4 files nor get mkvmerge to append the files after splitting them. Code is below but be gentle it is my first try.
@echo off
setlocal enableDelayedExpansion
REM CODE BELOW CREATES JSON FILES FOR ALL MP4 FILES WITHIN THE SAME DIRECTORY
ffprobe -v quiet -print_format json -show_chapters -loglevel error "01x01.mp4" > "01x01.json"
REM CODE BELOW SETS VARIABLES FROM EACH SPECIFIC JSON
FOR /F "delims=" %%i in ('jq .chapters[2].start ^< 01x01.json') DO SET /A start1=%%i
FOR /F "delims=" %%j in ('jq .chapters[2].end ^< 01x01.json') DO SET /A end1=%%j
FOR /F "delims=" %%k in ('jq .chapters[4].start ^< 01x01.json') DO SET /A start2=%%k
FOR /F "delims=" %%l in ('jq .chapters[4].end ^< 01x01.json') DO SET /A end2=%%l
FOR /F "delims=" %%m in ('jq .chapters[6].start ^< 01x01.json') DO SET /A start3=%%m
FOR /F "delims=" %%n in ('jq .chapters[6].end ^< 01x01.json') DO SET /A end3=%%n
FOR /F "delims=" %%o in ('jq .chapters[8].start ^< 01x01.json') DO SET /A start4=%%o
FOR /F "delims=" %%p in ('jq .chapters[8].end ^< 01x01.json') DO SET /A end4=%%p
REM SETS THE DURATION OF EACH FILE TO USE PRECISION TIMING FOR START AND STOP TIMES
CALL vbs (%end1%-%start1%)/1000
SET duration1=%val%
CALL vbs (%end2%-%start2%)/1000
SET duration2=%val%
CALL vbs (%end3%-%start3%)/1000
SET duration3=%val%
CALL vbs (%end4%-%start4%)/1000
SET duration4=%val%
REM SETS THE START TIME IN SECONDS VS MILLISECONDS
CALL vbs (%start1%)/1000
SET start1=%val%
CALL vbs (%start2%)/1000
SET start2=%val%
CALL vbs (%start3%)/1000
SET start3=%val%
CALL vbs (%start4%)/1000
SET start4=%val%
REM TRIM AND SPLIT ORIGINAL FILE INTO SEPERATE SECTIONS BASED ON CHAPTER MARKERS
ffmpeg -ss %START1% -i 01x01.mp4 -ss 0 -c copy -to %DURATION1% -avoid_negative_ts make_zero 01x01-1.mp4
ffmpeg -ss %START2% -i 01x01.mp4 -ss 0 -c copy -to %DURATION2% -avoid_negative_ts make_zero 01x01-2.mp4
ffmpeg -ss %START3% -i 01x01.mp4 -ss 0 -c copy -to %DURATION3% -avoid_negative_ts make_zero 01x01-3.mp4
ffmpeg -ss %START4% -i 01x01.mp4 -ss 0 -c copy -to %DURATION4% -avoid_negative_ts make_zero 01x01-4.mp4
REM DELETES UNNEEDED JSON AFTER USE
del /s *.json
REM APPEND ALL MP4 FILES INTO COHESIVE MKV
for /d /r %%D in (*) do (
pushd %%D
set files=
for %%F in (*.mp4) do set files=!files! + ^( "%%F" ^)
if not "!files!"=="" %mkvmerge% -o "01x01-FINAL.mkv" !files:~2!
popd
)
REM DELETE UNNEEDED MP4 ORIGINALS AND SPLIT FILES
del /s *.mp4
I know it is super long and every time I try to use a variable or a loop to run through all files it can't read the json file. I've been at this all day and I can use the script as is but I have to make a file for each iteration.
I was also hoping to be able to have it only pull chapters labeled as "video" but I haven't quite figured that one out yet.
I'll add the vbs batch file for the arithmetic section as well as the sample json if it will help.
@echo off
>"%temp%\VBS.vbs" echo Set fso = CreateObject("Scripting.FileSystemObject") : Wscript.echo (%*)
for /f "delims=" %%a in ('cscript /nologo "%temp%\VBS.vbs"') do set "val=%%a"
del "%temp%\VBS.vbs"
{
"chapters": [
{
"id": 0,
"time_base": "1/1000",
"start": 0,
"start_time": "0.000000",
"end": 5590,
"end_time": "5.590000",
"tags": {
"title": "Video"
}
},
{
"id": 1,
"time_base": "1/1000",
"start": 5590,
"start_time": "5.590000",
"end": 13994,
"end_time": "13.994000",
"tags": {
"title": "Advertisement"
}
},
{
"id": 2,
"time_base": "1/1000",
"start": 13994,
"start_time": "13.994000",
"end": 163964,
"end_time": "163.964000",
"tags": {
"title": "Video"
}
},
{
"id": 3,
"time_base": "1/1000",
"start": 163964,
"start_time": "163.964000",
"end": 195940,
"end_time": "195.940000",
"tags": {
"title": "Advertisement"
}
},
{
"id": 4,
"time_base": "1/1000",
"start": 195940,
"start_time": "195.940000",
"end": 547849,
"end_time": "547.849000",
"tags": {
"title": "Video"
}
},
{
"id": 5,
"time_base": "1/1000",
"start": 547849,
"start_time": "547.849000",
"end": 595850,
"end_time": "595.850000",
"tags": {
"title": "Advertisement"
}
},
{
"id": 6,
"time_base": "1/1000",
"start": 595850,
"start_time": "595.850000",
"end": 1413588,
"end_time": "1413.588000",
"tags": {
"title": "Video"
}
},
{
"id": 7,
"time_base": "1/1000",
"start": 1413588,
"start_time": "1413.588000",
"end": 1477569,
"end_time": "1477.569000",
"tags": {
"title": "Advertisement"
}
},
{
"id": 8,
"time_base": "1/1000",
"start": 1477569,
"start_time": "1477.569000",
"end": 1529696,
"end_time": "1529.696000",
"tags": {
"title": "Video"
}
}
]
}
I also tried using the start_time so I didn't have to do extra calculations but jq didn't like that either.
mkvmerge doesn't even try to run when I have it in here and I still need to cut 7 seconds off the end and 12 seconds off the front of it once it is all one file again.
Any help would be appreciated, I know it's a lot but I seem to have hit a roadblock or just sleep deprived at this point.
UPDATE
This works amazing I just need to figure out how to use files with spaces and I'm all set. I guess I could run a batch before hand replacing all spaces with underscores. That would probably work but I would like to not change filenames if I can help it.
@echo off
for %%i in (*.mp4) do (
FOR /F "delims=" %%A IN ('ffprobe -v quiet -print_format json -show_chapters -loglevel error "%%i" ^| xidel - -se "$json/(chapters)()[id!=0 and tags/title='Video']/concat('ffmpeg -ss ',start div 1000,' -i %%i -to ',((end - start) div 1000),' -c copy -avoid_negative_ts make_zero %%~ni-',position(),'.mp4')"') DO %%A
FOR /F "delims=" %%A IN ('xidel -s --xquery "concat('mkvmerge -o "%%~ni-FINAL.mkv" "',join(file:list(.,false(),'%%~ni-*.mp4'),'" + "'),'"')"') DO %%A
)
If I understand correctly, you want to remove the "Advertisement"-chapters.
This could probably be done entirely with
ffmpeg
, but without the source video I can't say for sure.Anyway, let me offer you a very elegant solution for the splitting procedure with the help of xidel.
First of all, you don't have to save
ffprobe
's output to a JSON-file. You can just pipe it toxidel
:Select the objects/chapters labeled as "Video":
(I'm not sure why you don't want the first chapter. If you do, then just remove
id!=0 and
.)To calculate the duration of the 4 chapters:
Create the
ffmpeg
strings/commands with these values andstart
:Prettified command/query:
To execute these
ffmpeg
commands simply put the entireffprobe
/xidel
command in aFOR
-loop:(Within a Batch-script use
%%A
and in case ofSETLOCAL ENABLEDELAYEDEXPANSION
escape the exclamationmarks;id^!=0
)With all of
cmd
's shortcomings I would again usexidel
to create themkvmerge
string/command:To execute the
mkvmerge
command again simply put the entirexidel
command in aFOR
-loop:(Why
--xquery
and"
? Because\"
will cause "escape-character-hell" in aFOR
-loop)If you use just these two
FOR
-loops, it also means thatSETLOCAL ENABLEDELAYEDEXPANSION
isn't necessary anymore.