Initial commit

This commit is contained in:
Joan
2024-03-19 15:23:02 +01:00
commit 487c57597f
15 changed files with 3340 additions and 0 deletions

65
server_nm3u8dl/Dockerfile Normal file
View File

@@ -0,0 +1,65 @@
#Use image Ubuntu
FROM ubuntu:jammy
#Update and install dependencies
RUN apt-get update
RUN apt-get install -y wget
RUN apt-get install -y xz-utils
RUN apt-get install -y libicu-dev
RUN apt-get install -y ffmpeg
#Install Node.js y npm
RUN apt-get update && apt-get install -y curl && \
curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
apt-get install -y nodejs
#Install dependencies project
COPY package.json .
RUN npm install
#Working directory
WORKDIR /app
#Copy server.js to container
COPY ./server.js .
#COPY ./mp4decrypt /usr/local/bin/mp4decrypt
#Install FFMPEG ( ERROR DOWNLOAD )
#RUN wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz \
#&& tar -xf ffmpeg-release-amd64-static.tar.xz \
#&& cd ffmpeg-6.1-amd64-static/ \
#&& mv ffmpeg /usr/bin/ \
#&& mv ffprobe /usr/bin/
#Copy ffmpeg-release-amd64-static.tar.xz to container
#COPY ./lib/ffmpeg-release-amd64-static.tar.xz /tmp/
#Install FFMPEG
#RUN tar -xf /tmp/ffmpeg-release-amd64-static.tar.xz -C /tmp/ \
# && mv /tmp/ffmpeg-*/ffmpeg /usr/bin/ \
# && mv /tmp/ffmpeg-*/ffprobe /usr/bin/ \
# && rm -rf /tmp/ffmpeg-*
#Download and config N_m3u8DL-RE
RUN wget https://github.com/nilaoda/N_m3u8DL-RE/releases/download/v0.2.0-beta/N_m3u8DL-RE_Beta_linux-x64_20230628.tar.gz \
&& tar xf N_m3u8DL-RE_Beta_linux-x64_20230628.tar.gz --strip-components 1 \
&& chmod +x N_m3u8DL-RE \
&& mv N_m3u8DL-RE /usr/local/bin
#Download and config shaka-packager
RUN wget https://github.com/shaka-project/shaka-packager/releases/download/v2.6.1/packager-linux-x64 \
&& chmod +x packager-linux-x64 \
&& mv packager-linux-x64 /usr/local/bin
#Create temp directory and grant permission
RUN mkdir /tmp/ramdisk \
&& chmod 777 /tmp/ramdisk
#Config ENV RE_LIVE_PIPE_OPTIONS
ENV RE_LIVE_PIPE_OPTIONS="-c copy -f mpegts pipe:1"
#Expose internal port server
EXPOSE 8080
#run server
CMD ["sh", "-c", "node server.js"]

Binary file not shown.

BIN
server_nm3u8dl/mp4decrypt Executable file

Binary file not shown.

View File

@@ -0,0 +1,18 @@
{
"name": "ubuntu-n_m3u8dl",
"version": "1.0.0",
"description": "",
"main": "server.js",
"directories": {
"lib": "lib"
},
"scripts": {
"start": "node server.js"
},
"keywords": [],
"author": "",
"license": "",
"dependencies": {
"express": "^4.18.2"
}
}

201
server_nm3u8dl/server.js Normal file
View File

@@ -0,0 +1,201 @@
const express = require('express');
const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
const app = express();
const port = 8080;
const tmp_folder = '/tmp/ramdisk/';
let channelsPath = path.resolve(__dirname, 'channels.json');
let channels = JSON.parse(fs.readFileSync(channelsPath, 'utf8'));
const activeStreams = {}; // Store active stream processes with unique IDs
fs.watch(channelsPath, (eventType, filename) => {
if (eventType === 'change') {
console.log(`channels.json was updated, reloading...`);
try {
channels = JSON.parse(fs.readFileSync(channelsPath, 'utf8'));
} catch (error) {
console.log(error.message);
}
}
});
async function startStreamlink(channel, clientID) {
const useragent = channel.useragent;
const authorization = channel.authorization;
const referer = channel.referer;
const proxy = channel.proxy;
const key1 = channel.key1;
const key2 = channel.key2;
const key3 = channel.key3;
const key4 = channel.key4;
const key5 = channel.key5;
const audios = channel.audios;
const resolution = channel.resolution;
const delay = channel.delay;
let args = [
channel.url,
'--use-shaka-packager',
// '--log-level', 'INFO',
'--no-log',
// '--live-real-time-merge', 'true',
// '--mp4-real-time-decryption', 'true',
// '--live-wait-time', '1',
'--thread-count', '3',
'--concurrent-download',
'--live-pipe-mux',
'--live-keep-segments', 'false',
'--check-segments-count', 'false',
// '--live-take-count', '10',
'--tmp-dir', tmp_folder + `${channel.name}_${clientID}`, // Unique folder for each client
'--del-after-done', 'true'
];
if (useragent) {
args.splice(1, 0, '--header', `User-Agent: ${useragent}`);
} else {
args.splice(1, 0, '--header', `User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299`);
}
if (authorization) {
args.splice(1, 0, '--header', `Authorization: ${authorization}`);
}
if (referer) {
args.splice(1, 0, '--header', `Referer: ${referer}`);
}
if (proxy) {
args.splice(1, 0, '--custom-proxy', proxy);
}
if (key5) {
args.splice(1, 0, '--key', key5);
}
if (key4) {
args.splice(1, 0, '--key', key4);
}
if (key3) {
args.splice(1, 0, '--key', key3);
}
if (key2) {
args.splice(1, 0, '--key', key2);
}
if (key1) {
args.splice(1, 0, '--key', key1);
}
if (audios) {
args.splice(1, 0, '-sa', audios);
} else {
args.splice(1, 0, '-sa', 'best2');
}
if (resolution) {
args.splice(1, 0, '-sv', 'res=' + resolution);
} else {
args.splice(1, 0, '-sv', 'best');
}
if (delay) {
args.splice(1, 0, '--live-pipe-options', '-itsoffset ' + delay + ' -re -loglevel error {INPUTS} -c copy -f mpegts -fflags +genpts -shortest pipe:1');
}
console.log(`Args: ${args}`);
const streamlinkProcess = spawn('N_m3u8DL-RE', args);
const processID = `${channel.name}_${clientID}`;
activeStreams[processID] = streamlinkProcess;
return streamlinkProcess;
}
app.get('/stream/:channelName', async (req, res) => {
const channelName = req.params.channelName;
const clientID = req.ip;
const channel = channels[channelName];
console.log(`Received request for /stream/${channelName} from Client IP: ${clientID}`);
channels[channelName]['name'] = channelName;
if (!channel) {
res.status(404).send('Channel not found');
return;
}
console.log(`Starting Streamlink command for channel: ${channelName} (Client IP: ${clientID})`);
try {
let streamlinkProcess = await startStreamlink(channel, clientID);
res.setHeader('Content-Type', 'video/MP2T');
streamlinkProcess.stdout.pipe(res);
streamlinkProcess.stderr.on('data', data => {
console.error(`Streamlink: ${data}`);
});
streamlinkProcess.on('close', code => {
console.log(`Streamlink process exited with code ${code} for Client IP: ${clientID}`);
res.end();
const processID = `${channelName}_${clientID}`;
delete activeStreams[processID];
const tempFolderPath = tmp_folder + `${channel.name}_${clientID}`;
if (fs.existsSync(tempFolderPath)) {
fs.rmSync(tempFolderPath, { recursive: true });
}
logActiveConnections();
});
res.on('close', () => {
console.log(`Response closed, killing channel ${channel.name} for Client IP: ${clientID}`);
const processID = `${channelName}_${clientID}`;
if (activeStreams[processID]) {
activeStreams[processID].kill();
delete activeStreams[processID];
}
const tempFolderPath = tmp_folder + `${channel.name}_${clientID}`;
if (fs.existsSync(tempFolderPath)) {
fs.rmSync(tempFolderPath, { recursive: true });
}
logActiveConnections();
});
req.on('abort', () => {
console.log(`Request aborted, killing Streamlink process for Client IP: ${clientID}`);
const processID = `${channelName}_${clientID}`;
if (activeStreams[processID]) {
activeStreams[processID].kill();
delete activeStreams[processID];
}
const tempFolderPath = tmp_folder + `${channel.name}_${clientID}`;
if (fs.existsSync(tempFolderPath)) {
fs.rmSync(tempFolderPath, { recursive: true });
}
logActiveConnections();
});
logActiveConnections();
} catch (error) {
console.error(`Streamlink failed: ${error}`);
res.status(500).send('Streamlink failed');
}
});
function logActiveConnections() {
const activeConnections = Object.keys(activeStreams).length;
console.log(`Active connections: ${activeConnections}`);
}
app.listen(port, "0.0.0.0", () => {
console.log(`Server listening on port ${port}`);
});
process.on('SIGINT', () => {
for (const processID in activeStreams) {
if (activeStreams.hasOwnProperty(processID)) {
activeStreams[processID].kill();
delete activeStreams[processID];
}
}
process.exit();
});

View File

@@ -0,0 +1,104 @@
const express = require('express');
const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
let channelsPath = path.resolve(__dirname, 'channels.json');
let channels = JSON.parse(fs.readFileSync(channelsPath, 'utf8'));
fs.watch(channelsPath, (eventType, filename) => {
if (eventType === 'change') {
console.log(`channels.json was updated, reloading...`);
channels = JSON.parse(fs.readFileSync(channelsPath, 'utf8'));
}
});
const app = express();
const port = 8080;
const tmp_folder = '/tmp/ramdisk/mpegts'
async function startStreamlink(channel) {
const useragent = channel.useragent;
const authorization = channel.authorization;
const proxy = channel.proxy;
const key1 = channel.key1;
const key2 = channel.key2;
const key3 = channel.key3;
let resolution = channel.resolution;
resolution = 'res=' + resolution;
let args = [
channel.url,
'--header', `User-Agent:${useragent}`,
'--use-shaka-packager',
'--log-level', 'INFO',
'--live-real-time-merge',
'--live-pipe-mux',
'--live-keep-segments', 'false',
'-sv', 'best',
'-sa', 'best2',
'--tmp-dir', tmp_folder,
'--del-after-done', 'true'
];
if (authorization) {
args.splice(2, 0, '--header', `Authorization=${authorization}`);
}
if (proxy) {
args.splice(2, 0, '--custom-proxy', proxy);
}
if (key1) {
args.splice(4, 0, '--key', key1);
}
if (key2) {
args.splice(6, 0, '--key', key2);
}
if (key3) {
args.splice(6, 0, '--key', key3);
}
return spawn('N_m3u8DL-RE', args);
}
app.get('/stream/:channelName', async (req, res) => {
const channelName = req.params.channelName;
console.log(`Received request for /stream/${channelName}`);
const channel = channels[channelName];
if (!channel) {
res.status(404).send('Channel not found');
return;
}
console.log(`Starting Streamlink command for channel: ${channelName}`);
try {
let streamlinkProcess = await startStreamlink(channel);
res.setHeader('Content-Type', 'video/MP2T');
streamlinkProcess.stdout.pipe(res);
streamlinkProcess.stderr.on('data', data => {
console.error(`Streamlink: ${data}`);
});
streamlinkProcess.on('close', code => {
console.log(`Streamlink process exited with code ${code}`);
res.end();
});
res.on('close', () => {
console.log(`Response closed, killing Streamlink process`);
streamlinkProcess.kill();
if (fs.existsSync(tmp_folder)) {
fs.rmSync(tmp_folder, { recursive: true });
}
});
req.on('abort', () => {
console.log(`Request aborted, killing Streamlink process`);
streamlinkProcess.kill();
});
} catch (error) {
console.error(`Streamlink failed: ${error}`);
res.status(500).send('Streamlink failed');
}
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});