feat: integrate authored nightdrive soundtrack

This commit is contained in:
2026-09-04 14:39:28 +03:00
parent eb7f276b91
commit 745fa8169e
39 changed files with 1013 additions and 714 deletions

View File

@@ -5,11 +5,12 @@ import { readFile, readdir, stat } from 'node:fs/promises';
import { relative, resolve, sep } from 'node:path';
import sharp from 'sharp';
import { fileTypeFromBuffer } from 'file-type';
import { probeVideo } from './lib/video-frames.mjs';
import { probeVideo, runMediaCommand } from './lib/video-frames.mjs';
import {
classifyArtifact,
evaluateBudgetTotals,
evaluateMediaAssetBudget,
normalizeDetectedMediaType,
resolveBudgetGateMode,
validateRuntimeBudgetTraceReport,
} from './lib/bundle-budgets.mjs';
@@ -34,7 +35,7 @@ const listFiles = async (directory) => {
return files;
};
const compressedSize = (path, bytes) => (/\.(?:avif|br|gif|jpe?g|mp4|png|webm|webp|woff2?)$/iu.test(path)
const compressedSize = (path, bytes) => (/\.(?:avif|br|gif|jpe?g|mp3|mp4|opus|png|webm|webp|woff2?)$/iu.test(path)
? bytes.byteLength
: brotliCompressSync(bytes, { params: { [zlibConstants.BROTLI_PARAM_QUALITY]: 11 } }).byteLength);
@@ -42,6 +43,21 @@ const readJson = async (path, label) => {
try { return JSON.parse(await readFile(path, 'utf8')); } catch { throw new Error(`${label} must be valid JSON.`); }
};
const probeAudio = async (path) => {
const { stdout } = await runMediaCommand('ffprobe', [
'-v', 'error', '-show_entries', 'format=duration:stream=codec_type', '-of', 'json', path,
], 'ASSET-AUDIO-PROBE-001', 20_000);
let result;
try { result = JSON.parse(stdout); } catch { throw new Error('ASSET-AUDIO-PROBE-001'); }
const streams = Array.isArray(result.streams) ? result.streams : [];
const duration = Number(result.format?.duration);
if (streams.length !== 1 || streams[0]?.codec_type !== 'audio'
|| !Number.isFinite(duration) || duration <= 0 || duration > 300) {
throw new Error('ASSET-AUDIO-PROBE-001');
}
return { duration };
};
const manifest = await readJson(resolve('content/public/asset-manifest.json'), 'Public asset manifest');
const content = await readJson(resolve('content/public/portfolio-content.json'), 'Portfolio content');
if (!Array.isArray(manifest?.assets)) throw new Error('Public asset manifest is invalid.');
@@ -59,6 +75,12 @@ for (const group of content.toolkit ?? []) for (const technology of group.techno
if (technology.assetId) assetClassById.set(technology.assetId, 'project-still');
}
if (content.metadata?.socialPreviewAssetId) assetClassById.set(content.metadata.socialPreviewAssetId, 'project-still');
if (content.soundtrack) {
for (const region of [content.soundtrack.intro, content.soundtrack.main]) {
if (region?.opusAssetId) assetClassById.set(region.opusAssetId, 'soundtrack-audio');
if (region?.mp3AssetId) assetClassById.set(region.mp3AssetId, 'soundtrack-audio');
}
}
const manifestByPath = new Map(manifest.assets.map((asset) => [`assets/${asset.fileName}`, asset]));
const records = [];
@@ -74,7 +96,8 @@ for (const absolute of await listFiles(root)) {
if (digest !== manifestAsset.sha256) throw new Error(`Published asset hash mismatch: ${path}`);
const mediaType = manifestAsset.mediaType;
const detected = await fileTypeFromBuffer(bytes);
const detectedMediaType = detected?.mime ?? (/^\s*<svg[\s>]/u.test(bytes.toString('utf8', 0, Math.min(bytes.length, 256))) ? 'image/svg+xml' : null);
const detectedMediaType = normalizeDetectedMediaType(detected?.mime)
?? (/^\s*<svg[\s>]/u.test(bytes.toString('utf8', 0, Math.min(bytes.length, 256))) ? 'image/svg+xml' : null);
if (detectedMediaType !== mediaType) throw new Error(`Manifest media type disagrees with deterministic probing: ${manifestAsset.id}`);
let probe;
if (mediaType?.startsWith('video/')) {
@@ -85,6 +108,10 @@ for (const absolute of await listFiles(root)) {
const image = await sharp(absolute, { failOn: 'error', limitInputPixels: 50_000_000 }).metadata();
probe = { mediaType, width: image.width, height: image.height, durationMs: null };
bucket = assetClassById.get(manifestAsset.id) === 'chart' ? 'chart' : 'project-still';
} else if (mediaType?.startsWith('audio/')) {
const audio = await probeAudio(absolute);
probe = { mediaType, width: null, height: null, durationMs: Math.round(audio.duration * 1_000) };
bucket = 'soundtrack-audio';
} else {
throw new Error(`Manifest asset has unsupported media type: ${manifestAsset.id}`);
}
@@ -92,11 +119,11 @@ for (const absolute of await listFiles(root)) {
manifestAsset,
probe,
compressedSize(path, bytes),
bucket === 'chart' ? 'chart' : 'project-still',
bucket === 'chart' ? 'chart' : bucket === 'soundtrack-audio' ? 'soundtrack-audio' : 'project-still',
).violations);
} else {
bucket = classifyArtifact(path, source);
if (['project-video', 'project-still', 'chart'].includes(bucket)) {
if (['project-video', 'project-still', 'chart', 'soundtrack-audio'].includes(bucket)) {
throw new Error(`Project media must be present in the public asset manifest: ${path}`);
}
}
@@ -114,6 +141,7 @@ const totals = {
deferredProjectVisuals: sum('project-still') + sum('chart'),
highTierExtras: sum('high-tier-extras'),
deferredNonVideo: sum('first-live-scene') + sum('high-tier-extras') + sum('project-still') + sum('chart'),
deferredAudio: sum('soundtrack-audio'),
};
const entry = records.find((record) => record.bucket === 'initial-application-js');