room vids

This commit is contained in:
Chris Ham
2018-09-25 20:24:32 -07:00
parent d21255d1ff
commit 2dad2a3bc5
7 changed files with 144 additions and 103 deletions

1
conf/rooms.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -48,5 +48,8 @@
"auwChance": 25,
"defaultSRVolume": 75,
"vodConfigFile": "./conf/vods.json",
"roomConfigFile": "./conf/rooms.json",
"roomVidsBasePath": "Y:\\media\\videos\\ALttP\\my-vids\\room-vids",
"roomVidPlaytime": 60,
"debug": false
}

View File

@@ -1,15 +1,25 @@
TODO:
☐ Room vid requests / import
☐ Importing
- Read the Y:\media\videos\ALttP\my-vids\room-vids directory
- Go through each folder and get all the .mp4 files
- Store the following as metadata:
- root folder name
- original file name
- keywords
- dungeon (parse from root folder name)
- room ID
- video length
☐ Support viewer skip voting
☐ 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)
☐ support for $pause
☐ Command to stop video rotation / timers (shutdown)
☐ Restrict # of requests a user can have in the queue at once
☐ Start/stop stream automation
☐ Stream alerts for chat
☐ remove currently playing video from vote choices
☐ restrict # of requests a user can have in the queue at once
☐ 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)
Ideas:

36
fgfm.js
View File

@@ -11,6 +11,7 @@ const GHOBS = require('./lib/ghobs');
// Read internal configuration
let config = require('./config.json');
config.vods = require(config.vodConfigFile);
config.rooms = require(config.roomConfigFile);
let snesGames = require('./conf/snesgames.json');
// Set up initial state
@@ -104,6 +105,23 @@ const streamInit = (config, twitch) => {
.catch(console.error);
};
const addRoomVideo = room => {
let loops = Math.floor(config.roomVidPlaytime / room.videoData.length);
console.log(`Adding ${loops} instances of room video for ${room.dungeonName} - ${room.roomName} to the queue`);
let video = {
"filePath": `${config.roomVidsBasePath}${room.winPath}`,
"sceneItem": (room.videoData.width === 960) ? "4x3ph" : "16x9ph",
"length": room.videoData.length,
"label": room.roomName,
"chatName": room.roomName
};
for (var i = 0; i < loops; i++) {
state.videoQueue.push(video);
}
};
// Picks the next video in the queue (shuffles if empty)
// Also handles "commercial breaks" if enabled
const nextVideo = () => {
@@ -247,6 +265,24 @@ const streamInit = (config, twitch) => {
obs.toggleVisible(sceneItem).catch(console.error);
// ROOM VIDS
} else if (commandNoPrefix === 'room') {
let roomId = commandParts[1] || false;
if (roomId.length !== 4) {
twitch.botChat.say(to, `Please provide a 4-digit room ID!`);
return;
}
let roomIndex = config.rooms.findIndex(e => e.dungeonId === roomId.substring(0,2) && e.roomId === roomId.substring(2,4));
if (roomIndex === -1) {
twitch.botChat.say(to, `No room found matching that ID!`);
return;
}
let room = config.rooms[roomIndex];
addRoomVideo(room);
twitch.botChat.say(to, `Added ${room.dungeonName} - ${room.roomName} to the queue!`);
// EVERYBODY WOW
} else if (commandNoPrefix === 'auw') {
state.commercialPlaying = true;

121
package-lock.json generated
View File

@@ -198,11 +198,6 @@
"safer-buffer": "2.1.2"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
@@ -223,6 +218,15 @@
"resolved": "https://registry.npmjs.org/ffmpeg-binaries/-/ffmpeg-binaries-3.2.2.tgz",
"integrity": "sha1-Nw8wIO9rTbpipVGkJcY01sdaOiE="
},
"fluent-ffmpeg": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz",
"integrity": "sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ=",
"requires": {
"async": "2.6.1",
"which": "1.3.1"
}
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -289,28 +293,6 @@
"resolved": "https://registry.npmjs.org/irc-colors/-/irc-colors-1.4.3.tgz",
"integrity": "sha512-VeAnFC9fkb4nB/s6UtTNf3BH2wOk/sSBSzIzCpFwrgoFxVl6J5sov7FXXA0kmbk/mVAZQXfXAsQFjWnGPf4cRg=="
},
"ircv3": {
"version": "0.8.12",
"resolved": "https://registry.npmjs.org/ircv3/-/ircv3-0.8.12.tgz",
"integrity": "sha512-q4WrRzbUPfN8FNunnv7zdQXAdguRr1uJg6F2P6fxBcrhu92u+QcfRXacDRTX03BGaxd+D1nQe9jkmP5T9c/4sw==",
"requires": {
"escape-string-regexp": "1.0.5",
"universal-websocket-client": "1.0.2",
"ws": "3.3.3"
},
"dependencies": {
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
"requires": {
"async-limiter": "1.0.0",
"safe-buffer": "5.1.2",
"ultron": "1.1.1"
}
}
}
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@@ -329,6 +311,11 @@
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -613,24 +600,6 @@
}
}
},
"request-promise-core": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
"integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
"requires": {
"lodash": "4.17.11"
}
},
"request-promise-native": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz",
"integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=",
"requires": {
"request-promise-core": "1.1.1",
"stealthy-require": "1.1.1",
"tough-cookie": "2.4.3"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -676,11 +645,6 @@
"tweetnacl": "0.14.5"
}
},
"stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
},
"tough-cookie": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
@@ -690,11 +654,6 @@
"punycode": "1.4.1"
}
},
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -709,50 +668,6 @@
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"optional": true
},
"twitch": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/twitch/-/twitch-1.0.0.tgz",
"integrity": "sha512-LRg7SwtwKPEtdXcKgQHh55YdT+oI2nqHKCBs+4coxK2Mtc5btwrQjaSbulEXwZaypEW1yyWT6x6g+Bo6IX3FBw==",
"requires": {
"request": "2.88.0",
"request-promise-native": "1.0.5",
"tslib": "1.9.3"
}
},
"twitch-chat-client": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/twitch-chat-client/-/twitch-chat-client-1.1.0.tgz",
"integrity": "sha512-5BfGHhDtxvlvLJ2samqYzb9EPRTpTvZ5J5eXfZOWmqdGix8TgDELgsptIcrVLI7llACI0kpN6wgyLG/4OlXUhQ==",
"requires": {
"ircv3": "0.8.12",
"tslib": "1.9.3"
}
},
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
},
"universal-websocket-client": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/universal-websocket-client/-/universal-websocket-client-1.0.2.tgz",
"integrity": "sha512-Pi6BdJtEAISb77GTbOLBLIWdYGezXgnJejrVBYKXxzNTsLcjJS+mWIJ2BRZElSlOG/wc7+yfOe5y30bzTu3Qqg==",
"requires": {
"ws": "3.3.3"
},
"dependencies": {
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
"requires": {
"async-limiter": "1.0.0",
"safe-buffer": "5.1.2",
"ultron": "1.1.1"
}
}
}
},
"uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
@@ -768,6 +683,14 @@
"extsprintf": "1.3.0"
}
},
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"requires": {
"isexe": "2.0.0"
}
},
"ws": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",

View File

@@ -7,6 +7,7 @@
"async": "^2.6.1",
"discord.js": "^11.4.2",
"ffmpeg-binaries": "^3.2.2",
"fluent-ffmpeg": "^2.1.2",
"irc": "^0.5.2",
"md5": "^2.2.1",
"memcache": "^0.3.0",

67
room-vid-import.js Executable file
View File

@@ -0,0 +1,67 @@
const fs = require('fs');
const path = require('path');
const util = require('./lib/util');
const ffmpeg = require('fluent-ffmpeg');
let roomVidPath = `/var/hypnoadmin/media/videos/ALttP/my-vids/room-vids`;
const getAllFiles = dir =>
fs.readdirSync(dir).reduce((files, file) => {
const name = path.join(dir, file);
const isDirectory = fs.statSync(name).isDirectory();
return isDirectory ? [...files, ...getAllFiles(name)] : [...files, name];
}, []);
let roomVidFiles = getAllFiles(roomVidPath);
populateDatabase();
async function populateDatabase() {
let database = [];
await util.asyncForEach(roomVidFiles, async (file) => {
// @TODO: ignore anything that's not an mp4
let shortPath = file.replace(roomVidPath, '');
if (!/\.mp4$/.test(shortPath)) {
return;
}
let entry = {
shortPath: shortPath,
winPath: shortPath.replace(/\//g, '\\')
};
// chop up the short path and extract metadata
let matches = shortPath.match(/^\/([0-9]{2})-([a-z]+)\/([0-9]{2})-(.+)\.mp4/);
if (matches) {
entry.dungeonId = matches[1];
entry.dungeonName = matches[2];
entry.roomId = matches[3];
entry.roomName = matches[4];
}
entry.videoData = await getVideoMetadata(file);
database.push(entry);
console.log('added entry', entry);
});
fs.writeFile('conf/rooms.json', JSON.stringify(database), 'utf8', () => {console.log('done')});
}
function getVideoMetadata(videoPath) {
return new Promise((resolve, reject) => {
ffmpeg.ffprobe(videoPath, (err, metadata) => {
// find the video stream
let stream = metadata.streams.find(e => e.codec_type === "video");
if (!stream) {
resolve(false);
}
resolve({
width: stream.width,
height: stream.height,
fps: parseInt(stream.r_frame_rate.replace('/1', '')),
length: stream.duration
});
});
});
}