remove old fgfm code, general cleanup
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
module.exports = {
|
||||
get: isOnCooldown,
|
||||
set: placeOnCooldown
|
||||
};
|
||||
|
||||
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
|
||||
function isOnCooldown(command, cooldownTime, callback)
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
let now = Date.now();
|
||||
let onCooldown = false;
|
||||
let key = keyPrefix + md5(command);
|
||||
|
||||
cache.get(key, function(err, timeUsed) {
|
||||
if (err) reject(err);
|
||||
|
||||
if (timeUsed !== null) {
|
||||
// Command was recently used, check timestamp to see if it's on cooldown
|
||||
if ((now - timeUsed) <= (cooldownTime*1000)) {
|
||||
// Calculate how much longer it's on cooldown
|
||||
onCooldown = ((cooldownTime*1000) - (now - timeUsed))/1000;
|
||||
}
|
||||
}
|
||||
|
||||
resolve(onCooldown);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Places a command on cooldown for cooldownTime (in seconds)
|
||||
function placeOnCooldown(command, cooldownTime)
|
||||
{
|
||||
let key = keyPrefix + md5(command);
|
||||
return cache.set(key, Date.now(), cooldownTime, handleCacheSet);
|
||||
}
|
||||
|
||||
function handleCacheSet(error, result) {}
|
||||
|
||||
process.on('exit', (code) => {cache.close()});
|
||||
324
lib/fgfm.js
324
lib/fgfm.js
@@ -1,324 +0,0 @@
|
||||
const util = require('./util'),
|
||||
emitter = require('events').EventEmitter,
|
||||
sysutil = require('util');
|
||||
|
||||
function FGFM(config) {
|
||||
// Set up initial state
|
||||
this.config = config.config;
|
||||
this.obs = config.obs;
|
||||
this.state = {
|
||||
showStatus: 'IDLE',
|
||||
videoQueue: [],
|
||||
recentlyPlayed: [],
|
||||
currentVideo: null,
|
||||
videoTimer: null,
|
||||
lastCommercialShownAt: Date.now(),
|
||||
commercialPlaying: false
|
||||
};
|
||||
|
||||
emitter.call(this);
|
||||
|
||||
this.startingSoon = (streamStartDelaySeconds, showStartDelaySeconds) => {
|
||||
// @TODO: Move these defaults to config
|
||||
if (typeof streamStartDelaySeconds === 'undefined') {
|
||||
streamStartDelaySeconds = 1;
|
||||
} else {
|
||||
streamStartDelaySeconds = parseInt(streamStartDelaySeconds);
|
||||
}
|
||||
|
||||
if (typeof showStartDelaySeconds === 'undefined') {
|
||||
showStartDelaySeconds = 300;
|
||||
} else {
|
||||
showStartDelaySeconds = parseInt(showStartDelaySeconds);
|
||||
showStartDelaySeconds += streamStartDelaySeconds;
|
||||
}
|
||||
|
||||
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 after delay
|
||||
console.log(`The stream will start in ${streamStartDelaySeconds} seconds!`);
|
||||
setTimeout(() => {this.obs.startStream().then(() => {this.emit('STREAM_STARTED')})}, streamStartDelaySeconds*1000);
|
||||
|
||||
// Start the "show" after stream+show delay
|
||||
// @TODO: Actually show the countdown in the scene
|
||||
console.log(`The show will start in ${showStartDelaySeconds} seconds!`);
|
||||
setTimeout(this.startTheShow, showStartDelaySeconds*1000);
|
||||
};
|
||||
|
||||
// Set up initial queue + start playback
|
||||
this.startTheShow = () => {
|
||||
// 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);
|
||||
|
||||
// restore volume
|
||||
this.obs.setVolume('headphones', 1.0);
|
||||
|
||||
this.state.showStatus = 'RUNNING';
|
||||
|
||||
this.emit('SHOW_STARTED');
|
||||
};
|
||||
|
||||
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!`);
|
||||
this.emit('SHOW_ENDING', creditsDelaySeconds);
|
||||
|
||||
let end = () => {
|
||||
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')
|
||||
.then(() => {
|
||||
this.emit('CREDITS_SHOWN', endDelaySeconds);
|
||||
|
||||
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';
|
||||
this.emit('SHOW_ENDED');
|
||||
}, endDelaySeconds*1000);
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
if (creditsDelaySeconds > 0) {
|
||||
setTimeout(end, creditsDelaySeconds*1000);
|
||||
} else {
|
||||
end();
|
||||
}
|
||||
};
|
||||
|
||||
// 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 = () => {
|
||||
// @TODO: Validate this.state.showStatus -- make sure the "show" hasn't been paused or stopped
|
||||
let ignoreStates = ['ENDING', 'ENDED', 'PAUSED'];
|
||||
if (ignoreStates.includes(this.state.showStatus)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 = () => {
|
||||
console.log('commercial is finished playing...');
|
||||
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 = [];
|
||||
};
|
||||
|
||||
this.pause = () => {
|
||||
this.state.showStatus = 'PAUSED';
|
||||
this.emit('SHOW_PAUSED');
|
||||
};
|
||||
|
||||
this.resume = () => {
|
||||
this.state.showStatus = 'RUNNING';
|
||||
this.nextVideo();
|
||||
this.emit('SHOW_RESUMED');
|
||||
};
|
||||
}
|
||||
|
||||
sysutil.inherits(FGFM, emitter);
|
||||
|
||||
module.exports = FGFM;
|
||||
178
lib/ghobs.js
178
lib/ghobs.js
@@ -1,178 +0,0 @@
|
||||
const OBSWebSocket = require('obs-websocket-js');
|
||||
|
||||
function GHOBS(config) {
|
||||
this.config = config;
|
||||
this.websocket = new OBSWebSocket();
|
||||
|
||||
this.init = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`Connecting to OBS Websocket...`);
|
||||
this.websocket.connect({ address: this.config.obs.websocket.address, password: this.config.obs.websocket.password })
|
||||
.then(() => {
|
||||
console.log(`Success! We're connected to OBS!`);
|
||||
this.websocket.getCurrentScene().then(currentScene => this.currentScene = currentScene.name);
|
||||
this.websocket.onSwitchScenes(newScene => this.currentScene = newScene.sceneName);
|
||||
resolve();
|
||||
})
|
||||
.catch(reject);
|
||||
|
||||
// Listen for errors from OBS
|
||||
// @TODO: Handle socket disconnect gracefully
|
||||
/** { status: 'error',
|
||||
description: 'There is no Socket connection available.',
|
||||
code: 'NOT_CONNECTED',
|
||||
error: 'There is no Socket connection available.' }*/
|
||||
this.websocket.on('error', err => {
|
||||
console.error(`OBS websocket error: ${JSON.stringify(err)}`);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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
|
||||
this.playVideo = (video, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// @TODO Validation of video
|
||||
|
||||
// set the file path on the source
|
||||
let sourceSettings = {
|
||||
local_file: video.filePath,
|
||||
looping: (typeof video.loops !== 'undefined' && video.loops > 1)
|
||||
};
|
||||
sourceSettings.loop = sourceSettings.looping;
|
||||
|
||||
this.websocket.setSourceSettings({"sourceName": video.sceneItem, "sourceSettings": sourceSettings})
|
||||
// show the video scene item
|
||||
.then(() => this.websocket.setSceneItemProperties({"item": video.sceneItem, "visible": true}))
|
||||
// when the video is over, hide it and trigger the user callback, but resolve promise immediately with the timer
|
||||
.then(() => {
|
||||
// if this video is being looped, adjust timeout length to allow the requested number of loops to complete
|
||||
if (sourceSettings.loop === true) {
|
||||
video.length *= video.loops;
|
||||
}
|
||||
|
||||
// resolve Promise with a timer of when the video will finish playback
|
||||
// trigger user callback when the video finishes
|
||||
let timer = setTimeout(() => {
|
||||
this.websocket.setSceneItemProperties({"item": video.sceneItem, "visible": false});
|
||||
if (typeof callback !== 'undefined') {
|
||||
callback();
|
||||
}
|
||||
}, parseInt(video.length*1000));
|
||||
|
||||
resolve(timer);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
// Shows a video in the given scene/item and then hides it and switches back to the original scene when finished
|
||||
this.playVideoInScene = (video, scene, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
video.scene = scene;
|
||||
let originalScene = this.currentScene || false;
|
||||
let handleVideoEnd = () => {
|
||||
if (originalScene !== false) {
|
||||
this.websocket.setCurrentScene({"scene-name": originalScene});
|
||||
}
|
||||
if (typeof callback !== 'undefined') {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
this.websocket.setCurrentScene({"scene-name": scene})
|
||||
.then(() => this.playVideo(video, handleVideoEnd))
|
||||
.then(timer => { resolve(timer) })
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
this.showActivity = (newActivity) => {
|
||||
let update = {
|
||||
"source": this.config.currentActivitySceneItemName,
|
||||
"scene-name": this.config.defaultSceneName,
|
||||
"render": true
|
||||
};
|
||||
|
||||
if (typeof newActivity !== 'undefined' && newActivity.length > 0) {
|
||||
update.text = newActivity;
|
||||
}
|
||||
|
||||
return this.websocket.setTextGDIPlusProperties(update);
|
||||
};
|
||||
|
||||
this.hideActivity = () => {
|
||||
return this.websocket.setSceneItemProperties({"item": this.config.currentActivitySceneItemName, "scene-name": this.config.defaultSceneName, "visible": false});
|
||||
};
|
||||
|
||||
this.showRoomGrind = (playTime, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.websocket.setSceneItemProperties({"item": "room-grind", "scene-name": this.config.defaultSceneName, "visible": true})
|
||||
.then(res => {
|
||||
this.showActivity("NOW SHOWING: TTAS Room Grind !ttas");
|
||||
resolve(setTimeout(() => {
|
||||
// after timeout, hide room-grind and call user callback
|
||||
this.websocket.setSceneItemProperties({"item": "room-grind", "scene-name": this.config.defaultSceneName, "visible": false});
|
||||
if (typeof callback !== 'undefined') callback();
|
||||
}, playTime*1000));
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
this.setVisible = (item, scene, visible) => {
|
||||
return this.websocket.setSceneItemProperties({"item": item, "scene-name": scene, "visible": visible});
|
||||
};
|
||||
|
||||
this.toggleVisible = (item) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.websocket.getSceneItemProperties({"item": item})
|
||||
.then(data => {
|
||||
let newVisibility = !data.visible;
|
||||
this.websocket.setSceneItemProperties({"item": item, "visible": newVisibility}).then(resolve);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
this.show = (item, scene) => {
|
||||
return this.setVisible(item, scene, true);
|
||||
};
|
||||
|
||||
this.hide = (item, scene) => {
|
||||
return this.setVisible(item, scene, false);
|
||||
};
|
||||
|
||||
this.switchToScene = (scene) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.currentScene === scene) {
|
||||
resolve(true);
|
||||
}
|
||||
|
||||
this.websocket.setCurrentScene({"scene-name": scene}).then(resolve).catch(reject);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = GHOBS;
|
||||
134
lib/spotify.js
134
lib/spotify.js
@@ -1,134 +0,0 @@
|
||||
var SpotifyWebApi = require('spotify-web-api-node');
|
||||
|
||||
function Spotify(config) {
|
||||
// Set up initial state
|
||||
this.config = config;
|
||||
|
||||
this.credentials = {
|
||||
clientId: this.config.clientId,
|
||||
clientSecret: this.config.clientSecret,
|
||||
redirectUri: this.config.redirectUri
|
||||
};
|
||||
|
||||
const spotifyApi = new SpotifyWebApi(this.credentials);
|
||||
|
||||
// The code that's returned as a query parameter to the redirect URI
|
||||
const code = this.config.userCode;
|
||||
|
||||
this.init = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Retrieve an access token and a refresh token
|
||||
spotifyApi.authorizationCodeGrant(code).then(
|
||||
function(data) {
|
||||
console.log('The token expires in ' + data.body['expires_in']);
|
||||
console.log('The access token is ' + data.body['access_token']);
|
||||
console.log('The refresh token is ' + data.body['refresh_token']);
|
||||
|
||||
// Set the access token on the API object to use it in later calls
|
||||
spotifyApi.setAccessToken(data.body['access_token']);
|
||||
spotifyApi.setRefreshToken(data.body['refresh_token']);
|
||||
|
||||
// clientId, clientSecret and refreshToken has been set on the api object previous to this call.
|
||||
setInterval(() => {
|
||||
spotifyApi.refreshAccessToken().then(
|
||||
function(data) {
|
||||
console.log('The access token has been refreshed!');
|
||||
|
||||
// Save the access token so that it's used in future calls
|
||||
spotifyApi.setAccessToken(data.body['access_token']);
|
||||
},
|
||||
function(err) {
|
||||
console.log('Could not refresh access token', err);
|
||||
}
|
||||
);
|
||||
}, data.body['expires_in']*1000);
|
||||
|
||||
resolve();
|
||||
},
|
||||
function(err) {
|
||||
console.log('Something went wrong!', JSON.stringify(err));
|
||||
reject(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
this.getMe = () => {
|
||||
spotifyApi.getMe()
|
||||
.then(function(data) {
|
||||
console.log('Some information about the authenticated user', data.body);
|
||||
}, function(err) {
|
||||
console.log('Something went wrong!', err);
|
||||
});
|
||||
};
|
||||
|
||||
this.getCurrentSong = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
spotifyApi.getMyCurrentPlaybackState({}, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let state = data.body;
|
||||
resolve({
|
||||
artists: state.item.artists,
|
||||
name: state.item.name,
|
||||
album: state.item.album.name,
|
||||
url: state.item.external_urls.spotify
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.getCurrentPlaylist = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
spotifyApi.getMyCurrentPlaybackState({}, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let state = data.body;
|
||||
if (state.context) {
|
||||
resolve(state.context.external_urls.spotify);
|
||||
} else {
|
||||
resolve(state.item.album.external_urls.spotify);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.playContext = (uri) => {
|
||||
return spotifyApi.play({"context_uri": uri});
|
||||
};
|
||||
|
||||
this.skip = () => {
|
||||
return spotifyApi.skipToNext();
|
||||
};
|
||||
|
||||
this.pause = () => {
|
||||
return spotifyApi.pause();
|
||||
};
|
||||
|
||||
this.resume = () => {
|
||||
return spotifyApi.play();
|
||||
};
|
||||
|
||||
this.setVolume = (volume) => {
|
||||
volume = parseInt(volume);
|
||||
if (volume < 0) volume = 0;
|
||||
if (volume > 100) volume = 100;
|
||||
return spotifyApi.setVolume(volume);
|
||||
};
|
||||
|
||||
this.shuffle = (state) => {
|
||||
return spotifyApi.setShuffle({"state": state});
|
||||
};
|
||||
|
||||
this.repeat = (state) => {
|
||||
return spotifyApi.setRepeat({"state": state});
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = Spotify;
|
||||
@@ -1,37 +0,0 @@
|
||||
const util = require('util'),
|
||||
emitter = require('events').EventEmitter;
|
||||
|
||||
function Timers()
|
||||
{
|
||||
let self = this;
|
||||
|
||||
emitter.call(self);
|
||||
|
||||
self.once = (forTimestamp, eventName) => {
|
||||
// figure out ms between now and scheduled time
|
||||
// setTimeout for event to be fired at that time
|
||||
let diff = forTimestamp - Date.now();
|
||||
if (diff < 0) return;
|
||||
setTimeout(() => {self.emit(eventName)}, diff);
|
||||
return self;
|
||||
};
|
||||
|
||||
self.repeat = (intervalSeconds, eventName) => {
|
||||
setInterval(() => {self.emit(eventName)}, intervalSeconds*1000);
|
||||
return self;
|
||||
};
|
||||
|
||||
self.onceAndRepeat = (forTimestamp, intervalSeconds, eventName) => {
|
||||
let diff = forTimestamp - Date.now();
|
||||
if (diff < 0) return self;
|
||||
setTimeout(() => {
|
||||
self.emit(eventName);
|
||||
self.repeat(intervalSeconds, eventName);
|
||||
}, diff);
|
||||
return self;
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(Timers, emitter);
|
||||
|
||||
module.exports = new Timers();
|
||||
60
lib/util.js
60
lib/util.js
@@ -1,60 +0,0 @@
|
||||
// Converts seconds to human-readable time
|
||||
String.prototype.toHHMMSS = function () {
|
||||
let sec_num = parseInt(this, 10); // don't forget the second param
|
||||
let hours = Math.floor(sec_num / 3600);
|
||||
let minutes = Math.floor((sec_num - (hours * 3600)) / 60);
|
||||
let seconds = sec_num - (hours * 3600) - (minutes * 60);
|
||||
|
||||
if (hours < 10) {hours = "0"+hours;}
|
||||
if (minutes < 10) {minutes = "0"+minutes;}
|
||||
if (seconds < 10) {seconds = "0"+seconds;}
|
||||
return hours+':'+minutes+':'+seconds;
|
||||
};
|
||||
|
||||
var exports = module.exports = {};
|
||||
|
||||
exports.asyncForEach = async function(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array)
|
||||
}
|
||||
};
|
||||
|
||||
exports.range = function(start,stop) {
|
||||
var result=[];
|
||||
for (var idx=start.charCodeAt(0),end=stop.charCodeAt(0); idx <=end; ++idx){
|
||||
result.push(String.fromCharCode(idx));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.randElement = function(arr) {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
};
|
||||
|
||||
exports.sum = function(e) {
|
||||
let sum = 0;
|
||||
for (let i = 0; i < e.length; i++) {
|
||||
sum += parseInt(e[i], 10);
|
||||
}
|
||||
|
||||
return sum;
|
||||
};
|
||||
|
||||
exports.average = function(e) {
|
||||
let sum = exports.sum(e);
|
||||
|
||||
let avg = sum / e.length;
|
||||
|
||||
return avg;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
lib/utils.js
Executable file
50
lib/utils.js
Executable file
@@ -0,0 +1,50 @@
|
||||
// Converts seconds to human-readable time
|
||||
String.prototype.toHHMMSS = function () {
|
||||
let sec_num = parseInt(this, 10); // don't forget the second param
|
||||
let hours = Math.floor(sec_num / 3600);
|
||||
let minutes = Math.floor((sec_num - hours * 3600) / 60);
|
||||
let seconds = sec_num - hours * 3600 - minutes * 60;
|
||||
|
||||
if (hours < 10) {
|
||||
hours = "0" + hours;
|
||||
}
|
||||
if (minutes < 10) {
|
||||
minutes = "0" + minutes;
|
||||
}
|
||||
if (seconds < 10) {
|
||||
seconds = "0" + seconds;
|
||||
}
|
||||
return hours + ":" + minutes + ":" + seconds;
|
||||
};
|
||||
|
||||
async function asyncForEach(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
|
||||
function randElement(arr) {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
function randSort() {
|
||||
return 0.5 - Math.random();
|
||||
}
|
||||
|
||||
function chunkSubstr(str, size) {
|
||||
const numChunks = Math.ceil(str.length / size);
|
||||
const chunks = new Array(numChunks);
|
||||
|
||||
for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
|
||||
chunks[i] = str.substr(o, size);
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
asyncForEach,
|
||||
randElement,
|
||||
randSort,
|
||||
chunkSubstr
|
||||
};
|
||||
Reference in New Issue
Block a user