const fs = require('node:fs') const upload = require('../utils/upload.js') const ytdlp = require('../utils/ytdlp.js') const redis = require('../utils/redis.js') const validate = require('../utils/validate.js') const metadata = require('../utils/metadata.js') const websocket = require('../utils/websocket.js') const captcha = require("../utils/captcha.js") const logger = require("../utils/logger.js") const { PrismaClient } = require('@prisma/client') const prisma = new PrismaClient() exports.save = async (ws, req) => { logger.info({ message: `${req.path} ${JSON.stringify(req.query)}` }) const id = await validate.validateVideoInput(req.query.url) if (id.fail) { ws.send(`ERROR - ${id.message}`) return ws.close() } if (await redis.get(id)) { ws.send('DATA - Someone is already downloading this video...') return ws.close() } if (await redis.get(`blacklist:${id}`)) { ws.send('DATA - You can\'t download that. The video is blacklisted.') return ws.close() } const already = await prisma.videos.findFirst({ where: { id: id } }) if (already) return ws.send(`DONE - ${process.env.FRONTEND}/watch?v=${id}`) ws.send('CAPTCHA - Please complete the captcha:') ws.on('message', async function(msg) { if (await redis.get(id) != 'downloading') { await redis.set(id, 'downloading') const confirm = await captcha.checkCaptcha(msg) if (confirm) startDownloading() else { await redis.del(id) ws.send('DATA - You little goofy goober tried to mess with the captcha...') ws.close() } } else { ws.send('DATA - You already sent captcha reply...') } }) async function startDownloading() { ws.send('INFO - Spawning yt-dlp!') const download = await ytdlp.downloadVideo(`https://www.youtube.com/watch?v=${id}`, ws) if (download.fail) { await redis.del(id) ws.send(`DATA - ${download.message}`) ws.close() } else { const file = fs.readdirSync("videos").find(f => f.includes(id)) if (file) { fs.renameSync(`./videos/${file}`, `./videos/${id}.webm`) ws.send('DATA - Uploading file...') const videoUrl = await upload.uploadVideo(`./videos/${id}.webm`) fs.unlinkSync(`./videos/${id}.webm`) await websocket.createDatabaseVideo(id, videoUrl) ws.send(`DONE - ${process.env.FRONTEND}/watch?v=${id}`) } await redis.del(id) ws.close(); } } } exports.playlist = async (ws, req) => { logger.info({ message: `${req.path} ${JSON.stringify(req.query)}` }) const playlistId = await validate.validatePlaylistInput(req.query.url) if (playlistId.fail) { ws.send(`ERROR - ${playlistId.message}`) return ws.close() } let status = 'captcha' ws.send('CAPTCHA - Please complete the captcha:') ws.on('message', async function(msg) { if (status == 'captcha') { status = 'downloading' const confirm = await captcha.checkCaptcha(msg) if (confirm) startDownloading() else { await redis.del(id) ws.send('DATA - You little goofy goober tried to mess with the captcha...') ws.close() } } else { ws.send('DATA - You already sent captcha reply...') } }) async function startDownloading() { const playlist = await metadata.getPlaylistVideos(playlistId) for (video of playlist.relatedStreams) { if (ws.readyState !== ws.OPEN) { return logger.info({ message: `Stopped downloading ${playlistId}, websocket is closed` }) } const id = video.url.match(/[?&]v=([^&]+)/)[1] const already = await prisma.videos.findFirst({ where: { id: id } }) if (already) { ws.send(`DATA - Already downloaded ${video.title}`) await prisma.videos.updateMany({ where: { id: id }, data: { playlist: playlistId } }) continue } if (await redis.get(id)) { ws.send(`DATA - Someone is already downloading ${video.title}, skipping.`) continue } if (await redis.get(`blacklist:${id}`)) { ws.send(`DATA - ${video.title} is blacklisted from downloading, skipping`) continue } ws.send(`INFO - Downloading ${video.title}

`) await redis.set(id, 'downloading') const download = await ytdlp.downloadVideo('https://www.youtube.com' + video.url, ws) if (download.fail) { ws.send(`DATA - ${download.message}`) await redis.del(id) continue } else { const file = fs.readdirSync("./videos").find(f => f.includes(id)) if (file) { try { fs.renameSync(`./videos/${file}`, `./videos/${id}.webm`) ws.send(`DATA - Downloaded ${video.title}`) ws.send(`DATA - Uploading ${video.title}`) const videoUrl = await upload.uploadVideo(`./videos/${id}.webm`) ws.send(`DATA - Uploaded ${video.title}`) fs.unlinkSync(`./videos/${id}.webm`) await websocket.createDatabaseVideo(id, videoUrl, playlistId) ws.send(`DATA - Created video page for ${video.title}`) } catch (e) { ws.send(`DATA - Failed downloading video ${video.title}. Going to next video`) logger.error(e) } } else { ws.send(`DATA - Failed to find file for ${video.title}. Going to next video in the playlist`) } await redis.del(id) } } ws.send(`DONE - ${process.env.FRONTEND}/playlist?list=${playlistId}`) } } exports.channel = async (ws, req) => { logger.info({ message: `${req.path} ${JSON.stringify(req.query)}` }) const channelId = await validate.validateChannelInput(req.query.url) if (channelId.fail) { ws.send(`ERROR - ${channelId.message}`) return ws.close() } let status = 'captcha' ws.send('CAPTCHA - Please complete the captcha:') ws.on('message', async function(msg) { if (status == 'captcha') { status = 'downloading' const confirm = await captcha.checkCaptcha(msg) if (confirm) startDownloading() else { ws.send('DATA - You little goofy goober tried to mess with the captcha...') ws.close() } } else { ws.send('DATA - You already sent captcha reply...') } }) async function startDownloading() { const videos = await metadata.getChannelVideos(channelId) for (video of videos) { if (ws.readyState !== ws.OPEN) { return logger.info({ message: `Stopped downloading ${channelId}, websocket is closed` }) } const id = video.url.match(/[?&]v=([^&]+)/)[1] const already = await prisma.videos.findFirst({ where: { id: id } }) if (already) { ws.send(`DATA - Already downloaded ${video.title}`) continue } if (await redis.get(id)) { ws.send(`DATA - Someone is already downloading ${video.title}, skipping.`) continue } if (await redis.get(`blacklist:${id}`)) { ws.send(`DATA - ${video.title} is blacklisted from downloading, skipping`) continue } ws.send(`INFO - Downloading ${video.title}

`) await redis.set(id, 'downloading') const download = await ytdlp.downloadVideo('https://www.youtube.com' + video.url, ws) if (download.fail) { ws.send(`DATA - ${download.message}`) await redis.del(id) continue } else { const file = fs.readdirSync("./videos").find(f => f.includes(id)) if (file) { try { fs.renameSync(`./videos/${file}`, `./videos/${id}.webm`) ws.send(`DATA - Downloaded ${video.title}`) ws.send(`DATA - Uploading ${video.title}`) const videoUrl = await upload.uploadVideo(`./videos/${id}.webm`) ws.send(`DATA - Uploaded ${video.title}`) fs.unlinkSync(`./videos/${id}.webm`) await websocket.createDatabaseVideo(id, videoUrl) ws.send(`DATA - Created video page for ${video.title}`) } catch (e) { ws.send(`DATA - Failed downloading video ${video.title}. Going to next video`) logger.error(e) } } else { ws.send(`DATA - Failed to find file for ${video.title}. Going to next video`) } await redis.del(id) } } ws.send(`DONE - ${process.env.FRONTEND}/channel/${channelId}`) } } exports.addAutodownload = async (req, res) => { const confirm = await captcha.checkCaptcha(req.query.captcha) if (!confirm) return res.status(500).send('You little goofy goober tried to mess with the captcha...') const channelId = await validate.validateChannelInput(req.query.url) if (channelId.fail) { return res.status(500).send(channelId.message) } const already = await prisma.autodownload.findFirst({ where: { channel: channelId } }) if (already) { res.status(500).send(`This channel is already being automatically downloaded...`) } else { await prisma.autodownload.create({ data: { channel: channelId } }) res.send('Perfect! Each time this channel uploads their videos will be downloaded') } }