File size: 6,344 Bytes
d60ecdf
 
 
 
 
 
 
 
 
 
 
 
8412930
d60ecdf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8412930
 
 
d60ecdf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f97ecc6
 
 
 
 
 
 
 
 
 
 
 
 
d60ecdf
 
 
 
 
f97ecc6
d60ecdf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f97ecc6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d60ecdf
 
 
 
 
f97ecc6
 
d60ecdf
 
 
8412930
f97ecc6
d60ecdf
 
 
f97ecc6
d60ecdf
 
f97ecc6
 
d60ecdf
f97ecc6
d60ecdf
 
 
 
 
 
 
 
 
 
f97ecc6
d60ecdf
 
 
 
f97ecc6
d60ecdf
f97ecc6
d60ecdf
 
f97ecc6
 
d60ecdf
 
 
 
 
 
 
 
8412930
d60ecdf
 
 
 
 
 
 
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import requests
from urllib.parse import urlparse, quote_plus
import subprocess
from pathlib import Path
from moviepy.editor import VideoFileClip
import streamlit as st
import http.server
import socketserver
import threading
import socket
import boto3
import s3fs
import os 

# Define output directory
output_dir = Path.cwd() / "output"
output_dir.mkdir(exist_ok=True)

# Set S3 credentials
AWS_S3_BUCKET = os.getenv("AWS_S3_BUCKET")
AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
session = boto3.Session(
    aws_access_key_id=AWS_ACCESS_KEY_ID,
    aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
    region_name='us-east-1'
)

s3 = session.client('s3')
fs = s3fs.S3FileSystem(anon=False, client_kwargs={'region_name': 'us-east-1'})

logging.basicConfig(level=logging.INFO)

standard_resolutions = [4320, 2160, 1440, 1080, 720, 480, 360, 240]

def start_http_server(port=8000):
    handler = http.server.SimpleHTTPRequestHandler
    httpd = socketserver.TCPServer(("", port), handler)
    logging.info(f"Serving at port {port}")
    thread = threading.Thread(target=httpd.serve_forever)
    thread.start()

def get_ip_address():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except Exception:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP

def download_file(url, 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):
    if video_file is not None:
        # Save the uploaded file to the output directory
        input_path = output_dir / video_file.name
        with open(input_path, "wb") as f:
            f.write(video_file.getbuffer())
        return input_path
    elif video_url:
        url_path = urlparse(video_url).path
        file_name = Path(url_path).name
        destination = output_dir / file_name
        download_file(video_url, destination)
        return destination
    else:
        raise ValueError("No input was provided.")

def get_output_path(input_path, res):
    output_path = output_dir / f"{Path(input_path).stem}_{res}p.m3u8"
    return output_path

def create_master_playlist(output_paths, input_filename):
    master_playlist_path = output_dir / f"{input_filename}_master_playlist.m3u8"
    with open(master_playlist_path, "w") as f:
        f.write("#EXTM3U\n")
        for path in output_paths:
            res = Path(path).stem.split("_")[1]
            f.write(f'#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION={res}\n')
            # read the .m3u8 file and replace relative links with absolute links
            with open(path, 'r') as playlist_file:
                content = playlist_file.readlines()
            content = [line.replace('.ts', f'.ts\n') if line.endswith('.ts\n') else line for line in content]
            content = [f"{quote_plus(line.rstrip())}\n" if line.endswith('.ts\n') else line for line in content]
            f.writelines(content)
    return master_playlist_path

def upload_file_to_s3(local_path, s3_key):
    s3 = boto3.client(
        "s3",
        aws_access_key_id=st.secrets["AWS_ACCESS_KEY_ID"],
        aws_secret_access_key=st.secrets["AWS_SECRET_ACCESS_KEY"],
        region_name=st.secrets["AWS_REGION"],
    )

    bucket_name = st.secrets["AWS_S3_BUCKET"]

    with open(local_path, "rb") as file:
        s3.upload_fileobj(file, bucket_name, s3_key)

    return f"https://{bucket_name}.s3.amazonaws.com/{s3_key}"


def convert_video(video_file, quality, aspect_ratio, video_url):
    # Ensure either a file or a URL is provided
    if not video_file and not video_url:
        raise ValueError("Please provide either a video file or a video URL.")
    
    input_path = get_input_path(video_file, video_url)
    st.write(f"Input file path: {input_path}")  # Added line to print input file path
    video = VideoFileClip(str(input_path))

    # Get the original height to avoid upscaling
    original_height = video.size[1]

    output_paths = []
    for res in reversed(standard_resolutions):
        if res > original_height:
            continue

        scale = "-1:" + str(res)
        output_path = get_output_path(input_path, str(res) + 'p')

        # use a lower CRF value for better quality
        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(output_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)

    master_playlist_path = create_master_playlist(output_paths, Path(input_path).stem)
    output_paths.append(master_playlist_path)

    output_links = []
    for path in output_paths:
        s3_path = f"s3://{AWS_S3_BUCKET}/{Path(input_path).stem}/{Path(path).name}"
        upload_file_to_s3(path, s3_path)
        output_links.append(f'<a href="https://{AWS_S3_BUCKET}.s3.amazonaws.com/{Path(input_path).stem}/{quote_plus(Path(path).name)}" target="_blank">Download {Path(path).stem}</a>')

    # upload ts files to s3
    for path in output_dir.glob(f"{Path(input_path).stem}_*p_*.ts"):
        s3_path = f"s3://{AWS_S3_BUCKET}/{Path(input_path).stem}/{Path(path).name}"
        upload_file_to_s3(path, s3_path)

    output_html = "<br>".join(output_links)
    return output_html

st.title("NEAR Hub Video Transcoder to m3u8")
st.markdown("Convert video files to m3u8 for VOD streaming in nearhub.club.")

video_file = st.file_uploader("Your video file")
quality = st.slider("Quality (0: Highest Quality - 50: Fastest)", min_value=0, max_value=51, value=23)
aspect_ratio = st.selectbox("Aspect Ratio", ["16:9", "4:3", "1:1", "3:4", "9:16", "1:1", "2:1", "1:2"], index=0)
video_url = st.text_input("Video URL", "")

if st.button("Transcode"):
    output_html = convert_video(video_file, quality, aspect_ratio, video_url)
    st.write(output_html, unsafe_allow_html=True)