Twitch chat now supports subscriptions

- refactored code to be cleaner and more modularized

Updated scrolling on twitch chat to actually scroll to the bottom with new messages

Fast forwarding in videos with a twitch chat is now faster and provides a smoother transition
This commit is contained in:
Isaac Abadi
2020-12-10 21:04:53 -05:00
parent ff1bb8dee1
commit 9de403245b
8 changed files with 107 additions and 80 deletions

View File

@@ -1192,7 +1192,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
&& config.getConfigItem('ytdl_use_twitch_api') && config.getConfigItem('ytdl_twitch_auto_download_chat')) {
let vodId = url.split('twitch.tv/videos/')[1];
vodId = vodId.split('?')[0];
downloadTwitchChatByVODID(vodId, file_name, type, options.user);
twitch_api.downloadTwitchChatByVODID(vodId, file_name, type, options.user);
}
// renames file if necessary due to bug
@@ -1768,42 +1768,6 @@ function removeFileExtension(filename) {
return filename_parts.join('.');
}
async function getTwitchChatByFileID(id, type, user_uid, uuid) {
let file_path = null;
if (user_uid) {
file_path = path.join('users', user_uid, type, id + '.twitch_chat.json');
} else {
file_path = path.join(type, id + '.twitch_chat.json');
}
var chat_file = null;
if (fs.existsSync(file_path)) {
chat_file = fs.readJSONSync(file_path);
}
return chat_file;
}
async function downloadTwitchChatByVODID(vodId, id, type, user_uid) {
const twitch_api_key = config_api.getConfigItem('ytdl_twitch_api_key');
const chat = await twitch_api.getCommentsForVOD(twitch_api_key, vodId);
// save file if needec params are included
if (id && type) {
let file_path = null;
if (user_uid) {
file_path = path.join('users', user_uid, type, id + '.twitch_chat.json');
} else {
file_path = path.join(type, id + '.twitch_chat.json');
}
if (chat) fs.writeJSONSync(file_path, chat);
}
return chat;
}
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
res.header("Access-Control-Allow-Origin", getOrigin());
@@ -2038,7 +2002,7 @@ app.post('/api/getFile', optionalJwt, function (req, res) {
}
// check if chat exists for twitch videos
if (file['url'].includes('twitch.tv')) file['chat_exists'] = fs.existsSync(file['path'].substring(0, file['path'].length - 4) + '.twitch_chat.json');
if (file && file['url'].includes('twitch.tv')) file['chat_exists'] = fs.existsSync(file['path'].substring(0, file['path'].length - 4) + '.twitch_chat.json');
if (file) {
res.send({
@@ -2109,11 +2073,12 @@ app.post('/api/getFullTwitchChat', optionalJwt, async (req, res) => {
var id = req.body.id;
var type = req.body.type;
var uuid = req.body.uuid;
var sub = req.body.sub;
var user_uid = null;
if (req.isAuthenticated()) user_uid = req.user.uid;
const chat_file = await getTwitchChatByFileID(id, type, user_uid, uuid);
const chat_file = await twitch_api.getTwitchChatByFileID(id, type, user_uid, uuid, sub);
res.send({
chat: chat_file
@@ -2125,28 +2090,19 @@ app.post('/api/downloadTwitchChatByVODID', optionalJwt, async (req, res) => {
var type = req.body.type;
var vodId = req.body.vodId;
var uuid = req.body.uuid;
var sub = req.body.sub;
var user_uid = null;
if (req.isAuthenticated()) user_uid = req.user.uid;
// check if file already exists. if so, send that instead
const file_exists_check = await getTwitchChatByFileID(id, type, user_uid, uuid);
const file_exists_check = await twitch_api.getTwitchChatByFileID(id, type, user_uid, uuid, sub);
if (file_exists_check) {
res.send({chat: file_exists_check});
return;
}
const full_chat = await downloadTwitchChatByVODID(vodId);
let file_path = null;
if (user_uid) {
file_path = path.join('users', req.user.uid, type, id + '.twitch_chat.json');
} else {
file_path = path.join(type, id + '.twitch_chat.json');
}
if (full_chat) fs.writeJSONSync(file_path, full_chat);
const full_chat = await twitch_api.downloadTwitchChatByVODID(vodId, id, type, user_uid, sub);
res.send({
chat: full_chat
@@ -2435,9 +2391,15 @@ app.post('/api/getSubscription', optionalJwt, async (req, res) => {
var file_obj = new utils.File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
parsed_files.push(file_obj);
}
} else {
// loop through files for extra processing
for (let i = 0; i < parsed_files.length; i++) {
const file = parsed_files[i];
// check if chat exists for twitch videos
if (file && file['url'].includes('twitch.tv')) file['chat_exists'] = fs.existsSync(file['path'].substring(0, file['path'].length - 4) + '.twitch_chat.json');
}
}
res.send({
subscription: subscription,
files: parsed_files

View File

@@ -6,7 +6,8 @@ var path = require('path');
var youtubedl = require('youtube-dl');
const config_api = require('./config');
var utils = require('./utils')
const twitch_api = require('./twitch');
var utils = require('./utils');
const debugMode = process.env.YTDL_MODE === 'debug';
@@ -418,6 +419,15 @@ function handleOutputJSON(sub, sub_db, output_json, multiUserMode = null, reset_
sub_db.get('videos').push(output_json).write();
} else {
db_api.registerFileDB(path.basename(output_json['_filename']), sub.type, multiUserMode, sub);
const url = output_json['webpage_url'];
if (sub.type === 'video' && url.includes('twitch.tv/videos/') && url.split('twitch.tv/videos/').length > 1
&& config_api.getConfigItem('ytdl_use_twitch_api') && config_api.getConfigItem('ytdl_twitch_auto_download_chat')) {
const file_name = path.basename(output_json['_filename']);
const id = file_name.substring(0, file_name.length-4);
let vodId = url.split('twitch.tv/videos/')[1];
vodId = vodId.split('?')[0];
twitch_api.downloadTwitchChatByVODID(vodId, id, sub.type, multiUserMode.user, sub);
}
}
}

View File

@@ -1,5 +1,8 @@
var moment = require('moment');
var Axios = require('axios');
var fs = require('fs-extra')
var path = require('path');
const config_api = require('./config');
async function getCommentsForVOD(clientID, vodId) {
let url = `https://api.twitch.tv/v5/videos/${vodId}/comments?content_offset_seconds=0`,
@@ -68,6 +71,58 @@ async function getCommentsForVOD(clientID, vodId) {
return comments;
}
async function getTwitchChatByFileID(id, type, user_uid, uuid, sub) {
let file_path = null;
if (user_uid) {
if (sub) {
file_path = path.join('users', user_uid, 'subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, id + '.twitch_chat.json');
} else {
file_path = path.join('users', user_uid, type, id + '.twitch_chat.json');
}
} else {
if (sub) {
file_path = path.join('subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, id + '.twitch_chat.json');
} else {
file_path = path.join(type, id + '.twitch_chat.json');
}
}
var chat_file = null;
if (fs.existsSync(file_path)) {
chat_file = fs.readJSONSync(file_path);
}
return chat_file;
}
async function downloadTwitchChatByVODID(vodId, id, type, user_uid, sub) {
const twitch_api_key = config_api.getConfigItem('ytdl_twitch_api_key');
const chat = await getCommentsForVOD(twitch_api_key, vodId);
// save file if needed params are included
let file_path = null;
if (user_uid) {
if (sub) {
file_path = path.join('users', user_uid, 'subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, id + '.twitch_chat.json');
} else {
file_path = path.join('users', user_uid, type, id + '.twitch_chat.json');
}
} else {
if (sub) {
file_path = path.join('subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, id + '.twitch_chat.json');
} else {
file_path = path.join(type, id + '.twitch_chat.json');
}
}
if (chat) fs.writeJSONSync(file_path, chat);
return chat;
}
module.exports = {
getCommentsForVOD: getCommentsForVOD
getCommentsForVOD: getCommentsForVOD,
getTwitchChatByFileID: getTwitchChatByFileID,
downloadTwitchChatByVODID: downloadTwitchChatByVODID
}