const mongoose = require('mongoose'); const mongoURI = 'mongodb://mongoadmin:something@192.168.71.137:27017'; mongoose.connect(mongoURI); mongoose.connection.on('connected', ()=>{console.log('MongoDB - Connected')}) .on('disconnected', ()=>{console.log('MongoDB - Disconnect')}) .on('error', (error)=>console.log('Mongoose Error:', error)); const path = require('path'); const fs = require('fs'); const { Stream } = require('stream'); const {Manga, Chapter} = require('./lib'); const FavModel = require('./models/favorite') const EventEmitter = require('events'); const Emitter = new EventEmitter(); const BaseDir = './public/Saved' var Queue = []; var slots = 2; updateQueue(); var queueUpdateTimer = setInterval(() => { updateQueue(); }, 5*60*1000); process.on('message', m=> { if(m.action==='add') { clearInterval(queueUpdateTimer); updateQueue(); queueUpdateTimer = setInterval(() => { updateQueue(); }, 5*60*1000); return } if(m.action==='remove') return removeFromQueue(m.id) }) function removeFromQueue(id) { Queue = Queue.filter(manga => manga._id !== id); } Emitter.on('queueUpdate', ()=> { if(!Queue[0]) return if(!Queue[0].inProgress) { Queue[0].inProgress = true; download(Queue[0]) } } ); async function updateQueue() { const Scanlators = fs.readdirSync(BaseDir); const Favs = await FavModel.find(); let Mangas = []; for(let i =0; i { return parseInt(a.split('_')[1]) - parseInt(b.split('_')[1]) }); let latestDownloaded = parseInt(chapters[chapters.length-1].split('_')[1]); let manga = {scanlator:Scanlators[i], title:mangas[j], latestDownloaded} Mangas.push(manga) } } let FavedAndDownloaded = []; let FavedButNotDownloaded = null; for (let i = 0; i < Mangas.length; i++) { FavedAndDownloaded = FavedAndDownloaded.concat(Favs.filter(item => { return (item.scanlator === Mangas[i].scanlator && item.title === Mangas[i].title); }).map((matchedItem) =>{ return ({ ...matchedItem._doc, latestDownloaded: Mangas[i].latestDownloaded }) }) ); } FavedButNotDownloaded = Favs.filter(fav => { // return !FavedAndDownloaded.includes(fav) return !FavedAndDownloaded.some(fad => fad._id === fav._id); }); if(FavedButNotDownloaded) { for(let i = 0; i < FavedButNotDownloaded.length; i++) { (async(i)=> { const {title, link, scanlator, _id} = FavedButNotDownloaded[i]; const manga = await new Manga(scanlator, link, title).get(); let id = _id.toString(); addToQueue(manga, id); })(i) } } if(FavedAndDownloaded) { for(let i = 0; i { const {title, link, scanlator, latestDownloaded, _id} = FavedAndDownloaded[i]; const manga = await new Manga(scanlator, link, title).get(); let id = _id.toString(); checkForMissingFiles(manga) if(latestDownloaded item.num > startFrom); Queue.push({...manga, _id, List, startFrom}) Emitter.emit('queueUpdate', ''); } } async function checkForMissingFiles(manga) { const {scanlator, title, List} = manga; let auxList = List.sort((a,b)=>{return a.num - b.num}) for(var i = 0; i{return item.split('.')[0]}) var auxChapterList = chapter.List.map(item=>{return item.split('/')[item.split('/').length-1].split('.')[0]}) const missingImages = auxChapterList.filter(item=>!auxChapterDir.includes(item)); if(missingImages.length == 0) continue; for(var j = 0; jitem.split('/')[item.split('/').length-1].split('.')[0] == missingImages[j])[0] const destination = `${destPath}/${imageLink.split('/')[(imageLink.split('/').length - 1)]}` const download = await downloadImage(imageLink,destination, 5); console.log('MissingImage:',scanlator, title, 'chapter',i+1, download.status) } } } async function download(manga) { if(slots <=0) return slots--; const { scanlator, title} = manga; const List = manga.List.sort((a, b) => { return a.num - b.num }); for (let i = 0; i < List.length; i++) { const ch = List[i]; if (isNaN(ch.num)) continue; const { num, link } = ch; const chapter = await new Chapter(scanlator, link, title, num).get(); // console.log(chapter) if (!chapter?.List?.length) continue; await downloadChapter(chapter); } Queue.shift(); slots++; Emitter.emit('queueUpdate', ''); } async function downloadChapter(chapter) { const {List, scanlator, title, chNum } = chapter; const images = List; for (let i = 0; i < images.length; i++) { const img = images[i]; const destination = `./public/Saved/${scanlator}/${title}/CH_${chNum}/${img.split('/')[(img.split('/').length - 1)]}`; try { const downloaded = await downloadImage(img, destination); console.log(`Downloaded: Scanlator:${scanlator}; Manga: ${title} Chapter:${chNum}; IMG:${img.split('/')[(img.split('/').length - 1)]} ${downloaded.status}`); } catch (error) { console.error(`Error downloading ${img}: ${error}`); } } return {chNum} } /** * * @param {Link} url * @param {Path} destPath * @param {Number} maxRetries * @returns {Download} * @typedef {String} Link * @typedef {Object} Download * @property {String} status * @property {Path} file */ async function downloadImage(url, destPath, maxRetries = 3) { let retries = 0; while (retries < maxRetries) { try { const response = await fetch(url); const dir = path.dirname(destPath); if (!response.ok) throw new Error(`Failed to download image. Status Code: ${response.status}`); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } const fileStream = fs.createWriteStream(destPath); await new Promise((resolve, reject) => { const stream = Stream.Readable.fromWeb(response.body).pipe(fileStream); stream.on('error', reject); fileStream.on('finish', () => { fileStream.close(); resolve(); }); }); return {status:'Downloaded', file:destPath} } catch (error) { console.error(`Error downloading image: ${destPath}, Retry ${retries + 1}/${maxRetries}`);//, error); retries++; if(retries == maxRetries) return {status:'Failed', file:destPath} } } }