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

@@ -59,6 +59,11 @@ const navigationItemSchema = z.object({
label: visibleString,
}).strict();
const soundtrackAssetSourcesSchema = z.object({
opusAssetId: opaqueId,
mp3AssetId: opaqueId,
}).strict();
const mediaPresentationBase = {
assetId: opaqueId,
alt: visibleString,
@@ -194,6 +199,10 @@ const portfolioContentSchema = z.object({
description: visibleString,
socialPreviewAssetId: opaqueId.optional(),
}).strict(),
soundtrack: z.object({
intro: soundtrackAssetSourcesSchema,
main: soundtrackAssetSourcesSchema,
}).strict().optional(),
sceneDescription: visibleString,
}).strict().superRefine((content, context) => {
const navigationIds = content.navigation.map((item) => item.id);
@@ -220,7 +229,11 @@ const portfolioContentSchema = z.object({
}
});
const mediaTypes = ['image/avif', 'image/jpeg', 'image/png', 'image/svg+xml', 'image/webp', 'video/mp4', 'video/webm'] as const;
const mediaTypes = [
'image/avif', 'image/jpeg', 'image/png', 'image/svg+xml', 'image/webp',
'video/mp4', 'video/webm',
'audio/ogg', 'audio/mpeg',
] as const;
const fileExtensionByMediaType: Record<(typeof mediaTypes)[number], string> = {
'image/avif': 'avif',
'image/jpeg': 'jpg',
@@ -229,10 +242,12 @@ const fileExtensionByMediaType: Record<(typeof mediaTypes)[number], string> = {
'image/webp': 'webp',
'video/mp4': 'mp4',
'video/webm': 'webm',
'audio/ogg': 'opus',
'audio/mpeg': 'mp3',
};
const publicAssetSchema = z.object({
id: opaqueId,
fileName: z.string().regex(/^[a-z0-9][a-z0-9-]*\.(avif|jpg|png|svg|webp|mp4|webm)$/, 'Asset filenames must be generated safe filenames.'),
fileName: z.string().regex(/^[a-z0-9][a-z0-9-]*\.(avif|jpg|png|svg|webp|mp4|webm|opus|mp3)$/, 'Asset filenames must be generated safe filenames.'),
sha256: z.string().regex(/^[a-f0-9]{64}$/, 'Asset sha256 must be lowercase hexadecimal.'),
mediaType: z.enum(mediaTypes),
width: z.number().int().positive().nullable(),
@@ -246,12 +261,17 @@ const publicAssetSchema = z.object({
context.addIssue({ code: 'custom', message: 'Asset filename extension must match media type.', path: ['fileName'] });
}
const image = asset.mediaType.startsWith('image/');
const video = asset.mediaType.startsWith('video/');
const audio = asset.mediaType.startsWith('audio/');
if (image && (asset.width === null || asset.height === null || asset.durationMs !== null)) {
context.addIssue({ code: 'custom', message: 'Image assets require dimensions and no duration.', path: ['width'] });
}
if (!image && (asset.width === null || asset.height === null || asset.durationMs === null)) {
if (video && (asset.width === null || asset.height === null || asset.durationMs === null)) {
context.addIssue({ code: 'custom', message: 'Video assets require dimensions and duration.', path: ['durationMs'] });
}
if (audio && (asset.width !== null || asset.height !== null || asset.durationMs === null)) {
context.addIssue({ code: 'custom', message: 'Audio assets require null dimensions and a positive duration.', path: ['durationMs'] });
}
});
const publicAssetManifestSchema = z.object({ assets: z.array(publicAssetSchema) }).strict().superRefine((manifest, context) => {
const ids = manifest.assets.map((asset) => asset.id);
@@ -296,6 +316,12 @@ export const parseProductionInputSnapshot = (input: unknown): ProductionInputs =
...content.toolkit.flatMap((group) => group.technologies.flatMap((technology) =>
technology.assetId === undefined ? [] : [technology.assetId])),
...(content.metadata.socialPreviewAssetId === undefined ? [] : [content.metadata.socialPreviewAssetId]),
...(content.soundtrack === undefined ? [] : [
content.soundtrack.intro.opusAssetId,
content.soundtrack.intro.mp3AssetId,
content.soundtrack.main.opusAssetId,
content.soundtrack.main.mp3AssetId,
]),
];
const seenReferences = new Set<string>();
for (const assetId of referencedAssetIds) {

View File

@@ -133,6 +133,16 @@ export interface MetadataContent {
socialPreviewAssetId?: string;
}
export interface SoundtrackAssetSources {
opusAssetId: string;
mp3AssetId: string;
}
export interface SoundtrackContent {
intro: SoundtrackAssetSources;
main: SoundtrackAssetSources;
}
export interface PortfolioContent {
identity: {
name: 'Luca Pîslaru';
@@ -148,6 +158,7 @@ export interface PortfolioContent {
toolkit: readonly ToolkitGroup[];
contact: ContactContent;
metadata: MetadataContent;
soundtrack?: SoundtrackContent;
sceneDescription: string;
}
@@ -158,7 +169,9 @@ export type PublicAssetMediaType =
| 'image/svg+xml'
| 'image/webp'
| 'video/mp4'
| 'video/webm';
| 'video/webm'
| 'audio/ogg'
| 'audio/mpeg';
export interface PublicAsset {
id: string;