nazib61 commited on
Commit
2da681a
Β·
verified Β·
1 Parent(s): 1910190

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -42
app.py CHANGED
@@ -3,10 +3,24 @@ import subprocess
3
  import os
4
  import uuid
5
  import json
6
- from difflib import SequenceMatcher
7
 
8
- def calculate_similarity(a, b):
9
- return SequenceMatcher(None, a.lower(), b.lower()).ratio()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  def get_video_dimensions(url):
12
  try:
@@ -19,74 +33,86 @@ def get_video_dimensions(url):
19
  def process_video(query, orientation, start_time, end_time, mute_audio, progress=gr.Progress()):
20
  if not query: return None, "⚠️ Enter a keyword."
21
 
22
- progress(0, desc="πŸ” Searching and analyzing metadata...")
23
  try:
24
  result = subprocess.run(["node", "scraper.js", query], capture_output=True, text=True, timeout=240)
25
 
26
- candidates = []
27
- for line in result.stdout.split('\n'):
28
- if "|" in line:
29
- url, metadata = line.strip().split("|", 1)
30
- candidates.append({"url": url, "metadata": metadata})
 
 
31
 
32
- if not candidates: return None, "❌ No videos found."
 
33
 
34
- # Filter by Orientation AND Rank by Metadata Similarity
35
- best_match = None
36
- highest_score = -1
37
-
38
- for i, item in enumerate(candidates):
39
- progress((i+1)/len(candidates), desc=f"Matching query with video data...")
40
 
41
- # Check Orientation first
42
  w, h = get_video_dimensions(item['url'])
43
- is_match = False
44
- if orientation == "Any": is_match = True
45
- elif orientation == "Portrait" and h > w: is_match = True
46
- elif orientation == "Landscape" and w > h: is_match = True
47
 
48
- if is_match:
49
- # Calculate how well the title matches the user's search query
50
- score = calculate_similarity(query, item['metadata'])
51
- if score > highest_score:
52
- highest_score = score
53
- best_match = item['url']
54
-
55
- if not best_match:
56
- return None, f"❌ Searched {len(candidates)} videos, but no matching orientation found."
57
 
58
- # 3. Final FFmpeg Process
59
- progress(0.9, desc="βš™οΈ Trimming & Finalizing...")
60
- output_filename = f"best_match_{uuid.uuid4().hex[:5]}.mp4"
 
 
 
 
 
61
  ffmpeg_cmd = [
62
  "ffmpeg", "-ss", str(start_time), "-to", str(end_time) if end_time > 0 else "999",
63
- "-i", best_match, "-vf", "unsharp=3:3:1.2:3:3:0.0,format=yuv420p",
64
  "-c:v", "libx264", "-crf", "18", "-preset", "veryfast", "-y", output_filename
65
  ]
66
 
67
  if mute_audio: ffmpeg_cmd.insert(-1, "-an")
68
- else: ffmpeg_cmd.insert(-1, "-c:a"); ffmpeg_cmd.insert(-1, "aac")
 
 
69
 
70
  subprocess.run(ffmpeg_cmd, check=True)
71
- return output_filename, f"βœ… Best Match Found (Score: {int(highest_score*100)}%)"
 
 
72
 
73
- except Exception as e: return None, f"❌ Error: {str(e)}"
 
74
 
 
75
  with gr.Blocks(theme=gr.themes.Default()) as demo:
76
- gr.Markdown("# πŸ“Œ Smart Pinterest Search")
 
 
77
  with gr.Row():
78
  with gr.Column():
79
- q = gr.Textbox(label="Deep Search Query", placeholder="e.g. Sunset Ocean Relaxing")
80
  o = gr.Dropdown(choices=["Any", "Portrait", "Landscape"], value="Any", label="Orientation")
81
  with gr.Row():
82
  s = gr.Number(label="Start (sec)", value=0)
83
  e = gr.Number(label="End (sec)", value=5)
84
  mute = gr.Checkbox(label="Mute Audio", value=False)
85
  btn = gr.Button("πŸš€ Smart Search & Process", variant="primary")
 
86
  with gr.Column():
87
- out_v = gr.Video(label="Result")
88
- out_s = gr.Textbox(label="Matching Score")
89
 
90
  btn.click(process_video, [q, o, s, e, mute], [out_v, out_s])
91
 
92
- demo.launch()
 
 
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:
 
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
 
117
+ if __name__ == "__main__":
118
+ demo.launch()