@happyvertical/smrt-video 0.45.1 → 0.45.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -136,9 +136,9 @@ Performer = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
136
136
  "delete"
137
137
  ] },
138
138
  mcp: { include: ["list", "get"] },
139
- cli: true
139
+ cli: { skipApiCheck: true }
140
140
  })], Performer);
141
141
  //#endregion
142
142
  export { Performer as t };
143
143
 
144
- //# sourceMappingURL=performer-DlDaK50F.js.map
144
+ //# sourceMappingURL=performer-CuiV1PPk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performer-CuiV1PPk.js","names":[],"sources":["../../src/performer.ts"],"sourcesContent":["/**\n * Performer Model\n *\n * Represents the physical likeness/face DNA for consistent face generation.\n * A Performer can play multiple Characters (PersonalityProfiles).\n * Ensures face consistency via IP-Adapter FaceID.\n */\n\nimport type { Asset } from '@happyvertical/smrt-assets';\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { crossPackageRef, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n assertValidVideoAssetRole,\n assertValidVideoAssetSortOrder,\n legacyVideoAssetMetaTypes,\n listCanonicalOwnedAssetIds,\n listLegacyOwnedAssetIds,\n mergeOwnedAssetIds,\n resolveOwnedAssets,\n} from './owned-asset-utils.js';\nimport type { PerformerAssetRole } from './performer-asset.js';\n\n/**\n * Performer DNA for IP-Adapter FaceID consistency\n */\nexport interface PerformerDNA {\n /** Gender */\n gender: 'male' | 'female' | 'neutral';\n\n /** Age range */\n ageRange:\n | 'child'\n | 'teen'\n | 'young_adult'\n | 'adult'\n | 'middle_aged'\n | 'senior';\n\n /** Face embedding for IP-Adapter FaceID (512-dim vector) */\n faceEmbedding?: number[];\n\n /** Default clothing style (can be overridden per Character) */\n defaultClothing?: {\n style: 'business' | 'casual' | 'formal' | 'outdoor';\n colors?: string[];\n description?: string;\n };\n\n /** IP-Adapter weight for consistency (0.5-1.0) */\n ipAdapterWeight: number;\n\n /** FaceID weight for face consistency */\n faceIdWeight?: number;\n}\n\n/**\n * Performer status\n */\nexport type PerformerStatus = 'pending' | 'ready';\n\n/**\n * Performer creation options\n */\nexport interface PerformerOptions extends SmrtObjectOptions {\n /** Human-readable name */\n name?: string;\n\n /** Description */\n description?: string | null;\n\n /** Performer DNA for consistent face generation */\n dna?: PerformerDNA;\n\n /** Reference images for IP-Adapter (multiple angles/expressions) */\n referenceAssetIds?: string[];\n\n /** Generated seed image asset ID */\n seedImageAssetId?: string | null;\n\n /** Default voice profile for this performer */\n voiceProfileId?: string | null;\n\n /** Performer status */\n status?: PerformerStatus;\n\n /** 1-1 profile record for this performer */\n profileId?: string | null;\n\n /** Tenant ID for multi-tenant isolation */\n tenantId?: string | null;\n}\n\n/**\n * Performer - The physical likeness/face DNA\n *\n * A Performer represents a person's visual identity for consistent\n * face generation across multiple Characters. One Performer can play\n * many Characters (e.g., same face as \"Evening Anchor\" and \"Weekend Host\").\n *\n * @example\n * ```typescript\n * import { Performer } from '@happyvertical/smrt-video';\n *\n * const performer = new Performer({\n * name: 'Alex Bentley',\n * dna: {\n * gender: 'male',\n * ageRange: 'adult',\n * ipAdapterWeight: 0.75,\n * },\n * referenceAssetIds: ['ref-img-1', 'ref-img-2'],\n * });\n * await performer.save();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: {\n include: ['list', 'get', 'create', 'update', 'delete'],\n },\n mcp: {\n include: ['list', 'get'],\n },\n cli: { skipApiCheck: true },\n})\nexport class Performer extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Human-readable name */\n name: string = '';\n\n /** Description */\n description: string | null = null;\n\n /** Performer DNA for consistent face generation */\n dna: PerformerDNA = {\n gender: 'neutral',\n ageRange: 'adult',\n ipAdapterWeight: 0.7,\n };\n\n /** Reference images for IP-Adapter (multiple angles/expressions) */\n referenceAssetIds: string[] = [];\n\n /** Generated seed image asset ID */\n @crossPackageRef('@happyvertical/smrt-assets:Asset')\n seedImageAssetId: string | null = null;\n\n /** Default voice profile for this performer */\n @crossPackageRef('@happyvertical/smrt-voice:VoiceProfile')\n voiceProfileId: string | null = null;\n\n /** Performer status */\n status: PerformerStatus = 'pending';\n\n /** 1-1 profile record for this performer */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n profileId: string | null = null;\n\n constructor(options: PerformerOptions = {}) {\n super(options);\n\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.dna !== undefined) this.dna = options.dna;\n if (options.referenceAssetIds !== undefined)\n this.referenceAssetIds = options.referenceAssetIds;\n if (options.seedImageAssetId !== undefined)\n this.seedImageAssetId = options.seedImageAssetId;\n if (options.voiceProfileId !== undefined)\n this.voiceProfileId = options.voiceProfileId;\n if (options.status !== undefined) this.status = options.status;\n if (options.profileId !== undefined) this.profileId = options.profileId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n\n private async getPerformerAssetCollection() {\n const { PerformerOwnedAssetCollection } = await import(\n './performer-assets.js'\n );\n return PerformerOwnedAssetCollection.create({ db: this.db });\n }\n\n private getLegacyFieldAssetIds(role?: PerformerAssetRole): string[] {\n if (role === 'reference') {\n return this.referenceAssetIds;\n }\n\n if (role === 'seed') {\n return this.seedImageAssetId ? [this.seedImageAssetId] : [];\n }\n\n return [...this.referenceAssetIds, this.seedImageAssetId].filter(\n (assetId): assetId is string => Boolean(assetId),\n );\n }\n\n private setLegacyFieldAssetId(\n role: PerformerAssetRole,\n assetId: string,\n ): boolean {\n if (role === 'seed') {\n if (this.seedImageAssetId === assetId) {\n return false;\n }\n\n this.seedImageAssetId = assetId;\n return true;\n }\n\n if (this.referenceAssetIds.includes(assetId)) {\n return false;\n }\n\n this.referenceAssetIds = [...this.referenceAssetIds, assetId];\n return true;\n }\n\n private clearLegacyFieldAssetId(\n assetId: string,\n role?: PerformerAssetRole,\n ): boolean {\n let changed = false;\n\n if ((!role || role === 'seed') && this.seedImageAssetId === assetId) {\n this.seedImageAssetId = null;\n changed = true;\n }\n\n if (!role || role === 'reference') {\n const remaining = this.referenceAssetIds.filter(\n (referenceAssetId) => referenceAssetId !== assetId,\n );\n\n if (remaining.length !== this.referenceAssetIds.length) {\n this.referenceAssetIds = remaining;\n changed = true;\n }\n }\n\n return changed;\n }\n\n async getAssets(role?: PerformerAssetRole): Promise<Asset[]> {\n const canonicalAssetIds = this.id\n ? await listCanonicalOwnedAssetIds({\n tableName: 'performer_assets',\n loadLinks: async () =>\n (await this.getPerformerAssetCollection()).byLeft(\n this.id as string,\n role ? { role } : {},\n ),\n })\n : [];\n const legacyFieldAssetIds = this.getLegacyFieldAssetIds(role);\n const legacyOwnedAssetIds = this.id\n ? await listLegacyOwnedAssetIds({\n db: this.db,\n ownerColumn: 'performer_id',\n ownerId: this.id,\n role,\n metaTypes: legacyVideoAssetMetaTypes('PerformerAsset'),\n })\n : [];\n\n return resolveOwnedAssets(\n this.db,\n this.tenantId,\n mergeOwnedAssetIds(\n canonicalAssetIds,\n legacyFieldAssetIds,\n legacyOwnedAssetIds,\n ),\n );\n }\n\n async getAssetByRole(role: PerformerAssetRole): Promise<Asset | null> {\n const assets = await this.getAssets(role);\n return assets[0] || null;\n }\n\n async addAsset(\n asset: Asset,\n role: PerformerAssetRole = 'reference',\n sortOrder = 0,\n ): Promise<void> {\n if (!this.id || !asset.id) {\n throw new Error('Cannot associate unsaved performer or asset');\n }\n\n assertValidVideoAssetRole(role);\n assertValidVideoAssetSortOrder(sortOrder);\n\n const performerAssets = await this.getPerformerAssetCollection();\n await performerAssets.attach(this.id, asset.id, {\n role,\n sortOrder,\n tenantId: this.tenantId,\n });\n\n if (this.setLegacyFieldAssetId(role, asset.id)) {\n await this.save();\n }\n }\n\n async removeAsset(assetId: string, role?: PerformerAssetRole): Promise<void> {\n if (!this.id) {\n return;\n }\n\n const performerAssets = await this.getPerformerAssetCollection();\n await performerAssets.detach(this.id, assetId, role ? { role } : {});\n\n if (this.clearLegacyFieldAssetId(assetId, role)) {\n await this.save();\n }\n }\n\n /** Check if the performer has reference images */\n get hasReferences(): boolean {\n return this.referenceAssetIds.length > 0;\n }\n\n /** Check if the performer has a face embedding */\n get hasFaceEmbedding(): boolean {\n return (\n Array.isArray(this.dna.faceEmbedding) && this.dna.faceEmbedding.length > 0\n );\n }\n\n /** Check if the performer is ready for generation */\n get isReady(): boolean {\n return this.status === 'ready' && this.hasReferences;\n }\n}\n"],"mappings":"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"}
@@ -164,9 +164,9 @@ Scene = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
164
164
  "delete"
165
165
  ] },
166
166
  mcp: { include: ["list", "get"] },
167
- cli: true
167
+ cli: { skipApiCheck: true }
168
168
  })], Scene);
169
169
  //#endregion
170
170
  export { Scene as t };
171
171
 
172
- //# sourceMappingURL=scene-CCQdUj6S.js.map
172
+ //# sourceMappingURL=scene-uTNd4OuQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-uTNd4OuQ.js","names":[],"sources":["../../src/scene.ts"],"sourcesContent":["/**\n * Scene Model\n *\n * Represents a virtual production scene (background environment).\n * Supports 360° panoramas, standard images, and video backgrounds.\n */\n\nimport type { Asset } from '@happyvertical/smrt-assets';\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { crossPackageRef, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n assertValidVideoAssetRole,\n assertValidVideoAssetSortOrder,\n legacyVideoAssetMetaTypes,\n listCanonicalOwnedAssetIds,\n listLegacyOwnedAssetIds,\n mergeOwnedAssetIds,\n resolveOwnedAssets,\n} from './owned-asset-utils.js';\nimport type { SceneAssetRole } from './scene-asset.js';\n\n/**\n * Viewpoint extracted from 360° scene\n */\nexport interface SceneViewpoint {\n /** Viewpoint identifier */\n id: string;\n\n /** Human-readable name */\n name: string;\n\n /** Horizontal rotation (-180 to 180°) */\n pan: number;\n\n /** Vertical rotation (-90 to 90°) */\n tilt: number;\n\n /** Field of view (60-120°) */\n fov: number;\n\n /** Generated rectilinear image asset ID */\n extractedAssetId?: string;\n\n /** Viewpoint-specific lighting profile */\n lightingProfile?: LightingProfile;\n}\n\n/**\n * Lighting profile for IC-Light matching\n */\nexport interface LightingProfile {\n /** Dominant light direction (normalized vector) */\n direction?: { x: number; y: number; z: number };\n\n /** Light color temperature (Kelvin) */\n colorTemperature?: number;\n\n /** Ambient light intensity (0-1) */\n ambientIntensity?: number;\n\n /** Key light intensity (0-1) */\n keyLightIntensity?: number;\n\n /** Shadow softness (0-1) */\n shadowSoftness?: number;\n\n /** Environment map asset ID for reflections */\n envMapAssetId?: string;\n}\n\n/**\n * Anchor point for character placement in scene\n */\nexport interface AnchorPoint {\n /** Anchor point identifier */\n id: string;\n\n /** Human-readable name */\n name: string;\n\n /** Position as normalized coordinates (0-1) */\n position: { x: number; y: number };\n\n /** Suggested character scale at this point */\n suggestedScale: number;\n\n /** Ground plane Y coordinate for perspective */\n groundY: number;\n\n /** Optional viewpoint this anchor belongs to */\n viewpointId?: string;\n}\n\n/**\n * Scene source type\n */\nexport type SceneSourceType =\n | 'image'\n | 'video'\n | 'panorama_360'\n | 'panorama_180';\n\n/**\n * Scene projection type\n */\nexport type SceneProjection = 'equirectangular' | 'cubemap';\n\n/**\n * Scene status\n */\nexport type SceneStatus = 'pending' | 'processing' | 'ready' | 'failed';\n\n/**\n * Scene creation options\n */\nexport interface SceneOptions extends SmrtObjectOptions {\n /** Human-readable name */\n name?: string;\n\n /** Description */\n description?: string | null;\n\n /** Source media asset ID */\n sourceAssetId?: string | null;\n\n /** Type of source media */\n sourceType?: SceneSourceType;\n\n /** Projection type for panoramas */\n projection?: SceneProjection | null;\n\n /** Extracted camera angles from 360° panoramas */\n viewpoints?: SceneViewpoint[];\n\n /** Lighting analysis for IC-Light matching */\n lightingProfile?: LightingProfile | null;\n\n /** Location metadata */\n location?: {\n name: string;\n coordinates?: { lat: number; lng: number };\n } | null;\n\n /** Anchor points for character placement */\n anchorPoints?: AnchorPoint[];\n\n /** Scene status */\n status?: SceneStatus;\n\n /** Tenant ID for multi-tenant isolation */\n tenantId?: string | null;\n}\n\n/**\n * Scene for virtual production compositing\n *\n * Supports 360° panoramas, standard images, and video backgrounds\n * with viewpoint extraction, lighting analysis, and character placement.\n *\n * @example\n * ```typescript\n * import { Scene } from '@happyvertical/smrt-video';\n *\n * const scene = new Scene({\n * name: 'Town Hall Exterior',\n * sourceAssetId: 'asset-townhall-pano',\n * sourceType: 'panorama_360',\n * projection: 'equirectangular',\n * });\n * await scene.save();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: {\n include: ['list', 'get', 'create', 'update', 'delete'],\n },\n mcp: {\n include: ['list', 'get'],\n },\n cli: { skipApiCheck: true },\n})\nexport class Scene extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Human-readable name */\n name: string = '';\n\n /** Description */\n description: string | null = null;\n\n /** Source media asset ID */\n @crossPackageRef('@happyvertical/smrt-assets:Asset')\n sourceAssetId: string | null = null;\n\n /** Type of source media */\n sourceType: SceneSourceType = 'image';\n\n /** Projection type for panoramas */\n projection: SceneProjection | null = null;\n\n /** Extracted camera angles from 360° panoramas */\n viewpoints: SceneViewpoint[] = [];\n\n /** Lighting analysis for IC-Light matching */\n lightingProfile: LightingProfile | null = null;\n\n /** Location metadata */\n location: {\n name: string;\n coordinates?: { lat: number; lng: number };\n } | null = null;\n\n /** Anchor points for character placement */\n anchorPoints: AnchorPoint[] = [];\n\n /** Scene status */\n status: SceneStatus = 'pending';\n\n constructor(options: SceneOptions = {}) {\n super(options);\n\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.sourceAssetId !== undefined)\n this.sourceAssetId = options.sourceAssetId;\n if (options.sourceType !== undefined) this.sourceType = options.sourceType;\n if (options.projection !== undefined) this.projection = options.projection;\n if (options.viewpoints !== undefined) this.viewpoints = options.viewpoints;\n if (options.lightingProfile !== undefined)\n this.lightingProfile = options.lightingProfile;\n if (options.location !== undefined) this.location = options.location;\n if (options.anchorPoints !== undefined)\n this.anchorPoints = options.anchorPoints;\n if (options.status !== undefined) this.status = options.status;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n\n private async getSceneAssetCollection() {\n const { SceneOwnedAssetCollection } = await import('./scene-assets.js');\n return SceneOwnedAssetCollection.create({ db: this.db });\n }\n\n private getLegacyFieldAssetIds(role?: SceneAssetRole): string[] {\n if (role === 'source') {\n return this.sourceAssetId ? [this.sourceAssetId] : [];\n }\n\n if (role === 'env-map') {\n return this.lightingProfile?.envMapAssetId\n ? [this.lightingProfile.envMapAssetId]\n : [];\n }\n\n if (role === 'viewpoint-extract') {\n return this.viewpoints\n .map((viewpoint) => viewpoint.extractedAssetId || null)\n .filter((assetId): assetId is string => Boolean(assetId));\n }\n\n return [\n this.sourceAssetId,\n this.lightingProfile?.envMapAssetId || null,\n ...this.viewpoints.map((viewpoint) => viewpoint.extractedAssetId || null),\n ].filter((assetId): assetId is string => Boolean(assetId));\n }\n\n private setLegacyFieldAssetId(\n role: SceneAssetRole,\n assetId: string,\n ): boolean {\n if (role === 'source') {\n if (this.sourceAssetId === assetId) {\n return false;\n }\n\n this.sourceAssetId = assetId;\n return true;\n }\n\n if (role === 'env-map') {\n if ((this.lightingProfile?.envMapAssetId || null) === assetId) {\n return false;\n }\n\n this.lightingProfile = {\n ...(this.lightingProfile || {}),\n envMapAssetId: assetId,\n };\n return true;\n }\n\n return false;\n }\n\n private clearLegacyFieldAssetId(\n assetId: string,\n role?: SceneAssetRole,\n ): boolean {\n let changed = false;\n\n if ((!role || role === 'source') && this.sourceAssetId === assetId) {\n this.sourceAssetId = null;\n changed = true;\n }\n\n if (\n (!role || role === 'env-map') &&\n this.lightingProfile?.envMapAssetId === assetId\n ) {\n this.lightingProfile = {\n ...(this.lightingProfile || {}),\n envMapAssetId: undefined,\n };\n changed = true;\n }\n\n if (!role || role === 'viewpoint-extract') {\n let viewpointChanged = false;\n const nextViewpoints = this.viewpoints.map((viewpoint) => {\n if (viewpoint.extractedAssetId !== assetId) {\n return viewpoint;\n }\n\n viewpointChanged = true;\n return {\n ...viewpoint,\n extractedAssetId: undefined,\n };\n });\n\n if (viewpointChanged) {\n this.viewpoints = nextViewpoints;\n changed = true;\n }\n }\n\n return changed;\n }\n\n async getAssets(role?: SceneAssetRole): Promise<Asset[]> {\n const canonicalAssetIds = this.id\n ? await listCanonicalOwnedAssetIds({\n tableName: 'scene_assets',\n loadLinks: async () =>\n (await this.getSceneAssetCollection()).byLeft(\n this.id as string,\n role ? { role } : {},\n ),\n })\n : [];\n const legacyFieldAssetIds = this.getLegacyFieldAssetIds(role);\n const legacyOwnedAssetIds = this.id\n ? await listLegacyOwnedAssetIds({\n db: this.db,\n ownerColumn: 'scene_id',\n ownerId: this.id,\n role,\n metaTypes: legacyVideoAssetMetaTypes('SceneAsset'),\n })\n : [];\n\n return resolveOwnedAssets(\n this.db,\n this.tenantId,\n mergeOwnedAssetIds(\n canonicalAssetIds,\n legacyFieldAssetIds,\n legacyOwnedAssetIds,\n ),\n );\n }\n\n async getAssetByRole(role: SceneAssetRole): Promise<Asset | null> {\n const assets = await this.getAssets(role);\n return assets[0] || null;\n }\n\n async addAsset(\n asset: Asset,\n role: SceneAssetRole = 'source',\n sortOrder = 0,\n ): Promise<void> {\n if (!this.id || !asset.id) {\n throw new Error('Cannot associate unsaved scene or asset');\n }\n\n assertValidVideoAssetRole(role);\n assertValidVideoAssetSortOrder(sortOrder);\n\n const sceneAssets = await this.getSceneAssetCollection();\n await sceneAssets.attach(this.id, asset.id, {\n role,\n sortOrder,\n tenantId: this.tenantId,\n });\n\n if (this.setLegacyFieldAssetId(role, asset.id)) {\n await this.save();\n }\n }\n\n async removeAsset(assetId: string, role?: SceneAssetRole): Promise<void> {\n if (!this.id) {\n return;\n }\n\n const sceneAssets = await this.getSceneAssetCollection();\n await sceneAssets.detach(this.id, assetId, role ? { role } : {});\n\n if (this.clearLegacyFieldAssetId(assetId, role)) {\n await this.save();\n }\n }\n\n /** Check if this is a 360° panorama */\n get isPanorama(): boolean {\n return (\n this.sourceType === 'panorama_360' || this.sourceType === 'panorama_180'\n );\n }\n\n /** Check if the scene has viewpoints extracted */\n get hasViewpoints(): boolean {\n return this.viewpoints.length > 0;\n }\n\n /** Check if the scene is ready for compositing */\n get isReady(): boolean {\n return this.status === 'ready' && this.sourceAssetId !== null;\n }\n}\n"],"mappings":"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"}