Skip to content

Instantly share code, notes, and snippets.

@Grub4K
Created August 6, 2022 17:32
Show Gist options
  • Save Grub4K/e3315a752c2d39c005322741d3d967f0 to your computer and use it in GitHub Desktop.
Save Grub4K/e3315a752c2d39c005322741d3d967f0 to your computer and use it in GitHub Desktop.
Doctor Who audio synced intro
import subprocess
from collections import namedtuple
from pathlib import Path
Timewarp = namedtuple("Timewarp", ["start", "end", "rate"])
def font_filter(stream: str, text: str):
return ":".join(
[
f"[{stream}]drawtext=fontfile=roboto.ttf",
f"text='{text}'",
"fontcolor=white",
"bordercolor=black",
"borderw=5",
"x=(w-text_w)/2",
f"y=10[{stream}]",
]
)
def create_intro(
input_file: Path,
output_file: Path,
start_time: float,
comparison=False,
):
command = ["ffmpeg", "-hide_banner", "-v", "error", "-stats", "-y"]
command.extend(["-i", str(input_file)])
filters = [
f"[0:v]trim={start_time}:duration=42.5,setpts=PTS-STARTPTS[intro]",
]
timewarps = [
Timewarp(0, 8, 0.75),
Timewarp(8, 23, 0.9),
Timewarp(23, 26, 0.6),
Timewarp(26, 29.7, 1.2),
Timewarp(29.7, 34.8, 0.8),
Timewarp(34.8, 36, 1.75),
Timewarp(36, 42, 1),
]
parts = "".join(f"[part{part}]" for part, _ in enumerate(timewarps))
filters.append(f"[intro]split={len(timewarps)}{parts}")
for part_num, timewarp in enumerate(timewarps):
filter_data = [
f"[part{part_num}]trim={timewarp.start}:{timewarp.end}",
"setpts=PTS-STARTPTS",
f"setpts={timewarp.rate}*PTS[part{part_num}]",
]
filters.append(",".join(filter_data))
filters.append(f"{parts}concat=n={len(timewarps)},setpts=PTS-STARTPTS[vout]")
filters.append(f"[0:a]atrim={start_time}:duration=37.5,asetpts=PTS-STARTPTS[aout]")
if comparison:
filters.append(
f"[0:v]trim={start_time}:duration=37.5,setpts=PTS-STARTPTS[vout_top]"
)
if Path("roboto.ttf").exists():
filters.extend(
[
font_filter("vout_top", "Original"),
font_filter("vout", "Audio synced"),
]
)
filters.append(
"[vout_top][vout]vstack[vout]",
)
# Theoretically not needed, but I messed up so here is the fix
filters.append(
"[vout]fps=23.98,setpts=PTS-STARTPTS,trim=0.7:37.5,setpts=PTS-STARTPTS[vout]"
)
filters.append("[aout]atrim=0.7:37.5,asetpts=PTS-STARTPTS[aout]")
command.extend(["-filter_complex", ";".join(filters)])
command.extend(["-map", "[vout]"])
command.extend(["-map", "[aout]"])
command.extend([str(output_file)])
subprocess.run(command)
def main():
import argparse
parser = argparse.ArgumentParser(
prog="dw_intro.py",
description="Creates a timewarped Doctor Who intro synced with the audio. "
+ "Designed to be used with Season 9 Episode 3. ",
epilog="Be careful using this, it might break.",
)
parser.add_argument(
"-i",
"--input",
type=Path,
required=True,
help="the input file from which to take the intro.",
)
parser.add_argument(
"-o",
"--output",
type=Path,
help="the output file. Defaults to either `synced.mp4` or `comparison.mp4`",
)
parser.add_argument(
"-s",
"--start",
type=float,
default=185.3,
help="the time in seconds at which the intro starts. Defaults to 185.3s",
)
parser.add_argument(
"-c",
"--comparison",
action="store_true",
help="create a comparison instead of only the synced intro",
)
args = parser.parse_args()
if args.output is None:
args.output = Path("comparison.mp4" if args.comparison else "synced.mp4")
create_intro(args.input, args.output, args.start, args.comparison)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment