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}`));