Advertisement
samipote

Untitled

Aug 26th, 2023
1,162
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Import required modules
  2. const express = require('express');
  3. const mongoose = require('mongoose');
  4. const cors = require('cors');
  5. const path = require('path');
  6. const methodOverride = require('method-override');
  7. const request = require('request');
  8. const restream = require('restream');
  9. const app = express();
  10. app.set('host', 'sportswatcher.ddns.net');
  11. const fs = require('fs');
  12. const fetch = require('node-fetch');
  13.  
  14. // Use middleware functions
  15. app.use(express.json()); // Parse JSON bodies
  16. app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies
  17. 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
  18. app.use(methodOverride('_method')); // Enable method override
  19. app.use(express.static(path.join(__dirname, 'public'))); // Serve static files from the public directory
  20.  
  21. // Connect to MongoDB
  22. const connectionString = 'mongodb://localhost:27017/streaming'; // Replace with your MongoDB connection string
  23. mongoose.connect(connectionString, { useNewUrlParser: true, useUnifiedTopology: true })
  24.   .then(() => {
  25.     console.log('Connected to MongoDB');
  26.   })
  27.   .catch((error) => {
  28.     console.error('Failed to connect to MongoDB', error);
  29.   });
  30.  
  31. // Define a schema and a model for Game
  32. const gameSchema = new mongoose.Schema({
  33.   title: String,
  34.   streamLink: String,
  35.   sport: String,
  36. });
  37. const Game = mongoose.model('Game', gameSchema);
  38.  
  39. // A function that returns a modified URL for .ts and .wbp files
  40. function getSegmentURL(targetURL) {
  41.   const baseURL = getBaseURL();
  42.   return baseURL + 'segment/' + targetURL.replace(/^https:\/\//, ''); // Use regex to only remove the protocol at the beginning of the URL
  43. }
  44.  
  45. // A function that returns a modified URL for .m3u8 files
  46. function getPlaylistURL(targetURL) {
  47.   const baseURL = getBaseURL();
  48.   // Get the base path of the target URL
  49.   const basePath = new URL(targetURL).origin + new URL(targetURL).pathname;
  50.   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
  51. }
  52.  
  53. // A function that returns the base URL of the app
  54. function getBaseURL() {
  55.   return `http://${app.get('host')}/`;
  56. }
  57.  
  58. // Improved header handling for proxy requests
  59. function setProxyHeaders(originalReq, targetURL) {
  60.   const headers = {
  61.     'host': new URL(targetURL).host,
  62.     'user-agent': originalReq.headers['user-agent'] || 'Mozilla/5.0',
  63.     'referer': new URL(targetURL).origin,
  64.   };
  65.   if (originalReq.headers['accept-encoding']) {
  66.     headers['accept-encoding'] = originalReq.headers['accept-encoding'];
  67.   }
  68.   return headers;
  69. }
  70.  
  71. async function handleM3U8Request(req, res, targetURL) {
  72.   const proxyRequestOptions = {
  73.     url: targetURL,
  74.     headers: setProxyHeaders(req, targetURL),
  75.     strictSSL: false,
  76.     encoding: null // Use null encoding to get a buffer instead of a string
  77.   };
  78.   request(proxyRequestOptions, (error, response, body) => {
  79.     if (error) {
  80.       console.error('Failed to fetch m3u8:', error.message);
  81.       return res.status(500).send('Proxy Error');
  82.     }
  83.     let m3u8Content = body.toString('utf8'); // Specify utf8 encoding to convert the buffer to a string
  84.  
  85.     // Fix for corrupted m3u8 files
  86.     if (m3u8Content.includes('#EXT-X-MAP')) { // Check if the m3u8 file has an EXT-X-MAP tag
  87.       const mapLineIndex = m3u8Content.indexOf('#EXT-X-MAP'); // Find the index of the map line
  88.       const nextLineIndex = m3u8Content.indexOf('\n', mapLineIndex); // Find the index of the next line break after the map line
  89.       const mapLine = m3u8Content.substring(mapLineIndex, nextLineIndex); // Extract the map line as a string
  90.       const uriIndex = mapLine.indexOf('URI="'); // Find the index of the URI attribute in the map line
  91.       const uriEndIndex = mapLine.indexOf('"', uriIndex + 5); // Find the index of the end quote of the URI attribute
  92.       const mapURI = mapLine.substring(uriIndex + 5, uriEndIndex); // Extract the URI value as a string
  93.       const absoluteMapURI = new URL(mapURI, targetURL).href; // Resolve the relative URI with the target URL
  94.       const modifiedMapLine = mapLine.replace(mapURI, getSegmentURL(absoluteMapURI)); // Replace the original URI with the modified one
  95.       m3u8Content = m3u8Content.replace(mapLine, modifiedMapLine); // Replace the original map line with the modified one
  96.     }
  97.  
  98.     m3u8Content = m3u8Content.split('\n').map(line => {
  99.       // Extract the default base path from the target URL
  100.       const defaultBasePath = new URL(targetURL).origin + new URL(targetURL).pathname.substring(0, new URL(targetURL).pathname.lastIndexOf("/") + 1);
  101.       const basePath = req.query.base || defaultBasePath;
  102.       if (line.endsWith('.ts') || line.endsWith('.wbp') || line.endsWith('.m3u8')) {
  103.         // If the line starts with 'http' or is absolute, use it directly, otherwise resolve with the base path
  104.         const absoluteURL = line.startsWith('http') ? line : new URL(line, basePath).href;
  105.         if (line.endsWith('.ts') || line.endsWith('.wbp')) {
  106.           return getSegmentURL(absoluteURL);
  107.         } else if (line.endsWith('.m3u8')) {
  108.           return getPlaylistURL(absoluteURL);
  109.         }
  110.       }
  111.       return line;
  112.     }).join('\n');
  113.     res.setHeader('Content-Type', 'application/x-mpegURL');
  114.     res.setHeader('Access-Control-Allow-Origin', '*');
  115.     res.send(m3u8Content);
  116.   });
  117. }
  118.  
  119. // A function that handles proxy requests for other files (.ts and .wbp)
  120. function handleOtherRequest(req, res, targetURL) {
  121.   // Create a proxyRequestOptions object with the headers copied from the original request
  122.   const proxyRequestOptions = {
  123.     url: targetURL,
  124.     headers: setProxyHeaders(req, targetURL), // Copy all headers from original request
  125.     strictSSL: false, // This is not recommended for production, use it for debugging purposes
  126.     encoding: null
  127.   };
  128.   const proxyRequest = request(proxyRequestOptions);
  129.   proxyRequest.on('response', function(sourceResponse) {
  130.     Object.keys(sourceResponse.headers).forEach(headerKey => {
  131.       res.setHeader(headerKey, sourceResponse.headers[headerKey]);
  132.     });
  133.     let contentType = sourceResponse.headers['content-type'];
  134.     if (!contentType || contentType === 'application/octet-stream') {
  135.       const extname = path.extname(targetURL).toLowerCase();
  136.       if (extname === '.m4s') {
  137.         res.setHeader('Content-Type', 'application/dash+xml');
  138.       } else if (extname === '.ts') {
  139.         res.setHeader('Content-Type', 'video/MP2T');
  140.       } else if (extname === '.wbp') {
  141.         res.setHeader('Content-Type', 'video/webm'); // Assume webm format for .wbp files
  142.       } else {
  143.         res.setHeader('Content-Type', 'application/x-mpegURL');
  144.       }
  145.     }
  146.     res.setHeader('Access-Control-Allow-Origin', '*');
  147.     sourceResponse.pipe(res);
  148.   });
  149.   proxyRequest.on('error', function(err) {
  150.     console.error('Proxy Error for URL:', targetURL, 'Error:', err.message);
  151.     res.status(500).send('Proxy Error');
  152.   });
  153.   res.on('error', (err) => {
  154.     console.error('Error during response pipe:', err);
  155.   });
  156.   req.pipe(proxyRequest);
  157. }
  158.  
  159. // A route for handling proxy requests
  160. app.use('/proxy/', async (req, res, next) => {
  161.   const fullURL = req.protocol + '://' + req.get('host') + req.originalUrl;
  162.   const { pathname, search } = new URL(fullURL);
  163.   console.log(`Received request for URL: ${fullURL}`);
  164.   const targetURL = pathname.replace('/proxy/', 'https://') + search; // Check if the request is for an m3u8 file
  165.   if (path.extname(targetURL).toLowerCase() === '.m3u8') {
  166.     handleM3U8Request(req, res, targetURL);
  167.     return;
  168.   }
  169.   // Handle other requests
  170.   handleOtherRequest(req, res, targetURL);
  171. });
  172.  
  173. // A route for handling segment requests
  174. app.use('/segment/*', (req, res) => {
  175.   const fullURL = req.protocol + '://' + req.get('host') + req.originalUrl;
  176.   console.log(`Received request for URL: ${fullURL}`);
  177.  
  178.   // Capture the segment path from the wildcard
  179.   const segmentPath = req.params[0];
  180.  
  181.   // Construct the targetURL with the captured segment path
  182.   const targetURL = 'https://' + segmentPath;
  183.  
  184.   // Handle the request
  185.   handleOtherRequest(req, res, targetURL);
  186. });
  187.  
  188. // A route for serving the main page
  189. app.get('/', (req, res) => {
  190.   res.render('main.ejs');
  191. });
  192.  
  193. // A route for serving the admin panel
  194. app.get('/admin', async (req, res) => {
  195.   try {
  196.     const games = await Game.find();
  197.     res.render('admin.ejs', { videos: games });
  198.   } catch (error) {
  199.     res.status(500).send(error);
  200.   }
  201. });
  202.  
  203. // A route for getting all games
  204. app.get('/games', async (req, res) => {
  205.   try {
  206.     const games = await Game.find();
  207.     res.send(games);
  208.   } catch (error) {
  209.     res.status(500).send(error);
  210.   }
  211. });
  212.  
  213. // A route for creating a new game
  214. app.post('/games', async (req, res) => {
  215.   try {
  216.     const game = new Game(req.body);
  217.     await game.save();
  218.     res.redirect('/admin');
  219.   } catch (error) {
  220.     res.status(500).send(error);
  221.   }
  222. });
  223.  
  224. // A route for updating a game
  225. app.put('/games/:id', async (req, res) => {
  226.   try {
  227.     const { id } = req.params;
  228.     const { title, streamLink, sport } = req.body;
  229.     await Game.findByIdAndUpdate(id, { title, streamLink, sport }, { new: true });
  230.     res.redirect('/admin');
  231.   } catch (error) {
  232.     res.status(500).send(error);
  233.   }
  234. });
  235.  
  236. // A route for deleting a game
  237. app.delete('/games/:id', async (req, res) => {
  238.   try {
  239.     await Game.deleteOne({ _id: req.params.id });
  240.     res.redirect('/admin');
  241.   } catch (error) {
  242.     res.status(500).send(error);
  243.   }
  244. });
  245.  
  246. // Start the server
  247. const port = process.env.PORT || 3000;
  248. app.listen(port, () => console.log(`Server running on port ${port}`));
  249.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement