brave-real-browser-mcp-server 2.43.22 → 2.43.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -58,7 +58,7 @@ async function pageController({ browser, page, proxy, turnstile, xvfbsession, pi
58
58
  url.includes('track');
59
59
  if (isAdPopup) {
60
60
  await newPage.close().catch(() => { });
61
- console.log('[popup-blocker] Blocked popup ad:', url.substring(0, 50));
61
+ console.error('[popup-blocker] Blocked popup ad:', url.substring(0, 50));
62
62
  }
63
63
  }
64
64
  }
@@ -333,7 +333,7 @@ async function pageController({ browser, page, proxy, turnstile, xvfbsession, pi
333
333
  get: () => 8,
334
334
  configurable: true
335
335
  });
336
-
336
+
337
337
  // Device memory should be 4, 8, or 16 GB
338
338
  if ('deviceMemory' in navigator) {
339
339
  Object.defineProperty(navigator, 'deviceMemory', {
@@ -341,7 +341,7 @@ async function pageController({ browser, page, proxy, turnstile, xvfbsession, pi
341
341
  configurable: true
342
342
  });
343
343
  }
344
-
344
+
345
345
  // ========== NOTIFICATION PERMISSION FIX ==========
346
346
  if (typeof Notification !== 'undefined') {
347
347
  Object.defineProperty(Notification, 'permission', {
@@ -349,11 +349,11 @@ async function pageController({ browser, page, proxy, turnstile, xvfbsession, pi
349
349
  configurable: true
350
350
  });
351
351
  }
352
-
352
+
353
353
  // ========== DOCUMENT FOCUS FIX ==========
354
354
  const originalHasFocus = document.hasFocus.bind(document);
355
- document.hasFocus = function() { return true; };
356
-
355
+ document.hasFocus = function () { return true; };
356
+
357
357
  // ========== CONNECTION API FIX ==========
358
358
  if (!navigator.connection) {
359
359
  Object.defineProperty(navigator, 'connection', {
@@ -363,14 +363,14 @@ async function pageController({ browser, page, proxy, turnstile, xvfbsession, pi
363
363
  effectiveType: '4g',
364
364
  saveData: false,
365
365
  type: 'wifi',
366
- addEventListener: function() {},
367
- removeEventListener: function() {},
368
- dispatchEvent: function() { return true; }
366
+ addEventListener: function () { },
367
+ removeEventListener: function () { },
368
+ dispatchEvent: function () { return true; }
369
369
  }),
370
370
  configurable: true
371
371
  });
372
372
  }
373
-
373
+
374
374
  // ========== PERFORMANCE MEMORY FIX ==========
375
375
  if (window.performance && !performance.memory) {
376
376
  Object.defineProperty(performance, 'memory', {
@@ -382,17 +382,17 @@ async function pageController({ browser, page, proxy, turnstile, xvfbsession, pi
382
382
  configurable: true
383
383
  });
384
384
  }
385
-
385
+
386
386
  // ========== CHROME RUNTIME FIX (Critical for CreepJS) ==========
387
- (function() {
387
+ (function () {
388
388
  const chromeObj = {
389
389
  app: {
390
390
  isInstalled: false,
391
391
  InstallState: { DISABLED: 'disabled', INSTALLED: 'installed', NOT_INSTALLED: 'not_installed' },
392
392
  RunningState: { CANNOT_RUN: 'cannot_run', READY_TO_RUN: 'ready_to_run', RUNNING: 'running' },
393
- getDetails: function() { return null; },
394
- getIsInstalled: function() { return false; },
395
- runningState: function() { return 'cannot_run'; }
393
+ getDetails: function () { return null; },
394
+ getIsInstalled: function () { return false; },
395
+ runningState: function () { return 'cannot_run'; }
396
396
  },
397
397
  runtime: {
398
398
  OnInstalledReason: { CHROME_UPDATE: 'chrome_update', INSTALL: 'install', SHARED_MODULE_UPDATE: 'shared_module_update', UPDATE: 'update' },
@@ -401,14 +401,14 @@ async function pageController({ browser, page, proxy, turnstile, xvfbsession, pi
401
401
  PlatformNaclArch: { ARM: 'arm', MIPS: 'mips', MIPS64: 'mips64', X86_32: 'x86-32', X86_64: 'x86-64' },
402
402
  PlatformOs: { ANDROID: 'android', CROS: 'cros', FUCHSIA: 'fuchsia', LINUX: 'linux', MAC: 'mac', OPENBSD: 'openbsd', WIN: 'win' },
403
403
  RequestUpdateCheckStatus: { NO_UPDATE: 'no_update', THROTTLED: 'throttled', UPDATE_AVAILABLE: 'update_available' },
404
- connect: function() { return { name: '', sender: undefined, onDisconnect: { addListener: function() {} }, onMessage: { addListener: function() {} }, postMessage: function() {}, disconnect: function() {} }; },
405
- sendMessage: function() {},
404
+ connect: function () { return { name: '', sender: undefined, onDisconnect: { addListener: function () { } }, onMessage: { addListener: function () { } }, postMessage: function () { }, disconnect: function () { } }; },
405
+ sendMessage: function () { },
406
406
  id: undefined
407
407
  },
408
- csi: function() {
408
+ csi: function () {
409
409
  return { startE: Date.now(), onloadT: Date.now(), pageT: Math.floor(Math.random() * 1000) + 500, tran: 15 };
410
410
  },
411
- loadTimes: function() {
411
+ loadTimes: function () {
412
412
  return {
413
413
  requestTime: Date.now() / 1000 - Math.random() * 2,
414
414
  startLoadTime: Date.now() / 1000 - Math.random(),
@@ -426,21 +426,21 @@ async function pageController({ browser, page, proxy, turnstile, xvfbsession, pi
426
426
  };
427
427
  }
428
428
  };
429
-
429
+
430
430
  const makeNative = (fn, name) => {
431
- Object.defineProperty(fn, 'toString', { value: function() { return 'function ' + name + '() { [native code] }'; } });
431
+ Object.defineProperty(fn, 'toString', { value: function () { return 'function ' + name + '() { [native code] }'; } });
432
432
  return fn;
433
433
  };
434
434
  chromeObj.csi = makeNative(chromeObj.csi, 'csi');
435
435
  chromeObj.loadTimes = makeNative(chromeObj.loadTimes, 'loadTimes');
436
-
436
+
437
437
  if (!window.chrome) {
438
438
  Object.defineProperty(window, 'chrome', { value: chromeObj, writable: true, enumerable: true, configurable: true });
439
439
  } else {
440
440
  Object.keys(chromeObj).forEach(key => { if (!window.chrome[key]) window.chrome[key] = chromeObj[key]; });
441
441
  }
442
442
  })();
443
-
443
+
444
444
  // ========== NATIVE DIALOGS FIX (MUST BE FIRST) ==========
445
445
  // Fix alert, confirm, prompt to pass SannySoft detection
446
446
  (function () {
@@ -58,7 +58,7 @@ export async function pageController({ browser, page, proxy, turnstile, xvfbsess
58
58
  url.includes('track');
59
59
  if (isAdPopup) {
60
60
  await newPage.close().catch(() => { });
61
- console.log('[popup-blocker] Blocked popup ad:', url.substring(0, 50));
61
+ console.error('[popup-blocker] Blocked popup ad:', url.substring(0, 50));
62
62
  }
63
63
  }
64
64
  }
@@ -333,7 +333,7 @@ export async function pageController({ browser, page, proxy, turnstile, xvfbsess
333
333
  get: () => 8,
334
334
  configurable: true
335
335
  });
336
-
336
+
337
337
  // Device memory should be 4, 8, or 16 GB
338
338
  if ('deviceMemory' in navigator) {
339
339
  Object.defineProperty(navigator, 'deviceMemory', {
@@ -341,7 +341,7 @@ export async function pageController({ browser, page, proxy, turnstile, xvfbsess
341
341
  configurable: true
342
342
  });
343
343
  }
344
-
344
+
345
345
  // ========== NOTIFICATION PERMISSION FIX ==========
346
346
  if (typeof Notification !== 'undefined') {
347
347
  Object.defineProperty(Notification, 'permission', {
@@ -349,11 +349,11 @@ export async function pageController({ browser, page, proxy, turnstile, xvfbsess
349
349
  configurable: true
350
350
  });
351
351
  }
352
-
352
+
353
353
  // ========== DOCUMENT FOCUS FIX ==========
354
354
  const originalHasFocus = document.hasFocus.bind(document);
355
- document.hasFocus = function() { return true; };
356
-
355
+ document.hasFocus = function () { return true; };
356
+
357
357
  // ========== CONNECTION API FIX ==========
358
358
  if (!navigator.connection) {
359
359
  Object.defineProperty(navigator, 'connection', {
@@ -363,14 +363,14 @@ export async function pageController({ browser, page, proxy, turnstile, xvfbsess
363
363
  effectiveType: '4g',
364
364
  saveData: false,
365
365
  type: 'wifi',
366
- addEventListener: function() {},
367
- removeEventListener: function() {},
368
- dispatchEvent: function() { return true; }
366
+ addEventListener: function () { },
367
+ removeEventListener: function () { },
368
+ dispatchEvent: function () { return true; }
369
369
  }),
370
370
  configurable: true
371
371
  });
372
372
  }
373
-
373
+
374
374
  // ========== PERFORMANCE MEMORY FIX ==========
375
375
  if (window.performance && !performance.memory) {
376
376
  Object.defineProperty(performance, 'memory', {
@@ -382,17 +382,17 @@ export async function pageController({ browser, page, proxy, turnstile, xvfbsess
382
382
  configurable: true
383
383
  });
384
384
  }
385
-
385
+
386
386
  // ========== CHROME RUNTIME FIX (Critical for CreepJS) ==========
387
- (function() {
387
+ (function () {
388
388
  const chromeObj = {
389
389
  app: {
390
390
  isInstalled: false,
391
391
  InstallState: { DISABLED: 'disabled', INSTALLED: 'installed', NOT_INSTALLED: 'not_installed' },
392
392
  RunningState: { CANNOT_RUN: 'cannot_run', READY_TO_RUN: 'ready_to_run', RUNNING: 'running' },
393
- getDetails: function() { return null; },
394
- getIsInstalled: function() { return false; },
395
- runningState: function() { return 'cannot_run'; }
393
+ getDetails: function () { return null; },
394
+ getIsInstalled: function () { return false; },
395
+ runningState: function () { return 'cannot_run'; }
396
396
  },
397
397
  runtime: {
398
398
  OnInstalledReason: { CHROME_UPDATE: 'chrome_update', INSTALL: 'install', SHARED_MODULE_UPDATE: 'shared_module_update', UPDATE: 'update' },
@@ -401,14 +401,14 @@ export async function pageController({ browser, page, proxy, turnstile, xvfbsess
401
401
  PlatformNaclArch: { ARM: 'arm', MIPS: 'mips', MIPS64: 'mips64', X86_32: 'x86-32', X86_64: 'x86-64' },
402
402
  PlatformOs: { ANDROID: 'android', CROS: 'cros', FUCHSIA: 'fuchsia', LINUX: 'linux', MAC: 'mac', OPENBSD: 'openbsd', WIN: 'win' },
403
403
  RequestUpdateCheckStatus: { NO_UPDATE: 'no_update', THROTTLED: 'throttled', UPDATE_AVAILABLE: 'update_available' },
404
- connect: function() { return { name: '', sender: undefined, onDisconnect: { addListener: function() {} }, onMessage: { addListener: function() {} }, postMessage: function() {}, disconnect: function() {} }; },
405
- sendMessage: function() {},
404
+ connect: function () { return { name: '', sender: undefined, onDisconnect: { addListener: function () { } }, onMessage: { addListener: function () { } }, postMessage: function () { }, disconnect: function () { } }; },
405
+ sendMessage: function () { },
406
406
  id: undefined
407
407
  },
408
- csi: function() {
408
+ csi: function () {
409
409
  return { startE: Date.now(), onloadT: Date.now(), pageT: Math.floor(Math.random() * 1000) + 500, tran: 15 };
410
410
  },
411
- loadTimes: function() {
411
+ loadTimes: function () {
412
412
  return {
413
413
  requestTime: Date.now() / 1000 - Math.random() * 2,
414
414
  startLoadTime: Date.now() / 1000 - Math.random(),
@@ -426,21 +426,21 @@ export async function pageController({ browser, page, proxy, turnstile, xvfbsess
426
426
  };
427
427
  }
428
428
  };
429
-
429
+
430
430
  const makeNative = (fn, name) => {
431
- Object.defineProperty(fn, 'toString', { value: function() { return 'function ' + name + '() { [native code] }'; } });
431
+ Object.defineProperty(fn, 'toString', { value: function () { return 'function ' + name + '() { [native code] }'; } });
432
432
  return fn;
433
433
  };
434
434
  chromeObj.csi = makeNative(chromeObj.csi, 'csi');
435
435
  chromeObj.loadTimes = makeNative(chromeObj.loadTimes, 'loadTimes');
436
-
436
+
437
437
  if (!window.chrome) {
438
438
  Object.defineProperty(window, 'chrome', { value: chromeObj, writable: true, enumerable: true, configurable: true });
439
439
  } else {
440
440
  Object.keys(chromeObj).forEach(key => { if (!window.chrome[key]) window.chrome[key] = chromeObj[key]; });
441
441
  }
442
442
  })();
443
-
443
+
444
444
  // ========== NATIVE DIALOGS FIX (MUST BE FIRST) ==========
445
445
  // Fix alert, confirm, prompt to pass SannySoft detection
446
446
  (function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.43.22",
3
+ "version": "2.43.24",
4
4
  "description": "MCP Server for Brave Real Browser - Puppeteer with Brave Browser, Stealth Mode, Ad Blocker, and Turnstile Auto-Solver for undetectable web automation.",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/esm/index.mjs",
@@ -74,7 +74,7 @@
74
74
  "license": "ISC",
75
75
  "dependencies": {
76
76
  "@modelcontextprotocol/sdk": "^1.25.3",
77
- "brave-real-puppeteer-core": "^24.37.3-brave.6",
77
+ "brave-real-puppeteer-core": "^24.37.3-brave.8",
78
78
  "ghost-cursor": "^1.4.2",
79
79
  "puppeteer-extra": "^3.3.6",
80
80
  "puppeteer-extra-plugin-stealth": "^2.11.2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-blocker",
3
- "version": "1.19.23",
3
+ "version": "1.19.25",
4
4
  "description": "Advanced uBlock Origin management and stealth features for Brave Real Browser",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -56,7 +56,7 @@
56
56
  "@cliqz/adblocker-puppeteer": "^1.34.0",
57
57
  "@ghostery/adblocker-puppeteer": "^2.14.1",
58
58
  "adm-zip": "^0.5.10",
59
- "brave-real-puppeteer-core": "^24.37.3-brave.6",
59
+ "brave-real-puppeteer-core": "^24.37.3-brave.8",
60
60
  "cross-fetch": "^4.1.0",
61
61
  "fs-extra": "^11.3.3",
62
62
  "got": "^13.0.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-launcher",
3
- "version": "1.25.22",
3
+ "version": "1.25.24",
4
4
  "description": "Launch Brave Browser with ease from node. Based on chrome-launcher with Brave-specific support.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -54,7 +54,7 @@
54
54
  "typescript": "^5.0.0"
55
55
  },
56
56
  "dependencies": {
57
- "brave-real-blocker": "^1.19.23",
57
+ "brave-real-blocker": "^1.19.25",
58
58
  "escape-string-regexp": "^5.0.0",
59
59
  "is-wsl": "^3.1.1",
60
60
  "which": "^6.0.1"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-playwright-core",
3
- "version": "1.61.22",
3
+ "version": "1.61.24",
4
4
  "description": "Brave-optimized Playwright Core (v1.57.0) with comprehensive stealth patches and error stack sanitization",
5
5
  "keywords": [
6
6
  "playwright",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-puppeteer-core",
3
- "version": "24.37.3-brave.6",
3
+ "version": "24.37.3-brave.8",
4
4
  "description": "🦁 Brave Real-World Optimized Puppeteer & Playwright Core with 1-5ms ultra-fast timing, 50+ professional stealth features, intelligent browser auto-detection, and 100% bot detection bypass. Features cross-platform Brave browser integration, comprehensive anti-detection, and breakthrough performance improvements.",
5
5
  "keywords": [
6
6
  "automation",
@@ -135,7 +135,7 @@
135
135
  },
136
136
  "dependencies": {
137
137
  "@puppeteer/browsers": "2.12.1",
138
- "brave-real-launcher": "^1.25.22",
138
+ "brave-real-launcher": "^1.25.24",
139
139
  "chromium-bidi": "14.0.0",
140
140
  "debug": "^4.4.3",
141
141
  "devtools-protocol": "0.0.1566079",
@@ -17,6 +17,7 @@ const ocr = require('../../lib/ocr-captcha-solver');
17
17
  let browserInstance = null;
18
18
  let pageInstance = null;
19
19
  let blockerInstance = null;
20
+ let setupPageFn = null; // CDP early injection function
20
21
  let networkRecords = [];
21
22
  let isRecordingNetwork = false;
22
23
  let progressTasks = {};
@@ -249,14 +250,14 @@ const handlers = {
249
250
  // Selectors for common modal close buttons
250
251
  const closeSelectors = [
251
252
  // Bootstrap/Standard Modals
252
- '.modal.show .btn-close',
253
- '.modal.show .close',
253
+ '.modal.show .btn-close',
254
+ '.modal.show .close',
254
255
  '.modal.in .close',
255
256
  '.modal-footer .btn-primary', // "OK" button usually
256
257
  '.modal-footer .btn-secondary', // "Close" button
257
258
  // Custom Overlays
258
- '#modal-close',
259
- '.popup-close',
259
+ '#modal-close',
260
+ '.popup-close',
260
261
  '.overlay-close',
261
262
  // Generic "X" buttons in overlays
262
263
  'div[role="dialog"] button[aria-label="Close"]',
@@ -563,6 +564,7 @@ const handlers = {
563
564
  browserInstance = result.browser;
564
565
  pageInstance = result.page;
565
566
  blockerInstance = result.blocker;
567
+ setupPageFn = result.setupPage; // Store CDP early injection function
566
568
 
567
569
  // ═══════════════════════════════════════════════════════════════
568
570
  // GLOBAL DIALOG HANDLER - Auto-handle dialogs
@@ -571,19 +573,19 @@ const handlers = {
571
573
  pageInstance.on('dialog', async (dialog) => {
572
574
  const dialogType = dialog.type();
573
575
  const msg = dialog.message().toLowerCase();
574
-
575
- notifyProgress('browser_init', 'progress',
576
+
577
+ notifyProgress('browser_init', 'progress',
576
578
  `🔔 Handling dialog: ${dialogType} - ${dialog.message().substring(0, 100)}...`);
577
-
579
+
578
580
  try {
579
581
  // Critical Fix: BLOCK redirects to external sites (e.g., eCommittee)
580
582
  // These redirects take the user away from the search page
581
583
  if (msg.includes('redirect') || msg.includes('external') || msg.includes('leaving')) {
582
- console.log('🚫 Blocking redirect dialog (Dismiss)');
583
- await dialog.dismiss(); // Simulate clicking 'Cancel'
584
+ console.error('🚫 Blocking redirect dialog (Dismiss)');
585
+ await dialog.dismiss(); // Simulate clicking 'Cancel'
584
586
  } else {
585
- // Auto-accept other dialogs (like alerts or simple confirmations)
586
- await dialog.accept(); // Simulate clicking 'OK'
587
+ // Auto-accept other dialogs (like alerts or simple confirmations)
588
+ await dialog.accept(); // Simulate clicking 'OK'
587
589
  }
588
590
  } catch (e) {
589
591
  // Ignore errors (dialog might be closed by injected script)
@@ -598,7 +600,7 @@ const handlers = {
598
600
  await pageInstance.evaluateOnNewDocument(() => {
599
601
  window.originalConfirm = window.confirm;
600
602
  window.originalAlert = window.alert;
601
-
603
+
602
604
  // Smart Confirm Handler
603
605
  window.confirm = (msg) => {
604
606
  console.log('Intercepted Confirm Dialog:', msg);
@@ -608,13 +610,13 @@ const handlers = {
608
610
  }
609
611
  return true; // Return TRUE = Click OK
610
612
  };
611
-
613
+
612
614
  // Silently ignore alerts (always OK)
613
615
  window.alert = (msg) => {
614
616
  console.log('Blocked Alert Dialog:', msg);
615
617
  return true;
616
618
  };
617
-
619
+
618
620
  // Silently return null for prompts
619
621
  window.prompt = (msg) => {
620
622
  console.log('Blocked Prompt Dialog:', msg);
@@ -646,6 +648,24 @@ const handlers = {
646
648
 
647
649
  notifyProgress('navigate', 'started', `Navigating to: ${url}`);
648
650
 
651
+ // ═══════════════════════════════════════════════════════════════
652
+ // CDP EARLY INJECTION - Setup BEFORE navigation for better ad blocking
653
+ // This ensures CSS and scripts are injected before page scripts run
654
+ // ═══════════════════════════════════════════════════════════════
655
+ console.error('[Navigate] setupPageFn available:', !!setupPageFn);
656
+ if (setupPageFn) {
657
+ try {
658
+ await setupPageFn(page);
659
+ notifyProgress('navigate', 'progress', 'CDP early injection setup complete');
660
+ console.error('[Navigate] CDP early injection SUCCESS');
661
+ } catch (e) {
662
+ // Non-critical error, continue navigation
663
+ console.error('[Navigate] CDP early injection failed:', e.message);
664
+ }
665
+ } else {
666
+ console.error('[Navigate] No setupPageFn available - CDP early injection skipped');
667
+ }
668
+
649
669
  let lastError = null;
650
670
 
651
671
  for (let attempt = 0; attempt <= retries; attempt++) {
@@ -796,11 +816,11 @@ const handlers = {
796
816
  // 5. Click (ENHANCED: iframe + hover + auto video player detection)
797
817
  async click(params) {
798
818
  const { page } = requireBrowser();
799
- const {
800
- selector,
801
- humanLike = true,
802
- clickCount = 1,
803
- delay = 0,
819
+ const {
820
+ selector,
821
+ humanLike = true,
822
+ clickCount = 1,
823
+ delay = 0,
804
824
  autoAcceptDialogs = true,
805
825
  retries = 3,
806
826
  timeout = 60000,
@@ -834,17 +854,17 @@ const handlers = {
834
854
  // ═══════════════════════════════════════════════════════════════
835
855
  if (autoDetectPlayer) {
836
856
  notifyProgress('click', 'progress', '🔍 Scanning all iframes for video players...');
837
-
857
+
838
858
  const frames = page.frames();
839
-
859
+
840
860
  for (let i = 0; i < frames.length; i++) {
841
861
  try {
842
862
  const frame = frames[i];
843
863
  const frameUrl = frame.url();
844
-
864
+
845
865
  // Skip blank frames
846
866
  if (frameUrl === 'about:blank' || !frameUrl) continue;
847
-
867
+
848
868
  // Detect player in this frame
849
869
  const playerInfo = await frame.evaluate(() => {
850
870
  const result = {
@@ -855,7 +875,7 @@ const handlers = {
855
875
  controls: [],
856
876
  downloadButton: null
857
877
  };
858
-
878
+
859
879
  // Check for video element
860
880
  const video = document.querySelector('video');
861
881
  if (video) {
@@ -867,7 +887,7 @@ const handlers = {
867
887
  readyState: video.readyState
868
888
  };
869
889
  }
870
-
890
+
871
891
  // 1. JWPlayer Detection
872
892
  if (window.jwplayer && typeof window.jwplayer === 'function') {
873
893
  try {
@@ -877,49 +897,49 @@ const handlers = {
877
897
  result.playerType = 'jwplayer';
878
898
  result.playerState = jw.getState();
879
899
  result.controls.push('.jw-icon-display', '.jw-icon-playback', '[aria-label="Play"]');
880
-
900
+
881
901
  // Find download button in JWPlayer
882
902
  const dlBtn = document.querySelector('[aria-label="Download"], .jw-icon-download, [class*="download"]');
883
903
  if (dlBtn) result.downloadButton = '[aria-label="Download"]';
884
904
  }
885
- } catch (e) {}
905
+ } catch (e) { }
886
906
  }
887
-
907
+
888
908
  // 2. VideoJS Detection
889
909
  if (window.videojs || document.querySelector('.video-js')) {
890
910
  result.hasPlayer = true;
891
911
  result.playerType = result.playerType || 'videojs';
892
912
  result.controls.push('.vjs-big-play-button', '.vjs-play-control');
893
913
  }
894
-
914
+
895
915
  // 3. Plyr Detection
896
916
  if (window.Plyr || document.querySelector('.plyr')) {
897
917
  result.hasPlayer = true;
898
918
  result.playerType = result.playerType || 'plyr';
899
919
  result.controls.push('.plyr__control--play', '[data-plyr="play"]');
900
920
  }
901
-
921
+
902
922
  // 4. VidStack Detection
903
923
  if (window.VidStack || document.querySelector('media-player')) {
904
924
  result.hasPlayer = true;
905
925
  result.playerType = result.playerType || 'vidstack';
906
926
  result.controls.push('media-play-button', '[data-media-play]');
907
927
  }
908
-
928
+
909
929
  // 5. DooPlayer Detection
910
930
  if (window.DooPlay || document.querySelector('#dooplay') || document.querySelector('.dooplay')) {
911
931
  result.hasPlayer = true;
912
932
  result.playerType = result.playerType || 'dooplayer';
913
933
  result.controls.push('.play-btn', '.dooplay-play');
914
934
  }
915
-
935
+
916
936
  // 6. Generic HTML5 Video
917
937
  if (result.hasVideo && !result.hasPlayer) {
918
938
  result.hasPlayer = true;
919
939
  result.playerType = 'html5';
920
940
  result.controls.push('video');
921
941
  }
922
-
942
+
923
943
  // Find any download button
924
944
  if (!result.downloadButton) {
925
945
  const dlSelectors = [
@@ -934,14 +954,14 @@ const handlers = {
934
954
  }
935
955
  }
936
956
  }
937
-
957
+
938
958
  return result;
939
959
  }).catch(() => ({ hasPlayer: false }));
940
-
960
+
941
961
  if (playerInfo.hasPlayer) {
942
962
  context = frame;
943
- frameInfo = {
944
- index: i,
963
+ frameInfo = {
964
+ index: i,
945
965
  url: frameUrl,
946
966
  autoDetected: true
947
967
  };
@@ -951,8 +971,8 @@ const handlers = {
951
971
  controls: playerInfo.controls,
952
972
  downloadButton: playerInfo.downloadButton
953
973
  };
954
-
955
- notifyProgress('click', 'progress',
974
+
975
+ notifyProgress('click', 'progress',
956
976
  `✅ Found ${playerInfo.playerType.toUpperCase()} in iframe ${i}: ${frameUrl.substring(0, 50)}...`);
957
977
  break;
958
978
  }
@@ -961,7 +981,7 @@ const handlers = {
961
981
  continue;
962
982
  }
963
983
  }
964
-
984
+
965
985
  if (!detectedPlayer) {
966
986
  notifyProgress('click', 'progress', '⚠️ No video player found in any iframe, using main page');
967
987
  }
@@ -971,7 +991,7 @@ const handlers = {
971
991
  if (!autoDetectPlayer && (iframe !== undefined || iframeSelector)) {
972
992
  try {
973
993
  const frames = page.frames();
974
-
994
+
975
995
  if (iframe !== undefined && frames[iframe]) {
976
996
  context = frames[iframe];
977
997
  frameInfo = { index: iframe, url: frames[iframe].url() };
@@ -1020,10 +1040,10 @@ const handlers = {
1020
1040
  // ═══════════════════════════════════════════════════════════════
1021
1041
  if (usePlayerAPI && detectedPlayer && (selector === 'video' || selector.includes('play') || selector.includes('Play'))) {
1022
1042
  notifyProgress('click', 'progress', `🎬 Using ${detectedPlayer.type} API for playback...`);
1023
-
1043
+
1024
1044
  playerResult = await context.evaluate((playerType) => {
1025
1045
  const result = { success: false, method: null, state: null };
1026
-
1046
+
1027
1047
  try {
1028
1048
  if (playerType === 'jwplayer' && window.jwplayer) {
1029
1049
  const jw = window.jwplayer();
@@ -1059,20 +1079,20 @@ const handlers = {
1059
1079
  } catch (e) {
1060
1080
  result.error = e.message;
1061
1081
  }
1062
-
1082
+
1063
1083
  return result;
1064
1084
  }, detectedPlayer.type).catch(e => ({ success: false, error: e.message }));
1065
-
1085
+
1066
1086
  if (playerResult.success) {
1067
1087
  notifyProgress('click', 'progress', `✅ ${playerResult.method} executed`);
1068
-
1088
+
1069
1089
  // Wait for play if requested
1070
1090
  if (waitForPlay) {
1071
1091
  notifyProgress('click', 'progress', '⏳ Waiting for video to start playing...');
1072
-
1092
+
1073
1093
  const startTime = Date.now();
1074
1094
  let isPlaying = false;
1075
-
1095
+
1076
1096
  while (Date.now() - startTime < playerTimeout) {
1077
1097
  const state = await context.evaluate(() => {
1078
1098
  const video = document.querySelector('video');
@@ -1089,21 +1109,21 @@ const handlers = {
1089
1109
  }
1090
1110
  return { playing: false };
1091
1111
  }).catch(() => ({ playing: false }));
1092
-
1112
+
1093
1113
  if (state.playing || state.currentTime > 0) {
1094
1114
  isPlaying = true;
1095
1115
  notifyProgress('click', 'progress', `▶️ Video is now playing (${state.currentTime?.toFixed(1) || 0}s)`);
1096
1116
  break;
1097
1117
  }
1098
-
1118
+
1099
1119
  await new Promise(r => setTimeout(r, 500));
1100
1120
  }
1101
-
1121
+
1102
1122
  if (!isPlaying) {
1103
1123
  notifyProgress('click', 'progress', '⚠️ Video may still be buffering');
1104
1124
  }
1105
1125
  }
1106
-
1126
+
1107
1127
  notifyProgress('click', 'completed', `Video playback started via ${playerResult.method}`, {
1108
1128
  selector,
1109
1129
  clicked: true,
@@ -1112,7 +1132,7 @@ const handlers = {
1112
1132
  iframe: frameInfo,
1113
1133
  playerResult
1114
1134
  });
1115
-
1135
+
1116
1136
  return {
1117
1137
  success: true,
1118
1138
  selector,
@@ -1153,7 +1173,7 @@ const handlers = {
1153
1173
  // HOVER functionality (for video player dynamic controls)
1154
1174
  if (hoverFirst || hoverOnly) {
1155
1175
  notifyProgress('click', 'progress', `Hovering over ${selector}...`);
1156
-
1176
+
1157
1177
  try {
1158
1178
  await context.hover(selector);
1159
1179
  notifyProgress('click', 'progress', `Hover successful, waiting ${hoverDuration}ms for controls...`);
@@ -1169,7 +1189,7 @@ const handlers = {
1169
1189
  }
1170
1190
  }
1171
1191
  }
1172
-
1192
+
1173
1193
  if (hoverOnly) {
1174
1194
  notifyProgress('click', 'completed', `Hover completed: ${selector}`, { selector, hovered: true, iframe: frameInfo });
1175
1195
  return { success: true, selector, hovered: true, clicked: false, iframe: frameInfo, detectedPlayer };
@@ -1187,7 +1207,7 @@ const handlers = {
1187
1207
  try {
1188
1208
  const { createCursor } = require('ghost-cursor');
1189
1209
  const cursor = createCursor(page);
1190
-
1210
+
1191
1211
  if (context !== page) {
1192
1212
  const element = await context.$(selector);
1193
1213
  if (element) {
@@ -1210,19 +1230,19 @@ const handlers = {
1210
1230
 
1211
1231
  await new Promise(r => setTimeout(r, 300));
1212
1232
 
1213
- notifyProgress('click', 'completed',
1214
- `${hoverFirst ? 'Hovered+' : ''}Clicked: ${selector}${dialogHandled ? ' (dialog auto-accepted)' : ''}`,
1233
+ notifyProgress('click', 'completed',
1234
+ `${hoverFirst ? 'Hovered+' : ''}Clicked: ${selector}${dialogHandled ? ' (dialog auto-accepted)' : ''}`,
1215
1235
  { selector, humanLike, dialogHandled, iframe: frameInfo, detectedPlayer, attempts: attempt }
1216
1236
  );
1217
1237
 
1218
- return {
1219
- success: true,
1220
- selector,
1221
- clicked: true,
1222
- dialogHandled,
1223
- iframe: frameInfo,
1238
+ return {
1239
+ success: true,
1240
+ selector,
1241
+ clicked: true,
1242
+ dialogHandled,
1243
+ iframe: frameInfo,
1224
1244
  detectedPlayer,
1225
- attempts: attempt
1245
+ attempts: attempt
1226
1246
  };
1227
1247
 
1228
1248
  } catch (attemptError) {
@@ -1246,10 +1266,10 @@ const handlers = {
1246
1266
  // 6. Type (ENHANCED: iframe support)
1247
1267
  async type(params) {
1248
1268
  const { page } = requireBrowser();
1249
- const {
1250
- selector,
1251
- text,
1252
- delay = 50,
1269
+ const {
1270
+ selector,
1271
+ text,
1272
+ delay = 50,
1253
1273
  clear = false,
1254
1274
  // NEW: iframe support
1255
1275
  iframe,
@@ -1268,7 +1288,7 @@ const handlers = {
1268
1288
  if (iframe !== undefined || iframeSelector) {
1269
1289
  try {
1270
1290
  const frames = page.frames();
1271
-
1291
+
1272
1292
  if (iframe !== undefined && frames[iframe]) {
1273
1293
  context = frames[iframe];
1274
1294
  frameInfo = { index: iframe, url: frames[iframe].url() };
@@ -1345,6 +1365,7 @@ const handlers = {
1345
1365
  browserInstance = null;
1346
1366
  pageInstance = null;
1347
1367
  blockerInstance = null;
1368
+ setupPageFn = null;
1348
1369
  }
1349
1370
 
1350
1371
  notifyProgress('browser_close', 'completed', 'Browser closed');
@@ -2752,8 +2773,8 @@ const handlers = {
2752
2773
  // 26. Execute JS (ENHANCED: iframe context support - FIXED)
2753
2774
  async execute_js(params) {
2754
2775
  const { page } = requireBrowser();
2755
- const {
2756
- code,
2776
+ const {
2777
+ code,
2757
2778
  returnValue = true,
2758
2779
  // NEW: iframe support (FIXED)
2759
2780
  iframe,
@@ -2771,7 +2792,7 @@ const handlers = {
2771
2792
  if (iframe !== undefined || iframeSelector) {
2772
2793
  try {
2773
2794
  const frames = page.frames();
2774
-
2795
+
2775
2796
  if (iframe !== undefined) {
2776
2797
  // iframe index provided
2777
2798
  if (iframe === 0) {
@@ -2820,9 +2841,9 @@ const handlers = {
2820
2841
  // Execute the code in the correct context
2821
2842
  const result = await context.evaluate(code);
2822
2843
 
2823
- notifyProgress('execute_js', 'completed', 'JavaScript executed', {
2824
- hasResult: result !== undefined,
2825
- iframe: frameInfo
2844
+ notifyProgress('execute_js', 'completed', 'JavaScript executed', {
2845
+ hasResult: result !== undefined,
2846
+ iframe: frameInfo
2826
2847
  });
2827
2848
 
2828
2849
  return { success: true, result: returnValue ? result : undefined, iframe: frameInfo };
@@ -4227,6 +4248,7 @@ async function cleanup() {
4227
4248
  browserInstance = null;
4228
4249
  pageInstance = null;
4229
4250
  blockerInstance = null;
4251
+ setupPageFn = null;
4230
4252
  }
4231
4253
  }
4232
4254