auto fade out
This commit is contained in:
35
fgfm.TODO
35
fgfm.TODO
@@ -4,6 +4,22 @@
|
|||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
✔ Add cooldowns @done (18-10-02 10:16)
|
✔ Add cooldowns @done (18-10-02 10:16)
|
||||||
|
☐ Silence detection
|
||||||
|
- If stream has been silent for more than X minutes, try skipping song
|
||||||
|
☐ Start/stop stream automation
|
||||||
|
☐ Set up the queue upon init so it can be managed during startup
|
||||||
|
☐ Start
|
||||||
|
✔ Start stream @done (18-10-26 09:44)
|
||||||
|
✔ Starting Soon is shown until countdown is triggered @done (18-10-26 09:44)
|
||||||
|
- Add parameter for countdown
|
||||||
|
- Countdown for X minutes is triggered and shown
|
||||||
|
- Once countdown finishes, switch to intro scene and play
|
||||||
|
- Switch to fgfm once intro finishes
|
||||||
|
☐ Stop
|
||||||
|
- Parameter for how long until the stream should end
|
||||||
|
- Switch to credits with 1 minute remaining
|
||||||
|
- Fade out audio sources with 5 seconds left
|
||||||
|
- Stop Stream
|
||||||
☐ Fix commercial playing issue (switches back to scene early)
|
☐ Fix commercial playing issue (switches back to scene early)
|
||||||
☐ Change $auw and $meme to queue up the videos just like the others (don't switch scenes)
|
☐ Change $auw and $meme to queue up the videos just like the others (don't switch scenes)
|
||||||
☐ Don't auto-init GHOBS or FGFM, make them on-demand
|
☐ Don't auto-init GHOBS or FGFM, make them on-demand
|
||||||
@@ -11,7 +27,6 @@ TODO:
|
|||||||
☐ 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
|
||||||
☐ Move vrmode timer to this bot, delete from SLCB
|
☐ 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
|
☐ Room vid requests / import
|
||||||
☐ Improved interface for viewer requests
|
☐ Improved interface for viewer requests
|
||||||
☐ Web interface? Twitch extension?
|
☐ Web interface? Twitch extension?
|
||||||
@@ -21,23 +36,15 @@ TODO:
|
|||||||
☐ Remove currently playing video from vote choices
|
☐ Remove currently playing video from vote choices
|
||||||
☐ Command to add sets of videos to the queue at once (like the entire ttas or all gold segments)
|
☐ Command to add sets of videos to the queue at once (like the entire ttas or all gold segments)
|
||||||
☐ Command to stop video rotation / timers (shutdown)
|
☐ Command to stop video rotation / timers (shutdown)
|
||||||
☐ Start/stop stream automation
|
☐ Support for $pause (pauses queue after current video finishes)
|
||||||
☐ Start
|
|
||||||
- Start stream
|
|
||||||
- Starting Soon is shown until countdown is triggered
|
|
||||||
- Countdown for X minutes is triggered and shown
|
|
||||||
- Once countdown finishes, switch to intro scene and play
|
|
||||||
- Switch to fgfm once intro finishes
|
|
||||||
☐ Stop
|
|
||||||
- Parameter for how long until the stream should end
|
|
||||||
- Switch to credits with 1 minute remaining
|
|
||||||
- Fade out audio sources with 5 seconds left
|
|
||||||
- Stop Stream
|
|
||||||
☐ Support for $pause -- not sure how to pull this off
|
|
||||||
☐ Tool to output list of video ID's / descriptions
|
☐ Tool to output list of video ID's / descriptions
|
||||||
☐ Stream alerts for chat
|
☐ Stream alerts for chat
|
||||||
☐ Rotating background images (leftside)
|
☐ Rotating background images (leftside)
|
||||||
☐ Support gif's via command through gifph
|
☐ Support gif's via command through gifph
|
||||||
|
- !slowdance
|
||||||
|
- !bender
|
||||||
|
- !carlton
|
||||||
|
- !weeb
|
||||||
|
|
||||||
Ideas:
|
Ideas:
|
||||||
☐ Web interface for viewers to issue commands -- twitch extension?!
|
☐ Web interface for viewers to issue commands -- twitch extension?!
|
||||||
|
|||||||
19
fgfm.js
19
fgfm.js
@@ -73,12 +73,29 @@ 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});
|
||||||
director.init();
|
|
||||||
|
|
||||||
// Chat commands
|
// Chat commands
|
||||||
const commands = {
|
const commands = {
|
||||||
admin: {
|
admin: {
|
||||||
|
|
||||||
|
init: (cmd) => {
|
||||||
|
let delaySeconds = cmd.args[1] || 300;
|
||||||
|
director.startingSoon(delaySeconds);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
start: (cmd) => {
|
||||||
|
director.startTheShow();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
end: (cmd) => {
|
||||||
|
let creditsDelay = cmd.args[1] || 0;
|
||||||
|
let endDelay = cmd.args[2] || 60;
|
||||||
|
director.endTheShow(creditsDelay, endDelay);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
changevis: (cmd, newVisibility) => {
|
changevis: (cmd, newVisibility) => {
|
||||||
let sceneItem = command.args[1] || false;
|
let sceneItem = command.args[1] || false;
|
||||||
if (!sceneItem) {
|
if (!sceneItem) {
|
||||||
|
|||||||
90
lib/fgfm.js
90
lib/fgfm.js
@@ -5,6 +5,7 @@ function FGFM(config) {
|
|||||||
this.config = config.config;
|
this.config = config.config;
|
||||||
this.obs = config.obs;
|
this.obs = config.obs;
|
||||||
this.state = {
|
this.state = {
|
||||||
|
showStatus: 'IDLE',
|
||||||
videoQueue: [],
|
videoQueue: [],
|
||||||
recentlyPlayed: [],
|
recentlyPlayed: [],
|
||||||
currentVideo: null,
|
currentVideo: null,
|
||||||
@@ -13,14 +14,96 @@ function FGFM(config) {
|
|||||||
commercialPlaying: false
|
commercialPlaying: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.startingSoon = (autostartDelaySeconds) => {
|
||||||
|
this.state.showStatus = 'STARTING_SOON';
|
||||||
|
|
||||||
|
// Set up the initial queue by randomly choosing the configured amount of vods included in shuffling
|
||||||
|
this.state.videoQueue = this.config.vods.alttp.filter(e => e.includeInShuffle === true).sort(util.randSort).slice(0, this.config.initialQueueSize);
|
||||||
|
|
||||||
|
// show the starting-soon scene
|
||||||
|
this.obs.switchToScene('starting-soon');
|
||||||
|
|
||||||
|
// restore volume
|
||||||
|
this.obs.setVolume('headphones', 1.0);
|
||||||
|
|
||||||
|
// start the stream
|
||||||
|
this.obs.startStream();
|
||||||
|
|
||||||
|
if (typeof autostartDelaySeconds === 'undefined') {
|
||||||
|
autostartDelaySeconds = 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autostartDelaySeconds !== false) {
|
||||||
|
// @TODO: Actually show the countdown in the scene
|
||||||
|
console.log(`The show will start in ${autostartDelaySeconds} seconds!`);
|
||||||
|
setTimeout(this.startTheShow, autostartDelaySeconds*1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Set up initial queue + start playback
|
// Set up initial queue + start playback
|
||||||
this.init = () => {
|
this.startTheShow = () => {
|
||||||
// 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);
|
||||||
|
|
||||||
// Start queue playback
|
// Start queue playback
|
||||||
this.state.currentVideo = this.state.videoQueue.shift();
|
this.state.currentVideo = this.state.videoQueue.shift();
|
||||||
this.showVideo(this.state.currentVideo);
|
this.showVideo(this.state.currentVideo);
|
||||||
|
|
||||||
|
// restore volume
|
||||||
|
this.obs.setVolume('headphones', 1.0);
|
||||||
|
|
||||||
|
this.state.showStatus = 'RUNNING';
|
||||||
|
};
|
||||||
|
|
||||||
|
this.endTheShow = (creditsDelaySeconds, endDelaySeconds) => {
|
||||||
|
if (typeof creditsDelaySeconds === 'undefined' || creditsDelaySeconds === false) {
|
||||||
|
creditsDelaySeconds = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof endDelaySeconds === 'undefined' || endDelaySeconds === false) {
|
||||||
|
endDelaySeconds = 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Credits will be shown in ${creditsDelaySeconds} seconds!`);
|
||||||
|
|
||||||
|
let end = () => {
|
||||||
|
this.state.showStatus = 'ENDING';
|
||||||
|
|
||||||
|
this.obs.switchToScene('credits')
|
||||||
|
.then(() => {
|
||||||
|
if (endDelaySeconds < 5) endDelaySeconds = 5;
|
||||||
|
console.log(`Stream will stop in ${endDelaySeconds} seconds`);
|
||||||
|
let fadeOutDelay = endDelaySeconds - 5;
|
||||||
|
|
||||||
|
// Fade out volume with 5 seconds left
|
||||||
|
setTimeout(() => {
|
||||||
|
this.obs.getVolume('headphones')
|
||||||
|
.then(currentVolume => {
|
||||||
|
console.log(`current volume of headphones: ${currentVolume}`);
|
||||||
|
let step = 0.1;
|
||||||
|
while (currentVolume > 0.1) {
|
||||||
|
currentVolume -= step;
|
||||||
|
console.log(`setting volume to: ${currentVolume}`);
|
||||||
|
this.obs.setVolume('headphones', currentVolume);
|
||||||
|
util.sleep(250);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}, fadeOutDelay*1000);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.obs.stopStream();
|
||||||
|
this.state.showStatus = 'ENDED';
|
||||||
|
}, endDelaySeconds*1000);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (creditsDelaySeconds > 0) {
|
||||||
|
setTimeout(end, creditsDelaySeconds*1000);
|
||||||
|
} else {
|
||||||
|
end();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Shows.. a... video
|
// Shows.. a... video
|
||||||
@@ -71,6 +154,11 @@ function FGFM(config) {
|
|||||||
// Picks the next video in the queue (shuffles if empty)
|
// Picks the next video in the queue (shuffles if empty)
|
||||||
// Also handles "commercial breaks" if enabled
|
// Also handles "commercial breaks" if enabled
|
||||||
this.nextVideo = () => {
|
this.nextVideo = () => {
|
||||||
|
// @TODO: Validate this.state.showStatus -- make sure the "show" hasn't been paused or stopped
|
||||||
|
if (this.state.showStatus === 'ENDING' || this.state.showStatus === 'ENDED') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 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() - this.state.lastCommercialShownAt) / 1000;
|
let secondsSinceLastCommercial = (Date.now() - this.state.lastCommercialShownAt) / 1000;
|
||||||
if (this.config.commercialsEnabled === true && secondsSinceLastCommercial >= this.config.commercialInterval) {
|
if (this.config.commercialsEnabled === true && secondsSinceLastCommercial >= this.config.commercialInterval) {
|
||||||
|
|||||||
22
lib/ghobs.js
22
lib/ghobs.js
@@ -28,6 +28,28 @@ function GHOBS(config) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.startStream = () => {
|
||||||
|
return this.websocket.startStreaming();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.stopStream = () => {
|
||||||
|
return this.websocket.stopStreaming();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setVolume = (source, volume) => {
|
||||||
|
return this.websocket.setVolume({source: source, volume: volume});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getVolume = (source) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.websocket.getVolume({source: source})
|
||||||
|
.then(res => {
|
||||||
|
resolve(res.volume);
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Plays a video in the current scene and hides when finished
|
// Plays a video in the current scene and hides when finished
|
||||||
this.playVideo = (video, callback) => {
|
this.playVideo = (video, callback) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|||||||
@@ -49,3 +49,12 @@ exports.average = function(e) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.randSort = () => { return 0.5 - Math.random() };
|
exports.randSort = () => { return 0.5 - Math.random() };
|
||||||
|
|
||||||
|
exports.sleep = (milliseconds) => {
|
||||||
|
var start = new Date().getTime();
|
||||||
|
for (var i = 0; i < 1e7; i++) {
|
||||||
|
if ((new Date().getTime() - start) > milliseconds){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user