director events
This commit is contained in:
15
fgfm.TODO
15
fgfm.TODO
@@ -1,15 +1,10 @@
|
|||||||
TODO:
|
TODO:
|
||||||
☐ Have director emit events for bots to listen to
|
☐ Handle socket disconnect
|
||||||
- show status changing
|
|
||||||
- video starting/ending/skipped
|
|
||||||
☐ Auto-enable vrmode timer when show starts (listen for event)
|
|
||||||
☐ Start/stop stream automation
|
☐ Start/stop stream automation
|
||||||
☐ Support scheduled start/stop
|
☐ Support scheduled start/stop
|
||||||
- instead of countdown for X seconds, use datetime argument
|
- instead of countdown for X seconds, use datetime argument
|
||||||
☐ Start
|
☐ Start
|
||||||
- Countdown for X minutes is triggered and shown
|
- 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
|
☐ Decouple twitch chat from GHOBS
|
||||||
☐ Move anything that calls director.state from app into fgfm lib
|
☐ Move anything that calls director.state from app into fgfm lib
|
||||||
☐ Restrict # of requests a user can have in the queue at once
|
☐ Restrict # of requests a user can have in the queue at once
|
||||||
@@ -46,6 +41,14 @@ StreamWebRemote:
|
|||||||
|
|
||||||
___________________
|
___________________
|
||||||
Archive:
|
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)
|
✔ 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)
|
✔ 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)
|
✔ 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 timersList = require('./conf/timers.json');
|
||||||
|
|
||||||
let activeTimers = [];
|
let activeTimers = [];
|
||||||
let skipVote = {
|
let skipVote = {target: null, count: 0};
|
||||||
target: null,
|
|
||||||
count: 0
|
|
||||||
};
|
|
||||||
// @TODO: Move to config
|
|
||||||
config.skipVoteThreshold = 2;
|
|
||||||
|
|
||||||
// Main screen turn on
|
// Main screen turn on
|
||||||
const obs = new GHOBS(config);
|
const obs = new GHOBS(config);
|
||||||
@@ -83,15 +78,34 @@ const streamInit = (config, twitch) => {
|
|||||||
|
|
||||||
// All your comfy are belong to us
|
// All your comfy are belong to us
|
||||||
const director = new FGFM({config: config, obs: obs});
|
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
|
// Chat commands
|
||||||
const commands = {
|
const commands = {
|
||||||
admin: {
|
admin: {
|
||||||
|
|
||||||
init: (cmd) => {
|
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) => {
|
end: (cmd) => {
|
||||||
let creditsDelay = cmd.args[1] || 0;
|
let creditsDelay = cmd.args[1] || 1;
|
||||||
let endDelay = cmd.args[2] || 60;
|
let endDelay = cmd.args[2] || 60;
|
||||||
director.endTheShow(creditsDelay, endDelay);
|
director.endTheShow(creditsDelay, endDelay);
|
||||||
},
|
},
|
||||||
@@ -147,38 +161,12 @@ const streamInit = (config, twitch) => {
|
|||||||
return;
|
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;
|
let timerStatus = cmd.args[2] || false;
|
||||||
if (!timerStatus || timerStatus !== 'on' || timerStatus !== 'off') {
|
|
||||||
// toggle by default
|
try {
|
||||||
if (currentTimerIndex === -1) {
|
manageTimer(timerName, timerStatus);
|
||||||
timerStatus = 'on';
|
} catch (e) {
|
||||||
} else {
|
twitch.botChat.say(cmd.to, e);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -460,6 +448,11 @@ const streamInit = (config, twitch) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Aliases for chat commands
|
||||||
|
const aliases = {
|
||||||
|
"rooms": "room"
|
||||||
|
};
|
||||||
|
|
||||||
// Listen for the above commands
|
// Listen for the above commands
|
||||||
twitch.botChat.addListener('message', (from, to, message) => {
|
twitch.botChat.addListener('message', (from, to, message) => {
|
||||||
// Ignore everything from blacklisted users
|
// Ignore everything from blacklisted users
|
||||||
@@ -481,6 +474,9 @@ const streamInit = (config, twitch) => {
|
|||||||
// Ignore messages without a command
|
// Ignore messages without a command
|
||||||
if (!key || key.length === 0) return;
|
if (!key || key.length === 0) return;
|
||||||
|
|
||||||
|
// Check for aliased commands
|
||||||
|
if (aliases.hasOwnProperty(key)) key = aliases[key];
|
||||||
|
|
||||||
// Ignore unrecognized commands
|
// Ignore unrecognized commands
|
||||||
if (!commands.admin.hasOwnProperty(key) && !commands.user.hasOwnProperty(key)) return;
|
if (!commands.admin.hasOwnProperty(key) && !commands.user.hasOwnProperty(key)) return;
|
||||||
|
|
||||||
@@ -507,7 +503,41 @@ const streamInit = (config, twitch) => {
|
|||||||
.catch(console.error);
|
.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
|
// @TODO: Modularize timed events
|
||||||
//console.log(`Initializing stream timers...`);
|
//console.log(`Initializing stream timers...`);
|
||||||
@@ -580,11 +610,5 @@ const streamInit = (config, twitch) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const startTimer = (timer) => {
|
|
||||||
setInterval(() => {
|
|
||||||
|
|
||||||
}, timer.interval*1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
// catches Promise errors
|
// catches Promise errors
|
||||||
process.on('unhandledRejection', console.error);
|
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) {
|
function FGFM(config) {
|
||||||
// Set up initial state
|
// Set up initial state
|
||||||
@@ -14,30 +16,36 @@ function FGFM(config) {
|
|||||||
commercialPlaying: false
|
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';
|
this.state.showStatus = 'STARTING_SOON';
|
||||||
|
|
||||||
// Set up the initial queue by randomly choosing the configured amount of vods included in shuffling
|
// 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);
|
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');
|
this.obs.switchToScene('starting-soon');
|
||||||
|
|
||||||
// restore volume
|
// Restore volume
|
||||||
this.obs.setVolume('headphones', 1.0);
|
this.obs.setVolume('headphones', 1.0);
|
||||||
|
|
||||||
// start the stream
|
// Start the stream
|
||||||
this.obs.startStream();
|
console.log(`The stream will start in ${streamStartDelaySeconds} seconds!`);
|
||||||
|
setTimeout(() => {this.obs.startStream().then(() => {this.emit('STREAM_STARTED')})}, streamStartDelaySeconds*1000);
|
||||||
|
|
||||||
if (typeof autostartDelaySeconds === 'undefined') {
|
// @TODO: Actually show the countdown in the scene
|
||||||
autostartDelaySeconds = 300;
|
console.log(`The show will start in ${showStartDelaySeconds} seconds!`);
|
||||||
}
|
setTimeout(this.startTheShow, showStartDelaySeconds*1000);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up initial queue + start playback
|
// Set up initial queue + start playback
|
||||||
@@ -53,6 +61,8 @@ function FGFM(config) {
|
|||||||
this.obs.setVolume('headphones', 1.0);
|
this.obs.setVolume('headphones', 1.0);
|
||||||
|
|
||||||
this.state.showStatus = 'RUNNING';
|
this.state.showStatus = 'RUNNING';
|
||||||
|
|
||||||
|
this.emit('SHOW_STARTED');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.endTheShow = (creditsDelaySeconds, endDelaySeconds) => {
|
this.endTheShow = (creditsDelaySeconds, endDelaySeconds) => {
|
||||||
@@ -65,14 +75,19 @@ function FGFM(config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Credits will be shown in ${creditsDelaySeconds} seconds!`);
|
console.log(`Credits will be shown in ${creditsDelaySeconds} seconds!`);
|
||||||
|
this.emit('SHOW_ENDING', creditsDelaySeconds);
|
||||||
|
|
||||||
let end = () => {
|
let end = () => {
|
||||||
clearTimeout(this.state.videoTimer);
|
|
||||||
|
|
||||||
this.state.showStatus = 'ENDING';
|
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')
|
this.obs.switchToScene('credits')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
this.emit('CREDITS_SHOWN', endDelaySeconds);
|
||||||
|
|
||||||
if (endDelaySeconds < 5) endDelaySeconds = 5;
|
if (endDelaySeconds < 5) endDelaySeconds = 5;
|
||||||
console.log(`Stream will stop in ${endDelaySeconds} seconds`);
|
console.log(`Stream will stop in ${endDelaySeconds} seconds`);
|
||||||
let fadeOutDelay = endDelaySeconds - 5;
|
let fadeOutDelay = endDelaySeconds - 5;
|
||||||
@@ -96,6 +111,7 @@ function FGFM(config) {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.obs.stopStream();
|
this.obs.stopStream();
|
||||||
this.state.showStatus = 'ENDED';
|
this.state.showStatus = 'ENDED';
|
||||||
|
this.emit('SHOW_ENDED');
|
||||||
}, endDelaySeconds*1000);
|
}, endDelaySeconds*1000);
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
@@ -287,12 +303,16 @@ function FGFM(config) {
|
|||||||
|
|
||||||
this.pause = () => {
|
this.pause = () => {
|
||||||
this.state.showStatus = 'PAUSED';
|
this.state.showStatus = 'PAUSED';
|
||||||
|
this.emit('SHOW_PAUSED');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.resume = () => {
|
this.resume = () => {
|
||||||
this.state.showStatus = 'RUNNING';
|
this.state.showStatus = 'RUNNING';
|
||||||
this.nextVideo();
|
this.nextVideo();
|
||||||
|
this.emit('SHOW_RESUMED');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sysutil.inherits(FGFM, emitter);
|
||||||
|
|
||||||
module.exports = FGFM;
|
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