File size: 4,502 Bytes
fed2287
 
 
4887e95
efb68a6
a4b5d8d
4814522
2da681a
 
 
efb68a6
 
4814522
 
 
 
efb68a6
 
f4082e2
4814522
 
6d9efef
ded726e
4814522
 
 
 
 
fed2287
 
ded726e
4814522
 
 
 
 
efb68a6
4814522
2da681a
0267b8a
4814522
ded726e
 
 
 
 
4814522
ded726e
 
 
 
 
 
 
 
 
 
 
 
 
 
2da681a
4814522
ded726e
4814522
ded726e
 
 
2da681a
ded726e
 
 
 
 
 
 
 
 
 
 
4887e95
4814522
 
 
 
ded726e
4814522
 
 
 
4887e95
4814522
 
 
2da681a
 
 
6dc2268
4887e95
ded726e
4887e95
2da681a
 
fed2287
2da681a
ded726e
 
 
2da681a
fed2287
4887e95
ded726e
4887e95
f4082e2
6d9efef
 
ded726e
2da681a
4887e95
ded726e
4814522
fed2287
ded726e
fed2287
2da681a
 
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
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()