Thanks for the merge python script, I'll try that.
You can also merge with SE: tools -> join subtitles
Just add the 2 subs and click "join".
Then: tools > Sort > By start time
Looking at your subs I see what happens: the missing subs come from the merge, but that has a penalty: there are parts where you have both subs generated by whisper and those merged for the same dialog.
Example starting at 47:42:507 for E02 (timestamp with my version):
One sub with 2 lines:
And then 2 subs with 2 lines and one line:Code:I always believed that I had a future in this country.
Code:I always believed we had a futureThat's a bit of a mess!Code:in this country.
About syncing subs.
Most US shows are designed to have ads. These ads are generally inserted at scene changes, when the video goes dark briefly.
2 different versions of a same show (web-dl and FOX edited to remove ads for example) will show a timing discrepancy at the scene changes.
My process is:
1) Use comskip, a tool to find ads, to detect scene changes to produce a VideoRedo .vprj file.
I use:
comskip82_010_donators\comskip.exe --threads=20 --videoredo --detectmethod=95 --verbose=0 "Bones S01-E01.mkv"
Even if the adds have already been removed, comskip usually finds where
2) Open the VideoRedo project. VideoRedo will clearly show where the ads where. It will show many false positions, but using F6 to navigate from one to the next shows the image, and when it's dark, it's likely where the adds were.
Example with an old FOX show where the adds were already removed:
[Attachment 89434 - Click to enlarge]
Use SE to adjust at the beginning using "Set start and offset the rest". Then navigate to the next scene change that you see with VideoRedo. Check before the timestamp if it's in sync (usually it is) and there after. If it's not, sync it at this location with ""Set start and offset the rest"".
That does not work with all the shows (not with Kabul for example that never had embedded ads anyway), but in my experience with many of them.
Not sure if the donator version of comskip is still available. Or if it's actually needed.
VideoRedo is no longer sold, it's hard to activate it now but I read somewhere that somebody has the rights to it now and can provide a legal way. You would need to do a search for that.
If I get more information, I'll send you a PM.
+ Reply to Thread
Results 61 to 77 of 77
-
-
SE engine is the culprit. No problems with CMD>CLIExample starting at 47:42:507 for E02 (timestamp with my version):
One sub with 2 lines:
Code:
I always believed that I
had a future in this country.
And then 2 subs with 2 lines and one line:
Code:
I always believed
we had a future
Code:
in this country.
[Attachment 89439 - Click to enlarge]
Doing the transcription again.Will update you with new subs soon.
Meanwhile, read my PM -
Uploaded complete clean subs of Kabul (Mini TV Series) 2025
https://www.opensubtitles.org
Uploader: SamGer
https://www.opensubtitles.org/en/ssearch/sublanguageid-eng/idmovie-2350514
https://sub-scene.com/
https://sub-scene.com/subtitle/3363962 -
Great, that will save me the trouble doing all the subs myself.
I actually tried to upload the Assembly ones, they were rejected because it's too obvious that they are AI generated.
Edit:
But there are still subs that overlap:
[Attachment 89446 - Click to enlarge]Last edited by robena; 29th Oct 2025 at 12:53.
-
Hi,
So, here is a perfect way for this Kabul series.
This uses my way to number episodes: "Kabul S01-E01.mkv"
This will likely fail if using another convention such as " "Kabul S01E01.mkv"
1) Use Faster-Whisper-XXL_r245.1_windows with this model (based on a REXX script, easy to transcribe for something else):
That outputs a file such as "Kabul S01-E01.srt" that my REXX script renames to "Kabul S01-E01-en.srt"Code:/* Just in case you want something else than English */ if lang = 'en' then do task = ' --task translate' prompt = ' --initial_prompt "Translate everything to English."' end else do task = ' --task transcribe' prompt = ' --initial_prompt "Transcribe in 'lang'."' end '--model large-v3' , task , ' --language 'lang , prompt , ' --device cuda' , ' --compute_type float16' , ' --batch_size 8' , ' --vad_method pyannote_onnx_v3' , ' --vad_device cuda' , ' --beep_off', ' --vad_threshold 0.1' , /* ULTRA LOW */ ' --vad_min_speech_duration_ms 50' , /* 50ms = catch whispers */ ' --vad_min_silence_duration_ms 100' , /* tighter gaps */ ' --hallucination_silence_threshold 0.6' , ' --no_speech_threshold 0.1' , /* catch ANY speech */ ' --logprob_threshold -2.0' , /* keep low-conf */ ' --compression_ratio_threshold 2.4' , ' --beam_size 5' , ' --best_of 5' , ' --temperature 0' , ' --repetition_penalty 1.1' , ' --no_repeat_ngram_size 3' , ' --condition_on_previous_text False' , ' --word_timestamps True' , ' --output_format all' , /* JSON + SRT */ ' --output_dir "'fdd(file)'"'
*** Having a filename ending with "-something" is necessary.
Then, store in the same directory: "Kabul S01-E01-F.srt"
These are the forced subs for the foreign dialogs.
*** Having '-F' is necessary.
Whisper has translated these foreign dialogs, but we want those that are already in "Kabul S01-E01-F.srt".
To get that, merge with this python script:
that will produce "Kabul S01-E01.srt"Code:#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ merge_subtitles.py - - -F.srt loaded FIRST - Every forced sub PRESERVED exactly - Whisper fills gaps OR is replaced on overlap - No duplicates, no loss """ import sys from pathlib import Path from typing import List, Tuple def time_to_seconds(t: str) -> float: h, m, s_ms = t.split(":") s, ms = s_ms.replace(",", ".").split(".") return int(h) * 3600 + int(m) * 60 + float(s) + float(ms) / 1000 def seconds_to_srt(sec: float) -> str: h = int(sec // 3600) m = int((sec % 3600) // 60) s = sec % 60 return f"{h:02}:{m:02}:{s:06.3f}".replace(".", ",")[:12] def parse_srt_robust(content: str, filename: str) -> List[Tuple[float, float, List[str], str]]: entries = [] lines = content.splitlines() i = 0 while i < len(lines): if lines[i].strip().isdigit(): i += 1 if i >= len(lines): break time_line = lines[i].strip() if "-->" not in time_line: i += 1 continue try: start_str, end_str = time_line.split("-->", 1) start = time_to_seconds(start_str.strip()) end = time_to_seconds(end_str.strip()) except: i += 1 continue i += 1 text_lines = [] while i < len(lines) and lines[i].strip() and not lines[i].strip().isdigit(): text_lines.append(lines[i].strip()) i += 1 if text_lines: entries.append((start, end, text_lines, filename)) else: i += 1 return entries def is_forced(filename: str) -> bool: return any(k in filename.lower() for k in ("-f.", "-forced", ".f.", "forced")) def main(mkv_path: str) -> None: mkv = Path(mkv_path) if not mkv.exists(): print(f"[ERROR] File not found: {mkv}") sys.exit(1) folder = mkv.parent base_name = mkv.stem output_srt = folder / f"{base_name}.srt" if output_srt.exists(): print("Skipping merge, file exists") return srt_files = list(folder.glob(f"{base_name}*.srt")) if not srt_files: print(f"[INFO] No SRT files") return forced_file = next((f for f in srt_files if is_forced(f.name)), None) whisper_file = next((f for f in srt_files if not is_forced(f.name)), None) if not forced_file: print("[ERROR] No -F.srt found!") return print(f"[INFO] Forced: {forced_file.name}") print(f"[INFO] Whisper: {whisper_file.name if whisper_file else 'None'}") # Parse forced try: forced_text = forced_file.read_text(encoding="utf-8", errors="replace") forced_subs = parse_srt_robust(forced_text, forced_file.name) print(f" ? {len(forced_subs)} forced lines") except Exception as e: print(f"[ERROR] Failed to read forced: {e}") return # Parse whisper whisper_subs = [] if whisper_file: try: whisper_text = whisper_file.read_text(encoding="utf-8", errors="replace") whisper_subs = parse_srt_robust(whisper_text, whisper_file.name) print(f" ? {len(whisper_subs)} whisper lines") except Exception as e: print(f"[WARNING] Whisper failed: {e}") forced_subs.sort(key=lambda x: x[0]) whisper_subs.sort(key=lambda x: x[0]) final_subs = [] w_idx = 0 W = len(whisper_subs) for f_start, f_end, f_lines, _ in forced_subs: # Add all Whisper subs that END before this forced sub starts while w_idx < W: w_start, w_end, _, _ = whisper_subs[w_idx] if w_end <= f_start: # No overlap final_subs.append(whisper_subs[w_idx][:3]) w_idx += 1 else: break # Now: skip all Whisper subs that overlap this forced sub while w_idx < W: w_start, w_end, _, _ = whisper_subs[w_idx] if w_start < f_end: # Overlaps w_idx += 1 else: break # Add forced sub final_subs.append((f_start, f_end, f_lines)) # Add remaining non-overlapping whisper subs while w_idx < W: final_subs.append(whisper_subs[w_idx][:3]) w_idx += 1 # Write output with open(output_srt, "w", encoding="utf-8") as f: for idx, (start, end, lines) in enumerate(final_subs, 1): f.write(f"{idx}\n") f.write(f"{seconds_to_srt(start)} --> {seconds_to_srt(end)}\n") for line in lines: f.write(f"{line}\n") f.write("\n") print(f"\n[OK] Merged {len(final_subs)} blocks ? {output_srt.name}") print(f" ? {len(forced_subs)} forced subs preserved (100%)") if __name__ == "__main__": if len(sys.argv) != 2: print("Usage: merge_subtitles.py <mkv_path>") sys.exit(1) main(sys.argv[1])
It will contain:
- English dialogs transcribed in English
- Foreign dialogs that are already in the '-F' forced subs "as is".
- Foreign dialogs missing in the '-F' forced subs translated by whisper.
I did all that with a mix of ChatGPT, Grok and Deepseek.
Here are all the episode batch processed:
https://limewire.com/d/9JGEX#qH5IhI1Y1x
Keep in mind that my SE settings are different than yours, so formating might not be 100% to your liking.
Also, it seems that you don't have exactly the same version, you will likely need to sync the start of the subs.Last edited by robena; 29th Oct 2025 at 20:47.
-
No.The lines are too big to read. Update the code with
--max_line_count 2 ^
--max_line_width 36 ^ -
Not too many lines like that, but I'll try and compare the results, that's easy!
The way I did it with a REXX script, it's just a right click to do everything. -
Updated Assembly Code [Assembly.c]
Perfect Two lines break. No need for SE batch for touchup. -
I did not test Assembly yet, but for whisper, I am surprised to see the difference it makes between --max_line_width 36 and --max_line_width 40, it's not only 4 characters.
I asked why, and got:
--max_line_width does not mean “no line may be longer than N characters”.
It tells Faster-Whisper the target width that the line-breaker tries to stay under while it is splitting a segment into subtitle lines.
Because the breaker also respects sentence boundaries, words that are already > N, and minimum-line-length rules, you will still see lines that are much longer than the value you passed – especially when you raise it from 36 to 40.
Thanks for pointing it out, I would never had thought by myself that it would make the subs that better.
Here they are:
https://limewire.com/d/ZpjSg#tNvqHhY3Gl
Whisper gives much more natural looking subs than Assembly. opensubtitles.org rejects Assembly ones.
Edit: I'll reserve judgment until I test your version!
Edit edit: I get "Failed to upload file." with your version. Firewall was open. Don't waste time on it for me, I'll use whisper from now on.
Edit edit edit: stupid, I forgot to update the API key!!!Last edited by robena; 30th Oct 2025 at 03:50.
-
Sam,
I remux my subs using a routine that detects the aspect ratio and creates PGS files that are located inside the active video region, just a few pixels above the black bar:
[Attachment 89467 - Click to enlarge]
Interested? -
Ofcource YES.
Kindly PM your code for Faster-Whisper-XXL that you usedThe way I did it with a REXX script, it's just a right click to do everything. -
Tehran [Season 03] KAN (Color-Yellow) ENG-NON Hi
subs uploaded > opensubtitles.org [uploader-SamGer]Last edited by sam12345; 30th Oct 2025 at 06:48.
-
@robena
Can you PM me the batch (.bat) files for above. Not familiar with REXX.I remux my subs using a routine that detects the aspect ratio and creates PGS files that are located inside the active video region, just a few pixels above the black bar:Last edited by sam12345; 1st Nov 2025 at 02:26.
-
I don't have them!
I don't use .bat scripts, I never learned how to do it. REXX is so much powerful and easy to use that I have no reason to.
You could show the REXX routines I sent you to ChatGPT, ask it to analyze them and re-write in python or power shell.**
Basically, my routines use tools like tsmuxer, mkvtoolnix and ffmpeg to do all that.
Here are some examples, this is the he script I sent you for getting the AR:
That runs ffmpeg and captures its output in variables tab.1, tab.2, etc...Code:#! d:\m1\sedit\obj\xed\srexx.exe /* ** AspectRatio: returns AR ** ** Called by edseries **/ signal on novalue option setenv mixed address exec procedure if 0 = index($PATH, $HOME'/rcmd') then $PATH = $PATH';'$HOME'/rcmd;'$HOME'/cmd' file = arg(1) if ft(file) = '' then file = file'.mkv' if ($AspectRatio_file = fn(file)||ft(file)) & $AspectRatio_found ~= '' then return $AspectRatio_found cmd = ffmpeg() say say '*** Finding Aspect Ratio for: 'file say say 'Running: 'cmd' -hwaccel cuda -ss 00:03:00 -i "'file'" -map 0:v:0 -copyts -vf cropdetect -frames:v 500 -f null -' /* Run cropdetect with hardware acceleration and tuned parameters */ crop_cmd = '"'cmd'" -hwaccel cuda -ss 00:03:00 -i "'file'" -map 0:v:0 -copyts -vf cropdetect -frames:v 500 -f null -' call windows crop_cmd, tab /* Log cropdetect output for debugging */ /* do i = 1 to tab.0 if pos('crop=', tab.i) > 0 then say 'Cropdetect: 'tab.i end */ do i = tab.0 to 1 by -1 parse value tab.i with 'crop='hh':'ww':' if hh ~= '' & ww ~= '' & ww ~= 0 & hh ~= 0 then { ar = hh/ww say 'Initial AR: 'ar if ar <= 1.90 then ar = '1.85' else if ar > 1.90 & ar <= 2.1 then ar = '2' else if ar > 2.10 & ar <= 2.25 then ar = '2.2' else if ar > 2.25 & ar < 2.60 then ar = '2.39' else if ar >= 2.60 then ar = '2.60' say 'Final AR: 'ar $AspectRatio_found = ar $AspectRatio_file = fn(file)||ft(file) return ar } end say 'Unable to determine aspect ratio' return ''
It parses the output and returns 1.85, 2, 2.2, 2.39 or 2.60.
Give that to Chat, if will do the same with another language.
This is the routine that produces 2 PGS files, one pure white and one darker in case HDR makes that too bright:
It makes a command file to be used by the command line version of tsmuxer.Code:#! d:\m1\sedit\obj\xed\srexx.exe /* ** MakeSup: Makes a sup from an srt ** ** TSMuxer with MUXOPT --no-pcr-on-video-pid --new-audio-pes --demux --vbr --vbv-len=500 S_TEXT/UTF8, "B:\prob\test.srt",font-name="Arial Unicode MS",font-size=48,font-color=0xffffffff,bottom-offset=158,font-border=5,text-align=center,video-width=1920,video-height=1080,fps=23.976 ** 06/06/24: Arial Unicode MS ** ** MakeSup ar fn1 fn2 .... ** ** Can read offset from offset.txt and size from SizeFont.size ** ** if $DoSupGey == 1, does grey subs too ** if $?LevelSup, uses $LevelSup ** if $?offset , uses $offset ** if $SizeFont , uses $SizeFont ** ** Size for PJ: font-size=48 ** Size for TV: font-size=50 WebFont = 60 IF VIDEO HEIGHT == 1600 (WEBRips) ** ** offset.25 of.25 SizeFont.55 sf.55 anamorphic 2.0 handbrake recode ** offset.25 of.25 SizeFont.50 anamorphic 2.35 handbrake recode ** ** greyUHD.145 ** greyHD.174 ** ** // ntweb.txt prevents testing WEB-DL for positioning -- Obsolete ** **/ signal on novalue option setenv mixed address exec if 0 = index($PATH, $HOME'/rcmd') then $PATH = $PATH';'$HOME'/rcmd;'$HOME'/cmd' procedure UseWebFont = 0 WebFont = WebFont() /* returns 56 but not used anymore */ /* trace x */ SizeFont = SizeFont() FontBorder = 5 FontBorderWeb = 3 /* FontName = 'Arial' */ FontName = 'Arial Unicode MS' if ~ $?LG_UHD then $LG_UHD = 0 movie = arg(2, 'x') movie = fdd(movie)'\'||fn(movie)||'.mkv' white = fdd(movie)'\'||fn(movie)||'.sup' grey = fdd(movie)'\'||fn(movie)'-grey'||'.sup' if state(white) & state(grey) then *{ say 'MakeSup: subs exist, skipping' return 0 *} if ft(arg(2, 'x')) * '.srt' then call GetStreams movie stv = value("stream.video.1.Display_aspect_ratio") if (UseWebFont & ~$?offset & stv ~= 'stream.video.1.Display_aspect_ratio' & stv ~= "16:9") then $offset = 20 nn = ReadNumExt('offset') call edseries fn(movie)||'.srt' if nn = '' then nn = ReadNumExt('of') if $?offset & nn = '' then { offset = $offset ar = arg(1, 'x') CustomOffset = 1 say 'Using read custom 'offset' offset' } else *{ if nn ~= '' then { offset = nn ar = 'Custom 'offset CustomOffset = 1 say 'Using read custom 'offset' offset' $IsRatio = 0 /* of.NN overides all */ $IsOf = 1 } else { ar = arg(1, 'x') CustomOffset = 0 select when ar = '2.60' then offset = 190 when ar = '2.35' | ar = '2.39' | ar = '2.40' then offset = 158 when ar = '2.20' then offset = 118 when ar = '2' then offset = 85 when ar = '1.85' | ar = '1.77' then offset = 50 otherwise offset = ar end $IsRatio = 1 /* of.NN overides all */ $IsOf = 0 } } ForceBR = arg(2, 'x') meta = cwd()'\srt2sub.meta' if 0 ~= pos("-DV", fn(movie), 1) then greyHD = '0xff919191' /* 145 145 145 */ else greyHD = '0xffAEAEAE' /* 174 174 174 */ /* greyHD = '0xff919191' \* 145 145 145 *\ */ /* greyHD = '0xff787878' \* 120 120 120 *\ \* LG *\ */ /* greyHD = '0xff3C3C3C' \* 60 60 60 *\ \* Envy too dark *\ */ /* greyHD = '0xff646464' \* 100 100 100 *\ \* LG *\ */ greyUHD = greyHD /* With Envy */ SizeWEB = 57 /* SizeWEB = 70 */ nn = ReadNumExt('greyUHD') if nn ~= ''then *{ gg = d2x(nn) if ½gg = 1 then gg = '0'gg gg = gg[1:2] greyUHD = '0xff'||gg||gg||gg *} nn = ReadNumExt('greyHD') if nn ~= '' then *{ gg = d2x(nn) if ½gg = 1 then gg = '0'gg gg = gg[1:2] greyHD = '0xff'||gg||gg||gg *} whitecolor = '0xffEBEBEB' /* 235 */ if $?LevelSup then *{ gg = d2x($LevelSup) greyHD = '0xff'||gg||gg||gg greyUHD = '0xff'||gg||gg||gg whitecolor = greyUHD *} if $LG_UHD then greycolor = greyUHD else greycolor = greyHD nn = ReadNumExt('SizeFont') if nn = '' then nn = ReadNumExt('sf') if nn ~= '' then *{ SizeFont = nn CustomSizeFont = 1 say 'Using read custom 'SizeFont' font size' *} else CustomSizeFont = 0 if $?SizeFont then *{ SizeFont = $SizeFont CustomSizeFont = 1 *} /* UseWebFont = ~state('ntweb.txt') */ das = value('stream.video.1.Display_aspect_ratio') if das * '16:9' then UseWebFont = 0 if UseWebFont then { if value('stream.video.1.Width') * '3 840 pixels' then { /* * WebRIPS with stream.video.1.Height = 1 600 need a 60 font, and a 40 ofset for 2.35 ratio */ if ar > 1.85 | das = '2.40:1' | das = '2.000:1' | das = '2.35:1' then { vv = value('stream.video.1.Height') vv = change(vv, ' ', '') /* 1 600 pixels */ vv = change(vv, 'pixels', '') if datatype(vv, 'num') & vv < 1650 then { if CustomSizeFont = 0 then SizeFont = WebFont /* trace x */ if CustomOffset = 0 /* & ~$IsRatio */ then offset = 20 FontBorder = FontBorderWeb /* offset = 30 */ /* offset = 25 */ /* offset = 20 */ /* if ~ $?GONG then call gong */ say say '************** WebRIP: switching to 'SizeFont' font size and 'offset' offset' say } else if datatype(vv, 'num') & vv < 1800 then /* 2.0 1714 pixels */ { if CustomSizeFont = 0 then SizeFont = SizeWEB if CustomOffset = 0 /* & ~$IsRatio */ then offset = 15 FontBorder = FontBorderWeb /* offset = 30 */ /* offset = 25 */ /* offset = 20 */ /* if ~ $?GONG then call gong */ say say '************** WebRIP: switching to 'SizeFont' font size and 'offset' offset' say } } } else if value('stream.video.1.Width') = '1 280 pixels' & value('stream.video.1.Height') < 540 then { /* * 720p WebRIPS with 2.35 AR */ if CustomSizeFont = 0 then SizeFont = WebFont if CustomOffset = 0 then offset = 13 FontBorder = FontBorderWeb /* offset = 30 */ /* offset = 25 */ /* offset = 20 */ /* if ~ $?GONG then call gong */ say say '************** 720p WebRIP: switching to 'SizeFont' font size and 'offset' offset' say } else { /* * WebRIPS with stream.video.1.Height = 816 need a 60 font, and a 40 ofset for 2.35 ratio */ if ar > 1.85 | das = '2.40:1' | das = '2.000:1' | das = '2.35:1' | (CustomOffset = 1 & offset > 100) then { vv = value('stream.video.1.Height') vv = change(vv, ' ', '') /* 816 pixels */ vv = change(vv, 'pixels', '') if vv < 850 then { if CustomSizeFont = 0 then SizeFont = WebFont if CustomOffset = 0 then offset = 20 FontBorder = FontBorderWeb /* offset = 30 */ /* offset = 25 */ /* offset = 20 */ /* if ~ $?GONG then call gong */ say say '************** WebRIP: switching to 'SizeFont' font size and 'offset' offset' say } else if vv < 970 then /* 2.0 872 -> 960 pixels */ { if CustomSizeFont = 0 then SizeFont = SizeWEB if CustomOffset = 0 then { stv = value('stream.video.1.Display_aspect_ratio') parse value stv with ar':'xxx { if ar >= '2' then offset = 15 else offset = 60 } } /* offset = 30 */ /* offset = 25 */ /* offset = 20 */ FontBorder = FontBorderWeb /* if ~ $?GONG then call gong */ say say '************** WebRIP: switching to 'SizeFont' font size and 'offset' offset' say } } } } /* trace x */ Say '.... Using 'greycolor' grey color, 'offset' offset and 'SizeFont' font size.' $MakeSupOffsetSizeFont = offset' offset/'SizeFont' font size' $MakeSupOffsetSizeFontGrey = offset' offset/'SizeFont' font size/'greycolor 'greycolor' /* greycolor = '0xff919191' */ /* 145 145 145 */ /* greycolor = '0xff646464' */ /* 100 100 100 */ /* greycolor = '0xff878787' */ /* 135 135 135 */ i = 2 file = arg(i, 'x') if ~$?IsCCEX & ~CustomOffset then *{ call edseries file if $?offset then offset = $offset CustomOffset = 1 *} rcode = 0 do while file ~= '' *call rm '"'meta'"' *if $DoSupGey = 1 then fontcolor = greycolor *else fontcolor = whitecolor *call lineout meta, 'MUXOPT --no-pcr-on-video-pid --new-audio-pes --demux --vbr --vbv-len=500' *call lineout meta, 'S_TEXT/UTF8, "'follow(file)'",font-name="'FontName'",font-size='SizeFont',font-color='fontcolor',bottom-offset='offset',font-border='FontBorder',text-align=center,video-width=1920,video-height=1080,fps=23.976' *call lineout meta *say '.... Using 'SizeFont' font size' *say 'D:/m1/soft/T/TSMuxer/tsMuxeR_2.6.12/tsMuxeR.exe "'meta'" "'cwd()'"' */* trace x */ *call tee 'D:/m1/soft/T/TSMuxer/tsMuxeR_2.6.12/tsMuxeR.exe "'meta'" "'cwd()'"', tab *rcode = rcode + rc */* Blank lines make TSMuxer stop before ending */ *amount = 0 *do k = 1 to tab.0 if 'flushing' * (tab.k)[1:8] then { l = k-1 parse var tab.l amount '%' foo } *end *if amount < 99 then rcode = -1 */* * With "G.I. Jane-s.srt", TSMuxer outputs "G.sup" */ *fn = fn(file) *len = ½fn *do k = 2 to len if fn[k] = '.' then { saved = fn[1:k-1]'.sup' needed = fn'.sup' call mv saved, needed leave k } *end */* * Make grey if called standalone */ *if ~$?MakeSupNotStand then { fontcolor = greycolor call rm '"'meta'"' file_grey = fdd(file)'\'fn(file)'-grey'||ft(file) call cp '"'file'" "'file_grey'"' call lineout meta, 'MUXOPT --no-pcr-on-video-pid --new-audio-pes --demux --vbr --vbv-len=500' call lineout meta, 'S_TEXT/UTF8, "'file_grey'",font-name="'FontName'",font-size=48,font-color='fontcolor',bottom-offset='offset',font-border='FontBorder',text-align=center,video-width=1920,video-height=1080,fps=23.976' call lineout meta say 'D:/m1/soft/T/TSMuxer/tsMuxeR_2.6.12/tsMuxeR.exe "'meta'" "'cwd()'"' call tee 'D:/m1/soft/T/TSMuxer/tsMuxeR_2.6.12/tsMuxeR.exe "'meta'" "'cwd()'"', tab call rm '"'file_grey'"' } *i = i+1 *file = arg(i, 'x') end call rm '"'meta'"' return rcode
It uses this function to get "media info" information"
Really not easy to do that in BAT format!Code:#! d:\m1\sedit\obj\xed\srexx.exe /* ** GetStreams file (quotes not needed) TGetStreams ** ** $TestMediaInfo uses new version of mediainfo ** ** ** Creates stream.video == NNN ** stream.video.1.xxxxx ** derived from content with "/" -> "." ** " " -> "_" ** "," -> "_" ** "(" -> "_" ** ")" -> "_" ** "*" -> "_" ** stream.audio == NNN ** stream.audio.1.xxxxx ** stream.audio.2.xxxxx ** ** stream.text == NNN ** stream.text.1.xxxxx ** stream.text.2.xxxxx *Example: call GetStreams 'b:\prob\WithAAC.mkv' do a in cvtails('stream.audio.1') interpret 'zz = stream.audio.1.'a say 'stream.audio.1.'a' = 'zz end say '---' do a in cvtails('stream.audio.2') interpret 'zz = stream.audio.2.'a say 'stream.audio.2.'a' = 'zz end say '---' say 'Video: 'stream.video do a in cvtails('stream.video.1') interpret 'zz = stream.video.1.'a say 'stream.video.1.'a' = 'zz end say '---' say 'Text: 'stream.text do a in cvtails('stream.text.1') interpret 'zz = stream.text.1.'a say 'stream.text.1.'a' = 'zz end Donne: Audio: 2 stream.audio.1.Channel_positions = Front: L R stream.audio.1.Channel_s_ = 2 channels stream.audio.1.Format_profile = LC stream.audio.1.Format = AAC stream.audio.1.ID = 1 stream.audio.1.Duration = 42mn 51s stream.audio.1.Compression_mode = Lossy stream.audio.1.Sampling_rate = 48.0 KHz stream.audio.1.Default = Yes stream.audio.1.Language = English stream.audio.1.Forced = No stream.audio.1.Format.Info = Advanced Audio Codec stream.audio.1.Codec_ID = A_AAC stream.audio.1.Rank = 0 --- stream.audio.2.Bit_rate = 384 Kbps stream.audio.2.Stream_size = 118 MiB (7%) stream.audio.2.Language = English stream.audio.2.Bit_depth = 16 bits stream.audio.2.Sampling_rate = 48.0 KHz stream.audio.2.ID = 3 stream.audio.2.Channel_positions = Front: L C R, Side: L R, LFE stream.audio.2.Format.Info = Audio Coding 3 stream.audio.2.Compression_mode = Lossy stream.audio.2.Default = No stream.audio.2.Format = AC-3 stream.audio.2.Forced = No stream.audio.2.Channel_s_ = 6 channels stream.audio.2.Codec_ID = A_AC3 stream.audio.2.Bit_rate_mode = Constant stream.audio.2.Duration = 42mn 51s stream.audio.2.Mode_extension = CM (complete main) stream.audio.2.Format_settings__Endianness = Big stream.audio.2.Rank = 1 --- Video: 1 stream.video.1.ID = 2 stream.video.1.Format.Info = Advanced Video Codec stream.video.1.Width = 1 920 pixels stream.video.1.Frame_rate = 23.976 fps stream.video.1.Format_settings__CABAC = Yes stream.video.1.Format = AVC stream.video.1.Forced = No stream.video.1.Format_profile = High@L4.0 stream.video.1.Display_aspect_ratio = 16:9 stream.video.1.Chroma_subsampling = 4:2:0 stream.video.1.Format_settings__ReFrames = 4 frames stream.video.1.Height = 1 080 pixels stream.video.1.Default = Yes stream.video.1.Matrix_coefficients = BT.709 stream.video.1.Scan_type = Progressive stream.video.1.Frame_rate_mode = Constant stream.video.1.Bit_depth = 8 bits stream.video.1.Codec_ID = V_MPEG4/ISO/AVC stream.video.1.Color_space = YUV stream.video.1.Duration = 42mn 51s stream.video.1.Transfer_characteristics = BT.709 stream.video.1.Color_primaries = BT.709 stream.video.1.Language = English stream.video.1.Rank = 3 --- Text: 1 stream.text.1.Codec_ID.Info = UTF-8 Plain Text stream.text.1.Format = UTF-8 stream.text.1.Codec_ID = S_TEXT/UTF8 stream.text.1.Default = Yes stream.text.1.Forced = No stream.text.1.ID = 4 stream.text.1.Rank = 4 --- Menu: 9 stream.menu.1 = 00:00:00.000 : Chapter 1 stream.menu.2 = 00:09:27.525 : Chapter 2 stream.menu.3 = 00:26:15.782 : Chapter 3 stream.menu.4 = 00:38:16.127 : Chapter 4 stream.menu.5 = 00:54:36.898 : Chapter 5 stream.menu.6 = 01:18:51.100 : Chapter 6 stream.menu.7 = 01:28:02.443 : Chapter 7 stream.menu.8 = 01:36:42.379 : Chapter 8 stream.menu.9 = 01:49:07.374 : Chapter 9 24/04/23 Change stream.text.text_i.RANK = text_i+1 **/ procedure expose stream. signal on novalue option setenv mixed address exec $debug_GetStream = 0 file = arg(1, 'x') if file[1] = '"' then file = file[2:(½file)-1] video_i = 0 audio_i = 0 text_i = 0 menu_i = 0 rank_i = 0 drop stream. media = MediaInfo() call windows(media '--Inform=Text "'file'"', tab) /* do i = 1 to tab.0 say tab.i end i exit */ do i = 1 to tab.0 if tab.i = "General" then { do k = i+1 to tab.0 if tab.k = '' then { i = k leave k } call do_stem tab.k, 'general' end k i = k } if (tab.i)[1:5] = "Video" then { video_i = video_i+1 do k = i+1 to tab.0 if tab.k = '' then { i = k leave k } call do_stem tab.k, 'video.'video_i end k call do_stem3 'video', video_i i = k } if (tab.i)[1:5] = "Audio" then { audio_i = audio_i+1 do k = i+1 to tab.0 if tab.k = '' then { i = k leave k } call do_stem tab.k, 'audio.'audio_i end k call do_stem3 'audio', audio_i i = k } if (tab.i)[1:4] = "Text" then { /* trace x */ text_i = text_i+1 do k = i+1 to tab.0 if tab.k = '' then { i = k leave k } call do_stem tab.k, 'text.'text_i end k call do_stem3 'text', text_i stream.text.text_i.RANK = text_i+1 i = k } if (tab.i)[1:4] * "Menu" then { /* trace x */ do k = i+1 to tab.0 if tab.k = '' then { i = k leave k } menu_i = menu_i+1 call do_stem2 tab.k, 'menu.'menu_i end k call do_stem3 'menu', menu_i i = k } end stream.video = video_i stream.audio = audio_i stream.text = text_i stream.menu = menu_i /* trace x say value('stream.text.1.Language') procedure expose stream. */ return do_stem: procedure expose stream. parse value arg(1) with left ':' right stem = 'stream.'arg(2)'.'clean(strip(left)) if 0 = pos('"', right) then sep = '"' else { sep = "'" right = change(right, "'", "''") } stem = change(stem, '-', '_') interpret stem'='sep||strip(right)sep if $debug_GetStream then { sayn 'Stem <'stem'> = "' interpret 'sayn 'stem say '"' } return do_stem2: procedure expose stream. stem = 'stream.'arg(2) interpret stem'=arg(1)' if $debug_GetStream then { sayn 'Stem <'stem'> = "' interpret 'sayn 'stem say '"' } return do_stem3: procedure expose stream. rank_i stem = 'stream.'arg(1)'.'arg(2)'.Rank' interpret stem'=rank_i' rank_i = rank_i + 1 if $debug_GetStream then { sayn 'Stem <'stem'> = "' interpret 'sayn 'stem say '"' } return clean:procedure *a = change(arg(1), "/", ".") *a = change(a , " ", "_") *a = change(a , ",", "_") *a = change(a , "(", "_") *a = change(a , ")", "_") *a = change(a , "*", "_") return a /* General Unique ID : 200995056992673479727480854705761339975 (0x97363D69AAC25B858AF6248DD0D6CA47) Complete name : WithAAC.mkv Format : Matroska Format version : Version 4 / Version 2 File size : 1.69 GiB Duration : 42mn 51s Overall bit rate : 5 629 Kbps Encoded date : UTC 2014-12-03 08:53:34 Writing application : mkvmerge v7.2.0 ('On Every Street') 32bit built on Sep 13 2014 15:42:11 Writing library : libebml v1.3.0 + libmatroska v1.4.1 DURATION : 00:41:58.815000000 NUMBER_OF_FRAMES : 758 NUMBER_OF_BYTES h : 32250 _STATISTICS_WRITING_APP : mkvmerge v7.2.0 ('On Every Street') 32bit built on Sep 13 2014 15:42:11 _STATISTICS_WRITING_DATE_UTC : 2014-12-03 08:53:34 _STATISTICS_TAGS : BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES Video ID : 2 Format : AVC Format/Info : Advanced Video Codec Format profile : High@L4.0 Format settings, CABAC : Yes Format settings, ReFrames : 4 frames Codec ID : V_MPEG4/ISO/AVC Duration : 42mn 51s Width : 1 920 pixels Height : 1 080 pixels Display aspect ratio : 16:9 Frame rate mode : Constant Frame rate : 23.976 fps Color space : YUV Chroma subsampling : 4:2:0 Bit depth : 8 bits Scan type : Progressive Language : English Default : Yes Forced : No Color primaries : BT.709 Transfer characteristics : BT.709 Matrix coefficients : BT.709 Audio #1 ID : 1 Format : AAC Format/Info : Advanced Audio Codec Format profile : LC Codec ID : A_AAC Duration : 42mn 51s Channel(s) : 2 channels Channel positions : Front: L R Sampling rate : 48.0 KHz Compression mode : Lossy Language : English Default : Yes Forced : No Audio #2 ID : 3 Format : AC-3 Format/Info : Audio Coding 3 Mode extension : CM (complete main) Format settings, Endianness : Big Codec ID : A_AC3 Duration : 42mn 51s Bit rate mode : Constant Bit rate : 384 Kbps Channel(s) : 6 channels Channel positions : Front: L C R, Side: L R, LFE Sampling rate : 48.0 KHz Bit depth : 16 bits Compression mode : Lossy Stream size : 118 MiB (7%) Language : English Default : No Forced : No Text ID : 4 Format : UTF-8 Codec ID : S_TEXT/UTF8 Codec ID/Info : UTF-8 Plain Text Default : Yes Forced : No */
Again, send it to Chat (or Grok, very good too) to analyze it and prompt it to make what you want using it as a template.
You'll be surprised how easy and fast it will be.
Then you can use MKVtoolnix (https://mkvtoolnix.download/downloads.html) to remux, either with the gui or with a script in command line mode.
My REXX routines to do all in one right click that are thousand of lines, all I can do is give you general guidelines.
They depend of a lot of tools like the one above, downloaded on a precise location on my D: drive, making a portable package would be a huge undertaking.
If you're stuck with some particular task, ask, I'll help.
Edit: for some reason, this forum alters what I try to post and adds in some places unwanted "*' characters, in the code also. I don't know how to avoid that.
If you know how, let me know and I'll edit this post.
It prints "*i = i+1" instead of "i = i+1"
Note also that this S/REXX version has features, like using {} or calling external routines as easily as using "nn = ReadNumExt('greyUHD')" when the*ReadNumExt file is in the path, that do not exist with the usual REXX implementation.
---
RobertLast edited by robena; 1st Nov 2025 at 05:20.
-
Sam,
I realize this might be a bit daunting, so I'm going to see if working on a virtual machine, I can cobble together something portable.
No promises, I'm just evaluating how much work it will require for now.
Similar Threads
-
Subtitle Edit - delete video and subtitle file after processing?
By svcds in forum SubtitleReplies: 0Last Post: 4th Jan 2024, 06:45 -
Subtitle edit - How to put 'A with a dash on top' in subtitle edit?
By SSEN in forum SubtitleReplies: 5Last Post: 21st Sep 2023, 21:57 -
Subtitle Edit : Capitalize Subtitle to Normal Subtitle incomplete
By kalemvar1 in forum SubtitleReplies: 6Last Post: 5th Aug 2023, 13:28 -
Subtitle Edit - Shortcut to set a subtitle minimum gap
By tren in forum SubtitleReplies: 2Last Post: 1st Aug 2023, 07:44 -
Subtitle edit, warning subtitle contains negative timing codes fix please
By jraju in forum Newbie / General discussionsReplies: 1Last Post: 16th Dec 2019, 19:52



Quote