Skip to content

Commit

Permalink
Fixed issue with NBC Sports not getting URLs properly. Changed how I'…
Browse files Browse the repository at this point in the history
…m handling manifests for FOX Sports 4K events. Removed option for slate.
  • Loading branch information
m0ngr31 committed Jan 15, 2023
1 parent d9aa2fb commit d0c36ec
Show file tree
Hide file tree
Showing 25 changed files with 383 additions and 310 deletions.
3 changes: 1 addition & 2 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.0.1**
Current version: **2.0.2**

# About
This takes ESPN/ESPN+, FOX Sports, and NBC Sports 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), [Channels](https://getchannels.com), or [xTeVe](https://github.com/xteve-project/xTeVe).
Expand All @@ -29,7 +29,6 @@ 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. If not set, the start channel will default to 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 > 100 if you are using ESPN+. | No. If not set, it will default to 150. |
| USE_SLATE | Play a short video with text while event is loading. This doesn't work in all players so use with caution! | No. This is off by default. |
| 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 |
| MAX_RESOLUTION | Max resolution to use. Valid options are `UHD/HDR`, `UHD/SDR`, `1080p`, `720p`, and `540p` (Some providers don't offer 4K or 1080p and will attempt to play the highest framerate available for selected resolution). | If not set, `UHD/SDR` is the default. |
Expand Down
97 changes: 49 additions & 48 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import express from 'express';
import fs from 'fs';
import path from 'path';
import _ from 'lodash';

import {generateM3u} from './services/generate-m3u';
import {getSlate, USE_SLATE} from './services/stream-slate';
import {initDirectories} from './services/init-directories';
import {generateXml} from './services/generate-xmltv';
import {launchChannel} from './services/launch-channel';
import {getEventSchedules} from './services/get-espn-events';
import {getEspnEventSchedules} from './services/get-espn-events';
import {scheduleEntries} from './services/build-schedule';
import {espnHandler} from './services/espn-handler';
import {foxHandler} from './services/fox-handler';
Expand All @@ -25,7 +22,7 @@ const shutDown = () => process.exit(0);

const schedule = async () => {
console.log('=== Getting events ===');
await getEventSchedules();
await getEspnEventSchedules();
await getFoxEventSchedules();
await getNbcEventSchedules();
console.log('=== Done getting events ===');
Expand Down Expand Up @@ -70,42 +67,56 @@ app.get('/channels/:id.m3u8', async (req, res) => {

let contents = null;

// Channel heatbeat
// Channel heatbeat needs initial object
if (!appStatus.channels[id]) {
appStatus.channels[id] = {};
}

appStatus.channels[id].heartbeat = new Date().valueOf();

const uri = `${req.protocol}://${req.headers.host}`;

if (USE_SLATE) {
if (!appStatus.channels[id].player?.m3u8) {
contents = getSlate(uri);

// Start stream
launchChannel(id, uri);
} else {
contents = appStatus.channels[id].player?.m3u8;
}
} else {
if (!appStatus.channels[id].player?.m3u8) {
try {
await launchChannel(id, uri);
} catch (e) {}
}

if (!appStatus.channels[id].player?.playlist) {
try {
contents = appStatus.channels[id].player?.m3u8;
await launchChannel(id, uri);
} catch (e) {}
}

if (!contents) {
console.log(
`Could not get a playlist for channel #${id}. Please make sure there is an event scheduled and you have access to it.`,
);
notFound(req, res);
return;
}
try {
contents = appStatus.channels[id].player?.playlist;
} catch (e) {}

if (!contents) {
console.log(
`Could not get a playlist for channel #${id}. Please make sure there is an event scheduled and you have access to it.`,
);
notFound(req, res);
return;
}

res.writeHead(200, {
'Cache-Control': 'no-cache',
'Content-Type': 'application/vnd.apple.mpegurl',
});
res.end(contents, 'utf-8');
});

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

let contents = null;

if (!appStatus.channels[id].player?.playlist) {
notFound(req, res);
return;
}

try {
contents = await appStatus.channels[id].player.cacheChunklist(chunklistid);
} catch (e) {}

if (!contents) {
console.log(`Could not get chunklist for channel #${id}.`);
notFound(req, res);
return;
}

res.writeHead(200, {
Expand Down Expand Up @@ -142,20 +153,11 @@ app.get('/channels/:id/:part.ts', async (req, res) => {
const {id, part} = req.params;
let contents;

const isSlate = id === 'starting';

if (isSlate) {
const fileStr = `slate/${id}/${part}.ts`;
const filename = path.join(process.cwd(), fileStr);

contents = fs.readFileSync(filename);
} else {
try {
contents = await appStatus.channels[id].player.getSegmentOrKey(part);
} catch (e) {
notFound(req, res);
return;
}
try {
contents = await appStatus.channels[id].player.getSegmentOrKey(part);
} catch (e) {
notFound(req, res);
return;
}

if (!contents) {
Expand All @@ -177,7 +179,7 @@ process.on('SIGTERM', shutDown);
process.on('SIGINT', shutDown);

(async () => {
console.log(`=== EPlusTV v${version} starting ===`);
console.log(`=== E+TV v${version} starting ===`);
initDirectories();

await espnHandler.initialize();
Expand Down Expand Up @@ -207,7 +209,6 @@ setInterval(() => {
if (now - val.heartbeat > 120 * 1000) {
if (val.player) {
console.log('Killing unwatched channel: ', key);
val.player.stop();
}
val.current = null;
val.player = null;
Expand Down
38 changes: 25 additions & 13 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eplustv",
"version": "2.0.1",
"version": "2.0.2",
"description": "",
"main": "index.js",
"scripts": {
Expand All @@ -12,7 +12,7 @@
"author": "Joe Ipson <[email protected]>",
"license": "MIT",
"dependencies": {
"axios": "^0.27.2",
"axios": "^1.2.2",
"dynamic-hls-proxy": "^1.1.21",
"express": "^4.17.1",
"fs-extra": "^10.0.0",
Expand Down
4 changes: 2 additions & 2 deletions services/adobe-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export const willAdobeTokenExpire = (token?: IAdobeAuth): boolean => {

try {
const parsedExp = parseInt(token.expires, 10);
// Will the token expire in the next hour?
return new Date().valueOf() + 3600 * 1000 > new Date(parsedExp).valueOf();
// Will the token expire in the next day?
return new Date().valueOf() + 3600 * 1000 * 24 > new Date(parsedExp).valueOf();
} catch (e) {
return true;
}
Expand Down
79 changes: 0 additions & 79 deletions services/cache-layer.ts

This file was deleted.

Loading

0 comments on commit d0c36ec

Please sign in to comment.