Les endpoints de téléchargement chapitre/volume plantaient (500 "file does
not exist") car le FileService traitait `pagesDirectory` comme un CBZ. Le
service reconstruit maintenant l'archive à la volée à partir des images du
dossier, et le nom du fichier chapitre inclut le titre du manga et le numéro.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Correction du dropdown toolbar : prop align (left/right) pour éviter le débordement hors écran côté droit
- Filtre de collection par statut (all/completed/ongoing) persisté dans userPreferencesStore
- toolbarConfig rendu réactif (computed) avec isSelected sur Filter, Sort et View
- Modale Options d'affichage par vue (Grille, Overview, Table) avec toggles persistés
- Composant ToggleRow réutilisable
- Normalisation author → authors dans l'entité Manga (l'API renvoie author string)
synchronizeChapters() retournait des UUID temporaires générés en mémoire. Ces UUID
n'étant jamais persistés, le Scraping domain ne pouvait pas retrouver le chapitre
(SQLSTATE 22P02 : invalid input syntax for type integer).
- ChapterSynchronizationServiceInterface : retourne float[] (numéros) au lieu de string[] (UUID)
- MangadxChapterSynchronizationService : retourne getNumber() au lieu de getId()
- RefreshMangaChaptersHandler : après save(), retrouve chaque chapitre par manga+numéro
via findChapterByMangaIdAndNumber() pour obtenir le vrai PK integer avant de dispatcher
ChapterReadyForScraping
Sans ce wrapper #[AsMessageHandler], Messenger ne trouvait aucun handler pour
le message CheckMonitoredMangas — le scheduler et la commande console échouaient
silencieusement avec NoHandlerForMessageException.
L'entrypoint faisait rm -rf var/cache/prod puis lançait FrankenPHP.
FrankenPHP compilait partiellement le container DI pendant que le script
Deployer lançait aussi cache:clear → fichiers manquants → crash.
- entrypoint.sh : ajouter cache:warmup après rm -rf, avant exec FrankenPHP
(l'entrypoint est séquentiel, FrankenPHP ne démarre qu'une fois le cache prêt)
- deploy.php : supprimer le docker exec cache:clear devenu inutile et dangereux
- MonitoringSchedule : supprimer la date passée au message (était évaluée une
seule fois au démarrage du container, rendant la requête caduque après le
premier cycle)
- CheckMonitoredMangasHandler : calculer `since` dynamiquement à l'exécution
(`new \DateTimeImmutable('-2 hours')`) plutôt que de dépendre du message
- AutoScrapingListener : corriger le TypeError silencieux — créer un ScrapingJob
avant d'appeler ScrapeChapterHandler (paramètre jobId manquant)
Ajoute les tests unitaires CheckMonitoredMangasHandlerTest et AutoScrapingListenerTest.
Quand un chapitre téléchargé est seul dans son groupe (volumeChapterCount === 1),
on affichait "Chapitre 42" au lieu du titre réel. La condition isVolumeGroup
s'appliquait même pour les groupes à un seul élément.
Fix : la mise en forme "Chapitres X-Y" n'est désormais appliquée que lorsque
volumeChapterCount > 1, sinon on affiche chapter.title comme pour les chapitres
non téléchargés.
Sans ce fix, les workers FrankenPHP démarrent avec l'ancien cache persisté
dans le volume Docker. Si les classes référencées (ex. EntityManagerGhost,
LazyGhostTrait) ne correspondent plus à la version déployée, les workers
crashent en boucle, rendant le conteneur instable et faisant échouer le
cache:clear lancé ensuite par Deployer (exit 137).
La suppression de var/cache/prod à l'entrypoint garantit que les workers
démarrent toujours sur un cache vierge, généré à chaud à la première requête.
- worker.Caddyfile : limiter à 2 workers FrankenPHP pour éviter l'OOM
lors du cache:clear en prod (chaque worker charge le kernel Symfony
complet, la valeur par défaut = nb de CPUs était trop élevée)
- Dockerfile : supprimer les COPY des assets UX (ux-live-component,
ux-react, ux-turbo) supprimés de composer.json
PHP : suppression de symfony/stimulus-bundle, ux-live-component, ux-react,
ux-turbo, twig/extra-bundle et symfony/form (plus utilisés depuis la
migration vers Vue.js SPA).
npm : suppression de @hotwired/stimulus, @hotwired/turbo, react, react-dom,
alpinejs, bootstrap, daisyui, sortablejs, vuedraggable et leurs types.
Corrige l'erreur de déploiement causée par vitest@^4.1.0 (introuvable)
requis par les anciens packages @symfony/ux-react et @symfony/ux-turbo v2.33.0.
- ContentSourceForm.vue : convertir testChapterNumber en float/null avant
envoi (évite d'envoyer "" pour ?float, rejeté par Symfony 8 strict)
- UpsertContentSourceResource : ajouter collectDenormalizationErrors: true
pour que les erreurs de type retournent 422 au lieu de 400 via le
chemin input: de API Platform 4
- ContentSource entity : corriger setImageSelector(string) → setImageSelector(?string)
cohérent avec la colonne nullable
- Ajouter les tests manquants (testChapterNumber float/null/chaîne vide)
qui auraient détecté ces bugs plus tôt
La migration vers DBAL 4 a changé le type de colonne genres de
Types::ARRAY (PHP sérialisé) vers Types::JSON. Les données existantes
en base doivent être converties via preUp() avant l'ALTER TABLE.
Sur un vrai appareil mobile, 100vh inclut le chrome du navigateur
(barre de navigation), ce qui cachait le contenu en bas.
- Remplace h-screen (100vh) par h-[100dvh] (dynamic viewport height)
- Ajoute viewport-fit=cover pour préparer le support safe-area iOS
RouterView n'avait pas de contrainte de hauteur dans le flex-col de main,
empêchant overflow-y-auto de se déclencher. Le contenu dépassait la hauteur
de main.overflow-hidden et était silencieusement tronqué sur mobile.
Les IntersectionObserver utilisaient root: null (viewport) au lieu du
conteneur de scroll réel (.infinite-reader). Le rootMargin de 1000px
était donc calculé par rapport au viewport, causant un montage/démontage
des pages à des moments imprécis et des sauts de layout lors du scroll
vers le haut.
Supprime également scroll-behavior: smooth sur le conteneur, qui entrait
en conflit avec le scroll anchoring du navigateur lors des corrections de
position, donnant l'impression que le scroll redescendait tout seul.
En mode scroll infini, le zoom était appliqué via transform: scale() qui
n'affecte pas le flux de mise en page. Les pages se chevauchaient visuellement
quand le zoom était modifié. Passage à la propriété CSS zoom dans les deux
modes pour un comportement de layout correct.
Met aussi à jour le calcul de hauteur des placeholders pour inclure le
facteur de zoom et éviter les sauts de layout lors du chargement paresseux.
- Nouveau domaine System/Domain/Model/SystemStatus (value object)
- QueryHandler agrégeant métriques mangas, chapitres, jobs (global/24h/7j), stockage et sources
- Endpoint GET /api/system/status via API Platform (singleton)
- Calcul de l'espace disque par RecursiveDirectoryIterator sur public/images
- Page Vue /system/status avec 6 cards (Mangas, Chapitres, Jobs, Stockage, Sources, Système)
- Nettoyage du router : suppression des PlaceholderComponent et routes placeholder
- Sidebar : suppression des entrées sans page réelle