Auto backup: 2026-02-21 07:01
This commit is contained in:
112
.venvs/transcribe/bin/srt-mux
Executable file
112
.venvs/transcribe/bin/srt-mux
Executable file
@@ -0,0 +1,112 @@
|
||||
#!/home/openclaw/.openclaw/workspace/.venvs/transcribe/bin/python3
|
||||
|
||||
"""Merge multiple subtitles together into one."""
|
||||
|
||||
import datetime
|
||||
import srt_tools.utils
|
||||
import logging
|
||||
import operator
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
TOP = r"{\an8}"
|
||||
BOTTOM = r"{\an2}"
|
||||
|
||||
|
||||
def parse_args():
|
||||
examples = {
|
||||
"Merge English and Chinese subtitles": "srt mux -i eng.srt -i chs.srt -o both.srt",
|
||||
"Merge subtitles, with one on top and one at the bottom": "srt mux -t -i eng.srt -i chs.srt -o both.srt",
|
||||
}
|
||||
parser = srt_tools.utils.basic_parser(
|
||||
description=__doc__, examples=examples, multi_input=True
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ms",
|
||||
metavar="MILLISECONDS",
|
||||
default=datetime.timedelta(milliseconds=600),
|
||||
type=lambda ms: datetime.timedelta(milliseconds=int(ms)),
|
||||
help="if subs being muxed are within this number of milliseconds "
|
||||
"of each other, they will have their times matched (default: 600)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-w",
|
||||
"--width",
|
||||
default=5,
|
||||
type=int,
|
||||
help="how many subs to consider for time matching at once (default: %(default)s)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top-and-bottom",
|
||||
action="store_true",
|
||||
help="use SSA-style tags to place files at the top and bottom, respectively. Turns off time matching",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-time-matching",
|
||||
action="store_true",
|
||||
help="don't try to do time matching for close subtitles (see --ms)",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def merge_subs(subs, acceptable_diff, attr, width):
|
||||
"""
|
||||
Merge subs with similar start/end times together. This prevents the
|
||||
subtitles jumping around the screen.
|
||||
|
||||
The merge is done in-place.
|
||||
"""
|
||||
sorted_subs = sorted(subs, key=operator.attrgetter(attr))
|
||||
|
||||
for subs in srt_tools.utils.sliding_window(sorted_subs, width=width):
|
||||
current_sub = subs[0]
|
||||
future_subs = subs[1:]
|
||||
current_comp = getattr(current_sub, attr)
|
||||
|
||||
for future_sub in future_subs:
|
||||
future_comp = getattr(future_sub, attr)
|
||||
if current_comp + acceptable_diff > future_comp:
|
||||
log.debug(
|
||||
"Merging %d's %s time into %d",
|
||||
future_sub.index,
|
||||
attr,
|
||||
current_sub.index,
|
||||
)
|
||||
setattr(future_sub, attr, current_comp)
|
||||
else:
|
||||
# Since these are sorted, and this one didn't match, we can be
|
||||
# sure future ones won't match either.
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
logging.basicConfig(level=args.log_level)
|
||||
|
||||
srt_tools.utils.set_basic_args(args)
|
||||
|
||||
muxed_subs = []
|
||||
for idx, subs in enumerate(args.input):
|
||||
for sub in subs:
|
||||
if args.top_and_bottom:
|
||||
if idx % 2 == 0:
|
||||
sub.content = TOP + sub.content
|
||||
else:
|
||||
sub.content = BOTTOM + sub.content
|
||||
muxed_subs.append(sub)
|
||||
|
||||
if args.no_time_matching or not args.top_and_bottom:
|
||||
merge_subs(muxed_subs, args.ms, "start", args.width)
|
||||
merge_subs(muxed_subs, args.ms, "end", args.width)
|
||||
|
||||
output = srt_tools.utils.compose_suggest_on_fail(muxed_subs, strict=args.strict)
|
||||
|
||||
try:
|
||||
args.output.write(output)
|
||||
except (UnicodeEncodeError, TypeError): # Python 2 fallback
|
||||
args.output.write(output.encode(args.encoding))
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
||||
Reference in New Issue
Block a user