Skip to content

Commit

Permalink
Removed streamlink and just went to ffmpeg, added STREAM_RESOLUTION e…
Browse files Browse the repository at this point in the history
…nvironment variable, added user agent switching
  • Loading branch information
m0ngr31 committed Nov 2, 2022
1 parent be9db53 commit 3e0b984
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 16 deletions.
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
FROM alpine:3.16.2

RUN echo '@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories
RUN mkdir -p /etc/udhcpc ; echo 'RESOLV_CONF="no"' >> /etc/udhcpc/udhcpc.conf

RUN apk update && apk add nodejs npm streamlink@testing ffmpeg
RUN apk update && apk add nodejs npm ffmpeg

RUN pw="$(head -c 20 /dev/urandom | base64 | head -c 10)"; ( echo "$pw"; echo "$pw" ) | adduser abc

Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
<img src="https://i.imgur.com/FIGZdR3.png">
</p>

Current version: **2.0.0**
Current version: **2.0.0-beta2**

## About
This takes ESPN+ 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).
This takes ESPN+ 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).

#### Notes
* This was not made for pirating streams. This is made for using your own credentials and have a different presentation than the ESPN+ app currently gives.
Expand All @@ -31,7 +31,8 @@ The recommended way of running is to pull the image from [Docker Hub](https://hu
| Environment Variable | Description | Required? |
|---|---|---|
| START_CHANNEL | What the first channel number should be. Keep in mind this generates 100 channels to keep a healthy buffer. | No. If not set, the start channel will default to 1. |
| USE_ESPN3 | Set if your ISP is an [affiliate for ESPN3](https://www.espn.com/espn3/affList). If not, ESPN3 events will not work. (This is a beta feature. It doesn't work currently) | No |
| STREAM_RESOLUTION | What stream resolution to use. Valid options are `720p60`, `720p`, and `540p` | No. If not set, `720p60` is the default. |
| USE_ESPN3 | Set if your ISP is an [affiliate for ESPN3](https://www.espn.com/espn3/affList). (THIS WILL SCHEDULE ESPN3 EVENTS BUT PLAYBACK IS NOT SUPPORTED YET) | No |


#### Volumes
Expand Down
5 changes: 2 additions & 3 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ const notFound = (_req, res) => res.status(404).send('404 not found');
const shutDown = () => {
try {
execSync('killall ffmpeg');
execSync('killall streamlink');
} catch (e) {}

process.exit(0);
Expand Down Expand Up @@ -216,7 +215,7 @@ setInterval(async () => {
await schedule();
}, 1000 * 60 * 60 * 4);

// Check for updated refresh tokens every hour
// Check for updated refresh tokens 30 minutes
setInterval(async () => {
await espnHandler.refreshTokens();
}, 1000 * 60 * 60);
}, 1000 * 60 * 30);
4 changes: 3 additions & 1 deletion services/espn-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import jwt_decode from 'jwt-decode';
import _ from 'lodash';
import url from 'url';

import { getUserAgent } from './user-agent';

global.WebSocket = ws;

const configPath = path.join(process.cwd(), 'config');
Expand Down Expand Up @@ -250,7 +252,7 @@ class EspnHandler {
headers: {
Authorization: this.account_token.access_token,
Accept: 'application/vnd.media-service+json; version=2',
'User-Agent': 'User-Agent Mozilla/5.0 (Windows NT 10.0; rv:105.0) Gecko/20100101 Firefox/105.0',
'User-Agent': getUserAgent(),
Origin: 'https://plus.espn.com',
}
});
Expand Down
22 changes: 21 additions & 1 deletion services/launch-channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ import { tmpPath } from './init-directories';
import { sleep } from './sleep';
import { espnHandler } from './espn-handler';
import { killChildren } from './kill-processes';
import { getUserAgent } from './user-agent';

const VALID_RESOLUTIONS = [
'720p60',
'720p',
'540p',
];

const getStreamProfile = () => {
const setProfile = _.includes(VALID_RESOLUTIONS, process.env.STREAM_RESOLUTION) ? process.env.STREAM_RESOLUTION : '720p60';

switch (setProfile) {
case '720p60':
return '0:32?';
case '720p':
return '0:24?';
default:
return '0:20?';
}
};

let checkingStream = {};

Expand Down Expand Up @@ -39,7 +59,7 @@ const startChannelStream = async (channelId: string, appStatus, appUrl) => {
fs.writeFileSync(path.join(tmpPath, `${channelId}/${channelId}.m3u8`), currentM3u8, 'utf8');

const out = fs.openSync(path.join(tmpPath, `${channelId}-log.txt`), 'a');
const child = spawn(path.join(process.cwd(), 'stream_channel.sh'), [], {env: {CHANNEL: channelId, URL: url, AUTH_TOKEN: authToken, APP_URL: appUrl}, detached: true, stdio: ['ignore', out, out]});
const child = spawn(path.join(process.cwd(), 'stream_channel.sh'), [], {env: {CHANNEL: channelId, URL: url, AUTH_TOKEN: authToken, APP_URL: appUrl, STREAM_PROFILE: getStreamProfile(), USER_AGENT: getUserAgent()}, detached: true, stdio: ['ignore', out, out]});

appStatus.channels[channelId].pid = child.pid;

Expand Down
11 changes: 11 additions & 0 deletions services/user-agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const getUserAgent = () => userAgents[Math.floor(Math.random() * userAgents.length)];

const userAgents = [
'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:106.0) Gecko/20100101 Firefox/106.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 13.0; rv:106.0) Gecko/20100101 Firefox/106.0',
'Mozilla/5.0 (X11; Linux i686; rv:106.0) Gecko/20100101 Firefox/106.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
];
17 changes: 11 additions & 6 deletions stream_channel.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
#!/bin/sh

streamlink \
--http-header "Authorization=${AUTH_TOKEN}" \
"hlsvariant://${URL}" \
--default-stream "720p_alt2,best" \
--stdout \
| ffmpeg -i pipe:0 -c copy -hls_base_url "${APP_URL}/channels/${CHANNEL}/" -hls_flags append_list+omit_endlist -hls_segment_filename tmp/eplustv/${CHANNEL}/%09d.ts tmp/eplustv/${CHANNEL}/${CHANNEL}.m3u8
ffmpeg \
-user_agent "${USER_AGENT}" \
-headers "Authorization: ${AUTH_TOKEN}" \
-protocol_whitelist file,http,https,tcp,tls,crypto \
-i "${URL}" \
-tune zerolatency \
-map ${STREAM_PROFILE} \
-c copy \
-hls_base_url "${APP_URL}/channels/${CHANNEL}/" \
-hls_flags append_list+omit_endlist \
-hls_segment_filename tmp/eplustv/${CHANNEL}/%09d.ts tmp/eplustv/${CHANNEL}/${CHANNEL}.m3u8

0 comments on commit 3e0b984

Please sign in to comment.