bed4dc5a8f
- Regenerate 60 covers (30 male + 30 female) via FLUX with story-specific prompts, replacing the prior gender-shared set - Crop covers to 4:5 (960×1200) via sharp attention cover; matches new homepage card aspectRatio - Persist all 60 prompts to public/home/prompts.json so the prebake step can reuse the cover's exact visual anchor (per-card styleGuide) and the first-act scene visually carries over from the poster the player clicked - Restore /play?card= prebaked instant-play path on homepage card click - Add OpenAI-compatible image route in ai-client for non-Runware endpoints - Hide Next.js dev indicators globally; tweak F-key fullscreen label Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
52 lines
1.7 KiB
JavaScript
52 lines
1.7 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* Compresses the freshly generated public/home/*.png into much
|
||
* smaller .webp files alongside them, then deletes the originals.
|
||
* Crops each image to a 4:5 vertical aspect ratio (matching the homepage
|
||
* StoryCard layout in app/page.tsx) using sharp's smart-attention cover
|
||
* strategy, so the most salient subject stays in frame. Output webps
|
||
* target 960×1200 at quality 78.
|
||
*/
|
||
|
||
import { readdirSync, statSync, unlinkSync } from "node:fs";
|
||
import { resolve, dirname, extname, basename } from "node:path";
|
||
import { fileURLToPath } from "node:url";
|
||
import sharp from "sharp";
|
||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
const DIR = resolve(__dirname, "..", "public", "home");
|
||
|
||
// 4:5 final, 1200 long edge → 960×1200
|
||
const TARGET_W = 960;
|
||
const TARGET_H = 1200;
|
||
const QUALITY = 78;
|
||
|
||
const files = readdirSync(DIR).filter((f) => f.toLowerCase().endsWith(".png"));
|
||
let totalIn = 0;
|
||
let totalOut = 0;
|
||
|
||
for (const f of files) {
|
||
const inPath = resolve(DIR, f);
|
||
const outPath = resolve(DIR, basename(f, extname(f)) + ".webp");
|
||
const inSize = statSync(inPath).size;
|
||
totalIn += inSize;
|
||
|
||
await sharp(inPath)
|
||
.resize({
|
||
width: TARGET_W,
|
||
height: TARGET_H,
|
||
fit: "cover",
|
||
position: sharp.strategy.attention,
|
||
})
|
||
.webp({ quality: QUALITY, effort: 5 })
|
||
.toFile(outPath);
|
||
const outSize = statSync(outPath).size;
|
||
totalOut += outSize;
|
||
console.log(`${f.padEnd(16)} ${(inSize / 1024).toFixed(0).padStart(5)} KB → ${(outSize / 1024).toFixed(0).padStart(4)} KB`);
|
||
unlinkSync(inPath);
|
||
}
|
||
|
||
console.log(
|
||
`\nTotal: ${(totalIn / 1024 / 1024).toFixed(1)} MB → ${(totalOut / 1024 / 1024).toFixed(2)} MB`,
|
||
);
|