Added:
- CbrToCbzConverter.php - import now convert .cbr to .cbz - import improvement, multiple files
This commit is contained in:
65
src/Controller/ConversionController.php
Normal file
65
src/Controller/ConversionController.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Service\CbrToCbzConverter;
|
||||
use App\Service\NotificationService;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class ConversionController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CbrToCbzConverter $cbrToCbzConverter,
|
||||
private readonly NotificationService $notificationService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/convert', name: 'app_convert')]
|
||||
public function convert(Request $request): Response
|
||||
{
|
||||
if ($request->isMethod('POST')) {
|
||||
/** @var UploadedFile $file */
|
||||
$file = $request->files->get('file');
|
||||
|
||||
if ($file && $file->getClientOriginalExtension() === 'cbr') {
|
||||
$originalFileName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
|
||||
$tempFilePath = $file->getPathname();
|
||||
|
||||
try {
|
||||
$cbzPath = $this->cbrToCbzConverter->convert($tempFilePath);
|
||||
|
||||
$response = new BinaryFileResponse($cbzPath);
|
||||
$response->setContentDisposition(
|
||||
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
|
||||
$originalFileName . '.cbz'
|
||||
);
|
||||
$response->headers->set('Content-Type', 'application/x-cbz');
|
||||
$response->headers->set('Turbo-Visit-Control', 'reload');
|
||||
|
||||
$response->deleteFileAfterSend(true);
|
||||
|
||||
return $response;
|
||||
} catch (\Exception $e) {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Une erreur est survenue lors de la conversion : ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Veuillez sélectionner un fichier CBR valide.'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('conversion/index.html.twig');
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace App\Controller;
|
||||
|
||||
use App\Repository\ChapterRepository;
|
||||
use App\Repository\MangaRepository;
|
||||
use App\Service\CbrToCbzConverter;
|
||||
use App\Service\CbzService;
|
||||
use App\Service\MangaImportService;
|
||||
use App\Service\NotificationService;
|
||||
@@ -18,16 +19,15 @@ use Symfony\Component\String\Slugger\SluggerInterface;
|
||||
|
||||
class ImportController extends AbstractController
|
||||
{
|
||||
private const UPLOADS_DIRECTORY = 'public/uploads';
|
||||
private const string UPLOADS_DIRECTORY = 'public/uploads';
|
||||
|
||||
public function __construct(
|
||||
private readonly string $projectDir,
|
||||
private readonly CbzService $cbzService,
|
||||
private readonly MangaImportService $mangaImportService,
|
||||
// private SluggerInterface $slugger,
|
||||
private NotificationService $notificationService,
|
||||
private MangaRepository $mangaRepository,
|
||||
private ChapterRepository $chapterRepository
|
||||
private readonly string $projectDir,
|
||||
private readonly CbzService $cbzService,
|
||||
private readonly MangaImportService $mangaImportService,
|
||||
private readonly NotificationService $notificationService,
|
||||
private readonly MangaRepository $mangaRepository,
|
||||
private readonly CbrToCbzConverter $cbrToCbzConverter
|
||||
)
|
||||
{
|
||||
|
||||
@@ -36,27 +36,44 @@ class ImportController extends AbstractController
|
||||
#[Route('/manga/import', name: 'app_manga_import')]
|
||||
public function index(Request $request, SessionInterface $session): Response
|
||||
{
|
||||
if ($request->isMethod('post')) {
|
||||
$file = $request->files->get('file');
|
||||
if ($file && $file->getClientOriginalExtension() === 'cbz') {
|
||||
$originalFileName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
|
||||
$filename = uniqid() . '.' . $file->getClientOriginalExtension();
|
||||
if ($request->isMethod('POST')) {
|
||||
$files = $request->files->get('files');
|
||||
if ($files) {
|
||||
$importFiles = [];
|
||||
foreach ($files as $file) {
|
||||
if ($file && in_array($file->getClientOriginalExtension(), ['cbz', 'cbr'])) {
|
||||
$originalFileName = $file->getClientOriginalName();
|
||||
$filename = uniqid() . '.' . $file->getClientOriginalExtension();
|
||||
|
||||
try {
|
||||
$file->move($this->projectDir . '/' . self::UPLOADS_DIRECTORY, $filename);
|
||||
$session->set('import_file_path', $this->projectDir . '/' .self::UPLOADS_DIRECTORY . '/' . $filename);
|
||||
$session->set('import_original_file_name', $originalFileName);
|
||||
try {
|
||||
$file->move($this->projectDir . '/' . self::UPLOADS_DIRECTORY, $filename);
|
||||
$importFiles[] = [
|
||||
'id' => uniqid(),
|
||||
'path' => $this->projectDir . '/' . self::UPLOADS_DIRECTORY . '/' . $filename,
|
||||
'original_name' => $originalFileName,
|
||||
];
|
||||
} catch (FileException $e) {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Une erreur est survenue lors de l\'import du fichier ' . $originalFileName,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Le fichier ' . $file->getClientOriginalName() . ' doit être au format CBZ ou CBR.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($importFiles)) {
|
||||
$session->set('import_files', $importFiles);
|
||||
return $this->redirectToRoute('import_match');
|
||||
} catch (FileException $e) {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Une erreur est survenue lors de l\'import du fichier.'
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Le fichier doit être au format CBZ.'
|
||||
'message' => 'Aucun fichier n\'a été sélectionné.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -70,128 +87,134 @@ class ImportController extends AbstractController
|
||||
#[Route('/import/match', name: 'import_match')]
|
||||
public function match(Request $request, SessionInterface $session): Response
|
||||
{
|
||||
$filePath = $session->get('import_file_path');
|
||||
$originalFileName = $session->get('import_original_file_name');
|
||||
if (!$filePath || !$originalFileName) {
|
||||
$files = $session->get('import_files', []);
|
||||
if (empty($files)) {
|
||||
return $this->redirectToRoute('app_manga_import');
|
||||
}
|
||||
|
||||
$metadata = $this->cbzService->extractMetadata($filePath, $originalFileName);
|
||||
if($metadata['title'] === '' || is_null($metadata['title'])){
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Impossible de détecter le titre du manga.'
|
||||
]);
|
||||
return $this->redirectToRoute('app_manga_import');
|
||||
}
|
||||
$processedFiles = [];
|
||||
foreach ($files as $fileId => $fileInfo) {
|
||||
$filePath = $fileInfo['path'];
|
||||
$originalFileName = $fileInfo['original_name'];
|
||||
|
||||
$mangas = $this->mangaRepository->findBySlug($metadata['title']);
|
||||
|
||||
$mangasChapters = [];
|
||||
foreach ($mangas as $manga) {
|
||||
if(!is_null($metadata['chapter'])){
|
||||
$chapters = $this->chapterRepository->findBy([
|
||||
'manga' => $manga,
|
||||
'number' => $metadata['chapter']
|
||||
]);
|
||||
$chapters = [$chapters[0]->getVolume() => $chapters];
|
||||
}else{
|
||||
$chapters = $this->chapterRepository->findBy([
|
||||
'manga' => $manga,
|
||||
'volume' => (int) $metadata['volume']
|
||||
]);
|
||||
|
||||
$chapters = [$metadata['volume'] => $chapters];
|
||||
$fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
|
||||
if (strtolower($fileExtension) === 'cbr') {
|
||||
$cbzPath = $this->cbrToCbzConverter->convert($filePath);
|
||||
$filePath = $cbzPath;
|
||||
$originalFileName = pathinfo($originalFileName, PATHINFO_FILENAME) . '.cbz';
|
||||
$files[$fileId]['path'] = $filePath;
|
||||
$files[$fileId]['original_name'] = $originalFileName;
|
||||
}
|
||||
$mangasChapters[$manga->getSlug()] = $chapters;
|
||||
|
||||
$metadata = $this->cbzService->extractMetadata($filePath, $originalFileName);
|
||||
$mangas = $this->mangaRepository->findBySlug($metadata['title']);
|
||||
|
||||
$mangaOptions = [];
|
||||
foreach ($mangas as $manga) {
|
||||
$mangaOptions[] = [
|
||||
'slug' => $manga->getSlug(),
|
||||
'title' => $manga->getTitle(),
|
||||
'author' => $manga->getAuthor(),
|
||||
'publicationYear' => $manga->getPublicationYear(),
|
||||
'genres' => $manga->getGenres(),
|
||||
'description' => $manga->getDescription()
|
||||
];
|
||||
}
|
||||
|
||||
$processedFiles[] = [
|
||||
'id' => $fileId,
|
||||
'originalFileName' => $originalFileName,
|
||||
'fileSize' => $this->formatBytes(filesize($filePath)),
|
||||
'metadata' => $metadata,
|
||||
'mangaOptions' => $mangaOptions
|
||||
];
|
||||
}
|
||||
|
||||
if(empty($mangas)) {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Aucun manga trouvé avec ce titre.'
|
||||
]);
|
||||
return $this->redirectToRoute('app_manga_search', ['query' => $metadata['title']]);
|
||||
}
|
||||
|
||||
if ($request->isMethod('post')) {
|
||||
$session->set('import_metadata', $request->request->all());
|
||||
return $this->redirectToRoute('import_confirm');
|
||||
}
|
||||
$session->set('import_files', $files);
|
||||
|
||||
return $this->render('import/match.html.twig', [
|
||||
'mangas' => $mangas,
|
||||
'volume' => $metadata['volume'],
|
||||
'chapters' => $mangasChapters
|
||||
'files' => $processedFiles
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/import/confirm', name: 'import_confirm')]
|
||||
private function formatBytes($bytes, $precision = 2) {
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
$bytes /= (1 << (10 * $pow));
|
||||
return round($bytes, $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
#[Route('/import/confirm', name: 'import_confirm', methods: ['POST'])]
|
||||
public function confirm(Request $request, SessionInterface $session): Response
|
||||
{
|
||||
if (!$request->isMethod('POST')) {
|
||||
return $this->redirectToRoute('app_manga_import');
|
||||
}
|
||||
$files = $session->get('import_files', []);
|
||||
$selectedFiles = $request->request->all('selected');
|
||||
$mangaSlugs = $request->request->all('manga_slug');
|
||||
$volumes = $request->request->all('volume');
|
||||
$chapters = $request->request->all('chapter');
|
||||
|
||||
$action = $request->request->get('action');
|
||||
$mangaSlug = $request->request->get('manga_slug');
|
||||
$volume = $request->request->get('volume');
|
||||
$importedFiles = [];
|
||||
$errors = [];
|
||||
|
||||
if ($action === 'confirm') {
|
||||
// Logique de confirmation
|
||||
$manga = $this->mangaRepository->findOneBy(['slug' => $mangaSlug]);
|
||||
if (!$manga) {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Manga non trouvé.'
|
||||
]);
|
||||
return $this->redirectToRoute('app_manga_import');
|
||||
foreach ($selectedFiles as $fileId) {
|
||||
if (!isset($files[$fileId])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filePath = $session->get('import_file_path');
|
||||
if (!$filePath) {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Fichier d\'import non trouvé.'
|
||||
]);
|
||||
return $this->redirectToRoute('app_manga_import');
|
||||
}
|
||||
$originalFileName = $session->get('import_original_file_name');
|
||||
|
||||
// Ici, vous pouvez ajouter la logique pour importer effectivement le fichier
|
||||
// Par exemple :
|
||||
// $this->mangaImportService->importVolume($manga, $volume, $filePath);
|
||||
$file = $files[$fileId];
|
||||
$mangaSlug = $mangaSlugs[$fileId] ?? null;
|
||||
$volume = $volumes[$fileId] ?? null;
|
||||
$chapter = $chapters[$fileId] ?? null;
|
||||
|
||||
try {
|
||||
$this->mangaImportService->importVolume($manga, (int)$volume, $filePath, $originalFileName);
|
||||
} catch (\Exception $e) {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Erreur lors de l\'import : ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
$manga = $this->mangaRepository->findOneBy(['slug' => $mangaSlug]);
|
||||
if (!$manga) {
|
||||
throw new \Exception('Manga non trouvé.');
|
||||
}
|
||||
|
||||
if(!is_null($chapter)){
|
||||
$chapter = $manga->getChapterByNumber($chapter);
|
||||
if (!$chapter) {
|
||||
throw new \Exception('Chapitre non trouvé.');
|
||||
}
|
||||
}
|
||||
|
||||
$importedFiles[] = $file['original_name'];
|
||||
$this->mangaImportService->importFile($manga, $volume, $chapter,$file['path']);
|
||||
} catch (\Exception $e) {
|
||||
$errors[] = "Erreur lors de l'import de {$file['original_name']} : " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Nettoyer les fichiers temporaires non importés
|
||||
foreach ($files as $fileId => $file) {
|
||||
if (!in_array($fileId, (array)$selectedFiles) && file_exists($file['path'])) {
|
||||
unlink($file['path']);
|
||||
}
|
||||
}
|
||||
|
||||
// Nettoyer la session
|
||||
$session->remove('import_files');
|
||||
|
||||
// Préparer le message de notification
|
||||
if (!empty($importedFiles)) {
|
||||
$successMessage = 'Fichiers importés avec succès : ' . implode(', ', $importedFiles);
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'success',
|
||||
'message' => 'Import confirmé avec succès.'
|
||||
]);
|
||||
|
||||
return $this->redirectToRoute('app_manga_show', ['mangaSlug' => $mangaSlug]);
|
||||
} elseif ($action === 'refuse') {
|
||||
// Logique de refus
|
||||
$filePath = $session->get('import_file_path');
|
||||
if ($filePath && file_exists($filePath)) {
|
||||
unlink($filePath); // Supprime le fichier temporaire
|
||||
}
|
||||
$session->remove('import_file_path');
|
||||
$session->remove('import_original_file_name');
|
||||
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'info',
|
||||
'message' => 'Import refusé. Le fichier a été supprimé.'
|
||||
'message' => $successMessage
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('app_manga_import');
|
||||
if (!empty($errors)) {
|
||||
$errorMessage = implode("\n", $errors);
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => $errorMessage
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('app_manga');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user