Guest User

Untitled

a guest
Mar 8th, 2025
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.77 KB | None | 0 0
  1. import argparse
  2. import decimal
  3. import pathlib
  4. import shutil
  5. import subprocess
  6. import tempfile
  7. import urllib.parse
  8.  
  9. import requests
  10.  
  11. scale_factor = 100
  12. # crop_window = {"width": (55, 100), "height": (50, 100)}
  13. # crop_window = {"width": (60, 100), "height": (50, 100)}
  14. crop_window = {"width": (0, 100), "height": (0, 100)}
  15.  
  16.  
  17. def make_filter(name, *args):
  18.     return f"{name}=" + ":".join(map(str, args))
  19.  
  20.  
  21. video_filters = ", ".join(
  22.     [
  23.         make_filter(
  24.             "crop",
  25.             f"in_w*({crop_window['width'][1] - crop_window['width'][0]}/100)",
  26.             f"in_h*({crop_window['height'][1] - crop_window['height'][0]}/100)",
  27.             f"in_w*({crop_window['width'][0]}/100)",
  28.             f"in_h*({crop_window['height'][0]}/100)",
  29.         ),
  30.         make_filter(
  31.             "scale",
  32.             f"trunc(in_w*({scale_factor}/100)/2)*2",
  33.             f"trunc(in_h*({scale_factor}/100)/2)*2",
  34.         ),
  35.     ]
  36. )
  37.  
  38. extra_video_args = ["-filter:v", video_filters]
  39.  
  40. MAX_FILESIZE = 1024 * 1024 * 4 * 95 // 100  # 4MB with 95% fudge factor
  41.  
  42.  
  43. def upload_catbox(fileobj):
  44.     response = requests.post(
  45.         "https://catbox.moe/user/api.php",
  46.         {
  47.             "reqtype": "fileupload",
  48.         },
  49.         files={
  50.             "fileToUpload": fileobj,
  51.         },
  52.     )
  53.     response.raise_for_status()
  54.     return response.text
  55.  
  56.  
  57. def render_preview(input_file, start_timestamp, duration):
  58.     with tempfile.TemporaryDirectory() as tempdir:
  59.         preview_file = pathlib.Path(tempdir) / "preview.mkv"
  60.         subprocess.run(
  61.             [
  62.                 "ffmpeg",
  63.                 "-ss",
  64.                 start_timestamp,
  65.                 "-i",
  66.                 input_file,
  67.                 "-t",
  68.                 str(duration),
  69.                 *extra_video_args,
  70.                 "-preset",
  71.                 "medium",
  72.                 "-y",
  73.                 preview_file,
  74.             ],
  75.             check=True,
  76.         )
  77.         subprocess.run(["ffplay", "-loop", "0", preview_file])
  78.  
  79.  
  80. def negotiate_time_range(input_file):
  81.     start_timestamp = "0"
  82.     duration = "1"
  83.     while True:
  84.         start_timestamp = (
  85.             input(f"Start timestamp (currently {start_timestamp!r})? ")
  86.             or start_timestamp
  87.         )
  88.         duration = input(f"Duration (currently {duration!r})? ") or duration
  89.         render_preview(input_file, start_timestamp, duration)
  90.         while True:
  91.             accepted = input(
  92.                 f"Okay with start={start_timestamp!r} and duration={duration!r} (N/y)? "
  93.             )
  94.             if accepted.lower() == "y":
  95.                 duration_whole, *_ = duration.split(".", 1)
  96.                 duration_seconds = decimal.Decimal(
  97.                     sum(
  98.                         int(value) * (60**idx)
  99.                         for idx, value in enumerate(duration_whole.split(":")[::-1])
  100.                     )
  101.                 )
  102.                 if "." in duration:
  103.                     duration_seconds += decimal.Decimal(
  104.                         "0." + duration.split(".", 1)[1]
  105.                     )
  106.                 return start_timestamp, duration_seconds
  107.             elif accepted.lower() == "n":
  108.                 break
  109.  
  110.  
  111. def upload_audio(input_file, start_timestamp, duration):
  112.     with tempfile.TemporaryDirectory() as tempdir:
  113.         audio_file = pathlib.Path(tempdir) / "audio.mp3"
  114.         subprocess.run(
  115.             [
  116.                 "ffmpeg",
  117.                 "-ss",
  118.                 start_timestamp,
  119.                 "-i",
  120.                 input_file,
  121.                 "-t",
  122.                 str(duration),
  123.                 "-vn",
  124.                 "-c:a",
  125.                 "libmp3lame",
  126.                 "-q:a",
  127.                 "0",
  128.                 "-y",
  129.                 audio_file,
  130.             ],
  131.             check=True,
  132.         )
  133.         with audio_file.open("rb") as f:
  134.             return upload_catbox(f)
  135.  
  136.  
  137. def create_video(input_file, start_timestamp, duration, sound_url):
  138.     with tempfile.TemporaryDirectory() as tempdir:
  139.         pass_log_file = pathlib.Path(tempdir) / "pass.log"
  140.         video_file = pathlib.Path(tempdir) / "output.webm"
  141.         for pass_number in ("1", "2"):
  142.             subprocess.run(
  143.                 [
  144.                     "ffmpeg",
  145.                     "-ss",
  146.                     start_timestamp,
  147.                     "-i",
  148.                     input_file,
  149.                     "-t",
  150.                     str(duration),
  151.                     "-an",
  152.                     "-sn",
  153.                     *extra_video_args,
  154.                     "-b:v",
  155.                     str(int(MAX_FILESIZE / duration) * 8),
  156.                     # "-crf", "10",
  157.                     "-threads",
  158.                     "4",
  159.                     "-pass",
  160.                     pass_number,
  161.                     "-passlogfile",
  162.                     pass_log_file,
  163.                     "-y",
  164.                     video_file,
  165.                 ],
  166.                 check=True,
  167.             )
  168.         video_name = input("Video name? ")
  169.         escaped_sound_url = urllib.parse.quote(
  170.             sound_url.replace("https://", ""), safe=""
  171.         )
  172.         destination_file = (
  173.             input_file.parent / f"{video_name} [sound={escaped_sound_url}].webm"
  174.         )
  175.         shutil.copy(video_file, destination_file)
  176.         return destination_file
  177.  
  178.  
  179. def get_args():
  180.     parser = argparse.ArgumentParser()
  181.     parser.add_argument("input_file", type=pathlib.Path)
  182.     return parser.parse_args()
  183.  
  184.  
  185. def main():
  186.     args = get_args()
  187.  
  188.     start_timestamp, duration = negotiate_time_range(args.input_file)
  189.     sound_url = upload_audio(args.input_file, start_timestamp, duration)
  190.     output_file = create_video(args.input_file, start_timestamp, duration, sound_url)
  191.  
  192.  
  193. if __name__ == "__main__":
  194.     main()
  195.  
Add Comment
Please, Sign In to add comment