|
|
|
|
@ -16,6 +16,7 @@ async function getCommentsForVOD(vodId) {
|
|
|
|
|
logger.error('VOD ID must be purely alphanumeric. Twitch chat download failed!');
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
const safeVodId = path.basename(vodId);
|
|
|
|
|
|
|
|
|
|
const is_windows = process.platform === 'win32';
|
|
|
|
|
const cliExt = is_windows ? '.exe' : ''
|
|
|
|
|
@ -26,17 +27,24 @@ async function getCommentsForVOD(vodId) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await exec(`${cliPath} chatdownload -u ${vodId} -o appdata/${vodId}.json`, {stdio:[0,1,2]});
|
|
|
|
|
const result = await exec(`${cliPath} chatdownload -u ${safeVodId} -o appdata/${safeVodId}.json`, {stdio:[0,1,2]});
|
|
|
|
|
|
|
|
|
|
if (result['stderr']) {
|
|
|
|
|
logger.error(`Failed to download twitch comments for ${vodId}`);
|
|
|
|
|
logger.error(`Failed to download twitch comments for ${safeVodId}`);
|
|
|
|
|
logger.error(result['stderr']);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const temp_chat_path = path.join('appdata', `${vodId}.json`);
|
|
|
|
|
const temp_chat_path = path.join('appdata', `${safeVodId}.json`);
|
|
|
|
|
const appdataBasePath = path.resolve('appdata');
|
|
|
|
|
const resolvedTempChatPath = path.resolve(temp_chat_path);
|
|
|
|
|
const relativeTempChatPath = path.relative(appdataBasePath, resolvedTempChatPath);
|
|
|
|
|
if (relativeTempChatPath.startsWith('..') || path.isAbsolute(relativeTempChatPath)) {
|
|
|
|
|
logger.error(`Refusing to access temporary twitch chat file outside appdata for ${safeVodId}`);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const raw_json = fs.readJSONSync(temp_chat_path);
|
|
|
|
|
const raw_json = fs.readJSONSync(resolvedTempChatPath);
|
|
|
|
|
const new_json = raw_json.comments.map(comment_obj => {
|
|
|
|
|
return {
|
|
|
|
|
timestamp: comment_obj.content_offset_seconds,
|
|
|
|
|
@ -47,7 +55,7 @@ async function getCommentsForVOD(vodId) {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
fs.unlinkSync(temp_chat_path);
|
|
|
|
|
fs.unlinkSync(resolvedTempChatPath);
|
|
|
|
|
|
|
|
|
|
return new_json;
|
|
|
|
|
}
|
|
|
|
|
@ -56,25 +64,43 @@ async function getTwitchChatByFileID(id, type, user_uid, uuid, sub) {
|
|
|
|
|
const usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
|
|
|
|
|
const subscriptionsFileFolder = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
|
|
|
|
let file_path = null;
|
|
|
|
|
let base_path = null;
|
|
|
|
|
const safeType = type === 'audio' || type === 'video' ? type : null;
|
|
|
|
|
|
|
|
|
|
if (user_uid) {
|
|
|
|
|
if (sub) {
|
|
|
|
|
base_path = path.join(usersFileFolder, user_uid, 'subscriptions', sub.isPlaylist ? 'playlists' : 'channels');
|
|
|
|
|
file_path = path.join(usersFileFolder, user_uid, 'subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, `${id}.twitch_chat.json`);
|
|
|
|
|
} else {
|
|
|
|
|
file_path = path.join(usersFileFolder, user_uid, type, `${id}.twitch_chat.json`);
|
|
|
|
|
if (!safeType) return null;
|
|
|
|
|
base_path = path.join(usersFileFolder, user_uid, safeType);
|
|
|
|
|
file_path = path.join(usersFileFolder, user_uid, safeType, `${id}.twitch_chat.json`);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (sub) {
|
|
|
|
|
base_path = path.join(subscriptionsFileFolder, sub.isPlaylist ? 'playlists' : 'channels');
|
|
|
|
|
file_path = path.join(subscriptionsFileFolder, sub.isPlaylist ? 'playlists' : 'channels', sub.name, `${id}.twitch_chat.json`);
|
|
|
|
|
} else {
|
|
|
|
|
const typeFolder = config_api.getConfigItem(`ytdl_${type}_folder_path`);
|
|
|
|
|
if (!safeType) return null;
|
|
|
|
|
const typeFolder = config_api.getConfigItem(`ytdl_${safeType}_folder_path`);
|
|
|
|
|
base_path = typeFolder;
|
|
|
|
|
file_path = path.join(typeFolder, `${id}.twitch_chat.json`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var chat_file = null;
|
|
|
|
|
if (fs.existsSync(file_path)) {
|
|
|
|
|
chat_file = fs.readJSONSync(file_path);
|
|
|
|
|
if (file_path && base_path) {
|
|
|
|
|
const resolvedBasePath = path.resolve(base_path);
|
|
|
|
|
const resolvedFilePath = path.resolve(file_path);
|
|
|
|
|
const relativeFilePath = path.relative(resolvedBasePath, resolvedFilePath);
|
|
|
|
|
if (relativeFilePath.startsWith('..') || path.isAbsolute(relativeFilePath)) {
|
|
|
|
|
logger.error(`Refusing to read twitch chat outside expected directory for file id '${id}'.`);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fs.existsSync(resolvedFilePath)) {
|
|
|
|
|
chat_file = fs.readJSONSync(resolvedFilePath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return chat_file;
|
|
|
|
|
@ -87,23 +113,42 @@ async function downloadTwitchChatByVODID(vodId, id, type, user_uid, sub, customF
|
|
|
|
|
|
|
|
|
|
// save file if needed params are included
|
|
|
|
|
let file_path = null;
|
|
|
|
|
let base_path = null;
|
|
|
|
|
const safeType = type === 'audio' || type === 'video' ? type : null;
|
|
|
|
|
if (customFileFolderPath) {
|
|
|
|
|
base_path = customFileFolderPath;
|
|
|
|
|
file_path = path.join(customFileFolderPath, `${id}.twitch_chat.json`)
|
|
|
|
|
} else if (user_uid) {
|
|
|
|
|
if (sub) {
|
|
|
|
|
base_path = path.join(usersFileFolder, user_uid, 'subscriptions', sub.isPlaylist ? 'playlists' : 'channels');
|
|
|
|
|
file_path = path.join(usersFileFolder, user_uid, 'subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, `${id}.twitch_chat.json`);
|
|
|
|
|
} else {
|
|
|
|
|
file_path = path.join(usersFileFolder, user_uid, type, `${id}.twitch_chat.json`);
|
|
|
|
|
if (!safeType) return null;
|
|
|
|
|
base_path = path.join(usersFileFolder, user_uid, safeType);
|
|
|
|
|
file_path = path.join(usersFileFolder, user_uid, safeType, `${id}.twitch_chat.json`);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (sub) {
|
|
|
|
|
base_path = path.join(subscriptionsFileFolder, sub.isPlaylist ? 'playlists' : 'channels');
|
|
|
|
|
file_path = path.join(subscriptionsFileFolder, sub.isPlaylist ? 'playlists' : 'channels', sub.name, `${id}.twitch_chat.json`);
|
|
|
|
|
} else {
|
|
|
|
|
file_path = path.join(type, `${id}.twitch_chat.json`);
|
|
|
|
|
if (!safeType) return null;
|
|
|
|
|
const typeFolder = config_api.getConfigItem(`ytdl_${safeType}_folder_path`);
|
|
|
|
|
base_path = typeFolder;
|
|
|
|
|
file_path = path.join(typeFolder, `${id}.twitch_chat.json`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chat) fs.writeJSONSync(file_path, chat);
|
|
|
|
|
if (chat && file_path && base_path) {
|
|
|
|
|
const resolvedBasePath = path.resolve(base_path);
|
|
|
|
|
const resolvedFilePath = path.resolve(file_path);
|
|
|
|
|
const relativeFilePath = path.relative(resolvedBasePath, resolvedFilePath);
|
|
|
|
|
if (relativeFilePath.startsWith('..') || path.isAbsolute(relativeFilePath)) {
|
|
|
|
|
logger.error(`Refusing to write twitch chat outside expected directory for file id '${id}'.`);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
fs.writeJSONSync(resolvedFilePath, chat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return chat;
|
|
|
|
|
}
|
|
|
|
|
@ -120,4 +165,4 @@ module.exports = {
|
|
|
|
|
getCommentsForVOD: getCommentsForVOD,
|
|
|
|
|
getTwitchChatByFileID: getTwitchChatByFileID,
|
|
|
|
|
downloadTwitchChatByVODID: downloadTwitchChatByVODID
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|