| 1 | import { chromium } from "playwright-core"; |
| 2 | import { readFileSync, readdirSync, renameSync, mkdirSync } from "fs"; |
| 3 | |
| 4 | const EXE = "/root/.npm/_npx/361ceb562f3b3235/node_modules/playwright-core/.local-browsers/chromium-1228/chrome-linux64/chrome"; |
| 5 | const SCR = "/tmp/claude-0/-shared-projects/26d7af11-61c4-4183-85c3-70d9aac72ef6/scratchpad"; |
| 6 | const REC = "/shared/projects/SteppingApp/promo/rec"; |
| 7 | const BASE = "http://REDACTED-IP:3000"; |
| 8 | const DJ = readFileSync(`${SCR}/.djid`, "utf8").trim(); |
| 9 | const EV = readFileSync(`${SCR}/.evid`, "utf8").trim(); |
| 10 | |
| 11 | const scenes = [ |
| 12 | { id: "s1", url: "/", vp: [1920, 1080], dur: 11.3, act: "hero" }, |
| 13 | { id: "s2", url: "/", vp: [1920, 1080], dur: 7.45, act: "toRoute" }, |
| 14 | { id: "s3", url: "/city/atlanta", vp: [1920, 1080], dur: 12.15, act: "scroll" }, |
| 15 | { id: "s4", url: `/city/atlanta/event/${EV}`, vp: [1920, 1080], dur: 8.18, act: "scroll" }, |
| 16 | { id: "s5", url: "/city/atlanta/yard", vp: [1920, 1080], dur: 11.3, act: "scroll" }, |
| 17 | { id: "s6", url: `/city/atlanta/profile/dj/${DJ}`, vp: [1920, 1080], dur: 5.55, act: "hold" }, |
| 18 | { id: "s7", url: "/city/atlanta/route", vp: [1920, 1080], dur: 11.5, act: "scroll" }, |
| 19 | { id: "s8", url: "/", vp: [430, 932], dur: 10.25, act: "scroll", mobile: true }, |
| 20 | { id: "s9", url: "/", vp: [1920, 1080], dur: 9.2, act: "hero" }, |
| 21 | ]; |
| 22 | |
| 23 | const args = ["--no-sandbox", "--disable-dev-shm-usage", "--use-gl=angle", "--use-angle=swiftshader", "--enable-unsafe-swiftshader", "--ignore-gpu-blocklist"]; |
| 24 | const browser = await chromium.launch({ executablePath: EXE, args }); |
| 25 | |
| 26 | for (const s of scenes) { |
| 27 | const dir = `${REC}/${s.id}`; |
| 28 | mkdirSync(dir, { recursive: true }); |
| 29 | const ctx = await browser.newContext({ |
| 30 | viewport: { width: s.vp[0], height: s.vp[1] }, |
| 31 | deviceScaleFactor: 1, |
| 32 | recordVideo: { dir, size: { width: s.vp[0], height: s.vp[1] } }, |
| 33 | ...(s.mobile ? { isMobile: true, hasTouch: true } : {}), |
| 34 | }); |
| 35 | const page = await ctx.newPage(); |
| 36 | await page.goto(`${BASE}${s.url}`, { waitUntil: "networkidle" }); |
| 37 | await page.waitForTimeout(s.act === "hero" ? 1800 : 900); |
| 38 | const ms = s.dur * 1000; |
| 39 | |
| 40 | if (s.act === "hero") { |
| 41 | const [W, H] = s.vp; |
| 42 | const pts = [[W * 0.6, H * 0.42], [W * 0.72, H * 0.55], [W * 0.54, H * 0.5], [W * 0.68, H * 0.46]]; |
| 43 | const per = ms / pts.length; |
| 44 | for (const [x, y] of pts) { |
| 45 | await page.mouse.move(x, y, { steps: 24 }); |
| 46 | await page.mouse.click(x, y); |
| 47 | await page.waitForTimeout(Math.max(per - 250, 200)); |
| 48 | } |
| 49 | } else if (s.act === "toRoute") { |
| 50 | await page.evaluate(() => { |
| 51 | const l = window.__lenis; const el = document.querySelector("#route"); |
| 52 | if (el && l) l.scrollTo(el.offsetTop - 90, { duration: 4.5 }); |
| 53 | }); |
| 54 | await page.waitForTimeout(ms); |
| 55 | } else if (s.act === "scroll") { |
| 56 | await page.evaluate((d) => { |
| 57 | const l = window.__lenis; const t = document.body.scrollHeight - window.innerHeight; |
| 58 | if (l) l.scrollTo(t, { duration: d }); else window.scrollTo({ top: t, behavior: "smooth" }); |
| 59 | }, s.dur - 1.5); |
| 60 | await page.waitForTimeout(ms); |
| 61 | } else { |
| 62 | await page.waitForTimeout(ms); |
| 63 | } |
| 64 | |
| 65 | await ctx.close(); |
| 66 | const f = readdirSync(dir).find((x) => x.endsWith(".webm")); |
| 67 | renameSync(`${dir}/${f}`, `${REC}/${s.id}.webm`); |
| 68 | console.log(s.id, "recorded"); |
| 69 | } |
| 70 | await browser.close(); |
| 71 | console.log("ALL RECORDED"); |