import { SourceItemType } from "./types" ; import { OBS } from "./OBS" ; import { SceneItem , SceneItemTransform } from "./SceneItem" ; import { DeepPartial } from "./types" ; import { wait } from "./utils" ; import { SourceFilters , Source } from "./Source" ; /**
 * Describes how a scene item should be created, including its base source and transform
 */ export type SceneItemSchema < T extends Source = Source > = { source : T ; } & DeepPartial < SceneItemTransform > ; /**
 * Describes a scene's map of scene items with  SceneItemSchema export type SceneItemSchemas < Items extends Record < string , Source > > = { [ K in keyof Items ] : SceneItemSchema < Items [ K ] > ; } ; interface LinkOptions { setProperties : boolean ; setSourceSettings : boolean ; } export interface SceneArgs < Items extends Record < string , Source > , Filters extends SourceFilters > { name : string ; items : SceneItemSchemas < Items > ; filters ? : Filters ; } export class Scene < Items extends Record < string , Source > = { } , Filters extends SourceFilters = { } > extends Source < Filters > { items : SceneItem [ ] = [ ] ; private itemsSchema : SceneItemSchemas < Items > ; /**
     * MAIN METHODS
     *
     * Methods that provide functionality specific to sceneify.
     */ /**  */ constructor ( args ) { super ( { ... args , kind : "scene" } ) ; this . itemsSchema = args . items ; } /**
     * Creates a scene in OBS and populates it with items as defined by the scene's items schema.
     */ async create ( obs ) { if ( this . exists ) return this ; await this . initialize ( obs ) ; if ( ! this . exists ) { await obs . call ( "CreateScene" , { sceneName : this . name , } ) ; await this . pushRefs ( ) ; } await wait ( 50 ) ; this . _exists = true ; obs . scenes . set ( this . name , this ) ; for ( const ref in this . itemsSchema ) { await this . createItem ( ref , this . itemsSchema [ ref ] ) ; } await this . setPrivateSettings ( { SIMPLE_OBS_LINKED : false , } ) ; return this ; } /**
     * Links to an existing scene in OBS, verifying that all sources as defined by the scene's items schema exist.
     * Will mark itself as existing if a matching scene is found, but will still throw if the items schema is not matched.
     */ async link ( obs , options ? ) { this . obs = obs ; if ( this . initalized ) throw new Error ( `Failed to link scene ${ this . name }: Scene is already initialized` ) ; const { sceneItems } = await obs . call ( "GetSceneItemList" , { sceneName : this . name , } ) . catch ( ( ) => { throw new Error ( `Failed to link scene ${ this . name }: Scene does not exist` ) ; } ) ; this . _exists = true ; let multipleItemSources = [ ] , noItemSources = [ ] ; for ( let ref in this . itemsSchema ) { let itemSchema = this . itemsSchema [ ref ] ; const sourceItems = sceneItems . filter ( ( i ) => i . sourceName === itemSchema . source . name ) ; if ( sourceItems . length === 0 ) multipleItemSources . push ( itemSchema . source ) ; else if ( sourceItems . length > 1 ) noItemSources . push ( itemSchema . source ) ; else continue ; } if ( multipleItemSources . length !== 0 || noItemSources . length !== 0 ) throw new Error ( `Failed to link scene ${ this . name }:${ multipleItemSources . length !== 0 ? ` Scene contians multiple items of source${ multipleItemSources . length > 1 ? "s" : "" } ${ multipleItemSources . map ( ( s ) => `'${ s . name }'` ) . join ( ", " ) }.` : `` }${ noItemSources . length !== 0 ? ` Scene contians no items of source${ noItemSources . length > 1 ? "s" : "" } ${ noItemSources . map ( ( s ) => `'${ s . name }'` ) . join ( ", " ) }.` : `` }` ) ; await Promise . all ( Object . entries ( this . itemsSchema ) . map ( async ( [ ref , { source , ... transform } ] ) => { const schemaItem = sceneItems . find ( ( i ) => i . sourceName === source . name ) ! ; const item = source . linkItem ( this , schemaItem . sceneItemId , ref ) ; this . items . push ( item ) ; await item . fetchProperties ( ) ; let optionRequests = [ ] ; if ( options ?. setProperties ) optionRequests . push ( item . setTransform ( transform ) ) ; return Promise . all ( optionRequests ) ; } ) ) ; await this . setPrivateSettings ( { SIMPLE_OBS_LINKED : true , } as any ) ; } async createItem < T extends Source > ( ref , itemSchema ) { const { source , ... transform } = itemSchema ; await source . initialize ( this . obs ) ; const item = await source . createSceneItem ( ref , this ) ; if ( Object . keys ( transform ) . length !== 0 ) await item . setTransform ( transform as SceneItemTransform ) ; await item . fetchProperties ( ) ; this . items . push ( item ) ; return item ; } item < R extends keyof Items > ( ref ) ; item ( ref ) ; /**
     * Gets a scene item from the scene by its ref.
     */ item ( ref ) { return this . items . find ( ( i ) => i . ref === ref ) ; } protected async createFirstSceneItem ( scene ) { await this . create ( scene . obs ) ; const { sceneItemId } = await this . obs . call ( "CreateSceneItem" , { sceneName : scene . name , sourceName : this . name , } ) ; this . obs . scenes . set ( this . name , this ) ; return sceneItemId ; } protected async fetchExists ( ) { try { await this . obs . call ( "GetSourcePrivateSettings" , { sourceName : this . name , } ) ; } catch { return false ; } const input = await this . obs . call ( "GetInputSettings" , { inputName : this . name , } ) . then ( ( input ) => input ) . catch ( ( ) => undefined ) ; if ( input ) throw new Error ( `Failed to initiailze scene ${ this . name }: Input of kind ${ input . inputKind } with this name already exists.` ) ; return true ; } param preview async makeCurrentScene ( preview ? ) { await this . obs . call ( preview ? "SetCurrentPreviewScene" : "SetCurrentProgramScene" , { sceneName : this . name } ) ; } }