SHARE
TWEET

Untitled

a guest Jan 21st, 2020 78 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const fs = require("fs-extra");
  2. const path = require("path");
  3. const express = require("express");
  4.  
  5. const YTDL = "python3";
  6. const YTDL_ARG = [path.join(__dirname, "../bin/youtube-dl"), "--no-warnings"];
  7. const YTDL_OPT = { maxBuffer: 1000 * 1000 * 2 };
  8.  
  9. const TIME_LIMIT = 1000 * 60 * 60 * 2;
  10.  
  11. const router = express.Router();
  12.  
  13. const { execFile, spawn } = require("promisify-child-process");
  14.  
  15. const timeStampToMili = (t) => new Date("1/1/1970 " + t).getTime() + 3600000;
  16.  
  17. const downloadYTDL = () =>
  18.     new Promise((resv) => {
  19.         const request = require("request");
  20.  
  21.         request("https://yt-dl.org/downloads/latest/youtube-dl")
  22.             .pipe(fs.createWriteStream(path.join(__dirname, "../bin/youtube-dl")))
  23.             .on("finish", () => {
  24.                 resv();
  25.             });
  26.     });
  27.  
  28. let videoList = {};
  29. let youtubeDLVersion = "?";
  30. let youtubeDLExtractors = [];
  31.  
  32. router.get("/", (req, res) => {
  33.     res.render("index", {
  34.         youtubeDLVersion,
  35.         youtubeDLExtractors,
  36.         videoList: Object.keys(videoList)
  37.             .filter((i) => videoList[i].status === "done")
  38.             .map((i) => ({ name: videoList[i].fileName, time: i })),
  39.         debugMode: "DEBUG" in process.env
  40.     });
  41. });
  42.  
  43. (async () => {
  44.     if (!fs.existsSync(path.join(__dirname, "../bin/youtube-dl"))) await downloadYTDL();
  45.  
  46.     fs.ensureDirSync(path.join(__dirname, "../videos"));
  47.  
  48.     videoList = {};
  49.  
  50.     youtubeDLVersion = (await execFile(YTDL, [...YTDL_ARG, "--version"], YTDL_OPT)).stdout.toString().split("\n")[0];
  51.  
  52.     youtubeDLExtractors = (await execFile(YTDL, [...YTDL_ARG, "--list-extractors"], YTDL_OPT)).stdout
  53.         .toString()
  54.         .split("\n");
  55.  
  56.     router.post("/getInfo", async (req, res) => {
  57.         if (
  58.             !req.body.url.match(
  59.                 /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/
  60.             )
  61.         )
  62.             return res.json({ success: false, error: "URL not valid" });
  63.  
  64.         try {
  65.             res.json({
  66.                 success: true,
  67.                 payload: JSON.parse(
  68.                     (await execFile(
  69.                         YTDL,
  70.                         [...YTDL_ARG, "--playlist-end", "1", "--dump-json", "--", req.body.url],
  71.                         YTDL_OPT
  72.                     )).stdout
  73.                         .toString()
  74.                         .split("\n")[0]
  75.                 )
  76.             });
  77.         } catch (err) {
  78.             console.error(err);
  79.  
  80.             res.json({ success: false, error: "An error has occured while retrieving info" });
  81.         }
  82.     });
  83.  
  84.     router.post("/startDownload", async (req, res) => {
  85.         if (
  86.             !req.body.url.match(
  87.                 /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/
  88.             )
  89.         )
  90.             return res.json({ success: false, error: "URL not valid" });
  91.  
  92.         let args = ["--playlist-end", "1", "--add-metadata"];
  93.  
  94.         if (["mp3", "m4a", "aac", "opus"].includes(req.body.out)) {
  95.             // music
  96.             if (req.body.how === "bestqc") args.push("-x", "--audio-format", req.body.out);
  97.             else if (req.body.how === "customfc") {
  98.                 if (!req.body.format) return res.json({ success: false, error: "No format selected" });
  99.                 if (req.body.format instanceof Array)
  100.                     return res.json({ success: false, error: "Only pick one format" });
  101.  
  102.                 args.push("-f", req.body.format, "--audio-format", req.body.out);
  103.             }
  104.         } else {
  105.             if (req.body.subtitle === "on") args.push("--embed-subs");
  106.  
  107.             if (req.body.how === "bestqc") args.push("--merge-output-format", req.body.out);
  108.             else if (req.body.how === "customfc") {
  109.                 if (!req.body.format) return res.json({ success: false, error: "No format selected" });
  110.                 if (!req.body.format instanceof Array || req.body.format.length > 2)
  111.                     return res.json({
  112.                         success: false,
  113.                         error: "Please pick only one video and one audio format"
  114.                     });
  115.  
  116.                 args.push("-f", req.body.format[0] + "+" + req.body.format[1], "--merge-output-format", req.body.out);
  117.             }
  118.         }
  119.  
  120.         if (req.body.how === "customfs") {
  121.             if (!req.body.format) return res.json({ success: false, error: "No format selected" });
  122.             if (req.body.format instanceof Array) return res.json({ success: false, error: "Only pick one format" });
  123.  
  124.             args.push("-f", req.body.format);
  125.         }
  126.  
  127.         const uuid = new Date().getTime().toString();
  128.  
  129.         await fs.ensureDir(path.join(__dirname, "../videos/", uuid));
  130.  
  131.         const video = spawn(
  132.             YTDL,
  133.             [
  134.                 ...YTDL_ARG,
  135.                 ...args,
  136.                 "-o",
  137.                 `${path.join(__dirname, "../videos/", uuid, "%(title)s.%(ext)s")}`,
  138.                 "--newline",
  139.                 "--postprocessor-args",
  140.                 "-strict -2",
  141.                 "--",
  142.                 req.body.url
  143.             ],
  144.             YTDL_OPT
  145.         );
  146.  
  147.         video.catch((err) => {
  148.             console.error(err);
  149.  
  150.             videoList[uuid].status = "failed";
  151.         });
  152.  
  153.         videoList[uuid] = {
  154.             status: "downloading",
  155.             progress: 0,
  156.             fileName: null
  157.         };
  158.  
  159.         let ytbuf = "";
  160.         video.stdout.on("data", (data) => {
  161.             if (!videoList[uuid].fileName) {
  162.                 ytbuf += data.toString();
  163.                 let match = ytbuf.match(/^ *?\[download] Destination: .*\/(.*?)$/m);
  164.  
  165.                 if (match) videoList[uuid].fileName = match[1].substr(match[1].indexOf("/") + 1);
  166.             }
  167.  
  168.             let dlMatch = data.toString().match(/^ *?\[download] +?(.+?)%/m);
  169.             if (dlMatch) videoList[uuid].progress = parseFloat(dlMatch[1]);
  170.  
  171.             let ffMatch =
  172.                 data.toString().match(/^ *?\[ffmpeg] Destination: .*\/(.*?)$/m) ||
  173.                 data.toString().match(/^ *?\[ffmpeg] Merging formats into ".*\/(.*?)"$/m);
  174.             if (ffMatch) videoList[uuid].fileName = ffMatch[1].substr(ffMatch[1].indexOf("/") + 1);
  175.         });
  176.  
  177.         let duration = -1;
  178.  
  179.         // ffmpeg is on stderr for some reason uhh
  180.         let ffbuf = "";
  181.         video.stderr.on("data", (data) => {
  182.             if (duration === -1) {
  183.                 ffbuf += data.toString();
  184.                 if (ffbuf.match(/^  Duration: (.*?), start:/m)) {
  185.                     duration = timeStampToMili(ffbuf.match(/^  Duration: (.*?), start:/m)[1]);
  186.                     videoList[uuid].status = "converting";
  187.                 }
  188.             } else if (data.toString().startsWith("frame=")) {
  189.                 let match = data.toString().match(/time=(.*?) /);
  190.  
  191.                 if (match) videoList[uuid].progress = (timeStampToMili(match[1]) / duration) * 100;
  192.             }
  193.         });
  194.  
  195.         video.on("close", (code) => {
  196.             videoList[uuid].status = code ? "failed" : "done";
  197.         });
  198.  
  199.         res.json({ success: true, payload: uuid });
  200.     });
  201.  
  202.     router.get("/checkDownload/:id", (req, res) => {
  203.         if (!(req.params.id in videoList) || videoList[req.params.id].status === "failed")
  204.             return res.json({ success: false, error: "Download failed" });
  205.  
  206.         if (videoList[req.params.id].status !== "done")
  207.             return res.json({
  208.                 success: true,
  209.                 payload: {
  210.                     done: false,
  211.                     status: videoList[req.params.id].status,
  212.                     progress: videoList[req.params.id].progress
  213.                 }
  214.             });
  215.         else return res.json({ success: true, payload: { done: true } });
  216.     });
  217.  
  218.     router.get("/download/:id", async (req, res) => {
  219.         if (!(req.params.id in videoList)) return res.send(404);
  220.  
  221.         try {
  222.             await fs.pathExists(
  223.                 path.join(__dirname, "../videos/", req.params.id, "/", videoList[req.params.id].fileName)
  224.             );
  225.  
  226.             res.download(path.join(__dirname, "../videos/", req.params.id, "/", videoList[req.params.id].fileName));
  227.         } catch (_) {
  228.             res.send(404);
  229.         }
  230.     });
  231.  
  232.     setInterval(async () => {
  233.         let dirs = await fs.readdir(path.join(__dirname, "../videos/"));
  234.         for (const dir of dirs) {
  235.             if (!parseInt(dir)) continue;
  236.  
  237.             if (parseInt(dir) < new Date().getTime() - TIME_LIMIT) fs.remove(path.join(__dirname, "../videos/", dir));
  238.         }
  239.  
  240.         let newVideoList = {};
  241.         Object.keys(videoList).map((i) => {
  242.             if (i >= new Date().getTime() - TIME_LIMIT) newVideoList[i] = videoList[i];
  243.         });
  244.  
  245.         videoList = newVideoList;
  246.     }, 1000 * 60); // minute
  247. })();
  248.  
  249. module.exports = router;
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top