diff --git a/extension/music/src/index.ts b/extension/music/src/index.ts index b1b5be2..2e1e290 100644 --- a/extension/music/src/index.ts +++ b/extension/music/src/index.ts @@ -168,9 +168,9 @@ class Music { }); // Listen to OBS transitions to play/pause correctly. - this.obs.conn.on('TransitionBegin', (data) => { - if (data['to-scene']) { - if (data['to-scene'].includes('[M]')) { + this.obs.on('transitionStarted', (current) => { + if (current) { + if (current.includes('[M]')) { this.play(); } else { this.pause(); diff --git a/extension/obs/src/index.ts b/extension/obs/src/index.ts index 859815c..0f4179f 100644 --- a/extension/obs/src/index.ts +++ b/extension/obs/src/index.ts @@ -1,7 +1,8 @@ import type NodeCGTypes from '@nodecg/types'; import clone from 'clone'; import { EventEmitter } from 'events'; -import ObsWebsocketJs from 'obs-websocket-js'; +import OBSWebSocket from 'obs-websocket-js'; +import { OBSResponseTypes } from 'obs-websocket-js/dist/types'; import { findBestMatch } from 'string-similarity'; import { OBS as OBSTypes } from '../../../types'; @@ -9,13 +10,15 @@ interface OBS { on(event: 'streamingStatusChanged', listener: (streaming: boolean, old?: boolean) => void): this; on(event: 'connectionStatusChanged', listener: (connected: boolean) => void): this; on(event: 'currentSceneChanged', listener: (current?: string, last?: string) => void): this; + on(event: 'transitionStarted', listener: (current: string, previous?: string) => void): this; on(event: 'sceneListChanged', listener: (list: string[]) => void): this; + on(event: 'ready', listener: () => void): this; } class OBS extends EventEmitter { private nodecg: NodeCGTypes.ServerAPI; private config: OBSTypes.Config; - conn = new ObsWebsocketJs(); + conn = new OBSWebSocket(); currentScene: string | undefined; sceneList: string [] = []; connected = false; @@ -28,7 +31,6 @@ class OBS extends EventEmitter { if (config.enabled) { nodecg.log.info('[OBS] Setting up connection'); - this.connect(); this.conn.on('ConnectionClosed', () => { this.connected = false; @@ -37,75 +39,91 @@ class OBS extends EventEmitter { setTimeout(() => this.connect(), 5000); }); - this.conn.on('SwitchScenes', (data) => { + this.conn.on('CurrentProgramSceneChanged', (data) => { const lastScene = this.currentScene; - if (lastScene !== data['scene-name']) { - this.currentScene = data['scene-name']; + if (lastScene !== data.sceneName) { + this.currentScene = data.sceneName; this.emit('currentSceneChanged', this.currentScene, lastScene); } }); - this.conn.on('ScenesChanged', async () => { - const scenes = await this.conn.send('GetSceneList'); - this.sceneList = scenes.scenes.map((s) => s.name); + this.conn.on('SceneListChanged', async ({ scenes }) => { + this.sceneList = (scenes as OBSTypes.SceneList) + .sort((s, b) => b.sceneIndex - s.sceneIndex) + .map((s) => s.sceneName); this.emit('sceneListChanged', this.sceneList); }); - this.conn.on('StreamStarted', () => { - this.streaming = true; - this.emit('streamingStatusChanged', this.streaming, !this.streaming); - }); - - this.conn.on('StreamStopped', () => { - this.streaming = false; + this.conn.on('StreamStateChanged', ({ outputActive }) => { + this.streaming = outputActive; this.emit('streamingStatusChanged', this.streaming, !this.streaming); }); - this.conn.on('error', (err) => { + this.conn.on('ConnectionError', (err) => { nodecg.log.warn('[OBS] Connection error'); nodecg.log.debug('[OBS] Connection error:', err); }); + + // @ts-expect-error better types are needed. + this.conn.on('SceneTransitionStarted', (data: OBSTypes.SexyTransitionData) => { + this.emit('transitionStarted', data.toScene, data.fromScene); + }); + + this.conn.on('Identified', () => { + // wait a few seconds to make sure OBS is properly loaded. + // Otherwise, we'll get "OBS is not ready to perform the request" + setTimeout(() => { + this.emit('ready'); + }, 5 * 1000); + }); + + this.connect(); } } async connect(): Promise { try { - await this.conn.connect({ - address: this.config.address, - password: this.config.password, - }); + let addr = this.config.address; + + if (!addr.startsWith('ws://')) { + addr = `ws://${addr}`; + } + + await this.conn.connect(addr, this.config.password); this.connected = true; - const scenes = await this.conn.send('GetSceneList'); + const scenes = await this.conn.call('GetSceneList'); // Get current scene on connection. const lastScene = this.currentScene; - if (lastScene !== scenes['current-scene']) { - this.currentScene = scenes['current-scene']; + if (lastScene !== scenes.currentProgramSceneName) { + this.currentScene = scenes.currentProgramSceneName; } // Get scene list on connection. const oldList = clone(this.sceneList); - const newList = scenes.scenes.map((s) => s.name); + const newList = (scenes.scenes as OBSTypes.SceneList) + .sort((s, b) => b.sceneIndex - s.sceneIndex) + .map((s) => s.sceneName); if (JSON.stringify(newList) !== JSON.stringify(oldList)) { this.sceneList = newList; } // Get streaming status on connection. - const streamingStatus = await this.conn.send('GetStreamingStatus'); + const streamingStatus = await this.conn.call('GetStreamStatus'); const lastStatus = this.streaming; - if (streamingStatus.streaming !== lastStatus) { - this.streaming = streamingStatus.streaming; + if (streamingStatus.outputActive !== lastStatus) { + this.streaming = streamingStatus.outputActive; } // Emit changes after everything start up related has finished. this.emit('connectionStatusChanged', this.connected); - if (lastScene !== scenes['current-scene']) { + if (lastScene !== scenes.currentProgramSceneName) { this.emit('currentSceneChanged', this.currentScene, lastScene); } if (JSON.stringify(newList) !== JSON.stringify(oldList)) { this.emit('sceneListChanged', this.sceneList); } - if (streamingStatus.streaming !== lastStatus) { + if (streamingStatus.outputActive !== lastStatus) { this.emit('streamingStatusChanged', this.streaming, lastStatus); } @@ -153,7 +171,9 @@ class OBS extends EventEmitter { try { const scene = this.findScene(name); if (scene) { - await this.conn.send('SetCurrentScene', { 'scene-name': scene }); + await this.conn.call('SetCurrentProgramScene', { + sceneName: scene, + }); } else { throw new Error('Scene could not be found'); } @@ -169,10 +189,7 @@ class OBS extends EventEmitter { * @param sourceName Name of the source. */ async getSourceSettings(sourceName: string): Promise<{ - messageId: string; - status: 'ok'; - sourceName: string; - sourceType: string; + inputKind: string; sourceSettings: Record; }> { if (!this.config.enabled || !this.connected) { @@ -180,8 +197,13 @@ class OBS extends EventEmitter { throw new Error('No connection available'); } try { - const resp = await this.conn.send('GetSourceSettings', { sourceName }); - return resp; + const resp = await this.conn.call('GetInputSettings', { + inputName: sourceName, + }); + return { + inputKind: resp.inputKind, + sourceSettings: resp.inputSettings, + }; } catch (err) { this.nodecg.log.warn(`[OBS] Cannot get source settings [${sourceName}]`); this.nodecg.log.debug(`[OBS] Cannot get source settings [${sourceName}]: ` @@ -193,20 +215,18 @@ class OBS extends EventEmitter { /** * Modify a sources settings. * @param sourceName Name of the source. - * @param sourceType Source type (has the be the internal name, not the display name). * @param sourceSettings Settings you wish to pass to OBS to change. */ // eslint-disable-next-line max-len - async setSourceSettings(sourceName: string, sourceType: string, sourceSettings: Record): Promise { + async setSourceSettings(sourceName: string, sourceSettings: Record): Promise { if (!this.config.enabled || !this.connected) { // OBS not enabled, don't even try to set. throw new Error('No connection available'); } try { - await this.conn.send('SetSourceSettings', { - sourceName, - sourceType, - sourceSettings, + await this.conn.call('SetInputSettings', { + inputName: sourceName, + inputSettings: sourceSettings as never, }); } catch (err) { this.nodecg.log.warn(`[OBS] Cannot set source settings [${sourceName}]`); @@ -216,6 +236,55 @@ class OBS extends EventEmitter { } } + async getSceneItemSettings( + scene: string, + item: string, + ): Promise<{ sceneItemTransform: OBSTypes.Transform, sceneItemEnabled: boolean }> { + // None of this is properly documented btw. + // I had to search their discord for this information. + const response = await this.conn.callBatch([ + { + requestType: 'GetSceneItemId', + requestData: { + sceneName: scene, + sourceName: item, + }, + // @ts-expect-error This is valid, just undocumented and not typed in obs-ws-js. + outputVariables: { + sceneItemIdVariable: 'sceneItemId', + }, + }, + { + requestType: 'GetSceneItemTransform', + // @ts-expect-error the sceneItemId var is optional cuz of the input vars + requestData: { + sceneName: scene, + }, + inputVariables: { + sceneItemId: 'sceneItemIdVariable', + }, + }, + { + requestType: 'GetSceneItemEnabled', + // @ts-expect-error the sceneItemId var is optional cuz of the input vars + requestData: { + sceneName: scene, + }, + inputVariables: { + sceneItemId: 'sceneItemIdVariable', + }, + }, + ]); + + const transformRes = response[1].responseData as OBSResponseTypes['GetSceneItemTransform']; + const enabledRes = response[2].responseData as OBSResponseTypes['GetSceneItemEnabled']; + + return { + sceneItemTransform: transformRes.sceneItemTransform as unknown as OBSTypes.Transform, + sceneItemEnabled: enabledRes.sceneItemEnabled, + }; + } + /** * Resets the scene item, then sets some properties if possible. * @param scene Name of scene that item is in @@ -246,28 +315,56 @@ class OBS extends EventEmitter { // OBS not enabled, don't even try to set. throw new Error('No connection available'); } - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore: Typings say we need to specify more than we actually do. - await this.conn.send('SetSceneItemProperties', { - 'scene-name': scene, - item: { name: item }, - visible: visible ?? true, - position: { - x: area?.x ?? 0, - y: area?.y ?? 0, + + // None of this is properly documented btw. + // I had to search their discord for this information. + await this.conn.callBatch([ + { + requestType: 'GetSceneItemId', + requestData: { + sceneName: scene, + sourceName: item, + }, + // @ts-expect-error This is valid, just undocumented and not typed in obs-ws-js. + outputVariables: { + sceneItemIdVariable: 'sceneItemId', + }, }, - bounds: { - type: 'OBS_BOUNDS_STRETCH', - x: area?.width ?? 1920, - y: area?.height ?? 1080, + { + requestType: 'SetSceneItemTransform', + // @ts-expect-error the sceneItemId var is optional cuz of the input vars + requestData: { + sceneName: scene, + sceneItemTransform: { + boundsHeight: area?.height ?? 1080, + boundsType: 'OBS_BOUNDS_STRETCH', + boundsWidth: area?.width ?? 1920, + + positionX: area?.x ?? 0, + positionY: area?.y ?? 0, + + cropBottom: crop?.bottom ?? 0, + cropLeft: crop?.left ?? 0, + cropRight: crop?.right ?? 0, + cropTop: crop?.top ?? 0, + }, + }, + inputVariables: { + sceneItemId: 'sceneItemIdVariable', + }, }, - crop: { - top: crop?.top ?? 0, - bottom: crop?.bottom ?? 0, - left: crop?.left ?? 0, - right: crop?.right ?? 0, + { + requestType: 'SetSceneItemEnabled', + // @ts-expect-error the sceneItemId var is optional cuz of the input vars + requestData: { + sceneName: scene, + sceneItemEnabled: visible ?? true, + }, + inputVariables: { + sceneItemId: 'sceneItemIdVariable', + }, }, - }); + ]); } catch (err) { this.nodecg.log.warn(`[OBS] Cannot configure scene item [${scene}: ${item}]`); this.nodecg.log.debug(`[OBS] Cannot configure scene item [${scene}: ${item}]: ` diff --git a/extension/video-player/src/index.ts b/extension/video-player/src/index.ts index 6395cb9..db99814 100644 --- a/extension/video-player/src/index.ts +++ b/extension/video-player/src/index.ts @@ -30,8 +30,8 @@ class VideoPlayer extends TypedEmitter { this.obs = obs; // Listens for when videos finish playing in OBS. - obs.conn.on('MediaEnded', (data) => { - if (data.sourceName === this.obsConfig.names.sources.videoPlayer + obs.conn.on('MediaInputPlaybackEnded', (data) => { + if (data.inputName === this.obsConfig.names.sources.videoPlayer && this.playing && this.index >= 0) { this.emit('videoEnded', this.playlist[this.index]); } @@ -110,9 +110,13 @@ class VideoPlayer extends TypedEmitter { this.playlist.length = 0; this.delayAC?.abort(); try { - await this.obs.conn.send( - 'StopMedia', - { sourceName: this.obsConfig.names.sources.videoPlayer }, + await this.obs.conn.call( + 'TriggerMediaInputAction', + { + inputName: this.obsConfig.names.sources.videoPlayer, + // This action is incorrectly marked as deprecated in the generated docs, but isn't. + mediaAction: 'OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP', + }, ); } catch (err) { /* do nothing */ } this.emit('playlistEnded', true); @@ -127,32 +131,34 @@ class VideoPlayer extends TypedEmitter { if (!this.obs.connected || !this.obsConfig.enabled) { throw new Error('no OBS connection available'); } - const source = await this.obs.conn.send('GetSourceSettings', { - sourceName: this.obsConfig.names.sources.videoPlayer, + const source = await this.obs.conn.call('GetInputSettings', { + inputName: this.obsConfig.names.sources.videoPlayer, }); const location = join(cwd(), `assets/${video.namespace}/${video.category}/${video.base}`); // File is the same as before, just restart it. // ONLY WORKS FOR ffmpeg_source, but might not even be needed for VLC source! // eslint-disable-next-line @typescript-eslint/no-explicit-any - if ((source.sourceSettings as any).local_file === location) { - await this.obs.conn.send('RestartMedia', { - sourceName: this.obsConfig.names.sources.videoPlayer, + if ((source.inputSettings as any).local_file === location) { + await this.obs.conn.call('TriggerMediaInputAction', { + inputName: this.obsConfig.names.sources.videoPlayer, + // This action is incorrectly marked as deprecated in the generated docs, but isn't. + mediaAction: 'OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART', }); // If different, explicitily set it. This also starts the playback. - } else if (source.sourceType === 'ffmpeg_source') { - await this.obs.conn.send('SetSourceSettings', { - sourceName: this.obsConfig.names.sources.videoPlayer, - sourceSettings: { + } else if (source.inputKind === 'ffmpeg_source') { + await this.obs.setSourceSettings( + this.obsConfig.names.sources.videoPlayer, + { is_local_file: true, local_file: location, looping: false, restart_on_activate: false, }, - }); - } else if (source.sourceType === 'vlc_source') { - await this.obs.conn.send('SetSourceSettings', { - sourceName: this.obsConfig.names.sources.videoPlayer, - sourceSettings: { + ); + } else if (source.inputKind === 'vlc_source') { + await this.obs.setSourceSettings( + this.obsConfig.names.sources.videoPlayer, + { loop: false, shuffle: false, playback_behavior: 'always_play', @@ -164,7 +170,7 @@ class VideoPlayer extends TypedEmitter { }, ], }, - }); + ); } else { throw new Error('No video player source found in OBS to trigger'); } diff --git a/package.json b/package.json index 9ddf098..e3ea096 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "get-video-duration": "3.0.2", "lodash": "^4.17.21", "node-fetch": "^2.7.0", - "obs-websocket-js": "^4.0.3", + "obs-websocket-js": "^5.0.4", "osc": "^2.4.4", "string-similarity": "^4.0.4", "tiny-typed-emitter": "^2.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 480ee2f..08701a9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,8 +30,8 @@ importers: specifier: ^2.7.0 version: 2.7.0 obs-websocket-js: - specifier: ^4.0.3 - version: 4.0.3 + specifier: ^5.0.4 + version: 5.0.5 osc: specifier: ^2.4.4 version: 2.4.4 @@ -149,6 +149,10 @@ packages: '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@msgpack/msgpack@2.8.0': + resolution: {integrity: sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==} + engines: {node: '>= 10'} + '@serialport/binding-mock@10.2.2': resolution: {integrity: sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==} engines: {node: '>=12.0.0'} @@ -383,6 +387,9 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + d@1.0.2: resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} engines: {node: '>=0.12'} @@ -446,6 +453,9 @@ packages: event-emitter@0.3.5: resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + execa@4.1.0: resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} engines: {node: '>=10'} @@ -572,8 +582,8 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isomorphic-ws@4.0.1: - resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} + isomorphic-ws@5.0.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} peerDependencies: ws: '*' @@ -721,8 +731,9 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - obs-websocket-js@4.0.3: - resolution: {integrity: sha512-28L5VHlrn9gT9uMeasR5VJkVTc+Xj6eWqZxSQWVsnzznRNJWrHJK5ldK6DMtnWOMTZarPznq8dp0ko4R+svqdg==} + obs-websocket-js@5.0.5: + resolution: {integrity: sha512-mSMqLXJ4z28jgwy7Ecv8CtpYh/xdbcn524kq0n6wT3kN6xkgWU/Zc6OtiVZo+gyyylC0anjehMLEVF+CDSwccw==} + engines: {node: '>12.0'} once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -825,10 +836,6 @@ packages: resolution: {integrity: sha512-7OYLDsu5i6bbv3lU81pGy076xe0JwpK6b49G6RjNvGibstUqQkI+I3/X491yBGtf4gaqUdOgoU1/5KZ/XxL4dw==} engines: {node: '>=12.0.0'} - sha.js@2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -932,6 +939,10 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} + type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + type@2.7.2: resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} @@ -986,18 +997,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@7.5.9: - resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.13.0: resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} engines: {node: '>=10.0.0'} @@ -1073,6 +1072,8 @@ snapshots: '@jsdevtools/ono@7.1.3': {} + '@msgpack/msgpack@2.8.0': {} + '@serialport/binding-mock@10.2.2': dependencies: '@serialport/bindings-interface': 1.2.2 @@ -1315,6 +1316,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypto-js@4.2.0: {} + d@1.0.2: dependencies: es5-ext: 0.10.64 @@ -1381,6 +1384,8 @@ snapshots: d: 1.0.2 es5-ext: 0.10.64 + eventemitter3@5.0.1: {} + execa@4.1.0: dependencies: cross-spawn: 7.0.3 @@ -1520,9 +1525,9 @@ snapshots: isexe@2.0.0: {} - isomorphic-ws@4.0.1(ws@7.5.9): + isomorphic-ws@5.0.0(ws@8.13.0): dependencies: - ws: 7.5.9 + ws: 8.13.0 js-yaml@4.1.0: dependencies: @@ -1682,12 +1687,15 @@ snapshots: object-assign@4.1.1: {} - obs-websocket-js@4.0.3: + obs-websocket-js@5.0.5: dependencies: + '@msgpack/msgpack': 2.8.0 + crypto-js: 4.2.0 debug: 4.3.4 - isomorphic-ws: 4.0.1(ws@7.5.9) - sha.js: 2.4.11 - ws: 7.5.9 + eventemitter3: 5.0.1 + isomorphic-ws: 5.0.0(ws@8.13.0) + type-fest: 3.13.1 + ws: 8.13.0 transitivePeerDependencies: - bufferutil - supports-color @@ -1820,11 +1828,6 @@ snapshots: - supports-color optional: true - sha.js@2.4.11: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -1930,6 +1933,8 @@ snapshots: type-fest@0.21.3: {} + type-fest@3.13.1: {} + type@2.7.2: {} undici-types@5.26.5: {} @@ -1979,8 +1984,6 @@ snapshots: wrappy@1.0.2: {} - ws@7.5.9: {} - ws@8.13.0: {} xkeys@2.4.0: diff --git a/types/OBS.d.ts b/types/OBS.d.ts index c2cf77d..a4a0de1 100644 --- a/types/OBS.d.ts +++ b/types/OBS.d.ts @@ -13,4 +13,36 @@ export namespace OBS { }; }; } + + interface SexyTransitionData { + fromScene: string; + toScene: string; + transitionName: string; + transitionUuid: string; + } + + interface Transform { + alignment: number; + boundsAlignment: number; + boundsHeight: number; + boundsType: string; + boundsWidth: number; + cropBottom: number; + cropLeft: number; + cropRight: number; + cropTop: number; + positionX: number; + positionY: number; + rotation: number; + scaleX: number; + scaleY: number; + sourceHeight: number; + sourceWidth: number; + width: number; + } + + type SceneList = [{ + sceneIndex: number; + sceneName: string; + }]; }