brave-real-browser-mcp-server 2.17.10 → 2.17.12
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/browser-manager.js +0 -8
- package/dist/debug-logger.js +28 -0
- package/dist/handlers/advanced-extraction-handlers.js +0 -80
- package/dist/handlers/deep-analysis-handler.js +119 -0
- package/dist/handlers/multi-element-handlers.js +0 -60
- package/dist/handlers/smart-data-extractors.js +0 -475
- package/dist/handlers/unified-captcha-handler.js +137 -0
- package/dist/handlers/unified-search-handler.js +137 -0
- package/dist/index.js +87 -63
- package/dist/tool-definitions.js +58 -186
- package/dist/workflows/forensic-media-extractor.js +5 -15
- package/dist/workflows/media-extraction-workflow.js +3 -8
- package/package.json +1 -1
- package/dist/handlers/advanced-video-media-handlers.js +0 -139
- package/dist/handlers/captcha-handlers.js +0 -257
- package/dist/handlers/data-quality-handlers.js +0 -82
- package/dist/handlers/search-filter-handlers.js +0 -264
package/dist/tool-definitions.js
CHANGED
|
@@ -237,17 +237,33 @@ export const TOOLS = [
|
|
|
237
237
|
},
|
|
238
238
|
{
|
|
239
239
|
name: 'solve_captcha',
|
|
240
|
-
description: '
|
|
240
|
+
description: 'Solve various types of CAPTCHAs (Auto-detect, OCR, Audio, Puzzle). Routes to appropriate solver based on arguments.',
|
|
241
241
|
inputSchema: {
|
|
242
242
|
type: 'object',
|
|
243
243
|
properties: {
|
|
244
|
-
|
|
244
|
+
strategy: {
|
|
245
245
|
type: 'string',
|
|
246
|
-
enum: ['recaptcha', 'hCaptcha', 'turnstile'],
|
|
247
|
-
description: '
|
|
246
|
+
enum: ['auto', 'ocr', 'audio', 'puzzle', 'recaptcha', 'hCaptcha', 'turnstile'],
|
|
247
|
+
description: 'Strategy to use. "auto" attempts to infer based on provided arguments.',
|
|
248
|
+
default: 'auto'
|
|
248
249
|
},
|
|
250
|
+
// Shared
|
|
251
|
+
url: { type: 'string' },
|
|
252
|
+
// OCR
|
|
253
|
+
selector: { type: 'string' },
|
|
254
|
+
imageUrl: { type: 'string' },
|
|
255
|
+
imageBuffer: { type: 'string' },
|
|
256
|
+
language: { type: 'string' },
|
|
257
|
+
// Audio
|
|
258
|
+
audioSelector: { type: 'string' },
|
|
259
|
+
audioUrl: { type: 'string' },
|
|
260
|
+
downloadPath: { type: 'string' },
|
|
261
|
+
// Puzzle
|
|
262
|
+
puzzleSelector: { type: 'string' },
|
|
263
|
+
sliderSelector: { type: 'string' },
|
|
264
|
+
method: { type: 'string' },
|
|
249
265
|
},
|
|
250
|
-
required: [
|
|
266
|
+
required: [],
|
|
251
267
|
},
|
|
252
268
|
},
|
|
253
269
|
{
|
|
@@ -336,18 +352,6 @@ export const TOOLS = [
|
|
|
336
352
|
},
|
|
337
353
|
// Smart Data Extractors
|
|
338
354
|
// DOM & HTML Extraction (Phase 1)
|
|
339
|
-
{
|
|
340
|
-
name: 'html_elements_extractor',
|
|
341
|
-
description: 'Extract detailed information about HTML elements matching a selector',
|
|
342
|
-
inputSchema: {
|
|
343
|
-
type: 'object',
|
|
344
|
-
properties: {
|
|
345
|
-
selector: { type: 'string', default: '*' },
|
|
346
|
-
maxElements: { type: 'number', default: 100 },
|
|
347
|
-
includeStyles: { type: 'boolean', default: false },
|
|
348
|
-
},
|
|
349
|
-
},
|
|
350
|
-
},
|
|
351
355
|
{
|
|
352
356
|
name: 'extract_json',
|
|
353
357
|
description: 'Extract embedded JSON/API data from the page',
|
|
@@ -398,19 +402,6 @@ export const TOOLS = [
|
|
|
398
402
|
required: ['selector'],
|
|
399
403
|
},
|
|
400
404
|
},
|
|
401
|
-
{
|
|
402
|
-
name: 'attribute_harvester',
|
|
403
|
-
description: 'Collect attributes (href, src, data-*) from elements',
|
|
404
|
-
inputSchema: {
|
|
405
|
-
type: 'object',
|
|
406
|
-
properties: {
|
|
407
|
-
selector: { type: 'string' },
|
|
408
|
-
attributes: { type: 'array', items: { type: 'string' } },
|
|
409
|
-
maxElements: { type: 'number', default: 100 },
|
|
410
|
-
},
|
|
411
|
-
required: ['selector'],
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
405
|
// Content Type Specific Extractors
|
|
415
406
|
{
|
|
416
407
|
name: 'link_harvester',
|
|
@@ -476,115 +467,60 @@ export const TOOLS = [
|
|
|
476
467
|
},
|
|
477
468
|
},
|
|
478
469
|
// Search & Filter Tools (5 tools)
|
|
470
|
+
// Search & Filter Tools (Consolidated)
|
|
479
471
|
{
|
|
480
|
-
name: '
|
|
481
|
-
description: '
|
|
472
|
+
name: 'search_content',
|
|
473
|
+
description: 'Search content using keywords or regex patterns.',
|
|
482
474
|
inputSchema: {
|
|
483
475
|
type: 'object',
|
|
484
476
|
properties: {
|
|
477
|
+
query: { type: 'string', description: 'Keyword or Regex pattern' },
|
|
478
|
+
type: { type: 'string', enum: ['text', 'regex'], default: 'text' },
|
|
485
479
|
url: { type: 'string' },
|
|
486
|
-
|
|
480
|
+
// Text options
|
|
487
481
|
caseSensitive: { type: 'boolean', default: false },
|
|
488
482
|
wholeWord: { type: 'boolean', default: false },
|
|
489
483
|
context: { type: 'number', default: 50 },
|
|
490
|
-
|
|
491
|
-
required: ['keywords'],
|
|
492
|
-
},
|
|
493
|
-
},
|
|
494
|
-
{
|
|
495
|
-
name: 'regex_pattern_matcher',
|
|
496
|
-
description: 'Search using regular expressions',
|
|
497
|
-
inputSchema: {
|
|
498
|
-
type: 'object',
|
|
499
|
-
properties: {
|
|
500
|
-
url: { type: 'string' },
|
|
501
|
-
pattern: { type: 'string', description: 'Regular expression pattern' },
|
|
484
|
+
// Regex options
|
|
502
485
|
flags: { type: 'string', default: 'g' },
|
|
503
|
-
selector: { type: 'string' },
|
|
486
|
+
selector: { type: 'string', description: 'Limit search to specific element' },
|
|
504
487
|
},
|
|
505
|
-
required: ['
|
|
488
|
+
required: ['query'],
|
|
506
489
|
},
|
|
507
490
|
},
|
|
508
491
|
{
|
|
509
|
-
name: '
|
|
510
|
-
description: '
|
|
492
|
+
name: 'find_element_advanced',
|
|
493
|
+
description: 'Find elements using XPath or Advanced CSS selectors.',
|
|
511
494
|
inputSchema: {
|
|
512
495
|
type: 'object',
|
|
513
496
|
properties: {
|
|
497
|
+
query: { type: 'string', description: 'Selector or XPath expression' },
|
|
498
|
+
type: { type: 'string', enum: ['css', 'xpath'], default: 'css' },
|
|
514
499
|
url: { type: 'string' },
|
|
515
|
-
|
|
516
|
-
returnType: { type: 'string', default: 'elements' },
|
|
517
|
-
},
|
|
518
|
-
required: ['xpath'],
|
|
519
|
-
},
|
|
520
|
-
},
|
|
521
|
-
{
|
|
522
|
-
name: 'advanced_css_selectors',
|
|
523
|
-
description: 'Support for complex CSS selectors',
|
|
524
|
-
inputSchema: {
|
|
525
|
-
type: 'object',
|
|
526
|
-
properties: {
|
|
527
|
-
url: { type: 'string' },
|
|
528
|
-
selector: { type: 'string' },
|
|
500
|
+
// CSS options
|
|
529
501
|
operation: { type: 'string', enum: ['query', 'closest', 'matches'], default: 'query' },
|
|
530
|
-
|
|
502
|
+
// Shared
|
|
503
|
+
returnType: { type: 'string', enum: ['elements', 'styles', 'html'], default: 'elements' },
|
|
531
504
|
},
|
|
532
|
-
required: ['
|
|
505
|
+
required: ['query'],
|
|
533
506
|
},
|
|
534
507
|
},
|
|
535
508
|
// Data Quality & Validation (5 tools)
|
|
509
|
+
// Deep Analysis Tool -- Trace Recording
|
|
536
510
|
{
|
|
537
|
-
name: '
|
|
538
|
-
description: '
|
|
539
|
-
inputSchema: {
|
|
540
|
-
type: 'object',
|
|
541
|
-
properties: {
|
|
542
|
-
data: { description: 'Data to validate' },
|
|
543
|
-
schema: { type: 'object', description: 'JSON Schema' },
|
|
544
|
-
},
|
|
545
|
-
required: ['data', 'schema'],
|
|
546
|
-
},
|
|
547
|
-
},
|
|
548
|
-
// Advanced Captcha Handling (3 tools)
|
|
549
|
-
{
|
|
550
|
-
name: 'ocr_engine',
|
|
551
|
-
description: 'Extract text from captcha images using OCR',
|
|
511
|
+
name: 'deep_analysis',
|
|
512
|
+
description: 'Perform a deep analysis of the page including network traces, console logs, DOM snapshot, and screenshot. Equivalent to a trace recording.',
|
|
552
513
|
inputSchema: {
|
|
553
514
|
type: 'object',
|
|
554
515
|
properties: {
|
|
555
516
|
url: { type: 'string' },
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
{
|
|
564
|
-
name: 'audio_captcha_solver',
|
|
565
|
-
description: 'Handle audio captchas',
|
|
566
|
-
inputSchema: {
|
|
567
|
-
type: 'object',
|
|
568
|
-
properties: {
|
|
569
|
-
url: { type: 'string' },
|
|
570
|
-
audioSelector: { type: 'string' },
|
|
571
|
-
audioUrl: { type: 'string' },
|
|
572
|
-
downloadPath: { type: 'string' },
|
|
573
|
-
},
|
|
574
|
-
},
|
|
575
|
-
},
|
|
576
|
-
{
|
|
577
|
-
name: 'puzzle_captcha_handler',
|
|
578
|
-
description: 'Handle slider and puzzle captchas',
|
|
579
|
-
inputSchema: {
|
|
580
|
-
type: 'object',
|
|
581
|
-
properties: {
|
|
582
|
-
url: { type: 'string' },
|
|
583
|
-
puzzleSelector: { type: 'string' },
|
|
584
|
-
sliderSelector: { type: 'string' },
|
|
585
|
-
method: { type: 'string', enum: ['auto', 'manual'], default: 'auto' },
|
|
586
|
-
},
|
|
587
|
-
},
|
|
517
|
+
duration: { type: 'number', default: 5000, description: 'Duration to record (ms)' },
|
|
518
|
+
screenshots: { type: 'boolean', default: true },
|
|
519
|
+
network: { type: 'boolean', default: true },
|
|
520
|
+
logs: { type: 'boolean', default: true },
|
|
521
|
+
dom: { type: 'boolean', default: true }
|
|
522
|
+
}
|
|
523
|
+
}
|
|
588
524
|
},
|
|
589
525
|
// Screenshot & Visual Tools (5 tools)
|
|
590
526
|
{
|
|
@@ -616,16 +552,6 @@ export const TOOLS = [
|
|
|
616
552
|
},
|
|
617
553
|
},
|
|
618
554
|
// Smart Data Extractors (Advanced)
|
|
619
|
-
{
|
|
620
|
-
name: 'fetch_xhr',
|
|
621
|
-
description: 'Capture fetch and XHR requests with responses',
|
|
622
|
-
inputSchema: {
|
|
623
|
-
type: 'object',
|
|
624
|
-
properties: {
|
|
625
|
-
duration: { type: 'number', default: 15000 },
|
|
626
|
-
},
|
|
627
|
-
},
|
|
628
|
-
},
|
|
629
555
|
{
|
|
630
556
|
name: 'network_recorder',
|
|
631
557
|
description: 'Record all network activity',
|
|
@@ -713,14 +639,6 @@ export const TOOLS = [
|
|
|
713
639
|
},
|
|
714
640
|
},
|
|
715
641
|
},
|
|
716
|
-
{
|
|
717
|
-
name: 'deobfuscate_js',
|
|
718
|
-
description: 'Deobfuscate JavaScript code and extract hidden URLs, domains, and base64-encoded content. Detects eval, atob, hex encoding, and identifier obfuscation.',
|
|
719
|
-
inputSchema: {
|
|
720
|
-
type: 'object',
|
|
721
|
-
properties: {},
|
|
722
|
-
},
|
|
723
|
-
},
|
|
724
642
|
{
|
|
725
643
|
name: 'multi_layer_redirect_trace',
|
|
726
644
|
description: 'Follow multiple layers of redirects (URL redirects and iframe chains) to find final video source. Traces up to specified depth.',
|
|
@@ -754,46 +672,6 @@ export const TOOLS = [
|
|
|
754
672
|
},
|
|
755
673
|
},
|
|
756
674
|
// Phase 3: Media & Video Tools
|
|
757
|
-
{
|
|
758
|
-
name: 'video_source_extractor',
|
|
759
|
-
description: 'Extract raw video sources from video tags and sources',
|
|
760
|
-
inputSchema: {
|
|
761
|
-
type: 'object',
|
|
762
|
-
properties: {
|
|
763
|
-
url: { type: 'string' }
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
},
|
|
767
|
-
{
|
|
768
|
-
name: 'video_player_finder',
|
|
769
|
-
description: 'Identify video players (JWPlayer, VideoJS, etc) and extract config',
|
|
770
|
-
inputSchema: {
|
|
771
|
-
type: 'object',
|
|
772
|
-
properties: {
|
|
773
|
-
url: { type: 'string' }
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
},
|
|
777
|
-
{
|
|
778
|
-
name: 'stream_detector',
|
|
779
|
-
description: 'Detects HLS (m3u8) and DASH (mpd) streams from network traffic',
|
|
780
|
-
inputSchema: {
|
|
781
|
-
type: 'object',
|
|
782
|
-
properties: {
|
|
783
|
-
duration: { type: 'number', description: 'Monitoring duration in ms' }
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
},
|
|
787
|
-
{
|
|
788
|
-
name: 'video_download_link_finder',
|
|
789
|
-
description: 'Find direct download links for video files',
|
|
790
|
-
inputSchema: {
|
|
791
|
-
type: 'object',
|
|
792
|
-
properties: {
|
|
793
|
-
extensions: { type: 'array', items: { type: 'string' } }
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
675
|
];
|
|
798
676
|
// Tool name constants for type safety
|
|
799
677
|
export const TOOL_NAMES = {
|
|
@@ -814,14 +692,11 @@ export const TOOL_NAMES = {
|
|
|
814
692
|
EXTRACT_SCHEMA: 'extract_schema',
|
|
815
693
|
// Multi-Element Extractors
|
|
816
694
|
BATCH_ELEMENT_SCRAPER: 'batch_element_scraper',
|
|
817
|
-
ATTRIBUTE_HARVESTER: 'attribute_harvester',
|
|
818
695
|
// Content Type Specific
|
|
819
696
|
LINK_HARVESTER: 'link_harvester',
|
|
820
697
|
MEDIA_EXTRACTOR: 'media_extractor',
|
|
821
698
|
// DOM & HTML Extraction (Phase 1)
|
|
822
|
-
HTML_ELEMENTS_EXTRACTOR: 'html_elements_extractor',
|
|
823
699
|
// Network Tools (Phase 1)
|
|
824
|
-
FETCH_XHR: 'fetch_xhr',
|
|
825
700
|
NETWORK_RECORDER: 'network_recorder',
|
|
826
701
|
API_FINDER: 'api_finder',
|
|
827
702
|
// Pagination Tools
|
|
@@ -831,20 +706,17 @@ export const TOOL_NAMES = {
|
|
|
831
706
|
SMART_SELECTOR_GENERATOR: 'smart_selector_generator',
|
|
832
707
|
CONTENT_CLASSIFICATION: 'content_classification',
|
|
833
708
|
// Phase 3: Media & Video
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
//
|
|
838
|
-
|
|
839
|
-
REGEX_PATTERN_MATCHER: 'regex_pattern_matcher',
|
|
840
|
-
XPATH_SUPPORT: 'xpath_support',
|
|
841
|
-
ADVANCED_CSS_SELECTORS: 'advanced_css_selectors',
|
|
709
|
+
// Search & Filter (Consolidated)
|
|
710
|
+
SEARCH_CONTENT: 'search_content',
|
|
711
|
+
FIND_ELEMENT_ADVANCED: 'find_element_advanced',
|
|
712
|
+
// Deep Analysis
|
|
713
|
+
DEEP_ANALYSIS: 'deep_analysis',
|
|
842
714
|
// Data Quality & Validation
|
|
843
|
-
DATA_TYPE_VALIDATOR
|
|
844
|
-
// Advanced Captcha Handling
|
|
845
|
-
OCR_ENGINE: 'ocr_engine',
|
|
846
|
-
AUDIO_CAPTCHA_SOLVER: 'audio_captcha_solver',
|
|
847
|
-
PUZZLE_CAPTCHA_HANDLER: 'puzzle_captcha_handler',
|
|
715
|
+
// (Removed DATA_TYPE_VALIDATOR)
|
|
716
|
+
// Advanced Captcha Handling (Consolidated)
|
|
717
|
+
// OCR_ENGINE: 'ocr_engine', // Merged into solve_captcha
|
|
718
|
+
// AUDIO_CAPTCHA_SOLVER: 'audio_captcha_solver', // Merged
|
|
719
|
+
// PUZZLE_CAPTCHA_HANDLER: 'puzzle_captcha_handler', // Merged
|
|
848
720
|
// Screenshot & Visual Tools
|
|
849
721
|
ELEMENT_SCREENSHOT: 'element_screenshot',
|
|
850
722
|
VIDEO_RECORDING: 'video_recording',
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { handleBrowserInit } from '../handlers/browser-handlers.js';
|
|
2
2
|
import { handleNavigate } from '../handlers/navigation-handlers.js';
|
|
3
|
-
import { handleAdProtectionDetector, handleAdvancedVideoExtraction
|
|
4
|
-
import { handleVideoDownloadLinkFinder } from '../handlers/advanced-video-media-handlers.js';
|
|
3
|
+
import { handleAdProtectionDetector, handleAdvancedVideoExtraction } from '../handlers/advanced-extraction-handlers.js';
|
|
5
4
|
import { handleSmartSelectorGenerator } from '../handlers/ai-powered-handlers.js';
|
|
6
5
|
import { handleNetworkRecorder, handleApiFinder } from '../handlers/smart-data-extractors.js';
|
|
7
|
-
import {
|
|
6
|
+
import { handleSearchContent } from '../handlers/unified-search-handler.js';
|
|
8
7
|
import { handleRandomScroll } from '../handlers/interaction-handlers.js';
|
|
9
8
|
async function main() {
|
|
10
9
|
const targetUrl = "https://multimovies.golf/movies/120-bahadur/";
|
|
@@ -48,15 +47,7 @@ async function main() {
|
|
|
48
47
|
}
|
|
49
48
|
// Logic Extraction (looking for player config)
|
|
50
49
|
try {
|
|
51
|
-
|
|
52
|
-
// Filter for relevant keys
|
|
53
|
-
if (deobfuscated && deobfuscated.content) {
|
|
54
|
-
const keyTerms = ['player', 'token', 'm3u8', 'mp4', 'config'];
|
|
55
|
-
const relevantScripts = deobfuscated.content
|
|
56
|
-
.filter((c) => c.text && keyTerms.some(term => c.text.includes(term)))
|
|
57
|
-
.map((c) => c.text.substring(0, 200) + "...");
|
|
58
|
-
report.infrastructure.event_handlers = relevantScripts;
|
|
59
|
-
}
|
|
50
|
+
console.log("Deobfuscation is now handled by Advanced Video Extraction.");
|
|
60
51
|
}
|
|
61
52
|
catch (e) {
|
|
62
53
|
console.log("Deobfuscation skipped");
|
|
@@ -79,7 +70,7 @@ async function main() {
|
|
|
79
70
|
];
|
|
80
71
|
report.infrastructure.patterns = [];
|
|
81
72
|
for (const check of regexChecks) {
|
|
82
|
-
const match = await
|
|
73
|
+
const match = await handleSearchContent({ query: check.pattern, type: 'regex' });
|
|
83
74
|
if (match && match.content) {
|
|
84
75
|
report.infrastructure.patterns.push({ type: check.name, result: match.content });
|
|
85
76
|
}
|
|
@@ -91,8 +82,7 @@ async function main() {
|
|
|
91
82
|
// Assuming videoAssets structure, we'd loop and add to report.targets
|
|
92
83
|
// For now, dump the raw result
|
|
93
84
|
report.targets_raw = videoAssets;
|
|
94
|
-
|
|
95
|
-
report.download_links = downloadLinks;
|
|
85
|
+
report.download_links = "Handled by Advanced Video Extraction";
|
|
96
86
|
console.log("--- MASTER REPORT JSON ---");
|
|
97
87
|
console.log(JSON.stringify(report, null, 2));
|
|
98
88
|
console.log("--- END REPORT ---");
|
|
@@ -3,8 +3,7 @@ import { handleNavigate } from '../handlers/navigation-handlers.js';
|
|
|
3
3
|
import { handleRandomScroll } from '../handlers/interaction-handlers.js';
|
|
4
4
|
import { handleElementScreenshot } from '../handlers/visual-tools-handlers.js';
|
|
5
5
|
import { handleNetworkRecorder } from '../handlers/smart-data-extractors.js';
|
|
6
|
-
import { handleAdvancedVideoExtraction,
|
|
7
|
-
import { handleVideoDownloadLinkFinder } from '../handlers/advanced-video-media-handlers.js';
|
|
6
|
+
import { handleAdvancedVideoExtraction, handleAdProtectionDetector } from '../handlers/advanced-extraction-handlers.js';
|
|
8
7
|
async function main() {
|
|
9
8
|
const targetUrl = process.argv[2];
|
|
10
9
|
if (!targetUrl) {
|
|
@@ -45,9 +44,7 @@ async function main() {
|
|
|
45
44
|
}
|
|
46
45
|
// 6. Obfuscation Bypass
|
|
47
46
|
if (!foundMedia) {
|
|
48
|
-
console.log("Deep Scan empty.
|
|
49
|
-
const deobfuscated = await handleDeobfuscateJS({});
|
|
50
|
-
console.log("Deobfuscation Results:", deobfuscated);
|
|
47
|
+
console.log("Deep Scan empty. Deobfuscation is now handled by Advanced Video Extraction.");
|
|
51
48
|
}
|
|
52
49
|
// 7. Network Traffic
|
|
53
50
|
if (!foundMedia) {
|
|
@@ -57,9 +54,7 @@ async function main() {
|
|
|
57
54
|
}
|
|
58
55
|
// Phase 3: Redirect & Link Validation
|
|
59
56
|
console.log("\n--- Phase 3: Redirect & Link Validation ---");
|
|
60
|
-
console.log("
|
|
61
|
-
const links = await handleVideoDownloadLinkFinder({});
|
|
62
|
-
console.log("Page Video Links:", links);
|
|
57
|
+
console.log("Direct download link finding is now integrated into Advanced Video Extraction.");
|
|
63
58
|
// Phase 4: Fallback
|
|
64
59
|
console.log("\n--- Phase 4: Fallback / Manual Aid ---");
|
|
65
60
|
if (!foundMedia) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brave-real-browser-mcp-server",
|
|
3
|
-
"version": "2.17.
|
|
3
|
+
"version": "2.17.12",
|
|
4
4
|
"description": "Universal AI IDE MCP Server - Auto-detects and supports all AI IDEs (Claude Desktop, Cursor, Windsurf, Cline, Zed, VSCode, Qoder AI, etc.) with Brave browser automation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { getPageInstance } from '../browser-manager.js';
|
|
2
|
-
/**
|
|
3
|
-
* Extract raw video sources from <video> tags and <source> elements
|
|
4
|
-
*/
|
|
5
|
-
export async function handleVideoSourceExtractor(args) {
|
|
6
|
-
const { url } = args;
|
|
7
|
-
const page = getPageInstance();
|
|
8
|
-
if (!page)
|
|
9
|
-
throw new Error('Browser not initialized. Call browser_init first.');
|
|
10
|
-
if (url && page.url() !== url)
|
|
11
|
-
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
|
12
|
-
const sources = await page.evaluate(() => {
|
|
13
|
-
return Array.from(document.querySelectorAll('video')).map((v, i) => ({
|
|
14
|
-
index: i,
|
|
15
|
-
src: v.src,
|
|
16
|
-
currentSrc: v.currentSrc,
|
|
17
|
-
sources: Array.from(v.querySelectorAll('source')).map(s => ({ src: s.src, type: s.type })),
|
|
18
|
-
poster: v.poster
|
|
19
|
-
}));
|
|
20
|
-
});
|
|
21
|
-
return { content: [{ type: 'text', text: JSON.stringify(sources, null, 2) }] };
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Identify common video players and configuration
|
|
25
|
-
*/
|
|
26
|
-
export async function handleVideoPlayerFinder(args) {
|
|
27
|
-
const { url } = args;
|
|
28
|
-
const page = getPageInstance();
|
|
29
|
-
if (!page)
|
|
30
|
-
throw new Error('Browser not initialized. Call browser_init first.');
|
|
31
|
-
if (url && page.url() !== url)
|
|
32
|
-
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
|
33
|
-
const players = await page.evaluate(() => {
|
|
34
|
-
const detected = [];
|
|
35
|
-
// @ts-ignore
|
|
36
|
-
if (window.jwplayer)
|
|
37
|
-
detected.push('JWPlayer');
|
|
38
|
-
// @ts-ignore
|
|
39
|
-
if (window.videojs)
|
|
40
|
-
detected.push('VideoJS');
|
|
41
|
-
// Check for iframes
|
|
42
|
-
document.querySelectorAll('iframe').forEach(f => {
|
|
43
|
-
if (f.src.includes('youtube.com/embed'))
|
|
44
|
-
detected.push('YouTube Embed');
|
|
45
|
-
if (f.src.includes('vimeo.com'))
|
|
46
|
-
detected.push('Vimeo Embed');
|
|
47
|
-
});
|
|
48
|
-
return [...new Set(detected)];
|
|
49
|
-
});
|
|
50
|
-
return { content: [{ type: 'text', text: `Detected Players: ${players.join(', ') || 'None found'}` }] };
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Detect HLS (m3u8) / DASH (mpd) streams in network traffic
|
|
54
|
-
*/
|
|
55
|
-
export async function handleStreamDetector(args) {
|
|
56
|
-
const page = getPageInstance();
|
|
57
|
-
if (!page)
|
|
58
|
-
throw new Error('Browser not initialized. Call browser_init first.');
|
|
59
|
-
const duration = args.duration || 10000;
|
|
60
|
-
const streams = [];
|
|
61
|
-
const handler = (response) => {
|
|
62
|
-
const url = response.url();
|
|
63
|
-
if (url.includes('.m3u8') || url.includes('.mpd')) {
|
|
64
|
-
streams.push({ url, type: url.includes('.m3u8') ? 'HLS' : 'DASH', status: response.status() });
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
page.on('response', handler);
|
|
68
|
-
await new Promise(resolve => setTimeout(resolve, duration));
|
|
69
|
-
page.off('response', handler);
|
|
70
|
-
return { content: [{ type: 'text', text: JSON.stringify(streams, null, 2) }] };
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Trace URL redirects
|
|
74
|
-
*/
|
|
75
|
-
export async function handleRedirectTracer(args) {
|
|
76
|
-
const page = getPageInstance();
|
|
77
|
-
if (!page)
|
|
78
|
-
throw new Error('Browser not initialized. Call browser_init first.');
|
|
79
|
-
const chain = [];
|
|
80
|
-
const handler = (response) => {
|
|
81
|
-
if ([301, 302, 303, 307, 308].includes(response.status())) {
|
|
82
|
-
chain.push(`${response.url()} -> ${response.headers()['location']}`);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
page.on('response', handler);
|
|
86
|
-
await page.goto(args.url, { waitUntil: 'networkidle2' });
|
|
87
|
-
page.off('response', handler);
|
|
88
|
-
return { content: [{ type: 'text', text: JSON.stringify({ finalUrl: page.url(), redirectChain: chain }, null, 2) }] };
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Find direct video download links
|
|
92
|
-
*/
|
|
93
|
-
export async function handleVideoDownloadLinkFinder(args) {
|
|
94
|
-
const page = getPageInstance();
|
|
95
|
-
if (!page)
|
|
96
|
-
throw new Error('Browser not initialized. Call browser_init first.');
|
|
97
|
-
const exts = args.extensions || ['.mp4', '.mkv', '.avi', '.mov', '.webm'];
|
|
98
|
-
const links = await page.evaluate((extensions) => {
|
|
99
|
-
return Array.from(document.querySelectorAll('a'))
|
|
100
|
-
.filter(a => extensions.some(ext => a.href.toLowerCase().endsWith(ext)))
|
|
101
|
-
.map(a => ({ text: a.textContent, href: a.href }));
|
|
102
|
-
}, exts);
|
|
103
|
-
return { content: [{ type: 'text', text: JSON.stringify(links, null, 2) }] };
|
|
104
|
-
}
|
|
105
|
-
// --- Implementation of missing "Ghost" handlers required by index.ts ---
|
|
106
|
-
// Aliases or specific implementations
|
|
107
|
-
export const handleVideoLinkFinder = handleVideoDownloadLinkFinder;
|
|
108
|
-
export async function handleVideoDownloadButton(args) {
|
|
109
|
-
// Basic implementation trying to find "Download" buttons contextually
|
|
110
|
-
const page = getPageInstance();
|
|
111
|
-
if (!page)
|
|
112
|
-
throw new Error('Browser not initialized');
|
|
113
|
-
const downloadProbability = await page.evaluate(() => {
|
|
114
|
-
const buttons = Array.from(document.querySelectorAll('button, a'));
|
|
115
|
-
return buttons.filter(b => b.textContent?.toLowerCase().includes('download')).map(b => ({
|
|
116
|
-
text: b.textContent,
|
|
117
|
-
outerHTML: b.outerHTML.substring(0, 100)
|
|
118
|
-
}));
|
|
119
|
-
});
|
|
120
|
-
return { content: [{ type: 'text', text: JSON.stringify(downloadProbability, null, 2) }] };
|
|
121
|
-
}
|
|
122
|
-
export async function handleVideoPlayPushSource(args) {
|
|
123
|
-
return { content: [{ type: 'text', text: "Video Play Push Source detected (Simulated)" }] };
|
|
124
|
-
}
|
|
125
|
-
export async function handleVideoPlayButtonClick(args) {
|
|
126
|
-
const page = getPageInstance();
|
|
127
|
-
if (!page)
|
|
128
|
-
throw new Error('Browser not initialized');
|
|
129
|
-
// Try to click the first play button found
|
|
130
|
-
const clicked = await page.evaluate(() => {
|
|
131
|
-
const playBtn = document.querySelector('button[aria-label="Play"], .vjs-big-play-button, .ytp-play-button');
|
|
132
|
-
if (playBtn instanceof HTMLElement) {
|
|
133
|
-
playBtn.click();
|
|
134
|
-
return true;
|
|
135
|
-
}
|
|
136
|
-
return false;
|
|
137
|
-
});
|
|
138
|
-
return { content: [{ type: 'text', text: clicked ? "Clicked Play Button" : "No Play Button Found" }] };
|
|
139
|
-
}
|