adding channel downloading and automatic downloading
This commit is contained in:
		
							parent
							
								
									27c79ab648
								
							
						
					
					
						commit
						283fa7c892
					
				| 
						 | 
					@ -172,3 +172,114 @@ exports.playlist = async (ws, req) => {
 | 
				
			||||||
        ws.send(`DONE - ${process.env.FRONTEND}/playlist?list=${playlistId}`)
 | 
					        ws.send(`DONE - ${process.env.FRONTEND}/playlist?list=${playlistId}`)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports.channel = async (ws, req) => {
 | 
				
			||||||
 | 
					    logger.info({ message: `${req.path} ${JSON.stringify(req.query)} ${JSON.stringify(req.headers)}` })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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 {
 | 
				
			||||||
 | 
					                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 instance = await metadata.getInstance()
 | 
				
			||||||
 | 
					        const channel = await metadata.getChannelVideos(instance, channelId)
 | 
				
			||||||
 | 
					        for (video of channel.relatedStreams) {
 | 
				
			||||||
 | 
					            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
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ws.send(`INFO - Downloading ${video.title}<br><br>`)
 | 
				
			||||||
 | 
					            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 {
 | 
				
			||||||
 | 
					                await redis.del(id)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                const file = fs.readdirSync("./videos").find(f => f.includes(id))
 | 
				
			||||||
 | 
					                if (file) {
 | 
				
			||||||
 | 
					                    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}`)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    ws.send(`DATA - Failed to find file for ${video.title}. Going to next video`)
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ws.send(`DONE - ${process.env.FRONTEND}/channel/${channelId}`)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports.addAutodownload = async (req, res) => {
 | 
				
			||||||
 | 
					    const confirm = await captcha.checkCaptcha(req.query.captcha)
 | 
				
			||||||
 | 
					    if (!confirm) 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) {
 | 
				
			||||||
 | 
					        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')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								index.js
								
								
								
								
							
							
						
						
									
										7
									
								
								index.js
								
								
								
								
							| 
						 | 
					@ -4,6 +4,7 @@ const express = require('express')
 | 
				
			||||||
const cors = require('cors')
 | 
					const cors = require('cors')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = require('./utils/logger.js')
 | 
					const logger = require('./utils/logger.js')
 | 
				
			||||||
 | 
					const auto = require('./utils/auto.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const latestController = require('./controller/latest.js')
 | 
					const latestController = require('./controller/latest.js')
 | 
				
			||||||
const videoController = require('./controller/video.js')
 | 
					const videoController = require('./controller/video.js')
 | 
				
			||||||
| 
						 | 
					@ -30,6 +31,12 @@ app.get('/transparency/:id', transparencyController.getReport)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.ws('/save', websocketController.save)
 | 
					app.ws('/save', websocketController.save)
 | 
				
			||||||
app.ws('/saveplaylist', websocketController.playlist)
 | 
					app.ws('/saveplaylist', websocketController.playlist)
 | 
				
			||||||
 | 
					app.ws('/savechannel', websocketController.channel)
 | 
				
			||||||
 | 
					app.get('/autodownload', websocketController.addAutodownload)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setInterval(() => {
 | 
				
			||||||
 | 
					  auto.handleCheck()
 | 
				
			||||||
 | 
					}, 300000)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
process.on('uncaughtException', err => {
 | 
					process.on('uncaughtException', err => {
 | 
				
			||||||
  logger.info({ message: `Error: ${err.message}` })
 | 
					  logger.info({ message: `Error: ${err.message}` })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,3 +35,8 @@ model reports {
 | 
				
			||||||
  details   String
 | 
					  details   String
 | 
				
			||||||
  date      DateTime  @default(now())
 | 
					  date      DateTime  @default(now())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					model autodownload {
 | 
				
			||||||
 | 
					  uuid      String    @id @default(uuid())
 | 
				
			||||||
 | 
					  channel   String
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,61 @@
 | 
				
			||||||
 | 
					const logger = require("./logger.js");
 | 
				
			||||||
 | 
					const metadata = require("./metadata.js")
 | 
				
			||||||
 | 
					const ytdlp = require("./ytdlp.js")
 | 
				
			||||||
 | 
					const redis = require("./redis.js")
 | 
				
			||||||
 | 
					const websocket = require("./websocket.js")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { PrismaClient } =  require('@prisma/client')
 | 
				
			||||||
 | 
					const prisma = new PrismaClient()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function handleCheck() {
 | 
				
			||||||
 | 
					    const channels = await prisma.autodownload.findMany()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (c of channels) {
 | 
				
			||||||
 | 
					        await handleDownload(c.channel)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function handleDownload(channelId) {
 | 
				
			||||||
 | 
					    logger.info({ message: `Checking ${channelId} for new videos...` })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const instance = await metadata.getInstance()
 | 
				
			||||||
 | 
					    const channel = await metadata.getChannelVideos(instance, channelId)
 | 
				
			||||||
 | 
					    for (video of channel.relatedStreams) {
 | 
				
			||||||
 | 
					        const id = video.url.match(/[?&]v=([^&]+)/)[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const already = await prisma.videos.findFirst({
 | 
				
			||||||
 | 
					            where: {
 | 
				
			||||||
 | 
					                id: id
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (already) continue
 | 
				
			||||||
 | 
					        logger.info({ message: `Starting to download ${video.title}, ${id}` })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const download = await ytdlp.downloadVideo('https://www.youtube.com' + video.url)
 | 
				
			||||||
 | 
					        if (download.fail) {
 | 
				
			||||||
 | 
					            logger.info({ message: `Failed downloading ${video.title}, ${id} -> ${download.message}` })
 | 
				
			||||||
 | 
					            await redis.del(id)
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            await redis.del(id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const file = fs.readdirSync("./videos").find(f => f.includes(id))
 | 
				
			||||||
 | 
					            if (file) {
 | 
				
			||||||
 | 
					                fs.renameSync(`./videos/${file}`, `./videos/${id}.webm`)
 | 
				
			||||||
 | 
					                logger.info({ message: `Downloaded ${video.title}, ${id}` })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const videoUrl = await upload.uploadVideo(`./videos/${id}.webm`)
 | 
				
			||||||
 | 
					                logger.info({ message: `Uploaded ${video.title}, ${id}` })
 | 
				
			||||||
 | 
					                fs.unlinkSync(`./videos/${id}.webm`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await websocket.createDatabaseVideo(id, videoUrl)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                logger.info({ message: `Couldn't find file for ${video.title}, ${id}` })
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = { handleCheck }
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ async function downloadVideo(url, ws) {
 | 
				
			||||||
            const msg = data.toString().trim()
 | 
					            const msg = data.toString().trim()
 | 
				
			||||||
            if (!msg) return 
 | 
					            if (!msg) return 
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
            ws.send(`DATA - ${msg}`)
 | 
					            if (ws) ws.send(`DATA - ${msg}`)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        child.on("close", async (code, signal) => {
 | 
					        child.on("close", async (code, signal) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue