Pin_Video_Reel / app.py
nazib61's picture
Update app.py
ded726e verified
import gradio as gr
import subprocess
import os
import uuid
import json
# Auto-install Node modules
if not os.path.exists("node_modules"):
subprocess.run(["npm", "install"], check=True)
def get_video_dimensions(url):
try:
cmd = [
"ffprobe", "-v", "error", "-select_streams", "v:0",
"-show_entries", "stream=width,height", "-of", "json", url
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
data = json.loads(result.stdout)
return data['streams'][0]['width'], data['streams'][0]['height']
except:
return 0, 0
def process_video(query, start_time, end_time, mute_audio, progress=gr.Progress()):
if not query:
return None, "⚠️ Enter a keyword."
progress(0, desc="πŸ” Searching Pinterest...")
search_query = query if "video" in query.lower() else f"{query} video"
try:
# 1. Scrape URLs
result = subprocess.run(
["node", "scraper.js", search_query],
capture_output=True, text=True, timeout=240
)
all_urls = [line.strip() for line in result.stdout.split('\n') if line.strip().startswith("http")]
if not all_urls:
return None, "❌ No videos found."
selected_url = None
needs_crop = False
# --- PHASE 1: SEARCH FOR PORTRAIT FIRST ---
progress(0.2, desc="πŸ” Phase 1: Looking for Portrait video...")
for url in all_urls:
w, h = get_video_dimensions(url)
if h > w: # Found Portrait
selected_url = url
needs_crop = False
break
# --- PHASE 2: SEARCH FOR LANDSCAPE FALLBACK ---
if not selected_url:
progress(0.5, desc="πŸ” Phase 2: No Portrait found. Looking for Landscape to crop...")
for url in all_urls:
w, h = get_video_dimensions(url)
if w > h: # Found Landscape
selected_url = url
needs_crop = True
break
if not selected_url:
return None, "❌ No videos found in any orientation."
# 3. Process Video
progress(0.8, desc="βš™οΈ Final Processing...")
output_filename = f"output_{uuid.uuid4().hex[:5]}.mp4"
# Define Filter Logic
if needs_crop:
# Crop middle to 9:16 (ih*9/16 is the width based on height)
# We then scale it to a standard height like 1280 to keep it clean
vf_filter = "crop=ih*9/16:ih,scale=-1:1280,unsharp=3:3:1.2:3:3:0.0,format=yuv420p"
status_msg = "βœ… Found Landscape: Cropped to 9:16"
else:
# Already Portrait: Just sharpen and standardize
vf_filter = "scale=-1:1280,unsharp=3:3:1.2:3:3:0.0,format=yuv420p"
status_msg = "βœ… Found Portrait: Keeping original ratio"
ffmpeg_cmd = [
"ffmpeg",
"-ss", str(start_time),
"-to", str(end_time) if end_time > 0 else "999",
"-i", selected_url,
"-vf", vf_filter,
"-c:v", "libx264",
"-crf", "18",
"-preset", "veryfast",
"-y", output_filename
]
if mute_audio:
ffmpeg_cmd.insert(-1, "-an")
else:
ffmpeg_cmd.insert(-1, "-c:a")
ffmpeg_cmd.insert(-1, "aac")
subprocess.run(ffmpeg_cmd, check=True)
return output_filename, status_msg
except Exception as e:
return None, f"❌ Error: {str(e)}"
# --- UI ---
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# πŸ“Œ Pinterest Smart 9:16 Downloader")
gr.Markdown("Prioritizes Vertical videos. If only Horizontal are found, it crops the center to 9:16.")
with gr.Row():
with gr.Column():
q = gr.Textbox(label="Topic", placeholder="e.g. Minecraft Gameplay")
with gr.Row():
s = gr.Number(label="Start (sec)", value=0)
e = gr.Number(label="End (sec)", value=5)
mute = gr.Checkbox(label="Mute Audio", value=False)
btn = gr.Button("πŸš€ Smart Search & Process", variant="primary")
with gr.Column():
out_v = gr.Video(label="9:16 Result")
out_s = gr.Textbox(label="Status")
btn.click(process_video, [q, s, e, mute], [out_v, out_s])
if __name__ == "__main__":
demo.launch()