brave-real-browser-mcp-server 2.9.21 → 2.11.0
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.
- package/dist/handlers/advanced-extraction-handlers.js +483 -0
- package/dist/handlers/advanced-video-media-handlers.js +150 -43
- package/dist/handlers/api-integration-handlers.js +2 -1
- package/dist/handlers/captcha-handlers.js +2 -1
- package/dist/handlers/dynamic-session-handlers.js +41 -9
- package/dist/handlers/pagination-handlers.js +5 -5
- package/dist/handlers/smart-data-extractors.js +4 -4
- package/dist/handlers/visual-tools-handlers.js +2 -1
- package/dist/index.js +231 -106
- package/dist/mcp-response-validator.js +145 -0
- package/dist/tool-definitions.js +50 -0
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// @ts-nocheck
|
|
4
4
|
import { getCurrentPage } from '../browser-manager.js';
|
|
5
5
|
import { validateWorkflow } from '../workflow-validation.js';
|
|
6
|
-
import { withErrorHandling } from '../system-utils.js';
|
|
6
|
+
import { withErrorHandling, sleep } from '../system-utils.js';
|
|
7
7
|
/**
|
|
8
8
|
* Video Link Finder - Find all video links on page
|
|
9
9
|
*/
|
|
@@ -184,7 +184,7 @@ export async function handleVideoDownloadButton(args) {
|
|
|
184
184
|
const selector = customSelector || 'a[download], button[download]';
|
|
185
185
|
try {
|
|
186
186
|
await page.click(selector);
|
|
187
|
-
await
|
|
187
|
+
await sleep(2000);
|
|
188
188
|
return {
|
|
189
189
|
content: [{
|
|
190
190
|
type: 'text',
|
|
@@ -252,7 +252,7 @@ export async function handleVideoPlayPushSource(args) {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
// Wait for sources to load
|
|
255
|
-
await
|
|
255
|
+
await sleep(3000);
|
|
256
256
|
page.off('response', responseHandler);
|
|
257
257
|
return {
|
|
258
258
|
content: [{
|
|
@@ -370,84 +370,154 @@ export async function handleUrlRedirectTraceEndpoints(args) {
|
|
|
370
370
|
* Network Recording Finder - Find and analyze network recordings
|
|
371
371
|
*/
|
|
372
372
|
export async function handleNetworkRecordingFinder(args) {
|
|
373
|
-
|
|
374
|
-
validateWorkflow('network_recording_finder', {
|
|
373
|
+
try {
|
|
374
|
+
const validation = validateWorkflow('network_recording_finder', {
|
|
375
375
|
requireBrowser: true,
|
|
376
376
|
requirePage: true,
|
|
377
377
|
});
|
|
378
|
+
if (!validation.isValid) {
|
|
379
|
+
return {
|
|
380
|
+
content: [{
|
|
381
|
+
type: 'text',
|
|
382
|
+
text: `⚠️ ${validation.errorMessage || 'Workflow validation failed'}`,
|
|
383
|
+
}],
|
|
384
|
+
isError: true,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
378
387
|
const page = getCurrentPage();
|
|
379
388
|
const duration = args.duration || 10000;
|
|
380
389
|
const filterType = args.filterType || 'video'; // video, audio, media
|
|
390
|
+
const navigateTo = args.navigateTo; // Optional URL to navigate to
|
|
391
|
+
const verbose = args.verbose !== false; // Default true for detailed logging
|
|
381
392
|
const recordings = [];
|
|
393
|
+
let totalResponses = 0;
|
|
394
|
+
let matchedResponses = 0;
|
|
382
395
|
const responseHandler = async (response) => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
else if (filterType === 'audio' && contentType.includes('audio')) {
|
|
391
|
-
shouldRecord = true;
|
|
392
|
-
}
|
|
393
|
-
else if (filterType === 'media' && (contentType.includes('video') || contentType.includes('audio'))) {
|
|
394
|
-
shouldRecord = true;
|
|
395
|
-
}
|
|
396
|
-
if (shouldRecord) {
|
|
397
|
-
try {
|
|
398
|
-
const buffer = await response.buffer();
|
|
399
|
-
recordings.push({
|
|
400
|
-
url,
|
|
401
|
-
contentType,
|
|
402
|
-
size: buffer.length,
|
|
403
|
-
status: response.status(),
|
|
404
|
-
timestamp: new Date().toISOString(),
|
|
405
|
-
});
|
|
396
|
+
try {
|
|
397
|
+
totalResponses++;
|
|
398
|
+
const url = response.url();
|
|
399
|
+
const contentType = response.headers()['content-type'] || '';
|
|
400
|
+
const resourceType = response.request().resourceType();
|
|
401
|
+
if (verbose && totalResponses % 10 === 0) {
|
|
402
|
+
console.log(`[Network Recording] Processed ${totalResponses} responses, ${matchedResponses} matched`);
|
|
406
403
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
404
|
+
let shouldRecord = false;
|
|
405
|
+
if (filterType === 'video' && (contentType.includes('video') || resourceType === 'media')) {
|
|
406
|
+
shouldRecord = true;
|
|
407
|
+
}
|
|
408
|
+
else if (filterType === 'audio' && contentType.includes('audio')) {
|
|
409
|
+
shouldRecord = true;
|
|
410
|
+
}
|
|
411
|
+
else if (filterType === 'media' && (contentType.includes('video') || contentType.includes('audio'))) {
|
|
412
|
+
shouldRecord = true;
|
|
413
|
+
}
|
|
414
|
+
if (shouldRecord) {
|
|
415
|
+
matchedResponses++;
|
|
416
|
+
if (verbose) {
|
|
417
|
+
console.log(`[Network Recording] ✅ Matched ${filterType}: ${url.substring(0, 100)}`);
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
const buffer = await response.buffer();
|
|
421
|
+
recordings.push({
|
|
422
|
+
url,
|
|
423
|
+
contentType,
|
|
424
|
+
size: buffer.length,
|
|
425
|
+
status: response.status(),
|
|
426
|
+
timestamp: new Date().toISOString(),
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
catch (e) {
|
|
430
|
+
recordings.push({
|
|
431
|
+
url,
|
|
432
|
+
contentType,
|
|
433
|
+
status: response.status(),
|
|
434
|
+
error: 'Could not capture buffer',
|
|
435
|
+
});
|
|
436
|
+
}
|
|
414
437
|
}
|
|
415
438
|
}
|
|
439
|
+
catch (e) {
|
|
440
|
+
// Ignore individual response errors
|
|
441
|
+
}
|
|
416
442
|
};
|
|
443
|
+
console.log(`[Network Recording] 🎬 Starting monitoring for ${filterType} (${duration}ms)${navigateTo ? ` + navigating to ${navigateTo}` : ''}`);
|
|
417
444
|
page.on('response', responseHandler);
|
|
418
|
-
|
|
445
|
+
// If navigateTo is provided, navigate first, then wait
|
|
446
|
+
if (navigateTo) {
|
|
447
|
+
try {
|
|
448
|
+
await page.goto(navigateTo, { waitUntil: 'networkidle2', timeout: 30000 });
|
|
449
|
+
console.log(`[Network Recording] ✅ Navigation complete, continuing monitoring...`);
|
|
450
|
+
}
|
|
451
|
+
catch (e) {
|
|
452
|
+
console.log(`[Network Recording] ⚠️ Navigation error (continuing anyway): ${e}`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
await sleep(duration);
|
|
419
456
|
page.off('response', responseHandler);
|
|
457
|
+
console.log(`[Network Recording] 🛑 Monitoring stopped. Total: ${totalResponses}, Matched: ${matchedResponses}, Recorded: ${recordings.length}`);
|
|
458
|
+
if (recordings.length === 0) {
|
|
459
|
+
return {
|
|
460
|
+
content: [{
|
|
461
|
+
type: 'text',
|
|
462
|
+
text: `ℹ️ No ${filterType} recordings found\n\n📊 Statistics:\n • Total responses checked: ${totalResponses}\n • Matched ${filterType} responses: ${matchedResponses}\n • Duration: ${duration}ms\n • Navigation: ${navigateTo || 'None'}\n\n💡 Suggestions:\n ${navigateTo ? '• Try longer duration if page loads slowly\n • Check if page actually has video/media content' : '• Use navigateTo parameter to capture requests during page load\n • Example: {"navigateTo": "https://example.com", "duration": 15000}'}\n • Consider 'advanced_video_extraction' for analyzing loaded content`,
|
|
463
|
+
}],
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
content: [{
|
|
468
|
+
type: 'text',
|
|
469
|
+
text: `✅ Network Recordings Found: ${recordings.length}\n\n📊 Statistics:\n • Total responses: ${totalResponses}\n • Matched: ${matchedResponses}\n • Recorded: ${recordings.length}\n\n${JSON.stringify(recordings, null, 2)}`,
|
|
470
|
+
}],
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
catch (error) {
|
|
420
474
|
return {
|
|
421
475
|
content: [{
|
|
422
476
|
type: 'text',
|
|
423
|
-
text:
|
|
477
|
+
text: `❌ Network recording finder failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
424
478
|
}],
|
|
479
|
+
isError: true,
|
|
425
480
|
};
|
|
426
|
-
}
|
|
481
|
+
}
|
|
427
482
|
}
|
|
428
483
|
/**
|
|
429
484
|
* Network Recording Extractors - Extract data from network recordings
|
|
430
485
|
*/
|
|
431
486
|
export async function handleNetworkRecordingExtractors(args) {
|
|
432
|
-
|
|
433
|
-
validateWorkflow('network_recording_extractors', {
|
|
487
|
+
try {
|
|
488
|
+
const validation = validateWorkflow('network_recording_extractors', {
|
|
434
489
|
requireBrowser: true,
|
|
435
490
|
requirePage: true,
|
|
436
491
|
});
|
|
492
|
+
if (!validation.isValid) {
|
|
493
|
+
return {
|
|
494
|
+
content: [{
|
|
495
|
+
type: 'text',
|
|
496
|
+
text: `⚠️ ${validation.errorMessage || 'Workflow validation failed'}`,
|
|
497
|
+
}],
|
|
498
|
+
isError: true,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
437
501
|
const page = getCurrentPage();
|
|
438
502
|
const duration = args.duration || 10000;
|
|
503
|
+
const navigateTo = args.navigateTo; // Optional URL to navigate to
|
|
504
|
+
const verbose = args.verbose !== false; // Default true
|
|
439
505
|
const extractedData = {
|
|
440
506
|
videos: [],
|
|
441
507
|
audio: [],
|
|
442
508
|
manifests: [],
|
|
443
509
|
apis: [],
|
|
444
510
|
};
|
|
511
|
+
let totalResponses = 0;
|
|
445
512
|
const responseHandler = async (response) => {
|
|
513
|
+
totalResponses++;
|
|
446
514
|
const url = response.url();
|
|
447
515
|
const contentType = response.headers()['content-type'] || '';
|
|
448
516
|
try {
|
|
449
517
|
// Video files
|
|
450
518
|
if (contentType.includes('video') || url.includes('.mp4') || url.includes('.webm')) {
|
|
519
|
+
if (verbose)
|
|
520
|
+
console.log(`[Extractor] 🎥 Video found: ${url.substring(0, 80)}`);
|
|
451
521
|
extractedData.videos.push({
|
|
452
522
|
url,
|
|
453
523
|
contentType,
|
|
@@ -456,6 +526,8 @@ export async function handleNetworkRecordingExtractors(args) {
|
|
|
456
526
|
}
|
|
457
527
|
// Audio files
|
|
458
528
|
if (contentType.includes('audio') || url.includes('.mp3') || url.includes('.m4a')) {
|
|
529
|
+
if (verbose)
|
|
530
|
+
console.log(`[Extractor] 🎵 Audio found: ${url.substring(0, 80)}`);
|
|
459
531
|
extractedData.audio.push({
|
|
460
532
|
url,
|
|
461
533
|
contentType,
|
|
@@ -463,6 +535,8 @@ export async function handleNetworkRecordingExtractors(args) {
|
|
|
463
535
|
}
|
|
464
536
|
// Manifest files (HLS, DASH)
|
|
465
537
|
if (url.includes('.m3u8') || url.includes('.mpd')) {
|
|
538
|
+
if (verbose)
|
|
539
|
+
console.log(`[Extractor] 📜 Manifest found: ${url.substring(0, 80)}`);
|
|
466
540
|
const text = await response.text();
|
|
467
541
|
extractedData.manifests.push({
|
|
468
542
|
url,
|
|
@@ -472,6 +546,8 @@ export async function handleNetworkRecordingExtractors(args) {
|
|
|
472
546
|
}
|
|
473
547
|
// API responses with video data
|
|
474
548
|
if (contentType.includes('json') && (url.includes('video') || url.includes('media'))) {
|
|
549
|
+
if (verbose)
|
|
550
|
+
console.log(`[Extractor] 📡 API found: ${url.substring(0, 80)}`);
|
|
475
551
|
const json = await response.json();
|
|
476
552
|
extractedData.apis.push({
|
|
477
553
|
url,
|
|
@@ -483,16 +559,47 @@ export async function handleNetworkRecordingExtractors(args) {
|
|
|
483
559
|
// Response not available
|
|
484
560
|
}
|
|
485
561
|
};
|
|
562
|
+
console.log(`[Extractor] 🎬 Starting extraction (${duration}ms)${navigateTo ? ` + navigating to ${navigateTo}` : ''}`);
|
|
486
563
|
page.on('response', responseHandler);
|
|
487
|
-
|
|
564
|
+
// If navigateTo is provided, navigate first, then wait
|
|
565
|
+
if (navigateTo) {
|
|
566
|
+
try {
|
|
567
|
+
await page.goto(navigateTo, { waitUntil: 'networkidle2', timeout: 30000 });
|
|
568
|
+
console.log(`[Extractor] ✅ Navigation complete, continuing extraction...`);
|
|
569
|
+
}
|
|
570
|
+
catch (e) {
|
|
571
|
+
console.log(`[Extractor] ⚠️ Navigation error (continuing): ${e}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
await sleep(duration);
|
|
488
575
|
page.off('response', responseHandler);
|
|
576
|
+
const totalFound = extractedData.videos.length + extractedData.audio.length +
|
|
577
|
+
extractedData.manifests.length + extractedData.apis.length;
|
|
578
|
+
console.log(`[Extractor] 🛑 Extraction complete. Total responses: ${totalResponses}, Extracted: ${totalFound}`);
|
|
579
|
+
if (totalFound === 0) {
|
|
580
|
+
return {
|
|
581
|
+
content: [{
|
|
582
|
+
type: 'text',
|
|
583
|
+
text: `ℹ️ No media content extracted\n\n📊 Statistics:\n • Total responses checked: ${totalResponses}\n • Duration: ${duration}ms\n • Navigation: ${navigateTo || 'None'}\n\n💡 Suggestions:\n ${navigateTo ? '• Try longer duration (15000-20000ms)\n • Verify page actually contains video/media' : '• Add navigateTo parameter: {"navigateTo": "https://example.com", "duration": 15000}'}\n • Use 'advanced_video_extraction' for analyzing loaded content\n • Check browser console logs for detailed monitoring`,
|
|
584
|
+
}],
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
return {
|
|
588
|
+
content: [{
|
|
589
|
+
type: 'text',
|
|
590
|
+
text: `✅ Network Recording Extraction Complete\n\n📊 Results:\n • Videos: ${extractedData.videos.length}\n • Audio: ${extractedData.audio.length}\n • Manifests: ${extractedData.manifests.length}\n • APIs: ${extractedData.apis.length}\n • Total responses: ${totalResponses}\n\n${JSON.stringify(extractedData, null, 2)}`,
|
|
591
|
+
}],
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
489
595
|
return {
|
|
490
596
|
content: [{
|
|
491
597
|
type: 'text',
|
|
492
|
-
text:
|
|
598
|
+
text: `❌ Network recording extraction failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
493
599
|
}],
|
|
600
|
+
isError: true,
|
|
494
601
|
};
|
|
495
|
-
}
|
|
602
|
+
}
|
|
496
603
|
}
|
|
497
604
|
/**
|
|
498
605
|
* Video Links Finders - Advanced video link detection
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import { getPageInstance } from '../browser-manager.js';
|
|
3
3
|
import axios from 'axios';
|
|
4
|
+
import { sleep } from '../system-utils.js';
|
|
4
5
|
/**
|
|
5
6
|
* REST API Endpoint Finder - Discover REST API endpoints
|
|
6
7
|
*/
|
|
@@ -46,7 +47,7 @@ export async function handleRESTAPIEndpointFinder(args) {
|
|
|
46
47
|
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
|
47
48
|
}
|
|
48
49
|
// Wait for additional requests
|
|
49
|
-
await
|
|
50
|
+
await sleep(scanDuration);
|
|
50
51
|
page.off('request', requestHandler);
|
|
51
52
|
}
|
|
52
53
|
// Also scan page content for API endpoints
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import { getPageInstance } from '../browser-manager.js';
|
|
3
3
|
import Tesseract from 'tesseract.js';
|
|
4
|
+
import { sleep } from '../system-utils.js';
|
|
4
5
|
/**
|
|
5
6
|
* OCR Engine - Extract text from captcha images using OCR
|
|
6
7
|
*/
|
|
@@ -207,7 +208,7 @@ export async function handlePuzzleCaptchaHandler(args) {
|
|
|
207
208
|
const stepSize = targetDistance / steps;
|
|
208
209
|
for (let i = 0; i < steps; i++) {
|
|
209
210
|
await page.mouse.move(box.x + box.width / 2 + (stepSize * i), box.y + box.height / 2, { steps: 5 });
|
|
210
|
-
await
|
|
211
|
+
await sleep(50 + Math.random() * 50); // Random delay for human-like behavior
|
|
211
212
|
}
|
|
212
213
|
await page.mouse.up();
|
|
213
214
|
result.attemptedSolve = true;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// @ts-nocheck
|
|
4
4
|
import { getCurrentPage } from '../browser-manager.js';
|
|
5
5
|
import { validateWorkflow } from '../workflow-validation.js';
|
|
6
|
-
import { withErrorHandling } from '../system-utils.js';
|
|
6
|
+
import { withErrorHandling, sleep } from '../system-utils.js';
|
|
7
7
|
/**
|
|
8
8
|
* Shadow DOM Extractor - Extract content from Shadow DOM
|
|
9
9
|
*/
|
|
@@ -291,11 +291,20 @@ export async function handleFormAutoFill(args) {
|
|
|
291
291
|
* AJAX Content Waiter - Wait for dynamic content to load
|
|
292
292
|
*/
|
|
293
293
|
export async function handleAjaxContentWaiter(args) {
|
|
294
|
-
|
|
295
|
-
validateWorkflow('ajax_content_waiter', {
|
|
294
|
+
try {
|
|
295
|
+
const validation = validateWorkflow('ajax_content_waiter', {
|
|
296
296
|
requireBrowser: true,
|
|
297
297
|
requirePage: true,
|
|
298
298
|
});
|
|
299
|
+
if (!validation.isValid) {
|
|
300
|
+
return {
|
|
301
|
+
content: [{
|
|
302
|
+
type: 'text',
|
|
303
|
+
text: `⚠️ ${validation.errorMessage || 'Workflow validation failed'}`,
|
|
304
|
+
}],
|
|
305
|
+
isError: true,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
299
308
|
const page = getCurrentPage();
|
|
300
309
|
const waitFor = args.waitFor || 'selector'; // selector, xhr, timeout
|
|
301
310
|
const value = args.value;
|
|
@@ -320,7 +329,7 @@ export async function handleAjaxContentWaiter(args) {
|
|
|
320
329
|
}
|
|
321
330
|
}
|
|
322
331
|
if (waitFor === 'xhr') {
|
|
323
|
-
const duration = value || 5000;
|
|
332
|
+
const duration = parseInt(value) || 5000;
|
|
324
333
|
let xhrCount = 0;
|
|
325
334
|
const requestHandler = (request) => {
|
|
326
335
|
if (request.resourceType() === 'xhr' || request.resourceType() === 'fetch') {
|
|
@@ -328,8 +337,16 @@ export async function handleAjaxContentWaiter(args) {
|
|
|
328
337
|
}
|
|
329
338
|
};
|
|
330
339
|
page.on('request', requestHandler);
|
|
331
|
-
await
|
|
340
|
+
await sleep(duration);
|
|
332
341
|
page.off('request', requestHandler);
|
|
342
|
+
if (xhrCount === 0) {
|
|
343
|
+
return {
|
|
344
|
+
content: [{
|
|
345
|
+
type: 'text',
|
|
346
|
+
text: `ℹ️ Waited ${duration}ms - No XHR/Fetch requests detected.\n\n💡 Note: Monitoring captures NEW requests made during wait period. Page already loaded before monitoring started.`,
|
|
347
|
+
}],
|
|
348
|
+
};
|
|
349
|
+
}
|
|
333
350
|
return {
|
|
334
351
|
content: [{
|
|
335
352
|
type: 'text',
|
|
@@ -338,8 +355,8 @@ export async function handleAjaxContentWaiter(args) {
|
|
|
338
355
|
};
|
|
339
356
|
}
|
|
340
357
|
if (waitFor === 'timeout') {
|
|
341
|
-
const duration = value || 3000;
|
|
342
|
-
await
|
|
358
|
+
const duration = parseInt(value) || 3000;
|
|
359
|
+
await sleep(duration);
|
|
343
360
|
return {
|
|
344
361
|
content: [{
|
|
345
362
|
type: 'text',
|
|
@@ -347,8 +364,23 @@ export async function handleAjaxContentWaiter(args) {
|
|
|
347
364
|
}],
|
|
348
365
|
};
|
|
349
366
|
}
|
|
350
|
-
|
|
351
|
-
|
|
367
|
+
return {
|
|
368
|
+
content: [{
|
|
369
|
+
type: 'text',
|
|
370
|
+
text: `❌ Unknown waitFor type: ${waitFor}. Valid types: 'selector', 'xhr', 'timeout'`,
|
|
371
|
+
}],
|
|
372
|
+
isError: true,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
return {
|
|
377
|
+
content: [{
|
|
378
|
+
type: 'text',
|
|
379
|
+
text: `❌ AJAX content waiter failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
380
|
+
}],
|
|
381
|
+
isError: true,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
352
384
|
}
|
|
353
385
|
/**
|
|
354
386
|
* Modal Popup Handler - Handle modal popups
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// @ts-nocheck
|
|
4
4
|
import { getCurrentPage } from '../browser-manager.js';
|
|
5
5
|
import { validateWorkflow } from '../workflow-validation.js';
|
|
6
|
-
import { withErrorHandling } from '../system-utils.js';
|
|
6
|
+
import { withErrorHandling, sleep } from '../system-utils.js';
|
|
7
7
|
import * as xml2js from 'xml2js';
|
|
8
8
|
/**
|
|
9
9
|
* "Next" button automatically detect और click करके सभी pages से data collect करता है
|
|
@@ -51,14 +51,14 @@ export async function handleAutoPagination(args) {
|
|
|
51
51
|
}
|
|
52
52
|
// Click next button
|
|
53
53
|
await nextButton.click();
|
|
54
|
-
await
|
|
54
|
+
await sleep(waitBetweenPages);
|
|
55
55
|
// Wait for navigation or content load
|
|
56
56
|
try {
|
|
57
57
|
await page.waitForNavigation({ timeout: 5000, waitUntil: 'domcontentloaded' });
|
|
58
58
|
}
|
|
59
59
|
catch (e) {
|
|
60
60
|
// No navigation occurred, content loaded dynamically
|
|
61
|
-
await
|
|
61
|
+
await sleep(1000);
|
|
62
62
|
}
|
|
63
63
|
currentPage++;
|
|
64
64
|
}
|
|
@@ -114,7 +114,7 @@ export async function handleInfiniteScroll(args) {
|
|
|
114
114
|
window.scrollTo(0, document.body.scrollHeight);
|
|
115
115
|
});
|
|
116
116
|
// Wait for new content to load
|
|
117
|
-
await
|
|
117
|
+
await sleep(scrollDelay);
|
|
118
118
|
scrollCount++;
|
|
119
119
|
}
|
|
120
120
|
return {
|
|
@@ -145,7 +145,7 @@ export async function handleMultiPageScraper(args) {
|
|
|
145
145
|
const url = urls[i];
|
|
146
146
|
try {
|
|
147
147
|
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
|
148
|
-
await
|
|
148
|
+
await sleep(waitBetweenPages);
|
|
149
149
|
const pageData = await page.evaluate((selector) => {
|
|
150
150
|
const elements = document.querySelectorAll(selector);
|
|
151
151
|
return Array.from(elements).map((el) => ({
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// @ts-nocheck
|
|
4
4
|
import { getCurrentPage } from '../browser-manager.js';
|
|
5
5
|
import { validateWorkflow } from '../workflow-validation.js';
|
|
6
|
-
import { withErrorHandling } from '../system-utils.js';
|
|
6
|
+
import { withErrorHandling, sleep } from '../system-utils.js';
|
|
7
7
|
/**
|
|
8
8
|
* HTML Elements Extractor - Extract all HTML elements with complete details
|
|
9
9
|
*/
|
|
@@ -203,7 +203,7 @@ export async function handleAjaxExtractor(args) {
|
|
|
203
203
|
if (url && page.url() !== url) {
|
|
204
204
|
await page.goto(url, { waitUntil: 'networkidle2' });
|
|
205
205
|
}
|
|
206
|
-
await
|
|
206
|
+
await sleep(duration);
|
|
207
207
|
page.off('request', requestHandler);
|
|
208
208
|
return {
|
|
209
209
|
content: [{
|
|
@@ -247,7 +247,7 @@ export async function handleFetchXHR(args) {
|
|
|
247
247
|
}
|
|
248
248
|
};
|
|
249
249
|
page.on('response', responseHandler);
|
|
250
|
-
await
|
|
250
|
+
await sleep(duration);
|
|
251
251
|
page.off('response', responseHandler);
|
|
252
252
|
return {
|
|
253
253
|
content: [{
|
|
@@ -296,7 +296,7 @@ export async function handleNetworkRecorder(args) {
|
|
|
296
296
|
};
|
|
297
297
|
page.on('request', requestHandler);
|
|
298
298
|
page.on('response', responseHandler);
|
|
299
|
-
await
|
|
299
|
+
await sleep(duration);
|
|
300
300
|
page.off('request', requestHandler);
|
|
301
301
|
page.off('response', responseHandler);
|
|
302
302
|
return {
|
|
@@ -4,6 +4,7 @@ import * as fs from 'fs/promises';
|
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import pixelmatch from 'pixelmatch';
|
|
6
6
|
import { PNG } from 'pngjs';
|
|
7
|
+
import { sleep } from '../system-utils.js';
|
|
7
8
|
/**
|
|
8
9
|
* Full Page Screenshot - Capture entire page
|
|
9
10
|
*/
|
|
@@ -236,7 +237,7 @@ export async function handleVideoRecording(args) {
|
|
|
236
237
|
const framePath = path.join(framesDir, `frame_${i.toString().padStart(4, '0')}.png`);
|
|
237
238
|
await page.screenshot({ path: framePath });
|
|
238
239
|
frames.push(framePath);
|
|
239
|
-
await
|
|
240
|
+
await sleep(frameDelay);
|
|
240
241
|
}
|
|
241
242
|
}
|
|
242
243
|
return {
|