Advertisement
Guest User

Untitled

a guest
Jan 21st, 2020
135
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.31 KB | None | 0 0
  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;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement