@studiomeyer/mcp-video 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- package/.github/workflows/ci.yml +34 -0
- package/CHANGELOG.md +24 -0
- package/CONTRIBUTING.md +75 -0
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/USAGE.md +144 -0
- package/dist/handlers/capcut.d.ts +6 -0
- package/dist/handlers/capcut.js +229 -0
- package/dist/handlers/capcut.js.map +1 -0
- package/dist/handlers/editing.d.ts +6 -0
- package/dist/handlers/editing.js +242 -0
- package/dist/handlers/editing.js.map +1 -0
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +33 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/post-production.d.ts +5 -0
- package/dist/handlers/post-production.js +109 -0
- package/dist/handlers/post-production.js.map +1 -0
- package/dist/handlers/smart-screenshot.d.ts +5 -0
- package/dist/handlers/smart-screenshot.js +83 -0
- package/dist/handlers/smart-screenshot.js.map +1 -0
- package/dist/handlers/tts.d.ts +5 -0
- package/dist/handlers/tts.js +83 -0
- package/dist/handlers/tts.js.map +1 -0
- package/dist/handlers/video.d.ts +5 -0
- package/dist/handlers/video.js +127 -0
- package/dist/handlers/video.js.map +1 -0
- package/dist/lib/dual-transport.d.ts +42 -0
- package/dist/lib/dual-transport.js +208 -0
- package/dist/lib/dual-transport.js.map +1 -0
- package/dist/lib/logger.d.ts +12 -0
- package/dist/lib/logger.js +42 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/types.d.ts +16 -0
- package/dist/lib/types.js +15 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/schemas/capcut.d.ts +608 -0
- package/dist/schemas/capcut.js +411 -0
- package/dist/schemas/capcut.js.map +1 -0
- package/dist/schemas/editing.d.ts +822 -0
- package/dist/schemas/editing.js +466 -0
- package/dist/schemas/editing.js.map +1 -0
- package/dist/schemas/index.d.ts +2366 -0
- package/dist/schemas/index.js +15 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/post-production.d.ts +379 -0
- package/dist/schemas/post-production.js +268 -0
- package/dist/schemas/post-production.js.map +1 -0
- package/dist/schemas/smart-screenshot.d.ts +127 -0
- package/dist/schemas/smart-screenshot.js +122 -0
- package/dist/schemas/smart-screenshot.js.map +1 -0
- package/dist/schemas/tts.d.ts +220 -0
- package/dist/schemas/tts.js +194 -0
- package/dist/schemas/tts.js.map +1 -0
- package/dist/schemas/video.d.ts +236 -0
- package/dist/schemas/video.js +210 -0
- package/dist/schemas/video.js.map +1 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +239 -0
- package/dist/server.js.map +1 -0
- package/dist/server.test.d.ts +1 -0
- package/dist/server.test.js +87 -0
- package/dist/server.test.js.map +1 -0
- package/dist/tools/engine/audio-mixer.d.ts +40 -0
- package/dist/tools/engine/audio-mixer.js +169 -0
- package/dist/tools/engine/audio-mixer.js.map +1 -0
- package/dist/tools/engine/audio.d.ts +22 -0
- package/dist/tools/engine/audio.js +73 -0
- package/dist/tools/engine/audio.js.map +1 -0
- package/dist/tools/engine/beat-sync.d.ts +31 -0
- package/dist/tools/engine/beat-sync.js +270 -0
- package/dist/tools/engine/beat-sync.js.map +1 -0
- package/dist/tools/engine/capture.d.ts +12 -0
- package/dist/tools/engine/capture.js +290 -0
- package/dist/tools/engine/capture.js.map +1 -0
- package/dist/tools/engine/chroma-key.d.ts +27 -0
- package/dist/tools/engine/chroma-key.js +154 -0
- package/dist/tools/engine/chroma-key.js.map +1 -0
- package/dist/tools/engine/concat.d.ts +49 -0
- package/dist/tools/engine/concat.js +149 -0
- package/dist/tools/engine/concat.js.map +1 -0
- package/dist/tools/engine/cursor.d.ts +26 -0
- package/dist/tools/engine/cursor.js +185 -0
- package/dist/tools/engine/cursor.js.map +1 -0
- package/dist/tools/engine/easing.d.ts +15 -0
- package/dist/tools/engine/easing.js +100 -0
- package/dist/tools/engine/easing.js.map +1 -0
- package/dist/tools/engine/editing.d.ts +158 -0
- package/dist/tools/engine/editing.js +541 -0
- package/dist/tools/engine/editing.js.map +1 -0
- package/dist/tools/engine/encoder.d.ts +31 -0
- package/dist/tools/engine/encoder.js +154 -0
- package/dist/tools/engine/encoder.js.map +1 -0
- package/dist/tools/engine/index.d.ts +30 -0
- package/dist/tools/engine/index.js +23 -0
- package/dist/tools/engine/index.js.map +1 -0
- package/dist/tools/engine/lut-presets.d.ts +25 -0
- package/dist/tools/engine/lut-presets.js +141 -0
- package/dist/tools/engine/lut-presets.js.map +1 -0
- package/dist/tools/engine/narrated-video.d.ts +63 -0
- package/dist/tools/engine/narrated-video.js +163 -0
- package/dist/tools/engine/narrated-video.js.map +1 -0
- package/dist/tools/engine/scenes.d.ts +17 -0
- package/dist/tools/engine/scenes.js +223 -0
- package/dist/tools/engine/scenes.js.map +1 -0
- package/dist/tools/engine/smart-screenshot.d.ts +80 -0
- package/dist/tools/engine/smart-screenshot.js +744 -0
- package/dist/tools/engine/smart-screenshot.js.map +1 -0
- package/dist/tools/engine/social-format.d.ts +66 -0
- package/dist/tools/engine/social-format.js +107 -0
- package/dist/tools/engine/social-format.js.map +1 -0
- package/dist/tools/engine/template-renderer.d.ts +45 -0
- package/dist/tools/engine/template-renderer.js +233 -0
- package/dist/tools/engine/template-renderer.js.map +1 -0
- package/dist/tools/engine/templates.d.ts +87 -0
- package/dist/tools/engine/templates.js +272 -0
- package/dist/tools/engine/templates.js.map +1 -0
- package/dist/tools/engine/text-animations.d.ts +33 -0
- package/dist/tools/engine/text-animations.js +192 -0
- package/dist/tools/engine/text-animations.js.map +1 -0
- package/dist/tools/engine/text-overlay.d.ts +27 -0
- package/dist/tools/engine/text-overlay.js +84 -0
- package/dist/tools/engine/text-overlay.js.map +1 -0
- package/dist/tools/engine/tts.d.ts +54 -0
- package/dist/tools/engine/tts.js +186 -0
- package/dist/tools/engine/tts.js.map +1 -0
- package/dist/tools/engine/types.d.ts +166 -0
- package/dist/tools/engine/types.js +13 -0
- package/dist/tools/engine/types.js.map +1 -0
- package/dist/tools/engine/voice-effects.d.ts +18 -0
- package/dist/tools/engine/voice-effects.js +215 -0
- package/dist/tools/engine/voice-effects.js.map +1 -0
- package/dist/tools/index.d.ts +32 -0
- package/dist/tools/index.js +23 -0
- package/dist/tools/index.js.map +1 -0
- package/package.json +56 -0
- package/scripts/check-deps.js +39 -0
- package/src/handlers/capcut.ts +245 -0
- package/src/handlers/editing.ts +260 -0
- package/src/handlers/index.ts +34 -0
- package/src/handlers/post-production.ts +136 -0
- package/src/handlers/smart-screenshot.ts +86 -0
- package/src/handlers/tts.ts +103 -0
- package/src/handlers/video.ts +137 -0
- package/src/lib/dual-transport.ts +272 -0
- package/src/lib/logger.ts +59 -0
- package/src/lib/types.ts +25 -0
- package/src/schemas/capcut.ts +418 -0
- package/src/schemas/editing.ts +476 -0
- package/src/schemas/index.ts +15 -0
- package/src/schemas/post-production.ts +273 -0
- package/src/schemas/smart-screenshot.ts +122 -0
- package/src/schemas/tts.ts +197 -0
- package/src/schemas/video.ts +211 -0
- package/src/server.test.ts +99 -0
- package/src/server.ts +289 -0
- package/src/tools/engine/audio-mixer.ts +244 -0
- package/src/tools/engine/audio.ts +115 -0
- package/src/tools/engine/beat-sync.ts +356 -0
- package/src/tools/engine/capture.ts +360 -0
- package/src/tools/engine/chroma-key.ts +202 -0
- package/src/tools/engine/concat.ts +242 -0
- package/src/tools/engine/cursor.ts +222 -0
- package/src/tools/engine/easing.ts +120 -0
- package/src/tools/engine/editing.ts +809 -0
- package/src/tools/engine/encoder.ts +208 -0
- package/src/tools/engine/index.ts +33 -0
- package/src/tools/engine/lut-presets.ts +235 -0
- package/src/tools/engine/narrated-video.ts +267 -0
- package/src/tools/engine/scenes.ts +309 -0
- package/src/tools/engine/smart-screenshot.ts +923 -0
- package/src/tools/engine/social-format.ts +146 -0
- package/src/tools/engine/template-renderer.ts +294 -0
- package/src/tools/engine/templates.ts +370 -0
- package/src/tools/engine/text-animations.ts +282 -0
- package/src/tools/engine/text-overlay.ts +143 -0
- package/src/tools/engine/tts.ts +284 -0
- package/src/tools/engine/types.ts +191 -0
- package/src/tools/engine/voice-effects.ts +258 -0
- package/src/tools/index.ts +67 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame-by-Frame Capture Engine
|
|
3
|
+
* The core of cinema-grade website video recording
|
|
4
|
+
*
|
|
5
|
+
* Pipeline: Playwright → Frame Screenshots → ffmpeg → MP4
|
|
6
|
+
* Result: Perfect 60fps video with zero frame drops
|
|
7
|
+
*/
|
|
8
|
+
import { chromium } from 'playwright';
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import * as os from 'os';
|
|
12
|
+
import { logger } from '../../lib/logger.js';
|
|
13
|
+
import { VIEWPORTS } from './types.js';
|
|
14
|
+
import { injectCursor, hideCursor } from './cursor.js';
|
|
15
|
+
import { executeScenes, createDefaultScenes } from './scenes.js';
|
|
16
|
+
import { encodeFrames, cleanupFrames } from './encoder.js';
|
|
17
|
+
const OUTPUT_DIR = process.env.VIDEO_OUTPUT_DIR || './output';
|
|
18
|
+
/**
|
|
19
|
+
* Record a website with cinema-quality frame-by-frame capture
|
|
20
|
+
*/
|
|
21
|
+
export async function recordWebsite(config) {
|
|
22
|
+
const { url, outputPath = path.join(OUTPUT_DIR, `website-video-${Date.now()}`), fps = 60, scenes: userScenes, cursor = { enabled: true }, encoding = {}, dismissOverlays = true, preloadContent = true, deviceScaleFactor = 1, darkMode = false, disableSmoothScroll = true, } = config;
|
|
23
|
+
// Resolve viewport
|
|
24
|
+
const viewport = typeof config.viewport === 'string'
|
|
25
|
+
? VIEWPORTS[config.viewport] ?? VIEWPORTS.desktop
|
|
26
|
+
: config.viewport ?? VIEWPORTS.desktop;
|
|
27
|
+
// Create temp directory for frames
|
|
28
|
+
const framesDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cinema-frames-'));
|
|
29
|
+
const framePattern = 'frame_%06d.png';
|
|
30
|
+
let browser;
|
|
31
|
+
let totalFrames = 0;
|
|
32
|
+
const startTime = Date.now();
|
|
33
|
+
try {
|
|
34
|
+
logger.info(`Starting cinema capture: ${url} (${viewport.width}x${viewport.height}, ${fps}fps)`);
|
|
35
|
+
// ─── 1. Launch Browser ────────────────────────────────────────
|
|
36
|
+
browser = await chromium.launch({
|
|
37
|
+
headless: true,
|
|
38
|
+
args: [
|
|
39
|
+
'--no-sandbox',
|
|
40
|
+
'--disable-setuid-sandbox',
|
|
41
|
+
'--disable-dev-shm-usage',
|
|
42
|
+
'--disable-gpu',
|
|
43
|
+
'--disable-web-security',
|
|
44
|
+
'--hide-scrollbars',
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
const contextOptions = {
|
|
48
|
+
viewport: { width: viewport.width, height: viewport.height },
|
|
49
|
+
deviceScaleFactor,
|
|
50
|
+
colorScheme: darkMode ? 'dark' : 'light',
|
|
51
|
+
};
|
|
52
|
+
// Mobile user agent
|
|
53
|
+
if (config.viewport === 'mobile' || config.viewport === 'mobile-landscape') {
|
|
54
|
+
contextOptions.userAgent = config.userAgent ??
|
|
55
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1';
|
|
56
|
+
contextOptions.isMobile = true;
|
|
57
|
+
contextOptions.hasTouch = true;
|
|
58
|
+
}
|
|
59
|
+
else if (config.userAgent) {
|
|
60
|
+
contextOptions.userAgent = config.userAgent;
|
|
61
|
+
}
|
|
62
|
+
const context = await browser.newContext(contextOptions);
|
|
63
|
+
const page = await context.newPage();
|
|
64
|
+
// ─── 2. Navigate to URL ───────────────────────────────────────
|
|
65
|
+
logger.info(`Navigating to ${url}...`);
|
|
66
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 }).catch(async () => {
|
|
67
|
+
// Fallback: try with just domcontentloaded
|
|
68
|
+
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
69
|
+
});
|
|
70
|
+
// Wait for content to render
|
|
71
|
+
await page.waitForTimeout(2000);
|
|
72
|
+
// ─── 3. Prepare Page ──────────────────────────────────────────
|
|
73
|
+
// Disable CSS smooth scroll to prevent double-easing
|
|
74
|
+
if (disableSmoothScroll) {
|
|
75
|
+
await page.addStyleTag({
|
|
76
|
+
content: `*, html { scroll-behavior: auto !important; }`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// Hide scrollbar
|
|
80
|
+
await page.addStyleTag({
|
|
81
|
+
content: `::-webkit-scrollbar { display: none !important; } * { scrollbar-width: none !important; }`,
|
|
82
|
+
});
|
|
83
|
+
// Dismiss overlays and cookie banners
|
|
84
|
+
if (dismissOverlays) {
|
|
85
|
+
await dismissPageOverlays(page);
|
|
86
|
+
// Wait for animations to complete (e.g. Framer Motion exit)
|
|
87
|
+
await page.waitForTimeout(1000);
|
|
88
|
+
}
|
|
89
|
+
// ─── 4. Pre-scroll to trigger lazy loading ────────────────────
|
|
90
|
+
if (preloadContent) {
|
|
91
|
+
await preloadAllContent(page, viewport.height);
|
|
92
|
+
}
|
|
93
|
+
// ─── 5. Inject cursor overlay ─────────────────────────────────
|
|
94
|
+
if (cursor.enabled) {
|
|
95
|
+
await injectCursor(page, cursor);
|
|
96
|
+
}
|
|
97
|
+
// Scroll back to top
|
|
98
|
+
await page.evaluate(() => window.scrollTo(0, 0));
|
|
99
|
+
await page.waitForTimeout(500);
|
|
100
|
+
// ─── 6. Execute scenes & capture frames ───────────────────────
|
|
101
|
+
const scenes = userScenes && userScenes.length > 0
|
|
102
|
+
? userScenes
|
|
103
|
+
: createDefaultScenes();
|
|
104
|
+
logger.info(`Executing ${scenes.length} scene(s)...`);
|
|
105
|
+
let frameIndex = 0;
|
|
106
|
+
totalFrames = await executeScenes(page, scenes, fps, async (_fi) => {
|
|
107
|
+
const framePath = path.join(framesDir, `frame_${String(frameIndex).padStart(6, '0')}.png`);
|
|
108
|
+
await page.screenshot({ path: framePath, type: 'png' });
|
|
109
|
+
frameIndex++;
|
|
110
|
+
// Progress logging every 60 frames (= 1 second at 60fps)
|
|
111
|
+
if (frameIndex % fps === 0) {
|
|
112
|
+
logger.info(`Captured ${frameIndex} frames (${(frameIndex / fps).toFixed(1)}s)`);
|
|
113
|
+
}
|
|
114
|
+
}, cursor.enabled);
|
|
115
|
+
// Hide cursor for final frame
|
|
116
|
+
if (cursor.enabled) {
|
|
117
|
+
await hideCursor(page);
|
|
118
|
+
}
|
|
119
|
+
// Take thumbnail from first frame position
|
|
120
|
+
await page.evaluate(() => window.scrollTo(0, 0));
|
|
121
|
+
await page.waitForTimeout(300);
|
|
122
|
+
const thumbnailBuffer = await page.screenshot({ type: 'png' });
|
|
123
|
+
const thumbnailPath = `${outputPath}-thumbnail.png`;
|
|
124
|
+
const thumbnailDir = path.dirname(thumbnailPath);
|
|
125
|
+
if (!fs.existsSync(thumbnailDir)) {
|
|
126
|
+
fs.mkdirSync(thumbnailDir, { recursive: true });
|
|
127
|
+
}
|
|
128
|
+
fs.writeFileSync(thumbnailPath, thumbnailBuffer);
|
|
129
|
+
// ─── 7. Close browser ─────────────────────────────────────────
|
|
130
|
+
await context.close();
|
|
131
|
+
await browser.close();
|
|
132
|
+
browser = undefined;
|
|
133
|
+
logger.info(`Capture complete: ${totalFrames} frames in ${((Date.now() - startTime) / 1000).toFixed(1)}s`);
|
|
134
|
+
// ─── 8. Encode video ──────────────────────────────────────────
|
|
135
|
+
logger.info('Encoding video with ffmpeg...');
|
|
136
|
+
const encodeResult = await encodeFrames(framesDir, framePattern, outputPath, totalFrames, { ...encoding, fps });
|
|
137
|
+
// ─── 9. Cleanup frames ────────────────────────────────────────
|
|
138
|
+
cleanupFrames(framesDir);
|
|
139
|
+
const captureTimeSec = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
140
|
+
logger.info(`Video ready: ${encodeResult.outputPath} (${encodeResult.sizeMB} MB, ${captureTimeSec}s total)`);
|
|
141
|
+
return {
|
|
142
|
+
success: true,
|
|
143
|
+
video: {
|
|
144
|
+
path: encodeResult.outputPath,
|
|
145
|
+
format: encodeResult.format,
|
|
146
|
+
codec: encodeResult.codec,
|
|
147
|
+
fps: encodeResult.fps,
|
|
148
|
+
duration: encodeResult.duration,
|
|
149
|
+
totalFrames: encodeResult.totalFrames,
|
|
150
|
+
resolution: { width: viewport.width, height: viewport.height },
|
|
151
|
+
sizeBytes: encodeResult.sizeBytes,
|
|
152
|
+
sizeMB: encodeResult.sizeMB,
|
|
153
|
+
},
|
|
154
|
+
thumbnail: {
|
|
155
|
+
path: thumbnailPath,
|
|
156
|
+
width: viewport.width,
|
|
157
|
+
height: viewport.height,
|
|
158
|
+
},
|
|
159
|
+
scenes: scenes.length,
|
|
160
|
+
url,
|
|
161
|
+
captureTime: `${captureTimeSec}s`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
// Cleanup on error
|
|
166
|
+
cleanupFrames(framesDir);
|
|
167
|
+
if (browser) {
|
|
168
|
+
try {
|
|
169
|
+
await browser.close();
|
|
170
|
+
}
|
|
171
|
+
catch { /* ignore */ }
|
|
172
|
+
}
|
|
173
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
174
|
+
logger.error(`Recording failed: ${message}`);
|
|
175
|
+
throw new Error(`Recording failed: ${message}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Dismiss cookie banners, fixed overlays, and popups
|
|
180
|
+
* Uses multiple strategies: localStorage pre-set, button clicks, CSS hiding
|
|
181
|
+
*/
|
|
182
|
+
async function dismissPageOverlays(page) {
|
|
183
|
+
logger.info('Dismissing overlays and cookie banners...');
|
|
184
|
+
// Strategy 1: Pre-set common cookie consent localStorage/cookie values
|
|
185
|
+
// This prevents banners from appearing in the first place
|
|
186
|
+
await page.evaluate(() => {
|
|
187
|
+
// Common cookie consent localStorage key
|
|
188
|
+
localStorage.setItem('cookie-consent', 'accepted');
|
|
189
|
+
// Common cookie consent libraries
|
|
190
|
+
localStorage.setItem('cookieConsent', 'accepted');
|
|
191
|
+
localStorage.setItem('cookie_consent', 'true');
|
|
192
|
+
localStorage.setItem('cookies-accepted', 'true');
|
|
193
|
+
localStorage.setItem('gdpr-consent', 'true');
|
|
194
|
+
localStorage.setItem('CookieConsent', 'true');
|
|
195
|
+
// CookieBot
|
|
196
|
+
localStorage.setItem('CookieConsentV2', '{"stamp":"","necessary":true,"preferences":true,"statistics":true,"marketing":true}');
|
|
197
|
+
// OneTrust
|
|
198
|
+
localStorage.setItem('OptanonAlertBoxClosed', new Date().toISOString());
|
|
199
|
+
// Set cookies too
|
|
200
|
+
document.cookie = 'cookie-consent=accepted; path=/; max-age=31536000';
|
|
201
|
+
document.cookie = 'cookieconsent_status=dismiss; path=/; max-age=31536000';
|
|
202
|
+
// Dispatch custom consent event
|
|
203
|
+
window.dispatchEvent(new Event('cookie-consent-accepted'));
|
|
204
|
+
});
|
|
205
|
+
await page.waitForTimeout(300);
|
|
206
|
+
// Strategy 2: Try clicking accept buttons (multilingual)
|
|
207
|
+
const acceptButtonSelectors = [
|
|
208
|
+
// By text content (most reliable)
|
|
209
|
+
'button:has-text("Akzeptieren")',
|
|
210
|
+
'button:has-text("Accept")',
|
|
211
|
+
'button:has-text("Aceptar")',
|
|
212
|
+
'button:has-text("Accept all")',
|
|
213
|
+
'button:has-text("Alle akzeptieren")',
|
|
214
|
+
'button:has-text("Got it")',
|
|
215
|
+
'button:has-text("OK")',
|
|
216
|
+
'button:has-text("Verstanden")',
|
|
217
|
+
];
|
|
218
|
+
for (const selector of acceptButtonSelectors) {
|
|
219
|
+
try {
|
|
220
|
+
const btn = page.locator(selector).first();
|
|
221
|
+
if (await btn.isVisible({ timeout: 500 }).catch(() => false)) {
|
|
222
|
+
await btn.click({ timeout: 1000 });
|
|
223
|
+
logger.info(`Clicked consent button: ${selector}`);
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Button not found, try next
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Strategy 3: Click by CSS selectors (fallback)
|
|
232
|
+
await page.evaluate(() => {
|
|
233
|
+
const cssSelectors = [
|
|
234
|
+
'[class*="cookie"] button',
|
|
235
|
+
'[class*="consent"] button',
|
|
236
|
+
'[id*="cookie"] button',
|
|
237
|
+
'[id*="consent"] button',
|
|
238
|
+
'button[class*="accept"]',
|
|
239
|
+
'button[class*="Accept"]',
|
|
240
|
+
'[data-testid*="cookie"] button',
|
|
241
|
+
];
|
|
242
|
+
for (const sel of cssSelectors) {
|
|
243
|
+
const btn = document.querySelector(sel);
|
|
244
|
+
if (btn) {
|
|
245
|
+
btn.click();
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
await page.waitForTimeout(500);
|
|
251
|
+
// Strategy 4: Force hide any remaining overlays via CSS
|
|
252
|
+
await page.addStyleTag({
|
|
253
|
+
content: `
|
|
254
|
+
[class*="cookie"], [class*="Cookie"],
|
|
255
|
+
[class*="consent"], [class*="Consent"],
|
|
256
|
+
[class*="popup"]:not([class*="menu"]),
|
|
257
|
+
[class*="Popup"]:not([class*="menu"]),
|
|
258
|
+
[id*="cookie"], [id*="consent"],
|
|
259
|
+
[role="dialog"],
|
|
260
|
+
[class*="banner"]:not(header):not(nav):not([class*="hero"]),
|
|
261
|
+
.fixed.bottom-0.left-0.right-0.z-50 {
|
|
262
|
+
display: none !important;
|
|
263
|
+
visibility: hidden !important;
|
|
264
|
+
opacity: 0 !important;
|
|
265
|
+
pointer-events: none !important;
|
|
266
|
+
}
|
|
267
|
+
`,
|
|
268
|
+
});
|
|
269
|
+
logger.info('Overlays dismissed');
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Pre-scroll entire page to trigger all lazy-loaded content
|
|
273
|
+
*/
|
|
274
|
+
async function preloadAllContent(page, viewportHeight) {
|
|
275
|
+
logger.info('Pre-scrolling to trigger lazy content...');
|
|
276
|
+
const scrollHeight = await page.evaluate(() => document.documentElement.scrollHeight);
|
|
277
|
+
const steps = Math.ceil(scrollHeight / (viewportHeight * 0.7));
|
|
278
|
+
for (let i = 0; i <= steps; i++) {
|
|
279
|
+
const y = Math.min(i * viewportHeight * 0.7, scrollHeight);
|
|
280
|
+
await page.evaluate((scrollY) => window.scrollTo(0, scrollY), y);
|
|
281
|
+
await page.waitForTimeout(200);
|
|
282
|
+
// Wait for any network requests to settle
|
|
283
|
+
await page.waitForLoadState('networkidle').catch(() => { });
|
|
284
|
+
}
|
|
285
|
+
// Scroll back to top
|
|
286
|
+
await page.evaluate(() => window.scrollTo(0, 0));
|
|
287
|
+
await page.waitForTimeout(500);
|
|
288
|
+
logger.info('Lazy content preloaded');
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../../src/tools/engine/capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chroma Key Engine — Green screen removal and background replacement.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - chromakey (YUV space, best for green/blue screens)
|
|
6
|
+
* - colorkey (RGB space, best for arbitrary key colors)
|
|
7
|
+
* - despill (removes green/blue color spill on edges)
|
|
8
|
+
* - Composite onto replacement background (video, image, or solid color)
|
|
9
|
+
*/
|
|
10
|
+
export interface ChromaKeyConfig {
|
|
11
|
+
/** Input video with green/blue screen */
|
|
12
|
+
inputPath: string;
|
|
13
|
+
outputPath: string;
|
|
14
|
+
/** Key color in hex (e.g., '00FF00' for green, '0000FF' for blue). Default: 00FF00 */
|
|
15
|
+
keyColor?: string;
|
|
16
|
+
/** How close a color must be to the key: 0.01-1.0. Higher = more removal. Default: 0.15 */
|
|
17
|
+
similarity?: number;
|
|
18
|
+
/** Edge softness: 0.0-1.0. Keep low (0.0-0.08) or entire frame becomes transparent. Default: 0.02 */
|
|
19
|
+
blend?: number;
|
|
20
|
+
/** Enable despill to remove green/blue color contamination on edges. Default: true */
|
|
21
|
+
despill?: boolean;
|
|
22
|
+
/** Background replacement — video file, image file, or hex color (e.g., '000000' for black). Required. */
|
|
23
|
+
background: string;
|
|
24
|
+
/** Use colorkey (RGB) instead of chromakey (YUV). Default: false (chromakey) */
|
|
25
|
+
useColorkey?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare function applyChromaKey(config: ChromaKeyConfig): Promise<string>;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chroma Key Engine — Green screen removal and background replacement.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - chromakey (YUV space, best for green/blue screens)
|
|
6
|
+
* - colorkey (RGB space, best for arbitrary key colors)
|
|
7
|
+
* - despill (removes green/blue color spill on edges)
|
|
8
|
+
* - Composite onto replacement background (video, image, or solid color)
|
|
9
|
+
*/
|
|
10
|
+
import { execFile } from 'child_process';
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
import { logger } from '../../lib/logger.js';
|
|
14
|
+
// ─── Helpers ────────────────────────────────────────────────────────
|
|
15
|
+
function runFfmpeg(args, timeoutMs = 300_000) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
execFile('ffmpeg', args, { maxBuffer: 100 * 1024 * 1024, timeout: timeoutMs }, (error, stdout, stderr) => {
|
|
18
|
+
if (error) {
|
|
19
|
+
logger.error(`ffmpeg failed: ${stderr}`);
|
|
20
|
+
reject(new Error(`ffmpeg failed: ${stderr || error.message}`));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
resolve(stdout);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function ensureDir(filePath) {
|
|
28
|
+
const dir = path.dirname(filePath);
|
|
29
|
+
if (!fs.existsSync(dir))
|
|
30
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
function assertExists(filePath, label = 'File') {
|
|
33
|
+
if (!fs.existsSync(filePath))
|
|
34
|
+
throw new Error(`${label} not found: ${filePath}`);
|
|
35
|
+
}
|
|
36
|
+
function fileInfo(filePath) {
|
|
37
|
+
const stats = fs.statSync(filePath);
|
|
38
|
+
return `${(stats.size / 1024 / 1024).toFixed(2)} MB`;
|
|
39
|
+
}
|
|
40
|
+
function isHexColor(s) {
|
|
41
|
+
return /^[0-9a-fA-F]{6}$/.test(s);
|
|
42
|
+
}
|
|
43
|
+
function isImageFile(filePath) {
|
|
44
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
45
|
+
return ['.png', '.jpg', '.jpeg', '.webp', '.bmp', '.tiff'].includes(ext);
|
|
46
|
+
}
|
|
47
|
+
// ─── Main Function ──────────────────────────────────────────────────
|
|
48
|
+
export async function applyChromaKey(config) {
|
|
49
|
+
const { inputPath, outputPath, keyColor = '00FF00', similarity = 0.15, blend = 0.02, despill: enableDespill = true, background, useColorkey = false, } = config;
|
|
50
|
+
assertExists(inputPath, 'Input video');
|
|
51
|
+
ensureDir(outputPath);
|
|
52
|
+
// Validate key color
|
|
53
|
+
const cleanColor = keyColor.replace(/^#|^0x/, '');
|
|
54
|
+
if (!isHexColor(cleanColor)) {
|
|
55
|
+
throw new Error(`Invalid key color: ${keyColor}. Use 6-digit hex (e.g., 00FF00 for green)`);
|
|
56
|
+
}
|
|
57
|
+
const sim = Math.max(0.01, Math.min(1, similarity));
|
|
58
|
+
const bld = Math.max(0, Math.min(0.1, blend)); // Cap at 0.1 to prevent full-frame transparency
|
|
59
|
+
logger.info(`Chroma key: color=0x${cleanColor}, sim=${sim}, blend=${bld}, despill=${enableDespill}, mode=${useColorkey ? 'colorkey' : 'chromakey'}`);
|
|
60
|
+
// Build the keying filter
|
|
61
|
+
const keyFilter = useColorkey
|
|
62
|
+
? `colorkey=0x${cleanColor}:${sim}:${bld}`
|
|
63
|
+
: `chromakey=0x${cleanColor}:${sim}:${bld}`;
|
|
64
|
+
// Optional despill
|
|
65
|
+
const despillFilter = enableDespill
|
|
66
|
+
? getDespillFilter(cleanColor)
|
|
67
|
+
: '';
|
|
68
|
+
const fgFilter = despillFilter
|
|
69
|
+
? `${keyFilter},${despillFilter}`
|
|
70
|
+
: keyFilter;
|
|
71
|
+
// Determine background type
|
|
72
|
+
const isSolidColor = isHexColor(background.replace(/^#|^0x/, ''));
|
|
73
|
+
const isImage = !isSolidColor && isImageFile(background);
|
|
74
|
+
const isVideo = !isSolidColor && !isImage;
|
|
75
|
+
if (isVideo)
|
|
76
|
+
assertExists(background, 'Background video');
|
|
77
|
+
if (isImage)
|
|
78
|
+
assertExists(background, 'Background image');
|
|
79
|
+
let args;
|
|
80
|
+
if (isSolidColor) {
|
|
81
|
+
// Solid color background — use color source
|
|
82
|
+
const bgColor = background.replace(/^#|^0x/, '');
|
|
83
|
+
const filterComplex = [
|
|
84
|
+
`color=c=0x${bgColor}:s=1920x1080:r=30[bg]`,
|
|
85
|
+
`[0:v]${fgFilter}[fg]`,
|
|
86
|
+
`[bg][fg]overlay=shortest=1[out]`,
|
|
87
|
+
].join(';');
|
|
88
|
+
args = [
|
|
89
|
+
'-y', '-i', inputPath,
|
|
90
|
+
'-filter_complex', filterComplex,
|
|
91
|
+
'-map', '[out]', '-map', '0:a?',
|
|
92
|
+
'-c:v', 'libx264', '-crf', '18', '-preset', 'medium',
|
|
93
|
+
'-pix_fmt', 'yuv420p', '-movflags', '+faststart',
|
|
94
|
+
outputPath,
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
else if (isImage) {
|
|
98
|
+
// Image background — use loop to make it a stream
|
|
99
|
+
const filterComplex = [
|
|
100
|
+
`[1:v]${fgFilter}[fg]`,
|
|
101
|
+
`[0:v][fg]overlay=shortest=1[out]`,
|
|
102
|
+
].join(';');
|
|
103
|
+
args = [
|
|
104
|
+
'-y',
|
|
105
|
+
'-loop', '1', '-i', background,
|
|
106
|
+
'-i', inputPath,
|
|
107
|
+
'-filter_complex', filterComplex,
|
|
108
|
+
'-map', '[out]', '-map', '1:a?',
|
|
109
|
+
'-c:v', 'libx264', '-crf', '18', '-preset', 'medium',
|
|
110
|
+
'-pix_fmt', 'yuv420p', '-movflags', '+faststart',
|
|
111
|
+
'-shortest',
|
|
112
|
+
outputPath,
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Video background
|
|
117
|
+
const filterComplex = [
|
|
118
|
+
`[1:v]${fgFilter}[fg]`,
|
|
119
|
+
`[0:v][fg]overlay=shortest=1[out]`,
|
|
120
|
+
].join(';');
|
|
121
|
+
args = [
|
|
122
|
+
'-y',
|
|
123
|
+
'-i', background,
|
|
124
|
+
'-i', inputPath,
|
|
125
|
+
'-filter_complex', filterComplex,
|
|
126
|
+
'-map', '[out]', '-map', '1:a?',
|
|
127
|
+
'-c:v', 'libx264', '-crf', '18', '-preset', 'medium',
|
|
128
|
+
'-pix_fmt', 'yuv420p', '-movflags', '+faststart',
|
|
129
|
+
'-shortest',
|
|
130
|
+
outputPath,
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
await runFfmpeg(args, 600_000); // Longer timeout for compositing
|
|
134
|
+
logger.info(`Chroma key applied: ${outputPath} (${fileInfo(outputPath)})`);
|
|
135
|
+
return outputPath;
|
|
136
|
+
}
|
|
137
|
+
/** Get despill filter based on key color */
|
|
138
|
+
function getDespillFilter(hexColor) {
|
|
139
|
+
const r = parseInt(hexColor.substring(0, 2), 16);
|
|
140
|
+
const g = parseInt(hexColor.substring(2, 4), 16);
|
|
141
|
+
const b = parseInt(hexColor.substring(4, 6), 16);
|
|
142
|
+
// Determine dominant channel for despill
|
|
143
|
+
if (g > r && g > b) {
|
|
144
|
+
// Green screen — despill green
|
|
145
|
+
return 'despill=type=0:mix=0.5:green=-1';
|
|
146
|
+
}
|
|
147
|
+
else if (b > r && b > g) {
|
|
148
|
+
// Blue screen — despill blue
|
|
149
|
+
return 'despill=type=0:mix=0.5:blue=-1';
|
|
150
|
+
}
|
|
151
|
+
// For other colors, skip despill
|
|
152
|
+
return '';
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=chroma-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chroma-key.js","sourceRoot":"","sources":["../../../src/tools/engine/chroma-key.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video concatenation engine — merge multiple clips with cinematic transitions
|
|
3
|
+
*/
|
|
4
|
+
export declare const TRANSITIONS: readonly ["fade", "fadeblack", "fadewhite", "dissolve", "wipeleft", "wiperight", "wipeup", "wipedown", "slideleft", "slideright", "slideup", "slidedown", "smoothleft", "smoothright", "smoothup", "smoothdown", "circlecrop", "circleopen", "circleclose", "rectcrop", "vertopen", "vertclose", "horzopen", "horzclose", "diagtl", "diagtr", "diagbl", "diagbr", "hlslice", "hrslice", "vuslice", "vdslice", "radial", "pixelize"];
|
|
5
|
+
export type TransitionType = typeof TRANSITIONS[number];
|
|
6
|
+
export interface ConcatClip {
|
|
7
|
+
/** Path to video file */
|
|
8
|
+
path: string;
|
|
9
|
+
/** Optional: trim start time (seconds) */
|
|
10
|
+
trimStart?: number;
|
|
11
|
+
/** Optional: trim end time (seconds) */
|
|
12
|
+
trimEnd?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface ConcatConfig {
|
|
15
|
+
/** Video clips to concatenate (in order) */
|
|
16
|
+
clips: ConcatClip[];
|
|
17
|
+
/** Output path */
|
|
18
|
+
outputPath: string;
|
|
19
|
+
/** Transition between clips (default: fade) */
|
|
20
|
+
transition?: TransitionType;
|
|
21
|
+
/** Transition duration in seconds (default: 1) */
|
|
22
|
+
transitionDuration?: number;
|
|
23
|
+
/** Normalize all clips to this resolution (default: 1920x1080) */
|
|
24
|
+
targetWidth?: number;
|
|
25
|
+
targetHeight?: number;
|
|
26
|
+
/** Target FPS (default: 60) */
|
|
27
|
+
targetFps?: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function concatenateVideos(config: ConcatConfig): Promise<string>;
|
|
30
|
+
export interface IntroConfig {
|
|
31
|
+
/** Text to display */
|
|
32
|
+
text: string;
|
|
33
|
+
/** Subtitle (optional) */
|
|
34
|
+
subtitle?: string;
|
|
35
|
+
/** Duration in seconds (default: 3) */
|
|
36
|
+
duration?: number;
|
|
37
|
+
/** Background color (default: #0a0a0a) */
|
|
38
|
+
backgroundColor?: string;
|
|
39
|
+
/** Text color (default: white) */
|
|
40
|
+
textColor?: string;
|
|
41
|
+
/** Resolution */
|
|
42
|
+
width?: number;
|
|
43
|
+
height?: number;
|
|
44
|
+
/** FPS (default: 60) */
|
|
45
|
+
fps?: number;
|
|
46
|
+
/** Output path */
|
|
47
|
+
outputPath: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function generateIntro(config: IntroConfig): Promise<string>;
|