Nymbo commited on
Commit
ac2b487
·
verified ·
1 Parent(s): a8f7368

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +150 -254
app.py CHANGED
@@ -6,280 +6,176 @@ from google_img_source_search import ReverseImageSearcher
6
  from PIL import Image
7
  import os
8
  import uuid
9
- from pathlib import Path
10
- import shutil
11
- import logging
12
- import sys
13
-
14
- # Set up logging
15
- logging.basicConfig(
16
- level=logging.INFO,
17
- format='%(asctime)s - %(levelname)s - %(message)s',
18
- handlers=[
19
- logging.StreamHandler(sys.stdout)
20
- ]
21
- )
22
- logger = logging.getLogger(__name__)
23
-
24
- # Custom CSS for better presentation
25
- CUSTOM_CSS = """
26
- .result-item {
27
- margin: 20px 0;
28
- padding: 15px;
29
- border: 1px solid #ddd;
30
- border-radius: 5px;
31
- }
32
- .result-item img {
33
- max-width: 100%;
34
- height: auto;
35
- margin: 10px 0;
36
- }
37
- .error-message {
38
- color: red;
39
- padding: 10px;
40
- border: 1px solid red;
41
- border-radius: 5px;
42
- margin: 10px 0;
43
- }
44
- """
45
-
46
- def create_temp_dir():
47
- """Create a temporary directory with UUID."""
48
- uid = str(uuid.uuid4())
49
- temp_dir = Path(f"temp_{uid}")
50
- temp_dir.mkdir(exist_ok=True)
51
- return temp_dir
52
-
53
- def cleanup_temp_dir(temp_dir):
54
- """Safely clean up temporary directory and its contents."""
55
- try:
56
- if temp_dir.exists():
57
- shutil.rmtree(str(temp_dir))
58
- except Exception as e:
59
- logger.error(f"Error cleaning up temporary directory: {e}")
60
 
61
  def dl(inp):
62
- """Download video from URL using yt-dlp."""
63
- if not inp or not inp.strip():
64
- return None, gr.HTML("<p class='error-message'>Please provide a valid URL</p>"), "", ""
65
-
66
- temp_dir = create_temp_dir()
67
  try:
68
- # Sanitize input filename
69
- inp_out = inp.replace("https://", "").replace("/", "_").replace(".", "_").replace("=", "_").replace("?", "_")
70
- output_path = str(temp_dir / f"{inp_out}.mp4")
71
-
72
- # Configure yt-dlp options
73
- ydl_opts = {
74
- 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4',
75
- 'outtmpl': output_path,
76
- 'quiet': True,
77
- 'no_warnings': True,
78
- 'extract_flat': False,
79
- 'merge_output_format': 'mp4'
80
- }
81
-
82
- # Handle Twitter URLs differently
83
- if "twitter" in inp.lower():
84
- ydl_opts['extractor_args'] = {'twitter': {'api': ['syndication']}}
85
-
86
- # Download the video
87
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
88
- logger.info(f"Downloading video from: {inp}")
89
- ydl.download([inp])
90
-
91
- if os.path.exists(output_path):
92
- logger.info(f"Successfully downloaded to: {output_path}")
93
- return output_path, gr.HTML(""), "", ""
94
  else:
95
- raise Exception("Download completed but file not found")
96
-
97
- except Exception as e:
98
- logger.error(f"Error downloading video: {e}")
99
- cleanup_temp_dir(temp_dir)
100
- return None, gr.HTML(f"<p class='error-message'>Error: {str(e)}</p>"), "", ""
101
-
102
- def process_vid(file, cur_frame, every_n):
103
- """Process video file and search for frames."""
104
- if not file:
105
- return gr.HTML("<p class='error-message'>No video file provided</p>"), "", ""
106
-
107
- temp_dir = create_temp_dir()
108
- capture = None
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  try:
111
- capture = cv2.VideoCapture(str(file))
112
- if not capture.isOpened():
113
- raise Exception("Failed to open video file")
114
-
115
- frame_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
116
- rev_img_searcher = ReverseImageSearcher()
117
- html_out = ""
118
- count = int(every_n)
119
-
120
- start_frame = int(cur_frame) if cur_frame and cur_frame.strip() else 0
121
-
122
  for i in range(start_frame, frame_count-1):
123
  if count == int(every_n):
124
  count = 1
125
- logger.info(f"Processing frame {i}")
126
  capture.set(cv2.CAP_PROP_POS_FRAMES, i)
127
- ret, frame = capture.read()
128
-
129
- if not ret:
130
- continue
131
-
132
- temp_image = temp_dir / f"frame_{i}.png"
133
- cv2.imwrite(str(temp_image), frame)
134
-
135
- # Generate the URL for the temporary image
136
- image_path = os.path.abspath(str(temp_image))
137
- image_url = f'https://nymbo-reverse-image.hf.space/file={image_path}'
138
-
139
- try:
140
- results = rev_img_searcher.search(image_url)
141
- if results:
142
- out_cnt = len(results)
143
- html_out = build_results_html(results)
144
- return gr.HTML(f'<h1>Total Found: {out_cnt}</h1><br>{html_out}'), f"Found frame: {i}", i+int(every_n)
145
- except Exception as search_error:
146
- logger.error(f"Search error on frame {i}: {search_error}")
147
-
148
- count += 1
149
-
 
 
 
 
 
 
 
 
 
150
  except Exception as e:
151
- logger.error(f"Error processing video: {e}")
152
- return gr.HTML(f'<p class="error-message">Error: {str(e)}</p>'), "", ""
153
- finally:
154
- if capture is not None:
155
- capture.release()
156
- cleanup_temp_dir(temp_dir)
157
-
158
- return gr.HTML('No frame matches found.'), "", ""
159
 
160
- def process_im(file, url):
161
- """Process image file or URL."""
162
  if not url.startswith("https://nymbo"):
163
  return url
164
-
165
- temp_dir = create_temp_dir()
166
- try:
167
- temp_image = temp_dir / "temp.png"
168
  read_file = Image.open(file)
169
- read_file.save(str(temp_image))
170
-
171
- out_url = f'https://nymbo-reverse-image.hf.space/file={os.path.abspath(str(temp_image))}'
172
- return out_url
173
- finally:
174
- cleanup_temp_dir(temp_dir)
175
 
176
  def rev_im(image):
177
- """Reverse image search for a single image."""
178
- if not image:
179
- return gr.HTML("<p class='error-message'>No image provided</p>")
180
-
181
- temp_dir = create_temp_dir()
182
- try:
183
- temp_image = temp_dir / "temp.png"
184
- image_cv = cv2.imread(image)
185
- cv2.imwrite(str(temp_image), image_cv)
186
-
187
- out_url = f'https://nymbo-reverse-image.hf.space/file={os.path.abspath(str(temp_image))}'
188
-
189
- rev_img_searcher = ReverseImageSearcher()
190
- results = rev_img_searcher.search(out_url)
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
- return gr.HTML(build_results_html(results, len(results)))
193
- except Exception as e:
194
- logger.error(f"Error in reverse image search: {e}")
195
- return gr.HTML(f'<p class="error-message">Error: {str(e)}</p>')
196
- finally:
197
- cleanup_temp_dir(temp_dir)
198
 
199
- def build_results_html(results, count=None):
200
- """Build HTML output for search results."""
201
- html_out = f"<h1>Total Found: {count}</h1><br>" if count is not None else ""
202
-
203
- for item in results:
204
- html_out += f"""
205
- <div class="result-item">
206
- <h3>{item.page_title}</h3>
207
- <p>Source: <a href='{item.page_url}' target='_blank' rel='noopener noreferrer'>{item.page_url}</a></p>
208
- <p>Image: <a href='{item.image_url}' target='_blank' rel='noopener noreferrer'>{item.image_url}</a></p>
209
- <img class='my_im' src='{item.image_url}' alt='{item.page_title}'>
210
- </div>
211
- """
212
- return html_out
213
 
214
- def create_ui():
215
- """Create the Gradio interface."""
216
- with gr.Blocks(css=CUSTOM_CSS) as app:
217
- with gr.Row():
218
- gr.Column()
219
- with gr.Column():
220
- source_tog = gr.Radio(
221
- choices=["Image", "Video"],
222
- value="Image",
223
- label="Search Type"
224
- )
225
-
226
- # Image search interface
227
- with gr.Box(visible=True) as im_box:
228
- inp_url = gr.Textbox(label="Image URL")
229
- load_im_btn = gr.Button("Load Image", variant="primary")
230
- inp_im = gr.Image(label="Search Image", type='filepath')
231
- go_btn_im = gr.Button("Search", variant="primary")
232
-
233
- # Video search interface
234
- with gr.Box(visible=False) as vid_box:
235
- vid_url = gr.Textbox(label="Video URL")
236
- vid_url_btn = gr.Button("Load URL", variant="primary")
237
- inp_vid = gr.Video(label="Search Video")
238
- with gr.Row():
239
- every_n = gr.Number(
240
- label="Every nth frame",
241
- value=10,
242
- minimum=1,
243
- maximum=100,
244
- step=1
245
- )
246
- stat_box = gr.Textbox(label="Status", interactive=False)
247
- with gr.Row():
248
- go_btn_vid = gr.Button("Start", variant="primary")
249
- next_btn = gr.Button("Next Frame", variant="secondary")
250
-
251
- gr.Column()
252
-
253
- with gr.Row():
254
- html_out = gr.HTML()
255
-
256
- with gr.Row(visible=False):
257
- hid_box = gr.Textbox()
258
 
259
- # Event handlers
260
- def toggle_interface(tog):
261
- """Toggle between image and video interfaces."""
262
- return (
263
- gr.update(visible=tog == "Image"),
264
- gr.update(visible=tog == "Video")
265
- )
266
 
267
- # Connect all event handlers
268
- source_tog.change(
269
- toggle_interface,
270
- [source_tog],
271
- [im_box, vid_box],
272
- cancels=[go_btn_vid.click, go_btn_im.click, load_im_btn.click, vid_url_btn.click]
273
- )
274
-
275
- load_im_btn.click(lambda x: x, inp_url, inp_im)
276
- next_btn.click(process_vid, [inp_vid, hid_box, every_n], [html_out, stat_box, hid_box])
277
- vid_url_btn.click(dl, vid_url, [inp_vid, html_out, stat_box, hid_box])
278
- go_btn_vid.click(process_vid, [inp_vid, hid_box, every_n], [html_out, stat_box, hid_box])
279
- go_btn_im.click(rev_im, inp_im, html_out)
280
-
281
- return app
282
 
283
- if __name__ == "__main__":
284
- app = create_ui()
285
- app.queue(concurrency_count=20).launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  from PIL import Image
7
  import os
8
  import uuid
9
+ uid=uuid.uuid4()
10
+ size_js="""
11
+ function imgSize(){
12
+ var myImg = document.getElementsByClassName("my_im");
13
+ var realWidth = myImg.naturalWidth;
14
+ var realHeight = myImg.naturalHeight;
15
+ alert("Original width=" + realWidth + ", " + "Original height=" + realHeight);
16
+ }"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  def dl(inp):
19
+ out = None
20
+ out_file=[]
 
 
 
21
  try:
22
+ inp_out=inp.replace("https://","")
23
+ inp_out=inp_out.replace("/","_").replace(".","_").replace("=","_").replace("?","_")
24
+ if "twitter" in inp:
25
+ os.system(f'yt-dlp "{inp}" --extractor-arg "twitter:api=syndication" --trim-filenames 160 -o "{uid}/{inp_out}.mp4" -S res,mp4 --recode mp4')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  else:
27
+ os.system(f'yt-dlp "{inp}" --trim-filenames 160 -o "{uid}/{inp_out}.mp4" -S res,mp4 --recode mp4')
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ #os.system(f'yt-dlp "{inp}" --trim-filenames 160 -o "{inp_out}.mp4" -S res,mp4 --recode mp4')
30
+ out = f"{uid}/{inp_out}.mp4"
31
+ #out_ap = os.path.abspath(out_f)
32
+ #out = f'https://nymbo-reverse-image.hf.space/file={out_ap}'
33
+ print (out)
34
+ except Exception as e:
35
+ print (e)
36
+ #out = f'{e}'
37
+ return out,gr.HTML(""),"",""
38
+
39
+ def process_vid(file,cur_frame,every_n):
40
+ new_video_in = str(file)
41
+ capture = cv2.VideoCapture(new_video_in)
42
+ frame_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
43
+ rev_img_searcher = ReverseImageSearcher()
44
+ html_out=""
45
+ count = int(every_n)
46
+ if cur_frame == "" or cur_frame==None:
47
+ start_frame = 0
48
+ elif cur_frame != "" and cur_frame!=None:
49
+ start_frame = int(cur_frame)
50
  try:
 
 
 
 
 
 
 
 
 
 
 
51
  for i in range(start_frame, frame_count-1):
52
  if count == int(every_n):
53
  count = 1
54
+ print(i)
55
  capture.set(cv2.CAP_PROP_POS_FRAMES, i)
56
+ ret, frame_f = capture.read(i)
57
+ cv2.imwrite(f"{uid}-vid_tmp{i}.png", frame_f)
58
+ out = os.path.abspath(f"{uid}-vid_tmp{i}.png")
59
+ out_url = f'https://nymbo-reverse-image.hf.space/file={out}'
60
+ print(out)
61
+ res = rev_img_searcher.search(out_url)
62
+ #print (res)
63
+ out_cnt =0
64
+ if len(res) > 0:
65
+ #count = 0
66
+ for search_item in res:
67
+ print (f'counting {count}')
68
+ out_cnt+=1
69
+ out_dict={
70
+ 'Title': f'{search_item.page_title}',
71
+ 'Site': f'{search_item.page_url}',
72
+ 'Img': f'{search_item.image_url}',
73
+ }
74
+ print (dir(search_item))
75
+ html_out = f"""{html_out}
76
+ <div>
77
+ Title: {search_item.page_title}<br>
78
+ Site: <a href='{search_item.page_url}' target='_blank' rel='noopener noreferrer'>{search_item.page_url}</a><br>
79
+ Img: <a href='{search_item.image_url}' target='_blank' rel='noopener noreferrer'>{search_item.image_url}</a><br>
80
+ <img class='my_im' src='{search_item.image_url}'><br>
81
+ </div>"""
82
+ return (gr.HTML(f'<h1>Total Found: {out_cnt}</h1><br>{html_out}'), f"Found frame: {i}", i+int(every_n))
83
+ else:
84
+ pass
85
+ count +=1
86
+ print (i+1)
87
+ #return (None,f"Searching Frame: {i}", "")
88
  except Exception as e:
89
+ return (gr.HTML(f'{e}'),"","")
90
+ return (gr.HTML('No frame matches found.'),"","")
 
 
 
 
 
 
91
 
92
+ def process_im(file,url):
 
93
  if not url.startswith("https://nymbo"):
94
  return url
95
+ else:
 
 
 
96
  read_file = Image.open(file)
97
+ read_file.save(f"{uid}-tmp.png")
98
+ action_input = f"{uid}-tmp.png"
99
+ out = os.path.abspath(action_input)
100
+ out_url = f'https://nymbo-reverse-image.hf.space/file={out}'
101
+ return (out_url)
 
102
 
103
  def rev_im(image):
104
+ #image_url = 'https://i.pinimg.com/originals/c4/50/35/c450352ac6ea8645ead206721673e8fb.png'
105
+ out_list = []
106
+ out_im = []
107
+ html_out = """"""
108
+ image=cv2.imread(image)
109
+ cv2.imwrite(f"{uid}-im_tmp.png", image)
110
+ out = os.path.abspath(f"{uid}-im_tmp.png")
111
+ out_url = f'https://nymbo-reverse-image.hf.space/file={out}'
112
+ rev_img_searcher = ReverseImageSearcher()
113
+ res = rev_img_searcher.search(out_url)
114
+ count = 0
115
+ for search_item in res:
116
+ count+=1
117
+ out_dict={
118
+ 'Title': f'{search_item.page_title}',
119
+ 'Site': f'{search_item.page_url}',
120
+ 'Img': f'{search_item.image_url}',
121
+ }
122
+ print (dir(search_item))
123
+ html_out = f"""{html_out}
124
+ <div>
125
+ Title: {search_item.page_title}<br>
126
+ Site: <a href='{search_item.page_url}' target='_blank' rel='noopener noreferrer'>{search_item.page_url}</a><br>
127
+ Img: <a href='{search_item.image_url}' target='_blank' rel='noopener noreferrer'>{search_item.image_url}</a><br>
128
+ <img class='my_im' src='{search_item.image_url}'><br>
129
+ </div>"""
130
 
131
+ return (gr.HTML(f'<h1>Total Found: {count}</h1><br>{html_out}'))
 
 
 
 
 
132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
 
 
 
 
 
 
 
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
+ with gr.Blocks() as app:
138
+ with gr.Row():
139
+ gr.Column()
140
+ with gr.Column():
141
+
142
+ source_tog=gr.Radio(choices=["Image","Video"],value="Image")
143
+ with gr.Box(visible=True) as im_box:
144
+ inp_url=gr.Textbox(label="Image URL")
145
+ load_im_btn=gr.Button("Load Image")
146
+ inp_im=gr.Image(label="Search Image",type='filepath')
147
+ go_btn_im=gr.Button()
148
+ with gr.Box(visible=False) as vid_box:
149
+ vid_url=gr.Textbox(label="Video URL")
150
+ vid_url_btn=gr.Button("Load URL")
151
+ inp_vid=gr.Video(label="Search Video")
152
+ with gr.Row():
153
+ every_n=gr.Number(label = "Every /nth frame", value = 10)
154
+ stat_box=gr.Textbox(label="Status")
155
+ with gr.Row():
156
+ go_btn_vid=gr.Button("Start")
157
+ next_btn=gr.Button("Next")
158
+
159
+ gr.Column()
160
+ #paste_clip = gr.Button("Paste from Clipboard")
161
+ with gr.Row():
162
+ html_out = gr.HTML("""""")
163
+ with gr.Row(visible=False):
164
+ hid_box=gr.Textbox()
165
+ def shuf(tog):
166
+ if tog == "Image":
167
+ return gr.update(visible=True),gr.update(visible=False)
168
+ if tog == "Video":
169
+ return gr.update(visible=False),gr.update(visible=True)
170
+ def load_image(url):
171
+ return url
172
+ im_load = load_im_btn.click(load_image,inp_url,inp_im)
173
+ next_btn.click(process_vid,[inp_vid,hid_box,every_n],[html_out,stat_box,hid_box])
174
+ vid_load = vid_url_btn.click(dl,vid_url,[inp_vid,html_out,stat_box,hid_box])
175
+ #inp_im.change(process_im,[inp_im,inp_url],[inp_url])
176
+ vid_proc = go_btn_vid.click(process_vid,[inp_vid,hid_box,every_n],[html_out,stat_box,hid_box])
177
+ im_proc = go_btn_im.click(rev_im,inp_im,[html_out])
178
+ source_tog.change(shuf,[source_tog],[im_box,vid_box],cancels=[vid_proc,im_proc,im_load,vid_load])
179
+
180
+ #go_btn_url.click(rev_im,inp_url,[html_out])
181
+ app.queue(concurrency_count=20).launch()