automemes

This commit is contained in:
Chris Ham
2018-09-19 12:50:09 -07:00
parent d6ae3aedbd
commit aad4326e52
3 changed files with 121 additions and 73 deletions

View File

@@ -55,11 +55,16 @@
"defaultPlaylist": "room-grind" "defaultPlaylist": "room-grind"
}, },
"initialQueueSize": 3, "initialQueueSize": 3,
"videoSceneName": "fgfm", "defaultSceneName": "fgfm",
"commercialSceneName": "commercial", "commercialSceneName": "commercial",
"videoPollSize": 5, "videoPollSize": 5,
"currentActivitySceneItemName": "now-showing-txt", "currentActivitySceneItemName": "now-showing-txt",
"commercialInterval": 3600, "commercialInterval": 3600,
"auwChance": 50,
"recentlyPlayedMemory": 5,
"roomGrindChance": 50,
"roomGrindPlaytime": 3600,
"defaultSRVolume": 50,
"vods": [ "vods": [
{ {
"id": "ot-seg-escape", "id": "ot-seg-escape",
@@ -930,13 +935,6 @@
"filePath": "Y:\\media\\videos\\ALttP\\trock-indoor-quake.mp4", "filePath": "Y:\\media\\videos\\ALttP\\trock-indoor-quake.mp4",
"sceneItem": "meme1", "sceneItem": "meme1",
"length": 17 "length": 17
},
{
"id": "imout",
"name": "imout",
"filePath": "Y:\\media\\videos\\ALttP\\water-tektite-imout.mp4",
"sceneItem": "meme1",
"length": 7
} }
], ],
"debug": false "debug": false

View File

@@ -4,18 +4,18 @@ TODO:
☐ modularize OBS and Twitch code ☐ modularize OBS and Twitch code
☐ Rotating background images (leftside) ☐ Rotating background images (leftside)
☐ Stream alerts for chat ☐ Stream alerts for chat
☐ add memes to commercial scene
☐ show commercials after a video length cap is hit -- show at conclusion of video
✔ add $setcurrent support (to update text label through obs websocket instead of chat) @done (18-09-17 18:06)
☐ support for $pause ☐ support for $pause
☐ remove currently playing video from vote choices ☐ remove currently playing video from vote choices
☐ restrict # of requests a user can have in the queue at once ☐ restrict # of requests a user can have in the queue at once
☐ Add cooldowns ☐ Add cooldowns
✔ remember the last X vids played, remove these from shuffle choices @done (18-09-17 14:34)
☐ Start/stop stream automation ☐ Start/stop stream automation
☐ Move vods to their own config ☐ Move vods to their own config
☐ Tool to output list of video ID's / descriptions ☐ Tool to output list of video ID's / descriptions
☐ Support viewer skip voting ☐ Support viewer skip voting
960x540 ___________________
896x504 Archive:
✔ show commercials after a video length cap is hit -- show at conclusion of video @done (18-09-19 11:11) @project(TODO)
✔ add memes to commercial scene @done (18-09-19 11:11) @project(TODO)
✔ add $setcurrent support (to update text label through obs websocket instead of chat) @done (18-09-17 18:06) @project(TODO)
✔ remember the last X vids played, remove these from shuffle choices @done (18-09-17 14:34) @project(TODO)

134
fgfm.js
View File

@@ -18,6 +18,7 @@ let videoQueue = recentlyPlayed = [];
let currentVideo; let currentVideo;
let videoTimer; let videoTimer;
let lastCommercialShownAt; let lastCommercialShownAt;
let commercialPlaying = false;
// Connect to OBS Websocket // Connect to OBS Websocket
const obs = new OBSWebSocket(); const obs = new OBSWebSocket();
@@ -125,23 +126,23 @@ const streamInit = (config, obs, twitch) => {
console.log(`Showing video: ${video.chatName}`); console.log(`Showing video: ${video.chatName}`);
let handleVideoFinish = () => { let handleVideoFinish = () => {
obs.setSceneItemProperties({"item": video.sceneItem, "scene-name": config.videoSceneName, "visible": false}) obs.setSceneItemProperties({"item": video.sceneItem, "scene-name": config.defaultSceneName, "visible": false})
.then(data => {nextVideo()}) .then(data => {nextVideo()})
.catch(console.error); .catch(console.error);
}; };
obs.setCurrentScene({"scene-name": config.videoSceneName}) obs.setCurrentScene({"scene-name": config.defaultSceneName})
.then(res => { .then(res => {
playVideoInScene(video, config.videoSceneName, handleVideoFinish) playVideoInScene(video, config.defaultSceneName, handleVideoFinish)
.then(timer => { .then(timer => {
// track timer so we can cancel callback later on if necessary // track timer so we can cancel callback later on if necessary
videoTimer = timer; videoTimer = timer;
// update activity label and show/hide appropriately // update activity label and show/hide appropriately
if (video.hasOwnProperty('label') && video.label !== false) { if (video.hasOwnProperty('label') && video.label !== false) {
obs.setTextGDIPlusProperties({"source": config.currentActivitySceneItemName, "scene-name": config.videoSceneName, "render": true, "text": video.label}); obs.setTextGDIPlusProperties({"source": config.currentActivitySceneItemName, "scene-name": config.defaultSceneName, "render": true, "text": video.label});
} else { } else {
obs.setSceneItemProperties({"item": config.currentActivitySceneItemName, "scene-name": config.videoSceneName, "visible": false}); obs.setSceneItemProperties({"item": config.currentActivitySceneItemName, "scene-name": config.defaultSceneName, "visible": false});
} }
}); });
}) })
@@ -153,20 +154,33 @@ const streamInit = (config, obs, twitch) => {
const nextVideo = () => { const nextVideo = () => {
// Show a "commercial break" if it's been long enough since the last one // Show a "commercial break" if it's been long enough since the last one
let secondsSinceLastCommercial = (Date.now() - lastCommercialShownAt) / 1000; let secondsSinceLastCommercial = (Date.now() - lastCommercialShownAt) / 1000;
console.log(`It has been ${secondsSinceLastCommercial} seconds since the last commercial`);
if (secondsSinceLastCommercial >= config.commercialInterval) { if (secondsSinceLastCommercial >= config.commercialInterval) {
console.log(`Showing commercial now...`); commercialPlaying = true;
// @TODO: Add a random chance here for it to be "everybody wow"
console.log(`It has been ${secondsSinceLastCommercial} seconds since the last commercial break!`);
// Random chance for it to be "everybody wow"
if ((Math.floor(Math.random() * 100) + 1) <= config.auwChance) {
console.log(`Showing AUW!`);
auw(() => {
// show next video in queue
lastCommercialShownAt = Date.now();
commercialPlaying = false;
nextVideo();
});
} else {
let commercial = config.memes.sort(randSort)[0]; let commercial = config.memes.sort(randSort)[0];
console.log(`Showing random meme: ${commercial.name}`);
obs.setCurrentScene({"scene-name": config.commercialSceneName}) obs.setCurrentScene({"scene-name": config.commercialSceneName})
.then(res => { .then(res => {
return playVideoInScene(commercial, config.commercialSceneName, () => { return playVideoInScene(commercial, config.commercialSceneName, () => {
// hide video // hide video
obs.setSceneItemProperties({"item": commercial.sceneItem, "scene-name": config.commercialSceneName, "visible": false}) obs.setSceneItemProperties({"item": commercial.sceneItem, "scene-name": config.commercialSceneName, "visible": false})
// unmute songrequest audio // unmute songrequest audio
twitch.editorChat.say(twitchChannel, '!volume 50'); twitch.editorChat.say(twitchChannel, `!volume ${config.defaultSRVolume}`);
// show next video in queue // show next video in queue
lastCommercialShownAt = Date.now(); lastCommercialShownAt = Date.now();
commercialPlaying = false;
nextVideo(); nextVideo();
}) })
}) })
@@ -175,22 +189,43 @@ const streamInit = (config, obs, twitch) => {
twitch.editorChat.say(twitchChannel, '!volume 0'); twitch.editorChat.say(twitchChannel, '!volume 0');
}) })
.catch(console.error); .catch(console.error);
}
return; return;
} }
// Keep track of recently played videos // Keep track of recently played videos
if (recentlyPlayed.length === 5) { if (recentlyPlayed.length === config.recentlyPlayedMemory) {
recentlyPlayed.shift(); recentlyPlayed.shift();
} }
recentlyPlayed.push(currentVideo.id); recentlyPlayed.push(currentVideo.id);
// @TODO: Add a random chance here for room grind to be played for an amount of time // if a commercial/meme is playing (manually triggered), wait until it's done and calls this function again
if (commercialPlaying === true) {
return;
}
// play the next video in the queue, or pick one at random if the queue is empty // play the next video in the queue, or pick one at random if the queue is empty
if (videoQueue.length > 0) { if (videoQueue.length > 0) {
currentVideo = videoQueue.shift(); currentVideo = videoQueue.shift();
} else { } 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.setSceneItemProperties({"item": "room-grind", "scene-name": config.defaultSceneName, "visible": true})
.then(res => {
obs.setTextGDIPlusProperties({"source": config.currentActivitySceneItemName, "scene-name": config.defaultSceneName, "render": true, "text": "NOW SHOWING: TTAS Room Grind !ttas"});
setTimeout(() => {
// after timeout, hide room-grind and call nextVideo()
obs.setSceneItemProperties({"item": "room-grind", "scene-name": config.defaultSceneName, "visible": false});
nextVideo();
}, config.roomGrindPlaytime*1000)
});
return;
}
// filter recently played from shuffle // filter recently played from shuffle
let freshVods = config.vods.filter(e => { let freshVods = config.vods.filter(e => {
return !recentlyPlayed.includes(e.id); return !recentlyPlayed.includes(e.id);
@@ -198,7 +233,6 @@ const streamInit = (config, obs, twitch) => {
currentVideo = freshVods.sort(randSort).slice(0, 1).shift(); currentVideo = freshVods.sort(randSort).slice(0, 1).shift();
} }
// @TODO: if a commercial/meme is playing (manually triggered), wait until it's done
showVideo(currentVideo); showVideo(currentVideo);
}; };
@@ -208,9 +242,44 @@ const streamInit = (config, obs, twitch) => {
currentVideo = videoQueue.shift(); currentVideo = videoQueue.shift();
showVideo(currentVideo); showVideo(currentVideo);
const auw = (callback) => {
let currentScene;
obs.getCurrentScene()
.then(res => {
currentScene = res.name;
// switch to commercial scene
return obs.setCurrentScene({"scene-name": config.commercialSceneName});
})
.then(res => {
// show the video
return obs.setSceneItemProperties({"item": "everybody-wow", "scene-name": config.commercialSceneName, "visible": true});
})
.then(res => {
// mute songrequest audio
twitch.editorChat.say(twitchChannel, '!volume 0');
// show owen
obs.setSceneItemProperties({"item": "owen", "scene-name": config.commercialSceneName, "visible": true});
// tell chat what's up
twitch.botChat.say(twitchChannel, 'Everybody OwenWow');
// swap back to the original scene after the video ends
setTimeout(() => {
// hide video
obs.setSceneItemProperties({"item": "everybody-wow", "scene-name": config.commercialSceneName, "visible": false})
// hide owen
obs.setSceneItemProperties({"item": "owen", "scene-name": config.commercialSceneName, "visible": false});
// unmute songrequest audio
twitch.editorChat.say(twitchChannel, `!volume ${config.defaultSRVolume}`);
// swap back to fgfm
obs.setCurrentScene({"scene-name": currentScene});
// trigger user callback
if (callback) callback();
}, 246500);
})
.catch(console.error);
};
// Twitch Chat Commands // Twitch Chat 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
if (config.twitch.blacklistedUsers.includes(from)) return; if (config.twitch.blacklistedUsers.includes(from)) return;
@@ -307,33 +376,13 @@ const streamInit = (config, obs, twitch) => {
// Black Box "Everybody Wow" // Black Box "Everybody Wow"
} else if (commandNoPrefix === 'auw') { } else if (commandNoPrefix === 'auw') {
obs.setCurrentScene({"scene-name": config.commercialSceneName}) commercialPlaying = true;
.then(res => { auw(() => {
// show the video commercialPlaying = false;
return obs.setSceneItemProperties({"item": "everybody-wow", "scene-name": config.commercialSceneName, "visible": true}); });
})
.then(res => {
// mute songrequest audio
twitch.editorChat.say(to, '!volume 0');
// show owen
obs.setSceneItemProperties({"item": "owen", "scene-name": config.commercialSceneName, "visible": true});
// tell chat what's up
twitch.botChat.say(to, 'Everybody OwenWow');
// swap back to fgfm scene after the video ends
setTimeout(() => {
// hide video
obs.setSceneItemProperties({"item": "everybody-wow", "scene-name": config.commercialSceneName, "visible": false})
// hide owen
obs.setSceneItemProperties({"item": "owen", "scene-name": config.commercialSceneName, "visible": false});
// unmute songrequest audio
twitch.editorChat.say(to, '!volume 50');
// swap back to fgfm
obs.setCurrentScene({"scene-name": config.videoSceneName});
}, 246500);
})
.catch(console.error);
// memes on-demand // memes on-demand
} else if (commandNoPrefix === 'meme') { } else if (commandNoPrefix === 'meme') {
commercialPlaying = true;
let commercial = config.memes.sort(randSort)[0]; let commercial = config.memes.sort(randSort)[0];
obs.setCurrentScene({"scene-name": config.commercialSceneName}) obs.setCurrentScene({"scene-name": config.commercialSceneName})
.then(res => { .then(res => {
@@ -341,9 +390,10 @@ const streamInit = (config, obs, twitch) => {
// video is done playing, hide it // video is done playing, hide it
obs.setSceneItemProperties({"item": commercial.sceneItem, "scene-name": config.commercialSceneName, "visible": false}) obs.setSceneItemProperties({"item": commercial.sceneItem, "scene-name": config.commercialSceneName, "visible": false})
// unmute songrequest audio // unmute songrequest audio
twitch.editorChat.say(to, '!volume 50'); twitch.editorChat.say(to, `!volume ${config.defaultSRVolume}`);
// swap back to fgfm // swap back to fgfm
obs.setCurrentScene({"scene-name": config.videoSceneName}); obs.setCurrentScene({"scene-name": config.defaultSceneName});
commercialPlaying = false;
}); });
}) })
.then(res => { .then(res => {
@@ -383,7 +433,7 @@ const streamInit = (config, obs, twitch) => {
return; return;
} }
obs.setTextGDIPlusProperties({"source": config.currentActivitySceneItemName, "scene-name": config.videoSceneName, "render": true, "text": target}) obs.setTextGDIPlusProperties({"source": config.currentActivitySceneItemName, "scene-name": config.defaultSceneName, "render": true, "text": target})
.then(res => { .then(res => {
twitch.botChat.say(to, `Activity updated!`); twitch.botChat.say(to, `Activity updated!`);
return; return;
@@ -401,7 +451,7 @@ const streamInit = (config, obs, twitch) => {
// SKIP // SKIP
} else if (commandNoPrefix === 'skip') { } else if (commandNoPrefix === 'skip') {
clearTimeout(videoTimer); clearTimeout(videoTimer);
obs.setSceneItemProperties({"item": currentVideo.sceneItem, "scene-name": config.videoSceneName, "visible": false}) obs.setSceneItemProperties({"item": currentVideo.sceneItem, "scene-name": config.defaultSceneName, "visible": false})
.then(res => { .then(res => {
nextVideo(); nextVideo();
}); });