Mercurial > codedump
annotate create.py @ 135:0c3cd90e91f7 default tip
*: add sanae.site scraper scripts
| author | Paper <paper@tflc.us> |
|---|---|
| date | Sat, 24 Jan 2026 15:10:05 -0500 |
| parents | 0d8eabdd12ab |
| children |
| rev | line source |
|---|---|
| 132 | 1 #!/usr/bin/env python3 |
| 2 # Simple python script to create a video from a bunch of wav files | |
| 3 # and an image. Also creates a cue-like track listing that contains | |
| 4 # track numbers etc. | |
| 5 | |
| 6 import argparse | |
| 7 import json | |
| 8 import subprocess | |
| 9 | |
|
133
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
10 def build_vf(blur) -> list[str]: |
|
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
11 if blur: |
|
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
12 return ["-filter_complex", "[0:v]split=2[bg][fg];[bg]scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080,boxblur=luma_radius=10:chroma_radius=10[bg_final];[fg]scale=1920:1080:force_original_aspect_ratio=decrease[fg_final];[bg_final][fg_final]overlay=(W-w)/2:(H-h)/2"] |
|
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
13 |
|
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
14 return ["-vf", "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2"] |
|
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
15 |
| 132 | 16 # Build filter string |
| 17 def build_filter_string(length: int, cover: bool) -> str: | |
| 18 f = "" | |
| 19 i = 1 if cover else 0 | |
| 20 for x in range(length): | |
| 21 f += ("[%d:0]" % (x + i)) | |
| 22 f += ("concat=n=%d:v=0:a=1[out]" % length) | |
| 23 return f | |
| 24 | |
|
133
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
25 def build_arguments_list(audio_files: list[str], cover, output, blur) -> list: |
| 132 | 26 args = ["ffmpeg", "-y"] |
| 27 if not cover is None: | |
| 28 args.extend(["-r", "1", "-loop", "1", "-i", cover]) | |
| 29 # I want you to play that song again | |
| 30 for f in audio_files: | |
| 31 args.extend(["-i", f]) | |
| 32 args.extend(["-filter_complex", build_filter_string(len(audio_files), False if cover is None else True), "-map", "[out]"]) | |
| 33 if not cover is None: | |
| 34 # Letterbox to 1920x1080 | |
|
133
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
35 args.extend(["-map", "0:v", "-tune", "stillimage", "-c:v", "libx264"]) |
|
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
36 args.extend(build_vf(blur)) |
| 132 | 37 args.extend(["-shortest"]) |
| 38 args.append(output + ".mkv") | |
| 39 return args | |
| 40 | |
| 41 | |
| 42 # Returns a Popen object that should be waited on | |
|
133
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
43 def run_ffmpeg(audio_files: list[str], cover, output, blur) -> subprocess.Popen: |
|
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
44 args = build_arguments_list(audio_files, cover, output, blur) |
| 132 | 45 print(args) |
| 46 return subprocess.Popen(args) | |
| 47 | |
| 48 | |
| 49 # Returns a Popen object that should be waited on. | |
| 50 # stdout is also piped... | |
| 51 def run_ffprobe(audio_file: str) -> subprocess.Popen: | |
| 52 return subprocess.Popen(["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", audio_file], stdout=subprocess.PIPE, text=True) | |
| 53 | |
| 54 | |
| 55 def get_title_from_tags(j, default: str) -> str: | |
| 56 # Is there an easier way to do this...? | |
| 57 for tag in j: | |
| 58 if tag.lower() == "title": | |
| 59 return j[tag] | |
| 60 | |
| 61 # Ugh | |
| 62 return default | |
| 63 | |
|
133
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
64 def doit(audio_files: list[str], cover, output, blur) -> int: |
| 132 | 65 ffprobes = [run_ffprobe(x) for x in audio_files] |
|
133
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
66 ffmpeg = run_ffmpeg(audio_files, cover, output, blur) |
| 132 | 67 |
| 68 # Wait for all ffprobe processes to finish their jobs | |
| 69 for f in ffprobes: | |
| 70 f.wait() | |
| 71 | |
| 72 # Iterate the list again, and add up all the times. | |
| 73 # This also accounts for milliseconds etc. to be | |
| 74 # more accurate | |
| 75 with open(output + ".txt", "w", encoding="utf-8") as fw: | |
| 76 dur = 0.0 | |
| 77 for f in ffprobes: | |
| 78 j = json.load(f.stdout) | |
|
133
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
79 fw.write("%d:%02d:%02d %s\n" % (int(dur) // 3600, int(dur) % 3600 // 60, int(dur) % 60, get_title_from_tags(j["format"]["tags"], "OOPS...."))) |
| 132 | 80 dur += float(j["format"]["duration"]) |
| 81 | |
| 82 # Finally, wait on the big ffmpeg process | |
| 83 ffmpeg.wait() | |
| 84 | |
| 85 def main() -> int: | |
| 86 parser = argparse.ArgumentParser(prog='create', description='Creates album videos', epilog='(c) paper 2025 all rights reversed') | |
| 87 parser.add_argument('-c', '--cover', required=True) | |
| 88 parser.add_argument('-o', '--output', required=True) | |
|
133
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
89 parser.add_argument('-b', '--blur', action='store_true') |
| 132 | 90 parser.add_argument('audio', action='extend', nargs='+', type=str) |
| 91 args = parser.parse_args() | |
|
133
0d8eabdd12ab
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
Paper <paper@tflc.us>
parents:
132
diff
changeset
|
92 doit(args.audio, args.cover, args.output, args.blur) |
| 132 | 93 |
| 94 if __name__ == "__main__": | |
| 95 exit(main()) |
