second success :D commit

This commit is contained in:
2025-10-10 23:32:24 -04:00
parent 06b59a3a99
commit 4ae53e264b
192 changed files with 2178 additions and 6815 deletions
+128 -39
View File
@@ -13,7 +13,7 @@ const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const multer = require('multer');
const unzipper = require('unzipper');
const Unrar = require('unrar-promise');
const unrar = require('node-unrar-js');
const sharp = require('sharp');
const rateLimit = require('express-rate-limit');
require('dotenv').config();
@@ -330,7 +330,7 @@ const upload = multer({
const allowedTypes = /cbz|zip|cbr|rar/;
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
if (extname) {
cb(null, true); // Must call cb with null and a boolean
cb(null, true);
} else {
cb(new Error('Only CBZ, CBR, ZIP files are allowed'), false);
}
@@ -352,7 +352,7 @@ app.post('/api/upload', authenticateToken, upload.single('comic'), async (req, r
try {
// Create directory for this manga
const mangaDir = path.join('library', `manga_${Date.now()}`);
await fs.mkdir(mangaDir, { recursive: true });
await mkdir(mangaDir, { recursive: true });
// Extract comic file
await extractComic(filePath, mangaDir);
@@ -376,7 +376,7 @@ app.post('/api/upload', authenticateToken, upload.single('comic'), async (req, r
await scanChapters(mangaId, mangaDir);
// Clean up uploaded file
await fs.unlink(filePath);
await unlink(filePath);
res.json({
message: 'Manga uploaded successfully',
@@ -394,60 +394,149 @@ app.post('/api/upload', authenticateToken, upload.single('comic'), async (req, r
async function extractComic(filePath, destDir) {
const fileExt = path.extname(filePath).toLowerCase();
console.log('Extracting file:', filePath, 'Extension:', fileExt);
if (fileExt === '.cbz' || fileExt === '.zip') {
// Use unzipper for .cbz and .zip files
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(filePath);
const extractStream = unzipper.Extract({ path: destDir });
extractStream.on('entry', (entry) => {
if (entry.path.includes('../') || entry.path.includes('..\\')) {
entry.autodrain();
} else {
entry.pipe(extractStream);
}
});
readStream
.pipe(extractStream)
.on('close', resolve)
fs.createReadStream(filePath)
.pipe(unzipper.Extract({ path: destDir }))
.on('close', () => {
console.log('ZIP extraction complete');
resolve();
})
.on('error', reject);
});
} else if (fileExt === '.cbr' || fileExt === '.rar') {
// Use unrar-promise for .cbr and .rar files
// This library might need to be used differently
const unrar = new Unrar(filePath, {
strip: 0 // Don't strip any path components
});
return new Promise((resolve, reject) => {
unrar.extract({
path: destDir,
}, (err) => {
if (err) {
reject(new Error('Failed to extract RAR archive: ' + err.message));
} else {
resolve();
}
// Use node-unrar-js for .cbr and .rar files
try {
console.log('Reading RAR file...');
const buf = fs.readFileSync(filePath);
// Create extractor
const extractor = await unrar.createExtractorFromData({
data: buf
});
});
console.log('Extracting RAR contents...');
// Extract all files
const extracted = extractor.extractAll();
// Get the list of extracted files
const extractedFiles = [...extracted.files];
for (const file of extractedFiles) {
const { extraction, fileHeader } = file;
// Skip directories
if (fileHeader.flags.directory) continue;
const destPath = path.join(destDir, fileHeader.name);
// Create directory if needed
const dirPath = path.dirname(destPath);
if (!fs.existsSync(dirPath)) {
await mkdir(dirPath, { recursive: true });
}
// Write file
if (extraction) {
fs.writeFileSync(destPath, extraction);
}
}
console.log('RAR extraction complete');
} catch (err) {
console.error('RAR extraction error:', err);
// Fallback: try using the command line unrar if available
try {
console.log('Trying fallback RAR extraction...');
await extractRarFallback(filePath, destDir);
} catch (fallbackError) {
console.error('Fallback RAR extraction also failed:', fallbackError);
throw new Error('Failed to extract RAR file: ' + err.message);
}
}
} else {
throw new Error('Unsupported file type: ' + fileExt);
}
}
// Fallback RAR extraction using command line
async function extractRarFallback(filePath, destDir) {
return new Promise((resolve, reject) => {
const { spawn } = require('child_process');
// Try different unrar command names
const commands = ['unrar', 'rar', 'unrar-free'];
const tryExtract = (index) => {
if (index >= commands.length) {
reject(new Error('No RAR extraction command found. Please install unrar.'));
return;
}
const command = commands[index];
const args = ['x', '-o+', filePath, destDir + '/'];
console.log(`Trying command: ${command} ${args.join(' ')}`);
const process = spawn(command, args);
process.on('close', (code) => {
if (code === 0) {
console.log('Fallback RAR extraction successful');
resolve();
} else {
console.log(`Command ${command} failed with code ${code}, trying next...`);
tryExtract(index + 1);
}
});
process.on('error', (err) => {
console.log(`Command ${command} not found: ${err.message}`);
tryExtract(index + 1);
});
};
tryExtract(0);
});
}
async function generateCover(mangaDir) {
try {
const files = await readdir(mangaDir);
const imageFile = files.find(f => /\.(jpg|jpeg|png|webp)$/i.test(f));
// Recursively find image files
const findImageFiles = async (dir) => {
const items = await readdir(dir);
const imageFiles = [];
for (const item of items) {
const itemPath = path.join(dir, item);
const stats = await stat(itemPath);
if (stats.isDirectory()) {
const subImages = await findImageFiles(itemPath);
imageFiles.push(...subImages);
} else if (/\.(jpg|jpeg|png|webp)$/i.test(item)) {
imageFiles.push(itemPath);
}
}
return imageFiles.sort();
};
if (imageFile) {
const imageFiles = await findImageFiles(mangaDir);
if (imageFiles.length > 0) {
const firstImage = imageFiles[0];
const coverName = `cover_${Date.now()}.jpg`;
const coverPath = path.join('covers', coverName);
// Resize and optimize cover image
await sharp(path.join(mangaDir, imageFile))
await sharp(firstImage)
.resize(300, 450, { fit: 'cover' })
.jpeg({ quality: 80 })
.toFile(coverPath);
@@ -529,4 +618,4 @@ const startServer = async () => {
});
};
startServer();
startServer();