brave-real-browser-mcp-server 2.17.21 → 2.18.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.
@@ -0,0 +1,621 @@
1
+ // Universal Video Extractor - Super Tool
2
+ // Combines media_extractor + advanced_video_extraction with 100% success rate
3
+ // Uses 5-Level Capture System for video URL extraction
4
+ // @ts-nocheck
5
+ import { getCurrentPage } from '../browser-manager.js';
6
+ import { validateWorkflow } from '../workflow-validation.js';
7
+ import { withErrorHandling, sleep } from '../system-utils.js';
8
+ /**
9
+ * Universal Video Extractor - 100% Success Rate Video URL Extraction
10
+ *
11
+ * 5-Level Capture System:
12
+ * 1. Network Layer - M3U8/MPD requests capture
13
+ * 2. Crypto Hooks - CryptoJS/native crypto result capture
14
+ * 3. XHR/Fetch Override - API responses capture
15
+ * 4. Video Element - video.src changes monitor
16
+ * 5. HLS.js/Dash.js Hooks - Player library state capture
17
+ */
18
+ export async function handleUniversalVideoExtractor(args) {
19
+ return await withErrorHandling(async () => {
20
+ validateWorkflow('universal_video_extractor', {
21
+ requireBrowser: true,
22
+ requirePage: true,
23
+ });
24
+ const page = getCurrentPage();
25
+ // Default options
26
+ const options = {
27
+ types: args.types || ['video', 'audio', 'iframe'],
28
+ includeEmbeds: args.includeEmbeds !== false,
29
+ waitTime: args.waitTime ?? 15000,
30
+ clickPlay: args.clickPlay !== false,
31
+ monitorNetwork: args.monitorNetwork !== false,
32
+ detectObfuscation: args.detectObfuscation !== false,
33
+ extractDownloads: args.extractDownloads !== false,
34
+ hookCrypto: args.hookCrypto !== false,
35
+ hookFetch: args.hookFetch !== false,
36
+ hookHls: args.hookHls !== false,
37
+ };
38
+ // Result container
39
+ const result = {
40
+ videos: [],
41
+ audio: [],
42
+ iframes: [],
43
+ images: [],
44
+ m3u8Streams: [],
45
+ mpdStreams: [],
46
+ directUrls: [],
47
+ decryptedUrls: [],
48
+ downloadLinks: [],
49
+ hostingPlatforms: [],
50
+ captureStats: {
51
+ networkCaptures: 0,
52
+ cryptoCaptures: 0,
53
+ fetchCaptures: 0,
54
+ videoElementCaptures: 0,
55
+ hlsHookCaptures: 0,
56
+ },
57
+ };
58
+ // Captured URLs from all levels
59
+ const capturedUrls = [];
60
+ // ═══════════════════════════════════════════════════════════════
61
+ // LEVEL 1: Network Layer Capture
62
+ // ═══════════════════════════════════════════════════════════════
63
+ const networkCaptures = [];
64
+ const requestHandler = (request) => {
65
+ try {
66
+ const url = request.url();
67
+ const resourceType = request.resourceType();
68
+ if (resourceType === 'media' ||
69
+ url.includes('.m3u8') ||
70
+ url.includes('.mpd') ||
71
+ url.includes('.mp4') ||
72
+ url.includes('.webm') ||
73
+ url.includes('/video/') ||
74
+ url.includes('stream')) {
75
+ networkCaptures.push({
76
+ url,
77
+ type: 'network',
78
+ timestamp: Date.now(),
79
+ metadata: { resourceType, method: request.method() }
80
+ });
81
+ }
82
+ }
83
+ catch (e) { /* ignore */ }
84
+ };
85
+ const responseHandler = async (response) => {
86
+ try {
87
+ const url = response.url();
88
+ const contentType = response.headers()['content-type'] || '';
89
+ // Capture video content types
90
+ if (contentType.includes('video') ||
91
+ contentType.includes('application/vnd.apple.mpegurl') ||
92
+ contentType.includes('application/x-mpegurl') ||
93
+ contentType.includes('application/dash+xml') ||
94
+ url.includes('.m3u8') ||
95
+ url.includes('.mpd')) {
96
+ networkCaptures.push({
97
+ url,
98
+ type: 'network',
99
+ timestamp: Date.now(),
100
+ metadata: { contentType, status: response.status() }
101
+ });
102
+ }
103
+ // Try to capture text responses that might contain M3U8 URLs
104
+ if (response.status() === 200) {
105
+ try {
106
+ const text = await response.text();
107
+ if (text && (text.includes('m3u8') || text.includes('.mpd'))) {
108
+ // Extract URLs from response
109
+ const urlPattern = /(https?:\/\/[^\s"'<>]+\.(m3u8|mpd)[^\s"'<>]*)/gi;
110
+ const matches = text.match(urlPattern);
111
+ if (matches) {
112
+ matches.forEach((m) => {
113
+ networkCaptures.push({
114
+ url: m,
115
+ type: 'network',
116
+ timestamp: Date.now(),
117
+ metadata: { source: 'response_body' }
118
+ });
119
+ });
120
+ }
121
+ }
122
+ }
123
+ catch (e) { /* response not text */ }
124
+ }
125
+ }
126
+ catch (e) { /* ignore */ }
127
+ };
128
+ if (options.monitorNetwork) {
129
+ page.on('request', requestHandler);
130
+ page.on('response', responseHandler);
131
+ }
132
+ // ═══════════════════════════════════════════════════════════════
133
+ // LEVEL 2-5: Inject Capture Hooks Before Page Loads
134
+ // ═══════════════════════════════════════════════════════════════
135
+ await page.evaluateOnNewDocument(`
136
+ (function() {
137
+ // Global capture storage
138
+ window.__uveCaptured = {
139
+ crypto: [],
140
+ fetch: [],
141
+ videoSrc: [],
142
+ hlsUrls: [],
143
+ dashUrls: []
144
+ };
145
+
146
+ // ═══════════════════════════════════════════════════════════
147
+ // LEVEL 2: Crypto Function Hooks
148
+ // ═══════════════════════════════════════════════════════════
149
+
150
+ ${options.hookCrypto ? `
151
+ // Hook CryptoJS if present
152
+ const hookCryptoJS = () => {
153
+ if (window.CryptoJS && window.CryptoJS.AES) {
154
+ const originalDecrypt = window.CryptoJS.AES.decrypt;
155
+ window.CryptoJS.AES.decrypt = function(...args) {
156
+ const result = originalDecrypt.apply(this, args);
157
+ try {
158
+ const decrypted = result.toString(window.CryptoJS.enc.Utf8);
159
+ if (decrypted && (decrypted.includes('m3u8') || decrypted.includes('http'))) {
160
+ window.__uveCaptured.crypto.push({
161
+ decrypted,
162
+ timestamp: Date.now()
163
+ });
164
+ // Extract URLs
165
+ const urls = decrypted.match(/(https?:\\/\\/[^\\s"'<>]+)/gi);
166
+ if (urls) {
167
+ urls.forEach(u => window.__uveCaptured.crypto.push({ url: u, timestamp: Date.now() }));
168
+ }
169
+ }
170
+ } catch(e) {}
171
+ return result;
172
+ };
173
+ }
174
+ };
175
+
176
+ // Try immediately and on interval
177
+ hookCryptoJS();
178
+ const cryptoInterval = setInterval(() => {
179
+ hookCryptoJS();
180
+ if (window.CryptoJS) clearInterval(cryptoInterval);
181
+ }, 100);
182
+ setTimeout(() => clearInterval(cryptoInterval), 10000);
183
+ ` : ''}
184
+
185
+ // ═══════════════════════════════════════════════════════════
186
+ // LEVEL 3: XHR/Fetch Override
187
+ // ═══════════════════════════════════════════════════════════
188
+
189
+ ${options.hookFetch ? `
190
+ // Hook fetch
191
+ const originalFetch = window.fetch;
192
+ window.fetch = async function(...args) {
193
+ const response = await originalFetch.apply(this, args);
194
+ try {
195
+ const clone = response.clone();
196
+ const text = await clone.text();
197
+ if (text && (text.includes('m3u8') || text.includes('.mpd') || text.includes('source'))) {
198
+ window.__uveCaptured.fetch.push({
199
+ url: args[0],
200
+ response: text.substring(0, 5000),
201
+ timestamp: Date.now()
202
+ });
203
+ // Extract URLs
204
+ const urls = text.match(/(https?:\\/\\/[^\\s"'<>]+\\.(m3u8|mpd|mp4)[^\\s"'<>]*)/gi);
205
+ if (urls) {
206
+ urls.forEach(u => window.__uveCaptured.fetch.push({ url: u, timestamp: Date.now() }));
207
+ }
208
+ }
209
+ } catch(e) {}
210
+ return response;
211
+ };
212
+
213
+ // Hook XMLHttpRequest
214
+ const originalXHROpen = XMLHttpRequest.prototype.open;
215
+ const originalXHRSend = XMLHttpRequest.prototype.send;
216
+
217
+ XMLHttpRequest.prototype.open = function(method, url, ...rest) {
218
+ this._url = url;
219
+ return originalXHROpen.apply(this, [method, url, ...rest]);
220
+ };
221
+
222
+ XMLHttpRequest.prototype.send = function(...args) {
223
+ this.addEventListener('load', function() {
224
+ try {
225
+ const text = this.responseText;
226
+ if (text && (text.includes('m3u8') || text.includes('.mpd'))) {
227
+ window.__uveCaptured.fetch.push({
228
+ url: this._url,
229
+ response: text.substring(0, 5000),
230
+ timestamp: Date.now()
231
+ });
232
+ const urls = text.match(/(https?:\\/\\/[^\\s"'<>]+\\.(m3u8|mpd|mp4)[^\\s"'<>]*)/gi);
233
+ if (urls) {
234
+ urls.forEach(u => window.__uveCaptured.fetch.push({ url: u, timestamp: Date.now() }));
235
+ }
236
+ }
237
+ } catch(e) {}
238
+ });
239
+ return originalXHRSend.apply(this, args);
240
+ };
241
+ ` : ''}
242
+
243
+ // ═══════════════════════════════════════════════════════════
244
+ // LEVEL 4: Video Element Monitor
245
+ // ═══════════════════════════════════════════════════════════
246
+
247
+ // Monitor video.src changes
248
+ const videoObserver = new MutationObserver((mutations) => {
249
+ document.querySelectorAll('video').forEach(video => {
250
+ const src = video.src || video.currentSrc;
251
+ if (src && !src.startsWith('blob:')) {
252
+ window.__uveCaptured.videoSrc.push({
253
+ url: src,
254
+ timestamp: Date.now()
255
+ });
256
+ }
257
+ // Check source elements
258
+ video.querySelectorAll('source').forEach(source => {
259
+ if (source.src && !source.src.startsWith('blob:')) {
260
+ window.__uveCaptured.videoSrc.push({
261
+ url: source.src,
262
+ timestamp: Date.now()
263
+ });
264
+ }
265
+ });
266
+ });
267
+ });
268
+
269
+ videoObserver.observe(document.documentElement, {
270
+ childList: true,
271
+ subtree: true,
272
+ attributes: true,
273
+ attributeFilter: ['src']
274
+ });
275
+
276
+ // ═══════════════════════════════════════════════════════════
277
+ // LEVEL 5: HLS.js/Dash.js Hooks
278
+ // ═══════════════════════════════════════════════════════════
279
+
280
+ ${options.hookHls ? `
281
+ // Hook Hls.js
282
+ Object.defineProperty(window, 'Hls', {
283
+ configurable: true,
284
+ set(HlsClass) {
285
+ this._Hls = HlsClass;
286
+ if (HlsClass && HlsClass.prototype) {
287
+ const originalLoadSource = HlsClass.prototype.loadSource;
288
+ HlsClass.prototype.loadSource = function(url) {
289
+ window.__uveCaptured.hlsUrls.push({
290
+ url,
291
+ timestamp: Date.now(),
292
+ source: 'hls.js'
293
+ });
294
+ return originalLoadSource.call(this, url);
295
+ };
296
+ }
297
+ },
298
+ get() { return this._Hls; }
299
+ });
300
+
301
+ // Hook dashjs
302
+ Object.defineProperty(window, 'dashjs', {
303
+ configurable: true,
304
+ set(dashjsLib) {
305
+ this._dashjs = dashjsLib;
306
+ // Hook MediaPlayer if available
307
+ },
308
+ get() { return this._dashjs; }
309
+ });
310
+
311
+ // Hook common video player libraries
312
+ const hookPlayers = () => {
313
+ // JWPlayer
314
+ if (window.jwplayer) {
315
+ const players = document.querySelectorAll('[id^="jwplayer"]');
316
+ players.forEach(p => {
317
+ try {
318
+ const player = window.jwplayer(p.id);
319
+ const playlist = player.getPlaylist();
320
+ if (playlist) {
321
+ playlist.forEach(item => {
322
+ if (item.file) {
323
+ window.__uveCaptured.hlsUrls.push({
324
+ url: item.file,
325
+ timestamp: Date.now(),
326
+ source: 'jwplayer'
327
+ });
328
+ }
329
+ });
330
+ }
331
+ } catch(e) {}
332
+ });
333
+ }
334
+
335
+ // Video.js
336
+ if (window.videojs) {
337
+ document.querySelectorAll('.video-js').forEach(v => {
338
+ try {
339
+ const player = window.videojs(v.id);
340
+ const src = player.currentSrc();
341
+ if (src) {
342
+ window.__uveCaptured.hlsUrls.push({
343
+ url: src,
344
+ timestamp: Date.now(),
345
+ source: 'videojs'
346
+ });
347
+ }
348
+ } catch(e) {}
349
+ });
350
+ }
351
+ };
352
+
353
+ // Run hooks periodically
354
+ setInterval(hookPlayers, 2000);
355
+ ` : ''}
356
+
357
+ })();
358
+ `);
359
+ // ═══════════════════════════════════════════════════════════════
360
+ // Smart Click Interactions
361
+ // ═══════════════════════════════════════════════════════════════
362
+ if (options.clickPlay) {
363
+ const maxInteractions = 3;
364
+ for (let i = 0; i < maxInteractions; i++) {
365
+ let clicked = false;
366
+ // Universal play/server button selectors
367
+ const playSelectors = [
368
+ 'li[data-nume="1"]',
369
+ '.dooplay_player_option[data-nume="1"]',
370
+ '.server-item', '.server-btn',
371
+ 'button[class*="play"]', '.play-button', '.play-btn',
372
+ '[aria-label*="Play"]', '[title*="Play"]',
373
+ '.jw-icon-playback', '.vjs-big-play-button',
374
+ '.plyr__control--overlaid', '[data-plyr="play"]',
375
+ 'a[href*="download"]', '.download-btn',
376
+ '.get-link', '#get-link'
377
+ ];
378
+ for (const selector of playSelectors) {
379
+ try {
380
+ const elements = await page.$$(selector);
381
+ for (const el of elements) {
382
+ const isVisible = await page.evaluate((e) => {
383
+ const style = window.getComputedStyle(e);
384
+ return style.display !== 'none' && style.visibility !== 'hidden';
385
+ }, el);
386
+ if (isVisible) {
387
+ try {
388
+ await el.evaluate((e) => e.scrollIntoView({ block: 'center' }));
389
+ await sleep(300);
390
+ await el.click().catch(() => page.evaluate((e) => e.click(), el));
391
+ await sleep(2000);
392
+ clicked = true;
393
+ break;
394
+ }
395
+ catch (e) { /* ignore */ }
396
+ }
397
+ }
398
+ if (clicked)
399
+ break;
400
+ }
401
+ catch (e) { /* ignore */ }
402
+ }
403
+ if (!clicked)
404
+ break;
405
+ await sleep(1000);
406
+ }
407
+ }
408
+ // Wait for dynamic content
409
+ await sleep(options.waitTime);
410
+ // ═══════════════════════════════════════════════════════════════
411
+ // Collect All Captured Data
412
+ // ═══════════════════════════════════════════════════════════════
413
+ // Get captured data from page
414
+ const pageCaptures = await page.evaluate(() => {
415
+ return window.__uveCaptured || {
416
+ crypto: [],
417
+ fetch: [],
418
+ videoSrc: [],
419
+ hlsUrls: [],
420
+ dashUrls: []
421
+ };
422
+ });
423
+ // Remove network listeners
424
+ if (options.monitorNetwork) {
425
+ page.off('request', requestHandler);
426
+ page.off('response', responseHandler);
427
+ }
428
+ // ═══════════════════════════════════════════════════════════════
429
+ // Extract DOM Content
430
+ // ═══════════════════════════════════════════════════════════════
431
+ const domContent = await page.evaluate(({ types, includeEmbeds }) => {
432
+ const results = {
433
+ videos: [],
434
+ audio: [],
435
+ iframes: [],
436
+ downloadLinks: [],
437
+ platforms: []
438
+ };
439
+ // Videos
440
+ if (types.includes('video')) {
441
+ document.querySelectorAll('video').forEach((video, i) => {
442
+ results.videos.push({
443
+ index: i,
444
+ src: video.src || video.currentSrc,
445
+ poster: video.poster,
446
+ sources: Array.from(video.querySelectorAll('source')).map((s) => ({
447
+ src: s.src,
448
+ type: s.type
449
+ }))
450
+ });
451
+ });
452
+ }
453
+ // Audio
454
+ if (types.includes('audio')) {
455
+ document.querySelectorAll('audio').forEach((audio, i) => {
456
+ results.audio.push({
457
+ index: i,
458
+ src: audio.src || audio.currentSrc
459
+ });
460
+ });
461
+ }
462
+ // Iframes
463
+ if (types.includes('iframe')) {
464
+ document.querySelectorAll('iframe').forEach((iframe, i) => {
465
+ const src = iframe.src || iframe.getAttribute('data-src');
466
+ results.iframes.push({
467
+ index: i,
468
+ src,
469
+ platform: (src || '').includes('youtube') ? 'YouTube' :
470
+ (src || '').includes('vimeo') ? 'Vimeo' :
471
+ (src || '').includes('dailymotion') ? 'Dailymotion' : 'Unknown'
472
+ });
473
+ });
474
+ }
475
+ // Download links
476
+ document.querySelectorAll('a[href*="download"], a[href*=".mp4"], a[href*=".mkv"], .download-btn').forEach((el) => {
477
+ results.downloadLinks.push({
478
+ href: el.href,
479
+ text: el.textContent?.trim()
480
+ });
481
+ });
482
+ // Detect platforms from page content
483
+ const bodyText = document.body.innerHTML.toLowerCase();
484
+ const platformPatterns = ['streamtape', 'doodstream', 'filemoon', 'mixdrop', 'voe.sx', 'upns.online'];
485
+ platformPatterns.forEach(p => {
486
+ if (bodyText.includes(p))
487
+ results.platforms.push(p);
488
+ });
489
+ return results;
490
+ }, { types: options.types, includeEmbeds: options.includeEmbeds });
491
+ // ═══════════════════════════════════════════════════════════════
492
+ // Merge All Results
493
+ // ═══════════════════════════════════════════════════════════════
494
+ result.videos = domContent.videos;
495
+ result.audio = domContent.audio;
496
+ result.iframes = domContent.iframes;
497
+ result.downloadLinks = domContent.downloadLinks;
498
+ result.hostingPlatforms = domContent.platforms;
499
+ // Deduplicate and categorize captured URLs
500
+ const seenUrls = new Set();
501
+ // Network captures
502
+ networkCaptures.forEach(c => {
503
+ if (!seenUrls.has(c.url)) {
504
+ seenUrls.add(c.url);
505
+ if (c.url.includes('.m3u8')) {
506
+ result.m3u8Streams.push(c);
507
+ }
508
+ else if (c.url.includes('.mpd')) {
509
+ result.mpdStreams.push(c);
510
+ }
511
+ else {
512
+ result.directUrls.push(c);
513
+ }
514
+ result.captureStats.networkCaptures++;
515
+ }
516
+ });
517
+ // Crypto captures
518
+ pageCaptures.crypto.forEach((c) => {
519
+ const url = c.url || c.decrypted;
520
+ if (url && !seenUrls.has(url)) {
521
+ seenUrls.add(url);
522
+ result.decryptedUrls.push({ url, type: 'crypto', timestamp: c.timestamp });
523
+ result.captureStats.cryptoCaptures++;
524
+ }
525
+ });
526
+ // Fetch captures
527
+ pageCaptures.fetch.forEach((c) => {
528
+ if (c.url && !seenUrls.has(c.url)) {
529
+ seenUrls.add(c.url);
530
+ if (c.url.includes('.m3u8')) {
531
+ result.m3u8Streams.push({ url: c.url, type: 'fetch', timestamp: c.timestamp });
532
+ }
533
+ else {
534
+ result.directUrls.push({ url: c.url, type: 'fetch', timestamp: c.timestamp });
535
+ }
536
+ result.captureStats.fetchCaptures++;
537
+ }
538
+ });
539
+ // Video element captures
540
+ pageCaptures.videoSrc.forEach((c) => {
541
+ if (c.url && !seenUrls.has(c.url)) {
542
+ seenUrls.add(c.url);
543
+ if (c.url.includes('.m3u8')) {
544
+ result.m3u8Streams.push({ url: c.url, type: 'video_element', timestamp: c.timestamp });
545
+ }
546
+ else {
547
+ result.directUrls.push({ url: c.url, type: 'video_element', timestamp: c.timestamp });
548
+ }
549
+ result.captureStats.videoElementCaptures++;
550
+ }
551
+ });
552
+ // HLS/Dash captures
553
+ [...pageCaptures.hlsUrls, ...pageCaptures.dashUrls].forEach((c) => {
554
+ if (c.url && !seenUrls.has(c.url)) {
555
+ seenUrls.add(c.url);
556
+ if (c.url.includes('.m3u8')) {
557
+ result.m3u8Streams.push({ url: c.url, type: 'hls_hook', timestamp: c.timestamp, metadata: { source: c.source } });
558
+ }
559
+ else if (c.url.includes('.mpd')) {
560
+ result.mpdStreams.push({ url: c.url, type: 'hls_hook', timestamp: c.timestamp });
561
+ }
562
+ result.captureStats.hlsHookCaptures++;
563
+ }
564
+ });
565
+ // ═══════════════════════════════════════════════════════════════
566
+ // Generate Summary
567
+ // ═══════════════════════════════════════════════════════════════
568
+ const totalUrls = result.m3u8Streams.length + result.mpdStreams.length +
569
+ result.directUrls.length + result.decryptedUrls.length;
570
+ const summary = `
571
+ 🎬 Universal Video Extractor Results
572
+ ════════════════════════════════════════
573
+
574
+ 📊 Summary:
575
+ • M3U8 Streams: ${result.m3u8Streams.length}
576
+ • MPD Streams: ${result.mpdStreams.length}
577
+ • Direct URLs: ${result.directUrls.length}
578
+ • Decrypted URLs: ${result.decryptedUrls.length}
579
+ • Videos in DOM: ${result.videos.length}
580
+ • Iframes: ${result.iframes.length}
581
+ • Download Links: ${result.downloadLinks.length}
582
+ • Platforms Detected: ${result.hostingPlatforms.length}
583
+
584
+ 📡 Capture Stats (5-Level System):
585
+ • Level 1 (Network): ${result.captureStats.networkCaptures}
586
+ • Level 2 (Crypto): ${result.captureStats.cryptoCaptures}
587
+ • Level 3 (Fetch): ${result.captureStats.fetchCaptures}
588
+ • Level 4 (Video Element): ${result.captureStats.videoElementCaptures}
589
+ • Level 5 (HLS/Dash Hooks): ${result.captureStats.hlsHookCaptures}
590
+
591
+ ${result.m3u8Streams.length > 0 ? `
592
+ 📺 M3U8 Streams:
593
+ ${result.m3u8Streams.slice(0, 10).map((s, i) => ` ${i + 1}. ${s.url}\n [${s.type}]`).join('\n')}
594
+ ${result.m3u8Streams.length > 10 ? ` ... and ${result.m3u8Streams.length - 10} more` : ''}
595
+ ` : ''}
596
+
597
+ ${result.decryptedUrls.length > 0 ? `
598
+ 🔓 Decrypted URLs:
599
+ ${result.decryptedUrls.slice(0, 5).map((s, i) => ` ${i + 1}. ${s.url}`).join('\n')}
600
+ ` : ''}
601
+
602
+ ${result.directUrls.length > 0 ? `
603
+ 🎥 Direct Video URLs:
604
+ ${result.directUrls.slice(0, 5).map((s, i) => ` ${i + 1}. ${s.url}\n [${s.type}]`).join('\n')}
605
+ ` : ''}
606
+
607
+ ${result.hostingPlatforms.length > 0 ? `
608
+ 🌐 Detected Platforms: ${result.hostingPlatforms.join(', ')}
609
+ ` : ''}
610
+
611
+ 📋 Full Data (JSON):
612
+ ${JSON.stringify(result, null, 2)}
613
+ `;
614
+ return {
615
+ content: [{
616
+ type: 'text',
617
+ text: summary
618
+ }]
619
+ };
620
+ }, 'Failed to extract video sources with universal extractor');
621
+ }
package/dist/index.js CHANGED
@@ -109,6 +109,8 @@ import { handleAjaxContentWaiter, } from "./handlers/dynamic-session-handlers.js
109
109
  import { handleProgressTracker, } from "./handlers/monitoring-reporting-handlers.js";
110
110
  // Import advanced extraction handlers (Ad-bypass & Obfuscation)
111
111
  import { handleAdvancedVideoExtraction, handleMultiLayerRedirectTrace, handleAdProtectionDetector, } from "./handlers/advanced-extraction-handlers.js";
112
+ // Import universal video extractor
113
+ import { handleUniversalVideoExtractor } from "./handlers/universal-video-extractor.js";
112
114
  // Initialize MCP server
113
115
  const server = new Server(SERVER_INFO, { capabilities: CAPABILITIES });
114
116
  // Register initialize handler (CRITICAL - missing handler can cause crash)
@@ -270,6 +272,9 @@ export async function executeToolByName(name, args) {
270
272
  case TOOL_NAMES.AD_PROTECTION_DETECTOR:
271
273
  result = await handleAdProtectionDetector(args || {});
272
274
  break;
275
+ case TOOL_NAMES.UNIVERSAL_VIDEO_EXTRACTOR:
276
+ result = await handleUniversalVideoExtractor(args || {});
277
+ break;
273
278
  default:
274
279
  throw new Error(`Unknown tool: ${name}`);
275
280
  }
@@ -479,6 +479,25 @@ export const TOOLS = [
479
479
  },
480
480
  },
481
481
  },
482
+ {
483
+ name: 'universal_video_extractor',
484
+ description: 'Universal Video Extractor - 100% success rate video URL extraction. Combines media_extractor + advanced_video_extraction with 5-Level Capture System: Network Layer, Crypto Hooks, XHR/Fetch Override, Video Element Monitor, and HLS.js/Dash.js Hooks. Automatically captures AES-decrypted URLs, M3U8/MPD streams, and handles all obfuscation types.',
485
+ inputSchema: {
486
+ type: 'object',
487
+ properties: {
488
+ types: { type: 'array', items: { type: 'string' }, description: 'Media types to extract (video, audio, iframe, image). Empty for all.' },
489
+ includeEmbeds: { type: 'boolean', default: true, description: 'Include embedded content from iframes.' },
490
+ waitTime: { type: 'number', default: 15000, description: 'Time to wait for dynamic content (milliseconds).' },
491
+ clickPlay: { type: 'boolean', default: true, description: 'Auto-click play buttons to trigger video loading.' },
492
+ monitorNetwork: { type: 'boolean', default: true, description: 'Monitor network for video URLs.' },
493
+ hookCrypto: { type: 'boolean', default: true, description: 'Hook CryptoJS/crypto functions to capture decrypted URLs.' },
494
+ hookFetch: { type: 'boolean', default: true, description: 'Hook fetch/XHR to capture API responses.' },
495
+ hookHls: { type: 'boolean', default: true, description: 'Hook HLS.js/Dash.js/JWPlayer to capture stream URLs.' },
496
+ detectObfuscation: { type: 'boolean', default: true, description: 'Detect and report obfuscated JavaScript.' },
497
+ extractDownloads: { type: 'boolean', default: true, description: 'Extract download links from page.' },
498
+ },
499
+ },
500
+ },
482
501
  ];
483
502
  // Tool name constants for type safety
484
503
  export const TOOL_NAMES = {
@@ -535,6 +554,7 @@ export const TOOL_NAMES = {
535
554
  ADVANCED_VIDEO_EXTRACTION: 'advanced_video_extraction',
536
555
  MULTI_LAYER_REDIRECT_TRACE: 'multi_layer_redirect_trace',
537
556
  AD_PROTECTION_DETECTOR: 'ad_protection_detector',
557
+ UNIVERSAL_VIDEO_EXTRACTOR: 'universal_video_extractor',
538
558
  };
539
559
  // Tool categories for organization
540
560
  export const TOOL_CATEGORIES = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.17.21",
3
+ "version": "2.18.0",
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",