135 lines
5.0 KiB
JavaScript
135 lines
5.0 KiB
JavaScript
const fs = require('fs').promises;
|
|
const path = require('path');
|
|
const helpers = require('../utils/helpers');
|
|
const ColaMangaScraper = require('../scrapers/ColaMangaScraper');
|
|
const MangaPoster = require('../posters/MangaPoster');
|
|
const config = require('../config/config');
|
|
|
|
/**
|
|
* Main class for downloading manga
|
|
*/
|
|
class MangaDownloader {
|
|
constructor() {
|
|
this.outputDir = config.baseDir;
|
|
this.maxConcurrentManga = config.getDownloadSetting('maxConcurrentDownloads');
|
|
this.mangaPoster = new MangaPoster();
|
|
}
|
|
|
|
/**
|
|
* Initialize the downloader
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async init_colamanga_scraper() {
|
|
this.mangaUrls = await helpers.readJsonFile(config.mangaUrlsPath) || {};
|
|
this.scraper = new ColaMangaScraper();
|
|
await this.scraper.init();
|
|
}
|
|
|
|
/**
|
|
* Download a single manga with memory management
|
|
* @param {string} mangaName - Manga name
|
|
* @param {string} mangaUrl - Manga URL
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async downloadManga(mangaName, mangaUrl) {
|
|
console.log(`Getting Manga: ${mangaName} info`);
|
|
|
|
try {
|
|
const mangaDetails = await this.scraper.getMangaInfo(mangaUrl);
|
|
|
|
// // Save manga info
|
|
// const infoPath = path.join(this.outputDir, 'info.json');
|
|
// await fs.writeFile(infoPath, JSON.stringify(mangaDetails, null, 2));
|
|
|
|
// Load manga info
|
|
// const mangaDetails = await helpers.readJsonFile(path.join(this.outputDir, 'info.json'));
|
|
|
|
const mangaDir = path.join(this.outputDir, mangaName);
|
|
await helpers.ensureDirectory(mangaDir);
|
|
|
|
// Save cover image
|
|
if (mangaDetails.coverPic && await helpers.fileExists(path.join(mangaDir, 'cover.jpg')) == false) {
|
|
const coverPath = path.join(mangaDir, 'cover.jpg');
|
|
await fs.writeFile(coverPath, mangaDetails.coverPic);
|
|
mangaDetails.coverPic = null;
|
|
}
|
|
|
|
const { chapters, coverPic, genres, ...mangaInfo } = mangaDetails;
|
|
let response = await this.mangaPoster.getMangaId(mangaDetails.mangaName);
|
|
|
|
if (response.success == true) {
|
|
mangaDetails.mangaId = response.mangaId;
|
|
}
|
|
else {
|
|
mangaDetails.mangaId = helpers.generateId();
|
|
this.mangaPoster.insertMangaInfo({mangaId: mangaDetails.mangaId, ...mangaInfo});
|
|
this.mangaPoster.insertMangaGenres(mangaDetails.mangaId, genres);
|
|
}
|
|
|
|
// Download chapters in sequence to manage memory
|
|
for (const chapter of chapters) {
|
|
if (await helpers.isDirectoryNotEmpty(path.join(mangaDir, chapter.chapterName))) {
|
|
console.log(`Skipping Manga: ${mangaDetails.mangaName}, Chapter: ${chapter.order} - ${chapter.chapterName} as it already exists`);
|
|
continue;
|
|
}
|
|
console.log(`Downloading Manga: ${mangaDetails.mangaName}, Chapter: ${chapter.order} - ${chapter.chapterName}`);
|
|
const chapterDir = path.join(mangaDir, chapter.chapterName);
|
|
await helpers.ensureDirectory(chapterDir);
|
|
await this.scraper.downloadChapterPics(chapter, chapterDir);
|
|
await this.mangaPoster.insertMangaChapter({
|
|
mangaId: mangaDetails.mangaId,
|
|
chapterName: chapter.chapterName,
|
|
chapterOrder: chapter.order
|
|
});
|
|
await this.scraper.cleanup();
|
|
|
|
// Add smart delay between chapter downloads
|
|
const delay = helpers.getSmartDelay();
|
|
console.log(`Waiting ${helpers.formatDelay(delay)} before next chapter...`);
|
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
}
|
|
|
|
console.log(`Completed downloading: ${mangaName}`);
|
|
} catch (error) {
|
|
throw new Error(`Failed to download manga ${mangaName}: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run the downloader with memory management
|
|
* @returns {Promise<Object>} Download results
|
|
*/
|
|
async run() {
|
|
const results = {
|
|
successful: [],
|
|
failed: []
|
|
};
|
|
|
|
try {
|
|
// Process manga sequentially to manage memory
|
|
for (const [mangaName, mangaUrl] of Object.entries(this.mangaUrls)) {
|
|
try {
|
|
await this.downloadManga(mangaName, mangaUrl);
|
|
results.successful.push(mangaName);
|
|
|
|
// Clean up after each manga
|
|
await this.scraper.cleanup();
|
|
|
|
// Force garbage collection
|
|
if (global.gc) {
|
|
global.gc();
|
|
}
|
|
} catch (error) {
|
|
results.failed.push({ name: mangaName, error: error.message });
|
|
}
|
|
}
|
|
} finally {
|
|
await this.scraper.close();
|
|
}
|
|
|
|
return results;
|
|
}
|
|
}
|
|
|
|
module.exports = MangaDownloader;
|