Skip to content

Commit

Permalink
Redid a lot of stuff - enabled linear feeds, changed MLB auth, fixed NFL
Browse files Browse the repository at this point in the history
  • Loading branch information
m0ngr31 committed Sep 10, 2024
1 parent f826f3b commit 145828c
Show file tree
Hide file tree
Showing 20 changed files with 568 additions and 361 deletions.
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<img src="https://i.imgur.com/FIGZdR3.png">
</p>

Current version: **2.3.0**
Current version: **3.0.0**

# About
This takes ESPN/ESPN+, FOX Sports, Paramount+, MSG+, NFL+, B1G+, 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).
Expand All @@ -18,6 +18,8 @@ The server exposes 2 main endpoints:
|---|---|
| /channels.m3u | The channel list you'll import into your client |
| /xmltv.xml | The schedule that you'll import into your client |
| /linear-channels.m3u | The linear channel list you'll import into your client (only useed with LINEAR_CHANNELS) |
| /linear-xmltv.xml | The linear schedule that you'll import into your client (only useed with LINEAR_CHANNELS)|

# Running
The recommended way of running is to pull the image from [Docker Hub](https://hub.docker.com/r/m0ngr31/eplustv).
Expand All @@ -27,6 +29,7 @@ The recommended way of running is to pull the image from [Docker Hub](https://hu
|---|---|---|---|
| START_CHANNEL | What the first channel number should be. | No | 1 |
| NUM_OF_CHANNELS | How many channels to create? This is dependent on the networks you are using. A good number to start with is >= 200 if you are using ESPN+. | No | 200 |
| LINEAR_CHANNELS | Break out dedicated linear channels (see Endpoints above to use) | No | False |
| PROXY_SEGMENTS | Proxy keyed `*.ts` files. | No | False |
| PUID | Current user ID. Use if you have permission issues. Needs to be combined with PGID. | No | - |
| PGID | Current group ID. Use if you have permission issues. Needs to be combined with PUID. | No | - |
Expand All @@ -39,24 +42,22 @@ Use if you would like to login with a TV provider or ESPN+ and access various ES
| Environment Variable | Description | Default |
|---|---|---|
| ESPNPLUS | Set to false if you don't want to use ESPN+ | True |
| ESPN | ESPN: Set if your TV provider supports it | False |
| ESPN2 | ESPN2: Set if your TV provider supports it | False |
| ESPN*** | ESPN: Set if your TV provider supports it | False |
| ESPN2*** | ESPN2: Set if your TV provider supports it | False |
| ESPN3 | ESPN2: Set if your TV provider supports it | False |
| ESPNU | ESPNU: Set if your TV provider supports it | False |
| SEC | SEC Network: Set if your TV provider supports it | False |
| ESPNU*** | ESPNU: Set if your TV provider supports it | False |
| SEC*** | SEC Network: Set if your TV provider supports it | False |
| SECPLUS | SEC Network+: Set if your TV provider supports it | False |
| ACCN | ACCN: Set if your TV provider supports it | False |
| ACCN*** | ACCN: Set if your TV provider supports it | False |
| ACCNX | ACCNX: Set if your TV provider supports it | False |
| LONGHORN | Longhorn Network: Set if your TV provider supports it | False |
| ESPNEWS | ESPNews: Set if your TV provider supports it | False |
| ESPNEWS*** | ESPNews: Set if your TV provider supports it | False |
| ESPN_PPV | PPV: Set if you have purchased PPV events | False |

#### FOX Sports
Use if you would like to login with a TV provider and access various FOX Sports events
| Environment Variable | Description | Required? | Default |
|---|---|---|---|
| FOXSPORTS | Set if your TV provider supports it | No | False |
| FOXSPORTS_ALLOW_REPLAYS | If you would like to schedule events that aren't live | No | False |
| FOXSPORTS** | Set if your TV provider supports it | No | False |
| MAX_RESOLUTION | Max resolution to use. Valid options are `UHD/HDR`, `UHD/SDR`, and `720p` (Some events don't offer 4K and will attempt to play the highest framerate available for selected resolution). | No | UHD/SDR |
| FOX_ONLY_4K | Only grab 4K events | No | False |

Expand All @@ -65,12 +66,16 @@ Use if you would like to login with Paramount+
| Environment Variable | Description | Required? | Default |
|---|---|---|---|
| PARAMOUNTPLUS | Set if you would like CBS Sports events | False | False |
| CBSSPORTSHQ* | Set if you would like the CBS Sports HQ channel (only available with LINEAR_CHANNELS) | False | False |
| 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+
| 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 |
| NFLREDZONE*** | Set if you would like NFL+ events | False | False |

#### B1G+
Use if you would like to login with your B1G+ account
Expand Down Expand Up @@ -99,10 +104,16 @@ Use if you would like to login with your MLB.tv account
| Environment Variable | Description | Default |
|---|---|---|
| MLBTV | Set if you would like to use MLB.tv | False |
| MLBTV_USER | MLB.tv Username | False |
| MLBTV_PASS | MLB.tv Password | False |
| MLBTV_ONLY_FREE | Only schedule free games | False |


### Notes
`*`: Dedicated linear channel - Will only schedule when `LINEAR_CHANNELS` is set

`**`: Some events are on linear channels and some aren't. If you use `LINEAR_CHANNELS`, only events that are on FOX will be scheduled normally. All other events will be scheduled to linear channels

`***`: Will create a dedicated linear channel if requested, if not, will schedule events normally

## Volumes
| Volume Name | Description | Required? |
|---|---|---|
Expand Down
43 changes: 40 additions & 3 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {msgHandler} from './services/msg-handler';
import {cleanEntries, removeChannelStatus} from './services/shared-helpers';
import {appStatus} from './services/app-status';
import {SERVER_PORT} from './services/port';
import {useLinear} from './services/channels';

import {version} from './package.json';

Expand Down Expand Up @@ -64,6 +65,25 @@ app.get('/channels.m3u', (req, res) => {
res.end(m3uFile, 'utf-8');
});

app.get('/linear-channels.m3u', (req, res) => {
if (!useLinear) {
notFound(req, res);
return;
}

const m3uFile = generateM3u(`${req.protocol}://${req.headers.host}`, true);

if (!m3uFile) {
notFound(req, res);
return;
}

res.writeHead(200, {
'Content-Type': 'application/x-mpegurl',
});
res.end(m3uFile, 'utf-8');
});

app.get('/xmltv.xml', async (req, res) => {
const xmlFile = await generateXml();

Expand All @@ -78,16 +98,33 @@ app.get('/xmltv.xml', async (req, res) => {
res.end(xmlFile, 'utf-8');
});

app.get('/linear-xmltv.xml', async (req, res) => {
if (!useLinear) {
notFound(req, res);
return;
}

const xmlFile = await generateXml(true);

if (!xmlFile) {
notFound(req, res);
return;
}

res.writeHead(200, {
'Content-Type': 'application/xml',
});
res.end(xmlFile, 'utf-8');
});

app.get('/channels/:id.m3u8', async (req, res) => {
const {id} = req.params;

let contents = null;

// Channel data needs initial object
if (!appStatus.channels[id]) {
appStatus.channels[id] = {
heartbeat: new Date(),
};
appStatus.channels[id] = {};
}

const uri = `${req.protocol}://${req.headers.host}`;
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eplustv",
"version": "2.3.0",
"version": "3.0.0",
"description": "",
"scripts": {
"start": "ts-node index.ts",
Expand Down
2 changes: 1 addition & 1 deletion services/b1g-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ class B1GHandler {
'&language=en',
`&metadata_id=${encodeURIComponent('159283,167702')}`,
`&date_time_from=${encodeURIComponent(moment().format())}`,
`&date_time_to=${encodeURIComponent(moment().add(3, 'days').format())}`,
`&date_time_to=${encodeURIComponent(moment().add(2, 'days').format())}`,
page > 1 ? `&page=${page}` : '',
].join('');

Expand Down
30 changes: 29 additions & 1 deletion services/build-schedule.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {NUM_OF_CHANNELS, START_CHANNEL} from './channels';
import {NUM_OF_CHANNELS, START_CHANNEL, useLinear} from './channels';
import {db, IDocument} from './database';
import {IChannel, IEntry} from './shared-interfaces';

Expand Down Expand Up @@ -32,6 +32,34 @@ const scheduleEntry = async (entry: IEntry & IDocument, startChannel: number): P
};

export const scheduleEntries = async (): Promise<void> => {
let needReschedule = false;

if (!useLinear) {
const linearEntries = await db.entries.count({linear: {$exists: true}});

if (linearEntries > 0) {
needReschedule = true;
}
}

if (needReschedule) {
console.log('');
console.log('====================================================================');
console.log('=== ===');
console.log('=== Need to rebuild the schedule because the LINEAR_CHANNELS ===');
console.log('=== variable is no longer being used. ===');
console.log('=== ===');
console.log('====================================================================');
console.log('=== THIS WILL BREAK SCHEDULED RECORDINGS IN YOUR DVR SOFTWARE ===');
console.log('====================================================================');
console.log('');

await db.schedule.remove({}, {multi: true});
await db.entries.update<IEntry>({}, {$unset: {channel: true, linear: true}}, {multi: true});

return await scheduleEntries();
}

const unscheduledEntries = await db.entries.find<IEntry>({channel: {$exists: false}}).sort({start: 1});

unscheduledEntries.length > 0 && console.log(`Scheduling ${unscheduledEntries.length} entries...`);
Expand Down
128 changes: 128 additions & 0 deletions services/channels.ts

Large diffs are not rendered by default.

4 changes: 0 additions & 4 deletions services/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {configPath} from './config';

export const entriesDb = path.join(configPath, 'entries.db');
export const scheduleDb = path.join(configPath, 'schedule.db');
export const linearDb = path.join(configPath, 'linear.db');

export interface IDocument {
_id: string;
Expand All @@ -15,9 +14,6 @@ export interface IDocument {
export const db = {
entries: Datastore.create(entriesDb),
schedule: Datastore.create(scheduleDb),
...(fs.existsSync(linearDb) && {
linear: Datastore.create(linearDb),
}),
};

export const initializeEntries = (): void => fs.writeFileSync(entriesDb, '');
Expand Down
25 changes: 13 additions & 12 deletions services/espn-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
useEspn2,
useEspn3,
useEspnU,
useLonghorn,
useSec,
useSecPlus,
useEspnPpv,
Expand All @@ -30,6 +29,7 @@ import {IAdobeAuth, isAdobeTokenValid, willAdobeTokenExpire, createAdobeAuthHead
import {getRandomHex} from './shared-helpers';
import {IEntry, IHeaders, IJWToken} from './shared-interfaces';
import {db} from './database';
import {useLinear} from './channels';

global.WebSocket = ws;

Expand Down Expand Up @@ -141,6 +141,8 @@ const BAM_API_KEY = 'ZXNwbiZicm93c2VyJjEuMC4w.ptUt7QxsteaRruuPmGZFaJByOoqKvDP2a5
const BAM_APP_CONFIG =
'https://bam-sdk-configs.bamgrid.com/bam-sdk/v2.0/espn-a9b93989/browser/v3.4/linux/chrome/prod.json';

const LINEAR_NETWORKS = ['espn1', 'espn2', 'espnu', 'sec', 'acc', 'espnews'];

const urlBuilder = (endpoint: string, provider: string) =>
`${DISNEY_ROOT_URL}${endpoint}`.replace('{id-provider}', provider);

Expand Down Expand Up @@ -246,9 +248,6 @@ const getNetworkInfo = (network?: string) => {
} else if (network === 'accnx') {
networks = '["9f538e0b-a896-3325-a417-79034e03a248"]';
packages = 'null';
} else if (network === 'longhorn') {
networks = '["5c1fd0f3-1022-3bc4-8af9-f785847baaf9"]';
packages = 'null';
} else if (network === 'espnews') {
networks = '["1e760a1c-c204-339d-8317-8e615c9cc0e0"]';
packages = 'null';
Expand Down Expand Up @@ -279,9 +278,15 @@ const parseAirings = async events => {
const entryExists = await db.entries.findOne<IEntry>({id: event.id});

if (!entryExists) {
const isLinear = useLinear && event.network?.id && LINEAR_NETWORKS.find(n => n === event.network?.id);

const start = moment(event.startDateTime);
const end = moment(event.startDateTime).add(event.duration, 'seconds').add(1, 'hour');

if (!isLinear) {
end.add(1, 'hour');
}

if (end.isBefore(now)) {
continue;
}
Expand All @@ -301,6 +306,10 @@ const parseAirings = async events => {
sport: event.subcategory?.name,
start: start.valueOf(),
url: event.source?.url,
...(isLinear && {
channel: event.network?.id,
linear: true,
}),
});
}
}
Expand Down Expand Up @@ -403,10 +412,6 @@ class EspnHandler {
const liveEntries = await this.getLiveEvents('accnx');
entries = [...entries, ...liveEntries];
}
if (useLonghorn) {
const liveEntries = await this.getLiveEvents('longhorn');
entries = [...entries, ...liveEntries];
}
if (useEspnews) {
const liveEntries = await this.getLiveEvents('espnews');
entries = [...entries, ...liveEntries];
Expand Down Expand Up @@ -461,10 +466,6 @@ class EspnHandler {
const upcomingEntries = await this.getUpcomingEvents(date.format('YYYY-MM-DD'), 'accnx');
entries = [...entries, ...upcomingEntries];
}
if (useLonghorn) {
const upcomingEntries = await this.getUpcomingEvents(date.format('YYYY-MM-DD'), 'longhorn');
entries = [...entries, ...upcomingEntries];
}
if (useEspnews) {
const upcomingEntries = await this.getUpcomingEvents(date.format('YYYY-MM-DD'), 'espnews');
entries = [...entries, ...upcomingEntries];
Expand Down
2 changes: 1 addition & 1 deletion services/flo-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class FloSportsHandler {
const events: IFloEvent[] = [];
const limit = 100;

const endSchedule = moment().add(3, 'days');
const endSchedule = moment().add(2, 'days');

while (hasNextPage) {
const url = [
Expand Down
Loading

0 comments on commit 145828c

Please sign in to comment.