#! /usr/bin/python
"""Call ffmpeg with youtube-dl to convert files or youtube videos to mp4 or webp files."""

import subprocess, argparse

def get_input_args(input_path: str) -> list[str]:
    """Generate ffmpeg input arguments for either local files or YouTube URLs."""
    f_args: list[str] = []
    if "https" in input_path:
        urls = subprocess.run(["yt-dlp", "--get-url", input_path], capture_output=True, encoding='utf-8')
        for url in urls.stdout.split('\n'):
            if len(url) > 10:
                f_args += ["-i", url]
    else:
        f_args += ["-i", input_path]
    return f_args


def get_webp_args(quality: str | None) -> list[str]:
    """Generate ffmpeg arguments for WebP output format.

    Args:
        quality (str): Output quality percentage, defaults to 70 if not specified
    """
    args: list[str] = []
    args += ["-vcodec", "libwebp"]
    args += ["-preset", "default"]
    args += ["-loop", "0", "-an", "-vsync", "0"]
    args += ["-qscale", quality if quality else "70"]
    return args

def get_mp4_args() -> list[str]:
    """Generate ffmpeg arguments for MP4 output format."""
    args: list[str] = []
    args += ["-c:v", "libx264"]
    args += ["-preset", "veryfast"]
    args += ["-crf", "26"]
    args += ["-c:a", "aac"]
    args += ["-b:a", "128k"]
    args += ["-pix_fmt", "yuv420p"]
    return args

def get_video_filter(crop_arg: str | None) -> str:
    """Generate ffmpeg video filter string for rescaling and optional cropping.

    Rescales video height to 720p or original height if smaller, maintaining aspect ratio.
    Supports standard, percentage-based, and edge-based cropping formats."""
    vfilter = "scale=-2:'min(720,ih)'"
    if crop_arg:
        if crop_arg.endswith('%'):
            # Percentage-based centered cropping (single percentage value)
            percent = float(crop_arg.replace('%', ''))
            vfilter += f",crop='in_w*{percent/100}:in_h*{percent/100}:(in_w-in_w*{percent/100})/2:(in_h-in_h*{percent/100})/2'"
        elif crop_arg.startswith('edge:'):
            # Edge-based cropping (edge:left:right:top:bottom or edge:width:height)
            parts = crop_arg[5:].split(':')
            if len(parts) == 1:
                l = r = t = b = int(parts[0])
                vfilter += f",crop='in_w-{l+r}:in_h-{t+b}:{l}:{t}'"
            elif len(parts) == 2:
                w, h = map(int, parts)
                vfilter += f",crop='{w}:{h}:(in_w-{w})/2:(in_h-{h})/2'"
            else:
                l, r, t, b = map(int, parts)
                vfilter += f",crop='in_w-{l+r}:in_h-{t+b}:{l}:{t}'"
        else:
            # Standard cropping (out_w:out_h:x:y or out_w:out_h for centered)
            parts = crop_arg.split(':')
            if len(parts) == 2:
                w, h = parts
                vfilter += f",crop={w}:{h}:(in_w-{w})/2:(in_h-{h})/2"
            else:
                vfilter += f",crop={crop_arg}"
    return vfilter

def main():
    parser = argparse.ArgumentParser(description='Call ffmpeg for converting small video files to mp4 or webp')
    parser.add_argument('input', help='input file or url')
    parser.add_argument('output', help='output filename')
    parser.add_argument('-s', '--start', help='start time, if not 00:00')
    parser.add_argument('-t', '--to', help='end time, if not end of file')
    parser.add_argument('-q', '--quality', help='quality of output, as a percentage')
    parser.add_argument('-S', '--silent', action='store_true', help='no audio')
    parser.add_argument('-c', '--crop', help="""
    Crop formats supported:
        - Standard: "out_w:out_h:x:y" or "out_w:out_h" for centered
        - Percentage: "NN%" for centered crop to NN percent
        - Edge: "edge:left:right:top:bottom" or "edge:width:height" or single value for all""")
    args = parser.parse_args()

    f_args = get_input_args(args.input)

    f_args += ["-vf", get_video_filter(args.crop)]

    if args.start:
        f_args += ["-ss", args.start]  # -ss specifies the start timestamp
    if args.to:
        f_args += ["-to", args.to]  # -to specifies the end timestamp
    if args.silent:
        f_args += ["-an"]  # -an disables audio output
    f_args += ["-sn"]  # -sn removes subtitles
    f_args += ["-dn"]  # -dn removes data streams
    f_args += ["-map_metadata:c", "-1"]  # Remove metadata from the output

    output_ext = args.output.split('.')[-1]
    if output_ext == 'webp':
        f_args += get_webp_args(args.quality)
    elif output_ext == 'mp4':
        f_args += get_mp4_args()
    else:
        print(f"Error: Output file must have either .mp4 or .webp extension, found: .{output_ext}")
        exit(1)

    print(f_args)

    subprocess.run(["ffmpeg", "-y"] + f_args + [args.output if args.output else "output.mp4"])

if __name__ == "__main__":
    main()
