real-browser-mcp-server 1.3.4 → 1.4.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.
Files changed (118) hide show
  1. package/README.md +96 -9
  2. package/dist/lib/cjs/index.d.ts +2 -0
  3. package/dist/lib/cjs/index.d.ts.map +1 -0
  4. package/dist/lib/cjs/index.js +386 -0
  5. package/dist/lib/cjs/index.js.map +1 -0
  6. package/dist/lib/cjs/module/pageController.d.ts +2 -0
  7. package/dist/lib/cjs/module/pageController.d.ts.map +1 -0
  8. package/{lib → dist/lib}/cjs/module/pageController.js +28 -29
  9. package/dist/lib/cjs/module/pageController.js.map +1 -0
  10. package/dist/lib/cjs/module/turnstile.d.ts +2 -0
  11. package/dist/lib/cjs/module/turnstile.d.ts.map +1 -0
  12. package/{lib → dist/lib}/cjs/module/turnstile.js +24 -12
  13. package/dist/lib/cjs/module/turnstile.js.map +1 -0
  14. package/dist/src/index.d.ts +11 -0
  15. package/dist/src/index.d.ts.map +1 -0
  16. package/dist/src/index.js +118 -0
  17. package/dist/src/index.js.map +1 -0
  18. package/dist/src/mcp/handlers/browser.d.ts +30 -0
  19. package/dist/src/mcp/handlers/browser.d.ts.map +1 -0
  20. package/dist/src/mcp/handlers/browser.js +232 -0
  21. package/dist/src/mcp/handlers/browser.js.map +1 -0
  22. package/dist/src/mcp/handlers/dom.d.ts +149 -0
  23. package/dist/src/mcp/handlers/dom.d.ts.map +1 -0
  24. package/dist/src/mcp/handlers/dom.js +577 -0
  25. package/dist/src/mcp/handlers/dom.js.map +1 -0
  26. package/dist/src/mcp/handlers/extract.d.ts +59 -0
  27. package/dist/src/mcp/handlers/extract.d.ts.map +1 -0
  28. package/dist/src/mcp/handlers/extract.js +455 -0
  29. package/dist/src/mcp/handlers/extract.js.map +1 -0
  30. package/dist/src/mcp/handlers/form-handlers.d.ts +9 -0
  31. package/dist/src/mcp/handlers/form-handlers.d.ts.map +1 -0
  32. package/dist/src/mcp/handlers/form-handlers.js +56 -0
  33. package/dist/src/mcp/handlers/form-handlers.js.map +1 -0
  34. package/dist/src/mcp/handlers/helpers.d.ts +47 -0
  35. package/dist/src/mcp/handlers/helpers.d.ts.map +1 -0
  36. package/dist/src/mcp/handlers/helpers.js +515 -0
  37. package/dist/src/mcp/handlers/helpers.js.map +1 -0
  38. package/dist/src/mcp/handlers/index.d.ts +6 -0
  39. package/dist/src/mcp/handlers/index.d.ts.map +1 -0
  40. package/dist/src/mcp/handlers/index.js +61 -0
  41. package/dist/src/mcp/handlers/index.js.map +1 -0
  42. package/dist/src/mcp/handlers/media-handlers.d.ts +10 -0
  43. package/dist/src/mcp/handlers/media-handlers.d.ts.map +1 -0
  44. package/dist/src/mcp/handlers/media-handlers.js +535 -0
  45. package/dist/src/mcp/handlers/media-handlers.js.map +1 -0
  46. package/dist/src/mcp/handlers/network.d.ts +147 -0
  47. package/dist/src/mcp/handlers/network.d.ts.map +1 -0
  48. package/dist/src/mcp/handlers/network.js +1135 -0
  49. package/dist/src/mcp/handlers/network.js.map +1 -0
  50. package/dist/src/mcp/handlers/state.d.ts +34 -0
  51. package/dist/src/mcp/handlers/state.d.ts.map +1 -0
  52. package/dist/src/mcp/handlers/state.js +226 -0
  53. package/dist/src/mcp/handlers/state.js.map +1 -0
  54. package/dist/src/mcp/handlers/utility-handlers.d.ts +167 -0
  55. package/dist/src/mcp/handlers/utility-handlers.d.ts.map +1 -0
  56. package/dist/src/mcp/handlers/utility-handlers.js +280 -0
  57. package/dist/src/mcp/handlers/utility-handlers.js.map +1 -0
  58. package/dist/src/mcp/handlers/vision.d.ts +127 -0
  59. package/dist/src/mcp/handlers/vision.d.ts.map +1 -0
  60. package/dist/src/mcp/handlers/vision.js +549 -0
  61. package/dist/src/mcp/handlers/vision.js.map +1 -0
  62. package/dist/src/mcp/index.d.ts +3 -0
  63. package/dist/src/mcp/index.d.ts.map +1 -0
  64. package/dist/src/mcp/index.js +166 -0
  65. package/dist/src/mcp/index.js.map +1 -0
  66. package/dist/src/mcp/server.d.ts +2 -0
  67. package/dist/src/mcp/server.d.ts.map +1 -0
  68. package/dist/src/mcp/server.js +117 -0
  69. package/dist/src/mcp/server.js.map +1 -0
  70. package/dist/src/mcp/tools.d.ts +8 -0
  71. package/dist/src/mcp/tools.d.ts.map +1 -0
  72. package/{src → dist/src}/mcp/tools.js +12 -11
  73. package/dist/src/mcp/tools.js.map +1 -0
  74. package/dist/src/shared/cache-manager.d.ts +80 -0
  75. package/dist/src/shared/cache-manager.d.ts.map +1 -0
  76. package/dist/src/shared/cache-manager.js +221 -0
  77. package/dist/src/shared/cache-manager.js.map +1 -0
  78. package/dist/src/shared/tools.d.ts +2 -0
  79. package/dist/src/shared/tools.d.ts.map +1 -0
  80. package/dist/src/shared/tools.js +606 -0
  81. package/dist/src/shared/tools.js.map +1 -0
  82. package/dist/src/types.d.ts +376 -0
  83. package/dist/src/types.d.ts.map +1 -0
  84. package/dist/src/types.js +9 -0
  85. package/dist/src/types.js.map +1 -0
  86. package/dist/test/cjs/test.d.ts +11 -0
  87. package/dist/test/cjs/test.d.ts.map +1 -0
  88. package/dist/test/cjs/test.js +289 -0
  89. package/dist/test/cjs/test.js.map +1 -0
  90. package/dist/test/mcp/smoke-test.d.ts +29 -0
  91. package/dist/test/mcp/smoke-test.d.ts.map +1 -0
  92. package/dist/test/mcp/smoke-test.js +132 -0
  93. package/dist/test/mcp/smoke-test.js.map +1 -0
  94. package/lib/esm/index.mjs +229 -184
  95. package/lib/esm/module/pageController.mjs +21 -18
  96. package/lib/esm/module/turnstile.mjs +7 -0
  97. package/package.json +25 -16
  98. package/typings.d.ts +7 -1
  99. package/.github/ISSUE_TEMPLATE/general_issue.yaml +0 -58
  100. package/.github/SETUP.md +0 -111
  101. package/.github/workflows/publish.yml +0 -135
  102. package/Dockerfile +0 -79
  103. package/lib/cjs/adblocker.bin +0 -0
  104. package/lib/cjs/index.js +0 -352
  105. package/src/ai/action-parser.js +0 -274
  106. package/src/ai/core.js +0 -378
  107. package/src/ai/element-finder.js +0 -466
  108. package/src/ai/index.js +0 -82
  109. package/src/ai/page-analyzer.js +0 -304
  110. package/src/ai/selector-healer.js +0 -236
  111. package/src/index.js +0 -121
  112. package/src/mcp/handlers.js +0 -5071
  113. package/src/mcp/index.js +0 -190
  114. package/src/mcp/server.js +0 -144
  115. package/src/shared/tools.js +0 -618
  116. package/test/cjs/test.js +0 -267
  117. package/test/esm/package.json +0 -13
  118. package/test/esm/test.js +0 -235
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.globalCache = exports.getHeadlessFromEnv = exports.notifyProgress = exports.setProgressCallback = exports.requireBrowser = exports.getState = exports.handlers = void 0;
4
+ exports.executeTool = executeTool;
5
+ exports.cleanup = cleanup;
6
+ // Handler aggregation hub
7
+ const browser_1 = require("./browser");
8
+ const dom_1 = require("./dom");
9
+ const network_1 = require("./network");
10
+ const vision_1 = require("./vision");
11
+ const extract_1 = require("./extract");
12
+ const helpers_1 = require("./helpers");
13
+ const utility_handlers_1 = require("./utility-handlers");
14
+ const media_handlers_1 = require("./media-handlers");
15
+ const form_handlers_1 = require("./form-handlers");
16
+ const state_1 = require("./state");
17
+ Object.defineProperty(exports, "setProgressCallback", { enumerable: true, get: function () { return state_1.setProgressCallback; } });
18
+ Object.defineProperty(exports, "notifyProgress", { enumerable: true, get: function () { return state_1.notifyProgress; } });
19
+ Object.defineProperty(exports, "getHeadlessFromEnv", { enumerable: true, get: function () { return state_1.getHeadlessFromEnv; } });
20
+ Object.defineProperty(exports, "getState", { enumerable: true, get: function () { return state_1.getState; } });
21
+ Object.defineProperty(exports, "requireBrowser", { enumerable: true, get: function () { return state_1.requireBrowser; } });
22
+ Object.defineProperty(exports, "globalCache", { enumerable: true, get: function () { return state_1.globalCache; } });
23
+ exports.handlers = {
24
+ ...browser_1.browserHandlers,
25
+ ...dom_1.domHandlers,
26
+ ...network_1.networkHandlers,
27
+ ...vision_1.visionHandlers,
28
+ ...extract_1.extractHandlers,
29
+ ...helpers_1.helpersHandlers,
30
+ ...utility_handlers_1.utilityHandlers,
31
+ ...media_handlers_1.mediaHandlers,
32
+ ...form_handlers_1.formHandlers
33
+ };
34
+ async function executeTool(name, args = {}) {
35
+ if (exports.handlers[name]) {
36
+ try {
37
+ return await exports.handlers[name](args);
38
+ }
39
+ catch (error) {
40
+ return { success: false, error: error.message };
41
+ }
42
+ }
43
+ return { success: false, error: `Tool ${name} not implemented` };
44
+ }
45
+ async function cleanup() {
46
+ if (state_1.state.browserInstance) {
47
+ try {
48
+ await state_1.state.browserInstance.close();
49
+ }
50
+ catch (e) {
51
+ if (typeof state_1.state.browserInstance.process === 'function') {
52
+ state_1.state.browserInstance.process()?.kill('SIGKILL');
53
+ }
54
+ }
55
+ state_1.state.browserInstance = null;
56
+ state_1.state.pageInstance = null;
57
+ state_1.state.blockerInstance = null;
58
+ state_1.state.setupPageFn = null;
59
+ }
60
+ }
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/mcp/handlers/index.ts"],"names":[],"mappings":";;;AAuBA,kCASC;AAED,0BAcC;AAhDD,0BAA0B;AAC1B,uCAA4C;AAC5C,+BAAoC;AACpC,uCAA4C;AAC5C,qCAA0C;AAC1C,uCAA4C;AAC5C,uCAA4C;AAC5C,yDAAqD;AACrD,qDAAiD;AACjD,mDAA+C;AAC/C,mCAAgI;AAwC7F,oGAxCnB,2BAAmB,OAwCmB;AAAE,+FAxCnB,sBAAc,OAwCmB;AAAE,mGAxCnB,0BAAkB,OAwCmB;AAAjF,yFAxCgE,gBAAQ,OAwChE;AAAE,+FAxCgE,sBAAc,OAwChE;AAA2D,4FAxCO,mBAAW,OAwCP;AAvC1F,QAAA,QAAQ,GAAQ;IAC3B,GAAG,yBAAe;IAClB,GAAG,iBAAW;IACd,GAAG,yBAAe;IAClB,GAAG,uBAAc;IACjB,GAAG,yBAAe;IAClB,GAAG,yBAAe;IAClB,GAAG,kCAAe;IAClB,GAAG,8BAAa;IAChB,GAAG,4BAAY;CAChB,CAAC;AAEK,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,OAAY,EAAE;IAC5D,IAAI,gBAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,OAAO,MAAM,gBAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAClD,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,IAAI,kBAAkB,EAAE,CAAC;AACnE,CAAC;AAEM,KAAK,UAAU,OAAO;IAC3B,IAAI,aAAK,CAAC,eAAe,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,aAAK,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QACtC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,OAAQ,aAAK,CAAC,eAAuB,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;gBAChE,aAAK,CAAC,eAAuB,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QACD,aAAK,CAAC,eAAe,GAAG,IAAI,CAAC;QAC7B,aAAK,CAAC,YAAY,GAAG,IAAI,CAAC;QAC1B,aAAK,CAAC,eAAe,GAAG,IAAI,CAAC;QAC7B,aAAK,CAAC,WAAW,GAAG,IAAI,CAAC;IAC3B,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ export declare const mediaHandlers: {
2
+ stream_extractor(params?: any): Promise<{
3
+ success: boolean;
4
+ streams: any;
5
+ totalCount: number;
6
+ }>;
7
+ player_api_hook(params?: any): Promise<any>;
8
+ media_extractor(params?: any): Promise<any>;
9
+ };
10
+ //# sourceMappingURL=media-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-handlers.d.ts","sourceRoot":"","sources":["../../../../src/mcp/handlers/media-handlers.ts"],"names":[],"mappings":"AAsRA,eAAO,MAAM,aAAa;8BACO,GAAG;;;;;6BA8CJ,GAAG;6BAuCH,GAAG;CAuOlC,CAAC"}
@@ -0,0 +1,535 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mediaHandlers = void 0;
4
+ // Media handlers — Stream extraction, player control, media tools
5
+ const state_1 = require("./state");
6
+ // ═══════════════════════════════════════════════════════════════
7
+ // Media Handlers — Stream extraction, player control, media tools
8
+ // ═══════════════════════════════════════════════════════════════
9
+ /**
10
+ * Shared helper: Extract streams from a page/frame context.
11
+ * Used by both stream_extractor and media_extractor to avoid duplication.
12
+ */
13
+ async function extractStreamsFromContext(context, contextName = 'main') {
14
+ return await context.evaluate(() => {
15
+ const result = { video: [], audio: [], hls: [], dash: [], download: [], embedded: [] };
16
+ // 1. Direct video/audio elements
17
+ document.querySelectorAll('video source, video').forEach(el => {
18
+ const src = el.src || el.getAttribute('src') || el.currentSrc;
19
+ if (src && src.startsWith('http'))
20
+ result.video.push({ src, type: el.type || 'video' });
21
+ });
22
+ document.querySelectorAll('audio source, audio').forEach(el => {
23
+ const src = el.src || el.getAttribute('src');
24
+ if (src && src.startsWith('http'))
25
+ result.audio.push({ src, type: el.type || 'audio' });
26
+ });
27
+ // 2. Script content analysis for HLS/DASH/MP4
28
+ const scripts = [...document.querySelectorAll('script')].map(s => s.textContent).join('\n');
29
+ const html = document.documentElement.innerHTML;
30
+ const combined = scripts + html;
31
+ // HLS streams
32
+ const hlsMatches = combined.match(/https?:\/\/[^\s"'<>]+\.m3u8[^\s"'<>]*/gi) || [];
33
+ result.hls = [...new Set(hlsMatches)].map(src => ({ src: src.replace(/\\"/g, ''), type: 'hls' }));
34
+ // DASH streams
35
+ const dashMatches = combined.match(/https?:\/\/[^\s"'<>]+\.mpd[^\s"'<>]*/gi) || [];
36
+ result.dash = [...new Set(dashMatches)].map(src => ({ src: src.replace(/\\"/g, ''), type: 'dash' }));
37
+ // Direct MP4/video links
38
+ const mp4Matches = combined.match(/https?:\/\/[^\s"'<>]+\.(mp4|mkv|avi|webm)[^\s"'<>]*/gi) || [];
39
+ result.download = [...new Set(mp4Matches)].map(src => ({ src: src.replace(/\\"/g, ''), type: 'direct' }));
40
+ // 3. Data attributes and hidden sources
41
+ document.querySelectorAll('[data-src], [data-video], [data-url], [data-file]').forEach(el => {
42
+ const dataSrc = el.dataset.src || el.dataset.video || el.dataset.url || el.dataset.file;
43
+ if (dataSrc && dataSrc.startsWith('http')) {
44
+ result.video.push({ src: dataSrc, type: 'data-attribute' });
45
+ }
46
+ });
47
+ // 4. Embedded player iframes (just URLs, not content)
48
+ document.querySelectorAll('iframe[src]').forEach(el => {
49
+ const src = el.src;
50
+ if (src && src.startsWith('http')) {
51
+ result.embedded.push({ src, type: 'iframe' });
52
+ }
53
+ });
54
+ // 5. JWPlayer / VideoJS / Plyr sources
55
+ if (window.jwplayer) {
56
+ try {
57
+ const jw = window.jwplayer();
58
+ const playlist = jw.getPlaylist?.() || [];
59
+ playlist.forEach((item) => {
60
+ if (item.file)
61
+ result.video.push({ src: item.file, type: 'jwplayer' });
62
+ (item.sources || []).forEach((s) => {
63
+ if (s.file)
64
+ result.video.push({ src: s.file, type: 'jwplayer-source' });
65
+ });
66
+ });
67
+ }
68
+ catch (e) { }
69
+ }
70
+ if (window.player && window.player.src) {
71
+ try {
72
+ const src = typeof window.player.src === 'function' ? window.player.src() : window.player.src;
73
+ if (src)
74
+ result.video.push({ src, type: 'player-api' });
75
+ }
76
+ catch (e) { }
77
+ }
78
+ // 6. Look for common patterns
79
+ const patterns = [
80
+ /file\s*:\s*["']([^"']+)["']/gi,
81
+ /source\s*:\s*["']([^"']+)["']/gi,
82
+ /src\s*:\s*["']([^"']+\.(?:m3u8|mp4|mkv))["']/gi,
83
+ /url\s*:\s*["']([^"']+\.(?:m3u8|mp4))["']/gi,
84
+ /video_url\s*=\s*["']([^"']+)["']/gi,
85
+ /sources\s*:\s*\[([^\]]+)\]/gi
86
+ ];
87
+ patterns.forEach(pattern => {
88
+ let match;
89
+ while ((match = pattern.exec(combined)) !== null) {
90
+ const url = match[1];
91
+ if (url && url.startsWith('http') && (url.includes('.mp4') || url.includes('.m3u8'))) {
92
+ result.download.push({ src: url, type: 'pattern-match' });
93
+ }
94
+ }
95
+ });
96
+ return result;
97
+ }).catch(() => ({ video: [], audio: [], hls: [], dash: [], download: [], embedded: [] }));
98
+ }
99
+ /**
100
+ * Shared helper: Detect video player in a page/frame context.
101
+ * Used by both player_api_hook and media_extractor.
102
+ */
103
+ async function detectPlayerInContext(context, actionOrParams, contextName = 'main') {
104
+ const action = typeof actionOrParams === 'string' ? actionOrParams : (actionOrParams?.action || 'info');
105
+ const playerType = typeof actionOrParams === 'object' ? actionOrParams?.playerType : 'auto';
106
+ return await context.evaluate(({ playerType, action }) => {
107
+ const result = { detected: false, type: null, sources: [], info: {} };
108
+ // 1. JWPlayer detection
109
+ if (window.jwplayer) {
110
+ try {
111
+ const jw = window.jwplayer();
112
+ if (jw) {
113
+ result.detected = true;
114
+ result.type = 'jwplayer';
115
+ result.info = {
116
+ duration: jw.getDuration?.(),
117
+ currentTime: jw.getPosition?.(),
118
+ volume: jw.getVolume?.(),
119
+ state: jw.getState?.()
120
+ };
121
+ if (action === 'sources') {
122
+ const playlist = jw.getPlaylist?.() || [];
123
+ playlist.forEach((item) => {
124
+ if (item.file)
125
+ result.sources.push({ src: item.file, type: 'jwplayer' });
126
+ (item.sources || []).forEach((s) => {
127
+ if (s.file)
128
+ result.sources.push({ src: s.file, type: 'jwplayer-source', label: s.label });
129
+ });
130
+ });
131
+ }
132
+ if (action === 'play')
133
+ jw.play?.();
134
+ if (action === 'pause')
135
+ jw.pause?.();
136
+ }
137
+ }
138
+ catch (e) { }
139
+ }
140
+ // 2. Video.js detection
141
+ if (window.videojs && !result.detected) {
142
+ try {
143
+ const players = document.querySelectorAll('.video-js');
144
+ if (players.length > 0) {
145
+ const player = window.videojs(players[0].id || players[0]);
146
+ result.detected = true;
147
+ result.type = 'videojs';
148
+ result.info = {
149
+ duration: player.duration?.(),
150
+ currentTime: player.currentTime?.(),
151
+ volume: player.volume?.()
152
+ };
153
+ if (action === 'sources') {
154
+ const src = player.currentSrc?.();
155
+ if (src)
156
+ result.sources.push({ src, type: 'videojs' });
157
+ }
158
+ if (action === 'play')
159
+ player.play?.();
160
+ if (action === 'pause')
161
+ player.pause?.();
162
+ }
163
+ }
164
+ catch (e) { }
165
+ }
166
+ // 3. Plyr detection
167
+ if (window.Plyr && !result.detected) {
168
+ try {
169
+ const plyrElements = document.querySelectorAll('.plyr');
170
+ if (plyrElements.length > 0 && plyrElements[0].plyr) {
171
+ const player = plyrElements[0].plyr;
172
+ result.detected = true;
173
+ result.type = 'plyr';
174
+ result.info = {
175
+ duration: player.duration,
176
+ currentTime: player.currentTime,
177
+ volume: player.volume
178
+ };
179
+ if (action === 'sources') {
180
+ const src = player.source;
181
+ if (src)
182
+ result.sources.push({ src, type: 'plyr' });
183
+ }
184
+ if (action === 'play')
185
+ player.play?.();
186
+ if (action === 'pause')
187
+ player.pause?.();
188
+ }
189
+ }
190
+ catch (e) { }
191
+ }
192
+ // 4. Generic window.player
193
+ if ((window.player || window.videoPlayer) && !result.detected) {
194
+ try {
195
+ const player = window.player || window.videoPlayer;
196
+ result.detected = true;
197
+ result.type = 'generic';
198
+ result.info = {
199
+ duration: player.getDuration?.() || player.duration,
200
+ currentTime: player.getCurrentTime?.() || player.currentTime,
201
+ volume: player.getVolume?.() || player.volume
202
+ };
203
+ if (action === 'sources') {
204
+ const sources = player.getSources?.() || player.getPlaylist?.() || [];
205
+ sources.forEach((s) => {
206
+ if (s.file || s.src)
207
+ result.sources.push({ src: s.file || s.src, type: 'generic' });
208
+ });
209
+ }
210
+ }
211
+ catch (e) { }
212
+ }
213
+ // 5. HTML5 Video fallback
214
+ if (!result.detected) {
215
+ const video = document.querySelector('video');
216
+ if (video) {
217
+ result.detected = true;
218
+ result.type = 'html5';
219
+ result.info = {
220
+ duration: video.duration, currentTime: video.currentTime,
221
+ volume: video.volume, paused: video.paused, src: video.src || video.currentSrc
222
+ };
223
+ if (action === 'sources') {
224
+ if (video.src)
225
+ result.sources.push({ src: video.src, type: 'html5' });
226
+ if (video.currentSrc && video.currentSrc !== video.src) {
227
+ result.sources.push({ src: video.currentSrc, type: 'html5-current' });
228
+ }
229
+ video.querySelectorAll('source').forEach(s => { if (s.src)
230
+ result.sources.push({ src: s.src, type: 'html5-source' }); });
231
+ }
232
+ if (action === 'play')
233
+ video.play();
234
+ if (action === 'pause')
235
+ video.pause();
236
+ if (action === 'seek' && typeof actionOrParams === 'object' && actionOrParams.seekTime)
237
+ video.currentTime = actionOrParams.seekTime;
238
+ }
239
+ }
240
+ // 6. Look for common obfuscated player variables
241
+ const commonPlayerVars = ['player', 'videoPlayer', 'mediaPlayer', 'vPlayer', 'hls', 'flv'];
242
+ for (const varName of commonPlayerVars) {
243
+ if (window[varName] && !result.detected) {
244
+ try {
245
+ const p = window[varName];
246
+ if (typeof p === 'object' && (p.play || p.getDuration || p.src)) {
247
+ result.detected = true;
248
+ result.type = `${varName}-object`;
249
+ result.info = { raw: true };
250
+ }
251
+ }
252
+ catch (e) { }
253
+ }
254
+ }
255
+ return result;
256
+ }, { playerType, action }).catch(() => ({ detected: false }));
257
+ }
258
+ /**
259
+ * Shared helper: Deduplicate streams by URL
260
+ */
261
+ function deduplicateStreams(streams) {
262
+ Object.keys(streams).forEach(key => {
263
+ if (Array.isArray(streams[key])) {
264
+ const seen = new Set();
265
+ streams[key] = streams[key].filter(item => {
266
+ if (seen.has(item.src))
267
+ return false;
268
+ seen.add(item.src);
269
+ return true;
270
+ });
271
+ }
272
+ });
273
+ return streams;
274
+ }
275
+ exports.mediaHandlers = {
276
+ async stream_extractor(params = {}) {
277
+ const { page } = (0, state_1.requireBrowser)();
278
+ const { types = ['all'], quality = 'best', searchIframes = true, deep = true } = params;
279
+ (0, state_1.notifyProgress)('stream_extractor', 'started', 'Extracting streams (enhanced mode)...');
280
+ // Extract from main page
281
+ const mainStreams = await extractStreamsFromContext(page, 'main');
282
+ let allStreams = { ...mainStreams };
283
+ // Search in iframes if enabled
284
+ if (searchIframes) {
285
+ const frames = page.frames();
286
+ (0, state_1.notifyProgress)('stream_extractor', 'progress', `Searching ${frames.length} frames...`);
287
+ for (let i = 1; i < frames.length && i < 10; i++) {
288
+ try {
289
+ const frame = frames[i];
290
+ const frameUrl = frame.url();
291
+ if (frameUrl && frameUrl !== 'about:blank') {
292
+ const frameStreams = await extractStreamsFromContext(frame, `frame-${i}`);
293
+ Object.keys(frameStreams).forEach(key => {
294
+ if (Array.isArray(frameStreams[key])) {
295
+ frameStreams[key].forEach(stream => {
296
+ stream.source = `iframe: ${frameUrl}`;
297
+ });
298
+ allStreams[key] = [...(allStreams[key] || []), ...frameStreams[key]];
299
+ }
300
+ });
301
+ }
302
+ }
303
+ catch (e) {
304
+ // Frame access error, skip
305
+ }
306
+ }
307
+ }
308
+ // Deduplicate
309
+ allStreams = deduplicateStreams(allStreams);
310
+ const totalStreams = Object.values(allStreams).reduce((sum, arr) => sum + (Array.isArray(arr) ? arr.length : 0), 0);
311
+ (0, state_1.notifyProgress)('stream_extractor', 'completed', `Found ${totalStreams} streams (including iframes)`, { totalStreams });
312
+ return { success: true, streams: allStreams, totalCount: totalStreams };
313
+ },
314
+ async player_api_hook(params = {}) {
315
+ const { page } = (0, state_1.requireBrowser)();
316
+ const { playerType = 'auto', action = 'info', searchIframes = true } = params;
317
+ (0, state_1.notifyProgress)('player_api_hook', 'started', `Player ${action}: ${playerType}`);
318
+ // Try main page first
319
+ let playerInfo = await detectPlayerInContext(page, { playerType, action }, 'main');
320
+ // Search iframes if no player found and searchIframes is enabled
321
+ if (!playerInfo.detected && searchIframes) {
322
+ const frames = page.frames();
323
+ (0, state_1.notifyProgress)('player_api_hook', 'progress', `Searching ${frames.length} frames for player...`);
324
+ for (let i = 1; i < frames.length && i < 10; i++) {
325
+ try {
326
+ const frame = frames[i];
327
+ const frameUrl = frame.url();
328
+ if (frameUrl && frameUrl !== 'about:blank') {
329
+ const framePlayer = await detectPlayerInContext(frame, { playerType, action }, `frame-${i}`);
330
+ if (framePlayer.detected) {
331
+ playerInfo = { ...framePlayer, frameSource: frameUrl };
332
+ (0, state_1.notifyProgress)('player_api_hook', 'progress', `Found player in iframe: ${frameUrl}`);
333
+ break;
334
+ }
335
+ }
336
+ }
337
+ catch (e) {
338
+ // Frame access error, skip
339
+ }
340
+ }
341
+ }
342
+ (0, state_1.notifyProgress)('player_api_hook', 'completed', playerInfo.detected ? `Player detected: ${playerInfo.type}${playerInfo.frameSource ? ' (in iframe)' : ''}` : 'No player found', { detected: playerInfo.detected, type: playerInfo.type });
343
+ return { success: true, ...playerInfo };
344
+ },
345
+ async media_extractor(params = {}) {
346
+ const { page } = (0, state_1.requireBrowser)();
347
+ const { action = 'extract', types = ['all'], quality = 'best', searchIframes = true, deep = true, selector, index, playerAction = 'info', encodedData, decoderType = 'auto', aesKey, aesIV, urls, aiOptimize = true } = params;
348
+ (0, state_1.notifyProgress)('media_extractor', 'started', `Media extraction action: ${action}`);
349
+ // Helper: Auto-detect decoder type
350
+ const autoDetectDecoder = (data) => {
351
+ if (data.includes('%'))
352
+ return 'url';
353
+ if (/^[A-Za-z0-9+/=]+$/.test(data) && data.length % 4 === 0)
354
+ return 'base64';
355
+ return 'url';
356
+ };
357
+ switch (action) {
358
+ case 'extract': {
359
+ (0, state_1.notifyProgress)('media_extractor', 'progress', 'Extracting all media...');
360
+ // Get iframes
361
+ const frames = page.frames();
362
+ const iframes = frames.map((f, i) => ({ index: i, name: f.name(), url: f.url() }));
363
+ // Extract streams from main page
364
+ let allStreams = await extractStreamsFromContext(page, 'main');
365
+ // Search in iframes
366
+ if (searchIframes) {
367
+ for (let i = 1; i < frames.length && i < 10; i++) {
368
+ try {
369
+ const frame = frames[i];
370
+ const frameUrl = frame.url();
371
+ if (frameUrl && frameUrl !== 'about:blank') {
372
+ const frameStreams = await extractStreamsFromContext(frame, `frame-${i}`);
373
+ Object.keys(frameStreams).forEach(key => {
374
+ if (Array.isArray(frameStreams[key])) {
375
+ frameStreams[key].forEach(stream => { stream.source = `iframe: ${frameUrl}`; });
376
+ allStreams[key] = [...(allStreams[key] || []), ...frameStreams[key]];
377
+ }
378
+ });
379
+ }
380
+ }
381
+ catch (e) { }
382
+ }
383
+ }
384
+ // Deduplicate
385
+ allStreams = deduplicateStreams(allStreams);
386
+ // Detect players
387
+ let playerInfo = await detectPlayerInContext(page, playerAction, 'main');
388
+ if (!playerInfo.detected && searchIframes) {
389
+ for (let i = 1; i < frames.length && i < 10; i++) {
390
+ try {
391
+ const frame = frames[i];
392
+ const frameUrl = frame.url();
393
+ if (frameUrl && frameUrl !== 'about:blank') {
394
+ const framePlayer = await detectPlayerInContext(frame, playerAction, `frame-${i}`);
395
+ if (framePlayer.detected) {
396
+ playerInfo = { ...framePlayer, frameSource: frameUrl };
397
+ break;
398
+ }
399
+ }
400
+ }
401
+ catch (e) { }
402
+ }
403
+ }
404
+ const totalStreams = Object.values(allStreams).reduce((sum, arr) => sum + (Array.isArray(arr) ? arr.length : 0), 0);
405
+ (0, state_1.notifyProgress)('media_extractor', 'completed', `Extracted ${totalStreams} streams, ${iframes.length} iframes, player: ${playerInfo.detected ? playerInfo.type : 'none'}`);
406
+ return {
407
+ success: true,
408
+ action: 'extract',
409
+ streams: allStreams,
410
+ iframes,
411
+ player: playerInfo,
412
+ totalStreams,
413
+ iframeCount: iframes.length
414
+ };
415
+ }
416
+ case 'list_iframes': {
417
+ const frames = page.frames();
418
+ const iframes = frames.map((f, i) => ({ index: i, name: f.name(), url: f.url() }));
419
+ (0, state_1.notifyProgress)('media_extractor', 'completed', `Found ${iframes.length} iframes`);
420
+ return { success: true, action: 'list_iframes', count: iframes.length, iframes };
421
+ }
422
+ case 'switch_iframe': {
423
+ const frames = page.frames();
424
+ const targetFrame = selector
425
+ ? await page.$(selector).then(el => el?.contentFrame())
426
+ : frames[index];
427
+ if (targetFrame) {
428
+ (0, state_1.notifyProgress)('media_extractor', 'completed', `Switched to iframe: ${targetFrame.url()}`);
429
+ return { success: true, action: 'switch_iframe', switched: true, url: targetFrame.url(), frameIndex: index };
430
+ }
431
+ (0, state_1.notifyProgress)('media_extractor', 'error', 'Iframe not found');
432
+ return { success: false, error: 'Iframe not found' };
433
+ }
434
+ case 'player_control': {
435
+ const { playerType, seekTime, volume } = params;
436
+ (0, state_1.notifyProgress)('media_extractor', 'progress', `Player control: ${playerAction}`);
437
+ let result = await detectPlayerInContext(page, playerAction, 'main');
438
+ if (!result.detected && searchIframes) {
439
+ const frames = page.frames();
440
+ for (let i = 1; i < frames.length && i < 10; i++) {
441
+ try {
442
+ const frame = frames[i];
443
+ const frameUrl = frame.url();
444
+ if (frameUrl && frameUrl !== 'about:blank') {
445
+ const frameResult = await detectPlayerInContext(frame, playerAction, `frame-${i}`);
446
+ if (frameResult.detected) {
447
+ result = { ...frameResult, frameSource: frameUrl };
448
+ break;
449
+ }
450
+ }
451
+ }
452
+ catch (e) { }
453
+ }
454
+ }
455
+ (0, state_1.notifyProgress)('media_extractor', 'completed', result.detected ? `Player ${playerAction} executed: ${result.type}` : 'No player found');
456
+ return { success: true, action: 'player_control', playerAction, ...result };
457
+ }
458
+ case 'decode_url': {
459
+ if (!encodedData) {
460
+ return { success: false, error: 'encodedData is required for decode_url action' };
461
+ }
462
+ const type = decoderType === 'auto' ? autoDetectDecoder(encodedData) : decoderType;
463
+ let decoded;
464
+ (0, state_1.notifyProgress)('media_extractor', 'progress', `Decoding with ${type}...`);
465
+ switch (type) {
466
+ case 'url':
467
+ decoded = state_1.decoders.urlDecode(encodedData);
468
+ break;
469
+ case 'base64':
470
+ decoded = state_1.decoders.base64Decode(encodedData);
471
+ break;
472
+ case 'aes':
473
+ if (!aesKey) {
474
+ return { success: false, error: 'aesKey is required for AES decryption' };
475
+ }
476
+ decoded = state_1.decoders.decryptAES(encodedData, aesKey, aesIV);
477
+ break;
478
+ default:
479
+ decoded = { success: false, error: 'Unknown decoder type' };
480
+ }
481
+ (0, state_1.notifyProgress)('media_extractor', 'completed', decoded.success ? 'Decoding successful' : 'Decoding failed');
482
+ return { action: 'decode_url', decoderType: type, ...decoded };
483
+ }
484
+ case 'batch_extract': {
485
+ if (!urls || !Array.isArray(urls) || urls.length === 0) {
486
+ return { success: false, error: 'urls array is required for batch_extract action' };
487
+ }
488
+ (0, state_1.notifyProgress)('media_extractor', 'progress', `Batch extracting from ${urls.length} URLs...`);
489
+ const results = [];
490
+ const errors = [];
491
+ for (let i = 0; i < urls.length; i++) {
492
+ const url = urls[i];
493
+ try {
494
+ (0, state_1.notifyProgress)('media_extractor', 'progress', `Processing ${i + 1}/${urls.length}: ${url}`);
495
+ await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
496
+ await new Promise(r => setTimeout(r, 2000));
497
+ const streams = await extractStreamsFromContext(page, 'main');
498
+ const dedupedStreams = deduplicateStreams(streams);
499
+ const totalCount = Object.values(dedupedStreams).reduce((sum, arr) => sum + (Array.isArray(arr) ? arr.length : 0), 0);
500
+ const meta = await page.evaluate(() => ({
501
+ title: document.title,
502
+ url: window.location.href
503
+ }));
504
+ results.push({
505
+ url,
506
+ success: true,
507
+ streams: dedupedStreams,
508
+ totalCount,
509
+ title: meta.title,
510
+ finalUrl: meta.url
511
+ });
512
+ }
513
+ catch (error) {
514
+ errors.push({ url, error: error.message });
515
+ results.push({ url, success: false, error: error.message });
516
+ }
517
+ }
518
+ const successCount = results.filter(r => r.success).length;
519
+ (0, state_1.notifyProgress)('media_extractor', 'completed', `Batch extraction complete: ${successCount}/${urls.length} successful`);
520
+ return {
521
+ success: true,
522
+ action: 'batch_extract',
523
+ totalUrls: urls.length,
524
+ successful: successCount,
525
+ failed: urls.length - successCount,
526
+ results,
527
+ errors
528
+ };
529
+ }
530
+ default:
531
+ return { success: false, error: `Unknown action: ${action}. Supported: extract, list_iframes, switch_iframe, player_control, decode_url, batch_extract` };
532
+ }
533
+ }
534
+ };
535
+ //# sourceMappingURL=media-handlers.js.map