diff --git a/fgfm.TODO b/fgfm.TODO index d226d15..156acca 100755 --- a/fgfm.TODO +++ b/fgfm.TODO @@ -1,6 +1,14 @@ +- Ability to control stream from web or twitch + - The OBS "director" and its state need to be accessible from both + - How to accomplish this? Websocket? + TODO: ✔ Add cooldowns @done (18-10-02 10:16) - ☐ Move anything that calls director.state from app into fgfm lib + ☐ Fix commercial playing issue (switches back to scene early) + ☐ Change $auw and $meme to queue up the videos just like the others (don't switch scenes) + ☐ Don't auto-init GHOBS or FGFM, make them on-demand + ☐ Decouple twitch chat from GHOBS + ☐ Move anything that calls director.state from app into fgfm lib ☐ Restrict # of requests a user can have in the queue at once ☐ Move vrmode timer to this bot, delete from SLCB ☐ Support a CLI flag to delay showing queue until command is issued @@ -9,7 +17,7 @@ TODO: ☐ Web interface? Twitch extension? ☐ Improvements ☐ When playing a room back, loop it at slower speeds for a few iterations - ☐ Support viewer skip voting + ☐ Support viewer $skip voting ☐ Remove currently playing video from vote choices ☐ Command to add sets of videos to the queue at once (like the entire ttas or all gold segments) ☐ Command to stop video rotation / timers (shutdown) diff --git a/lib/ghobs.js b/lib/ghobs.js index 6e5a4e2..e5696d6 100755 --- a/lib/ghobs.js +++ b/lib/ghobs.js @@ -10,76 +10,76 @@ function GHOBS(config) { this.websocket.connect({ address: this.config.obs.websocket.address, password: this.config.obs.websocket.password }) .then(() => { console.log(`Success! We're connected to OBS!`); - this.websocket.getCurrentScene().then(res => this.currentScene = res.name); - this.websocket.onSwitchScenes(data => { - //console.log(`New Active Scene: ${data.sceneName}`); - this.currentScene = data.sceneName; - }); + this.websocket.getCurrentScene().then(currentScene => this.currentScene = currentScene.name); + this.websocket.onSwitchScenes(newScene => this.currentScene = newScene.sceneName); resolve(); }) .catch(reject); // Listen for errors from OBS + // @TODO: Handle socket disconnect gracefully + /** { status: 'error', + description: 'There is no Socket connection available.', + code: 'NOT_CONNECTED', + error: 'There is no Socket connection available.' }*/ this.websocket.on('error', err => { console.error(`OBS websocket error: ${JSON.stringify(err)}`); }); }); }; - // @TODO: pass any unrecognized commands to the websocket + // Plays a video in the current scene and hides when finished + this.playVideo = (video, callback) => { + return new Promise((resolve, reject) => { + // @TODO Validation of video + + // set the file path on the source + let sourceSettings = { + local_file: video.filePath, + looping: (typeof video.loops !== 'undefined' && video.loops > 1) + }; + sourceSettings.loop = sourceSettings.looping; + + this.websocket.setSourceSettings({"sourceName": video.sceneItem, "sourceSettings": sourceSettings}) + // show the video scene item + .then(() => this.websocket.setSceneItemProperties({"item": video.sceneItem, "visible": true})) + // when the video is over, hide it and trigger the user callback, but resolve promise immediately with the timer + .then(() => { + // if this video is being looped, adjust timeout length to allow the requested number of loops to complete + if (sourceSettings.loop === true) { + video.length *= video.loops; + } + + // resolve Promise with a timer of when the video will finish playback + // trigger user callback when the video finishes + resolve(setTimeout(() => { + this.websocket.setSceneItemProperties({"item": video.sceneItem, "visible": false}); + if (typeof callback !== 'undefined') { + callback(); + } + }, parseInt(video.length*1000))) + }) + .catch(reject); + }); + } // Shows a video in the given scene/item and then hides it and switches back to the original scene when finished this.playVideoInScene = (video, scene, callback) => { return new Promise((resolve, reject) => { + video.scene = scene; let originalScene = this.currentScene || false; - //console.log(`Changing scene from ${originalScene} to ${scene}`); + let handleVideoEnd = () => { + if (originalScene !== false) { + this.websocket.setCurrentScene({"scene-name": originalScene}); + } + if (typeof callback !== 'undefined') { + callback(); + } + }; + this.websocket.setCurrentScene({"scene-name": scene}) - .then(res => { - // set the file path on the source - //console.log(`Setting file path to: ${video.filePath}`); - let sourceSettings = { - "local_file": video.filePath, - "looping": (typeof video.loops !== 'undefined' && video.loops > 1) - }; - sourceSettings.loop = sourceSettings.looping; - - // @TODO loop room vids at a slower speed for a few iterations - // @TODO support any sourceSetting? - // - /*{ close_when_inactive: true, - local_file: 'Y:\\media\\videos\\ALttP\\my-vids\\room-vids\\11-mire\\38-wizzpot-rta-hook-610.mp4', - loop: true, - looping: false, - restart_on_activate: false, - speed_percent: 100 }*/ - - //this.websocket.getSourceSettings({"sourceName": video.sceneItem}).then(console.log); - - this.websocket.setSourceSettings({"sourceName": video.sceneItem, "sourceSettings": sourceSettings}) - // show the video scene item - .then(() => this.websocket.setSceneItemProperties({"item": video.sceneItem, "scene-name": scene, "visible": true})) - // when the video is over, hide it and trigger the user callback, but resolve promise immediately with the timer - .then(data => { - // adjust timeout length to allow the requested number of loops to complete - if (sourceSettings.loop === true) { - video.length *= video.loops; - console.log(`Video is set to loop, adjusted length to ${video.length}`); - } - - resolve(setTimeout(() => { - //console.log(`Hiding ${video.sceneItem}`); - this.websocket.setSceneItemProperties({"item": video.sceneItem, "scene-name": scene, "visible": false}); - if (originalScene) { - //console.log(`Switching scene back to ${originalScene}`); - this.websocket.setCurrentScene({"scene-name": originalScene}); - } - if (typeof callback !== 'undefined') { - //console.log('Triggering user callback'); - callback(data); - } - }, parseInt(video.length*1000))) - }); - }) + .then(() => this.playVideo(video, handleVideoEnd)) + .then(timer => resolve) .catch(reject); }); };