const find = require('findit'); const path = require('path'); const mongoose = require('mongoose'); const crypto = require('crypto'); module.exports.Modules = class Modules { /** * * @param {String} name Scanlator Name - Same as in the Module. */ constructor (name=null) { this.name = name; this.Modules; } /** * @returns {Array} * @typedef {Class} Modules */ getAll() { let finder = find(path.resolve('Modules')); finder.on('file', file => { let Module = require(file); if (Module.constructor) { this.Modules.push(Module); } }); finder.on('end', () => { return {Modules:this.Modules} }); } /** * * @returns {Boolean} */ async exists() { if (!this.name) return false; return new Promise((resolve, reject) => { const finder = find(path.resolve('Modules')); finder.on('file', async file => { let Module = require(file); if (Module.constructor) { const test = new Module(); if (this.name == test.scanlator) { finder.removeAllListeners(); resolve(true); } } }); finder.on('error', err => { reject(false); }); finder.on('end', () => { resolve(false); }); }); } /** * * @param {String} title * @returns {Boolean} */ async titleExists(title) { if(!this.name) return false; const module = require(`./Modules/${this.name.split('-')[0]+'Module'}`); return await new module().hasTitle(title); } } module.exports.Search = class Search { /** * * @param {String} searchString - String to search by. * @param {Scanlator} scanlator - Scanlator Name * @typedef {String} Scanlator * @pattern /^[\w-]+-scans$/ */ constructor(searchString, scanlator=null) { this.ss = searchString; this.scanlator = scanlator; this.Modules = []; this.results = []; this.initialized = new Promise((resolve) => { let finder = find(path.resolve('Modules')); finder.on('file', file => { let Module = require(file); if (Module.constructor) { this.Modules.push(Module); } }); finder.on('end', () => { resolve(); }); }) } /** * * @returns {Array} * @typedef {Object} Results * @property {Array} Results * @property {Scanlator} scanlator * @typedef {Object} Manga * @property {String} link - Manga Link * @property {String} title - Manga Title * @property {String} img - Image Link * @typedef {String} Scanlator * @pattern /^[\w-]+-scans$/ */ async search() { await this.initialized; if(this.Modules.length==0) return for(const module of this.Modules) { const auxModule = new module() if(!this.scanlator || this.scanlator == auxModule.scanlator ) try { var results = await auxModule.Search(this.ss); //TODO: Defaults for missing info from the modules; // console.log('Lib: Search: search method: result:', result); results.sort((a, b) => { if (a.title.toUpperCase() < b.title.toUpperCase()) return -1; else return 1; }); for(var i = 0; i< results.length; i++) { results[i]['Status'] = results[i].status; } this.results.push({ Results:results, scanlator:auxModule.scanlator }); } catch (error) { console.error('Lib: Module Errored:', auxModule.scanlator, 'Error:', error) } } return this.results } /** * SLOWER * @returns {Array} * @typedef {Object} Results * @property {Array} Results * @property {Scanlator} scanlator * @typedef {Object} Manga * @property {String} link - Manga Link * @property {String} title - Manga Title * @property {String} img - Image Link * @property {Array} tags - Manga Tags * @typedef {String} Scanlator * @pattern /^[\w-]+-scans$/ */ async searchExtraInfo() { await this.initialized; if(this.Modules.length==0) return for(const module of this.Modules) { const auxModule = new module() if(!this.scanlator || this.scanlator == auxModule.scanlator ) try { var results = await auxModule.Search(this.ss); //TODO: Defaults for missing info from the modules; // console.log('Lib: Search: search method: result:', result); for(let i = 0; i { if (a.title.toUpperCase() < b.title.toUpperCase()) return -1; else return 1; }); for(var i = 0; i< results.length; i++) { results[i]['Status'] = results[i].status; } this.results.push({ Results:results, scanlator:auxModule.scanlator }); } catch (error) { console.error('Lib: Module Errored:', auxModule.scanlator, 'Error:', error) } } return this.results } } module.exports.SearchByTag = class SearchByTag { /** * * @param {String} tag */ constructor(tag) { this.ss = tag.toLowerCase(); this.Modules = []; this.results = []; this.initialized = new Promise((resolve) => { let finder = find(path.resolve('Modules')); finder.on('file', file => { let Module = require(file); if (Module.constructor) { this.Modules.push(Module); } }); finder.on('end', () => { resolve(); }); }) } /** * @param {Number} ammount - 5 by default * @returns {Array} * @typedef {Object} Results * @property {Array} Results * @property {Scanlator} scanlator * @typedef {Object} Manga * @property {String} link - Manga Link * @property {String} title - Manga Title * @property {String} img - Image Link * @typedef {String} Scanlator * @pattern /^[\w-]+-scans$/ */ async search(ammount=5) { await this.initialized; if(this.Modules.length==0) return for(const module of this.Modules) { const auxModule = new module() try { let results = await auxModule.SearchByTag(this.ss, ammount); //TODO: Defaults for missing info from the modules; if(results) { results.sort((a, b) => { if (a.title.toUpperCase() < b.title.toUpperCase()) return -1; else return 1; }); for(let i = 0; i< results.length; i++) { let manga = await auxModule.GetManga(results[i].link, results[i].title); results[i] = {...manga}; } this.results.push({ Results:results, scanlator:auxModule.scanlator }); } } catch (error) { console.error('Lib: Module Errored:', auxModule.scanlator, 'Error:', error) } } return this.results } } module.exports.Manga = class Manga { /** * * @param {Scanlator} scanlator * @param {Link} link Manga Link * @param {String} title * @typedef {String} Link * @pattern ^(https?:\/\/)?([\w\d.-]+)\.([a-z]{2,})(:[0-9]+)?(\/\S*)?$ * @typedef {String} Scanlator * @pattern /^[\w-]+-scans$/ */ constructor(scanlator, link, title) { this.scanlator = scanlator; this.link = link; this.title = title; this.Engine; this.initialized = new Promise((resolve) => { let finder = find(path.resolve('Modules')); finder.on('file', file => { let Module = require(file); if (Module.constructor) { if(this.scanlator == new Module().scanlator) { this.Engine = Module; resolve(); } } }); }) } /** * * @returns {Manga} * @typedef {Object} Manga * @property {String} link - Manga Link * @property {String} title - Manga Title * @property {String} img - Image Link * @property {Array} tags - Manga Tags * @property {Array} List - Chapter List * @typedef {Object} Chapter * @property {Number} num - Chapter Number * @property {Link} link - Chapter Link * @property {Date_} date - Chapter uploaded date * @typedef {String} Link * @typedef {String} Date_ */ async get() { await this.initialized; try { const Scanlator = new this.Engine(); const Manga = await Scanlator.GetManga(this.link, this.title); let List = await Scanlator.ChapterList(this.link, this.title); List = List.sort((a,b)=> { return a.num-b.num; }) const Status = Manga.Status?Manga.Status:Manga.status; //TODO: Defaults for missing info from the modules; return {...Manga, Status, link:this.link, scanlator:this.scanlator, List, title:this.title} } catch (error) { console.error('Lib: Manga: Error:' ,error) } } } module.exports.Chapter = class Chapter { /** * * @param {Scanlator} scanlator - Scanlator Name * @param {Link} Link - Chapter Link * @param {String} title - Manga Title * @param {Number} chNum - Chapter Number * @typedef {String} Link * @pattern ^(https?:\/\/)?([\w\d.-]+)\.([a-z]{2,})(:[0-9]+)?(\/\S*)?$ * @typedef {String} Scanlator * @pattern /^[\w-]+-scans$/ */ constructor(scanlator, Link,title, chNum) { this.scanlator = scanlator; this.title = title; this.Link = Link; this.chNum = chNum; this.Engine; this.initialized = new Promise((resolve) => { let finder = find(path.resolve('Modules')); finder.on('file', file => { let Module = require(file); if (Module.constructor) { if(this.scanlator == new Module().scanlator) { this.Engine = Module; resolve(); } } }); }) } /** * * @returns {Chapter} * @typedef {Object} Chapter * @property {Manga} Manga - Manga Link * @property {Array} List - Chapter List of images * @property {Number} chNum - Chapter number * @typedef {Object} Manga * @property {String} link - Manga Link * @property {String} title - Manga Title * @property {String} img - Image Link * @typedef {String} Link * @typedef {Link} Img */ async get() { await this.initialized; try { const Scanlator = new this.Engine(); const {List, mangaLink} = await Scanlator.Chapter(this.Link); const {latestChap } = await Scanlator.GetManga(mangaLink); //TODO: Defaults for missing info from the modules; return {List, latestChap,mangaLink, chNum:parseInt(this.chNum), link:this.Link,title:this.title, scanlator:this.scanlator} } catch (error) { console.error('Lib: Chapter: Error:', error) } } async getMangaLink() { await this.initialized; try { const Scanlator = new this.Engine(); const {mangaLink} = await Scanlator.Chapter(this.Link); return mangaLink; } catch (error) { console.error('Lib: Chapter: Error:', error); } } }; exports.decodeHTML = (encodedString)=> { return encodedString.replace(/ |&#(\d+);|\n/g, function(match, dec) { if (dec) { return String.fromCharCode(dec); // Replace HTML character codes } else { return ''; // Remove newline characters } }); } exports.isValidID = (id) => { return mongoose.Types.ObjectId.isValid(id); } /** * * @param {String} cookie * @param {String} name * @returns */ exports.cookieParser = (cookie, name)=> { if(!cookie.includes(name)) return null return cookie.split(name+'=')[1].split(';')[0]; } /** * @argument {Response} res - Response: express response object. * @argument {String} name - Name of the cookie. * @argument {*} value - Value of the cookie. */ exports.Baker = async (res, name, value, maxAge =2) => { await res.cookie(name, '',{maxAge:0}); await res.cookie(name, value, {maxAge:hours(2),path:'/',secure:false,httpOnly:false}); return; } /** * Use it to transform an hour value into miliseconds. * @param {Number} Hours - Hours to transform */ function hours(Hours) { return Hours*60*60*1000; } exports.Crypto = class Crypto { /** * * @param {String} username * @param {String} password */ constructor(username, password) { this.salt = '4$3H6Q9B8d4VXcE3Rft4'; this.secret = '7xJYrX@KF7oSNr9Zm4UH'; this.pepper = String.fromCharCode(this.getRandomInt(65, 90)); this.password = password; this.user = username; this.algorithm = 'aes-192-cbc'; } get Hash() // to use on register { return this.hash() } hash(p) { if(!p) { return crypto.createHmac('sha256', this.secret).update(this.user+this.password+this.salt+this.pepper).digest('hex'); } else { return crypto.createHmac('sha256', this.secret).update(this.user+this.password+this.salt+p).digest('hex'); } } getRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min)) + min; } }