automemes
This commit is contained in:
14
config.json
14
config.json
@@ -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
|
||||||
|
|||||||
12
fgfm.TODO
12
fgfm.TODO
@@ -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)
|
||||||
|
|||||||
168
fgfm.js
168
fgfm.js
@@ -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,52 +154,85 @@ 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"
|
|
||||||
let commercial = config.memes.sort(randSort)[0];
|
console.log(`It has been ${secondsSinceLastCommercial} seconds since the last commercial break!`);
|
||||||
obs.setCurrentScene({"scene-name": config.commercialSceneName})
|
// Random chance for it to be "everybody wow"
|
||||||
.then(res => {
|
if ((Math.floor(Math.random() * 100) + 1) <= config.auwChance) {
|
||||||
return playVideoInScene(commercial, config.commercialSceneName, () => {
|
console.log(`Showing AUW!`);
|
||||||
// hide video
|
auw(() => {
|
||||||
obs.setSceneItemProperties({"item": commercial.sceneItem, "scene-name": config.commercialSceneName, "visible": false})
|
// show next video in queue
|
||||||
// unmute songrequest audio
|
lastCommercialShownAt = Date.now();
|
||||||
twitch.editorChat.say(twitchChannel, '!volume 50');
|
commercialPlaying = false;
|
||||||
// show next video in queue
|
nextVideo();
|
||||||
lastCommercialShownAt = Date.now();
|
});
|
||||||
nextVideo();
|
} else {
|
||||||
|
let commercial = config.memes.sort(randSort)[0];
|
||||||
|
console.log(`Showing random meme: ${commercial.name}`);
|
||||||
|
|
||||||
|
obs.setCurrentScene({"scene-name": config.commercialSceneName})
|
||||||
|
.then(res => {
|
||||||
|
return playVideoInScene(commercial, config.commercialSceneName, () => {
|
||||||
|
// hide video
|
||||||
|
obs.setSceneItemProperties({"item": commercial.sceneItem, "scene-name": config.commercialSceneName, "visible": false})
|
||||||
|
// unmute songrequest audio
|
||||||
|
twitch.editorChat.say(twitchChannel, `!volume ${config.defaultSRVolume}`);
|
||||||
|
// show next video in queue
|
||||||
|
lastCommercialShownAt = Date.now();
|
||||||
|
commercialPlaying = false;
|
||||||
|
nextVideo();
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
.then(res => {
|
||||||
.then(res => {
|
// mute songrequest audio
|
||||||
// mute songrequest audio
|
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);
|
||||||
});
|
});
|
||||||
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();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user