@ -1,90 +1,53 @@
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 ` ,
batch ,
cursor ;
let comments = null ;
try {
do {
batch = ( await Axios . get ( url , {
headers : {
'Client-ID' : clientID ,
Accept : 'application/vnd.twitchtv.v5+json; charset=UTF-8' ,
'Content-Type' : 'application/json; charset=UTF-8' ,
}
} ) ) . data ;
const str = batch . comments . map ( c => {
let {
created _at : msgCreated ,
content _offset _seconds : timestamp ,
commenter : {
name ,
_id ,
created _at : acctCreated
} ,
message : {
body : msg ,
user _color : user _color
}
} = c ;
const timestamp _str = moment . duration ( timestamp , 'seconds' )
. toISOString ( )
. replace ( /P.*?T(?:(\d+?)H)?(?:(\d+?)M)?(?:(\d+).*?S)?/ ,
( _ , ... ms ) => {
const seg = v => v ? v . padStart ( 2 , '0' ) : '00' ;
return ` ${ seg ( ms [ 0 ] ) } : ${ seg ( ms [ 1 ] ) } : ${ seg ( ms [ 2 ] ) } ` ;
} ) ;
acctCreated = moment ( acctCreated ) . utc ( ) ;
msgCreated = moment ( msgCreated ) . utc ( ) ;
if ( ! comments ) comments = [ ] ;
comments . push ( {
timestamp : timestamp ,
timestamp _str : timestamp _str ,
name : name ,
message : msg ,
user _color : user _color
} ) ;
// let line = `${timestamp},${msgCreated.format(tsFormat)},${name},${_id},"${msg.replace(/"/g, '""')}",${acctCreated.format(tsFormat)}`;
// return line;
} ) . join ( '\n' ) ;
cursor = batch . _next ;
url = ` https://api.twitch.tv/v5/videos/ ${ vodId } /comments?cursor= ${ cursor } ` ;
await new Promise ( res => setTimeout ( res , 300 ) ) ;
} while ( cursor ) ;
} catch ( err ) {
console . error ( err ) ;
const logger = require ( './logger' ) ;
const moment = require ( 'moment' ) ;
const fs = require ( 'fs-extra' )
const path = require ( 'path' ) ;
async function getCommentsForVOD ( clientID , clientSecret , vodId ) {
const { promisify } = require ( 'util' ) ;
const child _process = require ( 'child_process' ) ;
const exec = promisify ( child _process . exec ) ;
const result = await exec ( ` tcd --video ${ vodId } --client-id ${ clientID } --client-secret ${ clientSecret } --format json -o appdata ` , { stdio : [ 0 , 1 , 2 ] } ) ;
if ( result [ 'stderr' ] ) {
logger . error ( ` Failed to download twitch comments for ${ vodId } ` ) ;
logger . error ( result [ 'stderr' ] ) ;
return null ;
}
return comments ;
const raw _json = fs . readJSONSync ( path . join ( 'appdata' , ` ${ vodId } .json ` ) ) ;
const new _json = raw _json . comments . map ( comment _obj => {
return {
timestamp : comment _obj . content _offset _seconds ,
timestamp _str : convertTimestamp ( comment _obj . content _offset _seconds ) ,
name : comment _obj . commenter . name ,
message : comment _obj . message . body ,
user _color : comment _obj . message . user _color
}
} ) ;
return new _json ;
}
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 ;
if ( user _uid ) {
if ( sub ) {
file _path = path . join ( 'users' , user _uid , 'subscriptions' , sub . isPlaylist ? 'playlists' : 'channels' , sub . name , id + '.twitch_chat.json' ) ;
file _path = path . join ( usersFileFolder , 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' ) ;
file _path = path . join ( usersFileFolder , 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' ) ;
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' ) ;
const typeFolder = config _api . getConfigItem ( ` ytdl_ ${ type } _folder_path ` ) ;
file _path = path . join ( typeFolder , ` ${ id } .twitch_chat.json ` ) ;
}
}
@ -96,23 +59,28 @@ async function getTwitchChatByFileID(id, type, user_uid, uuid, sub) {
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 ) ;
async function downloadTwitchChatByVODID ( vodId , id , type , user _uid , sub , customFileFolderPath = null ) {
const usersFileFolder = config _api . getConfigItem ( 'ytdl_users_base_path' ) ;
const subscriptionsFileFolder = config _api . getConfigItem ( 'ytdl_subscriptions_base_path' ) ;
const twitch _client _id = config _api . getConfigItem ( 'ytdl_twitch_client_id' ) ;
const twitch _client _secret = config _api . getConfigItem ( 'ytdl_twitch_client_secret' ) ;
const chat = await getCommentsForVOD ( twitch _client _id , twitch _client _secret , vodId ) ;
// save file if needed params are included
let file _path = null ;
if ( user _uid ) {
if ( customFileFolderPath ) {
file _path = path . join ( customFileFolderPath , ` ${ id } .twitch_chat.json ` )
} else if ( user _uid ) {
if ( sub ) {
file _path = path . join ( 'users' , user _uid , 'subscriptions' , sub . isPlaylist ? 'playlists' : 'channels' , sub . name , id + '.twitch_chat.json' ) ;
file _path = path . join ( usersFileFolder , 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' ) ;
file _path = path . join ( usersFileFolder , 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' ) ;
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' ) ;
file _path = path . join ( type , ` ${ id } .twitch_chat.json ` ) ;
}
}
@ -121,6 +89,14 @@ async function downloadTwitchChatByVODID(vodId, id, type, user_uid, sub) {
return chat ;
}
const convertTimestamp = ( timestamp ) => moment . duration ( timestamp , 'seconds' )
. toISOString ( )
. replace ( /P.*?T(?:(\d+?)H)?(?:(\d+?)M)?(?:(\d+).*?S)?/ ,
( _ , ... ms ) => {
const seg = v => v ? v . padStart ( 2 , '0' ) : '00' ;
return ` ${ seg ( ms [ 0 ] ) } : ${ seg ( ms [ 1 ] ) } : ${ seg ( ms [ 2 ] ) } ` ;
} ) ;
module . exports = {
getCommentsForVOD : getCommentsForVOD ,
getTwitchChatByFileID : getTwitchChatByFileID ,