File size: 5,393 Bytes
d51b3b7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import logging
import shutil
from pathlib import Path
from moviepy.editor import VideoFileClip
import gradio as gr
import requests
from urllib.parse import urlparse
import subprocess
import atexit
from flask import Flask, send_from_directory
from threading import Thread

# Initialize a Flask application
app = Flask(__name__)

@app.route('/files/<path:path>')
def serve_file(path):
    return send_from_directory(Path.cwd(), path)

# Start the Flask server in a new thread
Thread(target=app.run, kwargs={'host': '0.0.0.0', 'port': 5000}).start()

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):
    """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 = Path.cwd() / file_name
        download_file(video_url, destination)
        return destination
    else:
        raise ValueError("No input was provided.")

def get_output_path(input_path, res):
    """Returns the path to the output file, creating it if necessary."""
    output_path = Path.cwd() / (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):
    """Creates a master playlist .m3u8 file that includes all other .m3u8 files."""
    master_playlist_path = Path.cwd() / "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  # make sure this is a single Path object


def convert_video(video_file, quality, aspect_ratio, video_url):
    input_path = get_input_path(video_file, video_url)
    aspect_ratio = get_aspect_ratio(input_path, aspect_ratio)

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

    output_paths = []  

    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, 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(Path.cwd() / 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)  

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

    html_components = []

    for path in output_paths:
        # Create a video player and a download link for each video file
        if path.suffix in ['.mp4', '.webm', '.ogg']:
            video_path = f"http://localhost:5000/files/{path.name}"
            video_component = f"<video width='320' height='240' controls><source src='{video_path}' type='video/{path.suffix.lstrip('.')}'>Your browser does not support the video tag.</video>"
            download_link = f"<p><a href='{video_path}' download>Download this video</a></p>"
            html_components.append(f"{video_component}{download_link}")

    return html_components,  # add more return values as needed

outputs = [
    gr.outputs.HTML(label="Video Players"),
    # add more outputs as needed
]


video_file = gr.inputs.File(label="Video File")
quality = gr.inputs.Dropdown(
    choices=["18", "23", "27", "28", "32"], 
    default="27", 
    label="Quality"
)
aspect_ratio = gr.inputs.Dropdown(
    choices=["16:9",  "1:1", "4:3", "3:2", "5:4", "21:9", "1.85:1", "2.35:1", "3:1", "360", "9:16", "2:1", "1:2", "9:1"], 
    default="16:9", 
    label="Aspect ratio (width:height)"
)
standard_resolutions = [4320, 2160, 1440, 1080, 720, 480, 360, 240, 144]  # 8K, 4K, 2K, Full HD, HD, SD in pixels
video_url = gr.inputs.Textbox(label="Or enter video URL")

outputs = [
    gr.outputs.HTML(label="Download Links"),
    gr.outputs.Video(label="Video Player"),
    gr.outputs.Textbox(label="Text Files", type="text")
]

interface = gr.Interface(
    fn=convert_video, 
    inputs=[video_file, quality, aspect_ratio, video_url],
    outputs=outputs,
    title="Video Converter",
    description="A simple video converter app",
    allow_flagging=False, 
    server_name="0.0.0.0",
    server_port=7860,
)
interface.launch()