File size: 4,350 Bytes
c16a803
 
5296733
c16a803
40446f2
e4ed31e
6c154f8
c16a803
 
b1ae044
c16a803
40446f2
6c154f8
 
 
c16a803
6c154f8
 
79683ca
6c154f8
 
79683ca
c047e75
e4ed31e
cbd85fc
 
6c154f8
 
 
 
 
3c753ee
712c716
6c154f8
712c716
6c154f8
79683ca
6c154f8
 
 
 
 
 
6f1f7b0
30f78b0
 
 
 
 
 
 
 
 
 
1229389
49ea8e3
88ed798
c16a803
 
 
5a36eaa
c16a803
6f1f7b0
88ed798
 
0212a08
b1ae044
e280197
88ed798
 
 
bd750f5
 
88ed798
 
5a36eaa
 
 
1229389
 
 
5a36eaa
30f78b0
1229389
 
 
 
 
30f78b0
1229389
30f78b0
1229389
c358ea4
1229389
 
 
 
6c154f8
1229389
 
 
 
 
 
 
88ed798
1229389
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import logging
import shutil
import tempfile
import subprocess
from pathlib import Path
from moviepy.editor import VideoFileClip
import gradio as gr
import requests
from urllib.parse import urlparse

logging.basicConfig(level=logging.INFO)

def download_file(url, destination):
    """Downloads a file from a url to a destination."""
    response = requests.get(url)
    response.raise_for_status()
    with open(destination, 'wb') as f:
        f.write(response.content)

def get_input_path(video_file, video_url, temp_dir):
    """Returns the path to the video file, downloading it if necessary."""
    if video_file is not None:
        return Path(video_file.name)
    elif video_url:
        url_path = urlparse(video_url).path
        file_name = Path(url_path).name
        destination = temp_dir / file_name
        download_file(video_url, destination)
        return destination
    else:
        raise ValueError("No input was provided.")

def get_output_path(input_path, temp_dir, res):
    """Returns the path to the output file, creating it if necessary."""
    output_path = temp_dir / (Path(input_path).stem + f"_{res}.m3u8")
    return output_path

def get_aspect_ratio(input_path, aspect_ratio):
    """Returns the aspect ratio of the video, calculating it if necessary."""
    if aspect_ratio is not None:
        return aspect_ratio
    video = VideoFileClip(str(input_path))
    return f"{video.size[0]}:{video.size[1]}"

def create_master_playlist(output_paths, temp_dir):
    """Creates a master playlist .m3u8 file that includes all other .m3u8 files."""
    master_playlist_path = temp_dir / "master_playlist.m3u8"
    with open(master_playlist_path, 'w') as f:
        f.write("#EXTM3U\n")
        for path in output_paths:
            f.write(f"#EXT-X-STREAM-INF:BANDWIDTH={1000*1000},RESOLUTION={path.stem.split('_')[-1]}\n")
            f.write(f"{path.name}\n")
    return master_playlist_path

def convert_video(video_file, quality, aspect_ratio, video_url):
    standard_resolutions = [4320, 2160, 1440, 1080, 720, 480, 360, 240, 144]  # 8K, 4K, 2K, Full HD, HD, SD in pixels

    with tempfile.TemporaryDirectory() as temp_dir:
        temp_dir = Path(temp_dir)
        input_path = get_input_path(video_file, video_url, temp_dir)

        aspect_ratio = get_aspect_ratio(input_path, aspect_ratio)

        video = VideoFileClip(str(input_path))
        original_height = video.size[1]

        output_paths = []  # Define output_paths as an empty list

        for res in standard_resolutions:
            # Skip if resolution is higher than original
            if res > original_height:
                continue

            scale = "-1:" + str(res)  # we scale the height to res and keep aspect ratio
            output_path = get_output_path(input_path, temp_dir, str(res) + 'p')  # pass the resolution to create a unique output file

            ffmpeg_command = [
                "ffmpeg", "-i", str(input_path), "-c:v", "libx264", "-crf", str(quality),
                "-vf", f"scale={scale}:force_original_aspect_ratio=decrease,pad=ceil(iw/2)*2:ceil(ih/2)*2",
                "-hls_time", "10", "-hls_playlist_type", "vod", "-hls_segment_filename",
                str(temp_dir / f"{output_path.stem}_%03d.ts"), str(output_path)
            ]

            logging.info("Running ffmpeg command: " + ' '.join(ffmpeg_command))
            subprocess.run(ffmpeg_command, check=True)

            output_paths.append(output_path)  # Append each completed output file to the output_paths list

        master_playlist_path = create_master_playlist(output_paths, temp_dir)
        output_paths.append(master_playlist_path)

    return output_paths  # Return the list of completed output files

video_file = gr.inputs.File(label="Upload video file")
quality = gr.inputs.Slider(18, 28, 1, 23, label="Video quality (18-28, lower is better)")
aspect_ratio = gr.inputs.Textbox("16:9", label="Aspect ratio (width:height)")
video_url = gr.inputs.Textbox(label="Or enter video URL")

iface = gr.Interface(
    convert_video,
    [video_file, quality, aspect_ratio, video_url],
    gr.outputs.File(label="Download video file(s)"),
    title="Video Transcoder",
    description="Transcodes a video file or URL to multiple resolutions for adaptive streaming. Higher quality values reduce the video bitrate."
)

iface.launch()