Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,129 +1,116 @@
|
|
1 |
import os
|
|
|
2 |
import subprocess
|
3 |
import uuid
|
4 |
import glob
|
5 |
import shutil
|
6 |
import gradio as gr
|
7 |
|
8 |
-
os.system("chmod +x ffmpeg")
|
9 |
-
# Set default acceleration and extra encoding options.
|
10 |
accel = 'auto'
|
11 |
-
|
12 |
|
13 |
-
# Fixed upload and conversion folders.
|
14 |
UPLOAD_FOLDER = 'uploads'
|
15 |
CONVERTED_FOLDER = 'converted'
|
16 |
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
17 |
os.makedirs(CONVERTED_FOLDER, exist_ok=True)
|
18 |
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
"""
|
28 |
-
Converts a video either from a YouTube URL or an uploaded file.
|
29 |
-
Returns a tuple (preview_file, download_file) with the output path.
|
30 |
-
"""
|
31 |
-
# Determine output filename based on whether only audio is desired.
|
32 |
-
if audio_only:
|
33 |
-
output_filename = f"{uuid.uuid4()}.mp3" if use_mp3 else f"{uuid.uuid4()}.aac"
|
34 |
-
else:
|
35 |
-
output_filename = f"{uuid.uuid4()}.mp4"
|
36 |
-
output_path = os.path.join(CONVERTED_FOLDER, output_filename)
|
37 |
-
|
38 |
-
if use_youtube:
|
39 |
-
if not youtube_url:
|
40 |
-
return "Error: YouTube URL is required.", None
|
41 |
-
|
42 |
-
quality = 'worstaudio' if audio_only and downscale else (
|
43 |
-
'worstvideo+worstaudio' if downscale else 'b')
|
44 |
-
|
45 |
-
yt_uuid = str(uuid.uuid4())
|
46 |
-
yt_input_filename = yt_uuid + ".%(ext)s"
|
47 |
-
yt_input_path = os.path.join(UPLOAD_FOLDER, yt_input_filename)
|
48 |
-
yt_dlp_cmd = ['yt-dlp', '-o', yt_input_path, '-f', quality, youtube_url]
|
49 |
-
try:
|
50 |
-
subprocess.run(yt_dlp_cmd, check=True)
|
51 |
-
except subprocess.CalledProcessError as e:
|
52 |
-
return f"An error occurred during YouTube download: {e}", None
|
53 |
-
|
54 |
-
pattern = os.path.join(UPLOAD_FOLDER, yt_uuid + ".*")
|
55 |
-
downloaded_files = glob.glob(pattern)
|
56 |
-
if not downloaded_files:
|
57 |
-
return "Failed to download YouTube video.", None
|
58 |
-
input_path = downloaded_files[0]
|
59 |
-
else:
|
60 |
-
if not video_file:
|
61 |
-
return "Error: No video file provided.", None
|
62 |
-
if os.path.exists(video_file):
|
63 |
-
ext = os.path.splitext(video_file)[1]
|
64 |
-
input_filename = f"{uuid.uuid4()}{ext}"
|
65 |
-
input_path = os.path.join(UPLOAD_FOLDER, input_filename)
|
66 |
-
shutil.copy2(video_file, input_path)
|
67 |
-
else:
|
68 |
-
input_path = video_file
|
69 |
-
|
70 |
-
# Handle optional downscale first
|
71 |
-
if downscale and not audio_only:
|
72 |
-
downscaled_path = os.path.join(UPLOAD_FOLDER, f"downscaled_{uuid.uuid4()}.mp4")
|
73 |
-
downscale_cmd = ['ffmpeg', '-hwaccel', accel, '-y', '-i', input_path,
|
74 |
-
'-vf', 'scale=144:-2', '-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '40',
|
75 |
-
'-c:a', 'copy', downscaled_path]
|
76 |
-
try:
|
77 |
-
subprocess.run(downscale_cmd, check=True)
|
78 |
-
except subprocess.CalledProcessError as e:
|
79 |
-
return f"An error occurred during downscaling: {e}", None
|
80 |
-
input_path = downscaled_path # Use this for next pass
|
81 |
-
|
82 |
-
# Now do the final encode
|
83 |
-
ffmpeg_cmd = ['./ffmpeg', '-hwaccel', accel, '-y', '-i', input_path] + extra.split()
|
84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
if faster:
|
86 |
-
|
|
|
87 |
|
88 |
-
|
89 |
-
ffmpeg_cmd.extend(['-vn'])
|
90 |
-
configure_audio(ffmpeg_cmd, use_mp3)
|
91 |
-
|
92 |
-
ffmpeg_cmd.append(output_path)
|
93 |
|
|
|
94 |
try:
|
95 |
-
|
96 |
-
|
97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
|
99 |
-
|
|
|
100 |
|
101 |
-
#
|
102 |
with gr.Blocks() as demo:
|
103 |
gr.Markdown("## Low Quality Video Inator")
|
104 |
gr.Markdown("Upload a video or use a YouTube URL and adjust the options below.")
|
105 |
-
|
106 |
with gr.Row():
|
107 |
-
use_youtube = gr.Checkbox(label="Use YouTube URL
|
108 |
-
youtube_url = gr.Textbox(label="YouTube URL"
|
109 |
video_file = gr.File(label="Upload Video")
|
110 |
-
|
111 |
with gr.Row():
|
112 |
downscale = gr.Checkbox(label="Downscale Video (144p)", value=False)
|
113 |
-
faster = gr.Checkbox(label="Faster Conversion
|
114 |
with gr.Row():
|
115 |
-
use_mp3 = gr.Checkbox(label="Use MP3 (video
|
116 |
-
audio_only = gr.Checkbox(label="Only Audio
|
117 |
-
|
118 |
-
convert_button = gr.Button("Convert
|
119 |
-
|
120 |
-
gr.
|
121 |
-
|
122 |
-
gr.
|
123 |
-
|
124 |
-
|
125 |
convert_button.click(
|
126 |
-
convert_video,
|
127 |
inputs=[use_youtube, youtube_url, video_file, downscale, faster, use_mp3, audio_only],
|
128 |
outputs=[video_preview, file_download]
|
129 |
)
|
|
|
1 |
import os
|
2 |
+
import asyncio
|
3 |
import subprocess
|
4 |
import uuid
|
5 |
import glob
|
6 |
import shutil
|
7 |
import gradio as gr
|
8 |
|
|
|
|
|
9 |
accel = 'auto'
|
10 |
+
video_extra = ['-crf', '63', '-c:v', 'libx264', '-preset', 'ultrafast', '-tune', 'zerolatency']
|
11 |
|
|
|
12 |
UPLOAD_FOLDER = 'uploads'
|
13 |
CONVERTED_FOLDER = 'converted'
|
14 |
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
15 |
os.makedirs(CONVERTED_FOLDER, exist_ok=True)
|
16 |
|
17 |
+
async def run_subprocess(cmd):
|
18 |
+
process = await asyncio.create_subprocess_exec(
|
19 |
+
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
20 |
+
)
|
21 |
+
stdout, stderr = await process.communicate()
|
22 |
+
if process.returncode != 0:
|
23 |
+
raise subprocess.CalledProcessError(process.returncode, cmd, stderr.decode())
|
24 |
+
return stdout.decode(), stderr.decode()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
|
26 |
+
async def convert_video_task(input_path, downscale, faster, use_mp3):
|
27 |
+
if use_mp3:
|
28 |
+
# Directly compress audio to MP3 with video untouched or skipped
|
29 |
+
output_path = os.path.join(CONVERTED_FOLDER, f"{uuid.uuid4()}.mp3")
|
30 |
+
ffmpeg_cmd = ['ffmpeg', '-y', '-i', input_path, '-vn',
|
31 |
+
'-c:a', 'libmp3lame', '-b:a', '8k', '-ar', '24000', '-ac', '1', output_path]
|
32 |
+
await run_subprocess(ffmpeg_cmd)
|
33 |
+
return output_path, None # No video output in this case
|
34 |
+
|
35 |
+
# Non-MP3 path: split streams
|
36 |
+
audio_wav = os.path.join(CONVERTED_FOLDER, f"{uuid.uuid4()}.wav")
|
37 |
+
audio_output = os.path.join(CONVERTED_FOLDER, f"{uuid.uuid4()}.m4a")
|
38 |
+
video_output = os.path.join(CONVERTED_FOLDER, f"{uuid.uuid4()}.mp4")
|
39 |
+
|
40 |
+
# Extract mono WAV
|
41 |
+
await run_subprocess([
|
42 |
+
'ffmpeg', '-y', '-i', input_path, '-ac', '1', '-ar', '8000', audio_wav
|
43 |
+
])
|
44 |
+
|
45 |
+
# Compress with fdkaac
|
46 |
+
await run_subprocess([
|
47 |
+
'fdkaac', '-b', '1k', '-C', '-f', '2', '-G', '1', '-w', '8000',
|
48 |
+
'-o', audio_output, audio_wav
|
49 |
+
])
|
50 |
+
|
51 |
+
# Video compression
|
52 |
+
video_cmd = ['ffmpeg', '-y', '-hwaccel', accel, '-i', input_path, *video_extra, '-an', video_output]
|
53 |
if faster:
|
54 |
+
video_cmd.extend(['-preset', 'ultrafast'])
|
55 |
+
await run_subprocess(video_cmd)
|
56 |
|
57 |
+
return audio_output, video_output
|
|
|
|
|
|
|
|
|
58 |
|
59 |
+
async def process_conversion(use_youtube, youtube_url, video_file, downscale, faster, use_mp3, audio_only):
|
60 |
try:
|
61 |
+
if use_youtube:
|
62 |
+
yt_uuid = str(uuid.uuid4())
|
63 |
+
yt_out = os.path.join(UPLOAD_FOLDER, yt_uuid + ".%(ext)s")
|
64 |
+
yt_cmd = ['yt-dlp', '-o', yt_out, '-f', 'b', youtube_url]
|
65 |
+
await run_subprocess(yt_cmd)
|
66 |
+
pattern = os.path.join(UPLOAD_FOLDER, yt_uuid + ".*")
|
67 |
+
files = glob.glob(pattern)
|
68 |
+
if not files:
|
69 |
+
return "Download failed.", None
|
70 |
+
input_path = files[0]
|
71 |
+
else:
|
72 |
+
if not video_file:
|
73 |
+
return "No video provided.", None
|
74 |
+
ext = os.path.splitext(video_file)[1]
|
75 |
+
input_path = os.path.join(UPLOAD_FOLDER, f"{uuid.uuid4()}{ext}")
|
76 |
+
shutil.copy2(video_file, input_path)
|
77 |
+
|
78 |
+
audio_out, video_out = await convert_video_task(input_path, downscale, faster, use_mp3)
|
79 |
+
|
80 |
+
if audio_only:
|
81 |
+
return audio_out, audio_out
|
82 |
+
return video_out or audio_out, video_out or audio_out
|
83 |
+
except Exception as e:
|
84 |
+
return str(e), None
|
85 |
|
86 |
+
def convert_video(*args):
|
87 |
+
return asyncio.run(process_conversion(*args))
|
88 |
|
89 |
+
# Gradio Interface
|
90 |
with gr.Blocks() as demo:
|
91 |
gr.Markdown("## Low Quality Video Inator")
|
92 |
gr.Markdown("Upload a video or use a YouTube URL and adjust the options below.")
|
93 |
+
|
94 |
with gr.Row():
|
95 |
+
use_youtube = gr.Checkbox(label="Use YouTube URL", value=False)
|
96 |
+
youtube_url = gr.Textbox(label="YouTube URL")
|
97 |
video_file = gr.File(label="Upload Video")
|
98 |
+
|
99 |
with gr.Row():
|
100 |
downscale = gr.Checkbox(label="Downscale Video (144p)", value=False)
|
101 |
+
faster = gr.Checkbox(label="Faster Conversion", value=False)
|
102 |
with gr.Row():
|
103 |
+
use_mp3 = gr.Checkbox(label="Use MP3 (skips video compression)", value=False)
|
104 |
+
audio_only = gr.Checkbox(label="Only Audio", value=False)
|
105 |
+
|
106 |
+
convert_button = gr.Button("Convert")
|
107 |
+
gr.Markdown("### Preview")
|
108 |
+
video_preview = gr.Video()
|
109 |
+
gr.Markdown("### Download")
|
110 |
+
file_download = gr.File()
|
111 |
+
|
|
|
112 |
convert_button.click(
|
113 |
+
convert_video,
|
114 |
inputs=[use_youtube, youtube_url, video_file, downscale, faster, use_mp3, audio_only],
|
115 |
outputs=[video_preview, file_download]
|
116 |
)
|