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.
@@ -0,0 +1,483 @@
1
+ // Advanced Video Extraction Handlers
2
+ // Ad-Protection Bypass, Obfuscation Detection, Hidden Video Source Extraction
3
+ // @ts-nocheck
4
+ import { getCurrentPage } from '../browser-manager.js';
5
+ import { validateWorkflow } from '../workflow-validation.js';
6
+ import { withErrorHandling, sleep } from '../system-utils.js';
7
+ /**
8
+ * Advanced Video Source Extractor - Bypass ad-protection and extract all video sources
9
+ */
10
+ export async function handleAdvancedVideoExtraction(args) {
11
+ return await withErrorHandling(async () => {
12
+ validateWorkflow('advanced_video_extraction', {
13
+ requireBrowser: true,
14
+ requirePage: true,
15
+ });
16
+ const page = getCurrentPage();
17
+ const waitTime = args.waitTime || 10000;
18
+ // Collect all video-related data
19
+ const videoData = {
20
+ directVideoUrls: [],
21
+ m3u8Streams: [],
22
+ mpdStreams: [],
23
+ iframeSources: [],
24
+ obfuscatedUrls: [],
25
+ redirectChains: [],
26
+ hostingPlatforms: [],
27
+ downloadLinks: [],
28
+ timestamp: new Date().toISOString()
29
+ };
30
+ // Monitor network for video content
31
+ const networkRequests = [];
32
+ const requestHandler = (request) => {
33
+ const url = request.url();
34
+ const resourceType = request.resourceType();
35
+ networkRequests.push({
36
+ url,
37
+ method: request.method(),
38
+ resourceType,
39
+ headers: request.headers()
40
+ });
41
+ // Detect video URLs
42
+ if (resourceType === 'media' ||
43
+ url.includes('.mp4') ||
44
+ url.includes('.m3u8') ||
45
+ url.includes('.mpd') ||
46
+ url.includes('.webm') ||
47
+ url.includes('video')) {
48
+ videoData.directVideoUrls.push({
49
+ url,
50
+ type: resourceType,
51
+ detected: 'network_monitor'
52
+ });
53
+ }
54
+ };
55
+ const responseHandler = async (response) => {
56
+ try {
57
+ const url = response.url();
58
+ const contentType = response.headers()['content-type'] || '';
59
+ // Check for video content
60
+ if (contentType.includes('video') || contentType.includes('application/vnd.apple.mpegurl')) {
61
+ videoData.directVideoUrls.push({
62
+ url,
63
+ contentType,
64
+ status: response.status(),
65
+ detected: 'response_monitor'
66
+ });
67
+ }
68
+ // Check for m3u8 playlists
69
+ if (url.includes('.m3u8')) {
70
+ videoData.m3u8Streams.push({
71
+ url,
72
+ status: response.status(),
73
+ type: 'HLS'
74
+ });
75
+ }
76
+ // Check for DASH manifests
77
+ if (url.includes('.mpd')) {
78
+ videoData.mpdStreams.push({
79
+ url,
80
+ status: response.status(),
81
+ type: 'DASH'
82
+ });
83
+ }
84
+ }
85
+ catch (e) {
86
+ // Ignore errors
87
+ }
88
+ };
89
+ page.on('request', requestHandler);
90
+ page.on('response', responseHandler);
91
+ // Extract page content
92
+ const pageAnalysis = await page.evaluate(() => {
93
+ const results = {
94
+ iframes: [],
95
+ videoElements: [],
96
+ obfuscatedScripts: [],
97
+ possibleHosts: [],
98
+ downloadButtons: []
99
+ };
100
+ // 1. Extract all iframes
101
+ document.querySelectorAll('iframe').forEach((iframe) => {
102
+ results.iframes.push({
103
+ src: iframe.src,
104
+ dataSrc: iframe.getAttribute('data-src'),
105
+ id: iframe.id,
106
+ className: iframe.className
107
+ });
108
+ });
109
+ // 2. Extract video elements
110
+ document.querySelectorAll('video').forEach((video) => {
111
+ const sources = [];
112
+ if (video.src)
113
+ sources.push({ src: video.src, type: 'direct' });
114
+ video.querySelectorAll('source').forEach((source) => {
115
+ sources.push({
116
+ src: source.src,
117
+ type: source.type,
118
+ quality: source.dataset.quality
119
+ });
120
+ });
121
+ results.videoElements.push({
122
+ sources,
123
+ poster: video.poster,
124
+ currentSrc: video.currentSrc
125
+ });
126
+ });
127
+ // 3. Detect obfuscated JavaScript
128
+ document.querySelectorAll('script').forEach((script) => {
129
+ const content = script.textContent || '';
130
+ // Check for common obfuscation patterns
131
+ if (content.includes('eval(') ||
132
+ content.includes('atob(') ||
133
+ content.includes('\\x') ||
134
+ content.match(/0x[0-9a-f]{4}/gi) ||
135
+ content.includes('_0x')) {
136
+ // Try to extract URLs from obfuscated code
137
+ const urlPatterns = [
138
+ /https?:\/\/[^\s"']+\.m3u8[^\s"']*/gi,
139
+ /https?:\/\/[^\s"']+\.mp4[^\s"']*/gi,
140
+ /https?:\/\/[^\s"']+\.mpd[^\s"']*/gi,
141
+ /https?:\/\/[^\s"']+video[^\s"']*/gi
142
+ ];
143
+ const foundUrls = [];
144
+ urlPatterns.forEach(pattern => {
145
+ const matches = content.match(pattern);
146
+ if (matches)
147
+ foundUrls.push(...matches);
148
+ });
149
+ results.obfuscatedScripts.push({
150
+ hasObfuscation: true,
151
+ patterns: {
152
+ hasEval: content.includes('eval('),
153
+ hasAtob: content.includes('atob('),
154
+ hasHexEncoding: content.includes('\\x'),
155
+ hasObfuscatedVars: content.includes('_0x')
156
+ },
157
+ extractedUrls: foundUrls,
158
+ snippet: content.substring(0, 200)
159
+ });
160
+ }
161
+ // Extract video hosting domains
162
+ const hostPatterns = [
163
+ /streamtape/gi,
164
+ /doodstream/gi,
165
+ /filemoon/gi,
166
+ /streamwish/gi,
167
+ /mixdrop/gi,
168
+ /upstream/gi,
169
+ /voe\.sx/gi,
170
+ /streamlare/gi,
171
+ /upns\.online/gi
172
+ ];
173
+ hostPatterns.forEach(pattern => {
174
+ if (pattern.test(content)) {
175
+ results.possibleHosts.push(pattern.source);
176
+ }
177
+ });
178
+ });
179
+ // 4. Find download buttons and links
180
+ const downloadSelectors = [
181
+ 'a[download]',
182
+ 'a[href*="download"]',
183
+ 'button[data-download]',
184
+ 'a[href*=".mp4"]',
185
+ 'a[href*=".mkv"]',
186
+ 'a[class*="download"]'
187
+ ];
188
+ downloadSelectors.forEach(selector => {
189
+ document.querySelectorAll(selector).forEach((el) => {
190
+ results.downloadButtons.push({
191
+ href: el.href || el.getAttribute('href'),
192
+ text: el.textContent?.trim(),
193
+ selector
194
+ });
195
+ });
196
+ });
197
+ return results;
198
+ });
199
+ // Merge page analysis into videoData
200
+ videoData.iframeSources = pageAnalysis.iframes;
201
+ videoData.obfuscatedUrls = pageAnalysis.obfuscatedScripts;
202
+ videoData.hostingPlatforms = [...new Set(pageAnalysis.possibleHosts)];
203
+ videoData.downloadLinks = pageAnalysis.downloadButtons;
204
+ // Wait for dynamic content
205
+ await sleep(waitTime);
206
+ // Try to click play buttons to trigger video loading
207
+ try {
208
+ const playButton = await page.$('button[class*="play"], .play-button, [aria-label*="Play"]');
209
+ if (playButton) {
210
+ await playButton.click();
211
+ await sleep(3000);
212
+ }
213
+ }
214
+ catch (e) {
215
+ // Play button not found or not clickable
216
+ }
217
+ page.off('request', requestHandler);
218
+ page.off('response', responseHandler);
219
+ // Deduplicate URLs
220
+ videoData.directVideoUrls = [...new Map(videoData.directVideoUrls.map((item) => [item.url, item])).values()];
221
+ videoData.m3u8Streams = [...new Map(videoData.m3u8Streams.map((item) => [item.url, item])).values()];
222
+ // Create summary
223
+ const summary = `
224
+ 🎬 Advanced Video Extraction Results
225
+ ════════════════════════════════════
226
+
227
+ 📊 Summary:
228
+ • Direct Video URLs: ${videoData.directVideoUrls.length}
229
+ • HLS Streams (m3u8): ${videoData.m3u8Streams.length}
230
+ • DASH Streams (mpd): ${videoData.mpdStreams.length}
231
+ • IFrame Sources: ${videoData.iframeSources.length}
232
+ • Obfuscated Scripts: ${videoData.obfuscatedUrls.length}
233
+ • Download Links: ${videoData.downloadLinks.length}
234
+ • Detected Platforms: ${videoData.hostingPlatforms.length}
235
+
236
+ ${videoData.directVideoUrls.length > 0 ? `
237
+ 🎥 Direct Video URLs:
238
+ ${videoData.directVideoUrls.map((v, i) => ` ${i + 1}. ${v.url}\n Type: ${v.type || 'unknown'}\n Detected: ${v.detected}`).join('\n')}
239
+ ` : ''}
240
+
241
+ ${videoData.m3u8Streams.length > 0 ? `
242
+ 📺 HLS Streams:
243
+ ${videoData.m3u8Streams.map((s, i) => ` ${i + 1}. ${s.url}`).join('\n')}
244
+ ` : ''}
245
+
246
+ ${videoData.iframeSources.length > 0 ? `
247
+ 🔗 IFrame Sources:
248
+ ${videoData.iframeSources.map((f, i) => ` ${i + 1}. ${f.src || f.dataSrc}`).join('\n')}
249
+ ` : ''}
250
+
251
+ ${videoData.hostingPlatforms.length > 0 ? `
252
+ 🌐 Detected Hosting Platforms:
253
+ ${videoData.hostingPlatforms.map((h, i) => ` ${i + 1}. ${h}`).join('\n')}
254
+ ` : ''}
255
+
256
+ ${videoData.obfuscatedUrls.length > 0 ? `
257
+ 🔐 Obfuscated Content Detected:
258
+ • Scripts with obfuscation: ${videoData.obfuscatedUrls.length}
259
+ • URLs extracted from obfuscated code: ${videoData.obfuscatedUrls.reduce((acc, s) => acc + (s.extractedUrls?.length || 0), 0)}
260
+ ` : ''}
261
+
262
+ ${videoData.downloadLinks.length > 0 ? `
263
+ ⬇️ Download Links:
264
+ ${videoData.downloadLinks.slice(0, 5).map((d, i) => ` ${i + 1}. ${d.text}: ${d.href}`).join('\n')}
265
+ ${videoData.downloadLinks.length > 5 ? ` ... and ${videoData.downloadLinks.length - 5} more` : ''}
266
+ ` : ''}
267
+
268
+ 📋 Full Data (JSON):
269
+ ${JSON.stringify(videoData, null, 2)}
270
+ `;
271
+ return {
272
+ content: [{
273
+ type: 'text',
274
+ text: summary
275
+ }]
276
+ };
277
+ }, 'Failed to extract advanced video sources');
278
+ }
279
+ /**
280
+ * Deobfuscate JavaScript - Attempt to decode obfuscated JavaScript
281
+ */
282
+ export async function handleDeobfuscateJS(args) {
283
+ return await withErrorHandling(async () => {
284
+ validateWorkflow('deobfuscate_js', {
285
+ requireBrowser: true,
286
+ requirePage: true,
287
+ });
288
+ const page = getCurrentPage();
289
+ const deobfuscationResults = await page.evaluate(() => {
290
+ const results = [];
291
+ document.querySelectorAll('script').forEach((script, index) => {
292
+ const content = script.textContent || '';
293
+ if (content.length < 100)
294
+ return;
295
+ const analysis = {
296
+ scriptIndex: index,
297
+ obfuscationType: [],
298
+ extractedData: {
299
+ urls: [],
300
+ domains: [],
301
+ apiKeys: [],
302
+ base64Strings: []
303
+ }
304
+ };
305
+ // Detect obfuscation types
306
+ if (content.includes('eval('))
307
+ analysis.obfuscationType.push('eval');
308
+ if (content.includes('atob('))
309
+ analysis.obfuscationType.push('base64');
310
+ if (content.match(/0x[0-9a-f]{4}/gi))
311
+ analysis.obfuscationType.push('hex');
312
+ if (content.match(/_0x[0-9a-f]+/gi))
313
+ analysis.obfuscationType.push('identifier_obfuscation');
314
+ if (content.includes('\\x'))
315
+ analysis.obfuscationType.push('hex_escape');
316
+ if (analysis.obfuscationType.length === 0)
317
+ return;
318
+ // Extract URLs
319
+ const urlPattern = /https?:\/\/[^\s"'<>]+/gi;
320
+ const urls = content.match(urlPattern);
321
+ if (urls) {
322
+ analysis.extractedData.urls = [...new Set(urls)];
323
+ }
324
+ // Extract base64 encoded strings
325
+ const base64Pattern = /["']([A-Za-z0-9+/]{20,}={0,2})["']/g;
326
+ let match;
327
+ while ((match = base64Pattern.exec(content)) !== null) {
328
+ try {
329
+ const decoded = atob(match[1]);
330
+ if (decoded.includes('http') || decoded.includes('video') || decoded.includes('.m3u8')) {
331
+ analysis.extractedData.base64Strings.push({
332
+ original: match[1].substring(0, 50) + '...',
333
+ decoded: decoded.substring(0, 200)
334
+ });
335
+ }
336
+ }
337
+ catch (e) {
338
+ // Not valid base64
339
+ }
340
+ }
341
+ // Extract potential domains
342
+ const domainPattern = /[a-z0-9][a-z0-9-]*\.(com|net|org|io|tv|online|xyz|cc)/gi;
343
+ const domains = content.match(domainPattern);
344
+ if (domains) {
345
+ analysis.extractedData.domains = [...new Set(domains)];
346
+ }
347
+ results.push(analysis);
348
+ });
349
+ return results.filter(r => r.obfuscationType.length > 0);
350
+ });
351
+ return {
352
+ content: [{
353
+ type: 'text',
354
+ text: `🔓 Deobfuscation Results:\n\nFound ${deobfuscationResults.length} obfuscated scripts\n\n${JSON.stringify(deobfuscationResults, null, 2)}`
355
+ }]
356
+ };
357
+ }, 'Failed to deobfuscate JavaScript');
358
+ }
359
+ /**
360
+ * Multi-Layer Redirect Tracer - Follow multiple redirect layers to find final video source
361
+ */
362
+ export async function handleMultiLayerRedirectTrace(args) {
363
+ return await withErrorHandling(async () => {
364
+ validateWorkflow('multi_layer_redirect_trace', {
365
+ requireBrowser: true,
366
+ requirePage: true,
367
+ });
368
+ const page = getCurrentPage();
369
+ const startUrl = args.url;
370
+ const maxDepth = args.maxDepth || 5;
371
+ if (!startUrl) {
372
+ throw new Error('URL is required');
373
+ }
374
+ const redirectChain = [];
375
+ let currentDepth = 0;
376
+ let currentUrl = startUrl;
377
+ while (currentDepth < maxDepth) {
378
+ try {
379
+ const allRequests = [];
380
+ const responseHandler = (response) => {
381
+ allRequests.push({
382
+ url: response.url(),
383
+ status: response.status(),
384
+ redirected: response.request().redirectChain().length > 0,
385
+ finalUrl: response.url()
386
+ });
387
+ };
388
+ page.on('response', responseHandler);
389
+ await page.goto(currentUrl, {
390
+ waitUntil: 'networkidle0',
391
+ timeout: 30000
392
+ });
393
+ await sleep(2000);
394
+ page.off('response', responseHandler);
395
+ const finalUrl = page.url();
396
+ // Check for iframe redirects
397
+ const iframes = await page.evaluate(() => {
398
+ return Array.from(document.querySelectorAll('iframe')).map((f) => f.src);
399
+ });
400
+ redirectChain.push({
401
+ depth: currentDepth,
402
+ startUrl: currentUrl,
403
+ finalUrl,
404
+ iframes,
405
+ requests: allRequests.length
406
+ });
407
+ // If we found an iframe, follow it
408
+ if (iframes.length > 0 && iframes[0] !== currentUrl) {
409
+ currentUrl = iframes[0];
410
+ currentDepth++;
411
+ }
412
+ else {
413
+ break;
414
+ }
415
+ }
416
+ catch (e) {
417
+ redirectChain.push({
418
+ depth: currentDepth,
419
+ error: e.message
420
+ });
421
+ break;
422
+ }
423
+ }
424
+ return {
425
+ content: [{
426
+ type: 'text',
427
+ text: `🔄 Multi-Layer Redirect Trace:\n\nTotal Layers: ${redirectChain.length}\nMax Depth Reached: ${currentDepth >= maxDepth}\n\n${JSON.stringify(redirectChain, null, 2)}`
428
+ }]
429
+ };
430
+ }, 'Failed to trace multi-layer redirects');
431
+ }
432
+ /**
433
+ * Ad Blocker Detector - Detect and report ad-protection mechanisms
434
+ */
435
+ export async function handleAdProtectionDetector(args) {
436
+ return await withErrorHandling(async () => {
437
+ validateWorkflow('ad_protection_detector', {
438
+ requireBrowser: true,
439
+ requirePage: true,
440
+ });
441
+ const page = getCurrentPage();
442
+ const adProtection = await page.evaluate(() => {
443
+ const results = {
444
+ adBlockDetection: false,
445
+ antiDebugger: false,
446
+ obfuscatedCode: false,
447
+ popupLayers: 0,
448
+ hiddenElements: 0,
449
+ suspiciousScripts: []
450
+ };
451
+ // Check for common ad-block detection
452
+ const adBlockIndicators = [
453
+ 'adblock',
454
+ 'ublock',
455
+ 'adguard',
456
+ 'please disable',
457
+ 'turn off ad blocker'
458
+ ];
459
+ const bodyText = document.body.textContent?.toLowerCase() || '';
460
+ results.adBlockDetection = adBlockIndicators.some(indicator => bodyText.includes(indicator));
461
+ // Check for anti-debugger code
462
+ document.querySelectorAll('script').forEach((script) => {
463
+ const content = script.textContent || '';
464
+ if (content.includes('debugger') || content.includes('devtools')) {
465
+ results.antiDebugger = true;
466
+ }
467
+ });
468
+ // Count popup layers
469
+ const overlays = document.querySelectorAll('[style*="position: fixed"], [style*="z-index"]');
470
+ results.popupLayers = overlays.length;
471
+ // Check for hidden elements
472
+ const hidden = document.querySelectorAll('[style*="display: none"], [style*="visibility: hidden"]');
473
+ results.hiddenElements = hidden.length;
474
+ return results;
475
+ });
476
+ return {
477
+ content: [{
478
+ type: 'text',
479
+ text: `🛡️ Ad Protection Analysis:\n\n${JSON.stringify(adProtection, null, 2)}`
480
+ }]
481
+ };
482
+ }, 'Failed to detect ad protection');
483
+ }