const express = require('express'); const { chromium } = require('playwright'); const bodyParser = require('body-parser'); const cors = require('cors'); const app = express(); app.use(bodyParser.json()); app.use(cors()); // Launch browser once to reuse let browser; (async () => { browser = await chromium.launch({ headless: true }); })(); // Route: serve minimal HTML const html = ` YouTube Transcript Generator

YouTube Transcript Generator

`; app.get('/', (_, res) => res.send(html)); app.post('/transcript', async (req, res) => { const { url } = req.body; if (!url) return res.status(400).json({ error: 'URL required' }); const context = await browser.newContext(); // Block images, styles, fonts, media for speed await context.route('**/*', route => { const req = route.request(); const t = req.resourceType(); if (['image', 'stylesheet', 'font', 'media'].includes(t)) route.abort(); else route.continue(); }); const page = await context.newPage(); try { await page.goto(url, { waitUntil: 'domcontentloaded' }); // Open transcript menu const moreBtn = await page.$('button[aria-label="More actions"]'); if (moreBtn) await moreBtn.click(); await page.click('tp-yt-paper-item[role="menuitem"]:has-text("Open transcript")'); await page.waitForSelector('ytd-transcript-renderer', { timeout: 5000 }); const transcript = await page.$$eval( 'ytd-transcript-segment-renderer .segment-text', els => els.map(el => el.textContent.trim()).join('\n') ); res.json({ transcript }); } catch (err) { console.error(err); res.json({ error: 'Failed to extract transcript' }); } finally { await context.close(); } }); const PORT = process.env.PORT || 7860; app.listen(PORT, () => console.log(`Server running on ${PORT}`));