const {Search, Chapter, Manga,SearchByTag, isValidID, Modules, Baker, Crypto, cookieParser} = require('../lib.js') const mongoose = require('mongoose') const FavoriteModel = require('../models/favorite.js'); const ChapterModel = require('../models/readChapter.js'); const fs = require('fs'); const path = require('path'); const { isNullOrUndefined } = require('util'); exports.home = async (req,res)=> { const cookieStr = req.headers.cookie; if(!cookieStr) return res.render('home.ejs',{data:null}); const cookie = cookieParser(cookieStr, 'hash'); res.render('home.ejs', {data:cookie}); } exports.searchBar = (req, res)=> { res.render('searchBar.ejs'); } exports.search = async (req, res)=> { if(!req.body?.searchstring) return res.sendStatus(404) var data = await new Search(req.body.searchstring).searchExtraInfo(); if(!Array.isArray(data)) return res.sendStatus(404); const favorites = await FavoriteModel.find() const favoriteScanlators = favorites.map(favorite => favorite.scanlator); data.forEach((item, index) => { if (favoriteScanlators.includes(item.scanlator)) { item.Results.find(result => { const matchingFavorite = favorites.find(favorite => { return result.title === favorite.title && item.scanlator === favorite.scanlator; }); if (matchingFavorite) { result.favorite = matchingFavorite._id; return true; } return false; }); } }); data = data.filter(item => item.Results.length > 0) data.sort((a,b)=> { const scanlatorA = a.scanlator.toLowerCase(); const scanlatorB = b.scanlator.toLowerCase(); return scanlatorA.localeCompare(scanlatorB); }) res.render('searchResults.ejs', {data}) } exports.bookmark = (worker)=> { return async (req, res)=> { if (!req.params || typeof req.params !== 'object' || !('scanlator' in req.params) || !('title' in req.params)) return res.sendStatus(404); const {scanlator, title, idorLink} = req.params; const {cookie} = req.headers; if(!cookie) return res.sendStatus(404); const config = require('../config.json'); if(cookieParser(cookie, 'hash')!=config.hash) return res.sendStatus(404); if(isValidID(idorLink)) { worker.send({ action:'remove', idorLink}) return await FavoriteModel.findOneAndDelete({_id:idorLink}).then((deleted)=> { return res.render('bookmark.ejs', {scanlator, title, link:encodeURIComponent(deleted.link)}) }) } const scanlatorExists = await new Modules(scanlator).exists(); if(!scanlatorExists) return res.sendStatus(404); const scanlatorHasTitle = await new Modules(scanlator).titleExists(title); if(!scanlatorHasTitle) return res.sendStatus(404); const fav = new FavoriteModel() fav.scanlator = scanlator; fav.title = title; fav.link = idorLink; await fav.save(); worker.send({action:'add', id:fav._id}) res.render('bookmarked.ejs', {scanlator, title, link:idorLink, id:fav._id}); let manga; try { manga = await new Manga(scanlator, idorLink, title).get(); } catch (error) { return FavoriteModel.findByIdAndDelete(fav._id) } const {tags} = manga; FavoriteModel.findByIdAndUpdate(fav._id,{tags}, { new: true}) .then((doc, err) => { if (err) console.error('test',err); }); } } exports.manga = async (req, res)=> { const {scanlator, link, title} = req.params; const scanlatorExists = await new Modules(scanlator).exists(); if(!scanlatorExists) return res.sendStatus(404); const scanlatorHasTitle = await new Modules(scanlator).titleExists(title); if(!scanlatorHasTitle) return res.sendStatus(404); let manga; let chaptersRead; try { manga = await new Manga(scanlator,link, title).get(); chaptersRead = await ChapterModel.find({scanlator,title}); for(let i = 0; i item.completely == true); filteredArray.sort((a, b) => a.chapterNum - b.chapterNum); const lastCompletelyRead = filteredArray.pop(); const sortedChapters = chaptersRead.sort((a, b) => a.chapterNum - b.chapterNum); const sortedAndFilteredChapters = sortedChapters.filter(item=> item.lastImageRead && !item.completely); const lastChapterWithReadImages=sortedAndFilteredChapters.shift(); const chapterNumToContinue = getChapterNumToContinue(lastCompletelyRead, lastChapterWithReadImages); manga.List[chapterNumToContinue-1]['continue'] = true; } catch (error) { console.log('There was an error:', error); } res.render('mangaPage.ejs', {data:{...manga, scanlator}}); } function getChapterNumToContinue(lastCompletelyRead, lastChapterWithReadImages) { if(lastCompletelyRead) { if(lastCompletelyRead.chapterNum < lastChapterWithReadImages.chapterNum) { return lastChapterWithReadImages.chapterNum; } return lastCompletelyRead.chapterNum+1; } return lastChapterWithReadImages.chapterNum; } exports.chapter = async (req, res)=> { const {scanlator, title, chapter, link} = req.params; //IF its already there even with wrong things its there just show it const fileExists = fs.existsSync(`./public/Saved/${scanlator}/${title}/CH_${chapter}`); if(fileExists) { const imgs =fs.readdirSync(`./public/Saved/${scanlator}/${title}/CH_${chapter}`); const latestChap = fs.readdirSync(`./public/Saved/${scanlator}/${title}`).length const List = imgs.map(filename => {return `./Saved/${scanlator}/${title}/CH_${chapter}/${filename}`}); const mangaLink = await new Chapter(scanlator, link, title, chapter).getMangaLink(); return res.render('chapterPage.ejs', {data:{title,latestChap,scanlator, chapterNum:parseInt(chapter), mangaLink, List}}); } //If it isn't make sure there is no bad params being passed const scanlatorExists = await new Modules(scanlator).exists(); if(!scanlatorExists) return res.sendStatus(404); const scanlatorHasTitle = await new Modules(scanlator).titleExists(title); if(!scanlatorHasTitle) return res.sendStatus(404); let manga; try { manga = await new Chapter(scanlator, link, title,chapter).get(); } catch (error) { return res.sendStatus(404); } if(!manga.List) { return res.redirect('/') } res.render('chapterPage.ejs', {data:{...manga, scanlator, chapterNum:parseInt(chapter)}}); } exports.favorites = async (req, res)=> { const favs = await FavoriteModel.find(); var mangas = []; if(favs.length == 0) return res.render('favorites.ejs', {isEmpty:true, mangas}) await Promise.all(Object.keys(favs).map(async (key) => { const manga = favs[key]; var aux = await new Manga(manga.scanlator, manga.link, manga.title).get(); if(aux) { aux.favorite = manga._id; mangas.push(aux); } })); function getUniqueScanlators(items) { const scanlators = {}; items.forEach(item => { scanlators[item.scanlator] = true; }); return Object.keys(scanlators); } const uniqueScanlators = getUniqueScanlators(mangas); const scanlatorsArray = uniqueScanlators.map(scanlator => { var scanlatorItems = mangas.filter(manga => manga.scanlator === scanlator); if(!scanlatorItems) return res.sendStatus(500); scanlatorItems.sort((a, b) => { if (a.title.toUpperCase() < b.title.toUpperCase()) return -1; else return 1; }); return { scanlator, Results: scanlatorItems }; }); scanlatorsArray.sort((a,b)=> { const scanlatorA = a.scanlator.toLowerCase(); const scanlatorB = b.scanlator.toLowerCase(); return scanlatorA.localeCompare(scanlatorB); }) res.render('favorites.ejs', {isEmpty:false, mangas:scanlatorsArray}) } exports.recommended = async (req,res)=> { const favorites = await FavoriteModel.find(); const favoriteScanlators = favorites.map(favorite => favorite.scanlator); let favTags = []; for(var i = 0; i { acc[curr] = (acc[curr]||0)+1; return acc; }, {}); const mostCommonTags = [...new Set(favTags)].sort((a,b)=> { if(counted[b] !== counted[a]) { return counted[b]-counted[a]; } else { return favTags.indexOf(a)-favTags.indexOf(b); } }) const firstTwoTags =favoriteScanlators.length <=0 ? ['action', 'shounen'] : mostCommonTags.slice(0,2); let recommended = []; for(var i =0; i<2; i++) { const results = await new SearchByTag(firstTwoTags[i]).search(); recommended.push(results); } const groupedByScanlator = recommended.reduce((acc, currentArray) => { currentArray.forEach(({ Results, scanlator }) => { if (!acc[scanlator]) { acc[scanlator] = { Results: [], scanlator }; } const uniqueResults = Results.filter(result => !acc[scanlator].Results.some(existingResult => existingResult.title === result.title) ); acc[scanlator].Results = acc[scanlator].Results.concat(uniqueResults); }); return acc; }, {}); recommended = Object.values(groupedByScanlator); recommended.forEach((item, index) => { if (favoriteScanlators.includes(item.scanlator)) { item.Results.find(result => { const matchingFavorite = favorites.find(favorite => { return result.title === favorite.title && item.scanlator === favorite.scanlator; }); if (matchingFavorite) { result.favorite = matchingFavorite._id; return true; } return false; }); } }); recommended.sort((a,b)=> { const scanlatorA = a.scanlator.toLowerCase(); const scanlatorB = b.scanlator.toLowerCase(); return scanlatorA.localeCompare(scanlatorB); }) res.render('searchResults.ejs', {data:recommended}); } exports.chapterNavInfo = async (req, res)=> { const {scanlator, title, mangaLink} = req.params; const scanlatorExists = await new Modules(scanlator).exists(); if(!scanlatorExists) return res.sendStatus(404); const scanlatorHasTitle = await new Modules(scanlator).titleExists(title); if(!scanlatorHasTitle) return res.sendStatus(404); let chapter = parseInt(req.params.chapter); let manga; try { manga = await new Manga(scanlator, mangaLink, title).get(); } catch (error) { return res.sendStatus(404); } const {latestChap, List} = manga; List.sort((a,b)=> {return a.num - b.num}); let nextChapterLink = chapter>= latestChap? '' : List[chapter].link; let prevChapterLink = chapter<=0? '' : List[chapter-1].link; res.render('chapterNav.ejs', {data:{title,latestChap,scanlator, chapterNum:chapter,mangaLink, nextChapterLink, prevChapterLink}}) } exports.hasConfig = (req, res, next) => { const exists = fs.existsSync('./config.json'); if(!exists) return this.config(req,res) next(); } exports.config = async (req, res)=> { function getSubDirectories(dir) { let protectedSubDirs = ['node_modules', '.git', 'controllers', 'models', 'Modules', 'css','images', 'js', 'routes', 'views']; const map = new Map(); const dirs = fs.readdirSync(dir) .filter(file => fs.statSync(path.join(dir, file)).isDirectory()) .filter(file => !protectedSubDirs.includes(file)) .filter(file=> !file.includes('scans')); for(let i =0; i { const {username, password, password2, saveFolder} = req.body; const {body} = req; if(!username || !password || !password2) return res.sendStatus(404); if(username.length < 4) return res.sendStatus(404); if(password !== password2) return res.sendStatus(404); if(!checkValidPath(saveFolder)) return res.sendStatus(404); if(!saveFolder) body.saveFolder = './public/Saved' body['hash'] = new Crypto(username, password).Hash; await fs.writeFileSync('./config.json', JSON.stringify(body)); res.redirect('/'); } /** * @param {String} dir * @returns {Boolean} */ function checkValidPath(dir) { if(!dir) return true; //by pass when there is no savePath (use default); dir = dir; return !path.isAbsolute(dir) } exports.loginPage = async (req, res)=> { res.render('login.ejs'); } exports.login = async (req, res)=> { const {username, password} = req.body; if(!username || !password ) return res.sendStatus(404); const config = require('../config.json'); if(username != config.username || password != config.password) return res.sendStatus(404); await Baker(res, 'hash', config.hash, 24) //TODO: change this to dashboard res.redirect('/'); } exports.logout = async (req, res)=> { const config = require('../config.json'); const cookieStr = req.headers.cookie; if(!cookieStr) return res.redirect('/'); const hash = cookieParser(cookieStr, 'hash'); if(hash != config.hash) return res.redirect('/'); await Baker(res, 'hash', '') res.render('dashboard.ejs'); } exports.errorPage = (req, res)=> { res.render('error.ejs') } exports.chapterRead = async (req, res)=> { const {scanlator, title, chapter, link} = req.params; const scanlatorExists = await new Modules(scanlator).exists(); if(!scanlatorExists) return res.sendStatus(404); const scanlatorHasTitle = await new Modules(scanlator).titleExists(title); if(!scanlatorHasTitle) return res.sendStatus(404); const manga = await new Manga(scanlator, link, title).get(); const {latestChap, List} = manga; if(chapter > latestChap || chapter < 0) return res.sendStatus(404); const chap = new ChapterModel(); let chapterNum = chapter; let chapterLink = List.filter(item=>item.num == chapterNum)[0].link; if(!chapterLink) return res.sendStatus(404); const _chapter = await new Chapter(scanlator, chapterLink,title, chapter).get(); let lastImageRead = _chapter.List[_chapter.List.length-1]; chap.lastImageRead = lastImageRead; const chaps = await ChapterModel.find({scanlator, title, link, chapterNum}); if(chaps.length>0) return updateChapter(chaps[0],res, lastImageRead, true) chap.scanlator = scanlator; chap.title = title; chap.link = link; chap.chapterNum = chapter; chap.lastImageRead = lastImageRead; chap.completely = true; chap.save() res.sendStatus(200); } async function updateChapter(chap, res, lastImageRead, completely=false) { await ChapterModel.findOneAndUpdate(chap._id,{completely, lastImageRead:lastImageRead}, {new:true}); res.sendStatus(200); } exports.chapterIncompleteRead = async (req,res)=> { const {scanlator, title, chapter, link, img} = req.params; const scanlatorExists = await new Modules(scanlator).exists(); if(!scanlatorExists) return res.sendStatus(404); const scanlatorHasTitle = await new Modules(scanlator).titleExists(title); if(!scanlatorHasTitle) return res.sendStatus(404); let chapterNum = chapter; const manga = await new Manga(scanlator, link, title).get(); const {latestChap, List} = manga; let chapterLink = List.filter(item=> item.num == chapterNum)[0].link; if(chapter > latestChap || chapter < 0) return res.sendStatus(404); const chap = new ChapterModel(); const _chapter = await new Chapter(scanlator, chapterLink,title, chapterNum).get(); let lastImageRead = _chapter.List.filter(item=> { let parsedImageURL = item.split('.')[item.split('.').length-2].split('/')[item.split('.')[item.split('.').length-2].split('/').length-1] return(parsedImageURL==img) })[0]; const chaps = await ChapterModel.find({scanlator, title, link, chapterNum}); if(chaps.length>0) return updateChapter(chaps[0],res, lastImageRead) chap.scanlator = scanlator; chap.title = title; chap.link = link; chap.chapterNum = chapter; chap.lastImageRead = lastImageRead; chap.completely = false; chap.save() res.sendStatus(200); } exports.chapterLastRead = async (req, res)=> { const {scanlator, title, chapter, link} = req.params; const scanlatorExists = await new Modules(scanlator).exists(); if(!scanlatorExists) return res.sendStatus(404); const scanlatorHasTitle = await new Modules(scanlator).titleExists(title); if(!scanlatorHasTitle) return res.sendStatus(404); let chapterNum = chapter; const chaps = await ChapterModel.find({scanlator, title, link, chapterNum}); if(chaps.length == 0) return res.sendStatus(404); let data = chaps[0].lastImageRead; res.render('data.ejs', {data}); } exports.dashboard = async (req, res)=> { let data = 0; res.render('dashboard.ejs', {data}); }