nazib61 commited on
Commit
4814522
Β·
verified Β·
1 Parent(s): 0cef924

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -67
app.py CHANGED
@@ -3,114 +3,103 @@ import subprocess
3
  import os
4
  import uuid
5
  import json
6
- import re
7
 
 
8
  if not os.path.exists("node_modules"):
9
  subprocess.run(["npm", "install"], check=True)
10
 
11
- def calculate_relevance(query, title, description):
12
- """Simple scoring based on keyword matches in title/description"""
13
- score = 0
14
- words = re.findall(r'\w+', query.lower())
15
- target_text = (title + " " + description).lower()
16
-
17
- for word in words:
18
- if word in target_text:
19
- score += 1
20
- # Extra points for title matches
21
- if word in title.lower():
22
- score += 2
23
- return score
24
-
25
  def get_video_dimensions(url):
26
  try:
27
- cmd = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of", "json", url]
 
 
 
28
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
29
  data = json.loads(result.stdout)
30
  return data['streams'][0]['width'], data['streams'][0]['height']
31
- except: return 0, 0
 
32
 
33
  def process_video(query, orientation, start_time, end_time, mute_audio, progress=gr.Progress()):
34
- if not query: return None, "⚠️ Enter a keyword."
 
 
 
 
35
 
36
- progress(0, desc="πŸ” Searching and Reading Metadata...")
37
  try:
38
- result = subprocess.run(["node", "scraper.js", query], capture_output=True, text=True, timeout=240)
 
 
 
 
 
39
 
40
- # Parse JSON results from Node
41
- raw_outputs = [line.strip() for line in result.stdout.split('\n') if line.strip().startswith("{")]
42
- video_candidates = []
43
- for raw in raw_outputs:
44
- try:
45
- video_candidates.append(json.loads(raw))
46
- except: continue
47
-
48
- if not video_candidates:
49
  return None, "❌ No videos found."
50
 
51
- # 1. Filter by Orientation and Calculate Scores
52
- scored_candidates = []
53
- for i, item in enumerate(video_candidates):
54
- progress((i+1)/len(video_candidates), desc=f"Matching relevance for {i+1} videos...")
55
-
56
- # Check orientation
57
- w, h = get_video_dimensions(item['url'])
58
- if orientation == "Portrait" and h <= w: continue
59
- if orientation == "Landscape" and w <= h: continue
60
-
61
- # Score this video
62
- score = calculate_relevance(query, item['title'], item['description'])
63
- item['score'] = score
64
- scored_candidates.append(item)
65
-
66
- if not scored_candidates:
67
- return None, f"❌ Found videos, but none match the {orientation} filter."
68
-
69
- # 2. Pick the HIGHEST scored video
70
- best_match = max(scored_candidates, key=lambda x: x['score'])
71
- selected_url = best_match['url']
72
 
73
- # 3. Process Final Video
74
- progress(0.9, desc="βš™οΈ Trimming and Finishing...")
75
- output_filename = f"final_{uuid.uuid4().hex[:5]}.mp4"
 
 
 
76
 
 
 
77
  ffmpeg_cmd = [
78
- "ffmpeg", "-ss", str(start_time), "-to", str(end_time) if end_time > 0 else "999",
79
- "-i", selected_url, "-vf", "unsharp=3:3:1.2:3:3:0.0,format=yuv420p",
80
- "-c:v", "libx264", "-crf", "18", "-preset", "veryfast", "-y", output_filename
 
 
 
 
 
 
81
  ]
82
-
83
- if mute_audio: ffmpeg_cmd.insert(-1, "-an")
 
84
  else:
85
  ffmpeg_cmd.insert(-1, "-c:a")
86
  ffmpeg_cmd.insert(-1, "aac")
87
 
88
  subprocess.run(ffmpeg_cmd, check=True)
89
-
90
- info_msg = f"βœ… Match Found! Score: {best_match['score']}\nTitle: {best_match['title'][:50]}..."
91
- return output_filename, info_msg
92
 
93
  except Exception as e:
94
  return None, f"❌ Error: {str(e)}"
95
 
96
  # --- UI ---
97
  with gr.Blocks(theme=gr.themes.Default()) as demo:
98
- gr.Markdown("# πŸ“Œ Smart Pinterest Matcher")
99
- gr.Markdown("Reads video titles & descriptions to find the absolute best match for your query.")
100
 
101
  with gr.Row():
102
  with gr.Column():
103
- q = gr.Textbox(label="Deep Search Query", placeholder="e.g. Cinematic Rain in Japan")
104
  o = gr.Dropdown(choices=["Any", "Portrait", "Landscape"], value="Any", label="Orientation")
105
  with gr.Row():
106
  s = gr.Number(label="Start (sec)", value=0)
107
  e = gr.Number(label="End (sec)", value=5)
108
  mute = gr.Checkbox(label="Mute Audio", value=False)
109
- btn = gr.Button("πŸš€ Smart Search & Process", variant="primary")
110
 
111
  with gr.Column():
112
- out_v = gr.Video(label="Best Match Result")
113
- out_s = gr.Textbox(label="Status / Metadata Match")
114
 
115
  btn.click(process_video, [q, o, s, e, mute], [out_v, out_s])
116
 
 
3
  import os
4
  import uuid
5
  import json
 
6
 
7
+ # Auto-install Node modules
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, "⚠️ Enter a keyword."
26
+
27
+ progress(0, desc="πŸ” Searching Pinterest...")
28
+ search_query = query if "video" in query.lower() else f"{query} video"
29
 
 
30
  try:
31
+ # 1. Scrape
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."
40
 
41
+ # 2. Orientation Filter
42
+ selected_url = None
43
+ for i, url in enumerate(all_urls):
44
+ progress((i+1)/len(all_urls), desc=f"Filtering {i+1}/{len(all_urls)}...")
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
50
+ if orientation == "Landscape" and w > h:
51
+ selected_url = url; break
 
 
 
 
 
 
 
 
 
 
52
 
53
+ if not selected_url:
54
+ return None, f"❌ Checked {len(all_urls)} videos, no {orientation} found."
55
+
56
+ # 3. Fast Processing (Original Quality)
57
+ progress(0.8, desc="βš™οΈ Trimming & Sharpening...")
58
+ output_filename = f"processed_{uuid.uuid4().hex[:5]}.mp4"
59
 
60
+ # We keep original resolution but add a light sharpening filter for clarity
61
+ # We also ensure the output is yuv420p for maximum compatibility
62
  ffmpeg_cmd = [
63
+ "ffmpeg",
64
+ "-ss", str(start_time),
65
+ "-to", str(end_time) if end_time > 0 else "999",
66
+ "-i", selected_url,
67
+ "-vf", "unsharp=3:3:1.2:3:3:0.0,format=yuv420p",
68
+ "-c:v", "libx264",
69
+ "-crf", "18",
70
+ "-preset", "veryfast",
71
+ "-y", output_filename
72
  ]
73
+
74
+ if mute_audio:
75
+ ffmpeg_cmd.insert(-1, "-an")
76
  else:
77
  ffmpeg_cmd.insert(-1, "-c:a")
78
  ffmpeg_cmd.insert(-1, "aac")
79
 
80
  subprocess.run(ffmpeg_cmd, check=True)
81
+ return output_filename, f"βœ… Success! (Checked {i+1} pins)"
 
 
82
 
83
  except Exception as e:
84
  return None, f"❌ Error: {str(e)}"
85
 
86
  # --- UI ---
87
  with gr.Blocks(theme=gr.themes.Default()) as demo:
88
+ gr.Markdown("# πŸ“Œ Pinterest Video Master")
 
89
 
90
  with gr.Row():
91
  with gr.Column():
92
+ q = gr.Textbox(label="Topic", placeholder="e.g. Minecraft")
93
  o = gr.Dropdown(choices=["Any", "Portrait", "Landscape"], value="Any", label="Orientation")
94
  with gr.Row():
95
  s = gr.Number(label="Start (sec)", value=0)
96
  e = gr.Number(label="End (sec)", value=5)
97
  mute = gr.Checkbox(label="Mute Audio", value=False)
98
+ btn = gr.Button("πŸš€ Search & Process", variant="primary")
99
 
100
  with gr.Column():
101
+ out_v = gr.Video(label="Result Video")
102
+ out_s = gr.Textbox(label="Status")
103
 
104
  btn.click(process_video, [q, o, s, e, mute], [out_v, out_s])
105