// Holt ein Standbild aus dem Hero-Video von Luximmo: Es steht als Hintergrundbild der Sektion, // bis das Video läuft (und für immer bei „Bewegung reduzieren“ oder Datensparen). ffmpeg gibt es // hier nicht, Chromium spielt WebM ab und zeichnet das Bild in ein Canvas. // // Aufruf: node scripts/build-luximmo-poster.mjs [Sekunde] import { readFileSync, writeFileSync } from 'node:fs'; import { chromium } from 'playwright'; const [quelle, ziel, sekunde = '0.4'] = process.argv.slice(2); if (!quelle || !ziel) { console.error('Aufruf: node scripts/build-luximmo-poster.mjs [Sekunde]'); process.exit(1); } const browser = await chromium.launch(); try { const page = await browser.newPage(); await page.setContent(''); const daten = readFileSync(quelle).toString('base64'); const png = await page.evaluate(async ([b64, t]) => { const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)); const video = document.querySelector('video'); video.src = URL.createObjectURL(new Blob([bytes], { type: 'video/webm' })); await new Promise((ok, fehler) => { video.onloadeddata = ok; video.onerror = () => fehler(new Error('Video lässt sich nicht lesen')); }); video.currentTime = Math.min(Number(t), Math.max(0, video.duration - 0.1)); await new Promise((ok) => { video.onseeked = ok; }); const canvas = document.createElement('canvas'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; canvas.getContext('2d').drawImage(video, 0, 0); return { url: canvas.toDataURL('image/png'), breite: video.videoWidth, hoehe: video.videoHeight, dauer: video.duration }; }, [daten, sekunde]); writeFileSync(ziel, Buffer.from(png.url.split(',')[1], 'base64')); console.log(`Standbild ${png.breite} × ${png.hoehe} (Video ${png.dauer.toFixed(1)} s) → ${ziel}`); } finally { await browser.close(); }