Jeffgold commited on
Commit
d60ecdf
1 Parent(s): 423bf83

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +160 -0
app.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import requests
3
+ from urllib.parse import urlparse, quote_plus
4
+ import subprocess
5
+ from pathlib import Path
6
+ from moviepy.editor import VideoFileClip
7
+ import streamlit as st
8
+ import http.server
9
+ import socketserver
10
+ import threading
11
+ import socket
12
+ import boto3
13
+ import s3fs
14
+ import os
15
+
16
+ # Define output directory
17
+ output_dir = Path.cwd() / "output"
18
+ output_dir.mkdir(exist_ok=True)
19
+
20
+ # Set S3 credentials
21
+ AWS_S3_BUCKET = os.getenv("AWS_S3_BUCKET")
22
+ AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
23
+ AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
24
+ session = boto3.Session(
25
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
26
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
27
+ region_name='us-east-1'
28
+ )
29
+
30
+ s3 = session.client('s3')
31
+ fs = s3fs.S3FileSystem(anon=False, client_kwargs={'region_name': 'us-east-1'})
32
+
33
+ logging.basicConfig(level=logging.INFO)
34
+
35
+ standard_resolutions = [4320, 2160, 1440, 1080, 720, 480, 360, 240]
36
+
37
+ def start_http_server(port=8000):
38
+ handler = http.server.SimpleHTTPRequestHandler
39
+ httpd = socketserver.TCPServer(("", port), handler)
40
+ logging.info(f"Serving at port {port}")
41
+ thread = threading.Thread(target=httpd.serve_forever)
42
+ thread.start()
43
+
44
+ def get_ip_address():
45
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
46
+ try:
47
+ s.connect(('10.255.255.255', 1))
48
+ IP = s.getsockname()[0]
49
+ except Exception:
50
+ IP = '127.0.0.1'
51
+ finally:
52
+ s.close()
53
+ return IP
54
+
55
+ def download_file(url, destination):
56
+ response = requests.get(url)
57
+ response.raise_for_status()
58
+ with open(destination, 'wb') as f:
59
+ f.write(response.content)
60
+
61
+ def get_input_path(video_file, video_url):
62
+ if video_file is not None:
63
+ return Path(video_file.name)
64
+ elif video_url:
65
+ url_path = urlparse(video_url).path
66
+ file_name = Path(url_path).name
67
+ destination = output_dir / file_name
68
+ download_file(video_url, destination)
69
+ return destination
70
+ else:
71
+ raise ValueError("No input was provided.")
72
+
73
+ def get_output_path(input_path, res):
74
+ output_path = output_dir / f"{Path(input_path).stem}_{res}p.m3u8"
75
+ return output_path
76
+
77
+ def create_master_playlist(output_paths, input_filename):
78
+ master_playlist_path = output_dir / f"{input_filename}_master_playlist.m3u8"
79
+ with open(master_playlist_path, "w") as f:
80
+ f.write("#EXTM3U\n")
81
+ for path in output_paths:
82
+ res = Path(path).stem.split("_")[1]
83
+ f.write(f'#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION={res}\n')
84
+ # read the .m3u8 file and replace relative links with absolute links
85
+ with open(path, 'r') as playlist_file:
86
+ content = playlist_file.readlines()
87
+ content = [line.replace('.ts', f'.ts\n') if line.endswith('.ts\n') else line for line in content]
88
+ content = [f"{quote_plus(line.rstrip())}\n" if line.endswith('.ts\n') else line for line in content]
89
+ f.writelines(content)
90
+ return master_playlist_path
91
+
92
+ def upload_file_to_s3(file_path, s3_path):
93
+ print(f"Uploading {file_path} to AWS at {s3_path}")
94
+ with open(file_path, 'rb') as file:
95
+ file_content = file.read()
96
+
97
+ with fs.open(s3_path, 'wb') as file:
98
+ file.write(file_content)
99
+
100
+
101
+ def convert_video(video_file, quality, aspect_ratio, video_url):
102
+ # Ensure either a file or a URL is provided
103
+ if not video_file and not video_url:
104
+ raise ValueError("Please provide either a video file or a video URL.")
105
+
106
+ input_path = get_input_path(video_file, video_url)
107
+ video = VideoFileClip(str(input_path))
108
+
109
+ # Get the original height to avoid upscaling
110
+ original_height = video.size[1]
111
+
112
+ output_paths = []
113
+ for res in reversed(standard_resolutions):
114
+ if res > original_height:
115
+ continue
116
+
117
+ scale = "-1:" + str(res)
118
+ output_path = get_output_path(input_path, str(res) + 'p')
119
+
120
+ # use a lower CRF value for better quality
121
+ ffmpeg_command = [
122
+ "ffmpeg", "-i", str(input_path), "-c:v", "libx264", "-crf", str(quality),
123
+ "-vf", f"scale={scale}:force_original_aspect_ratio=decrease,pad=ceil(iw/2)*2:ceil(ih/2)*2",
124
+ "-hls_time", "10", "-hls_playlist_type", "vod", "-hls_segment_filename",
125
+ str(output_dir / f"{output_path.stem}_%03d.ts"), str(output_path)
126
+ ]
127
+
128
+ logging.info("Running ffmpeg command: " + ' '.join(ffmpeg_command))
129
+ subprocess.run(ffmpeg_command, check=True)
130
+
131
+ output_paths.append(output_path)
132
+
133
+ master_playlist_path = create_master_playlist(output_paths, Path(input_path).stem)
134
+ output_paths.append(master_playlist_path)
135
+
136
+ output_links = []
137
+ for path in output_paths:
138
+ s3_path = f"s3://{AWS_S3_BUCKET}/{Path(input_path).stem}/{Path(path).name}"
139
+ upload_file_to_s3(path, s3_path)
140
+ 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>')
141
+
142
+ # upload ts files to s3
143
+ for path in output_dir.glob(f"{Path(input_path).stem}_*p_*.ts"):
144
+ s3_path = f"s3://{AWS_S3_BUCKET}/{Path(input_path).stem}/{Path(path).name}"
145
+ upload_file_to_s3(path, s3_path)
146
+
147
+ output_html = "<br>".join(output_links)
148
+ return output_html
149
+
150
+ st.title("NEAR Hub Video Transcoder to m3u8")
151
+ st.markdown("Convert video files to m3u8 for VOD streaming in nearhub.club.")
152
+
153
+ video_file = st.file_uploader("Your video file")
154
+ quality = st.slider("Quality (0: Highest Quality - 50: Fastest)", min_value=0, max_value=51, value=23)
155
+ aspect_ratio = st.selectbox("Aspect Ratio", ["16:9", "4:3", "1:1", "3:4", "9:16", "1:1", "2:1", "1:2"], index=0)
156
+ video_url = st.text_input("Video URL", "")
157
+
158
+ if st.button("Transcode"):
159
+ output_html = convert_video(video_file, quality, aspect_ratio, video_url)
160
+ st.write(output_html, unsafe_allow_html=True)