You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
675 lines
26 KiB
675 lines
26 KiB
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');
|
|
|
|
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 = [...new Set(favorites.map(favorite => favorite.scanlator))];
|
|
for(let i = 0; i<data.length; i++)
|
|
{
|
|
for(let j= 0; j < data[i].Results.length; j++)
|
|
{
|
|
const scanlator = data[i].scanlator;
|
|
if(!favoriteScanlators.includes(scanlator)) continue
|
|
let item = data[i].Results[j];
|
|
const matchingFavorite = favorites.find(fav=>
|
|
{
|
|
return item.title === fav.title && scanlator === fav.scanlator;
|
|
})
|
|
if(matchingFavorite) data[i].Results[j].favorite = matchingFavorite._id;
|
|
}
|
|
}
|
|
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<manga.List.length; i++)
|
|
{
|
|
for(let j = 0; j<chaptersRead.length; j++)
|
|
{
|
|
if(manga.List[i].num==chaptersRead[j].chapterNum)
|
|
{
|
|
manga.List[i]['completely'] = chaptersRead[j].completely
|
|
manga.List[i]['Read'] = true;
|
|
manga.List[i]['_id'] = chaptersRead[j]._id;
|
|
}
|
|
}
|
|
}
|
|
const filteredArray = chaptersRead.length>0?chaptersRead.filter(item => item.completely == true):chaptersRead;
|
|
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();
|
|
let chapterNumToContinue = getChapterNumToContinue(lastCompletelyRead, lastChapterWithReadImages, manga.List);
|
|
if(!manga.List.some(item=> item.num==chapterNumToContinue)) chapterNumToContinue = Math.trunc(chapterNumToContinue);
|
|
if(lastCompletelyRead || lastChapterWithReadImages)
|
|
{
|
|
let index = manga.List.indexOf(manga.List.filter(item=>
|
|
{
|
|
return item.num == chapterNumToContinue
|
|
})[0]);
|
|
manga.List[index]['continue'] = true;
|
|
}
|
|
}
|
|
catch (error)
|
|
{
|
|
console.log('There was an error:', error);
|
|
}
|
|
res.render('mangaPage.ejs', {data:{...manga, scanlator}});
|
|
}
|
|
/**
|
|
*
|
|
* @param {Chapter} lastCompletelyRead
|
|
* @param {Chapter} lastChapterWithReadImages
|
|
* @param {ChapterList} List
|
|
* @returns {Nyumber}
|
|
*/
|
|
function getChapterNumToContinue(lastCompletelyRead, lastChapterWithReadImages, List)
|
|
{
|
|
if(!lastCompletelyRead && !lastChapterWithReadImages) return List[0].num;
|
|
const firstIndex = List.indexOf(List.filter(item=>item.num == lastCompletelyRead.chapterNum)[0]);
|
|
if(lastCompletelyRead)
|
|
{
|
|
if(lastCompletelyRead.chapterNum < lastChapterWithReadImages.chapterNum)
|
|
{
|
|
return lastChapterWithReadImages.chapterNum;
|
|
}
|
|
const chNum = lastCompletelyRead.chapterNum+1;
|
|
const secondIndex = List.indexOf(List.filter(item=>item.num == chNum)[0]);
|
|
if(secondIndex - firstIndex > 1) return List[firstIndex + 1].num;
|
|
return chNum
|
|
}
|
|
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:parseFloat(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:parseFloat(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<favorites.length; i++)
|
|
{
|
|
favTags.push(...favorites[i].tags)
|
|
}
|
|
const counted = favTags.reduce((acc, curr)=>
|
|
{
|
|
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 = parseFloat(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<dirs.length; i++)
|
|
{
|
|
map.set(dirs[i], getSubDirectories(path.join(dir, dirs[i])));
|
|
}
|
|
return map.size==0?'':map;
|
|
}
|
|
function getPaths(map, prefix='.')
|
|
{
|
|
const paths = [];
|
|
for (const [key, value] of map.entries())
|
|
{
|
|
const fullPath = `${prefix}/${key}`;
|
|
if (value instanceof Map)
|
|
{
|
|
paths.push(...getPaths(value, fullPath));
|
|
}
|
|
else
|
|
{
|
|
paths.push(fullPath);
|
|
}
|
|
}
|
|
return paths;
|
|
}
|
|
|
|
const subDirs = await getSubDirectories('./');
|
|
const data = getPaths(subDirs)
|
|
|
|
res.render('config.ejs', {data});
|
|
}
|
|
exports.configPost = async (req, res)=>
|
|
{
|
|
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 cookieStr = req.headers.cookie;
|
|
if(!cookieStr) return res.sendStatus(404);
|
|
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 chapFromList = List.filter(item=>item.num == chapterNum)[0];
|
|
let chapterLink = chapFromList.link;
|
|
let chapterDate = chapFromList.date;
|
|
if(!chapterLink) return res.sendStatus(404);
|
|
const _chapter = await new Chapter(scanlator, chapterLink,title, chapterNum).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) updateChapter(chaps[0], lastImageRead, true);
|
|
_chapter['num'] = _chapter.chNum;
|
|
_chapter['date'] = chapterDate;
|
|
_chapter['Read'] = true;
|
|
_chapter['_id'] = chap._id;
|
|
_chapter['completely'] = true;
|
|
res.render('chapterButton.ejs', {chapter:_chapter, scanlator, title, link, shortCut:false});
|
|
chap.scanlator = scanlator;
|
|
chap.title = title;
|
|
chap.link = link;
|
|
chap.chapterNum = chapter;
|
|
chap.lastImageRead = lastImageRead;
|
|
chap.completely = true;
|
|
chap.save()
|
|
}
|
|
async function updateChapter(chap,lastImageRead, completely=false,res)
|
|
{
|
|
await ChapterModel.findOneAndUpdate(chap._id,{completely, lastImageRead:lastImageRead}, {new:true});
|
|
if(res) return 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.continue = async (req, res)=>
|
|
{
|
|
async function getChaptersToContinue()
|
|
{
|
|
const favs = await FavoriteModel.find();
|
|
const chaps = await ChapterModel.find();
|
|
const favMap = new Map(favs.map(fav => [`${fav.title}_${fav.scanlator}`, {latestComplete: null, incomplete: []}]));
|
|
const favoritesNotYetStarted = [];
|
|
// Filter chapters directly into their respective categories
|
|
chaps.forEach(chap =>
|
|
{
|
|
const key = `${chap.title}_${chap.scanlator}`;
|
|
if (favMap.has(key))
|
|
{
|
|
const group = favMap.get(key);
|
|
if (chap.completely) {
|
|
if (!group.latestComplete || chap.chapterNum > group.latestComplete.chapterNum) group.latestComplete = chap;
|
|
}
|
|
else
|
|
{
|
|
group.incomplete.push(chap);
|
|
}
|
|
}
|
|
});
|
|
favs.forEach((element) =>
|
|
{
|
|
const {title, scanlator} = element;
|
|
if (!chaps.some(chap => chap.title == title && chap.scanlator == scanlator)) {
|
|
favoritesNotYetStarted.push(element);
|
|
}
|
|
});
|
|
|
|
// Compile the results
|
|
let results = Array.from(favMap.values()).flatMap(group =>
|
|
group.latestComplete ? [group.latestComplete, ...group.incomplete] : group.incomplete
|
|
);
|
|
|
|
results = await Promise.all(results.map(async (chap) =>
|
|
{
|
|
if (!chap.completely) return chap;
|
|
let next = await getNextChapter(chap);
|
|
return next ? next : null;
|
|
}));
|
|
|
|
results = results.filter(chap => chap !== null);
|
|
// Filter to find unique chapters that are not completed
|
|
const uniqueContinuationChapters = chaps.filter(chap =>
|
|
{
|
|
return !chap.completely && !results.some(f => f.scanlator === chap.scanlator && f.title === chap.title)
|
|
});
|
|
|
|
const chaptersToStart = [];
|
|
for(var i = 0; i< favoritesNotYetStarted.length; i++)
|
|
{
|
|
const {title, link, scanlator} = favoritesNotYetStarted[i];
|
|
const {List, img} = await new Manga(scanlator, link, title).get();
|
|
let aux = {byPass:true, chapterNum:0};
|
|
const chap = await new Chapter(scanlator, List[0].link, title, List[0].num).get();
|
|
const imgReadOutOfTotal = 0+'/'+(chap.List.length-1)
|
|
aux = {...aux, ...chap, mangaCoverImg:img, imgReadOutOfTotal,lastImageRead:chap.List[0]}
|
|
chaptersToStart.push(aux);
|
|
}
|
|
|
|
return [...results, ...uniqueContinuationChapters, ...chaptersToStart];
|
|
}
|
|
|
|
|
|
const chaptersToContinue = await getChaptersToContinue();
|
|
chaptersToContinue.sort((a, b) => {
|
|
if (a.chapterNum === 0) return 1;
|
|
if (b.chapterNum === 0) return -1;
|
|
return a.chapterNum - b.chapterNum;
|
|
});
|
|
for(let i = 0; i<chaptersToContinue.length; i++)
|
|
{
|
|
if(chaptersToContinue[i].byPass) continue
|
|
if(!chaptersToContinue[i].mangaLink)
|
|
{
|
|
const {scanlator, link, title, chapterNum} = chaptersToContinue[i];
|
|
const {List, img} = await new Manga(scanlator, link, title).get();
|
|
if(!List.some(item=>item.num == chapterNum)) return;
|
|
let chapter = await new Chapter(scanlator,List[chapterNum-1].link, title,chapterNum).get();
|
|
chaptersToContinue[i].link = chapter.link;
|
|
let index = chapter.List.indexOf(chaptersToContinue[i].lastImageRead)
|
|
const imgReadOutOfTotal = (index !=-1 ? index:0)+'/'+(chapter.List.length-1)
|
|
let aux = {...chaptersToContinue[i]._doc, mangaCoverImg:img, imgReadOutOfTotal}
|
|
chaptersToContinue[i] = aux;
|
|
}
|
|
}
|
|
res.render('chaptersToContinue.ejs', {data:chaptersToContinue});
|
|
}
|
|
async function getNextChapter (chapter)
|
|
{
|
|
const {scanlator, link, title, _id } = chapter;
|
|
let chapterNum = chapter.chapterNum;
|
|
const manga = await new Manga(scanlator, link, title).get();
|
|
if((chapterNum+1)>manga.latestChap) return false;
|
|
if(!manga.List.some(item=>item.num == (chapterNum+1))) chapterNum = Math.trunc(chapterNum);
|
|
const chapterFromList = manga.List.filter(item=>item.num == (chapterNum+1))[0]
|
|
const {date, num} = chapterFromList;
|
|
const nextChapter = await new Chapter(scanlator, chapterFromList.link, title, (chapterNum+1)).get();
|
|
let index = chapter.lastImageRead?nextChapter.List.indexOf(chapter.lastImageRead):-1;
|
|
const imgReadOutOfTotal = (index !=-1 ? index:0)+'/'+(nextChapter.List.length-1)
|
|
return {
|
|
...nextChapter,
|
|
num,
|
|
date,
|
|
_id,
|
|
mangaCoverImg:manga.img,
|
|
chapterNum:nextChapter.chNum,
|
|
lastImageRead:nextChapter.List[0],
|
|
completely:false,
|
|
imgReadOutOfTotal,
|
|
byPass:true
|
|
};
|
|
}
|
|
exports.chapterToContinue = async (req, res)=>
|
|
{
|
|
const {scanlator, mangaLink, title} = req.params;
|
|
const chaptersRead = await ChapterModel.find({scanlator, title}).then(res=>{return res});
|
|
let sorted = chaptersRead.sort((a,b)=>{return b.chapterNum-a.chapterNum});
|
|
const newChapter = await getNextChapter({scanlator, link:mangaLink, title,chapterNum:parseFloat(sorted[0].chapterNum), _id:null});
|
|
res.render('chapterButton.ejs',{chapter:newChapter, scanlator, title,shortCut:true})
|
|
|
|
}
|
|
exports.removeChapter = async (req, res)=>
|
|
{
|
|
const cookieStr = req.headers.cookie;
|
|
if(!cookieStr) return res.sendStatus(404);
|
|
if(!isValidID(req.params.id)) return res.sendStatus(404);
|
|
const {link, scanlator, title, chapterNum} = await ChapterModel.findOneAndDelete({_id:req.params.id}).then(deleted=>{return deleted});
|
|
const {List} = await new Manga(scanlator, link, title).get();
|
|
const chapter = List.filter(item=>item.num == chapterNum)[0];
|
|
res.render('chapterButton.ejs', {chapter, scanlator, title, shortCut:false});
|
|
}
|
|
exports.dashboard = async (req, res)=>
|
|
{
|
|
let data = 0;
|
|
res.render('dashboard.ejs', {data});
|
|
}
|