Spaces:
Sleeping
Sleeping
| import os | |
| import asyncio | |
| import subprocess | |
| import uuid | |
| import glob | |
| import shutil | |
| import gradio as gr | |
| # Set executable fdkaac path | |
| fdkaac_path = os.path.abspath("./fdkaac") | |
| ffmpeg_path = shutil.which("ffmpeg") | |
| yt_dlp_path = shutil.which("yt-dlp") | |
| if not os.path.isfile(fdkaac_path): | |
| raise FileNotFoundError("fdkaac binary not found in current directory.") | |
| if not ffmpeg_path: | |
| raise FileNotFoundError("FFmpeg not found in PATH.") | |
| if not yt_dlp_path: | |
| raise FileNotFoundError("yt-dlp not found in PATH.") | |
| accel = 'auto' | |
| video_base_opts = ['-crf', '63', '-c:v', 'libx264', '-tune', 'zerolatency'] | |
| UPLOAD_FOLDER = 'uploads' | |
| CONVERTED_FOLDER = 'converted' | |
| os.makedirs(UPLOAD_FOLDER, exist_ok=True) | |
| os.makedirs(CONVERTED_FOLDER, exist_ok=True) | |
| async def run_subprocess(cmd, use_fdkaac=False): | |
| env = os.environ.copy() | |
| if use_fdkaac: | |
| env["LD_LIBRARY_PATH"] = env.get("LD_LIBRARY_PATH", "") + ":/usr/local/lib" | |
| print(f"[DEBUG] Running command:\n{' '.join(cmd)}\n") | |
| process = await asyncio.create_subprocess_exec( | |
| *cmd, | |
| stdout=asyncio.subprocess.PIPE, | |
| stderr=asyncio.subprocess.PIPE, | |
| env=env | |
| ) | |
| stdout, stderr = await process.communicate() | |
| if process.returncode != 0: | |
| print(f"[ERROR] Command failed:\n{stderr.decode()}\n") | |
| raise subprocess.CalledProcessError(process.returncode, cmd, stderr.decode()) | |
| print(f"[DEBUG] Command succeeded:\n{stdout.decode()}\n") | |
| return stdout.decode(), stderr.decode() | |
| async def convert_video_task(input_path, downscale, faster, use_mp3): | |
| if use_mp3: | |
| output_path = os.path.join(CONVERTED_FOLDER, f"{uuid.uuid4()}.mp3") | |
| ffmpeg_cmd = [ | |
| ffmpeg_path, '-y', '-i', input_path, '-vn', | |
| '-c:a', 'libmp3lame', '-b:a', '8k', '-ar', '24000', '-ac', '1', output_path | |
| ] | |
| await run_subprocess(ffmpeg_cmd) | |
| return output_path, None | |
| audio_wav = os.path.join(CONVERTED_FOLDER, f"{uuid.uuid4()}.wav") | |
| audio_output = os.path.join(CONVERTED_FOLDER, f"{uuid.uuid4()}.m4a") | |
| video_output = os.path.join(CONVERTED_FOLDER, f"{uuid.uuid4()}.mp4") | |
| await run_subprocess([ | |
| ffmpeg_path, '-y', '-i', input_path, '-ac', '1', '-ar', '8000', audio_wav | |
| ]) | |
| await run_subprocess([ | |
| fdkaac_path, '-b', '1k', '-C', '-f', '2', '-G', '1', '-w', '8000', | |
| '-o', audio_output, audio_wav | |
| ], use_fdkaac=True) | |
| video_cmd = [ | |
| ffmpeg_path, '-y', '-hwaccel', accel, '-i', input_path, | |
| *video_base_opts, '-an', video_output | |
| ] | |
| if faster: | |
| video_cmd.extend(['-preset', 'ultrafast']) | |
| await run_subprocess(video_cmd) | |
| return audio_output, video_output | |
| async def process_conversion(use_youtube, youtube_url, video_file, downscale, faster, use_mp3, audio_only): | |
| try: | |
| if use_youtube: | |
| yt_uuid = str(uuid.uuid4()) | |
| yt_out = os.path.join(UPLOAD_FOLDER, yt_uuid + ".%(ext)s") | |
| yt_cmd = [yt_dlp_path, '-o', yt_out, '-f', 'b', youtube_url] | |
| await run_subprocess(yt_cmd) | |
| pattern = os.path.join(UPLOAD_FOLDER, yt_uuid + ".*") | |
| files = glob.glob(pattern) | |
| if not files: | |
| return "Download failed.", None | |
| input_path = files[0] | |
| else: | |
| if not video_file: | |
| return "No video provided.", None | |
| ext = os.path.splitext(video_file)[1] | |
| input_path = os.path.join(UPLOAD_FOLDER, f"{uuid.uuid4()}{ext}") | |
| shutil.copy2(video_file, input_path) | |
| audio_out, video_out = await convert_video_task(input_path, downscale, faster, use_mp3) | |
| if audio_only: | |
| return audio_out, audio_out | |
| return video_out or audio_out, video_out or audio_out | |
| except Exception as e: | |
| return str(e), None | |
| def convert_video(*args): | |
| return asyncio.run(process_conversion(*args)) | |
| # Gradio Interface | |
| with gr.Blocks(theme=gr.themes.Default(primary_hue="rose")) as demo: | |
| gr.Markdown(""" | |
| # π₯ **Low Quality Video Inator** | |
| Welcome to your personal bad-video-making machine. | |
| Upload a video or paste a YouTube URL below, then tweak the settings to ruin your video (on purpose). | |
| """) | |
| with gr.Group(): | |
| with gr.Row(): | |
| use_youtube = gr.Checkbox(label="π Use YouTube URL (experimental)", value=False) | |
| youtube_url = gr.Textbox(label="YouTube URL", placeholder="Paste YouTube URL here") | |
| video_file = gr.File(label="π Upload Video File") | |
| gr.Markdown("### βοΈ **Conversion Settings**") | |
| with gr.Group(): | |
| with gr.Row(): | |
| downscale = gr.Checkbox(label="Downscale Video to 144p", value=False) | |
| faster = gr.Checkbox(label="Faster Video Compression", value=False) | |
| with gr.Row(): | |
| use_mp3 = gr.Checkbox(label="Use MP3 Audio", value=False) | |
| audio_only = gr.Checkbox(label="Audio Only", value=False) | |
| convert_button = gr.Button("Convert Now", variant="primary") | |
| gr.Markdown("### **Conversion Preview**") | |
| video_preview = gr.Video(label="Preview Output (if applicable)") | |
| gr.Markdown("### **Download Your Masterpiece**") | |
| file_download = gr.File(label="Download Result") | |
| convert_button.click( | |
| convert_video, | |
| inputs=[use_youtube, youtube_url, video_file, downscale, faster, use_mp3, audio_only], | |
| outputs=[video_preview, file_download] | |
| ) | |
| demo.launch() |