director events
This commit is contained in:
15
fgfm.TODO
15
fgfm.TODO
@@ -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)
|
||||
|
||||
116
fgfm.js
116
fgfm.js
@@ -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);
|
||||
@@ -83,15 +78,34 @@ 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);
|
||||
|
||||
54
lib/fgfm.js
54
lib/fgfm.js
@@ -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);
|
||||
}
|
||||
// @TODO: Actually show the countdown in the scene
|
||||
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
BIN
sfx/wobbuffet.mp3
Executable file
Binary file not shown.
Reference in New Issue
Block a user