big refactor
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
TODO:
|
||||
☐ Add cooldowns
|
||||
☐ Move vrmode timer to this bot, delete from SLCB
|
||||
☐ Support a CLI flag to delay showing queue until command is issued
|
||||
☐ Room vid requests / import
|
||||
@@ -18,7 +19,6 @@ TODO:
|
||||
- Once countdown finishes, switch to intro scene and play
|
||||
- Switch to fgfm once intro finishes
|
||||
☐ Support for $pause -- not sure how to pull this off
|
||||
☐ Add cooldowns
|
||||
☐ Tool to output list of video ID's / descriptions
|
||||
☐ Stream alerts for chat
|
||||
☐ Rotating background images (leftside)
|
||||
|
||||
905
fgfm.js
905
fgfm.js
@@ -2,11 +2,16 @@
|
||||
* FG.fm Automation
|
||||
*/
|
||||
|
||||
// Import modules
|
||||
// Import 3rd party packages
|
||||
const irc = require('irc');
|
||||
const schedule = require('node-schedule');
|
||||
const util = require('./lib/util');
|
||||
const md5 = require('md5');
|
||||
|
||||
// Import local packages
|
||||
const GHOBS = require('./lib/ghobs');
|
||||
const FGFM = require('./lib/fgfm');
|
||||
const cooldowns = require('./lib/cooldowns');
|
||||
const util = require('./lib/util');
|
||||
|
||||
// Read internal configuration
|
||||
let config = require('./config.json');
|
||||
@@ -14,22 +19,12 @@ config.vods = require(config.vodConfigFile);
|
||||
config.rooms = require(config.roomConfigFile);
|
||||
let snesGames = require('./conf/snesgames.json');
|
||||
|
||||
// Set up initial state
|
||||
let state = {
|
||||
"videoQueue": [],
|
||||
"recentlyPlayed": [],
|
||||
"currentVideo": null,
|
||||
"videoTimer": null,
|
||||
"lastCommercialShownAt": Date.now(),
|
||||
"commercialPlaying": false
|
||||
};
|
||||
|
||||
// Main screen turn on
|
||||
const obs = new GHOBS(config);
|
||||
obs.init()
|
||||
.then(() => {return twitchInit(config.twitch)})
|
||||
.then(twitch => {return streamInit(config, twitch)})
|
||||
.catch(console.error);
|
||||
.then(() => twitchInit(config.twitch))
|
||||
.then(twitch => streamInit(config, twitch))
|
||||
.catch(console.error);
|
||||
|
||||
// Connect to twitch, set up basic event listeners
|
||||
const twitchInit = (config) => {
|
||||
@@ -75,504 +70,398 @@ const twitchInit = (config) => {
|
||||
|
||||
// Initialize Stream automation
|
||||
const streamInit = (config, twitch) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Set up initial queue + start playback
|
||||
const init = () => {
|
||||
// Set up the initial queue by randomly choosing the configured amount of vods included in shuffling
|
||||
state.videoQueue = config.vods.alttp.filter(e => e.includeInShuffle === true).sort(util.randSort).slice(0, config.initialQueueSize);
|
||||
console.log(`Initial video queue: ${state.videoQueue.map((c, i) => `[${i+1}] ${c.chatName}`).join(' | ')}`);
|
||||
|
||||
// Start queue playback
|
||||
state.currentVideo = state.videoQueue.shift();
|
||||
showVideo(state.currentVideo);
|
||||
// All your comfy are belong to us
|
||||
const director = new FGFM({config: config, obs: obs});
|
||||
director.init();
|
||||
|
||||
// Chat commands
|
||||
const commands = {
|
||||
admin: {
|
||||
|
||||
changevis: (cmd, newVisibility) => {
|
||||
let sceneItem = command.args[1] || false;
|
||||
if (!sceneItem) {
|
||||
twitch.botChat.say(cmd.to, `A scene item name is required!`);
|
||||
return;
|
||||
}
|
||||
|
||||
let sceneOrGroup = command.args[2] || obs.currentScene;
|
||||
obs.setVisible(sceneItem, sceneOrGroup, newVisibility).catch(console.error);
|
||||
},
|
||||
|
||||
|
||||
show: (cmd) => {
|
||||
commands.admin.changevis(cmd, true);
|
||||
},
|
||||
|
||||
|
||||
hide: (cmd) => {
|
||||
commands.admin.changevis(cmd, false);
|
||||
},
|
||||
|
||||
|
||||
t: (cmd) => {
|
||||
let sceneItem = cmd.args[1] || false;
|
||||
if (!sceneItem) {
|
||||
twitch.botChat.say(cmd.to, `A scene item name is required!`);
|
||||
return;
|
||||
}
|
||||
|
||||
obs.toggleVisible(sceneItem).catch(console.error);
|
||||
},
|
||||
|
||||
|
||||
auw: (cmd) => {
|
||||
director.showMeme('auw');
|
||||
},
|
||||
|
||||
|
||||
meme: (cmd) => {
|
||||
let memeId = cmd.args[1] || false;
|
||||
if (memeId) {
|
||||
console.log(`${memeId} meme requested by ${cmd.from}`);
|
||||
if ( config.vods.memes.findIndex(e => e.id === memeId) === -1) {
|
||||
twitch.botChat.say(cmd.to, `No meme with that ID exists!`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
memeId = config.vods.memes.sort(util.randSort)[0].id;
|
||||
console.log(`${memeId} meme randomly selected`);
|
||||
}
|
||||
|
||||
director.showMeme(memeId);
|
||||
},
|
||||
|
||||
|
||||
switch: (cmd) => {
|
||||
let newScene = cmd.args[1] || false;
|
||||
if (!newScene) {
|
||||
twitch.botChat.say(cmd.to, `A scene name is required!`);
|
||||
return;
|
||||
}
|
||||
|
||||
obs.switchToScene(newScene).catch(console.error);
|
||||
},
|
||||
|
||||
|
||||
setact: (cmd) => {
|
||||
let newActivity = cmd.args.slice(1).join(' ');
|
||||
if (!newActivity) {
|
||||
twitch.botChat.say(cmd.to, `Please provide a new activity`);
|
||||
return;
|
||||
}
|
||||
|
||||
obs.showActivity(newActivity).catch(console.error);
|
||||
},
|
||||
|
||||
|
||||
add: (cmd) => {
|
||||
// @TODO: DRY this out with the checks in vr
|
||||
let requestedVideoId = cmd.args[1] || false;
|
||||
if (requestedVideoId === false) {
|
||||
twitch.botChat.say(cmd.to, `Missing video ID`);
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure request vid isn't in the queue already
|
||||
// @TODO: Move into FGFM
|
||||
if (director.state.videoQueue.findIndex(e => e.id == requestedVideoId) !== -1) {
|
||||
twitch.botChat.say(cmd.to, `That video is in the queue already!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// search for req'd vid by id in config.vods.alttp
|
||||
let vodIndex = config.vods.alttp.findIndex(e => e.id == requestedVideoId);
|
||||
if (vodIndex === -1) {
|
||||
twitch.botChat.say(cmd.to, `A video with that ID does not exist!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// add to queue if it exists
|
||||
// @TODO: Move into FGFM
|
||||
if (director.addVideo(config.vods.alttp[vodIndex])) {
|
||||
twitch.botChat.say(cmd.to, `${config.vods.alttp[vodIndex].chatName} has been added to the queue [${director.state.videoQueue.length}]`);
|
||||
} else {
|
||||
twitch.botChat.say(cmd.to, `Video could not be added to queue!`);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
skip: (cmd) => {
|
||||
director.skip();
|
||||
},
|
||||
|
||||
|
||||
clear: (cmd) => {
|
||||
director.clearQueue();
|
||||
},
|
||||
|
||||
|
||||
startvote: (cmd) => {
|
||||
videoVoteJob.reschedule(`*/${config.videoPollIntervalMinutes} * * * *`);
|
||||
twitch.botChat.say(cmd.to, `Video Queue Voting will start in ${config.videoPollIntervalMinutes} minutes!`);
|
||||
},
|
||||
|
||||
|
||||
pausevote: (cmd) => {
|
||||
clearInterval(rtvInterval);
|
||||
videoVoteJob.cancel();
|
||||
twitch.botChat.say(cmd.to, `Video Queue Voting has been paused.`);
|
||||
},
|
||||
|
||||
|
||||
reboot: (cmd) => {
|
||||
console.log('Received request from admin to reboot...');
|
||||
twitch.botChat.say(cmd.to, 'Rebooting...');
|
||||
process.exit(0); // requires process manager with autoreboot to work
|
||||
}
|
||||
},
|
||||
|
||||
user: {
|
||||
|
||||
vote: (cmd) => {
|
||||
let userVote = cmd.args[1] || false;
|
||||
|
||||
if (userVote === false) {
|
||||
rockTheVote();
|
||||
return;
|
||||
}
|
||||
|
||||
userVote = Number.parseInt(userVote);
|
||||
|
||||
if (!Number.isInteger(userVote) || userVote < 1 || userVote > currentChoices.length) {
|
||||
return twitch.botChat.say(cmd.to, `@${from}, please choose an option from 1 - ${currentChoices.length}!`);
|
||||
}
|
||||
|
||||
// Check for uniqueness of vote
|
||||
// if it's not unique, update the vote
|
||||
let prevVote = userVotes.findIndex(e => e.from === from);
|
||||
if (prevVote !== -1) {
|
||||
if (userVotes[prevVote].vote !== userVote) {
|
||||
// update vote and inform the user
|
||||
userVotes[prevVote].vote = userVote;
|
||||
twitch.botChat.say(cmd.to, `@${from}, your vote has been updated!`);
|
||||
} else {
|
||||
twitch.botChat.say(cmd.to, `@${from}, your vote is already in!`);
|
||||
}
|
||||
} else {
|
||||
// log user vote
|
||||
userVotes.push({"from": from, "vote": userVote});
|
||||
twitch.botChat.say(cmd.to, `@${from}, your vote has been logged!`);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
queue: (cmd) => {
|
||||
// @TODO: Move into FGFM
|
||||
if (director.state.videoQueue.length > 0) {
|
||||
let chatQueue = director.state.videoQueue.slice(0, 10).map((c, i) => {
|
||||
return `[${i+1}] ${c.chatName}`;
|
||||
});
|
||||
twitch.botChat.say(cmd.to, chatQueue.join(' | '));
|
||||
} else {
|
||||
twitch.botChat.say(cmd.to, `No videos currently in queue!`);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
current: (cmd) => {
|
||||
// @TODO: Move into FGFM
|
||||
twitch.botChat.say(cmd.to, `Now Playing: ${director.state.currentVideo.chatName}`);
|
||||
},
|
||||
|
||||
|
||||
next: (cmd) => {
|
||||
// @TODO: Move into FGFM
|
||||
if (director.state.videoQueue.length > 0) {
|
||||
twitch.botChat.say(cmd.to, `Next Video: ${director.state.videoQueue[0].chatName}`);
|
||||
} else {
|
||||
twitch.botChat.say(cmd.to, `No videos currently in queue!`);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
vr: (cmd) => {
|
||||
let requestedVideoId = cmd.args[1] || false;
|
||||
if (requestedVideoId === false) {
|
||||
twitch.botChat.say(cmd.to, `Useage: ${config.twitch.cmdPrefix}vr <video-id> | Videos: https://pastebin.com/qv0wDkvB`);
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure request vid isn't in the queue already
|
||||
// @TODO: Move into FGFM
|
||||
if (director.state.videoQueue.findIndex(e => e.id === requestedVideoId) !== -1) {
|
||||
twitch.botChat.say(cmd.to, `That video is in the queue already!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// search for req'd vid by id in config.vods.alttp
|
||||
let vodIndex = config.vods.alttp.findIndex(e => e.id === requestedVideoId);
|
||||
if (vodIndex === -1) {
|
||||
twitch.botChat.say(cmd.to, `A video with that ID does not exist!`);
|
||||
return;
|
||||
}
|
||||
|
||||
config.vods.alttp[vodIndex].requestedBy = cmd.from;
|
||||
|
||||
// add to queue if it exists
|
||||
// @TODO: Return queue position from addVideo
|
||||
if (director.addVideo(config.vods.alttp[vodIndex])) {
|
||||
twitch.botChat.say(cmd.to, `${config.vods.alttp[vodIndex].chatName} has been added to the queue [${director.state.videoQueue.length}]`);
|
||||
} else {
|
||||
twitch.botChat.say(cmd.to, `${config.vods.alttp[vodIndex].chatName} could not be added to the queue!`);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
room: (cmd) => {
|
||||
let roomId = cmd.args[1] || false;
|
||||
let room;
|
||||
|
||||
if (roomId !== false) {
|
||||
let roomIndex = config.rooms.findIndex(e => e.id === parseInt(roomId));
|
||||
|
||||
if (roomIndex === -1) {
|
||||
twitch.botChat.say(cmd.to, `No room found matching that ID!`);
|
||||
return;
|
||||
}
|
||||
|
||||
room = config.rooms[roomIndex];
|
||||
} else {
|
||||
twitch.botChat.say(cmd.to, `Useage: ${config.twitch.cmdPrefix}room <room-id> | Rooms: https://goo.gl/qoNmuH`);
|
||||
return;
|
||||
}
|
||||
|
||||
room.requestedBy = cmd.from;
|
||||
|
||||
director.addRoomVideo(room);
|
||||
// @TODO: Return new queue position from addRoomVideo and use below
|
||||
twitch.botChat.say(cmd.to, `Added ${room.dungeonName||'?'} - ${room.roomName||'?'} to the queue [${director.state.videoQueue.length}]!`);
|
||||
},
|
||||
|
||||
|
||||
rngames: (cmd) => {
|
||||
twitch.botChat.say(cmd.to, snesGames.sort(util.randSort).slice(0, 10).join(' | '));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Listen for the above commands
|
||||
twitch.botChat.addListener('message', (from, to, message) => {
|
||||
// Ignore everything from blacklisted users
|
||||
if (config.twitch.blacklistedUsers.includes(from)) return;
|
||||
|
||||
// Ignore commands that don't start with the designated prefix
|
||||
if (!message.startsWith(config.twitch.cmdPrefix)) return;
|
||||
|
||||
// Remove command prefix for parsing
|
||||
let noPrefix = message.slice(config.twitch.cmdPrefix.length);
|
||||
|
||||
// Ignore blank commands
|
||||
if (noPrefix.length === 0) return;
|
||||
|
||||
// Parse command arguments
|
||||
let args = noPrefix.split(' ');
|
||||
let key = args[0] || '';
|
||||
|
||||
// Ignore messages without a command
|
||||
if (!key || key.length === 0) return;
|
||||
|
||||
// Ignore unrecognized commands
|
||||
if (!commands.admin.hasOwnProperty(key) && !commands.user.hasOwnProperty(key)) return;
|
||||
|
||||
// Check if the command is on cooldown for this user in this channel (admins bypass this)
|
||||
let cooldownKey = md5(from+to+key);
|
||||
cooldowns.get(cooldownKey, config.twitch.defaultUserCooldown)
|
||||
.then(onCooldown => {
|
||||
if (onCooldown === false || config.twitch.admins.includes(from)) {
|
||||
let command = {message: message, from: from, to: to, key: key, args: args};
|
||||
|
||||
// Handle admin commands
|
||||
if (commands.admin.hasOwnProperty(command.key) && config.twitch.admins.includes(from)) {
|
||||
return commands.admin[command.key](command);
|
||||
}
|
||||
|
||||
// Handle all other user commands
|
||||
if (commands.user.hasOwnProperty(command.key)) {
|
||||
// Place this command on cooldown for the user
|
||||
cooldowns.set(cooldownKey, config.twitch.defaultUserCooldown);
|
||||
return commands.user[command.key](command);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
});
|
||||
|
||||
// @TODO: Modularize timed events
|
||||
//console.log(`Initializing stream timers...`);
|
||||
let userVotes = currentChoices = [];
|
||||
let rockTheVote = () => {};
|
||||
// @TODO: Move this interval to config
|
||||
let rtvInterval = setInterval(() => {rockTheVote()}, 300000);
|
||||
let videoVoteJob = new schedule.Job(async () => {
|
||||
// Tally votes from previous election (if there was one), add the winner to the queue
|
||||
let winner;
|
||||
if (currentChoices.length > 0) {
|
||||
if (userVotes.length === 0) {
|
||||
// choose a random element from currentChoices
|
||||
winner = util.randElement(currentChoices);
|
||||
console.log(`VIDEO CHOSEN RANDOMLY: ${winner.chatName}`);
|
||||
twitch.botChat.say(config.twitch.channel, `No Votes Logged -- Next Video Chosen at Random: ${winner.chatName}`);
|
||||
} else {
|
||||
// tally and sort votes
|
||||
let voteTallies = [];
|
||||
await util.asyncForEach(userVotes, async (vote) => {
|
||||
tallyIndex = voteTallies.findIndex(e => e.id === vote.vote);
|
||||
if (tallyIndex !== -1) {
|
||||
voteTallies[tallyIndex].count++;
|
||||
} else {
|
||||
voteTallies.push({id: vote.vote, count: 1});
|
||||
}
|
||||
});
|
||||
voteTallies.sort((a, b) => {
|
||||
if (a.count < b.count) {
|
||||
return -1;
|
||||
}
|
||||
if (a.count > b.count) {
|
||||
return 1;
|
||||
}
|
||||
// a must be equal to b
|
||||
return 0;
|
||||
});
|
||||
|
||||
console.log(`Voting Results: ${JSON.stringify(voteTallies)}`);
|
||||
winner = currentChoices[voteTallies[0].id-1];
|
||||
console.log(`WINNER OF THE VOTE: ${winner.chatName}`);
|
||||
twitch.botChat.say(config.twitch.channel, `Winner of the Video Vote: ${winner.chatName}`);
|
||||
|
||||
// clear user votes
|
||||
userVotes = [];
|
||||
}
|
||||
|
||||
director.addVideo(winner);
|
||||
}
|
||||
|
||||
// Show a gameplay vod
|
||||
const showVideo = video => {
|
||||
console.log(`Showing video: ${video.chatName}`);
|
||||
|
||||
obs.playVideoInScene(video, config.defaultSceneName, nextVideo)
|
||||
.then(timer => {
|
||||
// track timer so we can cancel callback later on if necessary
|
||||
state.videoTimer = timer;
|
||||
|
||||
// update activity label and show/hide appropriately
|
||||
if (video.hasOwnProperty('label') && video.label !== false) {
|
||||
obs.showActivity(video.label);
|
||||
} else {
|
||||
obs.hideActivity();
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
const addRoomVideo = (room, loop) => {
|
||||
let loops = 1;
|
||||
if (typeof loop === 'undefined' || loop === true) {
|
||||
loops = Math.floor(config.roomVidPlaytime / room.videoData.length);
|
||||
}
|
||||
console.log(`Adding room video for ${room.dungeonName} - ${room.roomName} to the queue (${loops} loops)`);
|
||||
|
||||
let video = {
|
||||
"filePath": `${config.roomVidsBasePath}${room.winPath}`,
|
||||
"sceneItem": (room.videoData.width === 960) ? "4x3ph" : "16x9ph",
|
||||
"length": room.videoData.length,
|
||||
"label": room.roomName,
|
||||
"chatName": room.roomName,
|
||||
"loops": loops,
|
||||
"requestedBy": room.requestedBy
|
||||
};
|
||||
|
||||
state.videoQueue.push(video);
|
||||
};
|
||||
|
||||
// Picks the next video in the queue (shuffles if empty)
|
||||
// Also handles "commercial breaks" if enabled
|
||||
const nextVideo = () => {
|
||||
// Show a "commercial break" if it's been long enough since the last one
|
||||
let secondsSinceLastCommercial = (Date.now() - state.lastCommercialShownAt) / 1000;
|
||||
if (config.commercialsEnabled === true && secondsSinceLastCommercial >= config.commercialInterval) {
|
||||
state.commercialPlaying = true;
|
||||
console.log(`It has been ${secondsSinceLastCommercial} seconds since the last commercial break!`);
|
||||
// Random chance for it to be "everybody wow"
|
||||
let memeId = false;
|
||||
if ((Math.floor(Math.random() * 100) + 1) <= config.auwChance) {
|
||||
console.log(`Showing AUW!`);
|
||||
memeId = 'auw';
|
||||
}
|
||||
showMeme(memeId).then(() => {
|
||||
state.lastCommercialShownAt = Date.now();
|
||||
state.commercialPlaying = false;
|
||||
nextVideo();
|
||||
}).catch(console.error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep track of recently played videos
|
||||
if (state.recentlyPlayed.length === config.recentlyPlayedMemory) {
|
||||
state.recentlyPlayed.shift();
|
||||
}
|
||||
state.recentlyPlayed.push(state.currentVideo.id);
|
||||
|
||||
// If a commercial is playing, wait until it's done to switch
|
||||
while (state.commercialPlaying === true) {}
|
||||
|
||||
// play the next video in the queue, or pick one at random if the queue is empty
|
||||
if (state.videoQueue.length > 0) {
|
||||
state.currentVideo = state.videoQueue.shift();
|
||||
} else {
|
||||
// Random chance for room grind to be played for an amount of time instead of another video be shuffled to
|
||||
if ((Math.floor(Math.random() * 100) + 1) <= config.roomGrindChance) {
|
||||
console.log(`Room grind selected!`);
|
||||
// show room-grind source
|
||||
obs.showRoomGrind(config.roomGrindPlaytime, () => {nextVideo()})
|
||||
.then(timer => {
|
||||
state.videoTimer = timer;
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Random chance for room videos to be added
|
||||
if ((Math.floor(Math.random() * 100) + 1) <= config.roomShuffleChance) {
|
||||
console.log(`Room vids selected!`);
|
||||
|
||||
let roomVid = config.rooms.sort(util.randSort).slice(0, 1).shift();
|
||||
roomVid.requestedBy = 'shuffle';
|
||||
|
||||
addRoomVideo(roomVid);
|
||||
|
||||
// play the first one
|
||||
state.currentVideo = state.videoQueue.shift();
|
||||
} else {
|
||||
// filter recently played from shuffle
|
||||
let freshVods = config.vods.alttp.filter(e => {
|
||||
return e.includeInShuffle === true && !state.recentlyPlayed.includes(e.id);
|
||||
});
|
||||
state.currentVideo = freshVods.sort(util.randSort).slice(0, 1).shift();
|
||||
}
|
||||
}
|
||||
|
||||
showVideo(state.currentVideo);
|
||||
};
|
||||
|
||||
// "Commercials"
|
||||
const showCommercial = (video, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let handleFinish = () => {
|
||||
// unmute songrequest audio
|
||||
//twitch.editorChat.say(config.twitch.channel, `!volume ${config.defaultSRVolume}`);
|
||||
if (typeof callback !== 'undefined') callback();
|
||||
};
|
||||
|
||||
obs.playVideoInScene(video, config.commercialSceneName, handleFinish)
|
||||
.then(timer => {
|
||||
// mute songrequest audio
|
||||
//twitch.editorChat.say(config.twitch.channel, `!volume 0`);
|
||||
resolve(timer);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
// Memes-By-Id
|
||||
const showMeme = (id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// find the vod in memes
|
||||
let video = config.vods.memes.find(e => e.id === id);
|
||||
if (!video) {
|
||||
reject(`No meme found matching ID ${id}`);
|
||||
}
|
||||
|
||||
let handleFinish = () => {
|
||||
if (id === 'auw') {
|
||||
obs.hide("owen", config.commercialSceneName);
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
|
||||
showCommercial(video, handleFinish)
|
||||
.then(videoHasStarted => {
|
||||
// in the case of 'auw', show owen + tell chat what's up
|
||||
if (id === 'auw') {
|
||||
obs.show("owen", config.commercialSceneName);
|
||||
twitch.botChat.say(config.twitch.channel, 'Everybody OwenWow');
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
});
|
||||
};
|
||||
|
||||
// Twitch Chat Commands
|
||||
twitch.botChat.addListener('message', (from, to, message) => {
|
||||
// Ignore everything from blacklisted users
|
||||
if (config.twitch.blacklistedUsers.includes(from)) return;
|
||||
|
||||
// Listen for commands that start with the designated prefix
|
||||
if (message.startsWith(config.twitch.cmdPrefix)) {
|
||||
|
||||
let commandParts = message.slice(config.twitch.cmdPrefix.length).split(' ');
|
||||
let commandNoPrefix = commandParts[0] || '';
|
||||
|
||||
// ADMIN COMMANDS
|
||||
if (config.twitch.admins.includes(from)) {
|
||||
|
||||
// SHOW/HIDE SOURCE
|
||||
if (commandNoPrefix === 'show' || commandNoPrefix === 'hide') {
|
||||
let newVisibility = (commandNoPrefix === 'show');
|
||||
|
||||
let sceneItem = commandParts[1] || false;
|
||||
if (!sceneItem) {
|
||||
twitch.botChat.say(to, `A scene item name is required!`);
|
||||
return;
|
||||
}
|
||||
|
||||
let sceneOrGroup = commandParts[2] || obs.currentScene;
|
||||
obs.setVisible(sceneItem, sceneOrGroup, newVisibility).catch(console.error);
|
||||
|
||||
// TOGGLE SOURCE VISIBILITY
|
||||
} else if (commandNoPrefix === 't') {
|
||||
let sceneItem = commandParts[1] || false;
|
||||
if (!sceneItem) {
|
||||
twitch.botChat.say(to, `A scene item name is required!`);
|
||||
return;
|
||||
}
|
||||
|
||||
obs.toggleVisible(sceneItem).catch(console.error);
|
||||
|
||||
// EVERYBODY WOW
|
||||
} else if (commandNoPrefix === 'auw') {
|
||||
state.commercialPlaying = true;
|
||||
showMeme('auw').then(() => state.commercialPlaying = false).catch(console.error);
|
||||
|
||||
// MEMES ON-DEMAND
|
||||
} else if (commandNoPrefix === 'meme') {
|
||||
let memeId = commandParts[1] || false;
|
||||
if (memeId) {
|
||||
console.log(`${memeId} meme requested`);
|
||||
if ( config.vods.memes.findIndex(e => e.id === memeId) === -1) {
|
||||
twitch.botChat.say(to, `No meme with that ID exists!`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
memeId = config.vods.memes.sort(util.randSort)[0].id;
|
||||
console.log(`${memeId} meme randomly selected`);
|
||||
}
|
||||
|
||||
state.commercialPlaying = true;
|
||||
showMeme(memeId).then(() => state.commercialPlaying = false).catch(console.error);
|
||||
|
||||
// SWITCH SCENES
|
||||
} else if (commandNoPrefix === 'switch') {
|
||||
|
||||
let newScene = commandParts[1] || false;
|
||||
if (!newScene) {
|
||||
twitch.botChat.say(to, `A scene name is required!`);
|
||||
return;
|
||||
}
|
||||
|
||||
obs.switchToScene(newScene).catch(console.error);
|
||||
|
||||
// SET ON-SCREEN ACTIVITY
|
||||
} else if (commandNoPrefix === 'setact') {
|
||||
let newActivity = commandParts.slice(1).join(' ');
|
||||
if (!newActivity) {
|
||||
twitch.botChat.say(to, `Please provide a new activity`);
|
||||
return;
|
||||
}
|
||||
|
||||
obs.showActivity(newActivity).catch(console.error);
|
||||
|
||||
// REBOOT
|
||||
} else if (commandNoPrefix === 'reboot') {
|
||||
console.log('Received request from admin to reboot...');
|
||||
twitch.botChat.say(to, 'Rebooting...');
|
||||
process.exit(0);
|
||||
|
||||
// SKIP
|
||||
} else if (commandNoPrefix === 'skip') {
|
||||
clearTimeout(state.videoTimer);
|
||||
obs.hide(state.currentVideo.sceneItem, config.defaultSceneName).then(nextVideo).catch(console.error);
|
||||
|
||||
// CLEAR QUEUE
|
||||
} else if (commandNoPrefix === 'clear') {
|
||||
state.videoQueue = [];
|
||||
|
||||
// ADD
|
||||
} else if (commandNoPrefix === 'add') {
|
||||
let requestedVideoId = commandParts[1] || false;
|
||||
if (requestedVideoId === false) {
|
||||
twitch.botChat.say(to, `Missing video ID`);
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure request vid isn't in the queue already
|
||||
if (state.videoQueue.findIndex(e => e.id == requestedVideoId) !== -1) {
|
||||
twitch.botChat.say(to, `That video is in the queue already!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// search for req'd vid by id in config.vods.alttp
|
||||
let vodIndex = config.vods.alttp.findIndex(e => e.id == requestedVideoId);
|
||||
if (vodIndex === -1) {
|
||||
twitch.botChat.say(to, `A video with that ID does not exist!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// add to queue if it exists
|
||||
state.videoQueue.push(config.vods.alttp[vodIndex]);
|
||||
twitch.botChat.say(to, `${config.vods.alttp[vodIndex].chatName} has been added to the queue [${state.videoQueue.length}]`);
|
||||
return;
|
||||
|
||||
// START VOTE
|
||||
} else if (commandNoPrefix === 'startvote') {
|
||||
videoVoteJob.reschedule(`*/${config.videoPollIntervalMinutes} * * * *`);
|
||||
twitch.botChat.say(to, `Video Queue Voting will start in ${config.videoPollIntervalMinutes} minutes!`);
|
||||
|
||||
// PAUSE VOTE
|
||||
} else if (commandNoPrefix === 'pausevote') {
|
||||
clearInterval(rtvInterval);
|
||||
videoVoteJob.cancel();
|
||||
twitch.botChat.say(to, `Video Queue Voting has been paused.`);
|
||||
}
|
||||
}
|
||||
////////////////
|
||||
|
||||
// USER COMMANDS
|
||||
//
|
||||
// VOTE FOR VIDEO
|
||||
if (commandNoPrefix === 'vote') {
|
||||
let userVote = commandParts[1] || false;
|
||||
|
||||
if (userVote === false) {
|
||||
rockTheVote();
|
||||
return;
|
||||
}
|
||||
|
||||
userVote = Number.parseInt(userVote);
|
||||
|
||||
if (!Number.isInteger(userVote) || userVote < 1 || userVote > currentChoices.length) {
|
||||
return twitch.botChat.say(to, `@${from}, please choose an option from 1 - ${currentChoices.length}!`);
|
||||
}
|
||||
|
||||
// Check for uniqueness of vote
|
||||
// if it's not unique, update the vote
|
||||
let prevVote = userVotes.findIndex(e => e.from === from);
|
||||
if (prevVote !== -1) {
|
||||
if (userVotes[prevVote].vote !== userVote) {
|
||||
// update vote and inform the user
|
||||
userVotes[prevVote].vote = userVote;
|
||||
twitch.botChat.say(to, `@${from}, your vote has been updated!`);
|
||||
} else {
|
||||
twitch.botChat.say(to, `@${from}, your vote is already in!`);
|
||||
}
|
||||
} else {
|
||||
// log user vote
|
||||
userVotes.push({"from": from, "vote": userVote});
|
||||
twitch.botChat.say(to, `@${from}, your vote has been logged!`);
|
||||
}
|
||||
|
||||
|
||||
// QUEUE STATUS
|
||||
} else if (commandNoPrefix === 'queue') {
|
||||
if (state.videoQueue.length > 0) {
|
||||
let chatQueue = state.videoQueue.slice(0, 10).map((c, i) => {
|
||||
return `[${i+1}] ${c.chatName}`;
|
||||
});
|
||||
twitch.botChat.say(to, chatQueue.join(' | '));
|
||||
} else {
|
||||
twitch.botChat.say(to, `No videos currently in queue!`);
|
||||
}
|
||||
|
||||
|
||||
// CURRENT VIDEO
|
||||
} else if (commandNoPrefix === 'current') {
|
||||
twitch.botChat.say(to, `Now Playing: ${state.currentVideo.chatName}`);
|
||||
|
||||
|
||||
// NEXT VIDEO
|
||||
} else if (commandNoPrefix === 'next') {
|
||||
if (state.videoQueue.length > 0) {
|
||||
twitch.botChat.say(to, `Next Video: ${state.videoQueue[0].chatName}`);
|
||||
} else {
|
||||
twitch.botChat.say(to, `No videos currently in queue!`);
|
||||
}
|
||||
|
||||
|
||||
// VIDEO REQUEST
|
||||
} else if (commandNoPrefix === 'vr') {
|
||||
let requestedVideoId = commandParts[1] || false;
|
||||
if (requestedVideoId === false) {
|
||||
twitch.botChat.say(to, `Useage: ${config.twitch.cmdPrefix}vr <video-id> | Videos: https://pastebin.com/qv0wDkvB`);
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure request vid isn't in the queue already
|
||||
if (state.videoQueue.findIndex(e => e.id === requestedVideoId) !== -1) {
|
||||
twitch.botChat.say(to, `That video is in the queue already!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// search for req'd vid by id in config.vods.alttp
|
||||
let vodIndex = config.vods.alttp.findIndex(e => e.id === requestedVideoId);
|
||||
if (vodIndex === -1) {
|
||||
twitch.botChat.say(to, `A video with that ID does not exist!`);
|
||||
return;
|
||||
}
|
||||
|
||||
config.vods.alttp[vodIndex].requestedBy = from;
|
||||
|
||||
// add to queue if it exists
|
||||
state.videoQueue.push(config.vods.alttp[vodIndex]);
|
||||
twitch.botChat.say(to, `${config.vods.alttp[vodIndex].chatName} has been added to the queue [${state.videoQueue.length}]`);
|
||||
return;
|
||||
|
||||
// ROOM VID REQUESTS
|
||||
} else if (commandNoPrefix === 'room') {
|
||||
let roomId = commandParts[1] || false;
|
||||
let room;
|
||||
|
||||
if (roomId !== false) {
|
||||
let roomIndex = config.rooms.findIndex(e => e.id === parseInt(roomId));
|
||||
|
||||
if (roomIndex === -1) {
|
||||
twitch.botChat.say(to, `No room found matching that ID!`);
|
||||
return;
|
||||
}
|
||||
|
||||
room = config.rooms[roomIndex];
|
||||
} else {
|
||||
// choose a room at random
|
||||
// room = config.rooms.sort(util.randSort).slice(0, 1).shift();
|
||||
twitch.botChat.say(to, `Useage: ${config.twitch.cmdPrefix}room <room-id> | Rooms: https://goo.gl/qoNmuH`);
|
||||
return;
|
||||
}
|
||||
|
||||
room.requestedBy = from;
|
||||
|
||||
addRoomVideo(room);
|
||||
twitch.botChat.say(to, `Added ${room.dungeonName||'?'} - ${room.roomName||'?'} to the queue [${state.videoQueue.length}]!`);
|
||||
|
||||
// RNGAMES
|
||||
} else if (commandNoPrefix === 'rngames') {
|
||||
twitch.botChat.say(to, snesGames.sort(util.randSort).slice(0, 10).join(' | '));
|
||||
}
|
||||
////////////////
|
||||
}
|
||||
// choose more random videos from config.vods.alttp (that aren't already in the queue)
|
||||
// @TODO: Move into FGFM
|
||||
let vodsNotInQueue = config.vods.alttp.filter(e => {
|
||||
let inQueue = director.state.videoQueue.findIndex(q => q.id === e.id) !== -1;
|
||||
return !inQueue;
|
||||
});
|
||||
currentChoices = vodsNotInQueue.sort(util.randSort).slice(0, config.videoPollSize);
|
||||
|
||||
// Poll the chat
|
||||
let chatChoices = currentChoices.map((c, i) => {
|
||||
return `[${i+1}] ${c.chatName}`;
|
||||
});
|
||||
|
||||
// @TODO: Modularize timed events
|
||||
console.log(`Initializing stream timers...`);
|
||||
let userVotes = currentChoices = [];
|
||||
let rockTheVote = () => {};
|
||||
let rtvInterval = setInterval(() => {rockTheVote()}, 300000);
|
||||
let videoVoteJob = new schedule.Job(async () => {
|
||||
// Tally votes from previous election (if there was one), add the winner to the queue
|
||||
let winner;
|
||||
if (currentChoices.length > 0) {
|
||||
if (userVotes.length === 0) {
|
||||
// choose a random element from currentChoices
|
||||
winner = util.randElement(currentChoices);
|
||||
console.log(`VIDEO CHOSEN RANDOMLY: ${winner.chatName}`);
|
||||
twitch.botChat.say(config.twitch.channel, `No Votes Logged -- Next Video Chosen at Random: ${winner.chatName}`);
|
||||
} else {
|
||||
// tally and sort votes
|
||||
let voteTallies = [];
|
||||
await util.asyncForEach(userVotes, async (vote) => {
|
||||
tallyIndex = voteTallies.findIndex(e => e.id === vote.vote);
|
||||
if (tallyIndex !== -1) {
|
||||
voteTallies[tallyIndex].count++;
|
||||
} else {
|
||||
voteTallies.push({id: vote.vote, count: 1});
|
||||
}
|
||||
});
|
||||
voteTallies.sort((a, b) => {
|
||||
if (a.count < b.count) {
|
||||
return -1;
|
||||
}
|
||||
if (a.count > b.count) {
|
||||
return 1;
|
||||
}
|
||||
// a must be equal to b
|
||||
return 0;
|
||||
});
|
||||
|
||||
console.log(`Voting Results: ${JSON.stringify(voteTallies)}`);
|
||||
winner = currentChoices[voteTallies[0].id-1];
|
||||
console.log(`WINNER OF THE VOTE: ${winner.chatName}`);
|
||||
twitch.botChat.say(config.twitch.channel, `Winner of the Video Vote: ${winner.chatName}`);
|
||||
|
||||
// clear user votes
|
||||
userVotes = [];
|
||||
}
|
||||
|
||||
state.videoQueue.push(winner);
|
||||
}
|
||||
|
||||
// choose more random videos from config.vods.alttp (that aren't already in the queue)
|
||||
let vodsNotInQueue = config.vods.alttp.filter(e => {
|
||||
let inQueue = state.videoQueue.findIndex(q => q.id === e.id) !== -1;
|
||||
return !inQueue;
|
||||
});
|
||||
currentChoices = vodsNotInQueue.sort(util.randSort).slice(0, config.videoPollSize);
|
||||
|
||||
// Poll the chat
|
||||
let chatChoices = currentChoices.map((c, i) => {
|
||||
return `[${i+1}] ${c.chatName}`;
|
||||
});
|
||||
|
||||
rockTheVote = () => {
|
||||
twitch.botChat.say(config.twitch.channel, `Vote for which video you'd like to add to the queue using ${config.twitch.cmdPrefix}vote #: ${chatChoices.join(' | ')}`)
|
||||
};
|
||||
clearInterval(rtvInterval);
|
||||
rockTheVote();
|
||||
rtvInterval = setInterval(() => {rockTheVote()}, 300000);
|
||||
});
|
||||
|
||||
init();
|
||||
resolve(obs);
|
||||
rockTheVote = () => {
|
||||
twitch.botChat.say(config.twitch.channel, `Vote for which video you'd like to add to the queue using ${config.twitch.cmdPrefix}vote #: ${chatChoices.join(' | ')}`)
|
||||
};
|
||||
clearInterval(rtvInterval);
|
||||
rockTheVote();
|
||||
rtvInterval = setInterval(() => {rockTheVote()}, 300000);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -3,13 +3,10 @@ module.exports = {
|
||||
set: placeOnCooldown
|
||||
};
|
||||
|
||||
const memcache = require('memcache'),
|
||||
md5 = require('md5'),
|
||||
keyPrefix = 'cd';
|
||||
|
||||
const cache = new memcache.Client();
|
||||
cache.on('error', console.error);
|
||||
cache.connect();
|
||||
const NodeCache = require('node-cache');
|
||||
const cache = new NodeCache({checkperiod: 1});
|
||||
const md5 = require('md5');
|
||||
const keyPrefix = 'cd';
|
||||
|
||||
// Given a cooldownTime in seconds and a command, returns false if the command is not on cooldown
|
||||
// returns the time in seconds until the command will be ready again otherwise
|
||||
@@ -40,7 +37,7 @@ function isOnCooldown(command, cooldownTime, callback)
|
||||
function placeOnCooldown(command, cooldownTime)
|
||||
{
|
||||
let key = keyPrefix + md5(command);
|
||||
return cache.set(key, Date.now(), handleCacheSet, cooldownTime);
|
||||
return cache.set(key, Date.now(), cooldownTime, handleCacheSet);
|
||||
}
|
||||
|
||||
function handleCacheSet(error, result) {}
|
||||
|
||||
197
lib/fgfm.js
197
lib/fgfm.js
@@ -0,0 +1,197 @@
|
||||
const util = require('./util');
|
||||
|
||||
function FGFM(config) {
|
||||
// Set up initial state
|
||||
this.config = config.config;
|
||||
this.obs = config.obs;
|
||||
this.state = {
|
||||
videoQueue: [],
|
||||
recentlyPlayed: [],
|
||||
currentVideo: null,
|
||||
videoTimer: null,
|
||||
lastCommercialShownAt: Date.now(),
|
||||
commercialPlaying: false
|
||||
};
|
||||
|
||||
// Set up initial queue + start playback
|
||||
this.init = () => {
|
||||
// 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);
|
||||
|
||||
// Start queue playback
|
||||
this.state.currentVideo = this.state.videoQueue.shift();
|
||||
this.showVideo(this.state.currentVideo);
|
||||
};
|
||||
|
||||
// Shows.. a... video
|
||||
this.showVideo = video => {
|
||||
console.log(`Showing video: ${video.chatName}`);
|
||||
|
||||
this.obs.playVideoInScene(video, this.config.defaultSceneName, this.nextVideo)
|
||||
.then(timer => {
|
||||
// track timer so we can cancel callback later on if necessary
|
||||
this.state.videoTimer = timer;
|
||||
|
||||
// update activity label and show/hide appropriately
|
||||
if (video.hasOwnProperty('label') && video.label !== false) {
|
||||
this.obs.showActivity(video.label);
|
||||
} else {
|
||||
this.obs.hideActivity();
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
// Adds a gameplay vod to the queue
|
||||
this.addVideo = video => {
|
||||
return this.state.videoQueue.push(video);
|
||||
};
|
||||
|
||||
// Adds a room to the queue and handles looping setup
|
||||
this.addRoomVideo = (room, loop) => {
|
||||
let loops = 1;
|
||||
if (typeof loop === 'undefined' || loop === true) {
|
||||
loops = Math.floor(this.config.roomVidPlaytime / room.videoData.length);
|
||||
}
|
||||
console.log(`Adding room video for ${room.dungeonName} - ${room.roomName} to the queue (${loops} loops)`);
|
||||
|
||||
let video = {
|
||||
filePath: `${this.config.roomVidsBasePath}${room.winPath}`,
|
||||
sceneItem: (room.videoData.width === 960) ? "4x3ph" : "16x9ph",
|
||||
length: room.videoData.length,
|
||||
label: room.roomName,
|
||||
chatName: room.roomName,
|
||||
loops: loops,
|
||||
requestedBy: room.requestedBy
|
||||
};
|
||||
|
||||
this.state.videoQueue.push(video);
|
||||
};
|
||||
|
||||
// Picks the next video in the queue (shuffles if empty)
|
||||
// Also handles "commercial breaks" if enabled
|
||||
this.nextVideo = () => {
|
||||
// Show a "commercial break" if it's been long enough since the last one
|
||||
let secondsSinceLastCommercial = (Date.now() - this.state.lastCommercialShownAt) / 1000;
|
||||
if (this.config.commercialsEnabled === true && secondsSinceLastCommercial >= this.config.commercialInterval) {
|
||||
console.log(`It has been ${secondsSinceLastCommercial} seconds since the last commercial break!`);
|
||||
// Random chance for it to be "everybody wow"
|
||||
let memeId = false;
|
||||
if ((Math.floor(Math.random() * 100) + 1) <= this.config.auwChance) {
|
||||
console.log(`Showing AUW!`);
|
||||
memeId = 'auw';
|
||||
}
|
||||
|
||||
this.showMeme(memeId)
|
||||
.then(() => {
|
||||
this.state.lastCommercialShownAt = Date.now();
|
||||
this.nextVideo();
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep track of recently played videos
|
||||
if (this.state.recentlyPlayed.length === this.config.recentlyPlayedMemory) {
|
||||
this.state.recentlyPlayed.shift();
|
||||
}
|
||||
this.state.recentlyPlayed.push(this.state.currentVideo.id);
|
||||
|
||||
// If a commercial is playing, wait until it's done to switch
|
||||
while (this.state.commercialPlaying === true) {}
|
||||
|
||||
// play the next video in the queue, or pick one at random if the queue is empty
|
||||
if (this.state.videoQueue.length > 0) {
|
||||
this.state.currentVideo = this.state.videoQueue.shift();
|
||||
} else {
|
||||
// Random chance for room grind to be played for an amount of time instead of another video be shuffled to
|
||||
if ((Math.floor(Math.random() * 100) + 1) <= this.config.roomGrindChance) {
|
||||
console.log(`Room grind selected!`);
|
||||
// show room-grind source
|
||||
this.obs.showRoomGrind(this.config.roomGrindPlaytime, () => {this.nextVideo()})
|
||||
.then(timer => {
|
||||
this.state.videoTimer = timer;
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Random chance for room videos to be added
|
||||
if ((Math.floor(Math.random() * 100) + 1) <= this.config.roomShuffleChance) {
|
||||
console.log(`Room vids selected!`);
|
||||
|
||||
this.addRoomVideo(this.config.rooms.sort(util.randSort).slice(0, 1).shift());
|
||||
|
||||
// play the first one
|
||||
this.state.currentVideo = this.state.videoQueue.shift();
|
||||
} else {
|
||||
// filter recently played from shuffle
|
||||
let freshVods = this.config.vods.alttp.filter(e => {
|
||||
return e.includeInShuffle === true && !this.state.recentlyPlayed.includes(e.id);
|
||||
});
|
||||
this.state.currentVideo = freshVods.sort(util.randSort).slice(0, 1).shift();
|
||||
}
|
||||
}
|
||||
|
||||
this.showVideo(this.state.currentVideo);
|
||||
};
|
||||
|
||||
// "Commercials"
|
||||
this.showCommercial = (video, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let handleFinish = () => {
|
||||
this.state.commercialPlaying = false;
|
||||
if (typeof callback !== 'undefined') callback();
|
||||
}
|
||||
|
||||
this.obs.playVideoInScene(video, this.config.commercialSceneName, handleFinish)
|
||||
.then(timer => {
|
||||
this.state.commercialPlaying = true;
|
||||
resolve(timer);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
// Memes-By-Id
|
||||
this.showMeme = id => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// find the vod in memes
|
||||
let video = this.config.vods.memes.find(e => e.id === id);
|
||||
if (!video) {
|
||||
reject(`No meme found matching ID ${id}`);
|
||||
}
|
||||
|
||||
let handleFinish = () => {
|
||||
if (id === 'auw') {
|
||||
this.obs.hide("owen", this.config.commercialSceneName);
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
|
||||
this.showCommercial(video, handleFinish)
|
||||
.then(videoHasStarted => {
|
||||
// in the case of 'auw', show owen
|
||||
if (id === 'auw') {
|
||||
this.obs.show("owen", this.config.commercialSceneName);
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
});
|
||||
};
|
||||
|
||||
// Skip the current video and play the next
|
||||
this.skip = () => {
|
||||
clearTimeout(this.state.videoTimer);
|
||||
this.obs.hide(this.state.currentVideo.sceneItem, this.config.defaultSceneName).then(this.nextVideo).catch(console.error);
|
||||
};
|
||||
|
||||
// Clears.. the... queue
|
||||
this.clearQueue = () => {
|
||||
this.state.videoQueue = [];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FGFM;
|
||||
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -106,6 +106,11 @@
|
||||
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
|
||||
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
|
||||
},
|
||||
"clone": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||
"integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@@ -406,6 +411,15 @@
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
|
||||
},
|
||||
"node-cache": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/node-cache/-/node-cache-4.2.0.tgz",
|
||||
"integrity": "sha512-obRu6/f7S024ysheAjoYFEEBqqDWv4LOMNJEuO8vMeEw2AT4z+NCzO4hlc2lhI4vATzbCQv6kke9FVdx0RbCOw==",
|
||||
"requires": {
|
||||
"clone": "2.1.2",
|
||||
"lodash": "4.17.11"
|
||||
}
|
||||
},
|
||||
"node-icu-charset-detector": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/node-icu-charset-detector/-/node-icu-charset-detector-0.2.0.tgz",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"md5": "^2.2.1",
|
||||
"memcache": "^0.3.0",
|
||||
"moment": "^2.22.2",
|
||||
"node-cache": "^4.2.0",
|
||||
"node-opus": "^0.2.9",
|
||||
"node-schedule": "^1.3.0",
|
||||
"obs-websocket-js": "^1.2.0",
|
||||
|
||||
Reference in New Issue
Block a user