apple creative-assets: App Store Header and Search Results (PRODUCT_PAGE_HEADER_ASSET, APP_STORE_SEARCH_RESULTS_ASSET) through the App Asset Library: local size / alpha / video checks, upload once by MD5, select only approved assets, replace on a changed file; check / validate note, MCP tool, gallery and desktop Art row, docs, pitfall 75

This commit is contained in:
CRTkafa 2026-10-18 13:09:38 +07:00
parent 14ffe3545a
commit e5369090fa
17 changed files with 486 additions and 16 deletions

View file

@ -6,13 +6,14 @@
// `shots check`: size, alpha, bytes, counts, text overflow from report.json, video rules) are outlined in red (error)
// or amber (warning) with the message under the tile. Images are linked by relative path, so the page stays small and
// always shows the current files; move it together with the renders folder.
import { existsSync, writeFileSync } from 'node:fs';
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { join, relative } from 'node:path';
import type { Ctx } from '../lib/ctx';
import { done, log, printJson, warn } from '../lib/log';
import { SIZES } from './lib';
import { allIssues, requiredSets, type RendersScan, resolvedTablets, scanRenders } from './report';
import { type Issue, IMAGE_SPECS, VIDEO_TARGETS } from './spec';
import { allIssues, ffprobe, requiredSets, type RendersScan, resolvedTablets, scanRenders } from './report';
import { type Issue, IMAGE_SPECS, imageInfo, VIDEO_TARGETS } from './spec';
import { creativeRows } from './creative';
export interface GalleryTile {
/** Path relative to the gallery file. */
@ -245,6 +246,13 @@ export async function run(ctx: Ctx): Promise<void> {
const scan = scanRenders(dir, { langs, variants, required: lang => requiredSets(config, lang, tablets) });
if (!scan.images.length && !scan.videos.length) warn('shots', `no renders in ${dir} (storebound shots render)`);
const data = galleryData(scan, { at: join(out, '..'), title: `${config.app.name} · store renders`, exists: existsSync });
// App Store Header and Search Results assets (<lang>/creative/), base variant only
if (config.apple) for (const [lang, row] of creativeRows(dir, langs, f => relative(join(out, '..'), f).split('\\').join('/'), ffprobe, f => readFileSync(f), imageInfo)) {
const base = data.variants[0]!;
let l = base.langs.find(x => x.lang === lang);
if (!l) base.langs.push(l = { lang, rows: [] });
l.rows.push(row);
}
// --json: the data only (the desktop gallery), with the renders folder relative to the app folder; writes nothing
if (a.has('json')) { printJson({ ...data, dir: relative(ctx.loaded.root, dir).split('\\').join('/') || '.' }); return; }
if (a.has('dry')) { log('shots', `would write ${out}: ${data.totals.images} images, ${data.totals.videos} videos, ${data.totals.errors} errors`); return; }