#!/usr/bin/env python """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()