second success :D commit
This commit is contained in:
+128
-39
@@ -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();
|
||||
Reference in New Issue
Block a user