// O's film-time seam probe (29 Oct): node _tools/o_fprobe.cjs "f1,f2" [halfwin=0.05] [step=1/60] [--page x.html] [--pre js] // prints, per film time, scene t and the joint of either body that moved most since the previous sample (a seam finder). Headless. const fs = require('fs'), os = require('os'), path = require('path'); let chromium; try { ({ chromium } = require('playwright')); } catch (e) { ({ chromium } = require('/Users/basha/node_modules/playwright')); } const A = process.argv.slice(2), pos = [], opt = {}; for (let i = 0; i < A.length; i++) { const a = A[i]; if (a.startsWith('--')) opt[a.slice(2)] = A[++i]; else pos.push(a); } const [key, g, fl, hw0, st0] = pos; const CHROME = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', '/usr/bin/google-chrome', '/usr/bin/chromium'].find((p) => fs.existsSync(p)); const ROOT = path.resolve(__dirname, '..'); (async () => { const udd = fs.mkdtempSync(path.join(os.tmpdir(), 'k58oprobe-')); const ctx = await chromium.launchPersistentContext(udd, Object.assign({ headless: true, args: ['--headless=new', '--allow-file-access-from-files'], viewport: { width: 1200, height: 900 } }, CHROME ? { executablePath: CHROME } : {})); try { const page = await ctx.newPage(); const pg = opt.page || '58k_' + key.split('.')[0] + '.html'; await page.goto('file://' + path.join(ROOT, pg)); await page.waitForFunction(() => document.documentElement.classList.contains('k58-loaded'), null, { timeout: 150000 }); const r = await page.evaluate(([key, g, fl, hw, st, pre]) => { if (pre) (0, eval)(pre); const d = K58.get(key), v = K58.offscreen(640, 295, {}); const FS = K58.FSTEP || 1 / 180; const out = []; const N = ['hip', 'mid', 'neck', 'head', 'sh', 'eF', 'hF', 'eB', 'hB', 'kF', 'fF', 'kB', 'fB']; const grab = (b) => N.map((n) => { try { return b.at(n); } catch (e) { return [0, 0]; } }); for (const f0 of fl.split(',').map(Number)) { v.load(d, { gore: +g }); let prev = null; const rows = []; for (let f = Math.max(0, f0 - hw); f <= f0 + hw + 1e-6; f += st) { v.advance(Math.round(f / FS), 1e9); const s = v.scene; const J = { V: grab(s.victim), A: grab(s.killer) }; let worst = null; if (prev) for (const who of ['V', 'A']) for (let i = 0; i < N.length; i++) { const dd = Math.hypot(J[who][i][0] - prev[who][i][0], J[who][i][1] - prev[who][i][1]); if (!worst || dd > worst.d) worst = { who, j: N[i], d: Math.round(dd * 10) / 10 }; } rows.push(f.toFixed(3) + ' t=' + s.t.toFixed(3) + (worst ? ' ' + worst.who + ' ' + worst.j + ' ' + worst.d : '') + ' | V ' + J.V.map((p) => p.map(Math.round).join(',')).join(' ')); prev = J; } out.push('== film ' + f0, ...rows); } K58.offRelease && K58.offRelease(v); return out; }, [key, g, fl, +(hw0 || 0.05), +(st0 || 1 / 60), opt.pre || '']); console.log(r.join('\n')); } catch (e) { console.error(e); } finally { await Promise.race([ctx.close(), new Promise((r) => setTimeout(r, 10000))]); try { fs.rmSync(udd, { recursive: true, force: true }); } catch (e) {} } process.exit(0); })();