nazib61 commited on
Commit
6d9efef
Β·
verified Β·
1 Parent(s): c400431

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -64
app.py CHANGED
@@ -4,46 +4,59 @@ import os
4
  import uuid
5
  import json
6
 
7
- # Startup: Install Node modules if missing
8
  if not os.path.exists("node_modules"):
9
  subprocess.run(["npm", "install"], check=True)
10
 
11
  def get_video_dimensions(url):
12
  try:
13
- cmd = [
14
- "ffprobe", "-v", "error", "-select_streams", "v:0",
15
- "-show_entries", "stream=width,height", "-of", "json", url
16
- ]
17
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
18
  data = json.loads(result.stdout)
19
  return data['streams'][0]['width'], data['streams'][0]['height']
20
- except:
21
- return 0, 0
 
 
 
 
 
 
 
 
 
22
 
23
  def process_video(query, orientation, start_time, end_time, mute_audio, progress=gr.Progress()):
24
- if not query:
25
- return None, "⚠️ Please enter a keyword."
26
 
27
- progress(0, desc="πŸ” Searching deep on Pinterest...")
28
- search_query = query if "video" in query.lower() else f"{query} video"
29
 
30
  try:
31
- # 1. Run Node Scraper
32
- result = subprocess.run(
33
- ["node", "scraper.js", search_query],
34
- capture_output=True, text=True, timeout=240
35
- )
36
- all_urls = [line.strip() for line in result.stdout.split('\n') if line.strip().startswith("http")]
37
 
38
- if not all_urls:
39
- return None, "❌ No videos found in search."
 
 
 
 
 
 
 
 
40
 
41
- # 2. Filter for orientation match
 
 
 
42
  selected_url = None
43
- for i, url in enumerate(all_urls):
44
- progress((i+1)/len(all_urls), desc=f"Checking video {i+1} for {orientation}...")
 
 
45
  if orientation == "Any":
46
  selected_url = url; break
 
47
  w, h = get_video_dimensions(url)
48
  if orientation == "Portrait" and h > w:
49
  selected_url = url; break
@@ -51,67 +64,47 @@ def process_video(query, orientation, start_time, end_time, mute_audio, progress
51
  selected_url = url; break
52
 
53
  if not selected_url:
54
- return None, f"❌ Checked {len(all_urls)} videos, but no {orientation} found."
55
 
56
- # 3. ULTRA 4K ENHANCEMENT & AUDIO HANDLING
57
- progress(0.8, desc="πŸ’Ž Upscaling to 4K & Applying Filters...")
58
- output_filename = f"final_4k_{uuid.uuid4().hex[:5]}.mp4"
59
 
60
- # Filter chain for 4K 9:16 Portrait + Sharpening
61
- filter_complex = (
62
- "scale=2160:3840:force_original_aspect_ratio=increase,"
63
- "crop=2160:3840,"
64
- "unsharp=5:5:1.5:5:5:0.0"
65
- )
66
-
67
  ffmpeg_cmd = [
68
- "ffmpeg",
69
- "-ss", str(start_time),
70
- "-to", str(end_time) if end_time > 0 else "999",
71
- "-i", selected_url,
72
- "-vf", filter_complex,
73
- "-c:v", "libx264",
74
- "-preset", "medium",
75
- "-crf", "18",
76
- "-y", output_filename
77
  ]
78
-
79
- # Audio Logic
80
- if mute_audio:
81
- ffmpeg_cmd.insert(-1, "-an") # Add Mute flag before output filename
82
- else:
83
- ffmpeg_cmd.insert(-1, "-c:a")
84
- ffmpeg_cmd.insert(-1, "aac") # Keep and re-encode audio
85
 
86
  subprocess.run(ffmpeg_cmd, check=True)
87
- return output_filename, f"βœ… Done! Found {orientation} video and processed with sound={'OFF' if mute_audio else 'ON'}"
88
 
89
  except Exception as e:
90
  return None, f"❌ Error: {str(e)}"
91
 
92
- # --- Gradio UI ---
93
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
94
- gr.Markdown("# πŸ“Œ Pinterest Ultra-HD 4K Video Tool")
95
- gr.Markdown("Search Pinterest, filter orientation, trim exactly, and upscale to **Sharpened 4K 9:16**.")
96
 
97
  with gr.Row():
98
  with gr.Column():
99
- q = gr.Textbox(label="Topic", placeholder="e.g. Rainy Mood")
100
- o = gr.Dropdown(choices=["Any", "Portrait", "Landscape"], value="Landscape", label="Orientation Filter")
101
-
102
  with gr.Row():
103
  s = gr.Number(label="Start (sec)", value=0)
104
- e = gr.Number(label="End (sec, 0=Full)", value=5)
105
-
106
- audio_toggle = gr.Checkbox(label="Mute Audio", value=False)
107
- btn = gr.Button("πŸš€ Deep Search & Process", variant="primary")
108
 
109
  with gr.Column():
110
- out_v = gr.Video(label="Enhanced Result")
111
- out_s = gr.Textbox(label="Status Log")
112
 
113
- # Link UI components to the function
114
- btn.click(process_video, [q, o, s, e, audio_toggle], [out_v, out_s])
115
 
116
  if __name__ == "__main__":
117
  demo.launch()
 
4
  import uuid
5
  import json
6
 
 
7
  if not os.path.exists("node_modules"):
8
  subprocess.run(["npm", "install"], check=True)
9
 
10
  def get_video_dimensions(url):
11
  try:
12
+ cmd = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of", "json", url]
 
 
 
13
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
14
  data = json.loads(result.stdout)
15
  return data['streams'][0]['width'], data['streams'][0]['height']
16
+ except: return 0, 0
17
+
18
+ def calculate_relevance(query, title, desc):
19
+ """Counts how many query words appear in the title or description"""
20
+ score = 0
21
+ words = query.lower().split()
22
+ combined_text = (title + " " + desc).lower()
23
+ for word in words:
24
+ if word in combined_text:
25
+ score += 1
26
+ return score
27
 
28
  def process_video(query, orientation, start_time, end_time, mute_audio, progress=gr.Progress()):
29
+ if not query: return None, "⚠️ Enter a keyword."
 
30
 
31
+ progress(0, desc="πŸ” Searching & Analyzing Data...")
 
32
 
33
  try:
34
+ result = subprocess.run(["node", "scraper.js", query], capture_output=True, text=True, timeout=240)
35
+ lines = [line.strip() for line in result.stdout.split('\n') if "|" in line]
 
 
 
 
36
 
37
+ if not lines: return None, "❌ No videos found."
38
+
39
+ # Parse the structured data: [URL, Title, Description]
40
+ candidate_videos = []
41
+ for line in lines:
42
+ parts = line.split('|')
43
+ if len(parts) >= 3:
44
+ url, title, desc = parts[0], parts[1], parts[2]
45
+ score = calculate_relevance(query, title, desc)
46
+ candidate_videos.append({"url": url, "score": score})
47
 
48
+ # Sort videos so the ones with the highest relevance score come first
49
+ candidate_videos = sorted(candidate_videos, key=lambda x: x['score'], reverse=True)
50
+
51
+ # 2. Filter for orientation match based on sorted list
52
  selected_url = None
53
+ for i, video in enumerate(candidate_videos):
54
+ url = video['url']
55
+ progress((i+1)/len(candidate_videos), desc=f"Matching relevance & orientation...")
56
+
57
  if orientation == "Any":
58
  selected_url = url; break
59
+
60
  w, h = get_video_dimensions(url)
61
  if orientation == "Portrait" and h > w:
62
  selected_url = url; break
 
64
  selected_url = url; break
65
 
66
  if not selected_url:
67
+ return None, f"❌ Found videos, but none matched the {orientation} requirement."
68
 
69
+ # 3. Final Process
70
+ progress(0.9, desc="βš™οΈ Finalizing Video...")
71
+ output_filename = f"smart_result_{uuid.uuid4().hex[:5]}.mp4"
72
 
 
 
 
 
 
 
 
73
  ffmpeg_cmd = [
74
+ "ffmpeg", "-ss", str(start_time), "-to", str(end_time) if end_time > 0 else "999",
75
+ "-i", selected_url, "-vf", "unsharp=3:3:1.2:3:3:0.0,format=yuv420p",
76
+ "-c:v", "libx264", "-crf", "18", "-preset", "veryfast", "-y", output_filename
 
 
 
 
 
 
77
  ]
78
+
79
+ if mute_audio: ffmpeg_cmd.insert(-1, "-an")
80
+ else: ffmpeg_cmd.insert(-1, "-c:a"); ffmpeg_cmd.insert(-1, "aac")
 
 
 
 
81
 
82
  subprocess.run(ffmpeg_cmd, check=True)
83
+ return output_filename, f"βœ… Best Match Found! (Relevance Score: {video['score']})"
84
 
85
  except Exception as e:
86
  return None, f"❌ Error: {str(e)}"
87
 
88
+ # --- UI ---
89
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
90
+ gr.Markdown("# πŸ“Œ Smart Pinterest Video Downloader")
91
+ gr.Markdown("This version reads Pin titles and descriptions to find the most relevant video for your query.")
92
 
93
  with gr.Row():
94
  with gr.Column():
95
+ q = gr.Textbox(label="Smart Search Query", placeholder="e.g. blue ocean waves")
96
+ o = gr.Dropdown(choices=["Any", "Portrait", "Landscape"], value="Any", label="Orientation")
 
97
  with gr.Row():
98
  s = gr.Number(label="Start (sec)", value=0)
99
+ e = gr.Number(label="End (sec)", value=5)
100
+ mute = gr.Checkbox(label="Mute Audio", value=False)
101
+ btn = gr.Button("πŸš€ Smart Search & Process", variant="primary")
 
102
 
103
  with gr.Column():
104
+ out_v = gr.Video(label="Result Video")
105
+ out_s = gr.Textbox(label="Status")
106
 
107
+ btn.click(process_video, [q, o, s, e, mute], [out_v, out_s])
 
108
 
109
  if __name__ == "__main__":
110
  demo.launch()