nakas commited on
Commit
cf419e6
·
verified ·
1 Parent(s): e5b7cc2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +189 -598
app.py CHANGED
@@ -7,9 +7,8 @@ from PIL import Image
7
  import io
8
  import logging
9
  import traceback
 
10
  from pathlib import Path
11
- import threading
12
- import queue
13
 
14
  # Configure logging
15
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -18,10 +17,6 @@ logger = logging.getLogger(__name__)
18
  # Create screenshots directory if it doesn't exist
19
  os.makedirs('screenshots', exist_ok=True)
20
 
21
- # Global queues for real-time updates
22
- status_queue = queue.Queue()
23
- image_queue = queue.Queue()
24
-
25
  def download_playwright_browsers():
26
  """Make sure Playwright and its browsers are installed properly"""
27
  try:
@@ -84,76 +79,38 @@ def verify_playwright_installation():
84
  logger.warning("Playwright browser not found in expected locations")
85
  return False
86
 
87
- def update_ui(status_text, image_path=None):
88
- """Add updates to queue for real-time UI updates"""
89
- status_queue.put(status_text)
90
- if image_path:
91
- image_queue.put(image_path)
92
-
93
- def run_instagram_liker_thread(username, password, max_likes):
94
- """Thread function to run the Instagram auto-liker to allow real-time updates"""
95
- result = run_instagram_liker(username, password, max_likes)
96
- # Put final results in queue
97
- status_queue.put(None) # Signal completion
98
- image_queue.put(None) # Signal completion
99
- return result
100
-
101
  def run_instagram_liker(username, password, max_likes):
102
  """Run the Instagram auto-liker with Playwright"""
103
- all_status_updates = ["Starting Instagram Auto-Liker with Playwright..."]
104
- update_ui(all_status_updates[-1])
105
-
106
  image_path = save_placeholder_image("start")
107
- update_ui(None, image_path)
108
-
109
- # Track all screenshots for gallery view
110
- all_screenshots = [image_path]
111
 
112
  # Check if browsers are installed, if not install them
113
  try:
114
- status_text = "Verifying Playwright installation..."
115
- all_status_updates.append(status_text)
116
- update_ui(status_text)
117
 
118
  # First try to import
119
  try:
120
  from playwright.sync_api import sync_playwright
121
  except ImportError:
122
- status_text = "Playwright not installed. Installing now..."
123
- all_status_updates.append(status_text)
124
- update_ui(status_text)
125
-
126
  download_playwright_browsers()
127
-
128
- status_text = "Playwright installation completed."
129
- all_status_updates.append(status_text)
130
- update_ui(status_text)
131
 
132
  # Then verify browser binary exists
133
  if not verify_playwright_installation():
134
- status_text = "Browser binary not found. Installing browsers..."
135
- all_status_updates.append(status_text)
136
- update_ui(status_text)
137
-
138
  download_playwright_browsers()
139
 
140
  # Double check installation worked
141
  if not verify_playwright_installation():
142
- status_text = "Failed to install browser binary. Please try running the script again."
143
- all_status_updates.append(status_text)
144
- update_ui(status_text)
145
- return "\n".join(all_status_updates), image_path, all_screenshots
146
 
147
- status_text = "Playwright installation verified."
148
- all_status_updates.append(status_text)
149
- update_ui(status_text)
150
 
151
  from playwright.sync_api import sync_playwright
152
 
153
- status_text = "Launching Playwright browser..."
154
- all_status_updates.append(status_text)
155
- update_ui(status_text)
156
-
157
  with sync_playwright() as p:
158
  try:
159
  # Launch chromium with specific arguments to ensure it works in containerized environments
@@ -178,36 +135,21 @@ def run_instagram_liker(username, password, max_likes):
178
  page = context.new_page()
179
 
180
  # Test browser by visiting Google
181
- status_text = "Testing browser connection..."
182
- all_status_updates.append(status_text)
183
- update_ui(status_text)
184
-
185
  page.goto("https://www.google.com")
186
-
187
- status_text = f"Browser working. Title: {page.title()}"
188
- all_status_updates.append(status_text)
189
- update_ui(status_text)
190
 
191
  # Take a screenshot
192
  try:
193
  test_screenshot = f"screenshots/test_pw_{int(time.time())}.png"
194
  page.screenshot(path=test_screenshot)
195
  image_path = test_screenshot
196
- all_screenshots.append(image_path)
197
-
198
- status_text = "Browser screenshot saved"
199
- all_status_updates.append(status_text)
200
- update_ui(status_text, image_path)
201
  except Exception as e:
202
- status_text = f"Error taking screenshot: {str(e)}"
203
- all_status_updates.append(status_text)
204
- update_ui(status_text)
205
 
206
  # Navigate to Instagram
207
- status_text = "Navigating to Instagram..."
208
- all_status_updates.append(status_text)
209
- update_ui(status_text)
210
-
211
  page.goto("https://www.instagram.com/")
212
  page.wait_for_load_state('networkidle')
213
 
@@ -216,21 +158,12 @@ def run_instagram_liker(username, password, max_likes):
216
  landing_screenshot = f"screenshots/landing_pw_{int(time.time())}.png"
217
  page.screenshot(path=landing_screenshot)
218
  image_path = landing_screenshot
219
- all_screenshots.append(image_path)
220
-
221
- status_text = "Instagram page loaded, screenshot saved"
222
- all_status_updates.append(status_text)
223
- update_ui(status_text, image_path)
224
  except Exception as e:
225
- status_text = f"Error taking screenshot: {str(e)}"
226
- all_status_updates.append(status_text)
227
- update_ui(status_text)
228
 
229
  # Introduce delay to let the page fully load
230
- status_text = "Waiting for page to fully load..."
231
- all_status_updates.append(status_text)
232
- update_ui(status_text)
233
-
234
  page.wait_for_timeout(5000)
235
 
236
  # Take another screenshot after waiting
@@ -238,26 +171,16 @@ def run_instagram_liker(username, password, max_likes):
238
  loaded_screenshot = f"screenshots/fully_loaded_{int(time.time())}.png"
239
  page.screenshot(path=loaded_screenshot)
240
  image_path = loaded_screenshot
241
- all_screenshots.append(image_path)
242
-
243
- status_text = "Page fully loaded, screenshot saved"
244
- all_status_updates.append(status_text)
245
- update_ui(status_text, image_path)
246
  except Exception as e:
247
- status_text = f"Error taking screenshot: {str(e)}"
248
- all_status_updates.append(status_text)
249
- update_ui(status_text)
250
 
251
  # Try clicking outside any potential popups
252
  try:
253
  page.mouse.click(10, 10)
254
- status_text = "Clicked to dismiss any initial popups"
255
- all_status_updates.append(status_text)
256
- update_ui(status_text)
257
  except Exception as e:
258
- status_text = f"Click error: {str(e)}"
259
- all_status_updates.append(status_text)
260
- update_ui(status_text)
261
 
262
  # Handle cookie dialog if present
263
  try:
@@ -273,38 +196,26 @@ def run_instagram_liker(username, password, max_likes):
273
  try:
274
  if page.query_selector(button_selector):
275
  page.click(button_selector)
276
- status_text = f"Clicked cookie consent button: {button_selector}"
277
- all_status_updates.append(status_text)
278
- update_ui(status_text)
279
  page.wait_for_timeout(2000)
280
  break
281
  except Exception as e:
282
  logger.debug(f"Button {button_selector} not found: {str(e)}")
283
  continue
284
  except Exception as e:
285
- status_text = f"Cookie dialog handling: {str(e)}"
286
- all_status_updates.append(status_text)
287
- update_ui(status_text)
288
 
289
  # Look for the login form
290
- status_text = "Looking for login form..."
291
- all_status_updates.append(status_text)
292
- update_ui(status_text)
293
 
294
  # Take a screenshot to see what we're working with
295
  try:
296
  form_screenshot = f"screenshots/login_form_{int(time.time())}.png"
297
  page.screenshot(path=form_screenshot)
298
  image_path = form_screenshot
299
- all_screenshots.append(image_path)
300
-
301
- status_text = "Login form screenshot saved"
302
- all_status_updates.append(status_text)
303
- update_ui(status_text, image_path)
304
  except Exception as e:
305
- status_text = f"Error taking screenshot: {str(e)}"
306
- all_status_updates.append(status_text)
307
- update_ui(status_text)
308
 
309
  # Try multiple selectors for username input
310
  username_selectors = [
@@ -318,35 +229,23 @@ def run_instagram_liker(username, password, max_likes):
318
  username_field = None
319
  for selector in username_selectors:
320
  try:
321
- status_text = f"Trying to find username field with selector: {selector}"
322
- all_status_updates.append(status_text)
323
- update_ui(status_text)
324
-
325
  field = page.query_selector(selector)
326
  if field:
327
  username_field = field
328
-
329
- status_text = f"Found username field with selector: {selector}"
330
- all_status_updates.append(status_text)
331
- update_ui(status_text)
332
  break
333
  except Exception as e:
334
  logger.debug(f"Selector {selector} failed: {str(e)}")
335
 
336
  if not username_field:
337
- status_text = "Could not find username field. Instagram may have changed their interface."
338
- all_status_updates.append(status_text)
339
- update_ui(status_text)
340
-
341
  # Take final screenshot before closing
342
  final_screenshot = f"screenshots/final_error_{int(time.time())}.png"
343
  page.screenshot(path=final_screenshot)
344
  image_path = final_screenshot
345
- all_screenshots.append(image_path)
346
- update_ui(None, image_path)
347
-
348
  browser.close()
349
- return "\n".join(all_status_updates), image_path, all_screenshots
350
 
351
  # Try multiple selectors for password input
352
  password_selectors = [
@@ -359,62 +258,38 @@ def run_instagram_liker(username, password, max_likes):
359
  password_field = None
360
  for selector in password_selectors:
361
  try:
362
- status_text = f"Trying to find password field with selector: {selector}"
363
- all_status_updates.append(status_text)
364
- update_ui(status_text)
365
-
366
  field = page.query_selector(selector)
367
  if field:
368
  password_field = field
369
-
370
- status_text = f"Found password field with selector: {selector}"
371
- all_status_updates.append(status_text)
372
- update_ui(status_text)
373
  break
374
  except Exception as e:
375
  logger.debug(f"Selector {selector} failed: {str(e)}")
376
 
377
  if not password_field:
378
- status_text = "Could not find password field. Instagram may have changed their interface."
379
- all_status_updates.append(status_text)
380
- update_ui(status_text)
381
-
382
  # Take final screenshot before closing
383
  final_screenshot = f"screenshots/final_error_{int(time.time())}.png"
384
  page.screenshot(path=final_screenshot)
385
  image_path = final_screenshot
386
- all_screenshots.append(image_path)
387
- update_ui(None, image_path)
388
-
389
  browser.close()
390
- return "\n".join(all_status_updates), image_path, all_screenshots
391
 
392
  # Enter credentials
393
- status_text = f"Entering username: {username}"
394
- all_status_updates.append(status_text)
395
- update_ui(status_text)
396
-
397
  username_field.fill(username)
398
  password_field.fill(password)
399
-
400
- status_text = "Credentials entered"
401
- all_status_updates.append(status_text)
402
- update_ui(status_text)
403
 
404
  # Take a screenshot of filled form
405
  try:
406
  creds_screenshot = f"screenshots/credentials_pw_{int(time.time())}.png"
407
  page.screenshot(path=creds_screenshot)
408
  image_path = creds_screenshot
409
- all_screenshots.append(image_path)
410
-
411
- status_text = "Credentials screenshot saved"
412
- all_status_updates.append(status_text)
413
- update_ui(status_text, image_path)
414
  except Exception as e:
415
- status_text = f"Error taking screenshot: {str(e)}"
416
- all_status_updates.append(status_text)
417
- update_ui(status_text)
418
 
419
  # Find login button
420
  login_button_selectors = [
@@ -428,48 +303,30 @@ def run_instagram_liker(username, password, max_likes):
428
  login_button = None
429
  for selector in login_button_selectors:
430
  try:
431
- status_text = f"Trying to find login button with selector: {selector}"
432
- all_status_updates.append(status_text)
433
- update_ui(status_text)
434
-
435
  button = page.query_selector(selector)
436
  if button:
437
  login_button = button
438
-
439
- status_text = f"Found login button with selector: {selector}"
440
- all_status_updates.append(status_text)
441
- update_ui(status_text)
442
  break
443
  except Exception as e:
444
  logger.debug(f"Selector {selector} failed: {str(e)}")
445
 
446
  if not login_button:
447
- status_text = "Could not find login button. Instagram may have changed their interface."
448
- all_status_updates.append(status_text)
449
- update_ui(status_text)
450
-
451
  # Take final screenshot before closing
452
  final_screenshot = f"screenshots/final_error_{int(time.time())}.png"
453
  page.screenshot(path=final_screenshot)
454
  image_path = final_screenshot
455
- all_screenshots.append(image_path)
456
- update_ui(None, image_path)
457
-
458
  browser.close()
459
- return "\n".join(all_status_updates), image_path, all_screenshots
460
 
461
  # Click login button
462
- status_text = "Clicking login button..."
463
- all_status_updates.append(status_text)
464
- update_ui(status_text)
465
-
466
  login_button.click()
467
 
468
  # Wait for navigation to complete
469
- status_text = "Waiting for login process..."
470
- all_status_updates.append(status_text)
471
- update_ui(status_text)
472
-
473
  page.wait_for_timeout(5000)
474
 
475
  # Take post-login screenshot
@@ -477,15 +334,9 @@ def run_instagram_liker(username, password, max_likes):
477
  post_login_screenshot = f"screenshots/post_login_pw_{int(time.time())}.png"
478
  page.screenshot(path=post_login_screenshot)
479
  image_path = post_login_screenshot
480
- all_screenshots.append(image_path)
481
-
482
- status_text = "Post-login screenshot saved"
483
- all_status_updates.append(status_text)
484
- update_ui(status_text, image_path)
485
  except Exception as e:
486
- status_text = f"Error taking screenshot: {str(e)}"
487
- all_status_updates.append(status_text)
488
- update_ui(status_text)
489
 
490
  # Check if login was successful
491
  current_url = page.url
@@ -510,20 +361,14 @@ def run_instagram_liker(username, password, max_likes):
510
  pass
511
 
512
  if error_message:
513
- status_text = f"Login failed: {error_message}"
514
- all_status_updates.append(status_text)
515
- update_ui(status_text)
516
  else:
517
- status_text = "Login failed: Reason unknown. Check your credentials."
518
- all_status_updates.append(status_text)
519
- update_ui(status_text)
520
 
521
  browser.close()
522
- return "\n".join(all_status_updates), image_path, all_screenshots
523
 
524
- status_text = "Login successful! Now handling post-login dialogs..."
525
- all_status_updates.append(status_text)
526
- update_ui(status_text)
527
 
528
  # Handle "Save Login Info" popup if it appears
529
  try:
@@ -546,20 +391,10 @@ def run_instagram_liker(username, password, max_likes):
546
  for selector in save_info_selectors:
547
  if page.query_selector(selector):
548
  dialog_found = True
549
-
550
- status_text = f"Save Login Info dialog found with: {selector}"
551
- all_status_updates.append(status_text)
552
- update_ui(status_text)
553
  break
554
 
555
  if dialog_found:
556
- # Take screenshot of dialog
557
- dialog_screenshot = f"screenshots/save_info_dialog_{int(time.time())}.png"
558
- page.screenshot(path=dialog_screenshot)
559
- image_path = dialog_screenshot
560
- all_screenshots.append(image_path)
561
- update_ui(None, image_path)
562
-
563
  # Try to click "Not Now"
564
  dismissed = False
565
  for not_now in not_now_selectors:
@@ -567,11 +402,7 @@ def run_instagram_liker(username, password, max_likes):
567
  button = page.query_selector(not_now)
568
  if button:
569
  button.click()
570
-
571
- status_text = f"Dismissed 'Save Login Info' popup using: {not_now}"
572
- all_status_updates.append(status_text)
573
- update_ui(status_text)
574
-
575
  page.wait_for_timeout(2000)
576
  dismissed = True
577
  break
@@ -580,32 +411,20 @@ def run_instagram_liker(username, password, max_likes):
580
  continue
581
 
582
  if not dismissed:
583
- status_text = "Found Save Login dialog but couldn't dismiss it"
584
- all_status_updates.append(status_text)
585
- update_ui(status_text)
586
  else:
587
- status_text = "No 'Save Login Info' popup detected"
588
- all_status_updates.append(status_text)
589
- update_ui(status_text)
590
  except Exception as e:
591
- status_text = f"Error handling Save Login dialog: {str(e)}"
592
- all_status_updates.append(status_text)
593
- update_ui(status_text)
594
 
595
  # Take a screenshot after handling first dialog
596
  try:
597
  after_save_screenshot = f"screenshots/after_save_dialog_{int(time.time())}.png"
598
  page.screenshot(path=after_save_screenshot)
599
  image_path = after_save_screenshot
600
- all_screenshots.append(image_path)
601
-
602
- status_text = "Screenshot after Save Info dialog"
603
- all_status_updates.append(status_text)
604
- update_ui(status_text, image_path)
605
  except Exception as e:
606
- status_text = f"Error taking screenshot: {str(e)}"
607
- all_status_updates.append(status_text)
608
- update_ui(status_text)
609
 
610
  # Handle notifications popup if it appears
611
  try:
@@ -628,20 +447,10 @@ def run_instagram_liker(username, password, max_likes):
628
  for selector in notifications_selectors:
629
  if page.query_selector(selector):
630
  dialog_found = True
631
-
632
- status_text = f"Notifications dialog found with: {selector}"
633
- all_status_updates.append(status_text)
634
- update_ui(status_text)
635
  break
636
 
637
  if dialog_found:
638
- # Take screenshot of dialog
639
- notif_screenshot = f"screenshots/notifications_dialog_{int(time.time())}.png"
640
- page.screenshot(path=notif_screenshot)
641
- image_path = notif_screenshot
642
- all_screenshots.append(image_path)
643
- update_ui(None, image_path)
644
-
645
  # Try to click "Not Now"
646
  dismissed = False
647
  for not_now in not_now_selectors:
@@ -649,11 +458,7 @@ def run_instagram_liker(username, password, max_likes):
649
  button = page.query_selector(not_now)
650
  if button:
651
  button.click()
652
-
653
- status_text = f"Dismissed notifications popup using: {not_now}"
654
- all_status_updates.append(status_text)
655
- update_ui(status_text)
656
-
657
  page.wait_for_timeout(2000)
658
  dismissed = True
659
  break
@@ -662,47 +467,34 @@ def run_instagram_liker(username, password, max_likes):
662
  continue
663
 
664
  if not dismissed:
665
- status_text = "Found Notifications dialog but couldn't dismiss it"
666
- all_status_updates.append(status_text)
667
- update_ui(status_text)
668
  else:
669
- status_text = "No notifications popup detected"
670
- all_status_updates.append(status_text)
671
- update_ui(status_text)
672
  except Exception as e:
673
- status_text = f"Error handling Notifications dialog: {str(e)}"
674
- all_status_updates.append(status_text)
675
- update_ui(status_text)
676
 
677
  # Take feed screenshot
678
  try:
679
  feed_screenshot = f"screenshots/feed_pw_{int(time.time())}.png"
680
  page.screenshot(path=feed_screenshot)
681
  image_path = feed_screenshot
682
- all_screenshots.append(image_path)
683
-
684
- status_text = "Feed screenshot saved"
685
- all_status_updates.append(status_text)
686
- update_ui(status_text, image_path)
687
  except Exception as e:
688
- status_text = f"Error taking screenshot: {str(e)}"
689
- all_status_updates.append(status_text)
690
- update_ui(status_text)
691
 
692
- status_text = "Successfully navigated to Instagram feed!"
693
- all_status_updates.append(status_text)
694
- update_ui(status_text)
695
 
696
  # Start liking posts
697
- status_text = f"Starting to like posts (target: {max_likes})..."
698
- all_status_updates.append(status_text)
699
- update_ui(status_text)
700
 
701
  # Like posts
702
  likes_count = 0
703
  scroll_count = 0
704
  max_scrolls = 30
705
 
 
 
 
706
  # Try to find posts with multiple selectors
707
  article_selectors = [
708
  "article",
@@ -717,299 +509,171 @@ def run_instagram_liker(username, password, max_likes):
717
  try:
718
  if page.query_selector(selector):
719
  article_found = True
720
-
721
- status_text = f"Found posts using selector: {selector}"
722
- all_status_updates.append(status_text)
723
- update_ui(status_text)
724
  break
725
  except Exception as e:
726
  logger.debug(f"Article selector {selector} failed: {str(e)}")
727
 
728
  if not article_found:
729
- status_text = "Could not find posts on feed. Instagram may have changed their interface."
730
- all_status_updates.append(status_text)
731
- update_ui(status_text)
732
-
733
  browser.close()
734
- return "\n".join(all_status_updates), image_path, all_screenshots
735
 
736
- # ============= FIXED LIKE BUTTON INTERACTION =============
737
  while likes_count < max_likes and scroll_count < max_scrolls:
738
- # Take screenshot of current view
739
  try:
740
- current_view = f"screenshots/scroll_{scroll_count}_{int(time.time())}.png"
741
- page.screenshot(path=current_view)
742
- image_path = current_view
743
- all_screenshots.append(image_path)
744
- update_ui(None, image_path)
745
- except Exception as e:
746
- status_text = f"Error taking screenshot: {str(e)}"
747
- all_status_updates.append(status_text)
748
- update_ui(status_text)
749
-
750
- # Try different selectors for like buttons
751
- like_button_selectors = [
752
- "article svg[aria-label='Like']", # Main selector
753
- "section svg[aria-label='Like']",
754
- "span[role='button'] svg[aria-label='Like']",
755
- "div[role='button'] svg[aria-label='Like']"
756
- ]
757
-
758
- # Get all like buttons in the current view
759
- all_like_buttons = []
760
- used_selector = ""
761
-
762
- for selector in like_button_selectors:
763
- try:
764
- buttons = page.query_selector_all(selector)
765
- if buttons and len(buttons) > 0:
766
- all_like_buttons = buttons
767
- used_selector = selector
768
-
769
- status_text = f"Found {len(buttons)} like buttons with selector: {selector}"
770
- all_status_updates.append(status_text)
771
- update_ui(status_text)
772
- break
773
- except Exception as e:
774
- logger.debug(f"Like button selector {selector} failed: {str(e)}")
775
-
776
- status_text = f"Found {len(all_like_buttons)} like buttons on scroll {scroll_count}"
777
- all_status_updates.append(status_text)
778
- update_ui(status_text)
779
-
780
- if len(all_like_buttons) == 0 and scroll_count > 5:
781
- status_text = "No more like buttons found. Stopping."
782
- all_status_updates.append(status_text)
783
- update_ui(status_text)
784
- break
785
-
786
- # Process each like button - NEW IMPROVED METHOD
787
- for i, button in enumerate(all_like_buttons):
788
- if likes_count >= max_likes:
789
- break
790
 
791
- try:
792
- # FIRST - Take a screenshot BEFORE interacting
793
- pre_like_screenshot = f"screenshots/pre_like_{likes_count+1}_{int(time.time())}.png"
794
- page.screenshot(path=pre_like_screenshot)
795
- all_screenshots.append(pre_like_screenshot)
796
-
797
- status_text = f"Pre-like screenshot for post {i+1}"
798
- all_status_updates.append(status_text)
799
- update_ui(status_text, pre_like_screenshot)
 
 
 
 
 
800
 
801
- # IMPORTANT: Use evaluate to find the closest article
802
- # This helps ensure we're working with the stable post container
803
- article = button.evaluate("""(node) => {
804
- // Find parent article or post container
805
- let current = node;
806
- while (current && current.tagName !== 'ARTICLE' &&
807
- !current.getAttribute('role')?.includes('presentation')) {
808
- current = current.parentElement;
809
- // Safety check - don't go too far up
810
- if (current.tagName === 'BODY') return null;
 
 
 
 
 
 
 
 
 
 
 
811
  }
812
- return current;
813
- }""")
814
 
815
- if not article:
816
- status_text = f"Couldn't find parent article for button {i+1}, skipping"
817
- all_status_updates.append(status_text)
818
- update_ui(status_text)
819
- continue
820
-
821
- # Scroll the article into view - critical for stability
822
- page.evaluate("""(article) => {
823
- article.scrollIntoView({
824
- behavior: 'smooth',
825
- block: 'center',
826
- inline: 'center'
827
- });
828
- }""", article)
829
-
830
- # Wait for scroll to complete
831
- page.wait_for_timeout(1000)
832
-
833
- # FIND THE LIKE BUTTON AGAIN after scrolling - very important
834
- # This ensures we're working with an attached element
835
- like_button = article.query_selector(used_selector)
836
 
837
- if not like_button:
838
- status_text = f"Like button disappeared after scrolling for post {i+1}, trying alternatives"
839
- all_status_updates.append(status_text)
840
- update_ui(status_text)
 
 
 
 
841
 
842
- # Try alternative selectors within this specific article
843
- for alt_selector in like_button_selectors:
844
- like_button = article.query_selector(alt_selector)
845
- if like_button:
846
- status_text = f"Found alternative like button with {alt_selector}"
847
- all_status_updates.append(status_text)
848
- update_ui(status_text)
849
- break
850
-
851
- if not like_button:
852
- status_text = f"Could not find like button for post {i+1} after scrolling, skipping"
853
- all_status_updates.append(status_text)
854
- update_ui(status_text)
855
- continue
856
-
857
- # Take one more screenshot right before clicking
858
- ready_screenshot = f"screenshots/ready_to_like_{likes_count+1}_{int(time.time())}.png"
859
- page.screenshot(path=ready_screenshot)
860
- all_screenshots.append(ready_screenshot)
861
-
862
- # IMPORTANT: Find the actual clickable button element
863
- # SVG itself is not clickable, need to find parent button
864
- clickable = like_button.evaluate("""(node) => {
865
- // If already a button, use it
866
- if (node.tagName === 'BUTTON') return node;
867
 
868
- // Otherwise find closest button parent
869
- let current = node;
870
- while (current && current.tagName !== 'BUTTON') {
871
- current = current.parentElement;
872
- // Safety check
873
- if (!current || current.tagName === 'BODY') return null;
874
- }
875
- return current;
876
- }""")
877
-
878
- if not clickable:
879
- status_text = f"Couldn't find clickable button for post {i+1}, trying direct click"
880
- all_status_updates.append(status_text)
881
- update_ui(status_text)
882
 
883
- # Try clicking the SVG directly as fallback
884
- like_button.click(force=True)
885
- else:
886
- # Click the proper button
887
- clickable.click()
888
-
889
- # Wait for the like action to register
890
- page.wait_for_timeout(1000)
891
-
892
- # Take screenshot AFTER clicking like
893
- post_like_screenshot = f"screenshots/liked_{likes_count+1}_{int(time.time())}.png"
894
- page.screenshot(path=post_like_screenshot)
895
- all_screenshots.append(post_like_screenshot)
896
-
897
- likes_count += 1
898
- status_text = f"Liked post {likes_count}/{max_likes} ✓"
899
- all_status_updates.append(status_text)
900
- update_ui(status_text, post_like_screenshot)
901
-
902
- # Randomize delay between likes to appear more natural
903
- wait_time = 2000 + (likes_count % 5) * 500 # Between 2-4.5 seconds
904
- page.wait_for_timeout(wait_time)
905
-
906
- except Exception as e:
907
- status_text = f"Error liking post {i+1}: {str(e)}"
908
- all_status_updates.append(status_text)
909
- update_ui(status_text)
910
-
911
- # Take error screenshot
912
- try:
913
- error_screenshot = f"screenshots/error_like_{int(time.time())}.png"
914
- page.screenshot(path=error_screenshot)
915
- all_screenshots.append(error_screenshot)
916
- update_ui(None, error_screenshot)
917
- except:
918
- pass
919
-
920
- continue
921
-
922
- # If we've reached our target, break out
923
- if likes_count >= max_likes:
924
- break
925
-
926
- # If we haven't found any like buttons or couldn't like any posts, scroll down
927
- status_text = "Scrolling down to load more posts..."
928
- all_status_updates.append(status_text)
929
- update_ui(status_text)
930
-
931
- # IMPROVED SCROLLING: Find the last post and scroll to it
932
- try:
933
- page.evaluate("""() => {
934
- // Scroll smoothly to improve content loading
935
- window.scrollBy({
936
- top: 800,
937
- behavior: 'smooth'
938
- });
939
- }""")
940
 
941
- # Wait longer for content to load
942
- page.wait_for_timeout(3000)
943
 
944
- status_text = f"Scrolled down to load more posts (scroll {scroll_count + 1})"
945
- all_status_updates.append(status_text)
946
- update_ui(status_text)
947
  except Exception as e:
948
- status_text = f"Error scrolling: {str(e)}"
949
- all_status_updates.append(status_text)
950
- update_ui(status_text)
951
-
952
- scroll_count += 1
 
 
 
 
 
 
953
 
954
- # Final status message
955
  final_message = f"Finished! Liked {likes_count} posts."
956
- all_status_updates.append(final_message)
957
- update_ui(final_message)
958
 
959
  # Final screenshot
960
  try:
961
  final_screenshot = f"screenshots/final_{int(time.time())}.png"
962
  page.screenshot(path=final_screenshot)
963
  image_path = final_screenshot
964
- all_screenshots.append(image_path)
965
-
966
- status_text = "Final screenshot saved"
967
- all_status_updates.append(status_text)
968
- update_ui(status_text, image_path)
969
  except Exception as e:
970
- status_text = f"Error taking final screenshot: {str(e)}"
971
- all_status_updates.append(status_text)
972
- update_ui(status_text)
973
 
974
  # Close the browser
975
  browser.close()
976
-
977
- status_text = "Browser closed"
978
- all_status_updates.append(status_text)
979
- update_ui(status_text)
980
 
981
  except Exception as e:
982
- status_text = f"Error during browser interaction: {str(e)}"
983
- all_status_updates.append(status_text)
984
- update_ui(status_text)
985
-
986
  traceback_msg = traceback.format_exc()
987
  logger.error(traceback_msg)
988
-
989
- status_text = "See logs for detailed error information"
990
- all_status_updates.append(status_text)
991
- update_ui(status_text)
992
 
993
  # Try to close browser in case of error
994
  try:
995
  browser.close()
996
- status_text = "Browser closed after error"
997
- all_status_updates.append(status_text)
998
- update_ui(status_text)
999
  except:
1000
  pass
1001
 
1002
  except Exception as e:
1003
  error_message = f"Error with Playwright: {str(e)}"
1004
  logger.error(error_message)
1005
- all_status_updates.append(error_message)
1006
- update_ui(error_message)
1007
-
1008
  logger.error(traceback.format_exc())
1009
 
1010
- return "\n".join(all_status_updates), image_path, all_screenshots
1011
 
1012
- # Gradio Interface with real-time updates
1013
  def create_interface():
1014
  with gr.Blocks(title="Instagram Auto-Liker") as app:
1015
  gr.Markdown("# Instagram Auto-Liker (Playwright)")
@@ -1029,85 +693,12 @@ def create_interface():
1029
 
1030
  with gr.Column(scale=2):
1031
  status_output = gr.Textbox(label="Status Log", lines=15)
1032
-
1033
- # Gallery view of all screenshots
1034
- with gr.Row():
1035
- # Main current image
1036
- image_output = gr.Image(label="Current Screenshot", type="filepath")
1037
-
1038
- # Add gallery for all screenshots
1039
- gallery = gr.Gallery(label="All Screenshots", show_label=True, elem_id="screenshot_gallery")
1040
-
1041
- # Function to handle real-time updates
1042
- def process_updates(username, password, max_likes):
1043
- # Clear status queue
1044
- while not status_queue.empty():
1045
- try:
1046
- status_queue.get_nowait()
1047
- except:
1048
- pass
1049
-
1050
- # Clear image queue
1051
- while not image_queue.empty():
1052
- try:
1053
- image_queue.get_nowait()
1054
- except:
1055
- pass
1056
-
1057
- # Start worker thread
1058
- thread = threading.Thread(
1059
- target=run_instagram_liker_thread,
1060
- args=(username, password, max_likes)
1061
- )
1062
- thread.daemon = True
1063
- thread.start()
1064
-
1065
- # Initial values
1066
- status_text = "Starting Instagram Auto-Liker..."
1067
- image_path = None
1068
- all_images = []
1069
-
1070
- # Function to update UI in real-time
1071
- def get_updates():
1072
- nonlocal status_text, image_path, all_images
1073
-
1074
- # Check for new status updates
1075
- try:
1076
- while not status_queue.empty():
1077
- new_status = status_queue.get_nowait()
1078
- if new_status is None: # End signal
1079
- return None, image_path, all_images
1080
- status_text += "\n" + new_status
1081
- except:
1082
- pass
1083
-
1084
- # Check for new images
1085
- try:
1086
- while not image_queue.empty():
1087
- new_image = image_queue.get_nowait()
1088
- if new_image is None: # End signal
1089
- return status_text, image_path, all_images
1090
- image_path = new_image
1091
- if new_image not in all_images:
1092
- all_images.append(new_image)
1093
- except:
1094
- pass
1095
-
1096
- return status_text, image_path, all_images
1097
-
1098
- return get_updates
1099
 
1100
- # Connect the button to start processing
1101
  submit_btn.click(
1102
- fn=process_updates,
1103
  inputs=[username, password, max_likes],
1104
- outputs=[status_output, image_output, gallery],
1105
- queue=False,
1106
- ).then(
1107
- lambda: gr.Textbox(interactive=False),
1108
- None,
1109
- [status_output],
1110
- queue=False,
1111
  )
1112
 
1113
  return app
 
7
  import io
8
  import logging
9
  import traceback
10
+ import random
11
  from pathlib import Path
 
 
12
 
13
  # Configure logging
14
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
17
  # Create screenshots directory if it doesn't exist
18
  os.makedirs('screenshots', exist_ok=True)
19
 
 
 
 
 
20
  def download_playwright_browsers():
21
  """Make sure Playwright and its browsers are installed properly"""
22
  try:
 
79
  logger.warning("Playwright browser not found in expected locations")
80
  return False
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  def run_instagram_liker(username, password, max_likes):
83
  """Run the Instagram auto-liker with Playwright"""
84
+ status_updates = ["Starting Instagram Auto-Liker with Playwright..."]
 
 
85
  image_path = save_placeholder_image("start")
 
 
 
 
86
 
87
  # Check if browsers are installed, if not install them
88
  try:
89
+ status_updates.append("Verifying Playwright installation...")
 
 
90
 
91
  # First try to import
92
  try:
93
  from playwright.sync_api import sync_playwright
94
  except ImportError:
95
+ status_updates.append("Playwright not installed. Installing now...")
 
 
 
96
  download_playwright_browsers()
97
+ status_updates.append("Playwright installation completed.")
 
 
 
98
 
99
  # Then verify browser binary exists
100
  if not verify_playwright_installation():
101
+ status_updates.append("Browser binary not found. Installing browsers...")
 
 
 
102
  download_playwright_browsers()
103
 
104
  # Double check installation worked
105
  if not verify_playwright_installation():
106
+ status_updates.append("Failed to install browser binary. Please try running the script again.")
107
+ return "\n".join(status_updates), image_path
 
 
108
 
109
+ status_updates.append("Playwright installation verified.")
 
 
110
 
111
  from playwright.sync_api import sync_playwright
112
 
113
+ status_updates.append("Launching Playwright browser...")
 
 
 
114
  with sync_playwright() as p:
115
  try:
116
  # Launch chromium with specific arguments to ensure it works in containerized environments
 
135
  page = context.new_page()
136
 
137
  # Test browser by visiting Google
138
+ status_updates.append("Testing browser connection...")
 
 
 
139
  page.goto("https://www.google.com")
140
+ status_updates.append(f"Browser working. Title: {page.title()}")
 
 
 
141
 
142
  # Take a screenshot
143
  try:
144
  test_screenshot = f"screenshots/test_pw_{int(time.time())}.png"
145
  page.screenshot(path=test_screenshot)
146
  image_path = test_screenshot
147
+ status_updates.append("Browser screenshot saved")
 
 
 
 
148
  except Exception as e:
149
+ status_updates.append(f"Error taking screenshot: {str(e)}")
 
 
150
 
151
  # Navigate to Instagram
152
+ status_updates.append("Navigating to Instagram...")
 
 
 
153
  page.goto("https://www.instagram.com/")
154
  page.wait_for_load_state('networkidle')
155
 
 
158
  landing_screenshot = f"screenshots/landing_pw_{int(time.time())}.png"
159
  page.screenshot(path=landing_screenshot)
160
  image_path = landing_screenshot
161
+ status_updates.append("Instagram page loaded, screenshot saved")
 
 
 
 
162
  except Exception as e:
163
+ status_updates.append(f"Error taking screenshot: {str(e)}")
 
 
164
 
165
  # Introduce delay to let the page fully load
166
+ status_updates.append("Waiting for page to fully load...")
 
 
 
167
  page.wait_for_timeout(5000)
168
 
169
  # Take another screenshot after waiting
 
171
  loaded_screenshot = f"screenshots/fully_loaded_{int(time.time())}.png"
172
  page.screenshot(path=loaded_screenshot)
173
  image_path = loaded_screenshot
174
+ status_updates.append("Page fully loaded, screenshot saved")
 
 
 
 
175
  except Exception as e:
176
+ status_updates.append(f"Error taking screenshot: {str(e)}")
 
 
177
 
178
  # Try clicking outside any potential popups
179
  try:
180
  page.mouse.click(10, 10)
181
+ status_updates.append("Clicked to dismiss any initial popups")
 
 
182
  except Exception as e:
183
+ status_updates.append(f"Click error: {str(e)}")
 
 
184
 
185
  # Handle cookie dialog if present
186
  try:
 
196
  try:
197
  if page.query_selector(button_selector):
198
  page.click(button_selector)
199
+ status_updates.append(f"Clicked cookie consent button: {button_selector}")
 
 
200
  page.wait_for_timeout(2000)
201
  break
202
  except Exception as e:
203
  logger.debug(f"Button {button_selector} not found: {str(e)}")
204
  continue
205
  except Exception as e:
206
+ status_updates.append(f"Cookie dialog handling: {str(e)}")
 
 
207
 
208
  # Look for the login form
209
+ status_updates.append("Looking for login form...")
 
 
210
 
211
  # Take a screenshot to see what we're working with
212
  try:
213
  form_screenshot = f"screenshots/login_form_{int(time.time())}.png"
214
  page.screenshot(path=form_screenshot)
215
  image_path = form_screenshot
216
+ status_updates.append("Login form screenshot saved")
 
 
 
 
217
  except Exception as e:
218
+ status_updates.append(f"Error taking screenshot: {str(e)}")
 
 
219
 
220
  # Try multiple selectors for username input
221
  username_selectors = [
 
229
  username_field = None
230
  for selector in username_selectors:
231
  try:
232
+ status_updates.append(f"Trying to find username field with selector: {selector}")
 
 
 
233
  field = page.query_selector(selector)
234
  if field:
235
  username_field = field
236
+ status_updates.append(f"Found username field with selector: {selector}")
 
 
 
237
  break
238
  except Exception as e:
239
  logger.debug(f"Selector {selector} failed: {str(e)}")
240
 
241
  if not username_field:
242
+ status_updates.append("Could not find username field. Instagram may have changed their interface.")
 
 
 
243
  # Take final screenshot before closing
244
  final_screenshot = f"screenshots/final_error_{int(time.time())}.png"
245
  page.screenshot(path=final_screenshot)
246
  image_path = final_screenshot
 
 
 
247
  browser.close()
248
+ return "\n".join(status_updates), image_path
249
 
250
  # Try multiple selectors for password input
251
  password_selectors = [
 
258
  password_field = None
259
  for selector in password_selectors:
260
  try:
261
+ status_updates.append(f"Trying to find password field with selector: {selector}")
 
 
 
262
  field = page.query_selector(selector)
263
  if field:
264
  password_field = field
265
+ status_updates.append(f"Found password field with selector: {selector}")
 
 
 
266
  break
267
  except Exception as e:
268
  logger.debug(f"Selector {selector} failed: {str(e)}")
269
 
270
  if not password_field:
271
+ status_updates.append("Could not find password field. Instagram may have changed their interface.")
 
 
 
272
  # Take final screenshot before closing
273
  final_screenshot = f"screenshots/final_error_{int(time.time())}.png"
274
  page.screenshot(path=final_screenshot)
275
  image_path = final_screenshot
 
 
 
276
  browser.close()
277
+ return "\n".join(status_updates), image_path
278
 
279
  # Enter credentials
280
+ status_updates.append(f"Entering username: {username}")
 
 
 
281
  username_field.fill(username)
282
  password_field.fill(password)
283
+ status_updates.append("Credentials entered")
 
 
 
284
 
285
  # Take a screenshot of filled form
286
  try:
287
  creds_screenshot = f"screenshots/credentials_pw_{int(time.time())}.png"
288
  page.screenshot(path=creds_screenshot)
289
  image_path = creds_screenshot
290
+ status_updates.append("Credentials screenshot saved")
 
 
 
 
291
  except Exception as e:
292
+ status_updates.append(f"Error taking screenshot: {str(e)}")
 
 
293
 
294
  # Find login button
295
  login_button_selectors = [
 
303
  login_button = None
304
  for selector in login_button_selectors:
305
  try:
306
+ status_updates.append(f"Trying to find login button with selector: {selector}")
 
 
 
307
  button = page.query_selector(selector)
308
  if button:
309
  login_button = button
310
+ status_updates.append(f"Found login button with selector: {selector}")
 
 
 
311
  break
312
  except Exception as e:
313
  logger.debug(f"Selector {selector} failed: {str(e)}")
314
 
315
  if not login_button:
316
+ status_updates.append("Could not find login button. Instagram may have changed their interface.")
 
 
 
317
  # Take final screenshot before closing
318
  final_screenshot = f"screenshots/final_error_{int(time.time())}.png"
319
  page.screenshot(path=final_screenshot)
320
  image_path = final_screenshot
 
 
 
321
  browser.close()
322
+ return "\n".join(status_updates), image_path
323
 
324
  # Click login button
325
+ status_updates.append("Clicking login button...")
 
 
 
326
  login_button.click()
327
 
328
  # Wait for navigation to complete
329
+ status_updates.append("Waiting for login process...")
 
 
 
330
  page.wait_for_timeout(5000)
331
 
332
  # Take post-login screenshot
 
334
  post_login_screenshot = f"screenshots/post_login_pw_{int(time.time())}.png"
335
  page.screenshot(path=post_login_screenshot)
336
  image_path = post_login_screenshot
337
+ status_updates.append("Post-login screenshot saved")
 
 
 
 
338
  except Exception as e:
339
+ status_updates.append(f"Error taking screenshot: {str(e)}")
 
 
340
 
341
  # Check if login was successful
342
  current_url = page.url
 
361
  pass
362
 
363
  if error_message:
364
+ status_updates.append(f"Login failed: {error_message}")
 
 
365
  else:
366
+ status_updates.append("Login failed: Reason unknown. Check your credentials.")
 
 
367
 
368
  browser.close()
369
+ return "\n".join(status_updates), image_path
370
 
371
+ status_updates.append("Login successful! Now handling post-login dialogs...")
 
 
372
 
373
  # Handle "Save Login Info" popup if it appears
374
  try:
 
391
  for selector in save_info_selectors:
392
  if page.query_selector(selector):
393
  dialog_found = True
394
+ status_updates.append(f"Save Login Info dialog found with: {selector}")
 
 
 
395
  break
396
 
397
  if dialog_found:
 
 
 
 
 
 
 
398
  # Try to click "Not Now"
399
  dismissed = False
400
  for not_now in not_now_selectors:
 
402
  button = page.query_selector(not_now)
403
  if button:
404
  button.click()
405
+ status_updates.append(f"Dismissed 'Save Login Info' popup using: {not_now}")
 
 
 
 
406
  page.wait_for_timeout(2000)
407
  dismissed = True
408
  break
 
411
  continue
412
 
413
  if not dismissed:
414
+ status_updates.append("Found Save Login dialog but couldn't dismiss it")
 
 
415
  else:
416
+ status_updates.append("No 'Save Login Info' popup detected")
 
 
417
  except Exception as e:
418
+ status_updates.append(f"Error handling Save Login dialog: {str(e)}")
 
 
419
 
420
  # Take a screenshot after handling first dialog
421
  try:
422
  after_save_screenshot = f"screenshots/after_save_dialog_{int(time.time())}.png"
423
  page.screenshot(path=after_save_screenshot)
424
  image_path = after_save_screenshot
425
+ status_updates.append("Screenshot after Save Info dialog")
 
 
 
 
426
  except Exception as e:
427
+ status_updates.append(f"Error taking screenshot: {str(e)}")
 
 
428
 
429
  # Handle notifications popup if it appears
430
  try:
 
447
  for selector in notifications_selectors:
448
  if page.query_selector(selector):
449
  dialog_found = True
450
+ status_updates.append(f"Notifications dialog found with: {selector}")
 
 
 
451
  break
452
 
453
  if dialog_found:
 
 
 
 
 
 
 
454
  # Try to click "Not Now"
455
  dismissed = False
456
  for not_now in not_now_selectors:
 
458
  button = page.query_selector(not_now)
459
  if button:
460
  button.click()
461
+ status_updates.append(f"Dismissed notifications popup using: {not_now}")
 
 
 
 
462
  page.wait_for_timeout(2000)
463
  dismissed = True
464
  break
 
467
  continue
468
 
469
  if not dismissed:
470
+ status_updates.append("Found Notifications dialog but couldn't dismiss it")
 
 
471
  else:
472
+ status_updates.append("No notifications popup detected")
 
 
473
  except Exception as e:
474
+ status_updates.append(f"Error handling Notifications dialog: {str(e)}")
 
 
475
 
476
  # Take feed screenshot
477
  try:
478
  feed_screenshot = f"screenshots/feed_pw_{int(time.time())}.png"
479
  page.screenshot(path=feed_screenshot)
480
  image_path = feed_screenshot
481
+ status_updates.append("Feed screenshot saved")
 
 
 
 
482
  except Exception as e:
483
+ status_updates.append(f"Error taking screenshot: {str(e)}")
 
 
484
 
485
+ status_updates.append("Successfully navigated to Instagram feed!")
 
 
486
 
487
  # Start liking posts
488
+ status_updates.append(f"Starting to like posts (target: {max_likes})...")
 
 
489
 
490
  # Like posts
491
  likes_count = 0
492
  scroll_count = 0
493
  max_scrolls = 30
494
 
495
+ # Wait for the feed to load fully before looking for posts
496
+ page.wait_for_timeout(3000)
497
+
498
  # Try to find posts with multiple selectors
499
  article_selectors = [
500
  "article",
 
509
  try:
510
  if page.query_selector(selector):
511
  article_found = True
512
+ status_updates.append(f"Found posts using selector: {selector}")
 
 
 
513
  break
514
  except Exception as e:
515
  logger.debug(f"Article selector {selector} failed: {str(e)}")
516
 
517
  if not article_found:
518
+ status_updates.append("Could not find posts on feed. Instagram may have changed their interface.")
 
 
 
519
  browser.close()
520
+ return "\n".join(status_updates), image_path
521
 
522
+ # Like one post at a time with full scroll stabilization
523
  while likes_count < max_likes and scroll_count < max_scrolls:
 
524
  try:
525
+ # Take a screenshot before each scroll to aid debugging
526
+ if scroll_count % 5 == 0:
527
+ try:
528
+ scroll_screenshot = f"screenshots/scroll_{scroll_count}_{int(time.time())}.png"
529
+ page.screenshot(path=scroll_screenshot)
530
+ image_path = scroll_screenshot
531
+ except Exception as e:
532
+ status_updates.append(f"Error taking scroll screenshot: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
533
 
534
+ # Wait for the page to be stable after scrolling
535
+ page.wait_for_timeout(2000)
536
+
537
+ # IMPORTANT FIX: Evaluate the selector directly in the page context to get fresh elements
538
+ # This addresses the "Element is not attached to the DOM" issue
539
+ like_buttons = page.evaluate("""() => {
540
+ // Try different selectors to find like buttons that haven't been clicked yet
541
+ const selectors = [
542
+ "article section svg[aria-label='Like']",
543
+ "article svg[aria-label='Like']",
544
+ "svg[aria-label='Like']",
545
+ "span[class*='_aamw'] svg[aria-label='Like']",
546
+ "article button[type='button'] svg:not([aria-label='Unlike'])"
547
+ ];
548
 
549
+ for (const selector of selectors) {
550
+ const buttons = Array.from(document.querySelectorAll(selector));
551
+ if (buttons.length > 0) {
552
+ // We need to find the actual button elements (parents of the SVGs)
553
+ const buttonElements = buttons.map(svg => {
554
+ // Find closest button parent
555
+ return svg.closest('button');
556
+ }).filter(btn => btn !== null);
557
+
558
+ if (buttonElements.length > 0) {
559
+ // Return info about the first valid button
560
+ const button = buttonElements[0];
561
+ const rect = button.getBoundingClientRect();
562
+
563
+ return {
564
+ found: true,
565
+ count: buttonElements.length,
566
+ x: rect.x + rect.width / 2,
567
+ y: rect.y + rect.height / 2
568
+ };
569
+ }
570
  }
571
+ }
 
572
 
573
+ return { found: false, count: 0 };
574
+ }""")
575
+
576
+ if like_buttons['found']:
577
+ status_updates.append(f"Found {like_buttons['count']} like buttons on scroll {scroll_count}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
 
579
+ # Click the first like button directly by coordinates
580
+ try:
581
+ # Take pre-click screenshot
582
+ try:
583
+ pre_like_screenshot = f"screenshots/pre_like_{likes_count}_{int(time.time())}.png"
584
+ page.screenshot(path=pre_like_screenshot)
585
+ except Exception as e:
586
+ status_updates.append(f"Error taking pre-like screenshot: {str(e)}")
587
 
588
+ # Click by coordinates rather than on the element
589
+ page.mouse.click(like_buttons['x'], like_buttons['y'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
 
591
+ likes_count += 1
592
+ status_updates.append(f"Liked post {likes_count}/{max_likes}")
 
 
 
 
 
 
 
 
 
 
 
 
593
 
594
+ # Take post-click screenshot
595
+ try:
596
+ post_like_screenshot = f"screenshots/post_like_{likes_count}_{int(time.time())}.png"
597
+ page.screenshot(path=post_like_screenshot)
598
+ image_path = post_like_screenshot
599
+ except Exception as e:
600
+ status_updates.append(f"Error taking post-like screenshot: {str(e)}")
601
+
602
+ # Add a random delay between likes (2-4 seconds)
603
+ delay = 2000 + random.randint(0, 2000)
604
+ page.wait_for_timeout(delay)
605
+
606
+ except Exception as e:
607
+ status_updates.append(f"Error clicking like button: {str(e)}")
608
+ else:
609
+ status_updates.append(f"No like buttons found on scroll {scroll_count}")
610
+
611
+ # Scroll down to load more
612
+ # Use a smaller scroll distance to ensure we don't miss posts
613
+ page.evaluate("window.scrollBy(0, 500);")
614
+ status_updates.append("Scrolled down to load more posts")
615
+
616
+ # Wait for new content to load
617
+ page.wait_for_load_state('networkidle', timeout=5000)
618
+
619
+ # Variable delay after scrolling (2-3 seconds)
620
+ delay = 2000 + random.randint(0, 1000)
621
+ page.wait_for_timeout(delay)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
622
 
623
+ scroll_count += 1
 
624
 
 
 
 
625
  except Exception as e:
626
+ status_updates.append(f"Error during scroll/like cycle: {str(e)}")
627
+ # Take error screenshot
628
+ try:
629
+ error_screenshot = f"screenshots/error_{int(time.time())}.png"
630
+ page.screenshot(path=error_screenshot)
631
+ image_path = error_screenshot
632
+ except:
633
+ pass
634
+
635
+ # Continue to the next scroll despite errors
636
+ scroll_count += 1
637
 
638
+ # Final status
639
  final_message = f"Finished! Liked {likes_count} posts."
640
+ status_updates.append(final_message)
 
641
 
642
  # Final screenshot
643
  try:
644
  final_screenshot = f"screenshots/final_{int(time.time())}.png"
645
  page.screenshot(path=final_screenshot)
646
  image_path = final_screenshot
647
+ status_updates.append("Final screenshot saved")
 
 
 
 
648
  except Exception as e:
649
+ status_updates.append(f"Error taking final screenshot: {str(e)}")
 
 
650
 
651
  # Close the browser
652
  browser.close()
653
+ status_updates.append("Browser closed")
 
 
 
654
 
655
  except Exception as e:
656
+ status_updates.append(f"Error during browser interaction: {str(e)}")
 
 
 
657
  traceback_msg = traceback.format_exc()
658
  logger.error(traceback_msg)
659
+ status_updates.append("See logs for detailed error information")
 
 
 
660
 
661
  # Try to close browser in case of error
662
  try:
663
  browser.close()
664
+ status_updates.append("Browser closed after error")
 
 
665
  except:
666
  pass
667
 
668
  except Exception as e:
669
  error_message = f"Error with Playwright: {str(e)}"
670
  logger.error(error_message)
671
+ status_updates.append(error_message)
 
 
672
  logger.error(traceback.format_exc())
673
 
674
+ return "\n".join(status_updates), image_path
675
 
676
+ # Gradio Interface
677
  def create_interface():
678
  with gr.Blocks(title="Instagram Auto-Liker") as app:
679
  gr.Markdown("# Instagram Auto-Liker (Playwright)")
 
693
 
694
  with gr.Column(scale=2):
695
  status_output = gr.Textbox(label="Status Log", lines=15)
696
+ image_output = gr.Image(label="Latest Screenshot", type="filepath")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
 
 
698
  submit_btn.click(
699
+ fn=run_instagram_liker,
700
  inputs=[username, password, max_likes],
701
+ outputs=[status_output, image_output]
 
 
 
 
 
 
702
  )
703
 
704
  return app