Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Import required modules
- const express = require('express');
- const mongoose = require('mongoose');
- const cors = require('cors');
- const path = require('path');
- const methodOverride = require('method-override');
- const request = require('request');
- const restream = require('restream');
- const app = express();
- app.set('host', 'sportswatcher.ddns.net');
- const fs = require('fs');
- const fetch = require('node-fetch');
- // Use middleware functions
- app.use(express.json()); // Parse JSON bodies
- app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies
- app.use(cors({ origin: '*', methods: ['GET', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['x-vsaas-session', 'x-no-redirect', 'origin', 'authorization', 'x-real-ip', 'accept', 'range'] })); // Enable CORS
- app.use(methodOverride('_method')); // Enable method override
- app.use(express.static(path.join(__dirname, 'public'))); // Serve static files from the public directory
- // Connect to MongoDB
- const connectionString = 'mongodb://localhost:27017/streaming'; // Replace with your MongoDB connection string
- mongoose.connect(connectionString, { useNewUrlParser: true, useUnifiedTopology: true })
- .then(() => {
- console.log('Connected to MongoDB');
- })
- .catch((error) => {
- console.error('Failed to connect to MongoDB', error);
- });
- // Define a schema and a model for Game
- const gameSchema = new mongoose.Schema({
- title: String,
- streamLink: String,
- sport: String,
- });
- const Game = mongoose.model('Game', gameSchema);
- // A function that returns a modified URL for .ts and .wbp files
- function getSegmentURL(targetURL) {
- const baseURL = getBaseURL();
- return baseURL + 'segment/' + targetURL.replace(/^https:\/\//, ''); // Use regex to only remove the protocol at the beginning of the URL
- }
- // A function that returns a modified URL for .m3u8 files
- function getPlaylistURL(targetURL) {
- const baseURL = getBaseURL();
- // Get the base path of the target URL
- const basePath = new URL(targetURL).origin + new URL(targetURL).pathname;
- return baseURL + 'proxy/' + targetURL.replace(/^https:\/\//, '') + '?base=' + encodeURIComponent(basePath); // Use regex to only remove the protocol at the beginning of the URL and add a query parameter for the base path
- }
- // A function that returns the base URL of the app
- function getBaseURL() {
- return `http://${app.get('host')}/`;
- }
- // Improved header handling for proxy requests
- function setProxyHeaders(originalReq, targetURL) {
- const headers = {
- 'host': new URL(targetURL).host,
- 'user-agent': originalReq.headers['user-agent'] || 'Mozilla/5.0',
- 'referer': new URL(targetURL).origin,
- };
- if (originalReq.headers['accept-encoding']) {
- headers['accept-encoding'] = originalReq.headers['accept-encoding'];
- }
- return headers;
- }
- async function handleM3U8Request(req, res, targetURL) {
- const proxyRequestOptions = {
- url: targetURL,
- headers: setProxyHeaders(req, targetURL),
- strictSSL: false,
- encoding: null // Use null encoding to get a buffer instead of a string
- };
- request(proxyRequestOptions, (error, response, body) => {
- if (error) {
- console.error('Failed to fetch m3u8:', error.message);
- return res.status(500).send('Proxy Error');
- }
- let m3u8Content = body.toString('utf8'); // Specify utf8 encoding to convert the buffer to a string
- // Fix for corrupted m3u8 files
- if (m3u8Content.includes('#EXT-X-MAP')) { // Check if the m3u8 file has an EXT-X-MAP tag
- const mapLineIndex = m3u8Content.indexOf('#EXT-X-MAP'); // Find the index of the map line
- const nextLineIndex = m3u8Content.indexOf('\n', mapLineIndex); // Find the index of the next line break after the map line
- const mapLine = m3u8Content.substring(mapLineIndex, nextLineIndex); // Extract the map line as a string
- const uriIndex = mapLine.indexOf('URI="'); // Find the index of the URI attribute in the map line
- const uriEndIndex = mapLine.indexOf('"', uriIndex + 5); // Find the index of the end quote of the URI attribute
- const mapURI = mapLine.substring(uriIndex + 5, uriEndIndex); // Extract the URI value as a string
- const absoluteMapURI = new URL(mapURI, targetURL).href; // Resolve the relative URI with the target URL
- const modifiedMapLine = mapLine.replace(mapURI, getSegmentURL(absoluteMapURI)); // Replace the original URI with the modified one
- m3u8Content = m3u8Content.replace(mapLine, modifiedMapLine); // Replace the original map line with the modified one
- }
- m3u8Content = m3u8Content.split('\n').map(line => {
- // Extract the default base path from the target URL
- const defaultBasePath = new URL(targetURL).origin + new URL(targetURL).pathname.substring(0, new URL(targetURL).pathname.lastIndexOf("/") + 1);
- const basePath = req.query.base || defaultBasePath;
- if (line.endsWith('.ts') || line.endsWith('.wbp') || line.endsWith('.m3u8')) {
- // If the line starts with 'http' or is absolute, use it directly, otherwise resolve with the base path
- const absoluteURL = line.startsWith('http') ? line : new URL(line, basePath).href;
- if (line.endsWith('.ts') || line.endsWith('.wbp')) {
- return getSegmentURL(absoluteURL);
- } else if (line.endsWith('.m3u8')) {
- return getPlaylistURL(absoluteURL);
- }
- }
- return line;
- }).join('\n');
- res.setHeader('Content-Type', 'application/x-mpegURL');
- res.setHeader('Access-Control-Allow-Origin', '*');
- res.send(m3u8Content);
- });
- }
- // A function that handles proxy requests for other files (.ts and .wbp)
- function handleOtherRequest(req, res, targetURL) {
- // Create a proxyRequestOptions object with the headers copied from the original request
- const proxyRequestOptions = {
- url: targetURL,
- headers: setProxyHeaders(req, targetURL), // Copy all headers from original request
- strictSSL: false, // This is not recommended for production, use it for debugging purposes
- encoding: null
- };
- const proxyRequest = request(proxyRequestOptions);
- proxyRequest.on('response', function(sourceResponse) {
- Object.keys(sourceResponse.headers).forEach(headerKey => {
- res.setHeader(headerKey, sourceResponse.headers[headerKey]);
- });
- let contentType = sourceResponse.headers['content-type'];
- if (!contentType || contentType === 'application/octet-stream') {
- const extname = path.extname(targetURL).toLowerCase();
- if (extname === '.m4s') {
- res.setHeader('Content-Type', 'application/dash+xml');
- } else if (extname === '.ts') {
- res.setHeader('Content-Type', 'video/MP2T');
- } else if (extname === '.wbp') {
- res.setHeader('Content-Type', 'video/webm'); // Assume webm format for .wbp files
- } else {
- res.setHeader('Content-Type', 'application/x-mpegURL');
- }
- }
- res.setHeader('Access-Control-Allow-Origin', '*');
- sourceResponse.pipe(res);
- });
- proxyRequest.on('error', function(err) {
- console.error('Proxy Error for URL:', targetURL, 'Error:', err.message);
- res.status(500).send('Proxy Error');
- });
- res.on('error', (err) => {
- console.error('Error during response pipe:', err);
- });
- req.pipe(proxyRequest);
- }
- // A route for handling proxy requests
- app.use('/proxy/', async (req, res, next) => {
- const fullURL = req.protocol + '://' + req.get('host') + req.originalUrl;
- const { pathname, search } = new URL(fullURL);
- console.log(`Received request for URL: ${fullURL}`);
- const targetURL = pathname.replace('/proxy/', 'https://') + search; // Check if the request is for an m3u8 file
- if (path.extname(targetURL).toLowerCase() === '.m3u8') {
- handleM3U8Request(req, res, targetURL);
- return;
- }
- // Handle other requests
- handleOtherRequest(req, res, targetURL);
- });
- // A route for handling segment requests
- app.use('/segment/*', (req, res) => {
- const fullURL = req.protocol + '://' + req.get('host') + req.originalUrl;
- console.log(`Received request for URL: ${fullURL}`);
- // Capture the segment path from the wildcard
- const segmentPath = req.params[0];
- // Construct the targetURL with the captured segment path
- const targetURL = 'https://' + segmentPath;
- // Handle the request
- handleOtherRequest(req, res, targetURL);
- });
- // A route for serving the main page
- app.get('/', (req, res) => {
- res.render('main.ejs');
- });
- // A route for serving the admin panel
- app.get('/admin', async (req, res) => {
- try {
- const games = await Game.find();
- res.render('admin.ejs', { videos: games });
- } catch (error) {
- res.status(500).send(error);
- }
- });
- // A route for getting all games
- app.get('/games', async (req, res) => {
- try {
- const games = await Game.find();
- res.send(games);
- } catch (error) {
- res.status(500).send(error);
- }
- });
- // A route for creating a new game
- app.post('/games', async (req, res) => {
- try {
- const game = new Game(req.body);
- await game.save();
- res.redirect('/admin');
- } catch (error) {
- res.status(500).send(error);
- }
- });
- // A route for updating a game
- app.put('/games/:id', async (req, res) => {
- try {
- const { id } = req.params;
- const { title, streamLink, sport } = req.body;
- await Game.findByIdAndUpdate(id, { title, streamLink, sport }, { new: true });
- res.redirect('/admin');
- } catch (error) {
- res.status(500).send(error);
- }
- });
- // A route for deleting a game
- app.delete('/games/:id', async (req, res) => {
- try {
- await Game.deleteOne({ _id: req.params.id });
- res.redirect('/admin');
- } catch (error) {
- res.status(500).send(error);
- }
- });
- // Start the server
- const port = process.env.PORT || 3000;
- app.listen(port, () => console.log(`Server running on port ${port}`));
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement