2025-09-28 17:55:05 +03:00
const dayjs = require ( 'dayjs' )
let cachedPrograms = {}
module . exports = {
site : 'tvtv.us' ,
days : 2 ,
url ({ date , channel }) {
return `https://www.tvtv.us/api/v1/lineup/USA-NY71652-X/grid/ ${ date . toJSON () } / ${ date
. add ( 1 , 'day' )
. toJSON () } / ${ channel . site_id } `
},
request : {
headers : {
Accept : '*/*' ,
Connection : 'keep-alive' ,
'User-Agent' :
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' ,
'sec-ch-ua' : '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"' ,
'sec-ch-ua-mobile' : '?0' ,
'sec-ch-ua-platform' : '"Windows"'
}
},
async parser ( ctx ) {
let programs = []
let queue = []
const items = parseItems ( ctx . content )
for ( const item of items ) {
const start = dayjs ( item . startTime )
const stop = start . add ( item . duration , 'minute' )
programs . push ({
id : item . programId ,
title : item . title ,
subtitle : item . subtitle || null ,
start ,
stop
})
// NOTE: This part of the code is commented out because loading additional data leads either to error 429 Too Many Requests or to even greater delays between requests.
// if (item.programId && !cachedPrograms[item.programId]) {
// queue.push({
// programId: item.programId,
// url: `https://tvtv.us/api/v1/programs/${item.programId}`,
// httpAgent: ctx.request.agent,
// httpsAgent: ctx.request.agent,
// headers: module.exports.request.headers
// })
// }
}
const axios = require ( 'axios' )
for ( const req of queue ) {
await wait ( 5000 )
const data = await axios ( req )
. then ( r => r . data )
. catch ( console . error )
if ( ! data || ! data . title ) continue
cachedPrograms [ req . programId ] = data
}
programs . forEach ( program => {
const data = cachedPrograms [ program . id ]
if ( ! data ) return
program . description = data . description || null
program . image = data . image ? `https://tvtv.us ${ data . image } ` : null
program . date = data . releaseYear ? data . releaseYear . toString () : null
program . directors = data . directors
program . categories = data . genres
program . actors = parseActors ( data )
program . writers = parseWriters ( data )
program . producers = parseProducers ( data )
program . ratings = parseRatings ( data )
program . season = parseSeason ( data )
program . episode = parseEpisode ( data )
})
return programs
}
}
function parseEpisode ( data ) {
if ( ! data ? . seriesEpisode ? . seasonEpisode ) return null
const [, episode ] = data . seriesEpisode . seasonEpisode . match ( /Episode (\d+)/ ) || [ null , null ]
return episode ? parseInt ( episode ) : null
}
function parseSeason ( data ) {
if ( ! data ? . seriesEpisode ? . seasonEpisode ) return null
const [, season ] = data . seriesEpisode . seasonEpisode . match ( /Season (\d+);/ ) || [ null , null ]
return season ? parseInt ( season ) : null
}
function parseRatings ( data ) {
return Array . isArray ( data . ratings )
? data . ratings . map ( rating => ({
value : rating . code ,
system : rating . body
}))
: []
}
function parseWriters ( data ) {
return data . crew . filter ( member => member . role . includes ( 'Writer' )). map ( member => member . name )
}
function parseProducers ( data ) {
return data . crew . filter ( member => member . role . includes ( 'Producer' )). map ( member => member . name )
}
function parseActors ( data ) {
return data . cast . map ( actor => {
const guest = actor . role . includes ( 'Guest Star' ) ? 'yes' : undefined
const role = actor . role . replace ( ' - Guest Star' , '' )
return {
value : actor . name ,
role ,
guest
}
})
}
function parseItems ( content ) {
try {
const json = JSON . parse ( content )
if ( ! json . length ) return []
return json [ 0 ]
} catch {
return []
}
}
function wait ( ms ) {
if ( process . env . NODE_ENV === 'test' ) return
return new Promise ( resolve => {
setTimeout ( resolve , ms )
})
}