Merge pull request #1696 from RevGear/directtv.com

Directv.com update
This commit is contained in:
Aleksandr Statciuk 2023-01-15 18:42:18 +03:00 committed by GitHub
commit 5b56b11714
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 4195 additions and 424 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,27 @@
{
"errors": [
{
"text": "Service failure: see errors or BulkOperationErrors for details",
"field": "",
"reason": "INTERNAL_SERVER_ERROR"
}
],
"statusCode": 500,
"apiResponse": {
"messages": "NOTE: see res.contingencies for size-filtered message values"
},
"reporting": {
"channelschedules": {
"success": false,
"reportingData": "reporting for app/json/channelschedules/channelschedules not implemented yet"
}
},
"messagekeys": null,
"contingencies": [
{
"key": "ent_ep_guide_backend_unavailable_error_message",
"value": "<!-- message: key=ent_ep_guide_backend_unavailable_error_message, deviceType=web -->Due to technical issues the guide is currently unavailable, please check back to soon.",
"level": "ERROR"
}
]
}

View file

@ -0,0 +1,47 @@
{
"programDetail": {
"title": "Men in Black II",
"episodeTitle": "",
"releaseYear": "2002",
"mainCategory": "Movies",
"rating": "TV14",
"runLength": "1 hr 28 min",
"runLengthOriginal": 88,
"tomatoScore": 39,
"tomatoImg": "rotten",
"audienceScore": 45,
"popcornImg": "popKnockedOver",
"price": 3.99,
"formats": [
"1080p",
"SD",
"4K",
"HD"
],
"starRating": "**+",
"starRatingNum": 25,
"episodeNumber": 0,
"episodeSeason": 0,
"originalAirDate": "",
"airDate": null,
"progType": "Feature Film",
"ltd": "",
"isInPlaylist": false,
"historical": false,
"detailsLinkUrl": "/movies/Men-in-Black-II-c0Frek5HeE1EaytueTN6VHJRZW9QQT09",
"description": "Kay (Tommy Lee Jones) and Jay (Will Smith) reunite to provide our best line of defense against a seductress who levels the toughest challenge yet to the MIBs mission statement: protecting the earth from the scum of the universe. While investigating a routine crime, Jay uncovers a plot masterminded by Serleena (Boyle), a Kylothian monster who disguises herself as a lingerie model. When Serleena takes the MIB building hostage, there is only one person Jay can turn to -- his former MIB partner.",
"primaryImageUrl": "/db_photos/movies/AllPhotosAPGI/29160/29160_aa.jpg",
"isLiveStreaming": false,
"tmsProgramID": "MV001173520000",
"firstRun": false,
"seriesID": 0
},
"reporting": {
"flip": {
"success": false,
"reportingData": "reporting for app/shared/nodules/json/flip/flip not implemented yet"
}
},
"messagekeys": null,
"contingencies": []
}

View file

@ -0,0 +1,45 @@
{
"programDetail": {
"title": "South Park",
"episodeTitle": "Goth Kids 3: Dawn of the Posers",
"mainCategory": "TV",
"rating": "TVMA",
"runLength": " 23 min",
"runLengthOriginal": 23,
"tomatoScore": 0,
"tomatoImg": "",
"audienceScore": 0,
"popcornImg": "",
"price": 2.99,
"formats": [
"1080p",
"SD"
],
"starRating": "",
"starRatingNum": 0,
"episodeNumber": 4,
"episodeSeason": 17,
"originalAirDate": "2013-10-23",
"airDate": "Wednesday, October 23rd",
"progType": "Series",
"ltd": "",
"isInPlaylist": false,
"historical": false,
"detailsLinkUrl": "/tv/South-Park-bldqUThGNWdxd289/Goth-Kids-3-Dawn-of-the-Posers-dXRwMUpueHkzeHVxdEtvZnF3bUxqUT09",
"seriesLinkUrl": "/tv/South-Park-bldqUThGNWdxd289",
"description": "The goth kids are sent to a camp for troubled children.",
"primaryImageUrl": "/db_photos/showcards/v5/AllPhotos/184338/p184338_b_v5_aa.jpg",
"isLiveStreaming": false,
"tmsProgramID": "EP002298270445",
"firstRun": false,
"seriesID": 184338
},
"reporting": {
"flip": {
"success": false,
"reportingData": "reporting for app/shared/nodules/json/flip/flip not implemented yet"
}
},
"messagekeys": null,
"contingencies": []
}

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,3 @@
const { padStart } = require('lodash')
const cheerio = require('cheerio') const cheerio = require('cheerio')
const axios = require('axios') const axios = require('axios')
const dayjs = require('dayjs') const dayjs = require('dayjs')
@ -9,14 +8,22 @@ dayjs.extend(utc)
module.exports = { module.exports = {
site: 'directv.com', site: 'directv.com',
days: 2, days: 2,
url({ channel, date }) { request: {
return `https://www.directv.com/json/channelschedule?channels=${ cache: {
channel.site_id ttl: 60 * 60 * 1000 // 1 hour
}&startTime=${date.format()}&hours=24` }
}, },
async parser({ content }) { url({ date, channel }) {
const [channelId, childId] = channel.site_id.split('#')
return `https://www.directv.com/json/channelschedule?channels=${
channelId
}&startTime=${date.format()}&hours=24&chId=${
childId
}`
},
async parser({ content, channel }) {
const programs = [] const programs = []
const items = parseItems(content) const items = parseItems(content, channel)
for (let item of items) { for (let item of items) {
if (item.programID === '-1') continue if (item.programID === '-1') continue
const detail = await loadProgramDetail(item.programID) const detail = await loadProgramDetail(item.programID)
@ -24,10 +31,14 @@ module.exports = {
const stop = start.add(item.duration, 'm') const stop = start.add(item.duration, 'm')
programs.push({ programs.push({
title: item.title, title: item.title,
sub_title: item.episodeTitle,
description: parseDescription(detail), description: parseDescription(detail),
rating: parseRating(item),
date: parseYear(detail),
category: item.subcategoryList, category: item.subcategoryList,
season: item.seasonNumber, season: item.seasonNumber,
episode: item.episodeNumber, episode: item.episodeNumber,
icon: parseIcon(item),
start, start,
stop stop
}) })
@ -65,7 +76,20 @@ module.exports = {
function parseDescription(detail) { function parseDescription(detail) {
return detail ? detail.description : null return detail ? detail.description : null
} }
function parseYear(detail) {
return detail ? detail.releaseYear : null
}
function parseRating(item) {
return item.rating
? {
system: 'MPA',
value: item.rating
}
: null
}
function parseIcon(item) {
return item.primaryImageUrl ? `https://www.directv.com${item.primaryImageUrl}` : null
}
function loadProgramDetail(programID) { function loadProgramDetail(programID) {
return axios return axios
.get(`https://www.directv.com/json/program/flip/${programID}`) .get(`https://www.directv.com/json/program/flip/${programID}`)
@ -78,8 +102,12 @@ function parseStart(item) {
return dayjs.utc(item.airTime) return dayjs.utc(item.airTime)
} }
function parseItems(content) { function parseItems(content, channel) {
const data = JSON.parse(content) const data = JSON.parse(content)
if (!data) return []
if (!Array.isArray(data.schedule)) return []
return data && data.schedule && data.schedule[0] ? data.schedule[0].schedules : [] const [, childId] = channel.site_id.split('#')
const channelData = data.schedule.find(i => i.chId == childId)
return channelData.schedules && Array.isArray(channelData.schedules) ? channelData.schedules : []
} }

View file

@ -2,6 +2,8 @@
// npx epg-grabber --config=sites/directv.com/directv.com.config.js --channels=sites/directv.com/directv.com.channels.xml --output=guide.xml --days=2 // npx epg-grabber --config=sites/directv.com/directv.com.config.js --channels=sites/directv.com/directv.com.channels.xml --output=guide.xml --days=2
const { parser, url } = require('./directv.com.config.js') const { parser, url } = require('./directv.com.config.js')
const fs = require('fs')
const path = require('path')
const axios = require('axios') const axios = require('axios')
const dayjs = require('dayjs') const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc') const utc = require('dayjs/plugin/utc')
@ -11,34 +13,37 @@ dayjs.extend(utc)
jest.mock('axios') jest.mock('axios')
const date = dayjs.utc('2021-10-24', 'YYYY-MM-DD').startOf('d') const date = dayjs.utc('2023-01-15', 'YYYY-MM-DD').startOf('d')
const channel = { const channel = {
site_id: '15', site_id: '249#249',
xmltv_id: 'WTAP.us' xmltv_id: 'ComedyCentralEast.us'
} }
const content = `{"schedule":[{"secLiveStreaming":"N","chNum":15,"authCode":"NA","chRec":true,"chCall":"WTAP","chId":2073,"secondaryChannelId":0,"chHd":true,"secondary":false,"blackOut":false,"chAdult":false,"chCat":["HDTV Channels","Local Channels"],"chLogoId":875,"detailsLinkUrl":"/Channels/Parkersburg-WV-WTAP-NBC-15-A3-HD-15","schedules":[{"primaryImageUrl":"/db_photos/default/TV/tv.jpg","restartAllowed":false,"subcategoryList":["Series","Reality"],"gridViewPrimaryImageUrl":"/db_photos/default/TV/tv_p.jpg","rating":"TVPG","description":null,"title":"Home Sweet Home","episodeNumber":3,"duration":60,"price":0,"repeat":false,"lookBack":false,"tvAdvisory":["L"],"dimension":"2D","ltd":"","programID":"EP039886740003","blackoutCode":"NA","airTime":"2021-10-30T00:00:00.000+0000","secLiveStreaming":"N","prOrd":0,"episodeTitle":"Art Is My God","authCode":"NA","format":"HD","seasonNumber":1,"listViewPrimaryImageUrl":"/db_photos/default/TV/tv_l.jpg","eventCode":"","mainCategory":"TV","hd":1,"liveStreaming":"N"}],"chKey":"2073_1476352800000","chName":"Parkersburg, WV WTAP NBC 15 A3 HD","chDesc":"NBC television services from WTAPDT-TV, 15, Parkersburg, WV.","liveStreaming":"N","digitalAdInsertableLive":false}],"reporting":{"channelschedules":{"success":false,"reportingData":"reporting for app/json/channelschedules/channelschedules not implemented yet"}},"messagekeys":null,"contingencies":[]}`
it('can generate valid url', () => { it('can generate valid url', () => {
const result = url({ date, channel }) const result = url({ date, channel })
expect(result).toBe( expect(result).toBe(
'https://www.directv.com/json/channelschedule?channels=15&startTime=2021-10-24T00:00:00Z&hours=24' 'https://www.directv.com/json/channelschedule?channels=249&startTime=2023-01-15T00:00:00Z&hours=24&chId=249'
) )
}) })
it('can parse response', done => { it('can parse response', done => {
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
axios.get.mockImplementation(url => { axios.get.mockImplementation(url => {
if (url === 'https://www.directv.com/json/program/flip/EP039886740003') { if (url === 'https://www.directv.com/json/program/flip/MV001173520000') {
return Promise.resolve({ return Promise.resolve({
data: JSON.parse( data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program1.json')))
`{"programDetail":{"title":"Home Sweet Home","episodeTitle":"Art Is My God","mainCategory":"TV","rating":"PG","runLength":"1 hr","runLengthOriginal":60,"tomatoScore":0,"tomatoImg":"","audienceScore":0,"popcornImg":"","price":0,"formats":["1080p"],"starRating":"","starRatingNum":0,"episodeNumber":3,"episodeSeason":1,"originalAirDate":"2021-10-29","airDate":"Friday, October 29th","progType":"Series","ltd":"","isInPlaylist":false,"historical":false,"detailsLinkUrl":"/tv/Home-Sweet-Home-bUdDOWFNWkFKQWlGby9GckxSaXJvUT09/Art-Is-My-God-QVZSbmFsVUNvK0pLL3JRTjl0OFNYUT09","seriesLinkUrl":"/tv/Home-Sweet-Home-bUdDOWFNWkFKQWlGby9GckxSaXJvUT09","description":"The Baltzers, a surfing Mormon family, and the Silversteins, an artistic Black and Latino family with Jewish heritage, discover that the struggle of living outside their comfort zones sparks rewarding moments.","primaryImageUrl":"/db_photos/default/TV/tv.jpg","isLiveStreaming":false,"tmsProgramID":"EP039886740003","firstRun":false,"seriesID":20584969},"reporting":{"flip":{"success":false,"reportingData":"reporting for app/shared/nodules/json/flip/flip not implemented yet"}},"messagekeys":null,"contingencies":[]}` })
) } else if (url === 'https://www.directv.com/json/program/flip/EP002298270445') {
return Promise.resolve({
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program2.json')))
}) })
} else { } else {
return Promise.resolve({ data: '' }) return Promise.resolve({ data: '' })
} }
}) })
parser({ date, channel, content }) parser({ content, channel })
.then(result => { .then(result => {
result = result.map(p => { result = result.map(p => {
p.start = p.start.toJSON() p.start = p.start.toJSON()
@ -48,34 +53,33 @@ it('can parse response', done => {
expect(result).toMatchObject([ expect(result).toMatchObject([
{ {
start: '2021-10-30T00:00:00.000Z', start: '2023-01-14T23:00:00.000Z',
stop: '2021-10-30T01:00:00.000Z', stop: '2023-01-15T01:00:00.000Z',
title: 'Home Sweet Home', title: 'Men in Black II',
description: description:
'The Baltzers, a surfing Mormon family, and the Silversteins, an artistic Black and Latino family with Jewish heritage, discover that the struggle of living outside their comfort zones sparks rewarding moments.', 'Kay (Tommy Lee Jones) and Jay (Will Smith) reunite to provide our best line of defense against a seductress who levels the toughest challenge yet to the MIBs mission statement: protecting the earth from the scum of the universe. While investigating a routine crime, Jay uncovers a plot masterminded by Serleena (Boyle), a Kylothian monster who disguises herself as a lingerie model. When Serleena takes the MIB building hostage, there is only one person Jay can turn to -- his former MIB partner.',
season: 1, date: '2002',
episode: 3, icon: 'https://www.directv.com/db_photos/movies/AllPhotosAPGI/29160/29160_aa.jpg',
category: ['Series', 'Reality'] category: ['Comedy', 'Movies Anywhere', 'Action/Adventure', 'Science Fiction'],
} rating: {
]) system: 'MPA',
done() value: 'TV14'
}) }
.catch(done) },
})
it('can handle missing details', done => {
axios.get.mockImplementation(url => {
if (url === 'https://www.directv.com/json/program/flip/EP039886740003') {
return Promise.resolve({ data: '' })
}
})
parser({ date, channel, content })
.then(result => {
expect(result).toMatchObject([
{ {
title: 'Home Sweet Home', start: '2023-01-15T06:00:00.000Z',
description: null stop: '2023-01-15T06:30:00.000Z',
title: 'South Park',
sub_title: 'Goth Kids 3: Dawn of the Posers',
description: 'The goth kids are sent to a camp for troubled children.',
icon: 'https://www.directv.com/db_photos/showcards/v5/AllPhotos/184338/p184338_b_v5_aa.jpg',
category: ['Series', 'Animation', 'Comedy'],
season: 17,
episode: 4,
rating: {
system: 'MPA',
value: 'TVMA'
}
} }
]) ])
done() done()
@ -84,11 +88,8 @@ it('can handle missing details', done => {
}) })
it('can handle empty guide', done => { it('can handle empty guide', done => {
parser({ const content = fs.readFileSync(path.resolve(__dirname, '__data__/no-content.json'))
date, parser({ content, channel })
channel,
content: `{"errors":[{"text":"Service failure: see errors or BulkOperationErrors for details","field":"","reason":"INTERNAL_SERVER_ERROR"}],"statusCode":500,"apiResponse":{"messages":"NOTE: see res.contingencies for size-filtered message values"},"reporting":{"channelschedules":{"success":false,"reportingData":"reporting for app/json/channelschedules/channelschedules not implemented yet"}},"messagekeys":null,"contingencies":[{"key":"ent_ep_guide_backend_unavailable_error_message","value":"<!-- message: key=ent_ep_guide_backend_unavailable_error_message, deviceType=web -->Due to technical issues the guide is currently unavailable, please check back to soon.","level":"ERROR"}]}`
})
.then(result => { .then(result => {
expect(result).toMatchObject([]) expect(result).toMatchObject([])
done() done()