diff --git a/README.md b/README.md index 2102794..93bb71b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

-Current version: **3.2.5** +Current version: **3.2.6** # About This takes ESPN/ESPN+, FOX Sports, Paramount+, MSG+, NFL+, B1G+, NESN, Mountain West, FloSports, or MLB.tv programming and transforms it into a "live TV" experience with virtual linear channels. It will discover what is on, and generate a schedule of channels that will give you M3U and XMLTV files that you can import into something like [Jellyfin](https://jellyfin.org) or [Channels](https://getchannels.com). @@ -70,14 +70,19 @@ Use if you would like to login with Paramount+ | GOLAZO* | Set if you would like the Golazo Network channel (only available with `LINEAR_CHANNELS`) | False | False | #### NFL+ -Use if you would like to login with NFL+ +Use if you would like to login with NFL+. + +Please note that if you only have an NFL account, you can still get events from Amazon Prime, Peacock, or your TV Provider. *** If you have access to NFL RedZone (NFL+ Premium), it will be scheduled. If `LINEAR_CHANNELS` is set, it will be on its own channel | Environment Variable | Description | Required? | Default | |---|---|---|---| | NFLPLUS | Set if you would like NFL+ events | False | False | -| NFLNETWORK* | Set if you would like the NFL Network channel (only available with `LINEAR_CHANNELS`) | False | False | +| NFL_TVE | Set if you would like NFL games from your TV Provider (not needed with NFL+) | False | False | +| NFL_PRIME | Set if you would like NFL games from Amazon Prime (not needed with NFL+) | False | False | +| NFL_PEACOCK | Set if you would like NFL games from Peacock (not needed with NFL+) | False | False | +| NFLNETWORK* | Set if you would like the NFL Network channel (only available with `LINEAR_CHANNELS`). Must have NFL+ or TV Provider. | False | False | | NFLCHANNEL* | Set if you would like the NFL Channel (only available with `LINEAR_CHANNELS`) | False | False | #### NESN diff --git a/package-lock.json b/package-lock.json index 0bef411..e22f05e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "eplustv", - "version": "3.2.5", + "version": "3.2.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "eplustv", - "version": "3.2.5", + "version": "3.2.6", "license": "MIT", "dependencies": { "axios": "^1.2.2", diff --git a/package.json b/package.json index ea81319..7e8eadc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eplustv", - "version": "3.2.5", + "version": "3.2.6", "description": "", "scripts": { "start": "ts-node index.ts", diff --git a/services/channels.ts b/services/channels.ts index 8b5065a..c1813d8 100644 --- a/services/channels.ts +++ b/services/channels.ts @@ -47,182 +47,187 @@ export const LINEAR_START_CHANNEL = nextStartChannel(startChannel + numOfChannel export const useLinear = process.env.LINEAR_CHANNELS?.toLowerCase() === 'true' ? true : false; /* eslint-disable sort-keys-custom-order-fix/sort-keys-custom-order-fix */ -export const CHANNEL_MAP = { - 0: { - canUse: useEspn1, - id: 'espn1', - logo: 'https://tmsimg.fancybits.co/assets/s32645_h3_aa.png?w=360&h=270', - name: 'ESPN', - stationId: '32645', - tvgName: 'ESPNHD', - }, - 1: { - canUse: useEspn2, - id: 'espn2', - logo: 'https://tmsimg.fancybits.co/assets/s45507_ll_h15_aa.png?w=360&h=270', - name: 'ESPN2', - stationId: '45507', - tvgName: 'ESPN2HD', - }, - 2: { - canUse: useEspnU, - id: 'espnu', - logo: 'https://tmsimg.fancybits.co/assets/s60696_ll_h15_aa.png?w=360&h=270', - name: 'ESPNU', - stationId: '60696', - tvgName: 'ESPNUHD', - }, - 3: { - canUse: useSec, - id: 'sec', - logo: 'https://tmsimg.fancybits.co/assets/s89714_ll_h15_aa.png?w=360&h=270', - name: 'SEC Network', - stationId: '89714', - tvgName: 'SECH', - }, - 4: { - canUse: useAccN, - id: 'acc', - logo: 'https://tmsimg.fancybits.co/assets/s111871_ll_h15_ac.png?w=360&h=270', - name: 'ACC Network', - stationId: '111871', - tvgName: 'ACC', - }, - 5: { - canUse: useEspnews, - id: 'espnews', - logo: 'https://tmsimg.fancybits.co/assets/s59976_ll_h15_aa.png?w=360&h=270', - name: 'ESPNews', - stationId: '59976', - tvgName: 'ESPNWHD', - }, - 10: { - canUse: useFoxSports, - id: 'fs1', - logo: 'https://tmsimg.fancybits.co/assets/s82547_ll_h15_aa.png?w=360&h=270', - name: 'FS1', - stationId: '82547', - tvgName: 'FS1HD', - }, - 11: { - canUse: useFoxSports, - id: 'fs2', - logo: 'https://tmsimg.fancybits.co/assets/s59305_ll_h15_aa.png?w=360&h=270', - name: 'FS2', - stationId: '59305', - tvgName: 'FS2HD', - }, - 12: { - canUse: useFoxSports, - id: 'btn', - logo: 'https://tmsimg.fancybits.co/assets/s58321_ll_h15_ac.png?w=360&h=270', - name: 'B1G Network', - stationId: '58321', - tvgName: 'BIG10HD', - }, - 13: { - canUse: useFoxSports, - id: 'fox-soccer-plus', - logo: 'https://tmsimg.fancybits.co/assets/s66880_ll_h15_aa.png?w=360&h=270', - name: 'FOX Soccer Plus', - stationId: '66880', - tvgName: 'FSCPLHD', - }, - 20: { - canUse: useParamount.cbsSportsHq, - id: 'cbssportshq', - logo: 'https://tmsimg.fancybits.co/assets/s108919_ll_h15_aa.png?w=360&h=270', - name: 'CBS Sports HQ', - stationId: '108919', - tvgName: 'CBSSPHQ', - }, - 21: { - canUse: useParamount.golazo, - id: 'golazo', - logo: 'https://tmsimg.fancybits.co/assets/s133691_ll_h15_aa.png?w=360&h=270', - name: 'GOLAZO Network', - stationId: '133691', - tvgName: 'GOLAZO', - }, - 30: { - canUse: useNfl.network, - id: 'NFLNETWORK', - logo: 'https://tmsimg.fancybits.co/assets/s45399_ll_h15_aa.png?w=360&h=270', - name: 'NFL Network', - stationId: '45399', - tvgName: 'NFLHD', - }, - 31: { - canUse: useNfl.redZone, - id: 'NFLNRZ', - logo: 'https://tmsimg.fancybits.co/assets/s65025_ll_h9_aa.png?w=360&h=270', - name: 'NFL RedZone', - stationId: '65025', - tvgName: 'NFLNRZD', - }, - 32: { - canUse: useNfl.channel, - id: 'NFLDIGITAL1_OO_v3', - logo: 'https://tmsimg.fancybits.co/assets/s121705_ll_h15_aa.png?w=360&h=270', - name: 'NFL Channel', - stationId: '121705', - tvgName: 'NFLDC1', - }, - 40: { - canUse: useMLBtv, - id: 'MLBTVBI', - logo: 'https://tmsimg.fancybits.co/assets/s119153_ll_h15_aa.png?w=360&h=270', - name: 'MLB Big Inning', - stationId: '119153', - tvgName: 'MLBTVBI', - }, - 50: { - canUse: useNesn, - id: 'NESN', - logo: 'https://tmsimg.fancybits.co/assets/s35038_ll_h15_ac.png?w=360&h=270', - name: 'New England Sports Network HD', - stationId: '35038', - tvgName: 'NESNHD', - }, - 51: { - canUse: useNesn, - id: 'NESN+', - logo: 'https://tmsimg.fancybits.co/assets/s63198_ll_h15_ac.png?w=360&h=270', - name: 'New England Sports Network Plus HD', - stationId: '63516', - tvgName: 'NESNPLD', - }, - 60: { - canUse: useMsgPlus, - id: 'MSG', - logo: 'https://tmsimg.fancybits.co/assets/s10979_ll_h15_ab.png?w=360&h=270', - name: 'MSG', - stationId: '10979', - tvgName: 'MSG', - }, - 61: { - canUse: useMsgPlus, - id: 'MSGSN', - logo: 'https://tmsimg.fancybits.co/assets/s11105_ll_h15_ac.png?w=360&h=270', - name: 'MSG Sportsnet HD', - stationId: '15273', - tvgName: 'MSGSNNP', - }, - 62: { - canUse: useMsgPlus, - id: 'MSG2', - logo: 'https://tmsimg.fancybits.co/assets/s70283_ll_h15_aa.png?w=360&h=270', - name: 'MSG2 HD', - stationId: '70283', - tvgName: 'MSG2HD', - }, - 63: { - canUse: useMsgPlus, - id: 'MSGSN2', - logo: 'https://tmsimg.fancybits.co/assets/s70285_ll_h15_ab.png?w=360&h=270', - name: 'MSG Sportsnet 2 HD', - stationId: '70285', - tvgName: 'MSG2SNH', +export const CHANNELS = { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + get MAP() { + return { + 0: { + canUse: useEspn1, + id: 'espn1', + logo: 'https://tmsimg.fancybits.co/assets/s32645_h3_aa.png?w=360&h=270', + name: 'ESPN', + stationId: '32645', + tvgName: 'ESPNHD', + }, + 1: { + canUse: useEspn2, + id: 'espn2', + logo: 'https://tmsimg.fancybits.co/assets/s45507_ll_h15_aa.png?w=360&h=270', + name: 'ESPN2', + stationId: '45507', + tvgName: 'ESPN2HD', + }, + 2: { + canUse: useEspnU, + id: 'espnu', + logo: 'https://tmsimg.fancybits.co/assets/s60696_ll_h15_aa.png?w=360&h=270', + name: 'ESPNU', + stationId: '60696', + tvgName: 'ESPNUHD', + }, + 3: { + canUse: useSec, + id: 'sec', + logo: 'https://tmsimg.fancybits.co/assets/s89714_ll_h15_aa.png?w=360&h=270', + name: 'SEC Network', + stationId: '89714', + tvgName: 'SECH', + }, + 4: { + canUse: useAccN, + id: 'acc', + logo: 'https://tmsimg.fancybits.co/assets/s111871_ll_h15_ac.png?w=360&h=270', + name: 'ACC Network', + stationId: '111871', + tvgName: 'ACC', + }, + 5: { + canUse: useEspnews, + id: 'espnews', + logo: 'https://tmsimg.fancybits.co/assets/s59976_ll_h15_aa.png?w=360&h=270', + name: 'ESPNews', + stationId: '59976', + tvgName: 'ESPNWHD', + }, + 10: { + canUse: useFoxSports, + id: 'fs1', + logo: 'https://tmsimg.fancybits.co/assets/s82547_ll_h15_aa.png?w=360&h=270', + name: 'FS1', + stationId: '82547', + tvgName: 'FS1HD', + }, + 11: { + canUse: useFoxSports, + id: 'fs2', + logo: 'https://tmsimg.fancybits.co/assets/s59305_ll_h15_aa.png?w=360&h=270', + name: 'FS2', + stationId: '59305', + tvgName: 'FS2HD', + }, + 12: { + canUse: useFoxSports, + id: 'btn', + logo: 'https://tmsimg.fancybits.co/assets/s58321_ll_h15_ac.png?w=360&h=270', + name: 'B1G Network', + stationId: '58321', + tvgName: 'BIG10HD', + }, + 13: { + canUse: useFoxSports, + id: 'fox-soccer-plus', + logo: 'https://tmsimg.fancybits.co/assets/s66880_ll_h15_aa.png?w=360&h=270', + name: 'FOX Soccer Plus', + stationId: '66880', + tvgName: 'FSCPLHD', + }, + 20: { + canUse: useParamount.cbsSportsHq, + id: 'cbssportshq', + logo: 'https://tmsimg.fancybits.co/assets/s108919_ll_h15_aa.png?w=360&h=270', + name: 'CBS Sports HQ', + stationId: '108919', + tvgName: 'CBSSPHQ', + }, + 21: { + canUse: useParamount.golazo, + id: 'golazo', + logo: 'https://tmsimg.fancybits.co/assets/s133691_ll_h15_aa.png?w=360&h=270', + name: 'GOLAZO Network', + stationId: '133691', + tvgName: 'GOLAZO', + }, + 30: { + canUse: useNfl.network, + id: 'NFLNETWORK', + logo: 'https://tmsimg.fancybits.co/assets/s45399_ll_h15_aa.png?w=360&h=270', + name: 'NFL Network', + stationId: '45399', + tvgName: 'NFLHD', + }, + 31: { + canUse: useNfl.redZone, + id: 'NFLNRZ', + logo: 'https://tmsimg.fancybits.co/assets/s65025_ll_h9_aa.png?w=360&h=270', + name: 'NFL RedZone', + stationId: '65025', + tvgName: 'NFLNRZD', + }, + 32: { + canUse: useNfl.channel, + id: 'NFLDIGITAL1_OO_v3', + logo: 'https://tmsimg.fancybits.co/assets/s121705_ll_h15_aa.png?w=360&h=270', + name: 'NFL Channel', + stationId: '121705', + tvgName: 'NFLDC1', + }, + 40: { + canUse: useMLBtv, + id: 'MLBTVBI', + logo: 'https://tmsimg.fancybits.co/assets/s119153_ll_h15_aa.png?w=360&h=270', + name: 'MLB Big Inning', + stationId: '119153', + tvgName: 'MLBTVBI', + }, + 50: { + canUse: useNesn, + id: 'NESN', + logo: 'https://tmsimg.fancybits.co/assets/s35038_ll_h15_ac.png?w=360&h=270', + name: 'New England Sports Network HD', + stationId: '35038', + tvgName: 'NESNHD', + }, + 51: { + canUse: useNesn, + id: 'NESN+', + logo: 'https://tmsimg.fancybits.co/assets/s63198_ll_h15_ac.png?w=360&h=270', + name: 'New England Sports Network Plus HD', + stationId: '63516', + tvgName: 'NESNPLD', + }, + 60: { + canUse: useMsgPlus, + id: 'MSG', + logo: 'https://tmsimg.fancybits.co/assets/s10979_ll_h15_ab.png?w=360&h=270', + name: 'MSG', + stationId: '10979', + tvgName: 'MSG', + }, + 61: { + canUse: useMsgPlus, + id: 'MSGSN', + logo: 'https://tmsimg.fancybits.co/assets/s11105_ll_h15_ac.png?w=360&h=270', + name: 'MSG Sportsnet HD', + stationId: '15273', + tvgName: 'MSGSNNP', + }, + 62: { + canUse: useMsgPlus, + id: 'MSG2', + logo: 'https://tmsimg.fancybits.co/assets/s70283_ll_h15_aa.png?w=360&h=270', + name: 'MSG2 HD', + stationId: '70283', + tvgName: 'MSG2HD', + }, + 63: { + canUse: useMsgPlus, + id: 'MSGSN2', + logo: 'https://tmsimg.fancybits.co/assets/s70285_ll_h15_ab.png?w=360&h=270', + name: 'MSG Sportsnet 2 HD', + stationId: '70285', + tvgName: 'MSG2SNH', + }, + }; }, }; /* eslint-enable sort-keys-custom-order-fix/sort-keys-custom-order-fix */ @@ -234,7 +239,7 @@ export const calculateChannelNumber = (channelNum: string): number | string => { return channelNum; } - const linearChannel = CHANNEL_MAP[chanNum - LINEAR_START_CHANNEL]; + const linearChannel = CHANNELS.MAP[chanNum - LINEAR_START_CHANNEL]; if (linearChannel) { return linearChannel.id; @@ -252,7 +257,7 @@ export const calculateChannelFromName = (channelName: string): number => { let channelNum = Number.MAX_SAFE_INTEGER; - _.forOwn(CHANNEL_MAP, (val, key) => { + _.forOwn(CHANNELS.MAP, (val, key) => { if (val.id === channelName) { channelNum = parseInt(key, 10) + LINEAR_START_CHANNEL; } diff --git a/services/generate-m3u.ts b/services/generate-m3u.ts index e1579a8..1a73a23 100644 --- a/services/generate-m3u.ts +++ b/services/generate-m3u.ts @@ -1,12 +1,12 @@ import _ from 'lodash'; -import {NUM_OF_CHANNELS, START_CHANNEL, CHANNEL_MAP, LINEAR_START_CHANNEL} from './channels'; +import {NUM_OF_CHANNELS, START_CHANNEL, CHANNELS, LINEAR_START_CHANNEL} from './channels'; export const generateM3u = (uri: string, linear = false): string => { let m3uFile = '#EXTM3U'; if (linear) { - _.forOwn(CHANNEL_MAP, (val, key) => { + _.forOwn(CHANNELS.MAP, (val, key) => { if (!val.canUse) { return; } diff --git a/services/generate-xmltv.ts b/services/generate-xmltv.ts index ec92deb..1a05651 100644 --- a/services/generate-xmltv.ts +++ b/services/generate-xmltv.ts @@ -4,7 +4,7 @@ import moment from 'moment'; import {db} from './database'; import {usesMultiple} from './networks'; -import {calculateChannelFromName, CHANNEL_MAP, LINEAR_START_CHANNEL, NUM_OF_CHANNELS, START_CHANNEL} from './channels'; +import {calculateChannelFromName, CHANNELS, LINEAR_START_CHANNEL, NUM_OF_CHANNELS, START_CHANNEL} from './channels'; import {IEntry} from './shared-interfaces'; const baseCategories = ['HD', 'HDTV', 'Sports event', 'Sports']; @@ -51,7 +51,7 @@ export const generateXml = async (linear = false): Promise => { }; if (linear) { - _.forOwn(CHANNEL_MAP, (val, key) => { + _.forOwn(CHANNELS.MAP, (val, key) => { if (!val.canUse) { return; } diff --git a/services/networks.ts b/services/networks.ts index 684382f..b045652 100644 --- a/services/networks.ts +++ b/services/networks.ts @@ -38,28 +38,35 @@ export const useMsgPlus = process.env.MSGPLUS?.toLowerCase() === 'true' ? true : export const useNfl = { _channel: process.env.NFLCHANNEL?.toLowerCase() === 'true' ? true : false, _network: process.env.NFLNETWORK?.toLowerCase() === 'true' ? true : false, - // _paramount: process.env.NFL_PARAMOUNT?.toLowerCase() === 'true' ? true : false, - _redZone: true, - // _tve: process.env.NFL_TVE?.toLowerCase() === 'true' ? true : false, + _peacock: process.env.NFL_PEACOCK?.toLowerCase() === 'true' ? true : false, + _prime: process.env.NFL_PRIME?.toLowerCase() === 'true' ? true : false, + _redZone: false, + _tve: process.env.NFL_TVE?.toLowerCase() === 'true' ? true : false, get channel(): boolean { return this._channel && this.plus; }, get network(): boolean { return this._network && this.plus; }, - // get paramount(): boolean { - // return this._paramount && this.plus; - // }, + set network(value: boolean) { + this._network = value; + }, + get peacock(): boolean { + return this._peacock && this.plus; + }, plus: process.env.NFLPLUS?.toLowerCase() === 'true' ? true : false, + get prime(): boolean { + return this._prime && this.plus; + }, get redZone(): boolean { return this._redZone && this.plus; }, set redZone(value: boolean) { this._redZone = value; }, - // get tve(): boolean { - // return this._tve && this.plus; - // }, + get tve(): boolean { + return this._tve && this.plus; + }, }; export const useMountainWest = process.env.MTNWEST?.toLowerCase() === 'true' ? true : false; diff --git a/services/nfl-handler.ts b/services/nfl-handler.ts index e4b3594..9b35a56 100644 --- a/services/nfl-handler.ts +++ b/services/nfl-handler.ts @@ -24,6 +24,9 @@ interface INFLChannelRes { } interface INFLEvent { + authorizations: { + [key: string]: any; + }; title: string; startTime: string; endTime: string; @@ -36,6 +39,7 @@ interface INFLEvent { description: string; callSign: string; linear: boolean; + networks: string[]; } const CLIENT_KEY = [ @@ -152,7 +156,13 @@ const TV_DEVICE_INFO = { const DEFAULT_CATEGORIES = ['NFL', 'NFL+', 'Football']; -type TOtherAuth = 'paramount' | 'tve'; +type TOtherAuth = 'prime' | 'tve' | 'peacock'; + +interface INFLJwt { + dmaCode: string; + plans: {plan: string; status: string}[]; + networks?: {[key: string]: string}; +} const parseAirings = async (events: INFLEvent[]) => { const now = moment(); @@ -191,6 +201,7 @@ const parseAirings = async (events: INFLEvent[]) => { categories: [...new Set(categories)], duration: end.diff(start, 'seconds'), end: end.valueOf(), + feed: event.networks?.[0], from: 'nfl+', id: event.externalId, image: event.preferredImage, @@ -220,11 +231,13 @@ class NflHandler { public tv_expires_at?: number; // Supplemental Auth - // public paramountPlusUserId?: string; - // public paramountPlusUUID?: string; - // public mvpdIdp?: string; - // public mvpdUserId?: string; - // public mvpdUUID?: string; + public mvpdIdp?: string; + public mvpdUserId?: string; + public mvpdUUID?: string; + public amazonPrimeUserId?: string; + public amazonPrimeUUID?: string; + public peacockUserId?: string; + public peacockUUID?: string; public initialize = async () => { if (!useNfl.plus) { @@ -243,13 +256,17 @@ class NflHandler { await this.startProviderAuthFlow(); } - // if (useNfl.paramount && (!this.paramountPlusUserId || !this.paramountPlusUUID)) { - // await this.startProviderAuthFlow('paramount'); - // } + if (useNfl.tve && (!this.mvpdIdp || !this.mvpdUserId || !this.mvpdUUID)) { + await this.startProviderAuthFlow('tve'); + } + + if (useNfl.peacock && (!this.peacockUserId || !this.peacockUUID)) { + await this.startProviderAuthFlow('peacock'); + } - // if (useNfl.tve && (!this.mvpdIdp || !this.mvpdUserId || !this.mvpdUUID)) { - // await this.startProviderAuthFlow('tve'); - // } + if (useNfl.prime && (!this.amazonPrimeUserId || !this.amazonPrimeUUID)) { + await this.startProviderAuthFlow('prime'); + } }; public refreshTokens = async () => { @@ -267,10 +284,11 @@ class NflHandler { return; } - const {dmaCode}: {dmaCode: string; plans: {plan: string; status: string}[]} = jwt_decode(this.access_token); + const {dmaCode}: INFLJwt = jwt_decode(this.access_token); const redZoneAccess = this.checkRedZoneAccess(); - const nflNetworkAccess = useLinear && useNfl.network; + const nflNetworkAccess = this.checkNetworkAccess(); + const hasPlus = this.checkPlusAccess(); if (!dmaCode) { console.log('DMA Code not found for NFL+. Not searching for events'); @@ -293,26 +311,38 @@ class NflHandler { }); data.data.items.forEach(i => { - if ( - i.contentType === 'GAME' && - moment(i.startTime).isBefore(endSchedule) && - i.dmaCodes.find(dc => dc === `${dmaCode}`) && - i.language.find(l => l === 'en') - ) { - events.push(i); - } else if ( - i.callSign === 'NFLNRZ' && - i.title === 'NFL RedZone' && - moment(i.startTime).isBefore(endSchedule) && - redZoneAccess - ) { - events.push(i); - } else if (i.callSign === 'NFLNETWORK' && moment(i.startTime).isBefore(endSchedule) && nflNetworkAccess) { - events.push(i); + if (moment(i.startTime).isBefore(endSchedule)) { + if ( + i.contentType === 'GAME' && + i.dmaCodes.find(dc => dc === `${dmaCode}`) && + i.language.find(l => l === 'en') + ) { + if ( + // If you have NFL+, you get the game + hasPlus || + // TVE + this.checkTVEEventAccess(i) || + // Peacock + (i.authorizations.peacock && this.checkPeacockAccess()) || + // Prime + (i.authorizations.amazon_prime && this.checkPrimeAccess()) + ) { + events.push(i); + } + } else if ( + i.callSign === 'NFLNRZ' && + i.title === 'NFL RedZone' && + // NFL+ Premium or TVE supports RedZone + redZoneAccess + ) { + events.push(i); + } else if (i.callSign === 'NFLNETWORK' && nflNetworkAccess) { + events.push(i); + } } }); - if (useNfl.channel) { + if (useNfl.channel && useLinear) { const url = [ 'https://', 'api.nfl.com', @@ -352,7 +382,22 @@ class NflHandler { const {data} = await axios.post( url, - {}, + { + ...(this.checkTVEAccess() && { + idp: this.mvpdIdp, + mvpdUUID: this.mvpdUUID, + mvpdUserId: this.mvpdUserId, + networks: event.feed, + }), + ...(this.checkPrimeAccess() && { + amazonPrimeUUID: this.amazonPrimeUUID, + amazonPrimeUserId: this.amazonPrimeUserId, + }), + ...(this.checkPeacockAccess() && { + peacockUUID: this.amazonPrimeUUID, + peacockUserId: this.amazonPrimeUserId, + }), + }, { headers: { 'Content-Type': 'application/json', @@ -371,17 +416,69 @@ class NflHandler { private checkRedZoneAccess = (): boolean => { try { - const {plans}: {dmaCode: string; plans: {plan: string; status: string}[]} = jwt_decode(this.access_token); + const {plans, networks}: INFLJwt = jwt_decode(this.access_token); if (plans) { useNfl.redZone = - plans.findIndex(p => p.plan === 'NFL_PLUS_PREMIUM' && p.status === 'ACTIVE') > -1 ? true : false; + plans.findIndex(p => p.plan === 'NFL_PLUS_PREMIUM' && p.status === 'ACTIVE') > -1 || networks?.NFLRZ + ? true + : false; } } catch (e) {} return useNfl.redZone; }; + private checkNetworkAccess = (): boolean => { + try { + const {plans, networks}: INFLJwt = jwt_decode(this.access_token); + + if (plans) { + const hasPlus = (this.checkPlusAccess() || networks?.NFLN) && useLinear ? true : false; + useNfl.network = hasPlus; + } + } catch (e) {} + + return useNfl.network; + }; + + private checkPlusAccess = (): boolean => { + let hasPlus = false; + + try { + const {plans}: INFLJwt = jwt_decode(this.access_token); + + if (plans) { + hasPlus = + plans.findIndex(p => (p.plan === 'NFL_PLUS' || p.plan === 'NFL_PLUS_PREMIUM') && p.status === 'ACTIVE') > -1 + ? true + : false; + } + } catch (e) {} + + return hasPlus; + }; + + private checkTVEAccess = (): boolean => (this.mvpdIdp && useNfl.tve ? true : false); + private checkPeacockAccess = (): boolean => (this.peacockUserId && useNfl.peacock ? true : false); + private checkPrimeAccess = (): boolean => (this.amazonPrimeUserId && useNfl.prime ? true : false); + + private checkTVEEventAccess = (event: INFLEvent): boolean => { + let hasChannel = false; + + try { + const {networks}: INFLJwt = jwt_decode(this.access_token); + + event.networks.forEach(n => { + if (networks[n]) { + hasChannel = true; + } + }); + } catch (e) {} + + return hasChannel; + }; + private extendTokens = async (): Promise => { await this.extendToken(); await this.extendTvToken(); @@ -405,15 +502,19 @@ class NflHandler { signatureTimestamp, uidSignature, }), - // ...(this.mvpdIdp && { - // mvpdIdp: this.mvpdIdp, - // mvpdUUID: this.mvpdUUID, - // mvpdUserId: this.mvpdUserId, - // }), - // ...(this.paramountPlusUserId && { - // paramountPlusUUID: this.paramountPlusUUID, - // paramountPlusUserId: this.paramountPlusUserId, - // }), + ...(this.mvpdIdp && { + mvpdIdp: this.mvpdIdp, + mvpdUUID: this.mvpdUUID, + mvpdUserId: this.mvpdUserId, + }), + ...(this.amazonPrimeUserId && { + amazonPrimeUUID: this.amazonPrimeUUID, + amazonPrimeUserId: this.amazonPrimeUserId, + }), + ...(this.peacockUserId && { + peacockUUID: this.peacockUUID, + peacockUserId: this.peacockUserId, + }), }, { headers: { @@ -425,9 +526,21 @@ class NflHandler { this.access_token = data.accessToken; this.refresh_token = data.refreshToken; this.expires_at = data.expiresIn; + + if (data.additionalInfo) { + data.additionalInfo.forEach(ai => { + if (ai.data) { + if (ai.data.idp === 'amazon') { + this.amazonPrimeUUID = ai.data.newUUID; + } + } + }); + } + this.save(); this.checkRedZoneAccess(); + this.checkNetworkAccess(); } catch (e) { console.error(e); console.log('Could not refresh token for NFL+'); @@ -452,15 +565,19 @@ class NflHandler { signatureTimestamp, uidSignature, }), - // ...(this.mvpdIdp && { - // mvpdIdp: this.mvpdIdp, - // mvpdUUID: this.mvpdUUID, - // mvpdUserId: this.mvpdUserId, - // }), - // ...(this.paramountPlusUserId && { - // paramountPlusUUID: this.paramountPlusUUID, - // paramountPlusUserId: this.paramountPlusUserId, - // }), + ...(this.mvpdIdp && { + mvpdIdp: this.mvpdIdp, + mvpdUUID: this.mvpdUUID, + mvpdUserId: this.mvpdUserId, + }), + ...(this.amazonPrimeUserId && { + amazonPrimeUUID: this.amazonPrimeUUID, + amazonPrimeUserId: this.amazonPrimeUserId, + }), + ...(this.peacockUserId && { + peacockUUID: this.peacockUUID, + peacockUserId: this.peacockUserId, + }), }, { headers: { @@ -474,7 +591,18 @@ class NflHandler { this.tv_expires_at = data.expiresIn; this.save(); + if (data.additionalInfo) { + data.additionalInfo.forEach(ai => { + if (ai.data) { + if (ai.data.idp === 'amazon') { + this.amazonPrimeUUID = ai.data.newUUID; + } + } + }); + } + this.checkRedZoneAccess(); + this.checkNetworkAccess(); } catch (e) { console.error(e); console.log('Could not refresh token for NFL+'); @@ -581,14 +709,18 @@ class NflHandler { nflAccount: true, nflToken: true, }), - // ...(otherAuth === 'paramount' && { - // idp: 'PARAMOUNT_PLUS', - // nflAccount: false, - // }), - // ...(otherAuth === 'tve' && { - // idp: 'TV_PROVIDER', - // nflAccount: false, - // }), + ...(otherAuth === 'tve' && { + idp: 'TV_PROVIDER', + nflAccount: false, + }), + ...(otherAuth === 'prime' && { + idp: 'AMAZON', + nflAccount: false, + }), + ...(otherAuth === 'peacock' && { + idp: 'PEACOCK', + nflAccount: false, + }), }, { headers: { @@ -599,8 +731,17 @@ class NflHandler { }, ); - console.log('=== NFL+ Auth ==='); - console.log('Please open a browser window and go to: https://id.nfl.com/account/activate'); + const otherAuthName = + otherAuth === 'tve' + ? '(TV Provider) ' + : otherAuth === 'prime' + ? '(Amazon Prime) ' + : otherAuth === 'peacock' + ? '(Peacock) ' + : ''; + + console.log(`=== NFL+ Auth ${otherAuthName}===`); + console.log(`Please open a browser window and go to: https://id.nfl.com/account/activate?regCode=${code}`); console.log('Enter code: ', code); console.log('App will continue when login has completed...'); @@ -658,16 +799,19 @@ class NflHandler { } if (otherAuth === 'tve') { - // this.mvpdIdp = data.idp; - // this.mvpdUserId = data.userId; - // this.mvpdUUID = data.uuid; - // this.save(); - } else if (otherAuth === 'paramount') { - // this.paramountPlusUserId = data.userId; - // this.paramountPlusUUID = data.uuid; - // this.save(); + this.mvpdIdp = data.idp; + this.mvpdUserId = data.userId; + this.mvpdUUID = data.uuid; + } else if (otherAuth === 'prime') { + this.amazonPrimeUserId = data.userId; + this.amazonPrimeUUID = data.uuid; + } else if (otherAuth === 'peacock') { + this.peacockUserId = data.userId; + this.peacockUUID = data.uuid; } + this.save(); + await this.extendToken(); await this.extendTvToken(); } else { @@ -702,11 +846,13 @@ class NflHandler { tv_access_token, tv_expires_at, tv_refresh_token, - // paramountPlusUserId, - // paramountPlusUUID, - // mvpdIdp, - // mvpdUserId, - // mvpdUUID, + mvpdIdp, + mvpdUserId, + mvpdUUID, + amazonPrimeUserId, + amazonPrimeUUID, + peacockUserId, + peacockUUID, } = fsExtra.readJSONSync(path.join(configPath, 'nfl_tokens.json')); this.device_id = device_id; @@ -719,11 +865,13 @@ class NflHandler { this.uid = uid; // Supplemental Auth - // this.paramountPlusUserId = paramountPlusUserId; - // this.paramountPlusUUID = paramountPlusUUID; - // this.mvpdIdp = mvpdIdp; - // this.mvpdUserId = mvpdUserId; - // this.mvpdUUID = mvpdUUID; + this.mvpdIdp = mvpdIdp; + this.mvpdUserId = mvpdUserId; + this.mvpdUUID = mvpdUUID; + this.amazonPrimeUUID = amazonPrimeUUID; + this.amazonPrimeUserId = amazonPrimeUserId; + this.peacockUserId = peacockUserId; + this.peacockUUID = peacockUUID; } }; }