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.
540 lines
15 KiB
540 lines
15 KiB
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<Modules>}
|
|
* @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<Results>}
|
|
* @typedef {Object} Results
|
|
* @property {Array<Manga>} 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<Results>}
|
|
* @typedef {Object} Results
|
|
* @property {Array<Manga>} Results
|
|
* @property {Scanlator} scanlator
|
|
* @typedef {Object} Manga
|
|
* @property {String} link - Manga Link
|
|
* @property {String} title - Manga Title
|
|
* @property {String} img - Image Link
|
|
* @property {Array<String>} 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<results.length; i++)
|
|
{
|
|
const manga = await auxModule.GetManga(results[i].link, results[i].title);
|
|
results[i]['tags'] = manga.tags;
|
|
results[i]['description'] = manga.description;
|
|
}
|
|
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
|
|
}
|
|
|
|
}
|
|
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<Results>}
|
|
* @typedef {Object} Results
|
|
* @property {Array<Manga>} 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<String>} tags - Manga Tags
|
|
* @property {Array<Chapter>} 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<Img>} 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;
|
|
}
|
|
}
|