diff --git a/chat_to_subtitle.py b/chat_to_subtitle.py new file mode 100644 index 0000000..a2f9c13 --- /dev/null +++ b/chat_to_subtitle.py @@ -0,0 +1,74 @@ + +import json +import sys + +import argh + +CHAT_BOX = (1220, 100, 1910, 810) + +def encode_time(time): + hours, time = divmod(time, 3600) + mins, secs = divmod(time, 60) + return f"{int(hours)}:{int(mins):02d}:{secs:05.2f}" + +def encode_dialogue(start, end, text): + return f"Dialogue: {encode_time(start)}, {encode_time(end)}, Chat, {text}" + +def message_to_line(message, time_base): + time = message["time"] - time_base + tags = message.get("tags", {}) + + sender = tags.get("display-name") + if sender is None: + sender = message["sender"] + + content = message["params"][1] + if content.startswith("\x01"): + content = content[1:-1].split(" ", 1)[1] + text = f"{sender} {content}" + else: + text = f"{sender}: {content}" + + color = tags.get("color") + if color is None: + color = "FFFFFF" + else: + color = color.lstrip("#") + text = f"{{ \\c&H{color}& }}" + text + return time, text + +def lines_to_dialogue(start, end, lines): + lines = "\\N".join([text for start, text in lines][::-1]) + clip_args = ",".join(map(str, CHAT_BOX)) + text = f"{{ \\clip({clip_args}) }}" + lines + return encode_dialogue(start, end, text) + +def gen_dialogues(messages, time_base, message_ttl=10): + window = [] + prev_start = None + for message in messages: + next_start, text = message_to_line(message, time_base) + while window and window[0][0] + message_ttl < next_start: + end = window[0][0] + message_ttl + yield lines_to_dialogue(prev_start, end, window) + window.pop(0) + prev_start = end + window.append((next_start, text)) + if prev_start is not None: + yield lines_to_dialogue(prev_start, next_start, window) + prev_start = next_start + # flush remaining messages + while window: + end = window[0][0] + message_ttl + yield lines_to_dialogue(prev_start, end, window) + window.pop(0) + prev_start = end + +def main(time_base=0): + messages = sys.stdin.read().strip().split("\n") + messages = [json.loads(line) for line in messages] + for dialogue in gen_dialogues(messages, time_base): + print(dialogue) + +if __name__ == '__main__': + argh.dispatch_command(main)