import { createServer } from 'node:http'; import { readFile } from 'node:fs/promises'; import { resolve, extname } from 'node:path'; export const layouts = ['classic', 'f7-swipe', 'glass-stage', 'bento-hub']; export const themes = ['dark', 'light', 'contrast']; export async function serve(frontend = 'frontend') { const root = resolve(frontend); const mime = { '.html':'text/html', '.js':'text/javascript', '.mjs':'text/javascript', '.css':'text/css', '.json':'application/json', '.woff2':'font/woff2', '.svg':'image/svg+xml', '.png':'image/png' }; const server = createServer(async (req, res) => { const pathname = new URL(req.url, 'http://localhost').pathname; const path = resolve(root, '.' + (pathname === '/' ? '/index.html' : pathname)); if (!path.startsWith(root + '/')) { res.writeHead(403).end(); return; } try { const body = await readFile(path); res.writeHead(200, { 'Content-Type':mime[extname(path)] || 'application/octet-stream' }).end(body); } catch { res.writeHead(404).end(); } }); await new Promise(r => server.listen(0, '127.0.0.1', r)); return { origin:`http://127.0.0.1:${server.address().port}`, close:() => new Promise(r => server.close(r)) }; } export async function contextFor(browser, origin, layout = 'classic', theme = 'dark') { const context = await browser.newContext({ viewport:{width:414,height:896}, deviceScaleFactor:2, isMobile:true, hasTouch:true, serviceWorkers:'block', reducedMotion:'reduce' }); await context.route('**/*', route => route.request().url().startsWith(origin) ? route.continue() : route.abort()); await context.addInitScript(({layout,theme}) => { localStorage.setItem('_ytpdata', JSON.stringify({ playlists:[], history:[], queue:[], settings:{layout,theme,reduceMotion:true} })); window.SpeechRecognition = window.webkitSpeechRecognition = function() {}; Object.defineProperty(navigator.connection || navigator, 'saveData', {value:true,configurable:true}); }, {layout,theme}); return context; } export async function appearance(page, layout, theme = 'dark') { await page.evaluate(async ({layout,theme}) => { if (window.Lazy) await Lazy.load('layout:' + layout); data.settings.layout = layout; data.settings.theme = theme; applyAppearance(); await document.fonts.ready; }, {layout,theme}); }