director events

This commit is contained in:
Chris Ham
2018-11-07 00:41:28 -08:00
parent d01439cf15
commit fd75d11eec
4 changed files with 116 additions and 69 deletions

View File

@@ -1,15 +1,10 @@
TODO:
☐ Have director emit events for bots to listen to
- show status changing
- video starting/ending/skipped
☐ Auto-enable vrmode timer when show starts (listen for event)
☐ Handle socket disconnect
☐ Start/stop stream automation
☐ Support scheduled start/stop
- instead of countdown for X seconds, use datetime argument
☐ Start
- Countdown for X minutes is triggered and shown
- Once countdown finishes, switch to intro scene and play
- Switch to fgfm once intro finishes
☐ 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
@@ -46,6 +41,14 @@ StreamWebRemote:
___________________
Archive:
✔ Stop @done (18-11-06 21:54) @project(TODO)
✔ video starting/ending/skipped @done (18-11-06 21:53) @project(TODO)
✔ show status changing @done (18-11-06 21:53) @project(TODO)
✔ Have director emit events for bots to listen to @done (18-11-06 21:53) @project(TODO)
✔ Auto-enable vrmode timer when show starts (listen for event) @done (18-11-06 21:53) @project(TODO)
✔ hide sources before switching to credits @done (18-11-06 21:41) @project(TODO)
✔ Support a delay for Start Streaming @done (18-11-06 21:41) @project(TODO)
✔ Add a $rooms alias for $room @done (18-11-06 15:05) @project(TODO)
✔ Remove currently playing video from vote choices @done (18-10-30 11:51) @project(TODO)
✔ track votes for the current playing video @done (18-10-30 11:48) @project(TODO)
✔ if threshold is met, skip @done (18-10-30 11:48) @project(TODO)

114
fgfm.js
View File

@@ -22,12 +22,7 @@ let snesGames = require('./conf/snesgames.json');
let timersList = require('./conf/timers.json');
let activeTimers = [];
let skipVote = {
target: null,
count: 0
};
// @TODO: Move to config
config.skipVoteThreshold = 2;
let skipVote = {target: null, count: 0};
// Main screen turn on
const obs = new GHOBS(config);
@@ -84,14 +79,33 @@ const streamInit = (config, twitch) => {
// All your comfy are belong to us
const director = new FGFM({config: config, obs: obs});
// Handle show events from the director
director.on('SHOW_STARTED', () => {
// Enable vrmode timer
manageTimer('vr', 'on');
});
director.on('SHOW_ENDING', (secondsUntilCredits) => {
// Disable vrmode timer
manageTimer('vr', 'off');
// Let the chat know the stream is ending soon
twitch.botChat.say(config.twitch.channel, `The stream will be ending in ${parseFloat(secondsUntilCredits/60).toFixed(0)} minutes!`);
});
director.on('CREDITS_SHOWN', (secondsUntilEnd) => {
twitch.editorChat.say(config.twitch.channel, `Thanks to everyone for watching and lurking! Have a wonderful night and stay comfy. greenhComfy`);
});
// Chat commands
const commands = {
admin: {
init: (cmd) => {
let delaySeconds = cmd.args[1] || 300;
let streamStartDelaySeconds = cmd.args[1] || 1;
let showStartDelaySeconds = cmd.args[2] || 300;
director.startingSoon(delaySeconds);
director.startingSoon(streamStartDelaySeconds, showStartDelaySeconds);
},
@@ -101,7 +115,7 @@ const streamInit = (config, twitch) => {
end: (cmd) => {
let creditsDelay = cmd.args[1] || 0;
let creditsDelay = cmd.args[1] || 1;
let endDelay = cmd.args[2] || 60;
director.endTheShow(creditsDelay, endDelay);
},
@@ -147,38 +161,12 @@ const streamInit = (config, twitch) => {
return;
}
// search timers for matching name
let theTimerIndex = timersList.findIndex(e => e.name === timerName);
if (theTimerIndex === -1) {
twitch.botChat.say(cmd.to, `Invalid timer name!`);
return;
}
let theTimer = timersList[theTimerIndex];
// look in activeTimers for current status
let currentTimerIndex = activeTimers.findIndex(e => e.name === timerName);
let timerStatus = cmd.args[2] || false;
if (!timerStatus || timerStatus !== 'on' || timerStatus !== 'off') {
// toggle by default
if (currentTimerIndex === -1) {
timerStatus = 'on';
} else {
timerStatus = 'off';
}
}
if (currentTimerIndex === -1 && timerStatus === 'on') {
let timerFunc = () => {
twitch.botChat.say(config.twitch.channel, theTimer.value);
};
let timerInterval = setInterval(timerFunc, theTimer.interval*1000);
activeTimers.push({name: theTimer.name, timer: timerInterval});
timerFunc();
} else if (timerStatus === 'off') {
clearInterval(activeTimers[currentTimerIndex].timer);
activeTimers.splice(currentTimerIndex, 1);
try {
manageTimer(timerName, timerStatus);
} catch (e) {
twitch.botChat.say(cmd.to, e);
}
},
@@ -460,6 +448,11 @@ const streamInit = (config, twitch) => {
}
};
// Aliases for chat commands
const aliases = {
"rooms": "room"
};
// Listen for the above commands
twitch.botChat.addListener('message', (from, to, message) => {
// Ignore everything from blacklisted users
@@ -481,6 +474,9 @@ const streamInit = (config, twitch) => {
// Ignore messages without a command
if (!key || key.length === 0) return;
// Check for aliased commands
if (aliases.hasOwnProperty(key)) key = aliases[key];
// Ignore unrecognized commands
if (!commands.admin.hasOwnProperty(key) && !commands.user.hasOwnProperty(key)) return;
@@ -507,7 +503,41 @@ const streamInit = (config, twitch) => {
.catch(console.error);
});
const manageTimer = (timerName, timerStatus) => {
// search timers for matching name
let theTimerIndex = timersList.findIndex(e => e.name === timerName);
if (theTimerIndex === -1) {
throw("Invalid timer name!");
}
let theTimer = timersList[theTimerIndex];
// look in activeTimers for current status
let currentTimerIndex = activeTimers.findIndex(e => e.name === timerName);
if (!timerStatus || timerStatus !== 'on' || timerStatus !== 'off') {
// toggle by default
if (currentTimerIndex === -1) {
timerStatus = 'on';
} else {
timerStatus = 'off';
}
}
if (currentTimerIndex === -1 && timerStatus === 'on') {
let timerFunc = () => {
twitch.botChat.say(config.twitch.channel, theTimer.value);
};
let timerInterval = setInterval(timerFunc, theTimer.interval*1000);
activeTimers.push({name: theTimer.name, timer: timerInterval});
timerFunc();
} else if (timerStatus === 'off') {
clearInterval(activeTimers[currentTimerIndex].timer);
activeTimers.splice(currentTimerIndex, 1);
}
return;
}
// @TODO: Modularize timed events
//console.log(`Initializing stream timers...`);
@@ -580,11 +610,5 @@ const streamInit = (config, twitch) => {
});
};
const startTimer = (timer) => {
setInterval(() => {
}, timer.interval*1000);
};
// catches Promise errors
process.on('unhandledRejection', console.error);

View File

@@ -1,4 +1,6 @@
const util = require('./util');
const util = require('./util'),
emitter = require('events').EventEmitter,
sysutil = require('util');
function FGFM(config) {
// Set up initial state
@@ -14,30 +16,36 @@ function FGFM(config) {
commercialPlaying: false
};
this.startingSoon = (autostartDelaySeconds) => {
emitter.call(this);
this.startingSoon = (streamStartDelaySeconds, showStartDelaySeconds) => {
// @TODO: Move these defaults to config
if (typeof streamStartDelaySeconds === 'undefined') {
streamStartDelaySeconds = 1;
}
if (typeof showStartDelaySeconds === 'undefined') {
showStartDelaySeconds = 300;
}
this.state.showStatus = 'STARTING_SOON';
// Set up the initial queue by randomly choosing the configured amount of vods included in shuffling
this.state.videoQueue = this.config.vods.alttp.filter(e => e.includeInShuffle === true).sort(util.randSort).slice(0, this.config.initialQueueSize);
// show the starting-soon scene
// Show the starting-soon scene
this.obs.switchToScene('starting-soon');
// restore volume
// Restore volume
this.obs.setVolume('headphones', 1.0);
// start the stream
this.obs.startStream();
// Start the stream
console.log(`The stream will start in ${streamStartDelaySeconds} seconds!`);
setTimeout(() => {this.obs.startStream().then(() => {this.emit('STREAM_STARTED')})}, streamStartDelaySeconds*1000);
if (typeof autostartDelaySeconds === 'undefined') {
autostartDelaySeconds = 300;
}
if (autostartDelaySeconds !== false) {
// @TODO: Actually show the countdown in the scene
console.log(`The show will start in ${autostartDelaySeconds} seconds!`);
setTimeout(this.startTheShow, autostartDelaySeconds*1000);
}
console.log(`The show will start in ${showStartDelaySeconds} seconds!`);
setTimeout(this.startTheShow, showStartDelaySeconds*1000);
};
// Set up initial queue + start playback
@@ -53,6 +61,8 @@ function FGFM(config) {
this.obs.setVolume('headphones', 1.0);
this.state.showStatus = 'RUNNING';
this.emit('SHOW_STARTED');
};
this.endTheShow = (creditsDelaySeconds, endDelaySeconds) => {
@@ -65,14 +75,19 @@ function FGFM(config) {
}
console.log(`Credits will be shown in ${creditsDelaySeconds} seconds!`);
this.emit('SHOW_ENDING', creditsDelaySeconds);
let end = () => {
clearTimeout(this.state.videoTimer);
this.state.showStatus = 'ENDING';
// Hide current video, don't play next video
this.obs.hide(this.state.currentVideo.sceneItem, this.config.defaultSceneName)
clearTimeout(this.state.videoTimer);
this.obs.switchToScene('credits')
.then(() => {
this.emit('CREDITS_SHOWN', endDelaySeconds);
if (endDelaySeconds < 5) endDelaySeconds = 5;
console.log(`Stream will stop in ${endDelaySeconds} seconds`);
let fadeOutDelay = endDelaySeconds - 5;
@@ -96,6 +111,7 @@ function FGFM(config) {
setTimeout(() => {
this.obs.stopStream();
this.state.showStatus = 'ENDED';
this.emit('SHOW_ENDED');
}, endDelaySeconds*1000);
})
.catch(console.error);
@@ -287,12 +303,16 @@ function FGFM(config) {
this.pause = () => {
this.state.showStatus = 'PAUSED';
this.emit('SHOW_PAUSED');
};
this.resume = () => {
this.state.showStatus = 'RUNNING';
this.nextVideo();
this.emit('SHOW_RESUMED');
};
}
sysutil.inherits(FGFM, emitter);
module.exports = FGFM;

BIN
sfx/wobbuffet.mp3 Executable file

Binary file not shown.