From 7d072b41d164cac78d43cdf3f59f6563a29b8eb0 Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Tue, 17 Mar 2026 19:07:09 -0300 Subject: [PATCH 01/11] refactor: reorganize exception handling and DTO imports --- pom.xml | 2 +- .../mangamochi/{model => common}/dto/DefaultResponseDTO.java | 2 +- .../mangamochi/{model => common}/dto/ErrorResponseDTO.java | 2 +- .../mangamochi/{ => common}/exception/ConflictException.java | 2 +- .../{ => common}/exception/GlobalExceptionHandler.java | 4 ++-- .../mangamochi/{ => common}/exception/NotFoundException.java | 2 +- .../{ => common}/exception/UnprocessableException.java | 2 +- .../mangamochi/controller/AuthenticationController.java | 1 + .../com/magamochi/mangamochi/controller/GenreController.java | 2 +- .../magamochi/mangamochi/controller/ManagementController.java | 2 +- .../mangamochi/controller/MangaChapterController.java | 1 + .../com/magamochi/mangamochi/controller/MangaController.java | 1 + .../mangamochi/controller/MangaImportController.java | 1 + .../mangamochi/controller/MangaImportReviewController.java | 2 +- .../magamochi/mangamochi/controller/ProviderController.java | 1 + .../mangamochi/controller/UserFavoriteMangaController.java | 2 +- .../com/magamochi/mangamochi/service/LanguageService.java | 2 +- .../com/magamochi/mangamochi/service/MangaChapterService.java | 2 +- .../mangamochi/service/MangaImportReviewService.java | 2 +- .../com/magamochi/mangamochi/service/MangaImportService.java | 2 +- .../java/com/magamochi/mangamochi/service/MangaService.java | 3 +-- .../mangamochi/service/ProviderManualMangaImportService.java | 2 +- .../mangamochi/service/UserFavoriteMangaService.java | 2 +- .../java/com/magamochi/mangamochi/service/UserService.java | 4 ++-- .../mangamochi/service/providers/impl/BatoProvider.java | 2 +- .../mangamochi/service/providers/impl/MangaDexProvider.java | 2 +- .../com/magamochi/mangamochi/task/UpdateMangaListTask.java | 2 +- .../resources/db/migration/V0017__MANGA_LIVRE_CLEANUP.sql | 3 ++- 28 files changed, 31 insertions(+), 26 deletions(-) rename src/main/java/com/magamochi/mangamochi/{model => common}/dto/DefaultResponseDTO.java (94%) rename src/main/java/com/magamochi/mangamochi/{model => common}/dto/ErrorResponseDTO.java (82%) rename src/main/java/com/magamochi/mangamochi/{ => common}/exception/ConflictException.java (71%) rename src/main/java/com/magamochi/mangamochi/{ => common}/exception/GlobalExceptionHandler.java (95%) rename src/main/java/com/magamochi/mangamochi/{ => common}/exception/NotFoundException.java (71%) rename src/main/java/com/magamochi/mangamochi/{ => common}/exception/UnprocessableException.java (82%) diff --git a/pom.xml b/pom.xml index 84c3513..5a0bfa7 100644 --- a/pom.xml +++ b/pom.xml @@ -162,7 +162,7 @@ com.diffplug.spotless spotless-maven-plugin - 2.46.1 + 3.3.0 diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/DefaultResponseDTO.java b/src/main/java/com/magamochi/mangamochi/common/dto/DefaultResponseDTO.java similarity index 94% rename from src/main/java/com/magamochi/mangamochi/model/dto/DefaultResponseDTO.java rename to src/main/java/com/magamochi/mangamochi/common/dto/DefaultResponseDTO.java index 36d2ac0..874626d 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/DefaultResponseDTO.java +++ b/src/main/java/com/magamochi/mangamochi/common/dto/DefaultResponseDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.mangamochi.common.dto; import jakarta.annotation.Nullable; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/ErrorResponseDTO.java b/src/main/java/com/magamochi/mangamochi/common/dto/ErrorResponseDTO.java similarity index 82% rename from src/main/java/com/magamochi/mangamochi/model/dto/ErrorResponseDTO.java rename to src/main/java/com/magamochi/mangamochi/common/dto/ErrorResponseDTO.java index d6533f4..7d6e06e 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/ErrorResponseDTO.java +++ b/src/main/java/com/magamochi/mangamochi/common/dto/ErrorResponseDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.mangamochi.common.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/mangamochi/exception/ConflictException.java b/src/main/java/com/magamochi/mangamochi/common/exception/ConflictException.java similarity index 71% rename from src/main/java/com/magamochi/mangamochi/exception/ConflictException.java rename to src/main/java/com/magamochi/mangamochi/common/exception/ConflictException.java index 2b1a357..f3c9918 100644 --- a/src/main/java/com/magamochi/mangamochi/exception/ConflictException.java +++ b/src/main/java/com/magamochi/mangamochi/common/exception/ConflictException.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.exception; +package com.magamochi.mangamochi.common.exception; public class ConflictException extends RuntimeException { public ConflictException(String message) { diff --git a/src/main/java/com/magamochi/mangamochi/exception/GlobalExceptionHandler.java b/src/main/java/com/magamochi/mangamochi/common/exception/GlobalExceptionHandler.java similarity index 95% rename from src/main/java/com/magamochi/mangamochi/exception/GlobalExceptionHandler.java rename to src/main/java/com/magamochi/mangamochi/common/exception/GlobalExceptionHandler.java index 5c8c79f..57383ad 100644 --- a/src/main/java/com/magamochi/mangamochi/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/magamochi/mangamochi/common/exception/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.exception; +package com.magamochi.mangamochi.common.exception; -import com.magamochi.mangamochi.model.dto.ErrorResponseDTO; +import com.magamochi.mangamochi.common.dto.ErrorResponseDTO; import java.time.Instant; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/com/magamochi/mangamochi/exception/NotFoundException.java b/src/main/java/com/magamochi/mangamochi/common/exception/NotFoundException.java similarity index 71% rename from src/main/java/com/magamochi/mangamochi/exception/NotFoundException.java rename to src/main/java/com/magamochi/mangamochi/common/exception/NotFoundException.java index d9ce138..3d39767 100644 --- a/src/main/java/com/magamochi/mangamochi/exception/NotFoundException.java +++ b/src/main/java/com/magamochi/mangamochi/common/exception/NotFoundException.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.exception; +package com.magamochi.mangamochi.common.exception; public class NotFoundException extends RuntimeException { public NotFoundException(String message) { diff --git a/src/main/java/com/magamochi/mangamochi/exception/UnprocessableException.java b/src/main/java/com/magamochi/mangamochi/common/exception/UnprocessableException.java similarity index 82% rename from src/main/java/com/magamochi/mangamochi/exception/UnprocessableException.java rename to src/main/java/com/magamochi/mangamochi/common/exception/UnprocessableException.java index 2db5e45..3cfa3bb 100644 --- a/src/main/java/com/magamochi/mangamochi/exception/UnprocessableException.java +++ b/src/main/java/com/magamochi/mangamochi/common/exception/UnprocessableException.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.exception; +package com.magamochi.mangamochi.common.exception; public class UnprocessableException extends RuntimeException { public UnprocessableException(String message) { diff --git a/src/main/java/com/magamochi/mangamochi/controller/AuthenticationController.java b/src/main/java/com/magamochi/mangamochi/controller/AuthenticationController.java index eeec11f..3b85ec1 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/AuthenticationController.java +++ b/src/main/java/com/magamochi/mangamochi/controller/AuthenticationController.java @@ -1,5 +1,6 @@ package com.magamochi.mangamochi.controller; +import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; import com.magamochi.mangamochi.model.dto.*; import com.magamochi.mangamochi.service.UserService; import io.swagger.v3.oas.annotations.Operation; diff --git a/src/main/java/com/magamochi/mangamochi/controller/GenreController.java b/src/main/java/com/magamochi/mangamochi/controller/GenreController.java index 1335dbc..4087d2f 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/GenreController.java +++ b/src/main/java/com/magamochi/mangamochi/controller/GenreController.java @@ -1,6 +1,6 @@ package com.magamochi.mangamochi.controller; -import com.magamochi.mangamochi.model.dto.DefaultResponseDTO; +import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; import com.magamochi.mangamochi.model.dto.GenreDTO; import com.magamochi.mangamochi.service.GenreService; import io.swagger.v3.oas.annotations.Operation; diff --git a/src/main/java/com/magamochi/mangamochi/controller/ManagementController.java b/src/main/java/com/magamochi/mangamochi/controller/ManagementController.java index e4da1e5..a0404ec 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/ManagementController.java +++ b/src/main/java/com/magamochi/mangamochi/controller/ManagementController.java @@ -1,7 +1,7 @@ package com.magamochi.mangamochi.controller; import com.magamochi.mangamochi.client.NtfyClient; -import com.magamochi.mangamochi.model.dto.DefaultResponseDTO; +import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; import com.magamochi.mangamochi.model.dto.UpdateMangaDataCommand; import com.magamochi.mangamochi.model.repository.UserRepository; import com.magamochi.mangamochi.queue.UpdateMangaDataProducer; diff --git a/src/main/java/com/magamochi/mangamochi/controller/MangaChapterController.java b/src/main/java/com/magamochi/mangamochi/controller/MangaChapterController.java index b67eba3..ea777e1 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/MangaChapterController.java +++ b/src/main/java/com/magamochi/mangamochi/controller/MangaChapterController.java @@ -1,5 +1,6 @@ package com.magamochi.mangamochi.controller; +import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; import com.magamochi.mangamochi.model.dto.*; import com.magamochi.mangamochi.model.enumeration.ArchiveFileType; import com.magamochi.mangamochi.service.MangaChapterService; diff --git a/src/main/java/com/magamochi/mangamochi/controller/MangaController.java b/src/main/java/com/magamochi/mangamochi/controller/MangaController.java index eeb0dcc..2dec3c0 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/MangaController.java +++ b/src/main/java/com/magamochi/mangamochi/controller/MangaController.java @@ -1,5 +1,6 @@ package com.magamochi.mangamochi.controller; +import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; import com.magamochi.mangamochi.model.dto.*; import com.magamochi.mangamochi.service.MangaService; import io.swagger.v3.oas.annotations.Operation; diff --git a/src/main/java/com/magamochi/mangamochi/controller/MangaImportController.java b/src/main/java/com/magamochi/mangamochi/controller/MangaImportController.java index 9a4fae7..2586c73 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/MangaImportController.java +++ b/src/main/java/com/magamochi/mangamochi/controller/MangaImportController.java @@ -1,5 +1,6 @@ package com.magamochi.mangamochi.controller; +import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; import com.magamochi.mangamochi.model.dto.*; import com.magamochi.mangamochi.service.MangaImportService; import com.magamochi.mangamochi.service.ProviderManualMangaImportService; diff --git a/src/main/java/com/magamochi/mangamochi/controller/MangaImportReviewController.java b/src/main/java/com/magamochi/mangamochi/controller/MangaImportReviewController.java index a3d1500..948fedb 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/MangaImportReviewController.java +++ b/src/main/java/com/magamochi/mangamochi/controller/MangaImportReviewController.java @@ -1,6 +1,6 @@ package com.magamochi.mangamochi.controller; -import com.magamochi.mangamochi.model.dto.DefaultResponseDTO; +import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; import com.magamochi.mangamochi.model.dto.ImportReviewDTO; import com.magamochi.mangamochi.service.MangaImportReviewService; import io.swagger.v3.oas.annotations.Operation; diff --git a/src/main/java/com/magamochi/mangamochi/controller/ProviderController.java b/src/main/java/com/magamochi/mangamochi/controller/ProviderController.java index 300e7cb..6ddf000 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/ProviderController.java +++ b/src/main/java/com/magamochi/mangamochi/controller/ProviderController.java @@ -1,5 +1,6 @@ package com.magamochi.mangamochi.controller; +import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; import com.magamochi.mangamochi.model.dto.*; import com.magamochi.mangamochi.service.ProviderService; import io.swagger.v3.oas.annotations.Operation; diff --git a/src/main/java/com/magamochi/mangamochi/controller/UserFavoriteMangaController.java b/src/main/java/com/magamochi/mangamochi/controller/UserFavoriteMangaController.java index 63c0d57..cf4703d 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/UserFavoriteMangaController.java +++ b/src/main/java/com/magamochi/mangamochi/controller/UserFavoriteMangaController.java @@ -1,6 +1,6 @@ package com.magamochi.mangamochi.controller; -import com.magamochi.mangamochi.model.dto.DefaultResponseDTO; +import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; import com.magamochi.mangamochi.service.UserFavoriteMangaService; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/mangamochi/service/LanguageService.java b/src/main/java/com/magamochi/mangamochi/service/LanguageService.java index 69d048c..7c7ba84 100644 --- a/src/main/java/com/magamochi/mangamochi/service/LanguageService.java +++ b/src/main/java/com/magamochi/mangamochi/service/LanguageService.java @@ -1,6 +1,6 @@ package com.magamochi.mangamochi.service; -import com.magamochi.mangamochi.exception.NotFoundException; +import com.magamochi.mangamochi.common.exception.NotFoundException; import com.magamochi.mangamochi.model.entity.Language; import com.magamochi.mangamochi.model.repository.LanguageRepository; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/mangamochi/service/MangaChapterService.java b/src/main/java/com/magamochi/mangamochi/service/MangaChapterService.java index 42cbe59..98085f9 100644 --- a/src/main/java/com/magamochi/mangamochi/service/MangaChapterService.java +++ b/src/main/java/com/magamochi/mangamochi/service/MangaChapterService.java @@ -1,7 +1,7 @@ package com.magamochi.mangamochi.service; import com.google.common.util.concurrent.RateLimiter; -import com.magamochi.mangamochi.exception.UnprocessableException; +import com.magamochi.mangamochi.common.exception.UnprocessableException; import com.magamochi.mangamochi.model.dto.MangaChapterArchiveDTO; import com.magamochi.mangamochi.model.dto.MangaChapterImagesDTO; import com.magamochi.mangamochi.model.entity.MangaChapter; diff --git a/src/main/java/com/magamochi/mangamochi/service/MangaImportReviewService.java b/src/main/java/com/magamochi/mangamochi/service/MangaImportReviewService.java index 6cd21a3..e0a0a30 100644 --- a/src/main/java/com/magamochi/mangamochi/service/MangaImportReviewService.java +++ b/src/main/java/com/magamochi/mangamochi/service/MangaImportReviewService.java @@ -4,7 +4,7 @@ import static java.util.Objects.isNull; import com.google.common.util.concurrent.RateLimiter; import com.magamochi.mangamochi.client.JikanClient; -import com.magamochi.mangamochi.exception.NotFoundException; +import com.magamochi.mangamochi.common.exception.NotFoundException; import com.magamochi.mangamochi.model.dto.ImportReviewDTO; import com.magamochi.mangamochi.model.dto.UpdateMangaDataCommand; import com.magamochi.mangamochi.model.entity.Manga; diff --git a/src/main/java/com/magamochi/mangamochi/service/MangaImportService.java b/src/main/java/com/magamochi/mangamochi/service/MangaImportService.java index 1948bfa..ef95e92 100644 --- a/src/main/java/com/magamochi/mangamochi/service/MangaImportService.java +++ b/src/main/java/com/magamochi/mangamochi/service/MangaImportService.java @@ -6,7 +6,7 @@ import static java.util.Objects.nonNull; import com.google.common.util.concurrent.RateLimiter; import com.magamochi.mangamochi.client.AniListClient; import com.magamochi.mangamochi.client.JikanClient; -import com.magamochi.mangamochi.exception.NotFoundException; +import com.magamochi.mangamochi.common.exception.NotFoundException; import com.magamochi.mangamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.mangamochi.model.entity.*; import com.magamochi.mangamochi.model.repository.*; diff --git a/src/main/java/com/magamochi/mangamochi/service/MangaService.java b/src/main/java/com/magamochi/mangamochi/service/MangaService.java index af57da5..4e39e72 100644 --- a/src/main/java/com/magamochi/mangamochi/service/MangaService.java +++ b/src/main/java/com/magamochi/mangamochi/service/MangaService.java @@ -3,7 +3,7 @@ package com.magamochi.mangamochi.service; import static java.util.Objects.nonNull; import com.magamochi.mangamochi.client.NtfyClient; -import com.magamochi.mangamochi.exception.NotFoundException; +import com.magamochi.mangamochi.common.exception.NotFoundException; import com.magamochi.mangamochi.model.dto.*; import com.magamochi.mangamochi.model.entity.Manga; import com.magamochi.mangamochi.model.entity.MangaChapter; @@ -13,7 +13,6 @@ import com.magamochi.mangamochi.model.repository.*; import com.magamochi.mangamochi.model.specification.MangaSpecification; import com.magamochi.mangamochi.queue.MangaChapterDownloadProducer; import com.magamochi.mangamochi.service.providers.ContentProviderFactory; -import java.net.*; import java.util.Comparator; import java.util.List; import java.util.Set; diff --git a/src/main/java/com/magamochi/mangamochi/service/ProviderManualMangaImportService.java b/src/main/java/com/magamochi/mangamochi/service/ProviderManualMangaImportService.java index db9413f..358a1df 100644 --- a/src/main/java/com/magamochi/mangamochi/service/ProviderManualMangaImportService.java +++ b/src/main/java/com/magamochi/mangamochi/service/ProviderManualMangaImportService.java @@ -3,7 +3,7 @@ package com.magamochi.mangamochi.service; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import com.magamochi.mangamochi.exception.NotFoundException; +import com.magamochi.mangamochi.common.exception.NotFoundException; import com.magamochi.mangamochi.model.dto.ImportMangaResponseDTO; import com.magamochi.mangamochi.model.dto.ImportRequestDTO; import com.magamochi.mangamochi.model.entity.*; diff --git a/src/main/java/com/magamochi/mangamochi/service/UserFavoriteMangaService.java b/src/main/java/com/magamochi/mangamochi/service/UserFavoriteMangaService.java index f8571db..e93c184 100644 --- a/src/main/java/com/magamochi/mangamochi/service/UserFavoriteMangaService.java +++ b/src/main/java/com/magamochi/mangamochi/service/UserFavoriteMangaService.java @@ -1,6 +1,6 @@ package com.magamochi.mangamochi.service; -import com.magamochi.mangamochi.exception.NotFoundException; +import com.magamochi.mangamochi.common.exception.NotFoundException; import com.magamochi.mangamochi.model.entity.Manga; import com.magamochi.mangamochi.model.entity.UserFavoriteManga; import com.magamochi.mangamochi.model.repository.MangaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/service/UserService.java b/src/main/java/com/magamochi/mangamochi/service/UserService.java index 8287619..d9ad125 100644 --- a/src/main/java/com/magamochi/mangamochi/service/UserService.java +++ b/src/main/java/com/magamochi/mangamochi/service/UserService.java @@ -2,8 +2,8 @@ package com.magamochi.mangamochi.service; import static java.util.Objects.isNull; -import com.magamochi.mangamochi.exception.ConflictException; -import com.magamochi.mangamochi.exception.NotFoundException; +import com.magamochi.mangamochi.common.exception.ConflictException; +import com.magamochi.mangamochi.common.exception.NotFoundException; import com.magamochi.mangamochi.model.dto.AuthenticationRequestDTO; import com.magamochi.mangamochi.model.dto.AuthenticationResponseDTO; import com.magamochi.mangamochi.model.dto.RefreshTokenRequestDTO; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/impl/BatoProvider.java b/src/main/java/com/magamochi/mangamochi/service/providers/impl/BatoProvider.java index d3bd1e1..fbd1a9b 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/impl/BatoProvider.java +++ b/src/main/java/com/magamochi/mangamochi/service/providers/impl/BatoProvider.java @@ -2,7 +2,7 @@ package com.magamochi.mangamochi.service.providers.impl; import static java.util.Objects.isNull; -import com.magamochi.mangamochi.exception.UnprocessableException; +import com.magamochi.mangamochi.common.exception.UnprocessableException; import com.magamochi.mangamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.mangamochi.model.entity.MangaProvider; import com.magamochi.mangamochi.service.FlareService; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaDexProvider.java b/src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaDexProvider.java index 2980be5..6948181 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaDexProvider.java +++ b/src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaDexProvider.java @@ -4,7 +4,7 @@ import static java.util.Objects.isNull; import com.google.common.util.concurrent.RateLimiter; import com.magamochi.mangamochi.client.MangaDexClient; -import com.magamochi.mangamochi.exception.UnprocessableException; +import com.magamochi.mangamochi.common.exception.UnprocessableException; import com.magamochi.mangamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.mangamochi.model.entity.MangaProvider; import com.magamochi.mangamochi.service.providers.ContentProvider; diff --git a/src/main/java/com/magamochi/mangamochi/task/UpdateMangaListTask.java b/src/main/java/com/magamochi/mangamochi/task/UpdateMangaListTask.java index 99cb900..315060a 100644 --- a/src/main/java/com/magamochi/mangamochi/task/UpdateMangaListTask.java +++ b/src/main/java/com/magamochi/mangamochi/task/UpdateMangaListTask.java @@ -1,6 +1,6 @@ package com.magamochi.mangamochi.task; -import com.magamochi.mangamochi.exception.NotFoundException; +import com.magamochi.mangamochi.common.exception.NotFoundException; import com.magamochi.mangamochi.model.dto.MangaListUpdateCommand; import com.magamochi.mangamochi.model.repository.ProviderRepository; import com.magamochi.mangamochi.queue.UpdateMangaListProducer; diff --git a/src/main/resources/db/migration/V0017__MANGA_LIVRE_CLEANUP.sql b/src/main/resources/db/migration/V0017__MANGA_LIVRE_CLEANUP.sql index 3530988..4442610 100644 --- a/src/main/resources/db/migration/V0017__MANGA_LIVRE_CLEANUP.sql +++ b/src/main/resources/db/migration/V0017__MANGA_LIVRE_CLEANUP.sql @@ -13,7 +13,8 @@ $$ WHERE name = _target_provider_name; IF _target_provider_id IS NULL THEN - RAISE EXCEPTION 'Provider with name "%" not found.', _target_provider_name; + RAISE NOTICE 'Provider with name "%" not found.', _target_provider_name; + RETURN; END IF; UPDATE providers SET status = 'INACTIVE' WHERE id = _target_provider_id; -- 2.49.1 From 9f1437f056f6543b0af503de205444e02e97b690 Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Tue, 17 Mar 2026 19:10:22 -0300 Subject: [PATCH 02/11] refactor: reorganize packages --- .../MangamochiApplication.java | 2 +- .../client/AniListClient.java | 2 +- .../{mangamochi => }/client/FlareClient.java | 2 +- .../{mangamochi => }/client/JikanClient.java | 2 +- .../client/MangaDexClient.java | 4 ++-- .../{mangamochi => }/client/NtfyClient.java | 2 +- .../common/dto/DefaultResponseDTO.java | 2 +- .../common/dto/ErrorResponseDTO.java | 2 +- .../common/exception/ConflictException.java | 2 +- .../exception/GlobalExceptionHandler.java | 4 ++-- .../common/exception/NotFoundException.java | 2 +- .../exception/UnprocessableException.java | 2 +- .../{mangamochi => }/config/RabbitConfig.java | 2 +- .../config/RateLimiterConfig.java | 2 +- .../config/S3ClientConfig.java | 2 +- .../config/SecurityConfig.java | 8 +++---- .../{mangamochi => }/config/WebConfig.java | 2 +- .../controller/AuthenticationController.java | 11 +++++---- .../controller/GenreController.java | 8 +++---- .../controller/ManagementController.java | 18 +++++++------- .../controller/MangaChapterController.java | 10 ++++---- .../controller/MangaController.java | 11 +++++---- .../controller/MangaImportController.java | 11 +++++---- .../MangaImportReviewController.java | 8 +++---- .../controller/ProviderController.java | 8 +++---- .../UserFavoriteMangaController.java | 6 ++--- .../model/enumeration/ArchiveFileType.java | 6 ----- .../model/enumeration/UserRole.java | 5 ---- .../model/dto/AuthenticationRequestDTO.java | 2 +- .../model/dto/AuthenticationResponseDTO.java | 4 ++-- ...ontentProviderMangaChapterResponseDTO.java | 2 +- .../ContentProviderMangaInfoResponseDTO.java | 4 ++-- .../{mangamochi => }/model/dto/GenreDTO.java | 4 ++-- .../model/dto/ImportMangaResponseDTO.java | 2 +- .../model/dto/ImportRequestDTO.java | 2 +- .../model/dto/ImportReviewDTO.java | 4 ++-- .../model/dto/LanguageDTO.java | 4 ++-- .../model/dto/MangaChapterArchiveDTO.java | 2 +- .../model/dto/MangaChapterDTO.java | 4 ++-- .../dto/MangaChapterDownloadCommand.java | 2 +- .../model/dto/MangaChapterImagesDTO.java | 6 ++--- .../model/dto/MangaChapterResponseDTO.java | 2 +- .../{mangamochi => }/model/dto/MangaDTO.java | 12 +++++----- .../model/dto/MangaDexMangaDTO.java | 2 +- .../model/dto/MangaListDTO.java | 4 ++-- .../model/dto/MangaListFilterDTO.java | 2 +- .../model/dto/MangaListUpdateCommand.java | 2 +- .../model/dto/MangaMessageDTO.java | 2 +- .../model/dto/ProviderListDTO.java | 4 ++-- .../model/dto/RefreshTokenRequestDTO.java | 2 +- .../model/dto/RegistrationRequestDTO.java | 2 +- .../model/dto/TitleMatchRequestDTO.java | 2 +- .../model/dto/TitleMatchResponseDTO.java | 2 +- .../model/dto/UpdateMangaDataCommand.java | 2 +- .../UpdateMangaFollowChapterListCommand.java | 2 +- .../{mangamochi => }/model/entity/Author.java | 2 +- .../model/entity/FlareSession.java | 2 +- .../{mangamochi => }/model/entity/Genre.java | 2 +- .../{mangamochi => }/model/entity/Image.java | 2 +- .../model/entity/Language.java | 2 +- .../{mangamochi => }/model/entity/Manga.java | 2 +- .../model/entity/MangaAlternativeTitle.java | 2 +- .../model/entity/MangaAuthor.java | 2 +- .../model/entity/MangaChapter.java | 2 +- .../model/entity/MangaChapterImage.java | 2 +- .../model/entity/MangaGenre.java | 2 +- .../model/entity/MangaImportReview.java | 2 +- .../model/entity/MangaProvider.java | 2 +- .../model/entity/Provider.java | 4 ++-- .../{mangamochi => }/model/entity/User.java | 4 ++-- .../model/entity/UserFavoriteManga.java | 2 +- .../model/entity/UserMangaFollow.java | 2 +- .../model/enumeration/ArchiveFileType.java | 6 +++++ .../model/enumeration/MangaStatus.java | 2 +- .../model/enumeration/ProviderStatus.java | 2 +- .../magamochi/model/enumeration/UserRole.java | 5 ++++ .../model/repository/AuthorRepository.java | 4 ++-- .../model/repository/GenreRepository.java | 4 ++-- .../model/repository/ImageRepository.java | 4 ++-- .../model/repository/LanguageRepository.java | 4 ++-- .../MangaAlternativeTitlesRepository.java | 4 ++-- .../repository/MangaAuthorRepository.java | 8 +++---- .../MangaChapterImageRepository.java | 6 ++--- .../repository/MangaChapterRepository.java | 6 ++--- .../repository/MangaGenreRepository.java | 6 +++-- .../MangaImportReviewRepository.java | 4 ++-- .../repository/MangaProviderRepository.java | 8 +++---- .../model/repository/MangaRepository.java | 4 ++-- .../model/repository/ProviderRepository.java | 4 ++-- .../UserFavoriteMangaRepository.java | 8 +++---- .../repository/UserMangaFollowRepository.java | 8 +++---- .../model/repository/UserRepository.java | 4 ++-- .../specification/MangaSpecification.java | 10 ++++---- .../queue/MangaChapterDownloadConsumer.java | 6 ++--- .../queue/MangaChapterDownloadProducer.java | 4 ++-- .../queue/UpdateMangaDataConsumer.java | 6 ++--- .../queue/UpdateMangaDataProducer.java | 4 ++-- .../UpdateMangaFollowChapterListConsumer.java | 6 ++--- .../UpdateMangaFollowChapterListProducer.java | 4 ++-- .../queue/UpdateMangaListConsumer.java | 6 ++--- .../queue/UpdateMangaListProducer.java | 4 ++-- .../registry/FlareSessionRegistry.java | 4 ++-- .../security/JwtRequestFilter.java | 6 ++--- .../service/CustomUserDetailsService.java | 4 ++-- .../service/FlareService.java | 4 ++-- .../service/FlareSessionManager.java | 8 +++---- .../service/GenreService.java | 6 ++--- .../service/ImageService.java | 6 ++--- .../service/LanguageService.java | 8 +++---- .../service/MangaChapterService.java | 20 ++++++++-------- .../service/MangaCreationService.java | 22 ++++++++--------- .../service/MangaImportReviewService.java | 24 +++++++++---------- .../service/MangaImportService.java | 16 ++++++------- .../service/MangaListService.java | 8 +++---- .../service/MangaService.java | 24 +++++++++---------- .../ProviderManualMangaImportService.java | 18 +++++++------- .../service/ProviderService.java | 10 ++++---- .../{mangamochi => }/service/S3Service.java | 2 +- .../service/TitleMatcherService.java | 6 ++--- .../service/UserFavoriteMangaService.java | 12 +++++----- .../{mangamochi => }/service/UserService.java | 22 ++++++++--------- .../service/providers/ContentProvider.java | 6 ++--- .../providers/ContentProviderFactory.java | 2 +- .../service/providers/ContentProviders.java | 2 +- .../ManualImportContentProvider.java | 2 +- .../ManualImportContentProviderFactory.java | 2 +- .../providers/PagedContentProvider.java | 4 ++-- .../PagedContentProviderFactory.java | 2 +- .../service/providers/impl/BatoProvider.java | 16 ++++++------- .../providers/impl/MangaDexProvider.java | 16 ++++++------- .../impl/MangaLivreBlogProvider.java | 16 ++++++------- .../providers/impl/MangaLivreProvider.java | 18 +++++++------- .../providers/impl/PinkRosaScanProvider.java | 18 +++++++------- .../task/FlareSessionCleanupTask.java | 6 ++--- .../task/FlareStartupCleanupTask.java | 4 ++-- .../task/ImageCleanupTask.java | 8 +++---- .../task/MangaFollowUpdateTask.java | 12 +++++----- .../task/UpdateMangaDataTask.java | 8 +++---- .../task/UpdateMangaListTask.java | 14 +++++------ .../{mangamochi => }/util/DoubleUtil.java | 2 +- .../{mangamochi => }/util/JwtUtil.java | 2 +- .../MangamochiApplicationTests.java | 2 +- 142 files changed, 411 insertions(+), 400 deletions(-) rename src/main/java/com/magamochi/{mangamochi => }/MangamochiApplication.java (94%) rename src/main/java/com/magamochi/{mangamochi => }/client/AniListClient.java (97%) rename src/main/java/com/magamochi/{mangamochi => }/client/FlareClient.java (98%) rename src/main/java/com/magamochi/{mangamochi => }/client/JikanClient.java (97%) rename src/main/java/com/magamochi/{mangamochi => }/client/MangaDexClient.java (93%) rename src/main/java/com/magamochi/{mangamochi => }/client/NtfyClient.java (92%) rename src/main/java/com/magamochi/{mangamochi => }/common/dto/DefaultResponseDTO.java (94%) rename src/main/java/com/magamochi/{mangamochi => }/common/dto/ErrorResponseDTO.java (82%) rename src/main/java/com/magamochi/{mangamochi => }/common/exception/ConflictException.java (71%) rename src/main/java/com/magamochi/{mangamochi => }/common/exception/GlobalExceptionHandler.java (95%) rename src/main/java/com/magamochi/{mangamochi => }/common/exception/NotFoundException.java (71%) rename src/main/java/com/magamochi/{mangamochi => }/common/exception/UnprocessableException.java (82%) rename src/main/java/com/magamochi/{mangamochi => }/config/RabbitConfig.java (97%) rename src/main/java/com/magamochi/{mangamochi => }/config/RateLimiterConfig.java (92%) rename src/main/java/com/magamochi/{mangamochi => }/config/S3ClientConfig.java (96%) rename src/main/java/com/magamochi/{mangamochi => }/config/SecurityConfig.java (94%) rename src/main/java/com/magamochi/{mangamochi => }/config/WebConfig.java (92%) rename src/main/java/com/magamochi/{mangamochi => }/controller/AuthenticationController.java (81%) rename src/main/java/com/magamochi/{mangamochi => }/controller/GenreController.java (73%) rename src/main/java/com/magamochi/{mangamochi => }/controller/ManagementController.java (86%) rename src/main/java/com/magamochi/{mangamochi => }/controller/MangaChapterController.java (91%) rename src/main/java/com/magamochi/{mangamochi => }/controller/MangaController.java (92%) rename src/main/java/com/magamochi/{mangamochi => }/controller/MangaImportController.java (87%) rename src/main/java/com/magamochi/{mangamochi => }/controller/MangaImportReviewController.java (87%) rename src/main/java/com/magamochi/{mangamochi => }/controller/ProviderController.java (77%) rename src/main/java/com/magamochi/{mangamochi => }/controller/UserFavoriteMangaController.java (86%) delete mode 100644 src/main/java/com/magamochi/mangamochi/model/enumeration/ArchiveFileType.java delete mode 100644 src/main/java/com/magamochi/mangamochi/model/enumeration/UserRole.java rename src/main/java/com/magamochi/{mangamochi => }/model/dto/AuthenticationRequestDTO.java (76%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/AuthenticationResponseDTO.java (71%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/ContentProviderMangaChapterResponseDTO.java (83%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/ContentProviderMangaInfoResponseDTO.java (61%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/GenreDTO.java (73%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/ImportMangaResponseDTO.java (70%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/ImportRequestDTO.java (76%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/ImportReviewDTO.java (84%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/LanguageDTO.java (81%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaChapterArchiveDTO.java (81%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaChapterDTO.java (84%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaChapterDownloadCommand.java (58%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaChapterImagesDTO.java (83%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaChapterResponseDTO.java (86%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaDTO.java (87%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaDexMangaDTO.java (86%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaListDTO.java (92%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaListFilterDTO.java (80%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaListUpdateCommand.java (65%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/MangaMessageDTO.java (78%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/ProviderListDTO.java (84%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/RefreshTokenRequestDTO.java (73%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/RegistrationRequestDTO.java (65%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/TitleMatchRequestDTO.java (83%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/TitleMatchResponseDTO.java (79%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/UpdateMangaDataCommand.java (55%) rename src/main/java/com/magamochi/{mangamochi => }/model/dto/UpdateMangaFollowChapterListCommand.java (63%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/Author.java (92%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/FlareSession.java (83%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/Genre.java (89%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/Image.java (91%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/Language.java (86%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/Manga.java (96%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/MangaAlternativeTitle.java (88%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/MangaAuthor.java (89%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/MangaChapter.java (95%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/MangaChapterImage.java (93%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/MangaGenre.java (89%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/MangaImportReview.java (91%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/MangaProvider.java (94%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/Provider.java (87%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/User.java (85%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/UserFavoriteManga.java (91%) rename src/main/java/com/magamochi/{mangamochi => }/model/entity/UserMangaFollow.java (89%) create mode 100644 src/main/java/com/magamochi/model/enumeration/ArchiveFileType.java rename src/main/java/com/magamochi/{mangamochi => }/model/enumeration/MangaStatus.java (62%) rename src/main/java/com/magamochi/{mangamochi => }/model/enumeration/ProviderStatus.java (50%) create mode 100644 src/main/java/com/magamochi/model/enumeration/UserRole.java rename src/main/java/com/magamochi/{mangamochi => }/model/repository/AuthorRepository.java (70%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/GenreRepository.java (70%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/ImageRepository.java (60%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/LanguageRepository.java (67%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/MangaAlternativeTitlesRepository.java (59%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/MangaAuthorRepository.java (53%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/MangaChapterImageRepository.java (59%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/MangaChapterRepository.java (71%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/MangaGenreRepository.java (59%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/MangaImportReviewRepository.java (67%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/MangaProviderRepository.java (63%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/MangaRepository.java (82%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/ProviderRepository.java (67%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/UserFavoriteMangaRepository.java (64%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/UserMangaFollowRepository.java (68%) rename src/main/java/com/magamochi/{mangamochi => }/model/repository/UserRepository.java (70%) rename src/main/java/com/magamochi/{mangamochi => }/model/specification/MangaSpecification.java (92%) rename src/main/java/com/magamochi/{mangamochi => }/queue/MangaChapterDownloadConsumer.java (81%) rename src/main/java/com/magamochi/{mangamochi => }/queue/MangaChapterDownloadProducer.java (86%) rename src/main/java/com/magamochi/{mangamochi => }/queue/UpdateMangaDataConsumer.java (78%) rename src/main/java/com/magamochi/{mangamochi => }/queue/UpdateMangaDataProducer.java (86%) rename src/main/java/com/magamochi/{mangamochi => }/queue/UpdateMangaFollowChapterListConsumer.java (79%) rename src/main/java/com/magamochi/{mangamochi => }/queue/UpdateMangaFollowChapterListProducer.java (86%) rename src/main/java/com/magamochi/{mangamochi => }/queue/UpdateMangaListConsumer.java (79%) rename src/main/java/com/magamochi/{mangamochi => }/queue/UpdateMangaListProducer.java (86%) rename src/main/java/com/magamochi/{mangamochi => }/registry/FlareSessionRegistry.java (83%) rename src/main/java/com/magamochi/{mangamochi => }/security/JwtRequestFilter.java (94%) rename src/main/java/com/magamochi/{mangamochi => }/service/CustomUserDetailsService.java (91%) rename src/main/java/com/magamochi/{mangamochi => }/service/FlareService.java (88%) rename src/main/java/com/magamochi/{mangamochi => }/service/FlareSessionManager.java (88%) rename src/main/java/com/magamochi/{mangamochi => }/service/GenreService.java (69%) rename src/main/java/com/magamochi/{mangamochi => }/service/ImageService.java (81%) rename src/main/java/com/magamochi/{mangamochi => }/service/LanguageService.java (64%) rename src/main/java/com/magamochi/{mangamochi => }/service/MangaChapterService.java (91%) rename src/main/java/com/magamochi/{mangamochi => }/service/MangaCreationService.java (88%) rename src/main/java/com/magamochi/{mangamochi => }/service/MangaImportReviewService.java (76%) rename src/main/java/com/magamochi/{mangamochi => }/service/MangaImportService.java (96%) rename src/main/java/com/magamochi/{mangamochi => }/service/MangaListService.java (87%) rename src/main/java/com/magamochi/{mangamochi => }/service/MangaService.java (90%) rename src/main/java/com/magamochi/{mangamochi => }/service/ProviderManualMangaImportService.java (81%) rename src/main/java/com/magamochi/{mangamochi => }/service/ProviderService.java (79%) rename src/main/java/com/magamochi/{mangamochi => }/service/S3Service.java (98%) rename src/main/java/com/magamochi/{mangamochi => }/service/TitleMatcherService.java (92%) rename src/main/java/com/magamochi/{mangamochi => }/service/UserFavoriteMangaService.java (78%) rename src/main/java/com/magamochi/{mangamochi => }/service/UserService.java (84%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/ContentProvider.java (55%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/ContentProviderFactory.java (91%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/ContentProviders.java (86%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/ManualImportContentProvider.java (62%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/ManualImportContentProviderFactory.java (92%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/PagedContentProvider.java (56%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/PagedContentProviderFactory.java (91%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/impl/BatoProvider.java (83%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/impl/MangaDexProvider.java (89%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/impl/MangaLivreBlogProvider.java (91%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/impl/MangaLivreProvider.java (88%) rename src/main/java/com/magamochi/{mangamochi => }/service/providers/impl/PinkRosaScanProvider.java (89%) rename src/main/java/com/magamochi/{mangamochi => }/task/FlareSessionCleanupTask.java (85%) rename src/main/java/com/magamochi/{mangamochi => }/task/FlareStartupCleanupTask.java (89%) rename src/main/java/com/magamochi/{mangamochi => }/task/ImageCleanupTask.java (87%) rename src/main/java/com/magamochi/{mangamochi => }/task/MangaFollowUpdateTask.java (80%) rename src/main/java/com/magamochi/{mangamochi => }/task/UpdateMangaDataTask.java (77%) rename src/main/java/com/magamochi/{mangamochi => }/task/UpdateMangaListTask.java (81%) rename src/main/java/com/magamochi/{mangamochi => }/util/DoubleUtil.java (91%) rename src/main/java/com/magamochi/{mangamochi => }/util/JwtUtil.java (98%) rename src/test/java/com/magamochi/{mangamochi => }/MangamochiApplicationTests.java (86%) diff --git a/src/main/java/com/magamochi/mangamochi/MangamochiApplication.java b/src/main/java/com/magamochi/MangamochiApplication.java similarity index 94% rename from src/main/java/com/magamochi/mangamochi/MangamochiApplication.java rename to src/main/java/com/magamochi/MangamochiApplication.java index 8f40b76..f774fd7 100644 --- a/src/main/java/com/magamochi/mangamochi/MangamochiApplication.java +++ b/src/main/java/com/magamochi/MangamochiApplication.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi; +package com.magamochi; import org.springframework.amqp.rabbit.annotation.EnableRabbit; import org.springframework.boot.SpringApplication; diff --git a/src/main/java/com/magamochi/mangamochi/client/AniListClient.java b/src/main/java/com/magamochi/client/AniListClient.java similarity index 97% rename from src/main/java/com/magamochi/mangamochi/client/AniListClient.java rename to src/main/java/com/magamochi/client/AniListClient.java index b5c5b86..5baea6e 100644 --- a/src/main/java/com/magamochi/mangamochi/client/AniListClient.java +++ b/src/main/java/com/magamochi/client/AniListClient.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.client; +package com.magamochi.client; import io.github.resilience4j.retry.annotation.Retry; import java.util.List; diff --git a/src/main/java/com/magamochi/mangamochi/client/FlareClient.java b/src/main/java/com/magamochi/client/FlareClient.java similarity index 98% rename from src/main/java/com/magamochi/mangamochi/client/FlareClient.java rename to src/main/java/com/magamochi/client/FlareClient.java index df4dc3a..6716757 100644 --- a/src/main/java/com/magamochi/mangamochi/client/FlareClient.java +++ b/src/main/java/com/magamochi/client/FlareClient.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.client; +package com.magamochi.client; import io.github.resilience4j.retry.annotation.Retry; import java.util.List; diff --git a/src/main/java/com/magamochi/mangamochi/client/JikanClient.java b/src/main/java/com/magamochi/client/JikanClient.java similarity index 97% rename from src/main/java/com/magamochi/mangamochi/client/JikanClient.java rename to src/main/java/com/magamochi/client/JikanClient.java index 5cba7f0..2cc1495 100644 --- a/src/main/java/com/magamochi/mangamochi/client/JikanClient.java +++ b/src/main/java/com/magamochi/client/JikanClient.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.client; +package com.magamochi.client; import io.github.resilience4j.retry.annotation.Retry; import java.time.OffsetDateTime; diff --git a/src/main/java/com/magamochi/mangamochi/client/MangaDexClient.java b/src/main/java/com/magamochi/client/MangaDexClient.java similarity index 93% rename from src/main/java/com/magamochi/mangamochi/client/MangaDexClient.java rename to src/main/java/com/magamochi/client/MangaDexClient.java index ffcd7ec..8bf0056 100644 --- a/src/main/java/com/magamochi/mangamochi/client/MangaDexClient.java +++ b/src/main/java/com/magamochi/client/MangaDexClient.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.client; +package com.magamochi.client; -import com.magamochi.mangamochi.model.dto.MangaDexMangaDTO; +import com.magamochi.model.dto.MangaDexMangaDTO; import io.github.resilience4j.retry.annotation.Retry; import java.util.List; import java.util.UUID; diff --git a/src/main/java/com/magamochi/mangamochi/client/NtfyClient.java b/src/main/java/com/magamochi/client/NtfyClient.java similarity index 92% rename from src/main/java/com/magamochi/mangamochi/client/NtfyClient.java rename to src/main/java/com/magamochi/client/NtfyClient.java index 64d7b1d..76bf158 100644 --- a/src/main/java/com/magamochi/mangamochi/client/NtfyClient.java +++ b/src/main/java/com/magamochi/client/NtfyClient.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.client; +package com.magamochi.client; import io.github.resilience4j.retry.annotation.Retry; import org.springframework.cloud.openfeign.FeignClient; diff --git a/src/main/java/com/magamochi/mangamochi/common/dto/DefaultResponseDTO.java b/src/main/java/com/magamochi/common/dto/DefaultResponseDTO.java similarity index 94% rename from src/main/java/com/magamochi/mangamochi/common/dto/DefaultResponseDTO.java rename to src/main/java/com/magamochi/common/dto/DefaultResponseDTO.java index 874626d..923a457 100644 --- a/src/main/java/com/magamochi/mangamochi/common/dto/DefaultResponseDTO.java +++ b/src/main/java/com/magamochi/common/dto/DefaultResponseDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.common.dto; +package com.magamochi.common.dto; import jakarta.annotation.Nullable; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/common/dto/ErrorResponseDTO.java b/src/main/java/com/magamochi/common/dto/ErrorResponseDTO.java similarity index 82% rename from src/main/java/com/magamochi/mangamochi/common/dto/ErrorResponseDTO.java rename to src/main/java/com/magamochi/common/dto/ErrorResponseDTO.java index 7d6e06e..c7d5580 100644 --- a/src/main/java/com/magamochi/mangamochi/common/dto/ErrorResponseDTO.java +++ b/src/main/java/com/magamochi/common/dto/ErrorResponseDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.common.dto; +package com.magamochi.common.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/mangamochi/common/exception/ConflictException.java b/src/main/java/com/magamochi/common/exception/ConflictException.java similarity index 71% rename from src/main/java/com/magamochi/mangamochi/common/exception/ConflictException.java rename to src/main/java/com/magamochi/common/exception/ConflictException.java index f3c9918..0c543ec 100644 --- a/src/main/java/com/magamochi/mangamochi/common/exception/ConflictException.java +++ b/src/main/java/com/magamochi/common/exception/ConflictException.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.common.exception; +package com.magamochi.common.exception; public class ConflictException extends RuntimeException { public ConflictException(String message) { diff --git a/src/main/java/com/magamochi/mangamochi/common/exception/GlobalExceptionHandler.java b/src/main/java/com/magamochi/common/exception/GlobalExceptionHandler.java similarity index 95% rename from src/main/java/com/magamochi/mangamochi/common/exception/GlobalExceptionHandler.java rename to src/main/java/com/magamochi/common/exception/GlobalExceptionHandler.java index 57383ad..a53d7ee 100644 --- a/src/main/java/com/magamochi/mangamochi/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/magamochi/common/exception/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.common.exception; +package com.magamochi.common.exception; -import com.magamochi.mangamochi.common.dto.ErrorResponseDTO; +import com.magamochi.common.dto.ErrorResponseDTO; import java.time.Instant; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/com/magamochi/mangamochi/common/exception/NotFoundException.java b/src/main/java/com/magamochi/common/exception/NotFoundException.java similarity index 71% rename from src/main/java/com/magamochi/mangamochi/common/exception/NotFoundException.java rename to src/main/java/com/magamochi/common/exception/NotFoundException.java index 3d39767..9fc1025 100644 --- a/src/main/java/com/magamochi/mangamochi/common/exception/NotFoundException.java +++ b/src/main/java/com/magamochi/common/exception/NotFoundException.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.common.exception; +package com.magamochi.common.exception; public class NotFoundException extends RuntimeException { public NotFoundException(String message) { diff --git a/src/main/java/com/magamochi/mangamochi/common/exception/UnprocessableException.java b/src/main/java/com/magamochi/common/exception/UnprocessableException.java similarity index 82% rename from src/main/java/com/magamochi/mangamochi/common/exception/UnprocessableException.java rename to src/main/java/com/magamochi/common/exception/UnprocessableException.java index 3cfa3bb..b2bdb69 100644 --- a/src/main/java/com/magamochi/mangamochi/common/exception/UnprocessableException.java +++ b/src/main/java/com/magamochi/common/exception/UnprocessableException.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.common.exception; +package com.magamochi.common.exception; public class UnprocessableException extends RuntimeException { public UnprocessableException(String message) { diff --git a/src/main/java/com/magamochi/mangamochi/config/RabbitConfig.java b/src/main/java/com/magamochi/config/RabbitConfig.java similarity index 97% rename from src/main/java/com/magamochi/mangamochi/config/RabbitConfig.java rename to src/main/java/com/magamochi/config/RabbitConfig.java index 09e13f0..9f920f7 100644 --- a/src/main/java/com/magamochi/mangamochi/config/RabbitConfig.java +++ b/src/main/java/com/magamochi/config/RabbitConfig.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.config; +package com.magamochi.config; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.connection.ConnectionFactory; diff --git a/src/main/java/com/magamochi/mangamochi/config/RateLimiterConfig.java b/src/main/java/com/magamochi/config/RateLimiterConfig.java similarity index 92% rename from src/main/java/com/magamochi/mangamochi/config/RateLimiterConfig.java rename to src/main/java/com/magamochi/config/RateLimiterConfig.java index 1120da5..2e8288b 100644 --- a/src/main/java/com/magamochi/mangamochi/config/RateLimiterConfig.java +++ b/src/main/java/com/magamochi/config/RateLimiterConfig.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.config; +package com.magamochi.config; import com.google.common.util.concurrent.RateLimiter; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/com/magamochi/mangamochi/config/S3ClientConfig.java b/src/main/java/com/magamochi/config/S3ClientConfig.java similarity index 96% rename from src/main/java/com/magamochi/mangamochi/config/S3ClientConfig.java rename to src/main/java/com/magamochi/config/S3ClientConfig.java index bcc3f26..a86ccd5 100644 --- a/src/main/java/com/magamochi/mangamochi/config/S3ClientConfig.java +++ b/src/main/java/com/magamochi/config/S3ClientConfig.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.config; +package com.magamochi.config; import java.net.URI; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/magamochi/mangamochi/config/SecurityConfig.java b/src/main/java/com/magamochi/config/SecurityConfig.java similarity index 94% rename from src/main/java/com/magamochi/mangamochi/config/SecurityConfig.java rename to src/main/java/com/magamochi/config/SecurityConfig.java index f4b92de..d873c90 100644 --- a/src/main/java/com/magamochi/mangamochi/config/SecurityConfig.java +++ b/src/main/java/com/magamochi/config/SecurityConfig.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.config; +package com.magamochi.config; -import com.magamochi.mangamochi.security.JwtRequestFilter; -import com.magamochi.mangamochi.service.CustomUserDetailsService; -import com.magamochi.mangamochi.util.JwtUtil; +import com.magamochi.security.JwtRequestFilter; +import com.magamochi.service.CustomUserDetailsService; +import com.magamochi.util.JwtUtil; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/com/magamochi/mangamochi/config/WebConfig.java b/src/main/java/com/magamochi/config/WebConfig.java similarity index 92% rename from src/main/java/com/magamochi/mangamochi/config/WebConfig.java rename to src/main/java/com/magamochi/config/WebConfig.java index 7f0df7a..4221d89 100644 --- a/src/main/java/com/magamochi/mangamochi/config/WebConfig.java +++ b/src/main/java/com/magamochi/config/WebConfig.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.config; +package com.magamochi.config; import lombok.NonNull; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/com/magamochi/mangamochi/controller/AuthenticationController.java b/src/main/java/com/magamochi/controller/AuthenticationController.java similarity index 81% rename from src/main/java/com/magamochi/mangamochi/controller/AuthenticationController.java rename to src/main/java/com/magamochi/controller/AuthenticationController.java index 3b85ec1..502601b 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/AuthenticationController.java +++ b/src/main/java/com/magamochi/controller/AuthenticationController.java @@ -1,8 +1,11 @@ -package com.magamochi.mangamochi.controller; +package com.magamochi.controller; -import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; -import com.magamochi.mangamochi.model.dto.*; -import com.magamochi.mangamochi.service.UserService; +import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.model.dto.AuthenticationRequestDTO; +import com.magamochi.model.dto.AuthenticationResponseDTO; +import com.magamochi.model.dto.RefreshTokenRequestDTO; +import com.magamochi.model.dto.RegistrationRequestDTO; +import com.magamochi.service.UserService; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/magamochi/mangamochi/controller/GenreController.java b/src/main/java/com/magamochi/controller/GenreController.java similarity index 73% rename from src/main/java/com/magamochi/mangamochi/controller/GenreController.java rename to src/main/java/com/magamochi/controller/GenreController.java index 4087d2f..c922a4b 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/GenreController.java +++ b/src/main/java/com/magamochi/controller/GenreController.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.controller; +package com.magamochi.controller; -import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; -import com.magamochi.mangamochi.model.dto.GenreDTO; -import com.magamochi.mangamochi.service.GenreService; +import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.model.dto.GenreDTO; +import com.magamochi.service.GenreService; import io.swagger.v3.oas.annotations.Operation; import java.util.List; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/mangamochi/controller/ManagementController.java b/src/main/java/com/magamochi/controller/ManagementController.java similarity index 86% rename from src/main/java/com/magamochi/mangamochi/controller/ManagementController.java rename to src/main/java/com/magamochi/controller/ManagementController.java index a0404ec..c35ed20 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/ManagementController.java +++ b/src/main/java/com/magamochi/controller/ManagementController.java @@ -1,13 +1,13 @@ -package com.magamochi.mangamochi.controller; +package com.magamochi.controller; -import com.magamochi.mangamochi.client.NtfyClient; -import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; -import com.magamochi.mangamochi.model.dto.UpdateMangaDataCommand; -import com.magamochi.mangamochi.model.repository.UserRepository; -import com.magamochi.mangamochi.queue.UpdateMangaDataProducer; -import com.magamochi.mangamochi.task.ImageCleanupTask; -import com.magamochi.mangamochi.task.MangaFollowUpdateTask; -import com.magamochi.mangamochi.task.UpdateMangaListTask; +import com.magamochi.client.NtfyClient; +import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.model.dto.UpdateMangaDataCommand; +import com.magamochi.model.repository.UserRepository; +import com.magamochi.queue.UpdateMangaDataProducer; +import com.magamochi.task.ImageCleanupTask; +import com.magamochi.task.MangaFollowUpdateTask; +import com.magamochi.task.UpdateMangaListTask; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/magamochi/mangamochi/controller/MangaChapterController.java b/src/main/java/com/magamochi/controller/MangaChapterController.java similarity index 91% rename from src/main/java/com/magamochi/mangamochi/controller/MangaChapterController.java rename to src/main/java/com/magamochi/controller/MangaChapterController.java index ea777e1..1160d35 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/MangaChapterController.java +++ b/src/main/java/com/magamochi/controller/MangaChapterController.java @@ -1,9 +1,9 @@ -package com.magamochi.mangamochi.controller; +package com.magamochi.controller; -import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; -import com.magamochi.mangamochi.model.dto.*; -import com.magamochi.mangamochi.model.enumeration.ArchiveFileType; -import com.magamochi.mangamochi.service.MangaChapterService; +import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.model.dto.MangaChapterImagesDTO; +import com.magamochi.model.enumeration.ArchiveFileType; +import com.magamochi.service.MangaChapterService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/src/main/java/com/magamochi/mangamochi/controller/MangaController.java b/src/main/java/com/magamochi/controller/MangaController.java similarity index 92% rename from src/main/java/com/magamochi/mangamochi/controller/MangaController.java rename to src/main/java/com/magamochi/controller/MangaController.java index 2dec3c0..48241bb 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/MangaController.java +++ b/src/main/java/com/magamochi/controller/MangaController.java @@ -1,8 +1,11 @@ -package com.magamochi.mangamochi.controller; +package com.magamochi.controller; -import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; -import com.magamochi.mangamochi.model.dto.*; -import com.magamochi.mangamochi.service.MangaService; +import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.model.dto.MangaChapterDTO; +import com.magamochi.model.dto.MangaDTO; +import com.magamochi.model.dto.MangaListDTO; +import com.magamochi.model.dto.MangaListFilterDTO; +import com.magamochi.service.MangaService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import java.util.List; diff --git a/src/main/java/com/magamochi/mangamochi/controller/MangaImportController.java b/src/main/java/com/magamochi/controller/MangaImportController.java similarity index 87% rename from src/main/java/com/magamochi/mangamochi/controller/MangaImportController.java rename to src/main/java/com/magamochi/controller/MangaImportController.java index 2586c73..a4c72a8 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/MangaImportController.java +++ b/src/main/java/com/magamochi/controller/MangaImportController.java @@ -1,9 +1,10 @@ -package com.magamochi.mangamochi.controller; +package com.magamochi.controller; -import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; -import com.magamochi.mangamochi.model.dto.*; -import com.magamochi.mangamochi.service.MangaImportService; -import com.magamochi.mangamochi.service.ProviderManualMangaImportService; +import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.model.dto.ImportMangaResponseDTO; +import com.magamochi.model.dto.ImportRequestDTO; +import com.magamochi.service.MangaImportService; +import com.magamochi.service.ProviderManualMangaImportService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; diff --git a/src/main/java/com/magamochi/mangamochi/controller/MangaImportReviewController.java b/src/main/java/com/magamochi/controller/MangaImportReviewController.java similarity index 87% rename from src/main/java/com/magamochi/mangamochi/controller/MangaImportReviewController.java rename to src/main/java/com/magamochi/controller/MangaImportReviewController.java index 948fedb..733943c 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/MangaImportReviewController.java +++ b/src/main/java/com/magamochi/controller/MangaImportReviewController.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.controller; +package com.magamochi.controller; -import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; -import com.magamochi.mangamochi.model.dto.ImportReviewDTO; -import com.magamochi.mangamochi.service.MangaImportReviewService; +import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.model.dto.ImportReviewDTO; +import com.magamochi.service.MangaImportReviewService; import io.swagger.v3.oas.annotations.Operation; import java.util.List; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/mangamochi/controller/ProviderController.java b/src/main/java/com/magamochi/controller/ProviderController.java similarity index 77% rename from src/main/java/com/magamochi/mangamochi/controller/ProviderController.java rename to src/main/java/com/magamochi/controller/ProviderController.java index 6ddf000..8accd60 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/ProviderController.java +++ b/src/main/java/com/magamochi/controller/ProviderController.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.controller; +package com.magamochi.controller; -import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; -import com.magamochi.mangamochi.model.dto.*; -import com.magamochi.mangamochi.service.ProviderService; +import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.model.dto.ProviderListDTO; +import com.magamochi.service.ProviderService; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/magamochi/mangamochi/controller/UserFavoriteMangaController.java b/src/main/java/com/magamochi/controller/UserFavoriteMangaController.java similarity index 86% rename from src/main/java/com/magamochi/mangamochi/controller/UserFavoriteMangaController.java rename to src/main/java/com/magamochi/controller/UserFavoriteMangaController.java index cf4703d..7e254d1 100644 --- a/src/main/java/com/magamochi/mangamochi/controller/UserFavoriteMangaController.java +++ b/src/main/java/com/magamochi/controller/UserFavoriteMangaController.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.controller; +package com.magamochi.controller; -import com.magamochi.mangamochi.common.dto.DefaultResponseDTO; -import com.magamochi.mangamochi.service.UserFavoriteMangaService; +import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.service.UserFavoriteMangaService; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/magamochi/mangamochi/model/enumeration/ArchiveFileType.java b/src/main/java/com/magamochi/mangamochi/model/enumeration/ArchiveFileType.java deleted file mode 100644 index 9982b20..0000000 --- a/src/main/java/com/magamochi/mangamochi/model/enumeration/ArchiveFileType.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.magamochi.mangamochi.model.enumeration; - -public enum ArchiveFileType { - CBZ, - CBR -} diff --git a/src/main/java/com/magamochi/mangamochi/model/enumeration/UserRole.java b/src/main/java/com/magamochi/mangamochi/model/enumeration/UserRole.java deleted file mode 100644 index 0823d20..0000000 --- a/src/main/java/com/magamochi/mangamochi/model/enumeration/UserRole.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.magamochi.mangamochi.model.enumeration; - -public enum UserRole { - USER -} diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/AuthenticationRequestDTO.java b/src/main/java/com/magamochi/model/dto/AuthenticationRequestDTO.java similarity index 76% rename from src/main/java/com/magamochi/mangamochi/model/dto/AuthenticationRequestDTO.java rename to src/main/java/com/magamochi/model/dto/AuthenticationRequestDTO.java index f5fada2..d45bbbe 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/AuthenticationRequestDTO.java +++ b/src/main/java/com/magamochi/model/dto/AuthenticationRequestDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/AuthenticationResponseDTO.java b/src/main/java/com/magamochi/model/dto/AuthenticationResponseDTO.java similarity index 71% rename from src/main/java/com/magamochi/mangamochi/model/dto/AuthenticationResponseDTO.java rename to src/main/java/com/magamochi/model/dto/AuthenticationResponseDTO.java index 0d5d1ec..dae2b22 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/AuthenticationResponseDTO.java +++ b/src/main/java/com/magamochi/model/dto/AuthenticationResponseDTO.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; -import com.magamochi.mangamochi.model.enumeration.UserRole; +import com.magamochi.model.enumeration.UserRole; import jakarta.validation.constraints.NotNull; public record AuthenticationResponseDTO( diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/ContentProviderMangaChapterResponseDTO.java b/src/main/java/com/magamochi/model/dto/ContentProviderMangaChapterResponseDTO.java similarity index 83% rename from src/main/java/com/magamochi/mangamochi/model/dto/ContentProviderMangaChapterResponseDTO.java rename to src/main/java/com/magamochi/model/dto/ContentProviderMangaChapterResponseDTO.java index 5fe280f..0ea052c 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/ContentProviderMangaChapterResponseDTO.java +++ b/src/main/java/com/magamochi/model/dto/ContentProviderMangaChapterResponseDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import jakarta.validation.constraints.NotBlank; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/ContentProviderMangaInfoResponseDTO.java b/src/main/java/com/magamochi/model/dto/ContentProviderMangaInfoResponseDTO.java similarity index 61% rename from src/main/java/com/magamochi/mangamochi/model/dto/ContentProviderMangaInfoResponseDTO.java rename to src/main/java/com/magamochi/model/dto/ContentProviderMangaInfoResponseDTO.java index f91ee58..82ced22 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/ContentProviderMangaInfoResponseDTO.java +++ b/src/main/java/com/magamochi/model/dto/ContentProviderMangaInfoResponseDTO.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; -import com.magamochi.mangamochi.model.enumeration.MangaStatus; +import com.magamochi.model.enumeration.MangaStatus; import jakarta.validation.constraints.NotBlank; public record ContentProviderMangaInfoResponseDTO( diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/GenreDTO.java b/src/main/java/com/magamochi/model/dto/GenreDTO.java similarity index 73% rename from src/main/java/com/magamochi/mangamochi/model/dto/GenreDTO.java rename to src/main/java/com/magamochi/model/dto/GenreDTO.java index f6f50c1..1283415 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/GenreDTO.java +++ b/src/main/java/com/magamochi/model/dto/GenreDTO.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; -import com.magamochi.mangamochi.model.entity.Genre; +import com.magamochi.model.entity.Genre; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/ImportMangaResponseDTO.java b/src/main/java/com/magamochi/model/dto/ImportMangaResponseDTO.java similarity index 70% rename from src/main/java/com/magamochi/mangamochi/model/dto/ImportMangaResponseDTO.java rename to src/main/java/com/magamochi/model/dto/ImportMangaResponseDTO.java index 7a4327c..3810e7f 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/ImportMangaResponseDTO.java +++ b/src/main/java/com/magamochi/model/dto/ImportMangaResponseDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/ImportRequestDTO.java b/src/main/java/com/magamochi/model/dto/ImportRequestDTO.java similarity index 76% rename from src/main/java/com/magamochi/mangamochi/model/dto/ImportRequestDTO.java rename to src/main/java/com/magamochi/model/dto/ImportRequestDTO.java index 2b0d9f9..e83fce5 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/ImportRequestDTO.java +++ b/src/main/java/com/magamochi/model/dto/ImportRequestDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/ImportReviewDTO.java b/src/main/java/com/magamochi/model/dto/ImportReviewDTO.java similarity index 84% rename from src/main/java/com/magamochi/mangamochi/model/dto/ImportReviewDTO.java rename to src/main/java/com/magamochi/model/dto/ImportReviewDTO.java index b771d0c..58935d7 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/ImportReviewDTO.java +++ b/src/main/java/com/magamochi/model/dto/ImportReviewDTO.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; -import com.magamochi.mangamochi.model.entity.MangaImportReview; +import com.magamochi.model.entity.MangaImportReview; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/LanguageDTO.java b/src/main/java/com/magamochi/model/dto/LanguageDTO.java similarity index 81% rename from src/main/java/com/magamochi/mangamochi/model/dto/LanguageDTO.java rename to src/main/java/com/magamochi/model/dto/LanguageDTO.java index e2d9163..438264f 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/LanguageDTO.java +++ b/src/main/java/com/magamochi/model/dto/LanguageDTO.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import static java.util.Objects.isNull; -import com.magamochi.mangamochi.model.entity.Language; +import com.magamochi.model.entity.Language; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterArchiveDTO.java b/src/main/java/com/magamochi/model/dto/MangaChapterArchiveDTO.java similarity index 81% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterArchiveDTO.java rename to src/main/java/com/magamochi/model/dto/MangaChapterArchiveDTO.java index 503602a..30c7500 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterArchiveDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaChapterArchiveDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterDTO.java b/src/main/java/com/magamochi/model/dto/MangaChapterDTO.java similarity index 84% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterDTO.java rename to src/main/java/com/magamochi/model/dto/MangaChapterDTO.java index 9da55db..9e008b1 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaChapterDTO.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; -import com.magamochi.mangamochi.model.entity.MangaChapter; +import com.magamochi.model.entity.MangaChapter; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterDownloadCommand.java b/src/main/java/com/magamochi/model/dto/MangaChapterDownloadCommand.java similarity index 58% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterDownloadCommand.java rename to src/main/java/com/magamochi/model/dto/MangaChapterDownloadCommand.java index 3f72132..13d8b3a 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterDownloadCommand.java +++ b/src/main/java/com/magamochi/model/dto/MangaChapterDownloadCommand.java @@ -1,3 +1,3 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; public record MangaChapterDownloadCommand(Long chapterId) {} diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterImagesDTO.java b/src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java similarity index 83% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterImagesDTO.java rename to src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java index f13bff6..35b3d3b 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterImagesDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; -import com.magamochi.mangamochi.model.entity.MangaChapter; -import com.magamochi.mangamochi.model.entity.MangaChapterImage; +import com.magamochi.model.entity.MangaChapter; +import com.magamochi.model.entity.MangaChapterImage; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.util.Comparator; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterResponseDTO.java b/src/main/java/com/magamochi/model/dto/MangaChapterResponseDTO.java similarity index 86% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterResponseDTO.java rename to src/main/java/com/magamochi/model/dto/MangaChapterResponseDTO.java index 2d7890d..46194d9 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaChapterResponseDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaChapterResponseDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import jakarta.validation.constraints.NotNull; import java.util.UUID; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaDTO.java b/src/main/java/com/magamochi/model/dto/MangaDTO.java similarity index 87% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaDTO.java rename to src/main/java/com/magamochi/model/dto/MangaDTO.java index 454a8e7..dd5301b 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaDTO.java @@ -1,12 +1,12 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import static java.util.Objects.isNull; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.entity.MangaAlternativeTitle; -import com.magamochi.mangamochi.model.entity.MangaChapter; -import com.magamochi.mangamochi.model.entity.MangaProvider; -import com.magamochi.mangamochi.model.enumeration.ProviderStatus; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.MangaAlternativeTitle; +import com.magamochi.model.entity.MangaChapter; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.enumeration.ProviderStatus; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.time.OffsetDateTime; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaDexMangaDTO.java b/src/main/java/com/magamochi/model/dto/MangaDexMangaDTO.java similarity index 86% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaDexMangaDTO.java rename to src/main/java/com/magamochi/model/dto/MangaDexMangaDTO.java index 38c6753..127fcdf 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaDexMangaDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaDexMangaDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import java.util.List; import java.util.Map; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaListDTO.java b/src/main/java/com/magamochi/model/dto/MangaListDTO.java similarity index 92% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaListDTO.java rename to src/main/java/com/magamochi/model/dto/MangaListDTO.java index e4cc66f..bd08320 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaListDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaListDTO.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import static java.util.Objects.nonNull; -import com.magamochi.mangamochi.model.entity.Manga; +import com.magamochi.model.entity.Manga; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.time.OffsetDateTime; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaListFilterDTO.java b/src/main/java/com/magamochi/model/dto/MangaListFilterDTO.java similarity index 80% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaListFilterDTO.java rename to src/main/java/com/magamochi/model/dto/MangaListFilterDTO.java index f025d7b..dfd670e 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaListFilterDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaListFilterDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import java.util.List; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaListUpdateCommand.java b/src/main/java/com/magamochi/model/dto/MangaListUpdateCommand.java similarity index 65% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaListUpdateCommand.java rename to src/main/java/com/magamochi/model/dto/MangaListUpdateCommand.java index 28f714e..3e15233 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaListUpdateCommand.java +++ b/src/main/java/com/magamochi/model/dto/MangaListUpdateCommand.java @@ -1,3 +1,3 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; public record MangaListUpdateCommand(String contentProviderName, Integer page) {} diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/MangaMessageDTO.java b/src/main/java/com/magamochi/model/dto/MangaMessageDTO.java similarity index 78% rename from src/main/java/com/magamochi/mangamochi/model/dto/MangaMessageDTO.java rename to src/main/java/com/magamochi/model/dto/MangaMessageDTO.java index 910d99b..d529faa 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/MangaMessageDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaMessageDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import java.util.List; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/ProviderListDTO.java b/src/main/java/com/magamochi/model/dto/ProviderListDTO.java similarity index 84% rename from src/main/java/com/magamochi/mangamochi/model/dto/ProviderListDTO.java rename to src/main/java/com/magamochi/model/dto/ProviderListDTO.java index 6cb226f..a92a2f7 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/ProviderListDTO.java +++ b/src/main/java/com/magamochi/model/dto/ProviderListDTO.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; -import com.magamochi.mangamochi.model.entity.*; +import com.magamochi.model.entity.Provider; import jakarta.validation.constraints.NotNull; import java.util.List; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/RefreshTokenRequestDTO.java b/src/main/java/com/magamochi/model/dto/RefreshTokenRequestDTO.java similarity index 73% rename from src/main/java/com/magamochi/mangamochi/model/dto/RefreshTokenRequestDTO.java rename to src/main/java/com/magamochi/model/dto/RefreshTokenRequestDTO.java index d93fc5d..0d78a49 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/RefreshTokenRequestDTO.java +++ b/src/main/java/com/magamochi/model/dto/RefreshTokenRequestDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/RegistrationRequestDTO.java b/src/main/java/com/magamochi/model/dto/RegistrationRequestDTO.java similarity index 65% rename from src/main/java/com/magamochi/mangamochi/model/dto/RegistrationRequestDTO.java rename to src/main/java/com/magamochi/model/dto/RegistrationRequestDTO.java index 55cb983..f3d544f 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/RegistrationRequestDTO.java +++ b/src/main/java/com/magamochi/model/dto/RegistrationRequestDTO.java @@ -1,3 +1,3 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; public record RegistrationRequestDTO(String name, String email, String password) {} diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/TitleMatchRequestDTO.java b/src/main/java/com/magamochi/model/dto/TitleMatchRequestDTO.java similarity index 83% rename from src/main/java/com/magamochi/mangamochi/model/dto/TitleMatchRequestDTO.java rename to src/main/java/com/magamochi/model/dto/TitleMatchRequestDTO.java index a31b88c..835f1c0 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/TitleMatchRequestDTO.java +++ b/src/main/java/com/magamochi/model/dto/TitleMatchRequestDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import java.util.List; import lombok.Builder; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/TitleMatchResponseDTO.java b/src/main/java/com/magamochi/model/dto/TitleMatchResponseDTO.java similarity index 79% rename from src/main/java/com/magamochi/mangamochi/model/dto/TitleMatchResponseDTO.java rename to src/main/java/com/magamochi/model/dto/TitleMatchResponseDTO.java index c2a3362..d5d9f0d 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/TitleMatchResponseDTO.java +++ b/src/main/java/com/magamochi/model/dto/TitleMatchResponseDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; import lombok.Builder; import lombok.Getter; diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/UpdateMangaDataCommand.java b/src/main/java/com/magamochi/model/dto/UpdateMangaDataCommand.java similarity index 55% rename from src/main/java/com/magamochi/mangamochi/model/dto/UpdateMangaDataCommand.java rename to src/main/java/com/magamochi/model/dto/UpdateMangaDataCommand.java index f37aac6..bca7526 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/UpdateMangaDataCommand.java +++ b/src/main/java/com/magamochi/model/dto/UpdateMangaDataCommand.java @@ -1,3 +1,3 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; public record UpdateMangaDataCommand(Long mangaId) {} diff --git a/src/main/java/com/magamochi/mangamochi/model/dto/UpdateMangaFollowChapterListCommand.java b/src/main/java/com/magamochi/model/dto/UpdateMangaFollowChapterListCommand.java similarity index 63% rename from src/main/java/com/magamochi/mangamochi/model/dto/UpdateMangaFollowChapterListCommand.java rename to src/main/java/com/magamochi/model/dto/UpdateMangaFollowChapterListCommand.java index 848720b..442ae9f 100644 --- a/src/main/java/com/magamochi/mangamochi/model/dto/UpdateMangaFollowChapterListCommand.java +++ b/src/main/java/com/magamochi/model/dto/UpdateMangaFollowChapterListCommand.java @@ -1,3 +1,3 @@ -package com.magamochi.mangamochi.model.dto; +package com.magamochi.model.dto; public record UpdateMangaFollowChapterListCommand(Long mangaProviderId) {} diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/Author.java b/src/main/java/com/magamochi/model/entity/Author.java similarity index 92% rename from src/main/java/com/magamochi/mangamochi/model/entity/Author.java rename to src/main/java/com/magamochi/model/entity/Author.java index e5217c6..22a2430 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/Author.java +++ b/src/main/java/com/magamochi/model/entity/Author.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/FlareSession.java b/src/main/java/com/magamochi/model/entity/FlareSession.java similarity index 83% rename from src/main/java/com/magamochi/mangamochi/model/entity/FlareSession.java rename to src/main/java/com/magamochi/model/entity/FlareSession.java index af02dba..e93224f 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/FlareSession.java +++ b/src/main/java/com/magamochi/model/entity/FlareSession.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import java.time.Instant; import lombok.Builder; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/Genre.java b/src/main/java/com/magamochi/model/entity/Genre.java similarity index 89% rename from src/main/java/com/magamochi/mangamochi/model/entity/Genre.java rename to src/main/java/com/magamochi/model/entity/Genre.java index de1b80d..a78e748 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/Genre.java +++ b/src/main/java/com/magamochi/model/entity/Genre.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import java.util.List; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/Image.java b/src/main/java/com/magamochi/model/entity/Image.java similarity index 91% rename from src/main/java/com/magamochi/mangamochi/model/entity/Image.java rename to src/main/java/com/magamochi/model/entity/Image.java index c13ce49..ec636cc 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/Image.java +++ b/src/main/java/com/magamochi/model/entity/Image.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/Language.java b/src/main/java/com/magamochi/model/entity/Language.java similarity index 86% rename from src/main/java/com/magamochi/mangamochi/model/entity/Language.java rename to src/main/java/com/magamochi/model/entity/Language.java index e09c187..b2b6250 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/Language.java +++ b/src/main/java/com/magamochi/model/entity/Language.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/Manga.java b/src/main/java/com/magamochi/model/entity/Manga.java similarity index 96% rename from src/main/java/com/magamochi/mangamochi/model/entity/Manga.java rename to src/main/java/com/magamochi/model/entity/Manga.java index 6c75e62..7eaaa32 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/Manga.java +++ b/src/main/java/com/magamochi/model/entity/Manga.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/MangaAlternativeTitle.java b/src/main/java/com/magamochi/model/entity/MangaAlternativeTitle.java similarity index 88% rename from src/main/java/com/magamochi/mangamochi/model/entity/MangaAlternativeTitle.java rename to src/main/java/com/magamochi/model/entity/MangaAlternativeTitle.java index 61f1d19..b178e04 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/MangaAlternativeTitle.java +++ b/src/main/java/com/magamochi/model/entity/MangaAlternativeTitle.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/MangaAuthor.java b/src/main/java/com/magamochi/model/entity/MangaAuthor.java similarity index 89% rename from src/main/java/com/magamochi/mangamochi/model/entity/MangaAuthor.java rename to src/main/java/com/magamochi/model/entity/MangaAuthor.java index d85e216..0e3468e 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/MangaAuthor.java +++ b/src/main/java/com/magamochi/model/entity/MangaAuthor.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/MangaChapter.java b/src/main/java/com/magamochi/model/entity/MangaChapter.java similarity index 95% rename from src/main/java/com/magamochi/mangamochi/model/entity/MangaChapter.java rename to src/main/java/com/magamochi/model/entity/MangaChapter.java index 0427f9e..d47f11d 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/MangaChapter.java +++ b/src/main/java/com/magamochi/model/entity/MangaChapter.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/MangaChapterImage.java b/src/main/java/com/magamochi/model/entity/MangaChapterImage.java similarity index 93% rename from src/main/java/com/magamochi/mangamochi/model/entity/MangaChapterImage.java rename to src/main/java/com/magamochi/model/entity/MangaChapterImage.java index abcfd00..25da2ba 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/MangaChapterImage.java +++ b/src/main/java/com/magamochi/model/entity/MangaChapterImage.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/MangaGenre.java b/src/main/java/com/magamochi/model/entity/MangaGenre.java similarity index 89% rename from src/main/java/com/magamochi/mangamochi/model/entity/MangaGenre.java rename to src/main/java/com/magamochi/model/entity/MangaGenre.java index 5f05135..7c0df2e 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/MangaGenre.java +++ b/src/main/java/com/magamochi/model/entity/MangaGenre.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/MangaImportReview.java b/src/main/java/com/magamochi/model/entity/MangaImportReview.java similarity index 91% rename from src/main/java/com/magamochi/mangamochi/model/entity/MangaImportReview.java rename to src/main/java/com/magamochi/model/entity/MangaImportReview.java index 9321c84..e291e7e 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/MangaImportReview.java +++ b/src/main/java/com/magamochi/model/entity/MangaImportReview.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/MangaProvider.java b/src/main/java/com/magamochi/model/entity/MangaProvider.java similarity index 94% rename from src/main/java/com/magamochi/mangamochi/model/entity/MangaProvider.java rename to src/main/java/com/magamochi/model/entity/MangaProvider.java index fbcb97d..0be20c9 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/MangaProvider.java +++ b/src/main/java/com/magamochi/model/entity/MangaProvider.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/Provider.java b/src/main/java/com/magamochi/model/entity/Provider.java similarity index 87% rename from src/main/java/com/magamochi/mangamochi/model/entity/Provider.java rename to src/main/java/com/magamochi/model/entity/Provider.java index bbea2ac..5e6d5d7 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/Provider.java +++ b/src/main/java/com/magamochi/model/entity/Provider.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; -import com.magamochi.mangamochi.model.enumeration.ProviderStatus; +import com.magamochi.model.enumeration.ProviderStatus; import jakarta.persistence.*; import java.time.Instant; import java.util.List; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/User.java b/src/main/java/com/magamochi/model/entity/User.java similarity index 85% rename from src/main/java/com/magamochi/mangamochi/model/entity/User.java rename to src/main/java/com/magamochi/model/entity/User.java index b40f6ff..dd52061 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/User.java +++ b/src/main/java/com/magamochi/model/entity/User.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; -import com.magamochi.mangamochi.model.enumeration.UserRole; +import com.magamochi.model.enumeration.UserRole; import jakarta.persistence.*; import java.util.Set; import lombok.*; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/UserFavoriteManga.java b/src/main/java/com/magamochi/model/entity/UserFavoriteManga.java similarity index 91% rename from src/main/java/com/magamochi/mangamochi/model/entity/UserFavoriteManga.java rename to src/main/java/com/magamochi/model/entity/UserFavoriteManga.java index a684f17..102f50d 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/UserFavoriteManga.java +++ b/src/main/java/com/magamochi/model/entity/UserFavoriteManga.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import java.time.Instant; diff --git a/src/main/java/com/magamochi/mangamochi/model/entity/UserMangaFollow.java b/src/main/java/com/magamochi/model/entity/UserMangaFollow.java similarity index 89% rename from src/main/java/com/magamochi/mangamochi/model/entity/UserMangaFollow.java rename to src/main/java/com/magamochi/model/entity/UserMangaFollow.java index 633c17d..c62932f 100644 --- a/src/main/java/com/magamochi/mangamochi/model/entity/UserMangaFollow.java +++ b/src/main/java/com/magamochi/model/entity/UserMangaFollow.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.entity; +package com.magamochi.model.entity; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/model/enumeration/ArchiveFileType.java b/src/main/java/com/magamochi/model/enumeration/ArchiveFileType.java new file mode 100644 index 0000000..4a26d3f --- /dev/null +++ b/src/main/java/com/magamochi/model/enumeration/ArchiveFileType.java @@ -0,0 +1,6 @@ +package com.magamochi.model.enumeration; + +public enum ArchiveFileType { + CBZ, + CBR +} diff --git a/src/main/java/com/magamochi/mangamochi/model/enumeration/MangaStatus.java b/src/main/java/com/magamochi/model/enumeration/MangaStatus.java similarity index 62% rename from src/main/java/com/magamochi/mangamochi/model/enumeration/MangaStatus.java rename to src/main/java/com/magamochi/model/enumeration/MangaStatus.java index d0dc22e..d7e3ca5 100644 --- a/src/main/java/com/magamochi/mangamochi/model/enumeration/MangaStatus.java +++ b/src/main/java/com/magamochi/model/enumeration/MangaStatus.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.enumeration; +package com.magamochi.model.enumeration; public enum MangaStatus { ONGOING, diff --git a/src/main/java/com/magamochi/mangamochi/model/enumeration/ProviderStatus.java b/src/main/java/com/magamochi/model/enumeration/ProviderStatus.java similarity index 50% rename from src/main/java/com/magamochi/mangamochi/model/enumeration/ProviderStatus.java rename to src/main/java/com/magamochi/model/enumeration/ProviderStatus.java index cb09460..c09ddb0 100644 --- a/src/main/java/com/magamochi/mangamochi/model/enumeration/ProviderStatus.java +++ b/src/main/java/com/magamochi/model/enumeration/ProviderStatus.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.model.enumeration; +package com.magamochi.model.enumeration; public enum ProviderStatus { ACTIVE, diff --git a/src/main/java/com/magamochi/model/enumeration/UserRole.java b/src/main/java/com/magamochi/model/enumeration/UserRole.java new file mode 100644 index 0000000..dc9b57e --- /dev/null +++ b/src/main/java/com/magamochi/model/enumeration/UserRole.java @@ -0,0 +1,5 @@ +package com.magamochi.model.enumeration; + +public enum UserRole { + USER +} diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/AuthorRepository.java b/src/main/java/com/magamochi/model/repository/AuthorRepository.java similarity index 70% rename from src/main/java/com/magamochi/mangamochi/model/repository/AuthorRepository.java rename to src/main/java/com/magamochi/model/repository/AuthorRepository.java index 4ab61ff..52e4465 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/AuthorRepository.java +++ b/src/main/java/com/magamochi/model/repository/AuthorRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.Author; +import com.magamochi.model.entity.Author; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/GenreRepository.java b/src/main/java/com/magamochi/model/repository/GenreRepository.java similarity index 70% rename from src/main/java/com/magamochi/mangamochi/model/repository/GenreRepository.java rename to src/main/java/com/magamochi/model/repository/GenreRepository.java index 438148e..3acbe30 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/GenreRepository.java +++ b/src/main/java/com/magamochi/model/repository/GenreRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.Genre; +import com.magamochi.model.entity.Genre; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/ImageRepository.java b/src/main/java/com/magamochi/model/repository/ImageRepository.java similarity index 60% rename from src/main/java/com/magamochi/mangamochi/model/repository/ImageRepository.java rename to src/main/java/com/magamochi/model/repository/ImageRepository.java index 688fa9b..9decd54 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/ImageRepository.java +++ b/src/main/java/com/magamochi/model/repository/ImageRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.Image; +import com.magamochi.model.entity.Image; import java.util.UUID; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/LanguageRepository.java b/src/main/java/com/magamochi/model/repository/LanguageRepository.java similarity index 67% rename from src/main/java/com/magamochi/mangamochi/model/repository/LanguageRepository.java rename to src/main/java/com/magamochi/model/repository/LanguageRepository.java index 5411c94..0c288c2 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/LanguageRepository.java +++ b/src/main/java/com/magamochi/model/repository/LanguageRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.Language; +import com.magamochi.model.entity.Language; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/MangaAlternativeTitlesRepository.java b/src/main/java/com/magamochi/model/repository/MangaAlternativeTitlesRepository.java similarity index 59% rename from src/main/java/com/magamochi/mangamochi/model/repository/MangaAlternativeTitlesRepository.java rename to src/main/java/com/magamochi/model/repository/MangaAlternativeTitlesRepository.java index 150f704..86bc9a0 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/MangaAlternativeTitlesRepository.java +++ b/src/main/java/com/magamochi/model/repository/MangaAlternativeTitlesRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.MangaAlternativeTitle; +import com.magamochi.model.entity.MangaAlternativeTitle; import org.springframework.data.jpa.repository.JpaRepository; public interface MangaAlternativeTitlesRepository diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/MangaAuthorRepository.java b/src/main/java/com/magamochi/model/repository/MangaAuthorRepository.java similarity index 53% rename from src/main/java/com/magamochi/mangamochi/model/repository/MangaAuthorRepository.java rename to src/main/java/com/magamochi/model/repository/MangaAuthorRepository.java index 5020397..78a4833 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/MangaAuthorRepository.java +++ b/src/main/java/com/magamochi/model/repository/MangaAuthorRepository.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.Author; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.entity.MangaAuthor; +import com.magamochi.model.entity.Author; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.MangaAuthor; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/MangaChapterImageRepository.java b/src/main/java/com/magamochi/model/repository/MangaChapterImageRepository.java similarity index 59% rename from src/main/java/com/magamochi/mangamochi/model/repository/MangaChapterImageRepository.java rename to src/main/java/com/magamochi/model/repository/MangaChapterImageRepository.java index a312418..0dd8574 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/MangaChapterImageRepository.java +++ b/src/main/java/com/magamochi/model/repository/MangaChapterImageRepository.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.MangaChapter; -import com.magamochi.mangamochi.model.entity.MangaChapterImage; +import com.magamochi.model.entity.MangaChapter; +import com.magamochi.model.entity.MangaChapterImage; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/MangaChapterRepository.java b/src/main/java/com/magamochi/model/repository/MangaChapterRepository.java similarity index 71% rename from src/main/java/com/magamochi/mangamochi/model/repository/MangaChapterRepository.java rename to src/main/java/com/magamochi/model/repository/MangaChapterRepository.java index 7e104f0..3292cff 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/MangaChapterRepository.java +++ b/src/main/java/com/magamochi/model/repository/MangaChapterRepository.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.MangaChapter; -import com.magamochi.mangamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.MangaChapter; +import com.magamochi.model.entity.MangaProvider; import jakarta.validation.constraints.NotBlank; import java.util.List; import java.util.Optional; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/MangaGenreRepository.java b/src/main/java/com/magamochi/model/repository/MangaGenreRepository.java similarity index 59% rename from src/main/java/com/magamochi/mangamochi/model/repository/MangaGenreRepository.java rename to src/main/java/com/magamochi/model/repository/MangaGenreRepository.java index fd100fc..c0843b9 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/MangaGenreRepository.java +++ b/src/main/java/com/magamochi/model/repository/MangaGenreRepository.java @@ -1,6 +1,8 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.*; +import com.magamochi.model.entity.Genre; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.MangaGenre; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/MangaImportReviewRepository.java b/src/main/java/com/magamochi/model/repository/MangaImportReviewRepository.java similarity index 67% rename from src/main/java/com/magamochi/mangamochi/model/repository/MangaImportReviewRepository.java rename to src/main/java/com/magamochi/model/repository/MangaImportReviewRepository.java index 8114803..023006f 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/MangaImportReviewRepository.java +++ b/src/main/java/com/magamochi/model/repository/MangaImportReviewRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.MangaImportReview; +import com.magamochi.model.entity.MangaImportReview; import org.springframework.data.jpa.repository.JpaRepository; public interface MangaImportReviewRepository extends JpaRepository { diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/MangaProviderRepository.java b/src/main/java/com/magamochi/model/repository/MangaProviderRepository.java similarity index 63% rename from src/main/java/com/magamochi/mangamochi/model/repository/MangaProviderRepository.java rename to src/main/java/com/magamochi/model/repository/MangaProviderRepository.java index 814d6d5..eb10676 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/MangaProviderRepository.java +++ b/src/main/java/com/magamochi/model/repository/MangaProviderRepository.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.entity.MangaProvider; -import com.magamochi.mangamochi.model.entity.Provider; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.Provider; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/MangaRepository.java b/src/main/java/com/magamochi/model/repository/MangaRepository.java similarity index 82% rename from src/main/java/com/magamochi/mangamochi/model/repository/MangaRepository.java rename to src/main/java/com/magamochi/model/repository/MangaRepository.java index 8e981e9..9f0a088 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/MangaRepository.java +++ b/src/main/java/com/magamochi/model/repository/MangaRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.Manga; +import com.magamochi.model.entity.Manga; import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/ProviderRepository.java b/src/main/java/com/magamochi/model/repository/ProviderRepository.java similarity index 67% rename from src/main/java/com/magamochi/mangamochi/model/repository/ProviderRepository.java rename to src/main/java/com/magamochi/model/repository/ProviderRepository.java index 762bc93..57ba04a 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/ProviderRepository.java +++ b/src/main/java/com/magamochi/model/repository/ProviderRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.Provider; +import com.magamochi.model.entity.Provider; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/UserFavoriteMangaRepository.java b/src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java similarity index 64% rename from src/main/java/com/magamochi/mangamochi/model/repository/UserFavoriteMangaRepository.java rename to src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java index 5567b26..365c5ba 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/UserFavoriteMangaRepository.java +++ b/src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.entity.User; -import com.magamochi.mangamochi.model.entity.UserFavoriteManga; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.User; +import com.magamochi.model.entity.UserFavoriteManga; import java.util.Optional; import java.util.Set; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/UserMangaFollowRepository.java b/src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java similarity index 68% rename from src/main/java/com/magamochi/mangamochi/model/repository/UserMangaFollowRepository.java rename to src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java index 9bb374a..154e532 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/UserMangaFollowRepository.java +++ b/src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.entity.User; -import com.magamochi.mangamochi.model.entity.UserMangaFollow; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.User; +import com.magamochi.model.entity.UserMangaFollow; import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/repository/UserRepository.java b/src/main/java/com/magamochi/model/repository/UserRepository.java similarity index 70% rename from src/main/java/com/magamochi/mangamochi/model/repository/UserRepository.java rename to src/main/java/com/magamochi/model/repository/UserRepository.java index 6b31929..f083182 100644 --- a/src/main/java/com/magamochi/mangamochi/model/repository/UserRepository.java +++ b/src/main/java/com/magamochi/model/repository/UserRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.model.repository; +package com.magamochi.model.repository; -import com.magamochi.mangamochi.model.entity.User; +import com.magamochi.model.entity.User; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/mangamochi/model/specification/MangaSpecification.java b/src/main/java/com/magamochi/model/specification/MangaSpecification.java similarity index 92% rename from src/main/java/com/magamochi/mangamochi/model/specification/MangaSpecification.java rename to src/main/java/com/magamochi/model/specification/MangaSpecification.java index 267938e..4720847 100644 --- a/src/main/java/com/magamochi/mangamochi/model/specification/MangaSpecification.java +++ b/src/main/java/com/magamochi/model/specification/MangaSpecification.java @@ -1,11 +1,11 @@ -package com.magamochi.mangamochi.model.specification; +package com.magamochi.model.specification; import static java.util.Objects.nonNull; -import com.magamochi.mangamochi.model.dto.MangaListFilterDTO; -import com.magamochi.mangamochi.model.entity.Author; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.entity.User; +import com.magamochi.model.dto.MangaListFilterDTO; +import com.magamochi.model.entity.Author; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.User; import jakarta.persistence.criteria.*; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/magamochi/mangamochi/queue/MangaChapterDownloadConsumer.java b/src/main/java/com/magamochi/queue/MangaChapterDownloadConsumer.java similarity index 81% rename from src/main/java/com/magamochi/mangamochi/queue/MangaChapterDownloadConsumer.java rename to src/main/java/com/magamochi/queue/MangaChapterDownloadConsumer.java index 3c286c2..43da465 100644 --- a/src/main/java/com/magamochi/mangamochi/queue/MangaChapterDownloadConsumer.java +++ b/src/main/java/com/magamochi/queue/MangaChapterDownloadConsumer.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.queue; +package com.magamochi.queue; -import com.magamochi.mangamochi.model.dto.MangaChapterDownloadCommand; -import com.magamochi.mangamochi.service.MangaChapterService; +import com.magamochi.model.dto.MangaChapterDownloadCommand; +import com.magamochi.service.MangaChapterService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.rabbit.annotation.RabbitListener; diff --git a/src/main/java/com/magamochi/mangamochi/queue/MangaChapterDownloadProducer.java b/src/main/java/com/magamochi/queue/MangaChapterDownloadProducer.java similarity index 86% rename from src/main/java/com/magamochi/mangamochi/queue/MangaChapterDownloadProducer.java rename to src/main/java/com/magamochi/queue/MangaChapterDownloadProducer.java index dfafb53..740eaf4 100644 --- a/src/main/java/com/magamochi/mangamochi/queue/MangaChapterDownloadProducer.java +++ b/src/main/java/com/magamochi/queue/MangaChapterDownloadProducer.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.queue; +package com.magamochi.queue; -import com.magamochi.mangamochi.model.dto.MangaChapterDownloadCommand; +import com.magamochi.model.dto.MangaChapterDownloadCommand; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.rabbit.core.RabbitTemplate; diff --git a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaDataConsumer.java b/src/main/java/com/magamochi/queue/UpdateMangaDataConsumer.java similarity index 78% rename from src/main/java/com/magamochi/mangamochi/queue/UpdateMangaDataConsumer.java rename to src/main/java/com/magamochi/queue/UpdateMangaDataConsumer.java index 519c5bc..2aa0169 100644 --- a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaDataConsumer.java +++ b/src/main/java/com/magamochi/queue/UpdateMangaDataConsumer.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.queue; +package com.magamochi.queue; -import com.magamochi.mangamochi.model.dto.UpdateMangaDataCommand; -import com.magamochi.mangamochi.service.MangaImportService; +import com.magamochi.model.dto.UpdateMangaDataCommand; +import com.magamochi.service.MangaImportService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.rabbit.annotation.RabbitListener; diff --git a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaDataProducer.java b/src/main/java/com/magamochi/queue/UpdateMangaDataProducer.java similarity index 86% rename from src/main/java/com/magamochi/mangamochi/queue/UpdateMangaDataProducer.java rename to src/main/java/com/magamochi/queue/UpdateMangaDataProducer.java index 12f67a9..8516218 100644 --- a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaDataProducer.java +++ b/src/main/java/com/magamochi/queue/UpdateMangaDataProducer.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.queue; +package com.magamochi.queue; -import com.magamochi.mangamochi.model.dto.UpdateMangaDataCommand; +import com.magamochi.model.dto.UpdateMangaDataCommand; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.rabbit.core.RabbitTemplate; diff --git a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaFollowChapterListConsumer.java b/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java similarity index 79% rename from src/main/java/com/magamochi/mangamochi/queue/UpdateMangaFollowChapterListConsumer.java rename to src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java index 4d1632f..64b40f2 100644 --- a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaFollowChapterListConsumer.java +++ b/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.queue; +package com.magamochi.queue; -import com.magamochi.mangamochi.model.dto.UpdateMangaFollowChapterListCommand; -import com.magamochi.mangamochi.service.MangaService; +import com.magamochi.model.dto.UpdateMangaFollowChapterListCommand; +import com.magamochi.service.MangaService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.rabbit.annotation.RabbitListener; diff --git a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaFollowChapterListProducer.java b/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListProducer.java similarity index 86% rename from src/main/java/com/magamochi/mangamochi/queue/UpdateMangaFollowChapterListProducer.java rename to src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListProducer.java index 14ee2c9..124d3a5 100644 --- a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaFollowChapterListProducer.java +++ b/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListProducer.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.queue; +package com.magamochi.queue; -import com.magamochi.mangamochi.model.dto.UpdateMangaFollowChapterListCommand; +import com.magamochi.model.dto.UpdateMangaFollowChapterListCommand; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.rabbit.core.RabbitTemplate; diff --git a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaListConsumer.java b/src/main/java/com/magamochi/queue/UpdateMangaListConsumer.java similarity index 79% rename from src/main/java/com/magamochi/mangamochi/queue/UpdateMangaListConsumer.java rename to src/main/java/com/magamochi/queue/UpdateMangaListConsumer.java index e8e3a0b..6a04c3c 100644 --- a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaListConsumer.java +++ b/src/main/java/com/magamochi/queue/UpdateMangaListConsumer.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.queue; +package com.magamochi.queue; -import com.magamochi.mangamochi.model.dto.MangaListUpdateCommand; -import com.magamochi.mangamochi.service.MangaListService; +import com.magamochi.model.dto.MangaListUpdateCommand; +import com.magamochi.service.MangaListService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.rabbit.annotation.RabbitListener; diff --git a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaListProducer.java b/src/main/java/com/magamochi/queue/UpdateMangaListProducer.java similarity index 86% rename from src/main/java/com/magamochi/mangamochi/queue/UpdateMangaListProducer.java rename to src/main/java/com/magamochi/queue/UpdateMangaListProducer.java index e154d0d..7d74af5 100644 --- a/src/main/java/com/magamochi/mangamochi/queue/UpdateMangaListProducer.java +++ b/src/main/java/com/magamochi/queue/UpdateMangaListProducer.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.queue; +package com.magamochi.queue; -import com.magamochi.mangamochi.model.dto.MangaListUpdateCommand; +import com.magamochi.model.dto.MangaListUpdateCommand; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.rabbit.core.RabbitTemplate; diff --git a/src/main/java/com/magamochi/mangamochi/registry/FlareSessionRegistry.java b/src/main/java/com/magamochi/registry/FlareSessionRegistry.java similarity index 83% rename from src/main/java/com/magamochi/mangamochi/registry/FlareSessionRegistry.java rename to src/main/java/com/magamochi/registry/FlareSessionRegistry.java index 33f6b68..969480f 100644 --- a/src/main/java/com/magamochi/mangamochi/registry/FlareSessionRegistry.java +++ b/src/main/java/com/magamochi/registry/FlareSessionRegistry.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.registry; +package com.magamochi.registry; -import com.magamochi.mangamochi.model.entity.FlareSession; +import com.magamochi.model.entity.FlareSession; import java.util.concurrent.ConcurrentHashMap; import lombok.Getter; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/magamochi/mangamochi/security/JwtRequestFilter.java b/src/main/java/com/magamochi/security/JwtRequestFilter.java similarity index 94% rename from src/main/java/com/magamochi/mangamochi/security/JwtRequestFilter.java rename to src/main/java/com/magamochi/security/JwtRequestFilter.java index 719c8f2..a41c07e 100644 --- a/src/main/java/com/magamochi/mangamochi/security/JwtRequestFilter.java +++ b/src/main/java/com/magamochi/security/JwtRequestFilter.java @@ -1,10 +1,10 @@ -package com.magamochi.mangamochi.security; +package com.magamochi.security; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import com.magamochi.mangamochi.service.CustomUserDetailsService; -import com.magamochi.mangamochi.util.JwtUtil; +import com.magamochi.service.CustomUserDetailsService; +import com.magamochi.util.JwtUtil; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/com/magamochi/mangamochi/service/CustomUserDetailsService.java b/src/main/java/com/magamochi/service/CustomUserDetailsService.java similarity index 91% rename from src/main/java/com/magamochi/mangamochi/service/CustomUserDetailsService.java rename to src/main/java/com/magamochi/service/CustomUserDetailsService.java index 9ce19af..c15b00a 100644 --- a/src/main/java/com/magamochi/mangamochi/service/CustomUserDetailsService.java +++ b/src/main/java/com/magamochi/service/CustomUserDetailsService.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; -import com.magamochi.mangamochi.model.repository.UserRepository; +import com.magamochi.model.repository.UserRepository; import java.util.Collections; import lombok.RequiredArgsConstructor; import org.springframework.security.core.authority.SimpleGrantedAuthority; diff --git a/src/main/java/com/magamochi/mangamochi/service/FlareService.java b/src/main/java/com/magamochi/service/FlareService.java similarity index 88% rename from src/main/java/com/magamochi/mangamochi/service/FlareService.java rename to src/main/java/com/magamochi/service/FlareService.java index bac79a2..8ee2d9d 100644 --- a/src/main/java/com/magamochi/mangamochi/service/FlareService.java +++ b/src/main/java/com/magamochi/service/FlareService.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; -import com.magamochi.mangamochi.client.FlareClient; +import com.magamochi.client.FlareClient; import lombok.RequiredArgsConstructor; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; diff --git a/src/main/java/com/magamochi/mangamochi/service/FlareSessionManager.java b/src/main/java/com/magamochi/service/FlareSessionManager.java similarity index 88% rename from src/main/java/com/magamochi/mangamochi/service/FlareSessionManager.java rename to src/main/java/com/magamochi/service/FlareSessionManager.java index 97c16d8..eea1d60 100644 --- a/src/main/java/com/magamochi/mangamochi/service/FlareSessionManager.java +++ b/src/main/java/com/magamochi/service/FlareSessionManager.java @@ -1,11 +1,11 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import com.magamochi.mangamochi.client.FlareClient; -import com.magamochi.mangamochi.model.entity.FlareSession; -import com.magamochi.mangamochi.registry.FlareSessionRegistry; +import com.magamochi.client.FlareClient; +import com.magamochi.model.entity.FlareSession; +import com.magamochi.registry.FlareSessionRegistry; import java.time.Duration; import java.time.Instant; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/mangamochi/service/GenreService.java b/src/main/java/com/magamochi/service/GenreService.java similarity index 69% rename from src/main/java/com/magamochi/mangamochi/service/GenreService.java rename to src/main/java/com/magamochi/service/GenreService.java index bf6d4c2..e18f4ef 100644 --- a/src/main/java/com/magamochi/mangamochi/service/GenreService.java +++ b/src/main/java/com/magamochi/service/GenreService.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; -import com.magamochi.mangamochi.model.dto.GenreDTO; -import com.magamochi.mangamochi.model.repository.GenreRepository; +import com.magamochi.model.dto.GenreDTO; +import com.magamochi.model.repository.GenreRepository; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/mangamochi/service/ImageService.java b/src/main/java/com/magamochi/service/ImageService.java similarity index 81% rename from src/main/java/com/magamochi/mangamochi/service/ImageService.java rename to src/main/java/com/magamochi/service/ImageService.java index 8162b06..da41ced 100644 --- a/src/main/java/com/magamochi/mangamochi/service/ImageService.java +++ b/src/main/java/com/magamochi/service/ImageService.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; -import com.magamochi.mangamochi.model.entity.Image; -import com.magamochi.mangamochi.model.repository.ImageRepository; +import com.magamochi.model.entity.Image; +import com.magamochi.model.repository.ImageRepository; import java.io.InputStream; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/magamochi/mangamochi/service/LanguageService.java b/src/main/java/com/magamochi/service/LanguageService.java similarity index 64% rename from src/main/java/com/magamochi/mangamochi/service/LanguageService.java rename to src/main/java/com/magamochi/service/LanguageService.java index 7c7ba84..b37ff9d 100644 --- a/src/main/java/com/magamochi/mangamochi/service/LanguageService.java +++ b/src/main/java/com/magamochi/service/LanguageService.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; -import com.magamochi.mangamochi.common.exception.NotFoundException; -import com.magamochi.mangamochi.model.entity.Language; -import com.magamochi.mangamochi.model.repository.LanguageRepository; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.model.entity.Language; +import com.magamochi.model.repository.LanguageRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/mangamochi/service/MangaChapterService.java b/src/main/java/com/magamochi/service/MangaChapterService.java similarity index 91% rename from src/main/java/com/magamochi/mangamochi/service/MangaChapterService.java rename to src/main/java/com/magamochi/service/MangaChapterService.java index 98085f9..3c0ab72 100644 --- a/src/main/java/com/magamochi/mangamochi/service/MangaChapterService.java +++ b/src/main/java/com/magamochi/service/MangaChapterService.java @@ -1,15 +1,15 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import com.google.common.util.concurrent.RateLimiter; -import com.magamochi.mangamochi.common.exception.UnprocessableException; -import com.magamochi.mangamochi.model.dto.MangaChapterArchiveDTO; -import com.magamochi.mangamochi.model.dto.MangaChapterImagesDTO; -import com.magamochi.mangamochi.model.entity.MangaChapter; -import com.magamochi.mangamochi.model.entity.MangaChapterImage; -import com.magamochi.mangamochi.model.enumeration.ArchiveFileType; -import com.magamochi.mangamochi.model.repository.MangaChapterImageRepository; -import com.magamochi.mangamochi.model.repository.MangaChapterRepository; -import com.magamochi.mangamochi.service.providers.ContentProviderFactory; +import com.magamochi.common.exception.UnprocessableException; +import com.magamochi.model.dto.MangaChapterArchiveDTO; +import com.magamochi.model.dto.MangaChapterImagesDTO; +import com.magamochi.model.entity.MangaChapter; +import com.magamochi.model.entity.MangaChapterImage; +import com.magamochi.model.enumeration.ArchiveFileType; +import com.magamochi.model.repository.MangaChapterImageRepository; +import com.magamochi.model.repository.MangaChapterRepository; +import com.magamochi.service.providers.ContentProviderFactory; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryRegistry; import java.io.BufferedInputStream; diff --git a/src/main/java/com/magamochi/mangamochi/service/MangaCreationService.java b/src/main/java/com/magamochi/service/MangaCreationService.java similarity index 88% rename from src/main/java/com/magamochi/mangamochi/service/MangaCreationService.java rename to src/main/java/com/magamochi/service/MangaCreationService.java index 6e22b1e..343ab56 100644 --- a/src/main/java/com/magamochi/mangamochi/service/MangaCreationService.java +++ b/src/main/java/com/magamochi/service/MangaCreationService.java @@ -1,18 +1,18 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static java.util.Objects.nonNull; import com.google.common.util.concurrent.RateLimiter; -import com.magamochi.mangamochi.client.AniListClient; -import com.magamochi.mangamochi.client.JikanClient; -import com.magamochi.mangamochi.model.dto.TitleMatchRequestDTO; -import com.magamochi.mangamochi.model.dto.UpdateMangaDataCommand; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.entity.MangaImportReview; -import com.magamochi.mangamochi.model.entity.Provider; -import com.magamochi.mangamochi.model.repository.MangaImportReviewRepository; -import com.magamochi.mangamochi.model.repository.MangaRepository; -import com.magamochi.mangamochi.queue.UpdateMangaDataProducer; +import com.magamochi.client.AniListClient; +import com.magamochi.client.JikanClient; +import com.magamochi.model.dto.TitleMatchRequestDTO; +import com.magamochi.model.dto.UpdateMangaDataCommand; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.MangaImportReview; +import com.magamochi.model.entity.Provider; +import com.magamochi.model.repository.MangaImportReviewRepository; +import com.magamochi.model.repository.MangaRepository; +import com.magamochi.queue.UpdateMangaDataProducer; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/mangamochi/service/MangaImportReviewService.java b/src/main/java/com/magamochi/service/MangaImportReviewService.java similarity index 76% rename from src/main/java/com/magamochi/mangamochi/service/MangaImportReviewService.java rename to src/main/java/com/magamochi/service/MangaImportReviewService.java index e0a0a30..7b772cd 100644 --- a/src/main/java/com/magamochi/mangamochi/service/MangaImportReviewService.java +++ b/src/main/java/com/magamochi/service/MangaImportReviewService.java @@ -1,19 +1,19 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static java.util.Objects.isNull; import com.google.common.util.concurrent.RateLimiter; -import com.magamochi.mangamochi.client.JikanClient; -import com.magamochi.mangamochi.common.exception.NotFoundException; -import com.magamochi.mangamochi.model.dto.ImportReviewDTO; -import com.magamochi.mangamochi.model.dto.UpdateMangaDataCommand; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.entity.MangaImportReview; -import com.magamochi.mangamochi.model.entity.MangaProvider; -import com.magamochi.mangamochi.model.repository.MangaImportReviewRepository; -import com.magamochi.mangamochi.model.repository.MangaProviderRepository; -import com.magamochi.mangamochi.model.repository.MangaRepository; -import com.magamochi.mangamochi.queue.UpdateMangaDataProducer; +import com.magamochi.client.JikanClient; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.model.dto.ImportReviewDTO; +import com.magamochi.model.dto.UpdateMangaDataCommand; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.MangaImportReview; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.repository.MangaImportReviewRepository; +import com.magamochi.model.repository.MangaProviderRepository; +import com.magamochi.model.repository.MangaRepository; +import com.magamochi.queue.UpdateMangaDataProducer; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/mangamochi/service/MangaImportService.java b/src/main/java/com/magamochi/service/MangaImportService.java similarity index 96% rename from src/main/java/com/magamochi/mangamochi/service/MangaImportService.java rename to src/main/java/com/magamochi/service/MangaImportService.java index ef95e92..eb6c578 100644 --- a/src/main/java/com/magamochi/mangamochi/service/MangaImportService.java +++ b/src/main/java/com/magamochi/service/MangaImportService.java @@ -1,16 +1,16 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import com.google.common.util.concurrent.RateLimiter; -import com.magamochi.mangamochi.client.AniListClient; -import com.magamochi.mangamochi.client.JikanClient; -import com.magamochi.mangamochi.common.exception.NotFoundException; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.mangamochi.model.entity.*; -import com.magamochi.mangamochi.model.repository.*; -import com.magamochi.mangamochi.util.DoubleUtil; +import com.magamochi.client.AniListClient; +import com.magamochi.client.JikanClient; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; +import com.magamochi.model.entity.*; +import com.magamochi.model.repository.*; +import com.magamochi.util.DoubleUtil; import java.io.*; import java.net.URI; import java.net.URISyntaxException; diff --git a/src/main/java/com/magamochi/mangamochi/service/MangaListService.java b/src/main/java/com/magamochi/service/MangaListService.java similarity index 87% rename from src/main/java/com/magamochi/mangamochi/service/MangaListService.java rename to src/main/java/com/magamochi/service/MangaListService.java index add1f16..6f51af4 100644 --- a/src/main/java/com/magamochi/mangamochi/service/MangaListService.java +++ b/src/main/java/com/magamochi/service/MangaListService.java @@ -1,10 +1,10 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static java.util.Objects.isNull; -import com.magamochi.mangamochi.model.entity.MangaProvider; -import com.magamochi.mangamochi.model.repository.MangaProviderRepository; -import com.magamochi.mangamochi.service.providers.PagedContentProviderFactory; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.repository.MangaProviderRepository; +import com.magamochi.service.providers.PagedContentProviderFactory; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/mangamochi/service/MangaService.java b/src/main/java/com/magamochi/service/MangaService.java similarity index 90% rename from src/main/java/com/magamochi/mangamochi/service/MangaService.java rename to src/main/java/com/magamochi/service/MangaService.java index 4e39e72..7df23a5 100644 --- a/src/main/java/com/magamochi/mangamochi/service/MangaService.java +++ b/src/main/java/com/magamochi/service/MangaService.java @@ -1,18 +1,18 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static java.util.Objects.nonNull; -import com.magamochi.mangamochi.client.NtfyClient; -import com.magamochi.mangamochi.common.exception.NotFoundException; -import com.magamochi.mangamochi.model.dto.*; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.entity.MangaChapter; -import com.magamochi.mangamochi.model.entity.MangaProvider; -import com.magamochi.mangamochi.model.entity.UserMangaFollow; -import com.magamochi.mangamochi.model.repository.*; -import com.magamochi.mangamochi.model.specification.MangaSpecification; -import com.magamochi.mangamochi.queue.MangaChapterDownloadProducer; -import com.magamochi.mangamochi.service.providers.ContentProviderFactory; +import com.magamochi.client.NtfyClient; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.model.dto.*; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.MangaChapter; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.UserMangaFollow; +import com.magamochi.model.repository.*; +import com.magamochi.model.specification.MangaSpecification; +import com.magamochi.queue.MangaChapterDownloadProducer; +import com.magamochi.service.providers.ContentProviderFactory; import java.util.Comparator; import java.util.List; import java.util.Set; diff --git a/src/main/java/com/magamochi/mangamochi/service/ProviderManualMangaImportService.java b/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java similarity index 81% rename from src/main/java/com/magamochi/mangamochi/service/ProviderManualMangaImportService.java rename to src/main/java/com/magamochi/service/ProviderManualMangaImportService.java index 358a1df..00cba42 100644 --- a/src/main/java/com/magamochi/mangamochi/service/ProviderManualMangaImportService.java +++ b/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java @@ -1,15 +1,17 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import com.magamochi.mangamochi.common.exception.NotFoundException; -import com.magamochi.mangamochi.model.dto.ImportMangaResponseDTO; -import com.magamochi.mangamochi.model.dto.ImportRequestDTO; -import com.magamochi.mangamochi.model.entity.*; -import com.magamochi.mangamochi.model.enumeration.ProviderStatus; -import com.magamochi.mangamochi.model.repository.*; -import com.magamochi.mangamochi.service.providers.ManualImportContentProviderFactory; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.model.dto.ImportMangaResponseDTO; +import com.magamochi.model.dto.ImportRequestDTO; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.Provider; +import com.magamochi.model.enumeration.ProviderStatus; +import com.magamochi.model.repository.MangaProviderRepository; +import com.magamochi.model.repository.ProviderRepository; +import com.magamochi.service.providers.ManualImportContentProviderFactory; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/mangamochi/service/ProviderService.java b/src/main/java/com/magamochi/service/ProviderService.java similarity index 79% rename from src/main/java/com/magamochi/mangamochi/service/ProviderService.java rename to src/main/java/com/magamochi/service/ProviderService.java index 88581c2..47f7e3d 100644 --- a/src/main/java/com/magamochi/mangamochi/service/ProviderService.java +++ b/src/main/java/com/magamochi/service/ProviderService.java @@ -1,11 +1,11 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static java.util.Objects.nonNull; -import com.magamochi.mangamochi.model.dto.ProviderListDTO; -import com.magamochi.mangamochi.model.entity.Provider; -import com.magamochi.mangamochi.model.enumeration.ProviderStatus; -import com.magamochi.mangamochi.model.repository.ProviderRepository; +import com.magamochi.model.dto.ProviderListDTO; +import com.magamochi.model.entity.Provider; +import com.magamochi.model.enumeration.ProviderStatus; +import com.magamochi.model.repository.ProviderRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/mangamochi/service/S3Service.java b/src/main/java/com/magamochi/service/S3Service.java similarity index 98% rename from src/main/java/com/magamochi/mangamochi/service/S3Service.java rename to src/main/java/com/magamochi/service/S3Service.java index 1d15bd7..77eb28c 100644 --- a/src/main/java/com/magamochi/mangamochi/service/S3Service.java +++ b/src/main/java/com/magamochi/service/S3Service.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static java.util.Objects.nonNull; diff --git a/src/main/java/com/magamochi/mangamochi/service/TitleMatcherService.java b/src/main/java/com/magamochi/service/TitleMatcherService.java similarity index 92% rename from src/main/java/com/magamochi/mangamochi/service/TitleMatcherService.java rename to src/main/java/com/magamochi/service/TitleMatcherService.java index de6ab3a..fc85034 100644 --- a/src/main/java/com/magamochi/mangamochi/service/TitleMatcherService.java +++ b/src/main/java/com/magamochi/service/TitleMatcherService.java @@ -1,10 +1,10 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.springframework.util.CollectionUtils.isEmpty; -import com.magamochi.mangamochi.model.dto.TitleMatchRequestDTO; -import com.magamochi.mangamochi.model.dto.TitleMatchResponseDTO; +import com.magamochi.model.dto.TitleMatchRequestDTO; +import com.magamochi.model.dto.TitleMatchResponseDTO; import lombok.extern.log4j.Log4j2; import org.apache.commons.text.similarity.LevenshteinDistance; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/mangamochi/service/UserFavoriteMangaService.java b/src/main/java/com/magamochi/service/UserFavoriteMangaService.java similarity index 78% rename from src/main/java/com/magamochi/mangamochi/service/UserFavoriteMangaService.java rename to src/main/java/com/magamochi/service/UserFavoriteMangaService.java index e93c184..cb160d7 100644 --- a/src/main/java/com/magamochi/mangamochi/service/UserFavoriteMangaService.java +++ b/src/main/java/com/magamochi/service/UserFavoriteMangaService.java @@ -1,10 +1,10 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; -import com.magamochi.mangamochi.common.exception.NotFoundException; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.entity.UserFavoriteManga; -import com.magamochi.mangamochi.model.repository.MangaRepository; -import com.magamochi.mangamochi.model.repository.UserFavoriteMangaRepository; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.UserFavoriteManga; +import com.magamochi.model.repository.MangaRepository; +import com.magamochi.model.repository.UserFavoriteMangaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/mangamochi/service/UserService.java b/src/main/java/com/magamochi/service/UserService.java similarity index 84% rename from src/main/java/com/magamochi/mangamochi/service/UserService.java rename to src/main/java/com/magamochi/service/UserService.java index d9ad125..32ad556 100644 --- a/src/main/java/com/magamochi/mangamochi/service/UserService.java +++ b/src/main/java/com/magamochi/service/UserService.java @@ -1,17 +1,17 @@ -package com.magamochi.mangamochi.service; +package com.magamochi.service; import static java.util.Objects.isNull; -import com.magamochi.mangamochi.common.exception.ConflictException; -import com.magamochi.mangamochi.common.exception.NotFoundException; -import com.magamochi.mangamochi.model.dto.AuthenticationRequestDTO; -import com.magamochi.mangamochi.model.dto.AuthenticationResponseDTO; -import com.magamochi.mangamochi.model.dto.RefreshTokenRequestDTO; -import com.magamochi.mangamochi.model.dto.RegistrationRequestDTO; -import com.magamochi.mangamochi.model.entity.User; -import com.magamochi.mangamochi.model.enumeration.UserRole; -import com.magamochi.mangamochi.model.repository.UserRepository; -import com.magamochi.mangamochi.util.JwtUtil; +import com.magamochi.common.exception.ConflictException; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.model.dto.AuthenticationRequestDTO; +import com.magamochi.model.dto.AuthenticationResponseDTO; +import com.magamochi.model.dto.RefreshTokenRequestDTO; +import com.magamochi.model.dto.RegistrationRequestDTO; +import com.magamochi.model.entity.User; +import com.magamochi.model.enumeration.UserRole; +import com.magamochi.model.repository.UserRepository; +import com.magamochi.util.JwtUtil; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.security.authentication.AuthenticationManager; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/ContentProvider.java b/src/main/java/com/magamochi/service/providers/ContentProvider.java similarity index 55% rename from src/main/java/com/magamochi/mangamochi/service/providers/ContentProvider.java rename to src/main/java/com/magamochi/service/providers/ContentProvider.java index 9cb16cb..c30a940 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/ContentProvider.java +++ b/src/main/java/com/magamochi/service/providers/ContentProvider.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.service.providers; +package com.magamochi.service.providers; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.mangamochi.model.entity.MangaProvider; +import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; +import com.magamochi.model.entity.MangaProvider; import java.util.List; import java.util.Map; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/ContentProviderFactory.java b/src/main/java/com/magamochi/service/providers/ContentProviderFactory.java similarity index 91% rename from src/main/java/com/magamochi/mangamochi/service/providers/ContentProviderFactory.java rename to src/main/java/com/magamochi/service/providers/ContentProviderFactory.java index c3b6ef9..25dc06f 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/ContentProviderFactory.java +++ b/src/main/java/com/magamochi/service/providers/ContentProviderFactory.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.service.providers; +package com.magamochi.service.providers; import java.util.Map; import java.util.Objects; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/ContentProviders.java b/src/main/java/com/magamochi/service/providers/ContentProviders.java similarity index 86% rename from src/main/java/com/magamochi/mangamochi/service/providers/ContentProviders.java rename to src/main/java/com/magamochi/service/providers/ContentProviders.java index c7f347a..d68908b 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/ContentProviders.java +++ b/src/main/java/com/magamochi/service/providers/ContentProviders.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.service.providers; +package com.magamochi.service.providers; public class ContentProviders { public static final String MANGA_LIVRE_TO = "Manga Livre.to"; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/ManualImportContentProvider.java b/src/main/java/com/magamochi/service/providers/ManualImportContentProvider.java similarity index 62% rename from src/main/java/com/magamochi/mangamochi/service/providers/ManualImportContentProvider.java rename to src/main/java/com/magamochi/service/providers/ManualImportContentProvider.java index a96c2c7..807f98c 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/ManualImportContentProvider.java +++ b/src/main/java/com/magamochi/service/providers/ManualImportContentProvider.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.service.providers; +package com.magamochi.service.providers; public interface ManualImportContentProvider { String getMangaTitle(String value); diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/ManualImportContentProviderFactory.java b/src/main/java/com/magamochi/service/providers/ManualImportContentProviderFactory.java similarity index 92% rename from src/main/java/com/magamochi/mangamochi/service/providers/ManualImportContentProviderFactory.java rename to src/main/java/com/magamochi/service/providers/ManualImportContentProviderFactory.java index e0d4654..a60f858 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/ManualImportContentProviderFactory.java +++ b/src/main/java/com/magamochi/service/providers/ManualImportContentProviderFactory.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.service.providers; +package com.magamochi.service.providers; import java.util.Map; import java.util.Objects; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/PagedContentProvider.java b/src/main/java/com/magamochi/service/providers/PagedContentProvider.java similarity index 56% rename from src/main/java/com/magamochi/mangamochi/service/providers/PagedContentProvider.java rename to src/main/java/com/magamochi/service/providers/PagedContentProvider.java index 7cc59fe..8ff4c82 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/PagedContentProvider.java +++ b/src/main/java/com/magamochi/service/providers/PagedContentProvider.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.service.providers; +package com.magamochi.service.providers; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaInfoResponseDTO; +import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; import java.util.List; public interface PagedContentProvider { diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/PagedContentProviderFactory.java b/src/main/java/com/magamochi/service/providers/PagedContentProviderFactory.java similarity index 91% rename from src/main/java/com/magamochi/mangamochi/service/providers/PagedContentProviderFactory.java rename to src/main/java/com/magamochi/service/providers/PagedContentProviderFactory.java index aa0dfba..4909c6e 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/PagedContentProviderFactory.java +++ b/src/main/java/com/magamochi/service/providers/PagedContentProviderFactory.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.service.providers; +package com.magamochi.service.providers; import java.util.Map; import java.util.Objects; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/impl/BatoProvider.java b/src/main/java/com/magamochi/service/providers/impl/BatoProvider.java similarity index 83% rename from src/main/java/com/magamochi/mangamochi/service/providers/impl/BatoProvider.java rename to src/main/java/com/magamochi/service/providers/impl/BatoProvider.java index fbd1a9b..c8de42b 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/impl/BatoProvider.java +++ b/src/main/java/com/magamochi/service/providers/impl/BatoProvider.java @@ -1,14 +1,14 @@ -package com.magamochi.mangamochi.service.providers.impl; +package com.magamochi.service.providers.impl; import static java.util.Objects.isNull; -import com.magamochi.mangamochi.common.exception.UnprocessableException; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.mangamochi.model.entity.MangaProvider; -import com.magamochi.mangamochi.service.FlareService; -import com.magamochi.mangamochi.service.providers.ContentProvider; -import com.magamochi.mangamochi.service.providers.ContentProviders; -import com.magamochi.mangamochi.service.providers.ManualImportContentProvider; +import com.magamochi.common.exception.UnprocessableException; +import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.service.FlareService; +import com.magamochi.service.providers.ContentProvider; +import com.magamochi.service.providers.ContentProviders; +import com.magamochi.service.providers.ManualImportContentProvider; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaDexProvider.java b/src/main/java/com/magamochi/service/providers/impl/MangaDexProvider.java similarity index 89% rename from src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaDexProvider.java rename to src/main/java/com/magamochi/service/providers/impl/MangaDexProvider.java index 6948181..6f96e8a 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaDexProvider.java +++ b/src/main/java/com/magamochi/service/providers/impl/MangaDexProvider.java @@ -1,15 +1,15 @@ -package com.magamochi.mangamochi.service.providers.impl; +package com.magamochi.service.providers.impl; import static java.util.Objects.isNull; import com.google.common.util.concurrent.RateLimiter; -import com.magamochi.mangamochi.client.MangaDexClient; -import com.magamochi.mangamochi.common.exception.UnprocessableException; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.mangamochi.model.entity.MangaProvider; -import com.magamochi.mangamochi.service.providers.ContentProvider; -import com.magamochi.mangamochi.service.providers.ContentProviders; -import com.magamochi.mangamochi.service.providers.ManualImportContentProvider; +import com.magamochi.client.MangaDexClient; +import com.magamochi.common.exception.UnprocessableException; +import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.service.providers.ContentProvider; +import com.magamochi.service.providers.ContentProviders; +import com.magamochi.service.providers.ManualImportContentProvider; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaLivreBlogProvider.java b/src/main/java/com/magamochi/service/providers/impl/MangaLivreBlogProvider.java similarity index 91% rename from src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaLivreBlogProvider.java rename to src/main/java/com/magamochi/service/providers/impl/MangaLivreBlogProvider.java index 648edb4..02173cf 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaLivreBlogProvider.java +++ b/src/main/java/com/magamochi/service/providers/impl/MangaLivreBlogProvider.java @@ -1,12 +1,12 @@ -package com.magamochi.mangamochi.service.providers.impl; +package com.magamochi.service.providers.impl; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaInfoResponseDTO; -import com.magamochi.mangamochi.model.entity.MangaProvider; -import com.magamochi.mangamochi.model.enumeration.MangaStatus; -import com.magamochi.mangamochi.service.providers.ContentProvider; -import com.magamochi.mangamochi.service.providers.ContentProviders; -import com.magamochi.mangamochi.service.providers.PagedContentProvider; +import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; +import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.enumeration.MangaStatus; +import com.magamochi.service.providers.ContentProvider; +import com.magamochi.service.providers.ContentProviders; +import com.magamochi.service.providers.PagedContentProvider; import java.io.IOException; import java.util.*; import java.util.regex.Pattern; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaLivreProvider.java b/src/main/java/com/magamochi/service/providers/impl/MangaLivreProvider.java similarity index 88% rename from src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaLivreProvider.java rename to src/main/java/com/magamochi/service/providers/impl/MangaLivreProvider.java index d133ac5..5d237ee 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/impl/MangaLivreProvider.java +++ b/src/main/java/com/magamochi/service/providers/impl/MangaLivreProvider.java @@ -1,15 +1,15 @@ -package com.magamochi.mangamochi.service.providers.impl; +package com.magamochi.service.providers.impl; import static java.util.Objects.nonNull; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaInfoResponseDTO; -import com.magamochi.mangamochi.model.entity.MangaProvider; -import com.magamochi.mangamochi.model.enumeration.MangaStatus; -import com.magamochi.mangamochi.service.FlareService; -import com.magamochi.mangamochi.service.providers.ContentProvider; -import com.magamochi.mangamochi.service.providers.ContentProviders; -import com.magamochi.mangamochi.service.providers.PagedContentProvider; +import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; +import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.enumeration.MangaStatus; +import com.magamochi.service.FlareService; +import com.magamochi.service.providers.ContentProvider; +import com.magamochi.service.providers.ContentProviders; +import com.magamochi.service.providers.PagedContentProvider; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; diff --git a/src/main/java/com/magamochi/mangamochi/service/providers/impl/PinkRosaScanProvider.java b/src/main/java/com/magamochi/service/providers/impl/PinkRosaScanProvider.java similarity index 89% rename from src/main/java/com/magamochi/mangamochi/service/providers/impl/PinkRosaScanProvider.java rename to src/main/java/com/magamochi/service/providers/impl/PinkRosaScanProvider.java index 6e30086..c02e6d3 100644 --- a/src/main/java/com/magamochi/mangamochi/service/providers/impl/PinkRosaScanProvider.java +++ b/src/main/java/com/magamochi/service/providers/impl/PinkRosaScanProvider.java @@ -1,16 +1,16 @@ -package com.magamochi.mangamochi.service.providers.impl; +package com.magamochi.service.providers.impl; import static java.util.Objects.isNull; import static org.apache.commons.lang3.StringUtils.isBlank; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.mangamochi.model.dto.ContentProviderMangaInfoResponseDTO; -import com.magamochi.mangamochi.model.entity.MangaProvider; -import com.magamochi.mangamochi.model.enumeration.MangaStatus; -import com.magamochi.mangamochi.service.FlareService; -import com.magamochi.mangamochi.service.providers.ContentProvider; -import com.magamochi.mangamochi.service.providers.ContentProviders; -import com.magamochi.mangamochi.service.providers.PagedContentProvider; +import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; +import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; +import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.enumeration.MangaStatus; +import com.magamochi.service.FlareService; +import com.magamochi.service.providers.ContentProvider; +import com.magamochi.service.providers.ContentProviders; +import com.magamochi.service.providers.PagedContentProvider; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; diff --git a/src/main/java/com/magamochi/mangamochi/task/FlareSessionCleanupTask.java b/src/main/java/com/magamochi/task/FlareSessionCleanupTask.java similarity index 85% rename from src/main/java/com/magamochi/mangamochi/task/FlareSessionCleanupTask.java rename to src/main/java/com/magamochi/task/FlareSessionCleanupTask.java index bf0efc9..0b957b1 100644 --- a/src/main/java/com/magamochi/mangamochi/task/FlareSessionCleanupTask.java +++ b/src/main/java/com/magamochi/task/FlareSessionCleanupTask.java @@ -1,7 +1,7 @@ -package com.magamochi.mangamochi.task; +package com.magamochi.task; -import com.magamochi.mangamochi.client.FlareClient; -import com.magamochi.mangamochi.registry.FlareSessionRegistry; +import com.magamochi.client.FlareClient; +import com.magamochi.registry.FlareSessionRegistry; import java.time.Duration; import java.time.Instant; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/mangamochi/task/FlareStartupCleanupTask.java b/src/main/java/com/magamochi/task/FlareStartupCleanupTask.java similarity index 89% rename from src/main/java/com/magamochi/mangamochi/task/FlareStartupCleanupTask.java rename to src/main/java/com/magamochi/task/FlareStartupCleanupTask.java index 5ceb54d..637a993 100644 --- a/src/main/java/com/magamochi/mangamochi/task/FlareStartupCleanupTask.java +++ b/src/main/java/com/magamochi/task/FlareStartupCleanupTask.java @@ -1,6 +1,6 @@ -package com.magamochi.mangamochi.task; +package com.magamochi.task; -import com.magamochi.mangamochi.client.FlareClient; +import com.magamochi.client.FlareClient; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.boot.context.event.ApplicationReadyEvent; diff --git a/src/main/java/com/magamochi/mangamochi/task/ImageCleanupTask.java b/src/main/java/com/magamochi/task/ImageCleanupTask.java similarity index 87% rename from src/main/java/com/magamochi/mangamochi/task/ImageCleanupTask.java rename to src/main/java/com/magamochi/task/ImageCleanupTask.java index f9756be..6bbac36 100644 --- a/src/main/java/com/magamochi/mangamochi/task/ImageCleanupTask.java +++ b/src/main/java/com/magamochi/task/ImageCleanupTask.java @@ -1,8 +1,8 @@ -package com.magamochi.mangamochi.task; +package com.magamochi.task; -import com.magamochi.mangamochi.model.entity.Image; -import com.magamochi.mangamochi.model.repository.*; -import com.magamochi.mangamochi.service.S3Service; +import com.magamochi.model.entity.Image; +import com.magamochi.model.repository.ImageRepository; +import com.magamochi.service.S3Service; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/magamochi/mangamochi/task/MangaFollowUpdateTask.java b/src/main/java/com/magamochi/task/MangaFollowUpdateTask.java similarity index 80% rename from src/main/java/com/magamochi/mangamochi/task/MangaFollowUpdateTask.java rename to src/main/java/com/magamochi/task/MangaFollowUpdateTask.java index 8e67714..6979d6d 100644 --- a/src/main/java/com/magamochi/mangamochi/task/MangaFollowUpdateTask.java +++ b/src/main/java/com/magamochi/task/MangaFollowUpdateTask.java @@ -1,10 +1,10 @@ -package com.magamochi.mangamochi.task; +package com.magamochi.task; -import com.magamochi.mangamochi.model.dto.UpdateMangaFollowChapterListCommand; -import com.magamochi.mangamochi.model.entity.Manga; -import com.magamochi.mangamochi.model.enumeration.ProviderStatus; -import com.magamochi.mangamochi.model.repository.MangaRepository; -import com.magamochi.mangamochi.queue.UpdateMangaFollowChapterListProducer; +import com.magamochi.model.dto.UpdateMangaFollowChapterListCommand; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.enumeration.ProviderStatus; +import com.magamochi.model.repository.MangaRepository; +import com.magamochi.queue.UpdateMangaFollowChapterListProducer; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/magamochi/mangamochi/task/UpdateMangaDataTask.java b/src/main/java/com/magamochi/task/UpdateMangaDataTask.java similarity index 77% rename from src/main/java/com/magamochi/mangamochi/task/UpdateMangaDataTask.java rename to src/main/java/com/magamochi/task/UpdateMangaDataTask.java index f0988b2..ff67b48 100644 --- a/src/main/java/com/magamochi/mangamochi/task/UpdateMangaDataTask.java +++ b/src/main/java/com/magamochi/task/UpdateMangaDataTask.java @@ -1,10 +1,10 @@ -package com.magamochi.mangamochi.task; +package com.magamochi.task; import static java.util.Objects.isNull; -import com.magamochi.mangamochi.model.dto.UpdateMangaDataCommand; -import com.magamochi.mangamochi.model.repository.*; -import com.magamochi.mangamochi.queue.UpdateMangaDataProducer; +import com.magamochi.model.dto.UpdateMangaDataCommand; +import com.magamochi.model.repository.MangaRepository; +import com.magamochi.queue.UpdateMangaDataProducer; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.scheduling.annotation.Scheduled; diff --git a/src/main/java/com/magamochi/mangamochi/task/UpdateMangaListTask.java b/src/main/java/com/magamochi/task/UpdateMangaListTask.java similarity index 81% rename from src/main/java/com/magamochi/mangamochi/task/UpdateMangaListTask.java rename to src/main/java/com/magamochi/task/UpdateMangaListTask.java index 315060a..54395dd 100644 --- a/src/main/java/com/magamochi/mangamochi/task/UpdateMangaListTask.java +++ b/src/main/java/com/magamochi/task/UpdateMangaListTask.java @@ -1,11 +1,11 @@ -package com.magamochi.mangamochi.task; +package com.magamochi.task; -import com.magamochi.mangamochi.common.exception.NotFoundException; -import com.magamochi.mangamochi.model.dto.MangaListUpdateCommand; -import com.magamochi.mangamochi.model.repository.ProviderRepository; -import com.magamochi.mangamochi.queue.UpdateMangaListProducer; -import com.magamochi.mangamochi.service.providers.PagedContentProvider; -import com.magamochi.mangamochi.service.providers.PagedContentProviderFactory; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.model.dto.MangaListUpdateCommand; +import com.magamochi.model.repository.ProviderRepository; +import com.magamochi.queue.UpdateMangaListProducer; +import com.magamochi.service.providers.PagedContentProvider; +import com.magamochi.service.providers.PagedContentProviderFactory; import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/magamochi/mangamochi/util/DoubleUtil.java b/src/main/java/com/magamochi/util/DoubleUtil.java similarity index 91% rename from src/main/java/com/magamochi/mangamochi/util/DoubleUtil.java rename to src/main/java/com/magamochi/util/DoubleUtil.java index a809d25..5452721 100644 --- a/src/main/java/com/magamochi/mangamochi/util/DoubleUtil.java +++ b/src/main/java/com/magamochi/util/DoubleUtil.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.util; +package com.magamochi.util; import java.math.BigDecimal; import java.math.RoundingMode; diff --git a/src/main/java/com/magamochi/mangamochi/util/JwtUtil.java b/src/main/java/com/magamochi/util/JwtUtil.java similarity index 98% rename from src/main/java/com/magamochi/mangamochi/util/JwtUtil.java rename to src/main/java/com/magamochi/util/JwtUtil.java index 4fc03b9..1b9964c 100644 --- a/src/main/java/com/magamochi/mangamochi/util/JwtUtil.java +++ b/src/main/java/com/magamochi/util/JwtUtil.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi.util; +package com.magamochi.util; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; diff --git a/src/test/java/com/magamochi/mangamochi/MangamochiApplicationTests.java b/src/test/java/com/magamochi/MangamochiApplicationTests.java similarity index 86% rename from src/test/java/com/magamochi/mangamochi/MangamochiApplicationTests.java rename to src/test/java/com/magamochi/MangamochiApplicationTests.java index 3846a8d..4ac1bfd 100644 --- a/src/test/java/com/magamochi/mangamochi/MangamochiApplicationTests.java +++ b/src/test/java/com/magamochi/MangamochiApplicationTests.java @@ -1,4 +1,4 @@ -package com.magamochi.mangamochi; +package com.magamochi; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; -- 2.49.1 From 3ecd38ef2c723ac072cf56277f68ddd2c3107207 Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Tue, 17 Mar 2026 19:42:17 -0300 Subject: [PATCH 03/11] chore(database): condensate all migrations into one --- .../db/migration/V0001__CREATE_TABLES.sql | 163 ++++++++++++++++++ .../db/migration/V0001__IMAGES_TABLE.sql | 7 - .../resources/db/migration/V0002__MANGA.sql | 11 -- .../db/migration/V0003__PROVIDER.sql | 21 --- .../resources/db/migration/V0004__CHAPTER.sql | 20 --- .../migration/V0005__MANGA_IMPORT_REVIEW.sql | 8 - .../db/migration/V0006__MANGA_DATA.sql | 5 - .../db/migration/V0007__MANGA_DATA.sql | 31 ---- .../db/migration/V0008__MANGA_CHAPTERS.sql | 1 - .../db/migration/V0009__MANGA_CHAPTERS.sql | 1 - .../resources/db/migration/V0010__USERS.sql | 7 - .../migration/V0011__USER_FAVORITE_MANGA.sql | 8 - .../db/migration/V0012__MANGA_CHAPTERS.sql | 2 - .../V0013__MANGA_CHAPTER_LANGUAGE.sql | 3 - .../V0014__MANGA_ALTERNATIVE_TITLES_TABLE.sql | 18 -- .../migration/V0015__PROVIDER_FETCH_FLAG.sql | 2 - .../db/migration/V0016__MANGA_FOLLOW.sql | 9 - .../migration/V0017__MANGA_LIVRE_CLEANUP.sql | 44 ----- .../migration/V0018__MANGA_PROVIDER_FLAG.sql | 6 - .../db/migration/V0019__CHAPTER_LANGUAGE.sql | 40 ----- .../db/migration/V0020__ADD_ANILIST_ID.sql | 1 - 21 files changed, 163 insertions(+), 245 deletions(-) create mode 100644 src/main/resources/db/migration/V0001__CREATE_TABLES.sql delete mode 100644 src/main/resources/db/migration/V0001__IMAGES_TABLE.sql delete mode 100644 src/main/resources/db/migration/V0002__MANGA.sql delete mode 100644 src/main/resources/db/migration/V0003__PROVIDER.sql delete mode 100644 src/main/resources/db/migration/V0004__CHAPTER.sql delete mode 100644 src/main/resources/db/migration/V0005__MANGA_IMPORT_REVIEW.sql delete mode 100644 src/main/resources/db/migration/V0006__MANGA_DATA.sql delete mode 100644 src/main/resources/db/migration/V0007__MANGA_DATA.sql delete mode 100644 src/main/resources/db/migration/V0008__MANGA_CHAPTERS.sql delete mode 100644 src/main/resources/db/migration/V0009__MANGA_CHAPTERS.sql delete mode 100644 src/main/resources/db/migration/V0010__USERS.sql delete mode 100644 src/main/resources/db/migration/V0011__USER_FAVORITE_MANGA.sql delete mode 100644 src/main/resources/db/migration/V0012__MANGA_CHAPTERS.sql delete mode 100644 src/main/resources/db/migration/V0013__MANGA_CHAPTER_LANGUAGE.sql delete mode 100644 src/main/resources/db/migration/V0014__MANGA_ALTERNATIVE_TITLES_TABLE.sql delete mode 100644 src/main/resources/db/migration/V0015__PROVIDER_FETCH_FLAG.sql delete mode 100644 src/main/resources/db/migration/V0016__MANGA_FOLLOW.sql delete mode 100644 src/main/resources/db/migration/V0017__MANGA_LIVRE_CLEANUP.sql delete mode 100644 src/main/resources/db/migration/V0018__MANGA_PROVIDER_FLAG.sql delete mode 100644 src/main/resources/db/migration/V0019__CHAPTER_LANGUAGE.sql delete mode 100644 src/main/resources/db/migration/V0020__ADD_ANILIST_ID.sql diff --git a/src/main/resources/db/migration/V0001__CREATE_TABLES.sql b/src/main/resources/db/migration/V0001__CREATE_TABLES.sql new file mode 100644 index 0000000..8f7aa62 --- /dev/null +++ b/src/main/resources/db/migration/V0001__CREATE_TABLES.sql @@ -0,0 +1,163 @@ +CREATE TABLE IF NOT EXISTS languages +( + id SERIAL PRIMARY KEY, + code VARCHAR(12) NOT NULL UNIQUE, + name VARCHAR(100) NOT NULL +); + +INSERT INTO languages (code, name) +VALUES ('en-US', 'English'), + ('es', 'Spanish'), + ('ja-JP', 'Japanese'), + ('pt-BR', 'Portuguese (Brazil)') +ON CONFLICT DO NOTHING; + +CREATE TABLE images +( + id UUID NOT NULL PRIMARY KEY, + file_key VARCHAR NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE mangas +( + id BIGSERIAL NOT NULL PRIMARY KEY, + ani_list_id BIGINT UNIQUE, + mal_id BIGINT UNIQUE, + title VARCHAR, + status VARCHAR, + synopsis VARCHAR, + cover_image_id UUID REFERENCES images (id), + score DOUBLE PRECISION, + published_from TIMESTAMPTZ, + published_to TIMESTAMPTZ, + chapter_count INT DEFAULT 0, + follow BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE content_providers +( + id BIGSERIAL NOT NULL PRIMARY KEY, + name VARCHAR NOT NULL, + active BOOLEAN NOT NULL DEFAULT TRUE, + supports_chapter_fetch BOOLEAN NOT NULL DEFAULT TRUE, + manual_import BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE manga_content_provider +( + id BIGSERIAL NOT NULL PRIMARY KEY, + manga_id BIGINT NOT NULL REFERENCES mangas (id) ON DELETE CASCADE, + content_provider_id BIGINT NOT NULL REFERENCES content_providers (id) ON DELETE CASCADE, + manga_title VARCHAR NOT NULL, + url VARCHAR NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE (manga_id, content_provider_id) +); + +CREATE TABLE manga_chapters +( + id BIGSERIAL NOT NULL PRIMARY KEY, + manga_content_provider_id BIGINT NOT NULL REFERENCES manga_content_provider (id) ON DELETE CASCADE, + title VARCHAR NOT NULL, + url VARCHAR NOT NULL, + chapter_number INTEGER, + language_id BIGINT REFERENCES languages (id), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE manga_chapter_images +( + id BIGSERIAL NOT NULL PRIMARY KEY, + manga_chapter_id BIGINT NOT NULL REFERENCES manga_chapters (id) ON DELETE CASCADE, + image_id UUID REFERENCES images (id) ON DELETE CASCADE, + position INT NOT NULL, + downloaded BOOLEAN DEFAULT FALSE, + read BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE manga_import_reviews +( + id BIGSERIAL NOT NULL PRIMARY KEY, + content_provider_id BIGINT NOT NULL REFERENCES content_providers (id) ON DELETE CASCADE, + title VARCHAR NOT NULL, + url VARCHAR NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE authors +( + id BIGSERIAL NOT NULL PRIMARY KEY, + mal_id BIGINT UNIQUE, + name VARCHAR, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE manga_author +( + id BIGSERIAL NOT NULL PRIMARY KEY, + manga_id BIGINT REFERENCES mangas (id), + author_id BIGINT REFERENCES authors (id), + UNIQUE (manga_id, author_id) +); + +CREATE TABLE genres +( + id BIGSERIAL NOT NULL PRIMARY KEY, + mal_id BIGINT UNIQUE, + name VARCHAR +); + +CREATE TABLE manga_genre +( + id BIGSERIAL NOT NULL PRIMARY KEY, + manga_id BIGINT REFERENCES mangas (id), + genre_id BIGINT REFERENCES genres (id), + UNIQUE (manga_id, genre_id) +); + +CREATE TABLE users +( + id SERIAL PRIMARY KEY, + email VARCHAR NOT NULL UNIQUE, + name VARCHAR NOT NULL, + password VARCHAR NOT NULL, + role VARCHAR +); + +CREATE TABLE user_favorite_mangas +( + id BIGSERIAL PRIMARY KEY, + user_id BIGINT NOT NULL, + manga_id BIGINT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE (user_id, manga_id) +); + +CREATE TABLE manga_alternative_titles +( + id BIGSERIAL PRIMARY KEY, + manga_id BIGINT REFERENCES mangas (id), + title VARCHAR NOT NULL +); + +CREATE INDEX idx_manga_alternative_titles_manga_id ON manga_alternative_titles (manga_id); + +CREATE INDEX idx_manga_alternative_titles_title ON manga_alternative_titles (title); + +CREATE TABLE user_manga_follow +( + id BIGSERIAL NOT NULL PRIMARY KEY, + user_id BIGINT REFERENCES users (id), + manga_id BIGINT REFERENCES mangas (id) +); diff --git a/src/main/resources/db/migration/V0001__IMAGES_TABLE.sql b/src/main/resources/db/migration/V0001__IMAGES_TABLE.sql deleted file mode 100644 index f501dbf..0000000 --- a/src/main/resources/db/migration/V0001__IMAGES_TABLE.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE images -( - id UUID NOT NULL PRIMARY KEY, - file_key VARCHAR NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); \ No newline at end of file diff --git a/src/main/resources/db/migration/V0002__MANGA.sql b/src/main/resources/db/migration/V0002__MANGA.sql deleted file mode 100644 index ba54fe3..0000000 --- a/src/main/resources/db/migration/V0002__MANGA.sql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE mangas -( - id BIGSERIAL NOT NULL PRIMARY KEY, - mal_id BIGINT UNIQUE, - title VARCHAR, - alternative_titles TEXT[], - status VARCHAR, - synopsis VARCHAR, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); diff --git a/src/main/resources/db/migration/V0003__PROVIDER.sql b/src/main/resources/db/migration/V0003__PROVIDER.sql deleted file mode 100644 index 95add80..0000000 --- a/src/main/resources/db/migration/V0003__PROVIDER.sql +++ /dev/null @@ -1,21 +0,0 @@ -CREATE TABLE providers -( - id BIGSERIAL NOT NULL PRIMARY KEY, - name VARCHAR NOT NULL, - status VARCHAR NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE manga_provider -( - id BIGSERIAL NOT NULL PRIMARY KEY, - manga_id BIGINT NOT NULL REFERENCES mangas (id) ON DELETE CASCADE, - provider_id BIGINT NOT NULL REFERENCES providers (id) ON DELETE CASCADE, - manga_title VARCHAR NOT NULL, - url VARCHAR NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE (manga_id, provider_id) -) - diff --git a/src/main/resources/db/migration/V0004__CHAPTER.sql b/src/main/resources/db/migration/V0004__CHAPTER.sql deleted file mode 100644 index b809161..0000000 --- a/src/main/resources/db/migration/V0004__CHAPTER.sql +++ /dev/null @@ -1,20 +0,0 @@ -CREATE TABLE manga_chapters -( - id BIGSERIAL NOT NULL PRIMARY KEY, - manga_provider_id BIGINT NOT NULL REFERENCES manga_provider (id) ON DELETE CASCADE, - title VARCHAR NOT NULL, - url VARCHAR NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE manga_chapter_images -( - id BIGSERIAL NOT NULL PRIMARY KEY, - manga_chapter_id BIGINT NOT NULL REFERENCES manga_chapters (id) ON DELETE CASCADE, - image_id UUID REFERENCES images (id) ON DELETE CASCADE, - position INT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -) - diff --git a/src/main/resources/db/migration/V0005__MANGA_IMPORT_REVIEW.sql b/src/main/resources/db/migration/V0005__MANGA_IMPORT_REVIEW.sql deleted file mode 100644 index a470787..0000000 --- a/src/main/resources/db/migration/V0005__MANGA_IMPORT_REVIEW.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE manga_import_reviews -( - id BIGSERIAL NOT NULL PRIMARY KEY, - provider_id BIGINT NOT NULL REFERENCES providers (id) ON DELETE CASCADE, - title VARCHAR NOT NULL, - url VARCHAR NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); diff --git a/src/main/resources/db/migration/V0006__MANGA_DATA.sql b/src/main/resources/db/migration/V0006__MANGA_DATA.sql deleted file mode 100644 index 07855df..0000000 --- a/src/main/resources/db/migration/V0006__MANGA_DATA.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE mangas - ADD COLUMN cover_image_id UUID REFERENCES images (id), - ADD COLUMN score DOUBLE PRECISION, - ADD COLUMN published_from TIMESTAMPTZ, - ADD COLUMN published_to TIMESTAMPTZ; \ No newline at end of file diff --git a/src/main/resources/db/migration/V0007__MANGA_DATA.sql b/src/main/resources/db/migration/V0007__MANGA_DATA.sql deleted file mode 100644 index 423884e..0000000 --- a/src/main/resources/db/migration/V0007__MANGA_DATA.sql +++ /dev/null @@ -1,31 +0,0 @@ -CREATE TABLE authors -( - id BIGSERIAL NOT NULL PRIMARY KEY, - mal_id BIGINT UNIQUE, - name VARCHAR, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE manga_author -( - id BIGSERIAL NOT NULL PRIMARY KEY, - manga_id BIGINT REFERENCES mangas (id), - author_id BIGINT REFERENCES authors (id), - UNIQUE (manga_id, author_id) -); - -CREATE TABLE genres -( - id BIGSERIAL NOT NULL PRIMARY KEY, - mal_id BIGINT UNIQUE, - name VARCHAR -); - -CREATE TABLE manga_genre -( - id BIGSERIAL NOT NULL PRIMARY KEY, - manga_id BIGINT REFERENCES mangas (id), - genre_id BIGINT REFERENCES genres (id), - UNIQUE (manga_id, genre_id) -); \ No newline at end of file diff --git a/src/main/resources/db/migration/V0008__MANGA_CHAPTERS.sql b/src/main/resources/db/migration/V0008__MANGA_CHAPTERS.sql deleted file mode 100644 index 38a5d65..0000000 --- a/src/main/resources/db/migration/V0008__MANGA_CHAPTERS.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE manga_chapters ADD COLUMN downloaded BOOLEAN DEFAULT FALSE; \ No newline at end of file diff --git a/src/main/resources/db/migration/V0009__MANGA_CHAPTERS.sql b/src/main/resources/db/migration/V0009__MANGA_CHAPTERS.sql deleted file mode 100644 index 919388c..0000000 --- a/src/main/resources/db/migration/V0009__MANGA_CHAPTERS.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE manga_chapters ADD COLUMN read BOOLEAN DEFAULT FALSE; \ No newline at end of file diff --git a/src/main/resources/db/migration/V0010__USERS.sql b/src/main/resources/db/migration/V0010__USERS.sql deleted file mode 100644 index ccb4ab3..0000000 --- a/src/main/resources/db/migration/V0010__USERS.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE users ( - id SERIAL PRIMARY KEY, - email VARCHAR NOT NULL UNIQUE, - name VARCHAR NOT NULL, - password VARCHAR NOT NULL, - role VARCHAR -); \ No newline at end of file diff --git a/src/main/resources/db/migration/V0011__USER_FAVORITE_MANGA.sql b/src/main/resources/db/migration/V0011__USER_FAVORITE_MANGA.sql deleted file mode 100644 index 8309f7c..0000000 --- a/src/main/resources/db/migration/V0011__USER_FAVORITE_MANGA.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE user_favorite_mangas -( - id BIGSERIAL PRIMARY KEY, - user_id BIGINT NOT NULL, - manga_id BIGINT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE (user_id, manga_id) -); \ No newline at end of file diff --git a/src/main/resources/db/migration/V0012__MANGA_CHAPTERS.sql b/src/main/resources/db/migration/V0012__MANGA_CHAPTERS.sql deleted file mode 100644 index 43685c2..0000000 --- a/src/main/resources/db/migration/V0012__MANGA_CHAPTERS.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE mangas - ADD COLUMN chapter_count INT DEFAULT 0; \ No newline at end of file diff --git a/src/main/resources/db/migration/V0013__MANGA_CHAPTER_LANGUAGE.sql b/src/main/resources/db/migration/V0013__MANGA_CHAPTER_LANGUAGE.sql deleted file mode 100644 index f7ac5df..0000000 --- a/src/main/resources/db/migration/V0013__MANGA_CHAPTER_LANGUAGE.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE manga_chapters - ADD COLUMN language VARCHAR(10) DEFAULT 'pt-br', - ADD COLUMN chapter_number INTEGER; \ No newline at end of file diff --git a/src/main/resources/db/migration/V0014__MANGA_ALTERNATIVE_TITLES_TABLE.sql b/src/main/resources/db/migration/V0014__MANGA_ALTERNATIVE_TITLES_TABLE.sql deleted file mode 100644 index 0efe1b5..0000000 --- a/src/main/resources/db/migration/V0014__MANGA_ALTERNATIVE_TITLES_TABLE.sql +++ /dev/null @@ -1,18 +0,0 @@ -CREATE TABLE manga_alternative_titles -( - id BIGSERIAL PRIMARY KEY, - manga_id BIGINT REFERENCES mangas (id), - title VARCHAR NOT NULL -); - -INSERT INTO manga_alternative_titles (manga_id, title) -SELECT id as manga_id, - unnest(alternative_titles) as title -FROM mangas -WHERE alternative_titles IS NOT NULL; - -CREATE INDEX idx_manga_alternative_titles_manga_id ON manga_alternative_titles (manga_id); - -CREATE INDEX idx_manga_alternative_titles_title ON manga_alternative_titles (title); - -ALTER TABLE mangas DROP COLUMN alternative_titles; \ No newline at end of file diff --git a/src/main/resources/db/migration/V0015__PROVIDER_FETCH_FLAG.sql b/src/main/resources/db/migration/V0015__PROVIDER_FETCH_FLAG.sql deleted file mode 100644 index 778fbac..0000000 --- a/src/main/resources/db/migration/V0015__PROVIDER_FETCH_FLAG.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE providers - ADD COLUMN supports_chapter_fetch BOOLEAN NOT NULL DEFAULT true; \ No newline at end of file diff --git a/src/main/resources/db/migration/V0016__MANGA_FOLLOW.sql b/src/main/resources/db/migration/V0016__MANGA_FOLLOW.sql deleted file mode 100644 index efbe73d..0000000 --- a/src/main/resources/db/migration/V0016__MANGA_FOLLOW.sql +++ /dev/null @@ -1,9 +0,0 @@ -ALTER TABLE mangas - ADD COLUMN follow BOOLEAN NOT NULL DEFAULT FALSE; - -CREATE TABLE user_manga_follow -( - id BIGSERIAL NOT NULL PRIMARY KEY, - user_id BIGINT REFERENCES users (id), - manga_id BIGINT REFERENCES mangas (id) -); \ No newline at end of file diff --git a/src/main/resources/db/migration/V0017__MANGA_LIVRE_CLEANUP.sql b/src/main/resources/db/migration/V0017__MANGA_LIVRE_CLEANUP.sql deleted file mode 100644 index 4442610..0000000 --- a/src/main/resources/db/migration/V0017__MANGA_LIVRE_CLEANUP.sql +++ /dev/null @@ -1,44 +0,0 @@ -DO -$$ - DECLARE - _target_provider_name TEXT := 'Manga Livre'; - _target_provider_id BIGINT; - _deleted_chapters_count INTEGER; - _deleted_manga_providers_count INTEGER; - - BEGIN - SELECT id - INTO _target_provider_id - FROM providers - WHERE name = _target_provider_name; - - IF _target_provider_id IS NULL THEN - RAISE NOTICE 'Provider with name "%" not found.', _target_provider_name; - RETURN; - END IF; - - UPDATE providers SET status = 'INACTIVE' WHERE id = _target_provider_id; - - -- Delete non-downloaded manga chapters associated with the target provider - DELETE - FROM manga_chapters mc - USING manga_provider mp - WHERE mc.manga_provider_id = mp.id - AND mp.provider_id = _target_provider_id - AND mc.downloaded = FALSE; - - GET DIAGNOSTICS _deleted_chapters_count = ROW_COUNT; - RAISE NOTICE 'Deleted % non-downloaded chapters for provider ID %.', _deleted_chapters_count, _target_provider_id; - - -- Delete MangaProvider records ONLY if NO chapters - DELETE - FROM manga_provider mp - WHERE mp.provider_id = _target_provider_id - AND NOT EXISTS (SELECT 1 - FROM manga_chapters mc - WHERE mc.manga_provider_id = mp.id); - - GET DIAGNOSTICS _deleted_manga_providers_count = ROW_COUNT; - RAISE NOTICE 'Deleted % manga_provider entries', _deleted_manga_providers_count; - END -$$; \ No newline at end of file diff --git a/src/main/resources/db/migration/V0018__MANGA_PROVIDER_FLAG.sql b/src/main/resources/db/migration/V0018__MANGA_PROVIDER_FLAG.sql deleted file mode 100644 index a450c21..0000000 --- a/src/main/resources/db/migration/V0018__MANGA_PROVIDER_FLAG.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE providers - ADD COLUMN manual_import BOOLEAN DEFAULT FALSE; - -UPDATE providers -SET manual_import = TRUE -WHERE name IN ('MangaDex', 'Bato'); \ No newline at end of file diff --git a/src/main/resources/db/migration/V0019__CHAPTER_LANGUAGE.sql b/src/main/resources/db/migration/V0019__CHAPTER_LANGUAGE.sql deleted file mode 100644 index 225e023..0000000 --- a/src/main/resources/db/migration/V0019__CHAPTER_LANGUAGE.sql +++ /dev/null @@ -1,40 +0,0 @@ -CREATE TABLE IF NOT EXISTS languages -( - id SERIAL PRIMARY KEY, - code VARCHAR(12) NOT NULL UNIQUE, - name VARCHAR(100) NOT NULL -); - -INSERT INTO languages (code, name) -VALUES ('en-US', 'English'), - ('es', 'Spanish'), - ('ja-JP', 'Japanese'), - ('pt-BR', 'Portuguese (Brazil)') -ON CONFLICT DO NOTHING; - -ALTER TABLE manga_chapters - ADD COLUMN IF NOT EXISTS language_id BIGINT REFERENCES languages (id); - -UPDATE manga_chapters -SET language = NULL; - -UPDATE manga_chapters mc -SET language_id = (SELECT id FROM languages WHERE code = 'pt-BR') -FROM manga_provider mp - JOIN providers p ON mp.provider_id = p.id -WHERE mc.manga_provider_id = mp.id - AND mc.language IS NULL - AND p.name ILIKE ANY - (ARRAY ['Manga Livre Blog', 'Pink Rosa Scan', 'Manga Livre.to', 'Manga Livre', 'MangaDex', 'Bato', 'Taimu']); - -UPDATE manga_chapters mc -SET language_id = (SELECT id FROM languages WHERE code = 'en-US') -FROM manga_provider mp - JOIN providers p ON mp.provider_id = p.id -WHERE mc.manga_provider_id = mp.id - AND mc.language IS NULL - AND p.name ILIKE ANY - (ARRAY ['Manual Import']); - -ALTER TABLE manga_chapters - DROP COLUMN IF EXISTS language; \ No newline at end of file diff --git a/src/main/resources/db/migration/V0020__ADD_ANILIST_ID.sql b/src/main/resources/db/migration/V0020__ADD_ANILIST_ID.sql deleted file mode 100644 index 0b9a4f7..0000000 --- a/src/main/resources/db/migration/V0020__ADD_ANILIST_ID.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE mangas ADD COLUMN ani_list_id BIGINT; -- 2.49.1 From d845dc4d123c627e6b8048af071e05aef5a88096 Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Tue, 17 Mar 2026 19:53:38 -0300 Subject: [PATCH 04/11] refactor: rename Provider to ContentProvider and update related references --- .../controller/ProviderController.java | 26 --------------- .../controller/IngestionController.java | 29 +++++++++++++++++ .../model/dto/ContentProviderListDTO.java | 19 +++++++++++ .../model/entity/ContentProvider.java} | 23 +++++++------ .../model/entity/FlareSession.java | 2 +- .../repository/ContentProviderRepository.java | 9 ++++++ .../service/ContentProviderService.java | 25 +++++++++++++++ .../{ => ingestion}/service/FlareService.java | 2 +- .../service/FlareSessionManager.java | 4 +-- .../task/FlareSessionCleanupTask.java | 2 +- .../task/FlareStartupCleanupTask.java | 2 +- .../magamochi/model/dto/ImportReviewDTO.java | 2 +- .../com/magamochi/model/dto/MangaDTO.java | 23 +++++++------ .../com/magamochi/model/dto/MangaListDTO.java | 2 +- .../magamochi/model/dto/ProviderListDTO.java | 17 ---------- .../com/magamochi/model/entity/Manga.java | 2 +- .../magamochi/model/entity/MangaChapter.java | 4 +-- ...rovider.java => MangaContentProvider.java} | 11 ++++--- .../model/entity/MangaImportReview.java | 5 +-- .../model/enumeration/ProviderStatus.java | 6 ---- .../repository/MangaChapterRepository.java | 8 ++--- .../MangaContentProviderRepository.java | 15 +++++++++ .../repository/MangaProviderRepository.java | 14 -------- .../model/repository/ProviderRepository.java | 9 ------ .../registry/FlareSessionRegistry.java | 2 +- .../service/MangaChapterService.java | 9 +++--- .../service/MangaCreationService.java | 14 ++++---- .../service/MangaImportReviewService.java | 16 +++++----- .../magamochi/service/MangaImportService.java | 26 ++++++++------- .../magamochi/service/MangaListService.java | 16 +++++----- .../com/magamochi/service/MangaService.java | 20 ++++++------ .../ProviderManualMangaImportService.java | 27 ++++++++-------- .../magamochi/service/ProviderService.java | 32 ++++++------------- .../service/providers/ContentProvider.java | 4 +-- .../service/providers/impl/BatoProvider.java | 7 ++-- .../providers/impl/MangaDexProvider.java | 5 +-- .../impl/MangaLivreBlogProvider.java | 8 ++--- .../providers/impl/MangaLivreProvider.java | 7 ++-- .../providers/impl/PinkRosaScanProvider.java | 7 ++-- .../magamochi/task/MangaFollowUpdateTask.java | 6 ++-- .../magamochi/task/UpdateMangaListTask.java | 6 ++-- 41 files changed, 244 insertions(+), 229 deletions(-) delete mode 100644 src/main/java/com/magamochi/controller/ProviderController.java create mode 100644 src/main/java/com/magamochi/ingestion/controller/IngestionController.java create mode 100644 src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java rename src/main/java/com/magamochi/{model/entity/Provider.java => ingestion/model/entity/ContentProvider.java} (53%) rename src/main/java/com/magamochi/{ => ingestion}/model/entity/FlareSession.java (83%) create mode 100644 src/main/java/com/magamochi/ingestion/model/repository/ContentProviderRepository.java create mode 100644 src/main/java/com/magamochi/ingestion/service/ContentProviderService.java rename src/main/java/com/magamochi/{ => ingestion}/service/FlareService.java (94%) rename src/main/java/com/magamochi/{ => ingestion}/service/FlareSessionManager.java (94%) rename src/main/java/com/magamochi/{ => ingestion}/task/FlareSessionCleanupTask.java (96%) rename src/main/java/com/magamochi/{ => ingestion}/task/FlareStartupCleanupTask.java (95%) delete mode 100644 src/main/java/com/magamochi/model/dto/ProviderListDTO.java rename src/main/java/com/magamochi/model/entity/{MangaProvider.java => MangaContentProvider.java} (70%) delete mode 100644 src/main/java/com/magamochi/model/enumeration/ProviderStatus.java create mode 100644 src/main/java/com/magamochi/model/repository/MangaContentProviderRepository.java delete mode 100644 src/main/java/com/magamochi/model/repository/MangaProviderRepository.java delete mode 100644 src/main/java/com/magamochi/model/repository/ProviderRepository.java diff --git a/src/main/java/com/magamochi/controller/ProviderController.java b/src/main/java/com/magamochi/controller/ProviderController.java deleted file mode 100644 index 8accd60..0000000 --- a/src/main/java/com/magamochi/controller/ProviderController.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.magamochi.controller; - -import com.magamochi.common.dto.DefaultResponseDTO; -import com.magamochi.model.dto.ProviderListDTO; -import com.magamochi.service.ProviderService; -import io.swagger.v3.oas.annotations.Operation; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/providers") -@RequiredArgsConstructor -public class ProviderController { - private final ProviderService providerService; - - @Operation( - summary = "Get a list of providers", - description = "Retrieve a list of content providers", - tags = {"Provider"}, - operationId = "getProviders") - @GetMapping - public DefaultResponseDTO getMangas( - @RequestParam(name = "manualImport", required = false) Boolean manualImport) { - return DefaultResponseDTO.ok(providerService.getProviders(manualImport)); - } -} diff --git a/src/main/java/com/magamochi/ingestion/controller/IngestionController.java b/src/main/java/com/magamochi/ingestion/controller/IngestionController.java new file mode 100644 index 0000000..581b76d --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/controller/IngestionController.java @@ -0,0 +1,29 @@ +package com.magamochi.ingestion.controller; + +import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.ingestion.model.dto.ContentProviderListDTO; +import com.magamochi.ingestion.service.ContentProviderService; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/ingestion") +@RequiredArgsConstructor +public class IngestionController { + private final ContentProviderService contentProviderService; + + @Operation( + summary = "Get a list of content providers", + description = "Retrieve a list of content providers", + tags = {"Ingestion"}, + operationId = "getContentProviders") + @GetMapping("/providers") + public DefaultResponseDTO getContentProviders( + @RequestParam(name = "manualImport", required = false) Boolean manualImport) { + return DefaultResponseDTO.ok(contentProviderService.getProviders(manualImport)); + } +} diff --git a/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java b/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java new file mode 100644 index 0000000..1aa266b --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java @@ -0,0 +1,19 @@ +package com.magamochi.ingestion.model.dto; + +import com.magamochi.ingestion.model.entity.ContentProvider; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +public record ContentProviderListDTO(@NotNull List providers) { + public static ContentProviderListDTO from(List contentProviders) { + return new ContentProviderListDTO( + contentProviders.stream().map(ContentProviderDTO::from).toList()); + } + + record ContentProviderDTO(long id, @NotBlank String name) { + public static ContentProviderDTO from(ContentProvider contentProvider) { + return new ContentProviderDTO(contentProvider.getId(), contentProvider.getName()); + } + } +} diff --git a/src/main/java/com/magamochi/model/entity/Provider.java b/src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java similarity index 53% rename from src/main/java/com/magamochi/model/entity/Provider.java rename to src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java index 5e6d5d7..85baedf 100644 --- a/src/main/java/com/magamochi/model/entity/Provider.java +++ b/src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java @@ -1,6 +1,6 @@ -package com.magamochi.model.entity; +package com.magamochi.ingestion.model.entity; -import com.magamochi.model.enumeration.ProviderStatus; +import com.magamochi.model.entity.MangaContentProvider; import jakarta.persistence.*; import java.time.Instant; import java.util.List; @@ -9,30 +9,29 @@ import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; @Entity -@Table(name = "providers") +@Table(name = "content_providers") @Builder @NoArgsConstructor @AllArgsConstructor @Getter @Setter -public class Provider { +public class ContentProvider { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; - @Enumerated(EnumType.STRING) - private ProviderStatus status; + private boolean active; + + private Boolean supportsChapterFetch; + + private Boolean manualImport; @CreationTimestamp private Instant createdAt; @UpdateTimestamp private Instant updatedAt; - @OneToMany(mappedBy = "provider") - private List mangaProviders; - - @Builder.Default private Boolean supportsChapterFetch = true; - - @Builder.Default private Boolean manualImport = false; + @OneToMany(mappedBy = "contentProvider") + private List mangaContentProviders; } diff --git a/src/main/java/com/magamochi/model/entity/FlareSession.java b/src/main/java/com/magamochi/ingestion/model/entity/FlareSession.java similarity index 83% rename from src/main/java/com/magamochi/model/entity/FlareSession.java rename to src/main/java/com/magamochi/ingestion/model/entity/FlareSession.java index e93224f..e9730c4 100644 --- a/src/main/java/com/magamochi/model/entity/FlareSession.java +++ b/src/main/java/com/magamochi/ingestion/model/entity/FlareSession.java @@ -1,4 +1,4 @@ -package com.magamochi.model.entity; +package com.magamochi.ingestion.model.entity; import java.time.Instant; import lombok.Builder; diff --git a/src/main/java/com/magamochi/ingestion/model/repository/ContentProviderRepository.java b/src/main/java/com/magamochi/ingestion/model/repository/ContentProviderRepository.java new file mode 100644 index 0000000..8232dec --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/model/repository/ContentProviderRepository.java @@ -0,0 +1,9 @@ +package com.magamochi.ingestion.model.repository; + +import com.magamochi.ingestion.model.entity.ContentProvider; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ContentProviderRepository extends JpaRepository { + Optional findByNameIgnoreCase(String name); +} diff --git a/src/main/java/com/magamochi/ingestion/service/ContentProviderService.java b/src/main/java/com/magamochi/ingestion/service/ContentProviderService.java new file mode 100644 index 0000000..fb7cbea --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/service/ContentProviderService.java @@ -0,0 +1,25 @@ +package com.magamochi.ingestion.service; + +import static java.util.Objects.nonNull; + +import com.magamochi.ingestion.model.dto.ContentProviderListDTO; +import com.magamochi.ingestion.model.entity.ContentProvider; +import com.magamochi.ingestion.model.repository.ContentProviderRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ContentProviderService { + private final ContentProviderRepository contentProviderRepository; + + public ContentProviderListDTO getProviders(Boolean manualImport) { + var providers = contentProviderRepository.findAll(); + + if (nonNull(manualImport) && manualImport) { + providers = providers.stream().filter(ContentProvider::getManualImport).toList(); + } + + return ContentProviderListDTO.from(providers); + } +} diff --git a/src/main/java/com/magamochi/service/FlareService.java b/src/main/java/com/magamochi/ingestion/service/FlareService.java similarity index 94% rename from src/main/java/com/magamochi/service/FlareService.java rename to src/main/java/com/magamochi/ingestion/service/FlareService.java index 8ee2d9d..b327e92 100644 --- a/src/main/java/com/magamochi/service/FlareService.java +++ b/src/main/java/com/magamochi/ingestion/service/FlareService.java @@ -1,4 +1,4 @@ -package com.magamochi.service; +package com.magamochi.ingestion.service; import com.magamochi.client.FlareClient; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/service/FlareSessionManager.java b/src/main/java/com/magamochi/ingestion/service/FlareSessionManager.java similarity index 94% rename from src/main/java/com/magamochi/service/FlareSessionManager.java rename to src/main/java/com/magamochi/ingestion/service/FlareSessionManager.java index eea1d60..81c7a11 100644 --- a/src/main/java/com/magamochi/service/FlareSessionManager.java +++ b/src/main/java/com/magamochi/ingestion/service/FlareSessionManager.java @@ -1,10 +1,10 @@ -package com.magamochi.service; +package com.magamochi.ingestion.service; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import com.magamochi.client.FlareClient; -import com.magamochi.model.entity.FlareSession; +import com.magamochi.ingestion.model.entity.FlareSession; import com.magamochi.registry.FlareSessionRegistry; import java.time.Duration; import java.time.Instant; diff --git a/src/main/java/com/magamochi/task/FlareSessionCleanupTask.java b/src/main/java/com/magamochi/ingestion/task/FlareSessionCleanupTask.java similarity index 96% rename from src/main/java/com/magamochi/task/FlareSessionCleanupTask.java rename to src/main/java/com/magamochi/ingestion/task/FlareSessionCleanupTask.java index 0b957b1..6e04d46 100644 --- a/src/main/java/com/magamochi/task/FlareSessionCleanupTask.java +++ b/src/main/java/com/magamochi/ingestion/task/FlareSessionCleanupTask.java @@ -1,4 +1,4 @@ -package com.magamochi.task; +package com.magamochi.ingestion.task; import com.magamochi.client.FlareClient; import com.magamochi.registry.FlareSessionRegistry; diff --git a/src/main/java/com/magamochi/task/FlareStartupCleanupTask.java b/src/main/java/com/magamochi/ingestion/task/FlareStartupCleanupTask.java similarity index 95% rename from src/main/java/com/magamochi/task/FlareStartupCleanupTask.java rename to src/main/java/com/magamochi/ingestion/task/FlareStartupCleanupTask.java index 637a993..d426c63 100644 --- a/src/main/java/com/magamochi/task/FlareStartupCleanupTask.java +++ b/src/main/java/com/magamochi/ingestion/task/FlareStartupCleanupTask.java @@ -1,4 +1,4 @@ -package com.magamochi.task; +package com.magamochi.ingestion.task; import com.magamochi.client.FlareClient; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/model/dto/ImportReviewDTO.java b/src/main/java/com/magamochi/model/dto/ImportReviewDTO.java index 58935d7..71dc51f 100644 --- a/src/main/java/com/magamochi/model/dto/ImportReviewDTO.java +++ b/src/main/java/com/magamochi/model/dto/ImportReviewDTO.java @@ -16,7 +16,7 @@ public record ImportReviewDTO( return new ImportReviewDTO( review.getId(), review.getTitle(), - review.getProvider().getName(), + review.getContentProvider().getName(), review.getUrl(), "Title match not found", review.getCreatedAt()); diff --git a/src/main/java/com/magamochi/model/dto/MangaDTO.java b/src/main/java/com/magamochi/model/dto/MangaDTO.java index dd5301b..1f339b1 100644 --- a/src/main/java/com/magamochi/model/dto/MangaDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaDTO.java @@ -5,8 +5,7 @@ import static java.util.Objects.isNull; import com.magamochi.model.entity.Manga; import com.magamochi.model.entity.MangaAlternativeTitle; import com.magamochi.model.entity.MangaChapter; -import com.magamochi.model.entity.MangaProvider; -import com.magamochi.model.enumeration.ProviderStatus; +import com.magamochi.model.entity.MangaContentProvider; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.time.OffsetDateTime; @@ -38,38 +37,38 @@ public record MangaDTO( manga.getPublishedFrom(), manga.getPublishedTo(), manga.getSynopsis(), - manga.getMangaProviders().size(), + manga.getMangaContentProviders().size(), manga.getAlternativeTitles().stream().map(MangaAlternativeTitle::getTitle).toList(), manga.getMangaGenres().stream().map(mangaGenre -> mangaGenre.getGenre().getName()).toList(), manga.getMangaAuthors().stream() .map(mangaAuthor -> mangaAuthor.getAuthor().getName()) .toList(), manga.getScore(), - manga.getMangaProviders().stream().map(MangaProviderDTO::from).toList(), + manga.getMangaContentProviders().stream().map(MangaProviderDTO::from).toList(), manga.getChapterCount(), favorite, following); } public record MangaProviderDTO( - @NotNull long id, + long id, @NotBlank String providerName, - @NotNull ProviderStatus providerStatus, + boolean active, @NotNull Integer chaptersAvailable, @NotNull Integer chaptersDownloaded, @NotNull Boolean supportsChapterFetch) { - public static MangaProviderDTO from(MangaProvider mangaProvider) { - var chapters = mangaProvider.getMangaChapters(); + public static MangaProviderDTO from(MangaContentProvider mangaContentProvider) { + var chapters = mangaContentProvider.getMangaChapters(); var chaptersAvailable = chapters.size(); var chaptersDownloaded = (int) chapters.stream().filter(MangaChapter::getDownloaded).count(); return new MangaProviderDTO( - mangaProvider.getId(), - mangaProvider.getProvider().getName(), - mangaProvider.getProvider().getStatus(), + mangaContentProvider.getId(), + mangaContentProvider.getContentProvider().getName(), + mangaContentProvider.getContentProvider().isActive(), chaptersAvailable, chaptersDownloaded, - mangaProvider.getProvider().getSupportsChapterFetch()); + mangaContentProvider.getContentProvider().getSupportsChapterFetch()); } } } diff --git a/src/main/java/com/magamochi/model/dto/MangaListDTO.java b/src/main/java/com/magamochi/model/dto/MangaListDTO.java index bd08320..c98e7fb 100644 --- a/src/main/java/com/magamochi/model/dto/MangaListDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaListDTO.java @@ -28,7 +28,7 @@ public record MangaListDTO( manga.getStatus(), manga.getPublishedFrom(), manga.getPublishedTo(), - manga.getMangaProviders().size(), + manga.getMangaContentProviders().size(), manga.getMangaGenres().stream().map(mangaGenre -> mangaGenre.getGenre().getName()).toList(), manga.getMangaAuthors().stream() .map(mangaAuthor -> mangaAuthor.getAuthor().getName()) diff --git a/src/main/java/com/magamochi/model/dto/ProviderListDTO.java b/src/main/java/com/magamochi/model/dto/ProviderListDTO.java deleted file mode 100644 index a92a2f7..0000000 --- a/src/main/java/com/magamochi/model/dto/ProviderListDTO.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.magamochi.model.dto; - -import com.magamochi.model.entity.Provider; -import jakarta.validation.constraints.NotNull; -import java.util.List; - -public record ProviderListDTO(@NotNull List providers) { - public static ProviderListDTO from(List providers) { - return new ProviderListDTO(providers.stream().map(ProviderDTO::from).toList()); - } - - record ProviderDTO(@NotNull Long id, @NotNull String name) { - public static ProviderDTO from(Provider provider) { - return new ProviderDTO(provider.getId(), provider.getName()); - } - } -} diff --git a/src/main/java/com/magamochi/model/entity/Manga.java b/src/main/java/com/magamochi/model/entity/Manga.java index 7eaaa32..09aac9e 100644 --- a/src/main/java/com/magamochi/model/entity/Manga.java +++ b/src/main/java/com/magamochi/model/entity/Manga.java @@ -35,7 +35,7 @@ public class Manga { @UpdateTimestamp private Instant updatedAt; @OneToMany(mappedBy = "manga") - private List mangaProviders; + private List mangaContentProviders; @ManyToOne @JoinColumn(name = "cover_image_id") diff --git a/src/main/java/com/magamochi/model/entity/MangaChapter.java b/src/main/java/com/magamochi/model/entity/MangaChapter.java index d47f11d..71f48b2 100644 --- a/src/main/java/com/magamochi/model/entity/MangaChapter.java +++ b/src/main/java/com/magamochi/model/entity/MangaChapter.java @@ -20,8 +20,8 @@ public class MangaChapter { private Long id; @ManyToOne - @JoinColumn(name = "manga_provider_id") - private MangaProvider mangaProvider; + @JoinColumn(name = "manga_content_provider_id") + private MangaContentProvider mangaContentProvider; private String title; diff --git a/src/main/java/com/magamochi/model/entity/MangaProvider.java b/src/main/java/com/magamochi/model/entity/MangaContentProvider.java similarity index 70% rename from src/main/java/com/magamochi/model/entity/MangaProvider.java rename to src/main/java/com/magamochi/model/entity/MangaContentProvider.java index 0be20c9..eddf5aa 100644 --- a/src/main/java/com/magamochi/model/entity/MangaProvider.java +++ b/src/main/java/com/magamochi/model/entity/MangaContentProvider.java @@ -1,5 +1,6 @@ package com.magamochi.model.entity; +import com.magamochi.ingestion.model.entity.ContentProvider; import jakarta.persistence.*; import java.time.Instant; import java.util.List; @@ -8,13 +9,13 @@ import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; @Entity -@Table(name = "manga_provider") +@Table(name = "manga_content_provider") @Builder @NoArgsConstructor @AllArgsConstructor @Getter @Setter -public class MangaProvider { +public class MangaContentProvider { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -24,14 +25,14 @@ public class MangaProvider { private Manga manga; @ManyToOne - @JoinColumn(name = "provider_id", nullable = false) - private Provider provider; + @JoinColumn(name = "content_provider_id", nullable = false) + private ContentProvider contentProvider; private String mangaTitle; private String url; - @OneToMany(mappedBy = "mangaProvider") + @OneToMany(mappedBy = "mangaContentProvider") List mangaChapters; @CreationTimestamp private Instant createdAt; diff --git a/src/main/java/com/magamochi/model/entity/MangaImportReview.java b/src/main/java/com/magamochi/model/entity/MangaImportReview.java index e291e7e..6c34b34 100644 --- a/src/main/java/com/magamochi/model/entity/MangaImportReview.java +++ b/src/main/java/com/magamochi/model/entity/MangaImportReview.java @@ -1,5 +1,6 @@ package com.magamochi.model.entity; +import com.magamochi.ingestion.model.entity.ContentProvider; import jakarta.persistence.*; import java.time.Instant; import lombok.*; @@ -22,8 +23,8 @@ public class MangaImportReview { private String url; @ManyToOne - @JoinColumn(name = "provider_id") - private Provider provider; + @JoinColumn(name = "content_provider_id") + private ContentProvider contentProvider; @CreationTimestamp private Instant createdAt; } diff --git a/src/main/java/com/magamochi/model/enumeration/ProviderStatus.java b/src/main/java/com/magamochi/model/enumeration/ProviderStatus.java deleted file mode 100644 index c09ddb0..0000000 --- a/src/main/java/com/magamochi/model/enumeration/ProviderStatus.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.magamochi.model.enumeration; - -public enum ProviderStatus { - ACTIVE, - INACTIVE -} diff --git a/src/main/java/com/magamochi/model/repository/MangaChapterRepository.java b/src/main/java/com/magamochi/model/repository/MangaChapterRepository.java index 3292cff..3b1b9c2 100644 --- a/src/main/java/com/magamochi/model/repository/MangaChapterRepository.java +++ b/src/main/java/com/magamochi/model/repository/MangaChapterRepository.java @@ -1,15 +1,15 @@ package com.magamochi.model.repository; import com.magamochi.model.entity.MangaChapter; -import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.MangaContentProvider; import jakarta.validation.constraints.NotBlank; import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface MangaChapterRepository extends JpaRepository { - Optional findByMangaProviderAndUrlIgnoreCase( - MangaProvider mangaProvider, @NotBlank String url); + Optional findByMangaContentProviderAndUrlIgnoreCase( + MangaContentProvider mangaContentProvider, @NotBlank String url); - List findByMangaProviderId(Long mangaProvider_id); + List findByMangaContentProviderId(Long mangaProvider_id); } diff --git a/src/main/java/com/magamochi/model/repository/MangaContentProviderRepository.java b/src/main/java/com/magamochi/model/repository/MangaContentProviderRepository.java new file mode 100644 index 0000000..066f3b4 --- /dev/null +++ b/src/main/java/com/magamochi/model/repository/MangaContentProviderRepository.java @@ -0,0 +1,15 @@ +package com.magamochi.model.repository; + +import com.magamochi.ingestion.model.entity.ContentProvider; +import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.MangaContentProvider; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MangaContentProviderRepository extends JpaRepository { + boolean existsByMangaAndContentProviderAndUrlIgnoreCase( + Manga manga, ContentProvider contentProvider, String url); + + Optional findByMangaTitleIgnoreCaseAndContentProvider( + String mangaTitle, ContentProvider contentProvider); +} diff --git a/src/main/java/com/magamochi/model/repository/MangaProviderRepository.java b/src/main/java/com/magamochi/model/repository/MangaProviderRepository.java deleted file mode 100644 index eb10676..0000000 --- a/src/main/java/com/magamochi/model/repository/MangaProviderRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.magamochi.model.repository; - -import com.magamochi.model.entity.Manga; -import com.magamochi.model.entity.MangaProvider; -import com.magamochi.model.entity.Provider; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface MangaProviderRepository extends JpaRepository { - boolean existsByMangaAndProviderAndUrlIgnoreCase(Manga manga, Provider provider, String url); - - Optional findByMangaTitleIgnoreCaseAndProvider( - String mangaTitle, Provider provider); -} diff --git a/src/main/java/com/magamochi/model/repository/ProviderRepository.java b/src/main/java/com/magamochi/model/repository/ProviderRepository.java deleted file mode 100644 index 57ba04a..0000000 --- a/src/main/java/com/magamochi/model/repository/ProviderRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.magamochi.model.repository; - -import com.magamochi.model.entity.Provider; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ProviderRepository extends JpaRepository { - Optional findByNameIgnoreCase(String name); -} diff --git a/src/main/java/com/magamochi/registry/FlareSessionRegistry.java b/src/main/java/com/magamochi/registry/FlareSessionRegistry.java index 969480f..dbf788c 100644 --- a/src/main/java/com/magamochi/registry/FlareSessionRegistry.java +++ b/src/main/java/com/magamochi/registry/FlareSessionRegistry.java @@ -1,6 +1,6 @@ package com.magamochi.registry; -import com.magamochi.model.entity.FlareSession; +import com.magamochi.ingestion.model.entity.FlareSession; import java.util.concurrent.ConcurrentHashMap; import lombok.Getter; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/magamochi/service/MangaChapterService.java b/src/main/java/com/magamochi/service/MangaChapterService.java index 3c0ab72..915843a 100644 --- a/src/main/java/com/magamochi/service/MangaChapterService.java +++ b/src/main/java/com/magamochi/service/MangaChapterService.java @@ -46,8 +46,9 @@ public class MangaChapterService { public void fetchChapter(Long chapterId) { var chapter = getMangaChapterThrowIfNotFound(chapterId); - var mangaProvider = chapter.getMangaProvider(); - var provider = contentProviderFactory.getContentProvider(mangaProvider.getProvider().getName()); + var mangaProvider = chapter.getMangaContentProvider(); + var provider = + contentProviderFactory.getContentProvider(mangaProvider.getContentProvider().getName()); var chapterImagesUrls = provider.getChapterImagesUrls(chapter.getUrl()); if (chapterImagesUrls.isEmpty()) { @@ -99,7 +100,7 @@ public class MangaChapterService { "Downloaded image {}/{} for manga {} chapter {}: {}", entry.getKey() + 1, chapterImagesUrls.size(), - chapter.getMangaProvider().getManga().getTitle(), + chapter.getMangaContentProvider().getManga().getTitle(), chapterId, entry.getValue()); @@ -127,7 +128,7 @@ public class MangaChapterService { var chapter = getMangaChapterThrowIfNotFound(chapterId); var chapters = - chapter.getMangaProvider().getMangaChapters().stream() + chapter.getMangaContentProvider().getMangaChapters().stream() .sorted(Comparator.comparing(MangaChapter::getId)) .toList(); Long prevId = null; diff --git a/src/main/java/com/magamochi/service/MangaCreationService.java b/src/main/java/com/magamochi/service/MangaCreationService.java index 343ab56..9e4f642 100644 --- a/src/main/java/com/magamochi/service/MangaCreationService.java +++ b/src/main/java/com/magamochi/service/MangaCreationService.java @@ -5,11 +5,11 @@ import static java.util.Objects.nonNull; import com.google.common.util.concurrent.RateLimiter; import com.magamochi.client.AniListClient; import com.magamochi.client.JikanClient; +import com.magamochi.ingestion.model.entity.ContentProvider; import com.magamochi.model.dto.TitleMatchRequestDTO; import com.magamochi.model.dto.UpdateMangaDataCommand; import com.magamochi.model.entity.Manga; import com.magamochi.model.entity.MangaImportReview; -import com.magamochi.model.entity.Provider; import com.magamochi.model.repository.MangaImportReviewRepository; import com.magamochi.model.repository.MangaRepository; import com.magamochi.queue.UpdateMangaDataProducer; @@ -33,7 +33,7 @@ public class MangaCreationService { private final UpdateMangaDataProducer updateMangaDataProducer; - public Manga getOrCreateManga(String title, String url, Provider provider) { + public Manga getOrCreateManga(String title, String url, ContentProvider contentProvider) { var existingManga = mangaRepository.findByTitleIgnoreCase(title); if (existingManga.isPresent()) { return existingManga.get(); @@ -42,7 +42,7 @@ public class MangaCreationService { jikanRateLimiter.acquire(); var jikanResults = jikanClient.mangaSearch(title).data(); if (jikanResults.isEmpty()) { - createMangaImportReview(title, url, provider); + createMangaImportReview(title, url, contentProvider); log.warn("No manga found with title {}", title); return null; } @@ -61,7 +61,7 @@ public class MangaCreationService { .build()); if (!titleMatchResponse.isMatchFound()) { - createMangaImportReview(title, url, provider); + createMangaImportReview(title, url, contentProvider); log.warn("No match found for manga with title {}", title); return null; } @@ -76,7 +76,7 @@ public class MangaCreationService { .contains(titleMatchResponse.getBestMatch())) .findFirst(); if (resultOptional.isEmpty()) { - createMangaImportReview(title, url, provider); + createMangaImportReview(title, url, contentProvider); log.warn("No match found for manga with title {}", title); return null; } @@ -152,12 +152,12 @@ public class MangaCreationService { }); } - private void createMangaImportReview(String title, String url, Provider provider) { + private void createMangaImportReview(String title, String url, ContentProvider contentProvider) { if (mangaImportReviewRepository.existsByTitleIgnoreCaseAndUrlIgnoreCase(title, url)) { return; } mangaImportReviewRepository.save( - MangaImportReview.builder().title(title).url(url).provider(provider).build()); + MangaImportReview.builder().title(title).url(url).contentProvider(contentProvider).build()); } } diff --git a/src/main/java/com/magamochi/service/MangaImportReviewService.java b/src/main/java/com/magamochi/service/MangaImportReviewService.java index 7b772cd..56e49c3 100644 --- a/src/main/java/com/magamochi/service/MangaImportReviewService.java +++ b/src/main/java/com/magamochi/service/MangaImportReviewService.java @@ -8,10 +8,10 @@ import com.magamochi.common.exception.NotFoundException; import com.magamochi.model.dto.ImportReviewDTO; import com.magamochi.model.dto.UpdateMangaDataCommand; import com.magamochi.model.entity.Manga; +import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.entity.MangaImportReview; -import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.repository.MangaContentProviderRepository; import com.magamochi.model.repository.MangaImportReviewRepository; -import com.magamochi.model.repository.MangaProviderRepository; import com.magamochi.model.repository.MangaRepository; import com.magamochi.queue.UpdateMangaDataProducer; import java.util.List; @@ -23,7 +23,7 @@ import org.springframework.stereotype.Service; public class MangaImportReviewService { private final MangaImportReviewRepository mangaImportReviewRepository; private final MangaRepository mangaRepository; - private final MangaProviderRepository mangaProviderRepository; + private final MangaContentProviderRepository mangaContentProviderRepository; private final JikanClient jikanClient; @@ -62,13 +62,13 @@ public class MangaImportReviewService { .malId(Long.parseLong(malId)) .build())); - if (!mangaProviderRepository.existsByMangaAndProviderAndUrlIgnoreCase( - manga, importReview.getProvider(), importReview.getUrl())) { - mangaProviderRepository.save( - MangaProvider.builder() + if (!mangaContentProviderRepository.existsByMangaAndContentProviderAndUrlIgnoreCase( + manga, importReview.getContentProvider(), importReview.getUrl())) { + mangaContentProviderRepository.save( + MangaContentProvider.builder() .manga(manga) .mangaTitle(importReview.getTitle()) - .provider(importReview.getProvider()) + .contentProvider(importReview.getContentProvider()) .url(importReview.getUrl()) .build()); } diff --git a/src/main/java/com/magamochi/service/MangaImportService.java b/src/main/java/com/magamochi/service/MangaImportService.java index eb6c578..d154672 100644 --- a/src/main/java/com/magamochi/service/MangaImportService.java +++ b/src/main/java/com/magamochi/service/MangaImportService.java @@ -7,6 +7,7 @@ import com.google.common.util.concurrent.RateLimiter; import com.magamochi.client.AniListClient; import com.magamochi.client.JikanClient; import com.magamochi.common.exception.NotFoundException; +import com.magamochi.ingestion.model.entity.ContentProvider; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.model.entity.*; import com.magamochi.model.repository.*; @@ -40,7 +41,7 @@ public class MangaImportService { private final GenreRepository genreRepository; private final MangaGenreRepository mangaGenreRepository; - private final MangaProviderRepository mangaProviderRepository; + private final MangaContentProviderRepository mangaContentProviderRepository; private final AuthorRepository authorRepository; private final MangaAuthorRepository mangaAuthorRepository; private final MangaChapterRepository mangaChapterRepository; @@ -368,13 +369,13 @@ public class MangaImportService { } public MangaChapter persistMangaChapter( - MangaProvider mangaProvider, ContentProviderMangaChapterResponseDTO chapter) { + MangaContentProvider mangaContentProvider, ContentProviderMangaChapterResponseDTO chapter) { var mangaChapter = mangaChapterRepository - .findByMangaProviderAndUrlIgnoreCase(mangaProvider, chapter.chapterUrl()) + .findByMangaContentProviderAndUrlIgnoreCase(mangaContentProvider, chapter.chapterUrl()) .orElseGet(MangaChapter::new); - mangaChapter.setMangaProvider(mangaProvider); + mangaChapter.setMangaContentProvider(mangaContentProvider); mangaChapter.setTitle(chapter.chapterTitle()); mangaChapter.setUrl(chapter.chapterUrl()); @@ -388,26 +389,27 @@ public class MangaImportService { log.warn( "Could not parse chapter number {} from manga {}", chapter.chapter(), - mangaProvider.getManga().getTitle()); + mangaContentProvider.getManga().getTitle()); } } return mangaChapterRepository.save(mangaChapter); } - private MangaProvider getOrCreateMangaProvider(String title, Provider provider) { - return mangaProviderRepository - .findByMangaTitleIgnoreCaseAndProvider(title, provider) + private MangaContentProvider getOrCreateMangaProvider( + String title, ContentProvider contentProvider) { + return mangaContentProviderRepository + .findByMangaTitleIgnoreCaseAndContentProvider(title, contentProvider) .orElseGet( () -> { jikanRateLimiter.acquire(); - var manga = mangaCreationService.getOrCreateManga(title, "manual", provider); + var manga = mangaCreationService.getOrCreateManga(title, "manual", contentProvider); - return mangaProviderRepository.save( - MangaProvider.builder() + return mangaContentProviderRepository.save( + MangaContentProvider.builder() .manga(manga) .mangaTitle(manga.getTitle()) - .provider(provider) + .contentProvider(contentProvider) .url("manual") .build()); }); diff --git a/src/main/java/com/magamochi/service/MangaListService.java b/src/main/java/com/magamochi/service/MangaListService.java index 6f51af4..21e1e08 100644 --- a/src/main/java/com/magamochi/service/MangaListService.java +++ b/src/main/java/com/magamochi/service/MangaListService.java @@ -2,8 +2,8 @@ package com.magamochi.service; import static java.util.Objects.isNull; -import com.magamochi.model.entity.MangaProvider; -import com.magamochi.model.repository.MangaProviderRepository; +import com.magamochi.model.entity.MangaContentProvider; +import com.magamochi.model.repository.MangaContentProviderRepository; import com.magamochi.service.providers.PagedContentProviderFactory; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -17,7 +17,7 @@ public class MangaListService { private final MangaCreationService mangaCreationService; private final PagedContentProviderFactory pagedContentProviderFactory; - private final MangaProviderRepository mangaProviderRepository; + private final MangaContentProviderRepository mangaContentProviderRepository; public void updateMangaList(String contentProviderName, Integer page) { var contentProvider = pagedContentProviderFactory.getPagedContentProvider(contentProviderName); @@ -28,7 +28,7 @@ public class MangaListService { mangas.forEach( mangaResponse -> { var mangaProvider = - mangaProviderRepository.findByMangaTitleIgnoreCaseAndProvider( + mangaContentProviderRepository.findByMangaTitleIgnoreCaseAndContentProvider( mangaResponse.title(), provider); if (mangaProvider.isPresent()) { @@ -43,13 +43,13 @@ public class MangaListService { return; } - if (!mangaProviderRepository.existsByMangaAndProviderAndUrlIgnoreCase( + if (!mangaContentProviderRepository.existsByMangaAndContentProviderAndUrlIgnoreCase( manga, provider, mangaResponse.url())) { - mangaProviderRepository.save( - MangaProvider.builder() + mangaContentProviderRepository.save( + MangaContentProvider.builder() .manga(manga) .mangaTitle(mangaResponse.title()) - .provider(provider) + .contentProvider(provider) .url(mangaResponse.url()) .build()); } diff --git a/src/main/java/com/magamochi/service/MangaService.java b/src/main/java/com/magamochi/service/MangaService.java index 7df23a5..db0b8f8 100644 --- a/src/main/java/com/magamochi/service/MangaService.java +++ b/src/main/java/com/magamochi/service/MangaService.java @@ -7,7 +7,7 @@ import com.magamochi.common.exception.NotFoundException; import com.magamochi.model.dto.*; import com.magamochi.model.entity.Manga; import com.magamochi.model.entity.MangaChapter; -import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.entity.UserMangaFollow; import com.magamochi.model.repository.*; import com.magamochi.model.specification.MangaSpecification; @@ -31,7 +31,7 @@ public class MangaService { private final MangaImportService mangaImportService; private final UserService userService; private final MangaRepository mangaRepository; - private final MangaProviderRepository mangaProviderRepository; + private final MangaContentProviderRepository mangaContentProviderRepository; private final ContentProviderFactory contentProviderFactory; private final UserFavoriteMangaRepository userFavoriteMangaRepository; @@ -45,7 +45,7 @@ public class MangaService { public void fetchAllNotDownloadedChapters(Long mangaProviderId) { var mangaProvider = - mangaProviderRepository + mangaContentProviderRepository .findById(mangaProviderId) .orElseThrow( () -> new NotFoundException("Manga Provider not found for ID: " + mangaProviderId)); @@ -118,19 +118,19 @@ public class MangaService { public void fetchFollowedMangaChapters(Long mangaProviderId) { var mangaProvider = - mangaProviderRepository + mangaContentProviderRepository .findById(mangaProviderId) .orElseThrow( () -> new NotFoundException("Manga Provider not found for ID: " + mangaProviderId)); var currentAvailableChapterCount = - mangaChapterRepository.findByMangaProviderId(mangaProviderId).size(); + mangaChapterRepository.findByMangaContentProviderId(mangaProviderId).size(); fetchMangaChapters(mangaProviderId); mangaChapterRepository.flush(); var availableChapterCount = - mangaChapterRepository.findByMangaProviderId(mangaProviderId).size(); + mangaChapterRepository.findByMangaContentProviderId(mangaProviderId).size(); if (availableChapterCount <= currentAvailableChapterCount) { return; @@ -145,14 +145,14 @@ public class MangaService { new NtfyClient.Request( "mangamochi-" + umf.getUser().getId().toString(), umf.getManga().getTitle(), - "New chapter available on " + mangaProvider.getProvider().getName()))); + "New chapter available on " + mangaProvider.getContentProvider().getName()))); } public void fetchMangaChapters(Long mangaProviderId) { var mangaProvider = getMangaProviderThrowIfNotFound(mangaProviderId); var contentProvider = - contentProviderFactory.getContentProvider(mangaProvider.getProvider().getName()); + contentProviderFactory.getContentProvider(mangaProvider.getContentProvider().getName()); var availableChapters = contentProvider.getAvailableChapters(mangaProvider); availableChapters.forEach( @@ -165,8 +165,8 @@ public class MangaService { .orElseThrow(() -> new NotFoundException("Manga not found for ID: " + mangaId)); } - private MangaProvider getMangaProviderThrowIfNotFound(Long mangaProviderId) { - return mangaProviderRepository + private MangaContentProvider getMangaProviderThrowIfNotFound(Long mangaProviderId) { + return mangaContentProviderRepository .findById(mangaProviderId) .orElseThrow( () -> new NotFoundException("Manga Provider not found for ID: " + mangaProviderId)); diff --git a/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java b/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java index 00cba42..1b88b71 100644 --- a/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java +++ b/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java @@ -4,13 +4,12 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import com.magamochi.common.exception.NotFoundException; +import com.magamochi.ingestion.model.entity.ContentProvider; +import com.magamochi.ingestion.model.repository.ContentProviderRepository; import com.magamochi.model.dto.ImportMangaResponseDTO; import com.magamochi.model.dto.ImportRequestDTO; -import com.magamochi.model.entity.MangaProvider; -import com.magamochi.model.entity.Provider; -import com.magamochi.model.enumeration.ProviderStatus; -import com.magamochi.model.repository.MangaProviderRepository; -import com.magamochi.model.repository.ProviderRepository; +import com.magamochi.model.entity.MangaContentProvider; +import com.magamochi.model.repository.MangaContentProviderRepository; import com.magamochi.service.providers.ManualImportContentProviderFactory; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -24,8 +23,8 @@ public class ProviderManualMangaImportService { private final ManualImportContentProviderFactory contentProviderFactory; - private final ProviderRepository providerRepository; - private final MangaProviderRepository mangaProviderRepository; + private final ContentProviderRepository contentProviderRepository; + private final MangaContentProviderRepository mangaContentProviderRepository; public ImportMangaResponseDTO importFromProvider(Long providerId, ImportRequestDTO requestDTO) { var provider = getProvider(providerId); @@ -45,13 +44,13 @@ public class ProviderManualMangaImportService { throw new NotFoundException("Manga could not be found or created for ID: " + requestDTO.id()); } - if (!mangaProviderRepository.existsByMangaAndProviderAndUrlIgnoreCase( + if (!mangaContentProviderRepository.existsByMangaAndContentProviderAndUrlIgnoreCase( manga, provider, requestDTO.id())) { - mangaProviderRepository.save( - MangaProvider.builder() + mangaContentProviderRepository.save( + MangaContentProvider.builder() .manga(manga) .mangaTitle(title) - .provider(provider) + .contentProvider(provider) .url(requestDTO.id()) .build()); } @@ -59,13 +58,13 @@ public class ProviderManualMangaImportService { return new ImportMangaResponseDTO(manga.getId()); } - public Provider getProvider(Long providerId) { + public ContentProvider getProvider(Long providerId) { var provider = - providerRepository + contentProviderRepository .findById(providerId) .orElseThrow(() -> new NotFoundException("Provider not found")); - if (!provider.getStatus().equals(ProviderStatus.ACTIVE)) { + if (!provider.isActive()) { throw new IllegalStateException("Provider is not active"); } diff --git a/src/main/java/com/magamochi/service/ProviderService.java b/src/main/java/com/magamochi/service/ProviderService.java index 47f7e3d..88a1467 100644 --- a/src/main/java/com/magamochi/service/ProviderService.java +++ b/src/main/java/com/magamochi/service/ProviderService.java @@ -1,42 +1,28 @@ package com.magamochi.service; -import static java.util.Objects.nonNull; - -import com.magamochi.model.dto.ProviderListDTO; -import com.magamochi.model.entity.Provider; -import com.magamochi.model.enumeration.ProviderStatus; -import com.magamochi.model.repository.ProviderRepository; +import com.magamochi.ingestion.model.entity.ContentProvider; +import com.magamochi.ingestion.model.repository.ContentProviderRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class ProviderService { - private final ProviderRepository providerRepository; + private final ContentProviderRepository contentProviderRepository; - public ProviderListDTO getProviders(Boolean manualImport) { - var providers = providerRepository.findAll(); - - if (nonNull(manualImport) && manualImport) { - providers = providers.stream().filter(Provider::getManualImport).toList(); - } - - return ProviderListDTO.from(providers); - } - - public Provider getOrCreateProvider(String providerName) { + public ContentProvider getOrCreateProvider(String providerName) { return getOrCreateProvider(providerName, true); } - public Provider getOrCreateProvider(String providerName, Boolean supportsChapterFetch) { - return providerRepository + public ContentProvider getOrCreateProvider(String providerName, Boolean supportsChapterFetch) { + return contentProviderRepository .findByNameIgnoreCase(providerName) .orElseGet( () -> - providerRepository.save( - Provider.builder() + contentProviderRepository.save( + ContentProvider.builder() .name(providerName) - .status(ProviderStatus.ACTIVE) + .active(true) .supportsChapterFetch(supportsChapterFetch) .build())); } diff --git a/src/main/java/com/magamochi/service/providers/ContentProvider.java b/src/main/java/com/magamochi/service/providers/ContentProvider.java index c30a940..fb43df0 100644 --- a/src/main/java/com/magamochi/service/providers/ContentProvider.java +++ b/src/main/java/com/magamochi/service/providers/ContentProvider.java @@ -1,12 +1,12 @@ package com.magamochi.service.providers; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.MangaContentProvider; import java.util.List; import java.util.Map; public interface ContentProvider { - List getAvailableChapters(MangaProvider provider); + List getAvailableChapters(MangaContentProvider provider); Map getChapterImagesUrls(String chapterUrl); } diff --git a/src/main/java/com/magamochi/service/providers/impl/BatoProvider.java b/src/main/java/com/magamochi/service/providers/impl/BatoProvider.java index c8de42b..99328ec 100644 --- a/src/main/java/com/magamochi/service/providers/impl/BatoProvider.java +++ b/src/main/java/com/magamochi/service/providers/impl/BatoProvider.java @@ -3,9 +3,9 @@ package com.magamochi.service.providers.impl; import static java.util.Objects.isNull; import com.magamochi.common.exception.UnprocessableException; +import com.magamochi.ingestion.service.FlareService; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.entity.MangaProvider; -import com.magamochi.service.FlareService; +import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.service.providers.ContentProvider; import com.magamochi.service.providers.ContentProviders; import com.magamochi.service.providers.ManualImportContentProvider; @@ -25,7 +25,8 @@ public class BatoProvider implements ContentProvider, ManualImportContentProvide private final FlareService flareService; @Override - public List getAvailableChapters(MangaProvider provider) { + public List getAvailableChapters( + MangaContentProvider provider) { try { var document = flareService.getContentAsJsoupDocument(provider.getUrl(), ContentProviders.BATO); diff --git a/src/main/java/com/magamochi/service/providers/impl/MangaDexProvider.java b/src/main/java/com/magamochi/service/providers/impl/MangaDexProvider.java index 6f96e8a..42ba712 100644 --- a/src/main/java/com/magamochi/service/providers/impl/MangaDexProvider.java +++ b/src/main/java/com/magamochi/service/providers/impl/MangaDexProvider.java @@ -6,7 +6,7 @@ import com.google.common.util.concurrent.RateLimiter; import com.magamochi.client.MangaDexClient; import com.magamochi.common.exception.UnprocessableException; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.service.providers.ContentProvider; import com.magamochi.service.providers.ContentProviders; import com.magamochi.service.providers.ManualImportContentProvider; @@ -26,7 +26,8 @@ public class MangaDexProvider implements ContentProvider, ManualImportContentPro private final RateLimiter mangaDexRateLimiter; @Override - public List getAvailableChapters(MangaProvider provider) { + public List getAvailableChapters( + MangaContentProvider provider) { try { mangaDexRateLimiter.acquire(); var response = diff --git a/src/main/java/com/magamochi/service/providers/impl/MangaLivreBlogProvider.java b/src/main/java/com/magamochi/service/providers/impl/MangaLivreBlogProvider.java index 02173cf..abc9ae4 100644 --- a/src/main/java/com/magamochi/service/providers/impl/MangaLivreBlogProvider.java +++ b/src/main/java/com/magamochi/service/providers/impl/MangaLivreBlogProvider.java @@ -2,7 +2,7 @@ package com.magamochi.service.providers.impl; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; -import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.enumeration.MangaStatus; import com.magamochi.service.providers.ContentProvider; import com.magamochi.service.providers.ContentProviders; @@ -29,14 +29,14 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv @Override public List getAvailableChapters( - MangaProvider mangaProvider) { + MangaContentProvider mangaContentProvider) { log.info( "Getting available chapters from {}, manga {}", ContentProviders.MANGA_LIVRE_BLOG, - mangaProvider.getManga().getTitle()); + mangaContentProvider.getManga().getTitle()); try { - var document = Jsoup.connect(mangaProvider.getUrl()).get(); + var document = Jsoup.connect(mangaContentProvider.getUrl()).get(); var chapterList = document.getElementsByClass("chapters-list").getFirst(); var chapterItems = chapterList.getElementsByClass("chapter-item"); diff --git a/src/main/java/com/magamochi/service/providers/impl/MangaLivreProvider.java b/src/main/java/com/magamochi/service/providers/impl/MangaLivreProvider.java index 5d237ee..b7d766a 100644 --- a/src/main/java/com/magamochi/service/providers/impl/MangaLivreProvider.java +++ b/src/main/java/com/magamochi/service/providers/impl/MangaLivreProvider.java @@ -2,11 +2,11 @@ package com.magamochi.service.providers.impl; import static java.util.Objects.nonNull; +import com.magamochi.ingestion.service.FlareService; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; -import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.enumeration.MangaStatus; -import com.magamochi.service.FlareService; import com.magamochi.service.providers.ContentProvider; import com.magamochi.service.providers.ContentProviders; import com.magamochi.service.providers.PagedContentProvider; @@ -27,7 +27,8 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider private final FlareService flareService; @Override - public List getAvailableChapters(MangaProvider provider) { + public List getAvailableChapters( + MangaContentProvider provider) { log.info( "Getting available chapters from {}, manga {}", ContentProviders.MANGA_LIVRE_TO, diff --git a/src/main/java/com/magamochi/service/providers/impl/PinkRosaScanProvider.java b/src/main/java/com/magamochi/service/providers/impl/PinkRosaScanProvider.java index c02e6d3..1cd6a89 100644 --- a/src/main/java/com/magamochi/service/providers/impl/PinkRosaScanProvider.java +++ b/src/main/java/com/magamochi/service/providers/impl/PinkRosaScanProvider.java @@ -3,11 +3,11 @@ package com.magamochi.service.providers.impl; import static java.util.Objects.isNull; import static org.apache.commons.lang3.StringUtils.isBlank; +import com.magamochi.ingestion.service.FlareService; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; -import com.magamochi.model.entity.MangaProvider; +import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.enumeration.MangaStatus; -import com.magamochi.service.FlareService; import com.magamochi.service.providers.ContentProvider; import com.magamochi.service.providers.ContentProviders; import com.magamochi.service.providers.PagedContentProvider; @@ -28,7 +28,8 @@ public class PinkRosaScanProvider implements ContentProvider, PagedContentProvid private final FlareService flareService; @Override - public List getAvailableChapters(MangaProvider provider) { + public List getAvailableChapters( + MangaContentProvider provider) { log.info( "Getting available chapters from {}, manga {}", ContentProviders.PINK_ROSA_SCAN, diff --git a/src/main/java/com/magamochi/task/MangaFollowUpdateTask.java b/src/main/java/com/magamochi/task/MangaFollowUpdateTask.java index 6979d6d..926c036 100644 --- a/src/main/java/com/magamochi/task/MangaFollowUpdateTask.java +++ b/src/main/java/com/magamochi/task/MangaFollowUpdateTask.java @@ -2,7 +2,6 @@ package com.magamochi.task; import com.magamochi.model.dto.UpdateMangaFollowChapterListCommand; import com.magamochi.model.entity.Manga; -import com.magamochi.model.enumeration.ProviderStatus; import com.magamochi.model.repository.MangaRepository; import com.magamochi.queue.UpdateMangaFollowChapterListProducer; import lombok.RequiredArgsConstructor; @@ -43,11 +42,10 @@ public class MangaFollowUpdateTask { private void updateFollowedManga(Manga manga) { log.info("Fetching available mangas for followed Manga {}", manga.getTitle()); - var mangaProviders = manga.getMangaProviders(); + var mangaProviders = manga.getMangaContentProviders(); mangaProviders.stream() - .filter( - mangaProvider -> mangaProvider.getProvider().getStatus().equals(ProviderStatus.ACTIVE)) + .filter(mangaProvider -> mangaProvider.getContentProvider().isActive()) .forEach( mangaProvider -> producer.sendUpdateMangaFollowChapterListCommand( diff --git a/src/main/java/com/magamochi/task/UpdateMangaListTask.java b/src/main/java/com/magamochi/task/UpdateMangaListTask.java index 54395dd..466776a 100644 --- a/src/main/java/com/magamochi/task/UpdateMangaListTask.java +++ b/src/main/java/com/magamochi/task/UpdateMangaListTask.java @@ -1,8 +1,8 @@ package com.magamochi.task; import com.magamochi.common.exception.NotFoundException; +import com.magamochi.ingestion.model.repository.ContentProviderRepository; import com.magamochi.model.dto.MangaListUpdateCommand; -import com.magamochi.model.repository.ProviderRepository; import com.magamochi.queue.UpdateMangaListProducer; import com.magamochi.service.providers.PagedContentProvider; import com.magamochi.service.providers.PagedContentProviderFactory; @@ -22,7 +22,7 @@ public class UpdateMangaListTask { private final PagedContentProviderFactory contentProviderFactory; private final UpdateMangaListProducer updateMangaListProducer; - private final ProviderRepository providerRepository; + private final ContentProviderRepository contentProviderRepository; @Scheduled(cron = "${content-providers.cron-expression}") public void updateMangaListScheduled() { @@ -42,7 +42,7 @@ public class UpdateMangaListTask { public void updateProviderMangaList(Long providerId) { var provider = - providerRepository + contentProviderRepository .findById(providerId) .orElseThrow(() -> new NotFoundException("Provider not found")); var contentProvider = contentProviderFactory.getPagedContentProvider(provider.getName()); -- 2.49.1 From a6f01bba308392a45808eb036aaf93e079b5f07a Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Tue, 17 Mar 2026 20:00:16 -0300 Subject: [PATCH 05/11] refactor: rename GenreController to CatalogController and update package structure --- .../controller/CatalogController.java} | 18 +++++++++--------- .../{ => catalog}/model/dto/GenreDTO.java | 4 ++-- .../{ => catalog}/model/entity/Genre.java | 3 ++- .../model/repository/GenreRepository.java | 4 ++-- .../{ => catalog}/service/GenreService.java | 6 +++--- .../com/magamochi/model/entity/MangaGenre.java | 1 + .../model/repository/MangaGenreRepository.java | 2 +- .../magamochi/service/MangaImportService.java | 2 ++ 8 files changed, 22 insertions(+), 18 deletions(-) rename src/main/java/com/magamochi/{controller/GenreController.java => catalog/controller/CatalogController.java} (56%) rename src/main/java/com/magamochi/{ => catalog}/model/dto/GenreDTO.java (75%) rename src/main/java/com/magamochi/{ => catalog}/model/entity/Genre.java (81%) rename src/main/java/com/magamochi/{ => catalog}/model/repository/GenreRepository.java (72%) rename src/main/java/com/magamochi/{ => catalog}/service/GenreService.java (70%) diff --git a/src/main/java/com/magamochi/controller/GenreController.java b/src/main/java/com/magamochi/catalog/controller/CatalogController.java similarity index 56% rename from src/main/java/com/magamochi/controller/GenreController.java rename to src/main/java/com/magamochi/catalog/controller/CatalogController.java index c922a4b..a2bd6b9 100644 --- a/src/main/java/com/magamochi/controller/GenreController.java +++ b/src/main/java/com/magamochi/catalog/controller/CatalogController.java @@ -1,25 +1,25 @@ -package com.magamochi.controller; +package com.magamochi.catalog.controller; +import com.magamochi.catalog.model.dto.GenreDTO; +import com.magamochi.catalog.service.GenreService; import com.magamochi.common.dto.DefaultResponseDTO; -import com.magamochi.model.dto.GenreDTO; -import com.magamochi.service.GenreService; import io.swagger.v3.oas.annotations.Operation; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @RestController -@RequestMapping("/genres") +@RequestMapping("/catalog") @RequiredArgsConstructor -public class GenreController { +public class CatalogController { private final GenreService genreService; @Operation( - summary = "Get a list of genres", - description = "Retrieve a list of genres.", - tags = {"Genre"}, + summary = "Get a list of manga genres", + description = "Retrieve a list of manga genres.", + tags = {"Catalog"}, operationId = "getGenres") - @GetMapping + @GetMapping("/genres") public DefaultResponseDTO> getGenres() { return DefaultResponseDTO.ok(genreService.getGenres()); } diff --git a/src/main/java/com/magamochi/model/dto/GenreDTO.java b/src/main/java/com/magamochi/catalog/model/dto/GenreDTO.java similarity index 75% rename from src/main/java/com/magamochi/model/dto/GenreDTO.java rename to src/main/java/com/magamochi/catalog/model/dto/GenreDTO.java index 1283415..94151c6 100644 --- a/src/main/java/com/magamochi/model/dto/GenreDTO.java +++ b/src/main/java/com/magamochi/catalog/model/dto/GenreDTO.java @@ -1,6 +1,6 @@ -package com.magamochi.model.dto; +package com.magamochi.catalog.model.dto; -import com.magamochi.model.entity.Genre; +import com.magamochi.catalog.model.entity.Genre; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/model/entity/Genre.java b/src/main/java/com/magamochi/catalog/model/entity/Genre.java similarity index 81% rename from src/main/java/com/magamochi/model/entity/Genre.java rename to src/main/java/com/magamochi/catalog/model/entity/Genre.java index a78e748..4c5f557 100644 --- a/src/main/java/com/magamochi/model/entity/Genre.java +++ b/src/main/java/com/magamochi/catalog/model/entity/Genre.java @@ -1,5 +1,6 @@ -package com.magamochi.model.entity; +package com.magamochi.catalog.model.entity; +import com.magamochi.model.entity.MangaGenre; import jakarta.persistence.*; import java.util.List; import lombok.*; diff --git a/src/main/java/com/magamochi/model/repository/GenreRepository.java b/src/main/java/com/magamochi/catalog/model/repository/GenreRepository.java similarity index 72% rename from src/main/java/com/magamochi/model/repository/GenreRepository.java rename to src/main/java/com/magamochi/catalog/model/repository/GenreRepository.java index 3acbe30..165c9fb 100644 --- a/src/main/java/com/magamochi/model/repository/GenreRepository.java +++ b/src/main/java/com/magamochi/catalog/model/repository/GenreRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.model.repository; +package com.magamochi.catalog.model.repository; -import com.magamochi.model.entity.Genre; +import com.magamochi.catalog.model.entity.Genre; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/service/GenreService.java b/src/main/java/com/magamochi/catalog/service/GenreService.java similarity index 70% rename from src/main/java/com/magamochi/service/GenreService.java rename to src/main/java/com/magamochi/catalog/service/GenreService.java index e18f4ef..d2ba420 100644 --- a/src/main/java/com/magamochi/service/GenreService.java +++ b/src/main/java/com/magamochi/catalog/service/GenreService.java @@ -1,7 +1,7 @@ -package com.magamochi.service; +package com.magamochi.catalog.service; -import com.magamochi.model.dto.GenreDTO; -import com.magamochi.model.repository.GenreRepository; +import com.magamochi.catalog.model.dto.GenreDTO; +import com.magamochi.catalog.model.repository.GenreRepository; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/model/entity/MangaGenre.java b/src/main/java/com/magamochi/model/entity/MangaGenre.java index 7c0df2e..15a2a5d 100644 --- a/src/main/java/com/magamochi/model/entity/MangaGenre.java +++ b/src/main/java/com/magamochi/model/entity/MangaGenre.java @@ -1,5 +1,6 @@ package com.magamochi.model.entity; +import com.magamochi.catalog.model.entity.Genre; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/model/repository/MangaGenreRepository.java b/src/main/java/com/magamochi/model/repository/MangaGenreRepository.java index c0843b9..7b68a95 100644 --- a/src/main/java/com/magamochi/model/repository/MangaGenreRepository.java +++ b/src/main/java/com/magamochi/model/repository/MangaGenreRepository.java @@ -1,6 +1,6 @@ package com.magamochi.model.repository; -import com.magamochi.model.entity.Genre; +import com.magamochi.catalog.model.entity.Genre; import com.magamochi.model.entity.Manga; import com.magamochi.model.entity.MangaGenre; import java.util.Optional; diff --git a/src/main/java/com/magamochi/service/MangaImportService.java b/src/main/java/com/magamochi/service/MangaImportService.java index d154672..737f5fb 100644 --- a/src/main/java/com/magamochi/service/MangaImportService.java +++ b/src/main/java/com/magamochi/service/MangaImportService.java @@ -4,6 +4,8 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import com.google.common.util.concurrent.RateLimiter; +import com.magamochi.catalog.model.entity.Genre; +import com.magamochi.catalog.model.repository.GenreRepository; import com.magamochi.client.AniListClient; import com.magamochi.client.JikanClient; import com.magamochi.common.exception.NotFoundException; -- 2.49.1 From 5f66490daee67b668ed557802b8c44ac62781571 Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Tue, 17 Mar 2026 20:37:25 -0300 Subject: [PATCH 06/11] refactor: content provider ingestion --- .../{ => common}/config/RabbitConfig.java | 20 +++- .../queue/command/MangaIngestCommand.java | 6 ++ .../controller/ManagementController.java | 28 +----- .../{ => ingestion}/client/FlareClient.java | 2 +- .../controller/IngestionController.java | 33 ++++++- .../model/dto/ContentProviderListDTO.java | 2 +- .../ingestion/model/dto/MangaInfoDTO.java | 6 ++ .../providers/ContentProvider.java | 2 +- .../providers/ContentProviderFactory.java | 2 +- .../providers/ContentProviders.java | 7 +- .../ManualImportContentProvider.java | 2 +- .../ManualImportContentProviderFactory.java | 2 +- .../providers/PagedContentProvider.java | 10 ++ .../PagedContentProviderFactory.java | 2 +- .../providers/impl/MangaDexProvider.java | 8 +- .../impl/MangaLivreBlogProvider.java | 18 ++-- .../providers/impl/MangaLivreProvider.java | 19 ++-- .../providers/impl/PinkRosaScanProvider.java | 16 +-- .../command/ProviderPageIngestCommand.java | 3 + .../consumer/ProviderPageIngestConsumer.java | 21 ++++ .../queue/producer/MangaIngestProducer.java | 23 +++++ .../producer/ProviderPageIngestProducer.java | 23 +++++ .../service/ContentProviderService.java | 10 ++ .../ingestion/service/FlareService.java | 2 +- .../service/FlareSessionManager.java | 3 +- .../service}/FlareSessionRegistry.java | 2 +- .../ingestion/service/IngestionService.java | 51 ++++++++++ .../task/FlareSessionCleanupTask.java | 4 +- .../task/FlareStartupCleanupTask.java | 2 +- .../task/IngestFromContentProvidersTask.java | 31 ++++++ .../ContentProviderMangaInfoResponseDTO.java | 7 -- .../magamochi/model/dto/MangaMessageDTO.java | 3 +- .../service/MangaChapterService.java | 2 +- .../magamochi/service/MangaListService.java | 2 +- .../com/magamochi/service/MangaService.java | 2 +- .../ProviderManualMangaImportService.java | 2 +- .../providers/PagedContentProvider.java | 10 -- .../service/providers/impl/BatoProvider.java | 99 ------------------- .../magamochi/task/UpdateMangaDataTask.java | 30 ------ .../magamochi/task/UpdateMangaListTask.java | 67 ------------- src/main/resources/application.yml | 4 + .../db/migration/V0001__CREATE_TABLES.sql | 1 + .../db/migration/V0002__CONTENT_PROVIDERS.sql | 4 + 43 files changed, 295 insertions(+), 298 deletions(-) rename src/main/java/com/magamochi/{ => common}/config/RabbitConfig.java (79%) create mode 100644 src/main/java/com/magamochi/common/queue/command/MangaIngestCommand.java rename src/main/java/com/magamochi/{ => ingestion}/client/FlareClient.java (98%) create mode 100644 src/main/java/com/magamochi/ingestion/model/dto/MangaInfoDTO.java rename src/main/java/com/magamochi/{service => ingestion}/providers/ContentProvider.java (89%) rename src/main/java/com/magamochi/{service => ingestion}/providers/ContentProviderFactory.java (93%) rename src/main/java/com/magamochi/{service => ingestion}/providers/ContentProviders.java (76%) rename src/main/java/com/magamochi/{service => ingestion}/providers/ManualImportContentProvider.java (67%) rename src/main/java/com/magamochi/{service => ingestion}/providers/ManualImportContentProviderFactory.java (93%) create mode 100644 src/main/java/com/magamochi/ingestion/providers/PagedContentProvider.java rename src/main/java/com/magamochi/{service => ingestion}/providers/PagedContentProviderFactory.java (93%) rename src/main/java/com/magamochi/{service => ingestion}/providers/impl/MangaDexProvider.java (95%) rename src/main/java/com/magamochi/{service => ingestion}/providers/impl/MangaLivreBlogProvider.java (92%) rename src/main/java/com/magamochi/{service => ingestion}/providers/impl/MangaLivreProvider.java (89%) rename src/main/java/com/magamochi/{service => ingestion}/providers/impl/PinkRosaScanProvider.java (90%) create mode 100644 src/main/java/com/magamochi/ingestion/queue/command/ProviderPageIngestCommand.java create mode 100644 src/main/java/com/magamochi/ingestion/queue/consumer/ProviderPageIngestConsumer.java create mode 100644 src/main/java/com/magamochi/ingestion/queue/producer/MangaIngestProducer.java create mode 100644 src/main/java/com/magamochi/ingestion/queue/producer/ProviderPageIngestProducer.java rename src/main/java/com/magamochi/{registry => ingestion/service}/FlareSessionRegistry.java (93%) create mode 100644 src/main/java/com/magamochi/ingestion/service/IngestionService.java create mode 100644 src/main/java/com/magamochi/ingestion/task/IngestFromContentProvidersTask.java delete mode 100644 src/main/java/com/magamochi/model/dto/ContentProviderMangaInfoResponseDTO.java delete mode 100644 src/main/java/com/magamochi/service/providers/PagedContentProvider.java delete mode 100644 src/main/java/com/magamochi/service/providers/impl/BatoProvider.java delete mode 100644 src/main/java/com/magamochi/task/UpdateMangaDataTask.java delete mode 100644 src/main/java/com/magamochi/task/UpdateMangaListTask.java create mode 100644 src/main/resources/db/migration/V0002__CONTENT_PROVIDERS.sql diff --git a/src/main/java/com/magamochi/config/RabbitConfig.java b/src/main/java/com/magamochi/common/config/RabbitConfig.java similarity index 79% rename from src/main/java/com/magamochi/config/RabbitConfig.java rename to src/main/java/com/magamochi/common/config/RabbitConfig.java index 9f920f7..f9c991e 100644 --- a/src/main/java/com/magamochi/config/RabbitConfig.java +++ b/src/main/java/com/magamochi/common/config/RabbitConfig.java @@ -1,4 +1,4 @@ -package com.magamochi.config; +package com.magamochi.common.config; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.connection.ConnectionFactory; @@ -10,6 +10,24 @@ import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { + @Value("${queues.manga-ingest}") + private String mangaIngestQueue; + + @Value("${queues.provider-page-ingest}") + private String providerPageIngestQueue; + + @Bean + public Queue mangaIngestQueue() { + return new Queue(mangaIngestQueue, false); + } + + @Bean + public Queue providerPageIngestQueue() { + return new Queue(providerPageIngestQueue, false); + } + + // TODO: remove unused queues + @Value("${rabbit-mq.queues.manga-data-update}") private String mangaDataUpdateQueue; diff --git a/src/main/java/com/magamochi/common/queue/command/MangaIngestCommand.java b/src/main/java/com/magamochi/common/queue/command/MangaIngestCommand.java new file mode 100644 index 0000000..e29223c --- /dev/null +++ b/src/main/java/com/magamochi/common/queue/command/MangaIngestCommand.java @@ -0,0 +1,6 @@ +package com.magamochi.common.queue.command; + +import jakarta.validation.constraints.NotBlank; + +public record MangaIngestCommand( + long providerId, @NotBlank String mangaTitle, @NotBlank String url) {} diff --git a/src/main/java/com/magamochi/controller/ManagementController.java b/src/main/java/com/magamochi/controller/ManagementController.java index c35ed20..4d8b0c6 100644 --- a/src/main/java/com/magamochi/controller/ManagementController.java +++ b/src/main/java/com/magamochi/controller/ManagementController.java @@ -2,12 +2,12 @@ package com.magamochi.controller; import com.magamochi.client.NtfyClient; import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.ingestion.task.IngestFromContentProvidersTask; import com.magamochi.model.dto.UpdateMangaDataCommand; import com.magamochi.model.repository.UserRepository; import com.magamochi.queue.UpdateMangaDataProducer; import com.magamochi.task.ImageCleanupTask; import com.magamochi.task.MangaFollowUpdateTask; -import com.magamochi.task.UpdateMangaListTask; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/management") @RequiredArgsConstructor public class ManagementController { - private final UpdateMangaListTask updateMangaListTask; + private final IngestFromContentProvidersTask ingestFromContentProvidersTask; private final ImageCleanupTask imageCleanupTask; private final MangaFollowUpdateTask mangaFollowUpdateTask; private final UserRepository userRepository; @@ -35,30 +35,6 @@ public class ManagementController { return DefaultResponseDTO.ok().build(); } - @Operation( - summary = "Queue update manga list", - description = "Queue the retrieval of the manga lists from the content providers", - tags = {"Management"}, - operationId = "updateMangaList") - @PostMapping("update-manga-list") - public DefaultResponseDTO updateMangaList() { - updateMangaListTask.updateMangaList(); - - return DefaultResponseDTO.ok().build(); - } - - @Operation( - summary = "Queue update provider manga list", - description = "Queue the retrieval of the manga list for a specific provider", - tags = {"Management"}, - operationId = "updateProviderMangaList") - @PostMapping("update-provider-manga-list") - public DefaultResponseDTO updateProviderMangaList(@RequestParam Long providerId) { - updateMangaListTask.updateProviderMangaList(providerId); - - return DefaultResponseDTO.ok().build(); - } - @Operation( summary = "Cleanup unused S3 images", description = "Triggers the cleanup of untracked S3 images", diff --git a/src/main/java/com/magamochi/client/FlareClient.java b/src/main/java/com/magamochi/ingestion/client/FlareClient.java similarity index 98% rename from src/main/java/com/magamochi/client/FlareClient.java rename to src/main/java/com/magamochi/ingestion/client/FlareClient.java index 6716757..42e52de 100644 --- a/src/main/java/com/magamochi/client/FlareClient.java +++ b/src/main/java/com/magamochi/ingestion/client/FlareClient.java @@ -1,4 +1,4 @@ -package com.magamochi.client; +package com.magamochi.ingestion.client; import io.github.resilience4j.retry.annotation.Retry; import java.util.List; diff --git a/src/main/java/com/magamochi/ingestion/controller/IngestionController.java b/src/main/java/com/magamochi/ingestion/controller/IngestionController.java index 581b76d..c4539e4 100644 --- a/src/main/java/com/magamochi/ingestion/controller/IngestionController.java +++ b/src/main/java/com/magamochi/ingestion/controller/IngestionController.java @@ -3,18 +3,17 @@ package com.magamochi.ingestion.controller; import com.magamochi.common.dto.DefaultResponseDTO; import com.magamochi.ingestion.model.dto.ContentProviderListDTO; import com.magamochi.ingestion.service.ContentProviderService; +import com.magamochi.ingestion.service.IngestionService; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/ingestion") @RequiredArgsConstructor public class IngestionController { private final ContentProviderService contentProviderService; + private final IngestionService ingestionService; @Operation( summary = "Get a list of content providers", @@ -26,4 +25,30 @@ public class IngestionController { @RequestParam(name = "manualImport", required = false) Boolean manualImport) { return DefaultResponseDTO.ok(contentProviderService.getProviders(manualImport)); } + + @Operation( + summary = "Fetch mangas from a content provider", + description = + "Triggers the ingestion process for a specific content provider, fetching manga data and queuing it for processing.", + tags = {"Ingestion"}, + operationId = "fetchContentProviderMangas") + @PostMapping("/providers/{providerId}/fetch") + public DefaultResponseDTO fetchContentProviderMangas(@PathVariable Long providerId) { + ingestionService.fetchContentProviderMangas(providerId); + + return DefaultResponseDTO.ok().build(); + } + + @Operation( + summary = "Fetch mangas from all content providers", + description = + "Triggers the ingestion process for all content providers, fetching manga data and queuing them for processing.", + tags = {"Ingestion"}, + operationId = "fetchAllContentProviderMangas") + @PostMapping("/providers/fetch") + public DefaultResponseDTO fetchAllContentProviderMangas() { + ingestionService.fetchAllContentProviderMangas(); + + return DefaultResponseDTO.ok().build(); + } } diff --git a/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java b/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java index 1aa266b..b78d549 100644 --- a/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java +++ b/src/main/java/com/magamochi/ingestion/model/dto/ContentProviderListDTO.java @@ -11,7 +11,7 @@ public record ContentProviderListDTO(@NotNull List providers contentProviders.stream().map(ContentProviderDTO::from).toList()); } - record ContentProviderDTO(long id, @NotBlank String name) { + public record ContentProviderDTO(long id, @NotBlank String name) { public static ContentProviderDTO from(ContentProvider contentProvider) { return new ContentProviderDTO(contentProvider.getId(), contentProvider.getName()); } diff --git a/src/main/java/com/magamochi/ingestion/model/dto/MangaInfoDTO.java b/src/main/java/com/magamochi/ingestion/model/dto/MangaInfoDTO.java new file mode 100644 index 0000000..192fb3e --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/model/dto/MangaInfoDTO.java @@ -0,0 +1,6 @@ +package com.magamochi.ingestion.model.dto; + +import com.magamochi.model.enumeration.MangaStatus; +import jakarta.validation.constraints.NotBlank; + +public record MangaInfoDTO(@NotBlank String title, @NotBlank String url, MangaStatus status) {} diff --git a/src/main/java/com/magamochi/service/providers/ContentProvider.java b/src/main/java/com/magamochi/ingestion/providers/ContentProvider.java similarity index 89% rename from src/main/java/com/magamochi/service/providers/ContentProvider.java rename to src/main/java/com/magamochi/ingestion/providers/ContentProvider.java index fb43df0..17eb162 100644 --- a/src/main/java/com/magamochi/service/providers/ContentProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/ContentProvider.java @@ -1,4 +1,4 @@ -package com.magamochi.service.providers; +package com.magamochi.ingestion.providers; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.model.entity.MangaContentProvider; diff --git a/src/main/java/com/magamochi/service/providers/ContentProviderFactory.java b/src/main/java/com/magamochi/ingestion/providers/ContentProviderFactory.java similarity index 93% rename from src/main/java/com/magamochi/service/providers/ContentProviderFactory.java rename to src/main/java/com/magamochi/ingestion/providers/ContentProviderFactory.java index 25dc06f..71fd653 100644 --- a/src/main/java/com/magamochi/service/providers/ContentProviderFactory.java +++ b/src/main/java/com/magamochi/ingestion/providers/ContentProviderFactory.java @@ -1,4 +1,4 @@ -package com.magamochi.service.providers; +package com.magamochi.ingestion.providers; import java.util.Map; import java.util.Objects; diff --git a/src/main/java/com/magamochi/service/providers/ContentProviders.java b/src/main/java/com/magamochi/ingestion/providers/ContentProviders.java similarity index 76% rename from src/main/java/com/magamochi/service/providers/ContentProviders.java rename to src/main/java/com/magamochi/ingestion/providers/ContentProviders.java index d68908b..ad860e6 100644 --- a/src/main/java/com/magamochi/service/providers/ContentProviders.java +++ b/src/main/java/com/magamochi/ingestion/providers/ContentProviders.java @@ -1,9 +1,8 @@ -package com.magamochi.service.providers; +package com.magamochi.ingestion.providers; public class ContentProviders { - public static final String MANGA_LIVRE_TO = "Manga Livre.to"; public static final String MANGA_LIVRE_BLOG = "Manga Livre Blog"; - public static final String MANGA_DEX = "MangaDex"; + public static final String MANGA_LIVRE_TO = "Manga Livre.to"; public static final String PINK_ROSA_SCAN = "Pink Rosa Scan"; - public static final String BATO = "Bato"; + public static final String MANGA_DEX = "MangaDex"; } diff --git a/src/main/java/com/magamochi/service/providers/ManualImportContentProvider.java b/src/main/java/com/magamochi/ingestion/providers/ManualImportContentProvider.java similarity index 67% rename from src/main/java/com/magamochi/service/providers/ManualImportContentProvider.java rename to src/main/java/com/magamochi/ingestion/providers/ManualImportContentProvider.java index 807f98c..1a36387 100644 --- a/src/main/java/com/magamochi/service/providers/ManualImportContentProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/ManualImportContentProvider.java @@ -1,4 +1,4 @@ -package com.magamochi.service.providers; +package com.magamochi.ingestion.providers; public interface ManualImportContentProvider { String getMangaTitle(String value); diff --git a/src/main/java/com/magamochi/service/providers/ManualImportContentProviderFactory.java b/src/main/java/com/magamochi/ingestion/providers/ManualImportContentProviderFactory.java similarity index 93% rename from src/main/java/com/magamochi/service/providers/ManualImportContentProviderFactory.java rename to src/main/java/com/magamochi/ingestion/providers/ManualImportContentProviderFactory.java index a60f858..009c19a 100644 --- a/src/main/java/com/magamochi/service/providers/ManualImportContentProviderFactory.java +++ b/src/main/java/com/magamochi/ingestion/providers/ManualImportContentProviderFactory.java @@ -1,4 +1,4 @@ -package com.magamochi.service.providers; +package com.magamochi.ingestion.providers; import java.util.Map; import java.util.Objects; diff --git a/src/main/java/com/magamochi/ingestion/providers/PagedContentProvider.java b/src/main/java/com/magamochi/ingestion/providers/PagedContentProvider.java new file mode 100644 index 0000000..593724c --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/providers/PagedContentProvider.java @@ -0,0 +1,10 @@ +package com.magamochi.ingestion.providers; + +import com.magamochi.ingestion.model.dto.MangaInfoDTO; +import java.util.List; + +public interface PagedContentProvider { + int getTotalPages(); + + List getMangasFromPage(int page); +} diff --git a/src/main/java/com/magamochi/service/providers/PagedContentProviderFactory.java b/src/main/java/com/magamochi/ingestion/providers/PagedContentProviderFactory.java similarity index 93% rename from src/main/java/com/magamochi/service/providers/PagedContentProviderFactory.java rename to src/main/java/com/magamochi/ingestion/providers/PagedContentProviderFactory.java index 4909c6e..bdddc6f 100644 --- a/src/main/java/com/magamochi/service/providers/PagedContentProviderFactory.java +++ b/src/main/java/com/magamochi/ingestion/providers/PagedContentProviderFactory.java @@ -1,4 +1,4 @@ -package com.magamochi.service.providers; +package com.magamochi.ingestion.providers; import java.util.Map; import java.util.Objects; diff --git a/src/main/java/com/magamochi/service/providers/impl/MangaDexProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/MangaDexProvider.java similarity index 95% rename from src/main/java/com/magamochi/service/providers/impl/MangaDexProvider.java rename to src/main/java/com/magamochi/ingestion/providers/impl/MangaDexProvider.java index 42ba712..5a46e81 100644 --- a/src/main/java/com/magamochi/service/providers/impl/MangaDexProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/MangaDexProvider.java @@ -1,15 +1,15 @@ -package com.magamochi.service.providers.impl; +package com.magamochi.ingestion.providers.impl; import static java.util.Objects.isNull; import com.google.common.util.concurrent.RateLimiter; import com.magamochi.client.MangaDexClient; import com.magamochi.common.exception.UnprocessableException; +import com.magamochi.ingestion.providers.ContentProvider; +import com.magamochi.ingestion.providers.ContentProviders; +import com.magamochi.ingestion.providers.ManualImportContentProvider; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.model.entity.MangaContentProvider; -import com.magamochi.service.providers.ContentProvider; -import com.magamochi.service.providers.ContentProviders; -import com.magamochi.service.providers.ManualImportContentProvider; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; diff --git a/src/main/java/com/magamochi/service/providers/impl/MangaLivreBlogProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java similarity index 92% rename from src/main/java/com/magamochi/service/providers/impl/MangaLivreBlogProvider.java rename to src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java index abc9ae4..f97bdd3 100644 --- a/src/main/java/com/magamochi/service/providers/impl/MangaLivreBlogProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java @@ -1,12 +1,12 @@ -package com.magamochi.service.providers.impl; +package com.magamochi.ingestion.providers.impl; +import com.magamochi.ingestion.model.dto.MangaInfoDTO; +import com.magamochi.ingestion.providers.ContentProvider; +import com.magamochi.ingestion.providers.ContentProviders; +import com.magamochi.ingestion.providers.PagedContentProvider; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.enumeration.MangaStatus; -import com.magamochi.service.providers.ContentProvider; -import com.magamochi.service.providers.ContentProviders; -import com.magamochi.service.providers.PagedContentProvider; import java.io.IOException; import java.util.*; import java.util.regex.Pattern; @@ -101,7 +101,7 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv } @Override - public List getMangasFromPage(Integer page) { + public List getMangasFromPage(int page) { log.info("Getting mangas from {}, page {}", ContentProviders.MANGA_LIVRE_BLOG, page); try { @@ -134,7 +134,7 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv default -> MangaStatus.UNKNOWN; }; - return new ContentProviderMangaInfoResponseDTO(title, url, status); + return new MangaInfoDTO(title, url, status); } catch (Exception e) { return null; } @@ -148,7 +148,7 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv } @Override - public Integer getTotalPages() { + public int getTotalPages() { log.info("Getting total pages for {}", ContentProviders.MANGA_LIVRE_BLOG); try { @@ -166,7 +166,7 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv return pageNumbers.stream().max(Integer::compareTo).orElse(null); } catch (IOException | NoSuchElementException e) { log.error("Error fetching total pages from MangaLivre", e); - return null; + return 0; } } } diff --git a/src/main/java/com/magamochi/service/providers/impl/MangaLivreProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java similarity index 89% rename from src/main/java/com/magamochi/service/providers/impl/MangaLivreProvider.java rename to src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java index b7d766a..e3dbd57 100644 --- a/src/main/java/com/magamochi/service/providers/impl/MangaLivreProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java @@ -1,15 +1,15 @@ -package com.magamochi.service.providers.impl; +package com.magamochi.ingestion.providers.impl; import static java.util.Objects.nonNull; +import com.magamochi.ingestion.model.dto.MangaInfoDTO; +import com.magamochi.ingestion.providers.ContentProvider; +import com.magamochi.ingestion.providers.ContentProviders; +import com.magamochi.ingestion.providers.PagedContentProvider; import com.magamochi.ingestion.service.FlareService; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.enumeration.MangaStatus; -import com.magamochi.service.providers.ContentProvider; -import com.magamochi.service.providers.ContentProviders; -import com.magamochi.service.providers.PagedContentProvider; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -90,7 +90,7 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider } @Override - public List getMangasFromPage(Integer page) { + public List getMangasFromPage(int page) { log.info("Getting mangas from {}, page {}", ContentProviders.MANGA_LIVRE_TO, page); try { @@ -116,8 +116,7 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider } } - return new ContentProviderMangaInfoResponseDTO( - title.trim(), url.trim(), MangaStatus.UNKNOWN); + return new MangaInfoDTO(title.trim(), url.trim(), MangaStatus.UNKNOWN); }) .toList(); } catch (NoSuchElementException e) { @@ -127,7 +126,7 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider } @Override - public Integer getTotalPages() { + public int getTotalPages() { log.info("Getting total pages for {}", ContentProviders.MANGA_LIVRE_TO); try { @@ -140,7 +139,7 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider return (int) Math.ceil((double) totalMangas / MANGAS_PER_PAGE); } catch (NoSuchElementException e) { log.error("Error parsing total pages from MangaLivre", e); - return null; + return 0; } } } diff --git a/src/main/java/com/magamochi/service/providers/impl/PinkRosaScanProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java similarity index 90% rename from src/main/java/com/magamochi/service/providers/impl/PinkRosaScanProvider.java rename to src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java index 1cd6a89..c9e7af1 100644 --- a/src/main/java/com/magamochi/service/providers/impl/PinkRosaScanProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java @@ -1,16 +1,16 @@ -package com.magamochi.service.providers.impl; +package com.magamochi.ingestion.providers.impl; import static java.util.Objects.isNull; import static org.apache.commons.lang3.StringUtils.isBlank; +import com.magamochi.ingestion.model.dto.MangaInfoDTO; +import com.magamochi.ingestion.providers.ContentProvider; +import com.magamochi.ingestion.providers.ContentProviders; +import com.magamochi.ingestion.providers.PagedContentProvider; import com.magamochi.ingestion.service.FlareService; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.enumeration.MangaStatus; -import com.magamochi.service.providers.ContentProvider; -import com.magamochi.service.providers.ContentProviders; -import com.magamochi.service.providers.PagedContentProvider; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -110,12 +110,12 @@ public class PinkRosaScanProvider implements ContentProvider, PagedContentProvid } @Override - public Integer getTotalPages() { + public int getTotalPages() { return 1; } @Override - public List getMangasFromPage(Integer page) { + public List getMangasFromPage(int page) { log.info("Getting mangas from {}", ContentProviders.PINK_ROSA_SCAN); try { @@ -142,7 +142,7 @@ public class PinkRosaScanProvider implements ContentProvider, PagedContentProvid var textElement = linkElement.getElementsByTag("h3"); var title = textElement.text().trim(); - return new ContentProviderMangaInfoResponseDTO(title, url, MangaStatus.UNKNOWN); + return new MangaInfoDTO(title, url, MangaStatus.UNKNOWN); }) .toList(); } catch (NoSuchElementException e) { diff --git a/src/main/java/com/magamochi/ingestion/queue/command/ProviderPageIngestCommand.java b/src/main/java/com/magamochi/ingestion/queue/command/ProviderPageIngestCommand.java new file mode 100644 index 0000000..8f6a395 --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/queue/command/ProviderPageIngestCommand.java @@ -0,0 +1,3 @@ +package com.magamochi.ingestion.queue.command; + +public record ProviderPageIngestCommand(long providerId, int page) {} diff --git a/src/main/java/com/magamochi/ingestion/queue/consumer/ProviderPageIngestConsumer.java b/src/main/java/com/magamochi/ingestion/queue/consumer/ProviderPageIngestConsumer.java new file mode 100644 index 0000000..1fef3cb --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/queue/consumer/ProviderPageIngestConsumer.java @@ -0,0 +1,21 @@ +package com.magamochi.ingestion.queue.consumer; + +import com.magamochi.ingestion.queue.command.ProviderPageIngestCommand; +import com.magamochi.ingestion.service.IngestionService; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class ProviderPageIngestConsumer { + private final IngestionService ingestionService; + + @RabbitListener(queues = "${queues.provider-page-ingest}") + public void receiveProviderPageIngestCommand(ProviderPageIngestCommand command) { + log.info("Received provider page ingest command: {}", command); + ingestionService.fetchProviderPageMangas(command.providerId(), command.page()); + } +} diff --git a/src/main/java/com/magamochi/ingestion/queue/producer/MangaIngestProducer.java b/src/main/java/com/magamochi/ingestion/queue/producer/MangaIngestProducer.java new file mode 100644 index 0000000..b67f334 --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/queue/producer/MangaIngestProducer.java @@ -0,0 +1,23 @@ +package com.magamochi.ingestion.queue.producer; + +import com.magamochi.common.queue.command.MangaIngestCommand; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaIngestProducer { + private final RabbitTemplate rabbitTemplate; + + @Value("${queues.manga-ingest}") + private String mangaIngestQueue; + + public void sendMangaIngestCommand(MangaIngestCommand command) { + rabbitTemplate.convertAndSend(mangaIngestQueue, command); + log.info("Sent manga ingest command: {}", command); + } +} diff --git a/src/main/java/com/magamochi/ingestion/queue/producer/ProviderPageIngestProducer.java b/src/main/java/com/magamochi/ingestion/queue/producer/ProviderPageIngestProducer.java new file mode 100644 index 0000000..2bacfca --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/queue/producer/ProviderPageIngestProducer.java @@ -0,0 +1,23 @@ +package com.magamochi.ingestion.queue.producer; + +import com.magamochi.ingestion.queue.command.ProviderPageIngestCommand; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class ProviderPageIngestProducer { + private final RabbitTemplate rabbitTemplate; + + @Value("${queues.provider-page-ingest}") + private String providerPageIngestQueue; + + public void sendProviderPageIngestCommand(ProviderPageIngestCommand command) { + rabbitTemplate.convertAndSend(providerPageIngestQueue, command); + log.info("Sent provider page ingest command: {}", command); + } +} diff --git a/src/main/java/com/magamochi/ingestion/service/ContentProviderService.java b/src/main/java/com/magamochi/ingestion/service/ContentProviderService.java index fb7cbea..026a31c 100644 --- a/src/main/java/com/magamochi/ingestion/service/ContentProviderService.java +++ b/src/main/java/com/magamochi/ingestion/service/ContentProviderService.java @@ -2,6 +2,7 @@ package com.magamochi.ingestion.service; import static java.util.Objects.nonNull; +import com.magamochi.common.exception.NotFoundException; import com.magamochi.ingestion.model.dto.ContentProviderListDTO; import com.magamochi.ingestion.model.entity.ContentProvider; import com.magamochi.ingestion.model.repository.ContentProviderRepository; @@ -22,4 +23,13 @@ public class ContentProviderService { return ContentProviderListDTO.from(providers); } + + public ContentProvider find(Long contentProviderId) { + return contentProviderRepository + .findById(contentProviderId) + .orElseThrow( + () -> + new NotFoundException( + "Content Provider not found (ID: " + contentProviderId + ").")); + } } diff --git a/src/main/java/com/magamochi/ingestion/service/FlareService.java b/src/main/java/com/magamochi/ingestion/service/FlareService.java index b327e92..f0ab713 100644 --- a/src/main/java/com/magamochi/ingestion/service/FlareService.java +++ b/src/main/java/com/magamochi/ingestion/service/FlareService.java @@ -1,6 +1,6 @@ package com.magamochi.ingestion.service; -import com.magamochi.client.FlareClient; +import com.magamochi.ingestion.client.FlareClient; import lombok.RequiredArgsConstructor; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; diff --git a/src/main/java/com/magamochi/ingestion/service/FlareSessionManager.java b/src/main/java/com/magamochi/ingestion/service/FlareSessionManager.java index 81c7a11..251f01a 100644 --- a/src/main/java/com/magamochi/ingestion/service/FlareSessionManager.java +++ b/src/main/java/com/magamochi/ingestion/service/FlareSessionManager.java @@ -3,9 +3,8 @@ package com.magamochi.ingestion.service; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import com.magamochi.client.FlareClient; +import com.magamochi.ingestion.client.FlareClient; import com.magamochi.ingestion.model.entity.FlareSession; -import com.magamochi.registry.FlareSessionRegistry; import java.time.Duration; import java.time.Instant; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/registry/FlareSessionRegistry.java b/src/main/java/com/magamochi/ingestion/service/FlareSessionRegistry.java similarity index 93% rename from src/main/java/com/magamochi/registry/FlareSessionRegistry.java rename to src/main/java/com/magamochi/ingestion/service/FlareSessionRegistry.java index dbf788c..4242d99 100644 --- a/src/main/java/com/magamochi/registry/FlareSessionRegistry.java +++ b/src/main/java/com/magamochi/ingestion/service/FlareSessionRegistry.java @@ -1,4 +1,4 @@ -package com.magamochi.registry; +package com.magamochi.ingestion.service; import com.magamochi.ingestion.model.entity.FlareSession; import java.util.concurrent.ConcurrentHashMap; diff --git a/src/main/java/com/magamochi/ingestion/service/IngestionService.java b/src/main/java/com/magamochi/ingestion/service/IngestionService.java new file mode 100644 index 0000000..fed47cf --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/service/IngestionService.java @@ -0,0 +1,51 @@ +package com.magamochi.ingestion.service; + +import com.magamochi.common.queue.command.MangaIngestCommand; +import com.magamochi.ingestion.providers.PagedContentProviderFactory; +import com.magamochi.ingestion.queue.command.ProviderPageIngestCommand; +import com.magamochi.ingestion.queue.producer.MangaIngestProducer; +import com.magamochi.ingestion.queue.producer.ProviderPageIngestProducer; +import java.util.stream.IntStream; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class IngestionService { + private final ContentProviderService contentProviderService; + private final PagedContentProviderFactory pagedContentProviderFactory; + + private final ProviderPageIngestProducer providerPageIngestProducer; + private final MangaIngestProducer mangaIngestProducer; + + public void fetchContentProviderMangas(long contentProviderId) { + var contentProvider = contentProviderService.find(contentProviderId); + var pagedContentProvider = + pagedContentProviderFactory.getPagedContentProvider(contentProvider.getName()); + var pages = pagedContentProvider.getTotalPages(); + + IntStream.rangeClosed(1, pages) + .forEach( + page -> + providerPageIngestProducer.sendProviderPageIngestCommand( + new ProviderPageIngestCommand(contentProvider.getId(), page))); + } + + public void fetchProviderPageMangas(long providerId, int page) { + var contentProvider = contentProviderService.find(providerId); + var pagedContentProvider = + pagedContentProviderFactory.getPagedContentProvider(contentProvider.getName()); + + var mangas = pagedContentProvider.getMangasFromPage(page); + + mangas.forEach( + manga -> + mangaIngestProducer.sendMangaIngestCommand( + new MangaIngestCommand(contentProvider.getId(), manga.title(), manga.url()))); + } + + public void fetchAllContentProviderMangas() { + var contentProviders = contentProviderService.getProviders(null); + contentProviders.providers().forEach(dto -> fetchContentProviderMangas(dto.id())); + } +} diff --git a/src/main/java/com/magamochi/ingestion/task/FlareSessionCleanupTask.java b/src/main/java/com/magamochi/ingestion/task/FlareSessionCleanupTask.java index 6e04d46..26610c5 100644 --- a/src/main/java/com/magamochi/ingestion/task/FlareSessionCleanupTask.java +++ b/src/main/java/com/magamochi/ingestion/task/FlareSessionCleanupTask.java @@ -1,7 +1,7 @@ package com.magamochi.ingestion.task; -import com.magamochi.client.FlareClient; -import com.magamochi.registry.FlareSessionRegistry; +import com.magamochi.ingestion.client.FlareClient; +import com.magamochi.ingestion.service.FlareSessionRegistry; import java.time.Duration; import java.time.Instant; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/ingestion/task/FlareStartupCleanupTask.java b/src/main/java/com/magamochi/ingestion/task/FlareStartupCleanupTask.java index d426c63..6e0ba9b 100644 --- a/src/main/java/com/magamochi/ingestion/task/FlareStartupCleanupTask.java +++ b/src/main/java/com/magamochi/ingestion/task/FlareStartupCleanupTask.java @@ -1,6 +1,6 @@ package com.magamochi.ingestion.task; -import com.magamochi.client.FlareClient; +import com.magamochi.ingestion.client.FlareClient; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.boot.context.event.ApplicationReadyEvent; diff --git a/src/main/java/com/magamochi/ingestion/task/IngestFromContentProvidersTask.java b/src/main/java/com/magamochi/ingestion/task/IngestFromContentProvidersTask.java new file mode 100644 index 0000000..aa7d823 --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/task/IngestFromContentProvidersTask.java @@ -0,0 +1,31 @@ +package com.magamochi.ingestion.task; + +import com.magamochi.ingestion.service.IngestionService; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Log4j2 +@Component +@RequiredArgsConstructor +public class IngestFromContentProvidersTask { + @Value("${content-providers.update-enabled}") + private Boolean updateEnabled; + + private final IngestionService ingestionService; + + @Scheduled(cron = "${content-providers.cron-expression}") + public void updateMangaListScheduled() { + if (!updateEnabled) { + return; + } + + log.info("Starting scheduling ingest from Content Providers."); + + ingestionService.fetchAllContentProviderMangas(); + + log.info("Finished scheduling ingest from Content Providers."); + } +} diff --git a/src/main/java/com/magamochi/model/dto/ContentProviderMangaInfoResponseDTO.java b/src/main/java/com/magamochi/model/dto/ContentProviderMangaInfoResponseDTO.java deleted file mode 100644 index 82ced22..0000000 --- a/src/main/java/com/magamochi/model/dto/ContentProviderMangaInfoResponseDTO.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.magamochi.model.dto; - -import com.magamochi.model.enumeration.MangaStatus; -import jakarta.validation.constraints.NotBlank; - -public record ContentProviderMangaInfoResponseDTO( - @NotBlank String title, @NotBlank String url, MangaStatus status) {} diff --git a/src/main/java/com/magamochi/model/dto/MangaMessageDTO.java b/src/main/java/com/magamochi/model/dto/MangaMessageDTO.java index d529faa..acda1c3 100644 --- a/src/main/java/com/magamochi/model/dto/MangaMessageDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaMessageDTO.java @@ -1,6 +1,7 @@ package com.magamochi.model.dto; +import com.magamochi.ingestion.model.dto.MangaInfoDTO; import java.util.List; public record MangaMessageDTO( - String contentProviderName, List mangaInfoResponseDTOs) {} + String contentProviderName, List mangaInfoResponseDTOs) {} diff --git a/src/main/java/com/magamochi/service/MangaChapterService.java b/src/main/java/com/magamochi/service/MangaChapterService.java index 915843a..099973b 100644 --- a/src/main/java/com/magamochi/service/MangaChapterService.java +++ b/src/main/java/com/magamochi/service/MangaChapterService.java @@ -2,6 +2,7 @@ package com.magamochi.service; import com.google.common.util.concurrent.RateLimiter; import com.magamochi.common.exception.UnprocessableException; +import com.magamochi.ingestion.providers.ContentProviderFactory; import com.magamochi.model.dto.MangaChapterArchiveDTO; import com.magamochi.model.dto.MangaChapterImagesDTO; import com.magamochi.model.entity.MangaChapter; @@ -9,7 +10,6 @@ import com.magamochi.model.entity.MangaChapterImage; import com.magamochi.model.enumeration.ArchiveFileType; import com.magamochi.model.repository.MangaChapterImageRepository; import com.magamochi.model.repository.MangaChapterRepository; -import com.magamochi.service.providers.ContentProviderFactory; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryRegistry; import java.io.BufferedInputStream; diff --git a/src/main/java/com/magamochi/service/MangaListService.java b/src/main/java/com/magamochi/service/MangaListService.java index 21e1e08..faf5bc8 100644 --- a/src/main/java/com/magamochi/service/MangaListService.java +++ b/src/main/java/com/magamochi/service/MangaListService.java @@ -2,9 +2,9 @@ package com.magamochi.service; import static java.util.Objects.isNull; +import com.magamochi.ingestion.providers.PagedContentProviderFactory; import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.repository.MangaContentProviderRepository; -import com.magamochi.service.providers.PagedContentProviderFactory; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/service/MangaService.java b/src/main/java/com/magamochi/service/MangaService.java index db0b8f8..8d36731 100644 --- a/src/main/java/com/magamochi/service/MangaService.java +++ b/src/main/java/com/magamochi/service/MangaService.java @@ -4,6 +4,7 @@ import static java.util.Objects.nonNull; import com.magamochi.client.NtfyClient; import com.magamochi.common.exception.NotFoundException; +import com.magamochi.ingestion.providers.ContentProviderFactory; import com.magamochi.model.dto.*; import com.magamochi.model.entity.Manga; import com.magamochi.model.entity.MangaChapter; @@ -12,7 +13,6 @@ import com.magamochi.model.entity.UserMangaFollow; import com.magamochi.model.repository.*; import com.magamochi.model.specification.MangaSpecification; import com.magamochi.queue.MangaChapterDownloadProducer; -import com.magamochi.service.providers.ContentProviderFactory; import java.util.Comparator; import java.util.List; import java.util.Set; diff --git a/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java b/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java index 1b88b71..b23e908 100644 --- a/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java +++ b/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java @@ -6,11 +6,11 @@ import static java.util.Objects.nonNull; import com.magamochi.common.exception.NotFoundException; import com.magamochi.ingestion.model.entity.ContentProvider; import com.magamochi.ingestion.model.repository.ContentProviderRepository; +import com.magamochi.ingestion.providers.ManualImportContentProviderFactory; import com.magamochi.model.dto.ImportMangaResponseDTO; import com.magamochi.model.dto.ImportRequestDTO; import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.repository.MangaContentProviderRepository; -import com.magamochi.service.providers.ManualImportContentProviderFactory; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/service/providers/PagedContentProvider.java b/src/main/java/com/magamochi/service/providers/PagedContentProvider.java deleted file mode 100644 index 8ff4c82..0000000 --- a/src/main/java/com/magamochi/service/providers/PagedContentProvider.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.magamochi.service.providers; - -import com.magamochi.model.dto.ContentProviderMangaInfoResponseDTO; -import java.util.List; - -public interface PagedContentProvider { - Integer getTotalPages(); - - List getMangasFromPage(Integer page); -} diff --git a/src/main/java/com/magamochi/service/providers/impl/BatoProvider.java b/src/main/java/com/magamochi/service/providers/impl/BatoProvider.java deleted file mode 100644 index 99328ec..0000000 --- a/src/main/java/com/magamochi/service/providers/impl/BatoProvider.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.magamochi.service.providers.impl; - -import static java.util.Objects.isNull; - -import com.magamochi.common.exception.UnprocessableException; -import com.magamochi.ingestion.service.FlareService; -import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.entity.MangaContentProvider; -import com.magamochi.service.providers.ContentProvider; -import com.magamochi.service.providers.ContentProviders; -import com.magamochi.service.providers.ManualImportContentProvider; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.stereotype.Service; - -@Log4j2 -@Service(ContentProviders.BATO) -@RequiredArgsConstructor -public class BatoProvider implements ContentProvider, ManualImportContentProvider { - private static final String URL = "https://battwo.com"; - - private final FlareService flareService; - - @Override - public List getAvailableChapters( - MangaContentProvider provider) { - try { - var document = - flareService.getContentAsJsoupDocument(provider.getUrl(), ContentProviders.BATO); - - // Direct selector for chapter links - var chapterLinks = document.select("div.scrollable-panel a[href*=/title/]"); - - // TODO: fix chapter and language code - return chapterLinks.stream() - .map( - chapterLink -> - new ContentProviderMangaChapterResponseDTO( - chapterLink.text(), chapterLink.attr("href"), null, null)) - .toList(); - } catch (Exception e) { - log.warn(e.getMessage()); - return null; - } - } - - @Override - public Map getChapterImagesUrls(String chapterUrl) { - try { - var document = - flareService.getContentAsJsoupDocument( - URL + chapterUrl + "?load=2", ContentProviders.BATO); - - // Select all chapter page images - var imgElements = document.select("img.z-10.w-full.h-full"); - - List imageUrls = new ArrayList<>(); - for (var img : imgElements) { - String src = img.attr("src"); - - // Normalize if needed - if (!src.startsWith("http")) { - src = "https://battwo.com" + src; - } - - imageUrls.add(src); - } - - return IntStream.range(0, imageUrls.size()) - .boxed() - .collect( - Collectors.toMap( - i -> i, imageUrls::get, (existing, replacement) -> existing, LinkedHashMap::new)); - - } catch (Exception e) { - log.warn(e.getMessage()); - return null; - } - } - - @Override - public String getMangaTitle(String value) { - var document = flareService.getContentAsJsoupDocument(value, ContentProviders.BATO); - - var titleElement = document.selectFirst("h3 a[href*=/title/]"); - if (isNull(titleElement)) { - titleElement = document.selectFirst("h3.item-title > a"); - - if (isNull(titleElement)) { - throw new UnprocessableException("Manga title not found for url: " + value); - } - } - - return titleElement.text(); - } -} diff --git a/src/main/java/com/magamochi/task/UpdateMangaDataTask.java b/src/main/java/com/magamochi/task/UpdateMangaDataTask.java deleted file mode 100644 index ff67b48..0000000 --- a/src/main/java/com/magamochi/task/UpdateMangaDataTask.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.magamochi.task; - -import static java.util.Objects.isNull; - -import com.magamochi.model.dto.UpdateMangaDataCommand; -import com.magamochi.model.repository.MangaRepository; -import com.magamochi.queue.UpdateMangaDataProducer; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -@Log4j2 -@Component -@RequiredArgsConstructor -public class UpdateMangaDataTask { - private final MangaRepository mangaRepository; - private final UpdateMangaDataProducer updateMangaDataProducer; - - @Scheduled(cron = "@daily") - public void updateMangaData() { - var mangas = - mangaRepository.findAll().stream().filter(manga -> isNull(manga.getScore())).toList(); - - mangas.forEach( - manga -> - updateMangaDataProducer.sendUpdateMangaDataCommand( - new UpdateMangaDataCommand(manga.getId()))); - } -} diff --git a/src/main/java/com/magamochi/task/UpdateMangaListTask.java b/src/main/java/com/magamochi/task/UpdateMangaListTask.java deleted file mode 100644 index 466776a..0000000 --- a/src/main/java/com/magamochi/task/UpdateMangaListTask.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.magamochi.task; - -import com.magamochi.common.exception.NotFoundException; -import com.magamochi.ingestion.model.repository.ContentProviderRepository; -import com.magamochi.model.dto.MangaListUpdateCommand; -import com.magamochi.queue.UpdateMangaListProducer; -import com.magamochi.service.providers.PagedContentProvider; -import com.magamochi.service.providers.PagedContentProviderFactory; -import java.util.stream.IntStream; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -@Log4j2 -@Component -@RequiredArgsConstructor -public class UpdateMangaListTask { - @Value("${content-providers.update-enabled}") - private Boolean updateEnabled; - - private final PagedContentProviderFactory contentProviderFactory; - private final UpdateMangaListProducer updateMangaListProducer; - private final ContentProviderRepository contentProviderRepository; - - @Scheduled(cron = "${content-providers.cron-expression}") - public void updateMangaListScheduled() { - if (!updateEnabled) { - return; - } - - updateMangaList(); - } - - public void updateMangaList() { - log.info("Queuing manga list updates..."); - - var contentProviders = contentProviderFactory.getContentProviders(); - contentProviders.forEach(this::updateProviderMangaList); - } - - public void updateProviderMangaList(Long providerId) { - var provider = - contentProviderRepository - .findById(providerId) - .orElseThrow(() -> new NotFoundException("Provider not found")); - var contentProvider = contentProviderFactory.getPagedContentProvider(provider.getName()); - - updateProviderMangaList(provider.getName(), contentProvider); - } - - private void updateProviderMangaList( - String contentProviderName, PagedContentProvider contentProvider) { - log.info("Getting total pages for provider {}", contentProviderName); - - var pages = contentProvider.getTotalPages(); - - IntStream.rangeClosed(1, pages) - .forEach( - page -> - updateMangaListProducer.sendUpdateMangaListCommand( - new MangaListUpdateCommand(contentProviderName, page))); - - log.info("Manga list update queued for content provider {}.", contentProviderName); - } -} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2ab855a..f9395d2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -88,6 +88,10 @@ resilience4j: - java.io.IOException - java.net.SocketTimeoutException +queues: + manga-ingest: ${MANGA_INGEST_QUEUE:mangaIngest} + provider-page-ingest: ${PROVIDER_PAGE_INGEST_QUEUE:providerPageIngest} + rabbit-mq: queues: manga-data-update: ${MANGA_DATA_UPDATE_QUEUE:mangaDataUpdateQueue} diff --git a/src/main/resources/db/migration/V0001__CREATE_TABLES.sql b/src/main/resources/db/migration/V0001__CREATE_TABLES.sql index 8f7aa62..df0130f 100644 --- a/src/main/resources/db/migration/V0001__CREATE_TABLES.sql +++ b/src/main/resources/db/migration/V0001__CREATE_TABLES.sql @@ -42,6 +42,7 @@ CREATE TABLE content_providers ( id BIGSERIAL NOT NULL PRIMARY KEY, name VARCHAR NOT NULL, + url VARCHAR NOT NULL, active BOOLEAN NOT NULL DEFAULT TRUE, supports_chapter_fetch BOOLEAN NOT NULL DEFAULT TRUE, manual_import BOOLEAN NOT NULL DEFAULT FALSE, diff --git a/src/main/resources/db/migration/V0002__CONTENT_PROVIDERS.sql b/src/main/resources/db/migration/V0002__CONTENT_PROVIDERS.sql new file mode 100644 index 0000000..dce63ff --- /dev/null +++ b/src/main/resources/db/migration/V0002__CONTENT_PROVIDERS.sql @@ -0,0 +1,4 @@ +INSERT INTO content_providers(name, url, active, supports_chapter_fetch, manual_import) +VALUES ('Manga Livre Blog', 'https://mangalivre.blog', TRUE, TRUE, FALSE), + ('Manga Livre.to', 'https://mangalivre.to', TRUE, TRUE, FALSE), + ('Pink Rosa Scan', 'https://scanpinkrosa.blogspot.com', TRUE, TRUE, FALSE); \ No newline at end of file -- 2.49.1 From 0feae82a9b4242a3c2d52d343a6242ce8362c513 Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Tue, 17 Mar 2026 21:39:18 -0300 Subject: [PATCH 07/11] refactor: manga ingestion --- pom.xml | 2 + .../catalog/client/AniListClient.java | 64 ++ .../{ => catalog}/client/JikanClient.java | 10 +- .../catalog/controller/CatalogController.java | 34 +- .../MangaIngestReviewController.java | 51 + .../{ => catalog}/model/dto/MangaDTO.java | 9 +- .../catalog/model/dto/MangaDataDTO.java | 20 + .../model/dto/MangaIngestReviewDTO.java} | 14 +- .../{ => catalog}/model/dto/MangaListDTO.java | 7 +- .../model/dto/MangaListFilterDTO.java | 2 +- .../{ => catalog}/model/entity/Author.java | 4 +- .../magamochi/catalog/model/entity/Genre.java | 3 - .../{ => catalog}/model/entity/Language.java | 2 +- .../{ => catalog}/model/entity/Manga.java | 30 +- .../model/entity/MangaAlternativeTitle.java | 2 +- .../model/entity/MangaAuthor.java | 2 +- .../model/entity/MangaGenre.java | 3 +- .../model/entity/MangaIngestReview.java} | 8 +- .../catalog/model/enumeration/MangaState.java | 6 + .../model/enumeration/MangaStatus.java | 2 +- .../model/repository/AuthorRepository.java | 6 + .../model/repository/GenreRepository.java | 7 +- .../model/repository/LanguageRepository.java | 4 +- .../MangaAlternativeTitlesRepository.java | 4 +- .../repository/MangaAuthorRepository.java | 8 +- .../MangaContentProviderRepository.java | 10 + .../repository/MangaGenreRepository.java | 6 +- .../MangaIngestReviewRepository.java | 8 + .../model/repository/MangaRepository.java | 4 +- .../queue/command/MangaUpdateCommand.java | 3 + .../queue/consumer/MangaIngestConsumer.java | 21 + .../queue/consumer/MangaUpdateConsumer.java | 21 + .../queue/producer/MangaUpdateProducer.java | 23 + .../catalog/service/AniListService.java | 179 ++++ .../service/LanguageService.java | 6 +- .../service/MangaIngestReviewService.java | 68 ++ .../catalog/service/MangaIngestService.java | 101 ++ .../service/MangaResolutionService.java | 133 +++ .../catalog/service/MangaService.java | 81 ++ .../catalog/service/MangaUpdateService.java | 91 ++ .../catalog/service/MyAnimeListService.java | 88 ++ .../service/TitleMatcherService.java | 39 +- .../{ => catalog}/util/DoubleUtil.java | 2 +- .../com/magamochi/client/AniListClient.java | 51 - .../magamochi/common/config/RabbitConfig.java | 16 + .../exception/GlobalExceptionHandler.java | 2 +- .../{ => model}/dto/DefaultResponseDTO.java | 2 +- .../{ => model}/dto/ErrorResponseDTO.java | 2 +- .../common/model/enumeration/ContentType.java | 5 + .../queue/command/ImageFetchCommand.java | 5 + .../queue/producer/ImageFetchProducer.java | 23 + .../magamochi/config/RateLimiterConfig.java | 5 + .../controller/AuthenticationController.java | 2 +- .../controller/ManagementController.java | 17 +- .../controller/MangaChapterController.java | 2 +- .../magamochi/controller/MangaController.java | 46 +- .../controller/MangaImportController.java | 8 +- .../MangaImportReviewController.java | 51 - .../UserFavoriteMangaController.java | 2 +- .../controller/IngestionController.java | 2 +- .../ingestion/model/dto/MangaInfoDTO.java | 3 +- .../impl/MangaLivreBlogProvider.java | 15 +- .../providers/impl/MangaLivreProvider.java | 3 +- .../providers/impl/PinkRosaScanProvider.java | 3 +- .../com/magamochi/model/dto/LanguageDTO.java | 2 +- .../model/dto/MangaListUpdateCommand.java | 3 - .../magamochi/model/dto/MangaMessageDTO.java | 7 - .../model/dto/TitleMatchRequestDTO.java | 15 - .../model/dto/TitleMatchResponseDTO.java | 12 - .../model/dto/UpdateMangaDataCommand.java | 3 - .../magamochi/model/entity/MangaChapter.java | 1 + .../model/entity/MangaContentProvider.java | 1 + .../model/entity/UserFavoriteManga.java | 1 + .../model/entity/UserMangaFollow.java | 1 + .../model/repository/AuthorRepository.java | 11 - .../MangaContentProviderRepository.java | 15 - .../MangaImportReviewRepository.java | 8 - .../UserFavoriteMangaRepository.java | 2 +- .../repository/UserMangaFollowRepository.java | 2 +- .../specification/MangaSpecification.java | 6 +- .../queue/UpdateMangaDataConsumer.java | 21 - .../queue/UpdateMangaDataProducer.java | 23 - .../UpdateMangaFollowChapterListConsumer.java | 6 +- .../queue/UpdateMangaListConsumer.java | 21 - .../queue/UpdateMangaListProducer.java | 23 - .../service/MangaCreationService.java | 163 ---- .../service/MangaImportReviewService.java | 86 -- .../magamochi/service/MangaImportService.java | 872 +++++++++--------- .../magamochi/service/MangaListService.java | 58 -- ...MangaService.java => OldMangaService.java} | 64 +- .../ProviderManualMangaImportService.java | 71 +- .../service/UserFavoriteMangaService.java | 4 +- .../magamochi/task/MangaFollowUpdateTask.java | 4 +- src/main/resources/application.yml | 2 + .../db/migration/V0001__CREATE_TABLES.sql | 15 +- 95 files changed, 1688 insertions(+), 1288 deletions(-) create mode 100644 src/main/java/com/magamochi/catalog/client/AniListClient.java rename src/main/java/com/magamochi/{ => catalog}/client/JikanClient.java (88%) create mode 100644 src/main/java/com/magamochi/catalog/controller/MangaIngestReviewController.java rename src/main/java/com/magamochi/{ => catalog}/model/dto/MangaDTO.java (91%) create mode 100644 src/main/java/com/magamochi/catalog/model/dto/MangaDataDTO.java rename src/main/java/com/magamochi/{model/dto/ImportReviewDTO.java => catalog/model/dto/MangaIngestReviewDTO.java} (57%) rename src/main/java/com/magamochi/{ => catalog}/model/dto/MangaListDTO.java (87%) rename src/main/java/com/magamochi/{ => catalog}/model/dto/MangaListFilterDTO.java (81%) rename src/main/java/com/magamochi/{ => catalog}/model/entity/Author.java (91%) rename src/main/java/com/magamochi/{ => catalog}/model/entity/Language.java (87%) rename src/main/java/com/magamochi/{ => catalog}/model/entity/Manga.java (73%) rename src/main/java/com/magamochi/{ => catalog}/model/entity/MangaAlternativeTitle.java (89%) rename src/main/java/com/magamochi/{ => catalog}/model/entity/MangaAuthor.java (90%) rename src/main/java/com/magamochi/{ => catalog}/model/entity/MangaGenre.java (82%) rename src/main/java/com/magamochi/{model/entity/MangaImportReview.java => catalog/model/entity/MangaIngestReview.java} (78%) create mode 100644 src/main/java/com/magamochi/catalog/model/enumeration/MangaState.java rename src/main/java/com/magamochi/{ => catalog}/model/enumeration/MangaStatus.java (63%) create mode 100644 src/main/java/com/magamochi/catalog/model/repository/AuthorRepository.java rename src/main/java/com/magamochi/{ => catalog}/model/repository/LanguageRepository.java (69%) rename src/main/java/com/magamochi/{ => catalog}/model/repository/MangaAlternativeTitlesRepository.java (60%) rename src/main/java/com/magamochi/{ => catalog}/model/repository/MangaAuthorRepository.java (55%) create mode 100644 src/main/java/com/magamochi/catalog/model/repository/MangaContentProviderRepository.java rename src/main/java/com/magamochi/{ => catalog}/model/repository/MangaGenreRepository.java (65%) create mode 100644 src/main/java/com/magamochi/catalog/model/repository/MangaIngestReviewRepository.java rename src/main/java/com/magamochi/{ => catalog}/model/repository/MangaRepository.java (83%) create mode 100644 src/main/java/com/magamochi/catalog/queue/command/MangaUpdateCommand.java create mode 100644 src/main/java/com/magamochi/catalog/queue/consumer/MangaIngestConsumer.java create mode 100644 src/main/java/com/magamochi/catalog/queue/consumer/MangaUpdateConsumer.java create mode 100644 src/main/java/com/magamochi/catalog/queue/producer/MangaUpdateProducer.java create mode 100644 src/main/java/com/magamochi/catalog/service/AniListService.java rename src/main/java/com/magamochi/{ => catalog}/service/LanguageService.java (75%) create mode 100644 src/main/java/com/magamochi/catalog/service/MangaIngestReviewService.java create mode 100644 src/main/java/com/magamochi/catalog/service/MangaIngestService.java create mode 100644 src/main/java/com/magamochi/catalog/service/MangaResolutionService.java create mode 100644 src/main/java/com/magamochi/catalog/service/MangaService.java create mode 100644 src/main/java/com/magamochi/catalog/service/MangaUpdateService.java create mode 100644 src/main/java/com/magamochi/catalog/service/MyAnimeListService.java rename src/main/java/com/magamochi/{ => catalog}/service/TitleMatcherService.java (55%) rename src/main/java/com/magamochi/{ => catalog}/util/DoubleUtil.java (92%) delete mode 100644 src/main/java/com/magamochi/client/AniListClient.java rename src/main/java/com/magamochi/common/{ => model}/dto/DefaultResponseDTO.java (94%) rename src/main/java/com/magamochi/common/{ => model}/dto/ErrorResponseDTO.java (84%) create mode 100644 src/main/java/com/magamochi/common/model/enumeration/ContentType.java create mode 100644 src/main/java/com/magamochi/common/queue/command/ImageFetchCommand.java create mode 100644 src/main/java/com/magamochi/common/queue/producer/ImageFetchProducer.java delete mode 100644 src/main/java/com/magamochi/controller/MangaImportReviewController.java delete mode 100644 src/main/java/com/magamochi/model/dto/MangaListUpdateCommand.java delete mode 100644 src/main/java/com/magamochi/model/dto/MangaMessageDTO.java delete mode 100644 src/main/java/com/magamochi/model/dto/TitleMatchRequestDTO.java delete mode 100644 src/main/java/com/magamochi/model/dto/TitleMatchResponseDTO.java delete mode 100644 src/main/java/com/magamochi/model/dto/UpdateMangaDataCommand.java delete mode 100644 src/main/java/com/magamochi/model/repository/AuthorRepository.java delete mode 100644 src/main/java/com/magamochi/model/repository/MangaContentProviderRepository.java delete mode 100644 src/main/java/com/magamochi/model/repository/MangaImportReviewRepository.java delete mode 100644 src/main/java/com/magamochi/queue/UpdateMangaDataConsumer.java delete mode 100644 src/main/java/com/magamochi/queue/UpdateMangaDataProducer.java delete mode 100644 src/main/java/com/magamochi/queue/UpdateMangaListConsumer.java delete mode 100644 src/main/java/com/magamochi/queue/UpdateMangaListProducer.java delete mode 100644 src/main/java/com/magamochi/service/MangaCreationService.java delete mode 100644 src/main/java/com/magamochi/service/MangaImportReviewService.java delete mode 100644 src/main/java/com/magamochi/service/MangaListService.java rename src/main/java/com/magamochi/service/{MangaService.java => OldMangaService.java} (71%) diff --git a/pom.xml b/pom.xml index 5a0bfa7..799c7d7 100644 --- a/pom.xml +++ b/pom.xml @@ -145,6 +145,8 @@ lombok + 22 + 22 diff --git a/src/main/java/com/magamochi/catalog/client/AniListClient.java b/src/main/java/com/magamochi/catalog/client/AniListClient.java new file mode 100644 index 0000000..754fa8f --- /dev/null +++ b/src/main/java/com/magamochi/catalog/client/AniListClient.java @@ -0,0 +1,64 @@ +package com.magamochi.catalog.client; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient(name = "aniList", url = "https://graphql.anilist.co") +public interface AniListClient { + @PostMapping + MangaResponse getManga(@RequestBody GraphQLRequest request); + + @PostMapping + MangaSearchResponse searchManga(@RequestBody SearchGraphQLRequest request); + + record GraphQLRequest(String query, Variables variables) { + public record Variables(Long id) {} + } + + record MangaResponse(Data data) { + public record Data(Manga Media) {} + } + + record SearchGraphQLRequest(String query, SearchVariables variables) { + public record SearchVariables(String search) {} + } + + record MangaSearchResponse(SearchData data) { + public record SearchData(@JsonProperty("Page") Page page) {} + + public record Page(List media) {} + } + + record Manga( + Long id, + Long idMal, + Title title, + String status, + String description, + Integer chapters, + Integer averageScore, + CoverImage coverImage, + List genres, + FuzzyDate startDate, + FuzzyDate endDate, + StaffConnection staff) { + + public record Title( + String romaji, String english, @JsonProperty("native") String nativeTitle) {} + + public record CoverImage(String large) {} + + public record FuzzyDate(Integer year, Integer month, Integer day) {} + + public record StaffConnection(List edges) { + public record StaffEdge(String role, Staff node) { + public record Staff(Name name) { + public record Name(String full) {} + } + } + } + } +} diff --git a/src/main/java/com/magamochi/client/JikanClient.java b/src/main/java/com/magamochi/catalog/client/JikanClient.java similarity index 88% rename from src/main/java/com/magamochi/client/JikanClient.java rename to src/main/java/com/magamochi/catalog/client/JikanClient.java index 2cc1495..9c27e53 100644 --- a/src/main/java/com/magamochi/client/JikanClient.java +++ b/src/main/java/com/magamochi/catalog/client/JikanClient.java @@ -1,6 +1,5 @@ -package com.magamochi.client; +package com.magamochi.catalog.client; -import io.github.resilience4j.retry.annotation.Retry; import java.time.OffsetDateTime; import java.util.List; import org.springframework.cloud.openfeign.FeignClient; @@ -9,7 +8,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "jikan", url = "https://api.jikan.moe/v4/manga") -@Retry(name = "JikanRetry") public interface JikanClient { @GetMapping SearchResponse mangaSearch(@RequestParam String q); @@ -30,10 +28,10 @@ public interface JikanClient { String title, List title_synonyms, String status, - boolean publishing, + Boolean publishing, String synopsis, - float score, - int chapters, + Float score, + Integer chapters, PublishData published, List authors, List genres) { diff --git a/src/main/java/com/magamochi/catalog/controller/CatalogController.java b/src/main/java/com/magamochi/catalog/controller/CatalogController.java index a2bd6b9..7e1a21e 100644 --- a/src/main/java/com/magamochi/catalog/controller/CatalogController.java +++ b/src/main/java/com/magamochi/catalog/controller/CatalogController.java @@ -1,11 +1,20 @@ package com.magamochi.catalog.controller; import com.magamochi.catalog.model.dto.GenreDTO; +import com.magamochi.catalog.model.dto.MangaDTO; +import com.magamochi.catalog.model.dto.MangaListDTO; +import com.magamochi.catalog.model.dto.MangaListFilterDTO; import com.magamochi.catalog.service.GenreService; -import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.catalog.service.MangaService; +import com.magamochi.common.model.dto.DefaultResponseDTO; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; @RestController @@ -13,6 +22,7 @@ import org.springframework.web.bind.annotation.*; @RequiredArgsConstructor public class CatalogController { private final GenreService genreService; + private final MangaService mangaService; @Operation( summary = "Get a list of manga genres", @@ -23,4 +33,26 @@ public class CatalogController { public DefaultResponseDTO> getGenres() { return DefaultResponseDTO.ok(genreService.getGenres()); } + + @Operation( + summary = "Get a list of mangas", + description = "Retrieve a list of mangas with their details.", + tags = {"Catalog"}, + operationId = "getMangas") + @GetMapping("/mangas") + public DefaultResponseDTO> getMangas( + @ParameterObject MangaListFilterDTO filterDTO, + @Parameter(hidden = true) @ParameterObject @PageableDefault Pageable pageable) { + return DefaultResponseDTO.ok(mangaService.get(filterDTO, pageable)); + } + + @Operation( + summary = "Get the details of a manga", + description = "Get the details of a manga by its ID", + tags = {"Catalog"}, + operationId = "getManga") + @GetMapping("/mangas/{mangaId}") + public DefaultResponseDTO getManga(@PathVariable Long mangaId) { + return DefaultResponseDTO.ok(mangaService.get(mangaId)); + } } diff --git a/src/main/java/com/magamochi/catalog/controller/MangaIngestReviewController.java b/src/main/java/com/magamochi/catalog/controller/MangaIngestReviewController.java new file mode 100644 index 0000000..da269bb --- /dev/null +++ b/src/main/java/com/magamochi/catalog/controller/MangaIngestReviewController.java @@ -0,0 +1,51 @@ +package com.magamochi.catalog.controller; + +import com.magamochi.catalog.model.dto.MangaIngestReviewDTO; +import com.magamochi.catalog.service.MangaIngestReviewService; +import com.magamochi.common.model.dto.DefaultResponseDTO; +import io.swagger.v3.oas.annotations.Operation; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/catalog") +@RequiredArgsConstructor +public class MangaIngestReviewController { + private final MangaIngestReviewService mangaIngestReviewService; + + @Operation( + summary = "Get list of pending manga ingest reviews", + description = "Get list of pending manga ingest reviews.", + tags = {"Manga Ingest Review"}, + operationId = "getMangaIngestReviews") + @GetMapping("/ingest-reviews") + public DefaultResponseDTO> getMangaIngestReviews() { + return DefaultResponseDTO.ok(mangaIngestReviewService.get()); + } + + @Operation( + summary = "Delete pending manga ingest review", + description = "Delete pending manga ingest review by ID.", + tags = {"Manga Ingest Review"}, + operationId = "deleteMangaIngestReview") + @DeleteMapping("/ingest-reviews/{id}") + public DefaultResponseDTO deleteMangaIngestReview(@PathVariable Long id) { + mangaIngestReviewService.deleteIngestReview(id); + + return DefaultResponseDTO.ok().build(); + } + + @Operation( + summary = "Resolve manga ingest review", + description = "Resolve manga ingest review by ID.", + tags = {"Manga Ingest Review"}, + operationId = "resolveMangaIngestReview") + @PostMapping("/ingest-reviews") + public DefaultResponseDTO resolveMangaIngestReview( + @RequestParam Long id, @RequestParam String malId) { + mangaIngestReviewService.resolveImportReview(id, malId); + + return DefaultResponseDTO.ok().build(); + } +} diff --git a/src/main/java/com/magamochi/model/dto/MangaDTO.java b/src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java similarity index 91% rename from src/main/java/com/magamochi/model/dto/MangaDTO.java rename to src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java index 1f339b1..0b8a6af 100644 --- a/src/main/java/com/magamochi/model/dto/MangaDTO.java +++ b/src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java @@ -1,9 +1,10 @@ -package com.magamochi.model.dto; +package com.magamochi.catalog.model.dto; import static java.util.Objects.isNull; -import com.magamochi.model.entity.Manga; -import com.magamochi.model.entity.MangaAlternativeTitle; +import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.catalog.model.entity.MangaAlternativeTitle; +import com.magamochi.catalog.model.enumeration.MangaStatus; import com.magamochi.model.entity.MangaChapter; import com.magamochi.model.entity.MangaContentProvider; import jakarta.validation.constraints.NotBlank; @@ -15,7 +16,7 @@ public record MangaDTO( @NotNull Long id, @NotBlank String title, String coverImageKey, - String status, + MangaStatus status, OffsetDateTime publishedFrom, OffsetDateTime publishedTo, String synopsis, diff --git a/src/main/java/com/magamochi/catalog/model/dto/MangaDataDTO.java b/src/main/java/com/magamochi/catalog/model/dto/MangaDataDTO.java new file mode 100644 index 0000000..b8c7663 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/model/dto/MangaDataDTO.java @@ -0,0 +1,20 @@ +package com.magamochi.catalog.model.dto; + +import com.magamochi.catalog.model.enumeration.MangaStatus; +import java.time.OffsetDateTime; +import java.util.List; +import lombok.Builder; + +@Builder +public record MangaDataDTO( + String title, + String synopsis, + MangaStatus status, + Double score, + OffsetDateTime publishedFrom, + OffsetDateTime publishedTo, + Integer chapterCount, + List authors, + List genres, + List alternativeTitles, + String coverImageUrl) {} diff --git a/src/main/java/com/magamochi/model/dto/ImportReviewDTO.java b/src/main/java/com/magamochi/catalog/model/dto/MangaIngestReviewDTO.java similarity index 57% rename from src/main/java/com/magamochi/model/dto/ImportReviewDTO.java rename to src/main/java/com/magamochi/catalog/model/dto/MangaIngestReviewDTO.java index 71dc51f..c731389 100644 --- a/src/main/java/com/magamochi/model/dto/ImportReviewDTO.java +++ b/src/main/java/com/magamochi/catalog/model/dto/MangaIngestReviewDTO.java @@ -1,21 +1,21 @@ -package com.magamochi.model.dto; +package com.magamochi.catalog.model.dto; -import com.magamochi.model.entity.MangaImportReview; +import com.magamochi.catalog.model.entity.MangaIngestReview; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.time.Instant; -public record ImportReviewDTO( +public record MangaIngestReviewDTO( @NotNull Long id, @NotBlank String title, - @NotBlank String providerName, + @NotBlank String contentProviderName, String externalUrl, @NotBlank String reason, @NotNull Instant createdAt) { - public static ImportReviewDTO from(MangaImportReview review) { - return new ImportReviewDTO( + public static MangaIngestReviewDTO from(MangaIngestReview review) { + return new MangaIngestReviewDTO( review.getId(), - review.getTitle(), + review.getMangaTitle(), review.getContentProvider().getName(), review.getUrl(), "Title match not found", diff --git a/src/main/java/com/magamochi/model/dto/MangaListDTO.java b/src/main/java/com/magamochi/catalog/model/dto/MangaListDTO.java similarity index 87% rename from src/main/java/com/magamochi/model/dto/MangaListDTO.java rename to src/main/java/com/magamochi/catalog/model/dto/MangaListDTO.java index c98e7fb..e68b8fc 100644 --- a/src/main/java/com/magamochi/model/dto/MangaListDTO.java +++ b/src/main/java/com/magamochi/catalog/model/dto/MangaListDTO.java @@ -1,8 +1,9 @@ -package com.magamochi.model.dto; +package com.magamochi.catalog.model.dto; import static java.util.Objects.nonNull; -import com.magamochi.model.entity.Manga; +import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.catalog.model.enumeration.MangaStatus; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.time.OffsetDateTime; @@ -12,7 +13,7 @@ public record MangaListDTO( @NotNull Long id, @NotBlank String title, String coverImageKey, - String status, + MangaStatus status, OffsetDateTime publishedFrom, OffsetDateTime publishedTo, Integer providerCount, diff --git a/src/main/java/com/magamochi/model/dto/MangaListFilterDTO.java b/src/main/java/com/magamochi/catalog/model/dto/MangaListFilterDTO.java similarity index 81% rename from src/main/java/com/magamochi/model/dto/MangaListFilterDTO.java rename to src/main/java/com/magamochi/catalog/model/dto/MangaListFilterDTO.java index dfd670e..c92bdb6 100644 --- a/src/main/java/com/magamochi/model/dto/MangaListFilterDTO.java +++ b/src/main/java/com/magamochi/catalog/model/dto/MangaListFilterDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.model.dto; +package com.magamochi.catalog.model.dto; import java.util.List; diff --git a/src/main/java/com/magamochi/model/entity/Author.java b/src/main/java/com/magamochi/catalog/model/entity/Author.java similarity index 91% rename from src/main/java/com/magamochi/model/entity/Author.java rename to src/main/java/com/magamochi/catalog/model/entity/Author.java index 22a2430..fa43822 100644 --- a/src/main/java/com/magamochi/model/entity/Author.java +++ b/src/main/java/com/magamochi/catalog/model/entity/Author.java @@ -1,4 +1,4 @@ -package com.magamochi.model.entity; +package com.magamochi.catalog.model.entity; import jakarta.persistence.*; import java.time.Instant; @@ -19,8 +19,6 @@ public class Author { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private Long malId; - private String name; @CreationTimestamp private Instant createdAt; diff --git a/src/main/java/com/magamochi/catalog/model/entity/Genre.java b/src/main/java/com/magamochi/catalog/model/entity/Genre.java index 4c5f557..14decf8 100644 --- a/src/main/java/com/magamochi/catalog/model/entity/Genre.java +++ b/src/main/java/com/magamochi/catalog/model/entity/Genre.java @@ -1,6 +1,5 @@ package com.magamochi.catalog.model.entity; -import com.magamochi.model.entity.MangaGenre; import jakarta.persistence.*; import java.util.List; import lombok.*; @@ -17,8 +16,6 @@ public class Genre { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private Long malId; - private String name; @OneToMany(mappedBy = "genre") diff --git a/src/main/java/com/magamochi/model/entity/Language.java b/src/main/java/com/magamochi/catalog/model/entity/Language.java similarity index 87% rename from src/main/java/com/magamochi/model/entity/Language.java rename to src/main/java/com/magamochi/catalog/model/entity/Language.java index b2b6250..a5bae30 100644 --- a/src/main/java/com/magamochi/model/entity/Language.java +++ b/src/main/java/com/magamochi/catalog/model/entity/Language.java @@ -1,4 +1,4 @@ -package com.magamochi.model.entity; +package com.magamochi.catalog.model.entity; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/model/entity/Manga.java b/src/main/java/com/magamochi/catalog/model/entity/Manga.java similarity index 73% rename from src/main/java/com/magamochi/model/entity/Manga.java rename to src/main/java/com/magamochi/catalog/model/entity/Manga.java index 09aac9e..1202f18 100644 --- a/src/main/java/com/magamochi/model/entity/Manga.java +++ b/src/main/java/com/magamochi/catalog/model/entity/Manga.java @@ -1,5 +1,10 @@ -package com.magamochi.model.entity; +package com.magamochi.catalog.model.entity; +import com.magamochi.catalog.model.enumeration.MangaState; +import com.magamochi.catalog.model.enumeration.MangaStatus; +import com.magamochi.model.entity.Image; +import com.magamochi.model.entity.MangaContentProvider; +import com.magamochi.model.entity.UserFavoriteManga; import jakarta.persistence.*; import java.time.Instant; import java.time.OffsetDateTime; @@ -26,14 +31,11 @@ public class Manga { private String title; - private String status; + @Enumerated(EnumType.STRING) + private MangaStatus status; private String synopsis; - @CreationTimestamp private Instant createdAt; - - @UpdateTimestamp private Instant updatedAt; - @OneToMany(mappedBy = "manga") private List mangaContentProviders; @@ -47,6 +49,18 @@ public class Manga { private OffsetDateTime publishedTo; + @Builder.Default private Integer chapterCount = 0; + + @Builder.Default private Boolean follow = false; + + @Enumerated(EnumType.STRING) + @Builder.Default + private MangaState state = MangaState.PENDING; + + @CreationTimestamp private Instant createdAt; + + @UpdateTimestamp private Instant updatedAt; + @OneToMany(mappedBy = "manga") private List mangaAuthors; @@ -58,8 +72,4 @@ public class Manga { @OneToMany(mappedBy = "manga") private List alternativeTitles; - - @Builder.Default private Integer chapterCount = 0; - - @Builder.Default private Boolean follow = false; } diff --git a/src/main/java/com/magamochi/model/entity/MangaAlternativeTitle.java b/src/main/java/com/magamochi/catalog/model/entity/MangaAlternativeTitle.java similarity index 89% rename from src/main/java/com/magamochi/model/entity/MangaAlternativeTitle.java rename to src/main/java/com/magamochi/catalog/model/entity/MangaAlternativeTitle.java index b178e04..a92cd0c 100644 --- a/src/main/java/com/magamochi/model/entity/MangaAlternativeTitle.java +++ b/src/main/java/com/magamochi/catalog/model/entity/MangaAlternativeTitle.java @@ -1,4 +1,4 @@ -package com.magamochi.model.entity; +package com.magamochi.catalog.model.entity; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/model/entity/MangaAuthor.java b/src/main/java/com/magamochi/catalog/model/entity/MangaAuthor.java similarity index 90% rename from src/main/java/com/magamochi/model/entity/MangaAuthor.java rename to src/main/java/com/magamochi/catalog/model/entity/MangaAuthor.java index 0e3468e..0e6890a 100644 --- a/src/main/java/com/magamochi/model/entity/MangaAuthor.java +++ b/src/main/java/com/magamochi/catalog/model/entity/MangaAuthor.java @@ -1,4 +1,4 @@ -package com.magamochi.model.entity; +package com.magamochi.catalog.model.entity; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/model/entity/MangaGenre.java b/src/main/java/com/magamochi/catalog/model/entity/MangaGenre.java similarity index 82% rename from src/main/java/com/magamochi/model/entity/MangaGenre.java rename to src/main/java/com/magamochi/catalog/model/entity/MangaGenre.java index 15a2a5d..4fcfa5f 100644 --- a/src/main/java/com/magamochi/model/entity/MangaGenre.java +++ b/src/main/java/com/magamochi/catalog/model/entity/MangaGenre.java @@ -1,6 +1,5 @@ -package com.magamochi.model.entity; +package com.magamochi.catalog.model.entity; -import com.magamochi.catalog.model.entity.Genre; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/model/entity/MangaImportReview.java b/src/main/java/com/magamochi/catalog/model/entity/MangaIngestReview.java similarity index 78% rename from src/main/java/com/magamochi/model/entity/MangaImportReview.java rename to src/main/java/com/magamochi/catalog/model/entity/MangaIngestReview.java index 6c34b34..83adb8e 100644 --- a/src/main/java/com/magamochi/model/entity/MangaImportReview.java +++ b/src/main/java/com/magamochi/catalog/model/entity/MangaIngestReview.java @@ -1,4 +1,4 @@ -package com.magamochi.model.entity; +package com.magamochi.catalog.model.entity; import com.magamochi.ingestion.model.entity.ContentProvider; import jakarta.persistence.*; @@ -7,18 +7,18 @@ import lombok.*; import org.hibernate.annotations.CreationTimestamp; @Entity -@Table(name = "manga_import_reviews") +@Table(name = "manga_ingest_reviews") @Builder @NoArgsConstructor @AllArgsConstructor @Getter @Setter -public class MangaImportReview { +public class MangaIngestReview { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String title; + private String mangaTitle; private String url; diff --git a/src/main/java/com/magamochi/catalog/model/enumeration/MangaState.java b/src/main/java/com/magamochi/catalog/model/enumeration/MangaState.java new file mode 100644 index 0000000..a14e814 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/model/enumeration/MangaState.java @@ -0,0 +1,6 @@ +package com.magamochi.catalog.model.enumeration; + +public enum MangaState { + PENDING, + AVAILABLE, +} diff --git a/src/main/java/com/magamochi/model/enumeration/MangaStatus.java b/src/main/java/com/magamochi/catalog/model/enumeration/MangaStatus.java similarity index 63% rename from src/main/java/com/magamochi/model/enumeration/MangaStatus.java rename to src/main/java/com/magamochi/catalog/model/enumeration/MangaStatus.java index d7e3ca5..df32633 100644 --- a/src/main/java/com/magamochi/model/enumeration/MangaStatus.java +++ b/src/main/java/com/magamochi/catalog/model/enumeration/MangaStatus.java @@ -1,4 +1,4 @@ -package com.magamochi.model.enumeration; +package com.magamochi.catalog.model.enumeration; public enum MangaStatus { ONGOING, diff --git a/src/main/java/com/magamochi/catalog/model/repository/AuthorRepository.java b/src/main/java/com/magamochi/catalog/model/repository/AuthorRepository.java new file mode 100644 index 0000000..d763ac4 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/model/repository/AuthorRepository.java @@ -0,0 +1,6 @@ +package com.magamochi.catalog.model.repository; + +import com.magamochi.catalog.model.entity.Author; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AuthorRepository extends JpaRepository {} diff --git a/src/main/java/com/magamochi/catalog/model/repository/GenreRepository.java b/src/main/java/com/magamochi/catalog/model/repository/GenreRepository.java index 165c9fb..0fd67b3 100644 --- a/src/main/java/com/magamochi/catalog/model/repository/GenreRepository.java +++ b/src/main/java/com/magamochi/catalog/model/repository/GenreRepository.java @@ -1,11 +1,6 @@ package com.magamochi.catalog.model.repository; import com.magamochi.catalog.model.entity.Genre; -import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -public interface GenreRepository extends JpaRepository { - Optional findByMalId(Long malId); - - Optional findByName(String name); -} +public interface GenreRepository extends JpaRepository {} diff --git a/src/main/java/com/magamochi/model/repository/LanguageRepository.java b/src/main/java/com/magamochi/catalog/model/repository/LanguageRepository.java similarity index 69% rename from src/main/java/com/magamochi/model/repository/LanguageRepository.java rename to src/main/java/com/magamochi/catalog/model/repository/LanguageRepository.java index 0c288c2..6900355 100644 --- a/src/main/java/com/magamochi/model/repository/LanguageRepository.java +++ b/src/main/java/com/magamochi/catalog/model/repository/LanguageRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.model.repository; +package com.magamochi.catalog.model.repository; -import com.magamochi.model.entity.Language; +import com.magamochi.catalog.model.entity.Language; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/model/repository/MangaAlternativeTitlesRepository.java b/src/main/java/com/magamochi/catalog/model/repository/MangaAlternativeTitlesRepository.java similarity index 60% rename from src/main/java/com/magamochi/model/repository/MangaAlternativeTitlesRepository.java rename to src/main/java/com/magamochi/catalog/model/repository/MangaAlternativeTitlesRepository.java index 86bc9a0..3329058 100644 --- a/src/main/java/com/magamochi/model/repository/MangaAlternativeTitlesRepository.java +++ b/src/main/java/com/magamochi/catalog/model/repository/MangaAlternativeTitlesRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.model.repository; +package com.magamochi.catalog.model.repository; -import com.magamochi.model.entity.MangaAlternativeTitle; +import com.magamochi.catalog.model.entity.MangaAlternativeTitle; import org.springframework.data.jpa.repository.JpaRepository; public interface MangaAlternativeTitlesRepository diff --git a/src/main/java/com/magamochi/model/repository/MangaAuthorRepository.java b/src/main/java/com/magamochi/catalog/model/repository/MangaAuthorRepository.java similarity index 55% rename from src/main/java/com/magamochi/model/repository/MangaAuthorRepository.java rename to src/main/java/com/magamochi/catalog/model/repository/MangaAuthorRepository.java index 78a4833..61f5425 100644 --- a/src/main/java/com/magamochi/model/repository/MangaAuthorRepository.java +++ b/src/main/java/com/magamochi/catalog/model/repository/MangaAuthorRepository.java @@ -1,8 +1,8 @@ -package com.magamochi.model.repository; +package com.magamochi.catalog.model.repository; -import com.magamochi.model.entity.Author; -import com.magamochi.model.entity.Manga; -import com.magamochi.model.entity.MangaAuthor; +import com.magamochi.catalog.model.entity.Author; +import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.catalog.model.entity.MangaAuthor; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/catalog/model/repository/MangaContentProviderRepository.java b/src/main/java/com/magamochi/catalog/model/repository/MangaContentProviderRepository.java new file mode 100644 index 0000000..de3f1bd --- /dev/null +++ b/src/main/java/com/magamochi/catalog/model/repository/MangaContentProviderRepository.java @@ -0,0 +1,10 @@ +package com.magamochi.catalog.model.repository; + +import com.magamochi.model.entity.MangaContentProvider; +import jakarta.validation.constraints.NotBlank; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MangaContentProviderRepository extends JpaRepository { + boolean existsByMangaTitleIgnoreCaseAndContentProvider_Id( + @NotBlank String mangaTitle, long contentProviderId); +} diff --git a/src/main/java/com/magamochi/model/repository/MangaGenreRepository.java b/src/main/java/com/magamochi/catalog/model/repository/MangaGenreRepository.java similarity index 65% rename from src/main/java/com/magamochi/model/repository/MangaGenreRepository.java rename to src/main/java/com/magamochi/catalog/model/repository/MangaGenreRepository.java index 7b68a95..cfc89fe 100644 --- a/src/main/java/com/magamochi/model/repository/MangaGenreRepository.java +++ b/src/main/java/com/magamochi/catalog/model/repository/MangaGenreRepository.java @@ -1,8 +1,8 @@ -package com.magamochi.model.repository; +package com.magamochi.catalog.model.repository; import com.magamochi.catalog.model.entity.Genre; -import com.magamochi.model.entity.Manga; -import com.magamochi.model.entity.MangaGenre; +import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.catalog.model.entity.MangaGenre; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/catalog/model/repository/MangaIngestReviewRepository.java b/src/main/java/com/magamochi/catalog/model/repository/MangaIngestReviewRepository.java new file mode 100644 index 0000000..56b5cf3 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/model/repository/MangaIngestReviewRepository.java @@ -0,0 +1,8 @@ +package com.magamochi.catalog.model.repository; + +import com.magamochi.catalog.model.entity.MangaIngestReview; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MangaIngestReviewRepository extends JpaRepository { + boolean existsByMangaTitleIgnoreCaseAndContentProvider_Id(String mangaTitle, long providerId); +} diff --git a/src/main/java/com/magamochi/model/repository/MangaRepository.java b/src/main/java/com/magamochi/catalog/model/repository/MangaRepository.java similarity index 83% rename from src/main/java/com/magamochi/model/repository/MangaRepository.java rename to src/main/java/com/magamochi/catalog/model/repository/MangaRepository.java index 9f0a088..f423f81 100644 --- a/src/main/java/com/magamochi/model/repository/MangaRepository.java +++ b/src/main/java/com/magamochi/catalog/model/repository/MangaRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.model.repository; +package com.magamochi.catalog.model.repository; -import com.magamochi.model.entity.Manga; +import com.magamochi.catalog.model.entity.Manga; import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/catalog/queue/command/MangaUpdateCommand.java b/src/main/java/com/magamochi/catalog/queue/command/MangaUpdateCommand.java new file mode 100644 index 0000000..43c67d6 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/queue/command/MangaUpdateCommand.java @@ -0,0 +1,3 @@ +package com.magamochi.catalog.queue.command; + +public record MangaUpdateCommand(long mangaId) {} diff --git a/src/main/java/com/magamochi/catalog/queue/consumer/MangaIngestConsumer.java b/src/main/java/com/magamochi/catalog/queue/consumer/MangaIngestConsumer.java new file mode 100644 index 0000000..e79cc69 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/queue/consumer/MangaIngestConsumer.java @@ -0,0 +1,21 @@ +package com.magamochi.catalog.queue.consumer; + +import com.magamochi.catalog.service.MangaIngestService; +import com.magamochi.common.queue.command.MangaIngestCommand; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaIngestConsumer { + private final MangaIngestService mangaIngestService; + + @RabbitListener(queues = "${queues.manga-ingest}") + public void receiveMangaIngestCommand(MangaIngestCommand command) { + log.info("Received manga ingest command: {}", command); + mangaIngestService.ingestManga(command.providerId(), command.mangaTitle(), command.url()); + } +} diff --git a/src/main/java/com/magamochi/catalog/queue/consumer/MangaUpdateConsumer.java b/src/main/java/com/magamochi/catalog/queue/consumer/MangaUpdateConsumer.java new file mode 100644 index 0000000..3ce0d0d --- /dev/null +++ b/src/main/java/com/magamochi/catalog/queue/consumer/MangaUpdateConsumer.java @@ -0,0 +1,21 @@ +package com.magamochi.catalog.queue.consumer; + +import com.magamochi.catalog.queue.command.MangaUpdateCommand; +import com.magamochi.catalog.service.MangaUpdateService; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaUpdateConsumer { + private final MangaUpdateService mangaUpdateService; + + @RabbitListener(queues = "${queues.manga-update}") + public void receiveMangaUpdateCommand(MangaUpdateCommand command) { + log.info("Received manga update command: {}", command); + mangaUpdateService.update(command.mangaId()); + } +} diff --git a/src/main/java/com/magamochi/catalog/queue/producer/MangaUpdateProducer.java b/src/main/java/com/magamochi/catalog/queue/producer/MangaUpdateProducer.java new file mode 100644 index 0000000..2bd5d1e --- /dev/null +++ b/src/main/java/com/magamochi/catalog/queue/producer/MangaUpdateProducer.java @@ -0,0 +1,23 @@ +package com.magamochi.catalog.queue.producer; + +import com.magamochi.catalog.queue.command.MangaUpdateCommand; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaUpdateProducer { + private final RabbitTemplate rabbitTemplate; + + @Value("${queues.manga-update}") + private String mangaUpdateQueue; + + public void sendMangaUpdateCommand(MangaUpdateCommand command) { + rabbitTemplate.convertAndSend(mangaUpdateQueue, command); + log.info("Sent manga update command: {}", command); + } +} diff --git a/src/main/java/com/magamochi/catalog/service/AniListService.java b/src/main/java/com/magamochi/catalog/service/AniListService.java new file mode 100644 index 0000000..fbd9589 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/service/AniListService.java @@ -0,0 +1,179 @@ +package com.magamochi.catalog.service; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; + +import com.google.common.util.concurrent.RateLimiter; +import com.magamochi.catalog.client.AniListClient; +import com.magamochi.catalog.model.dto.MangaDataDTO; +import com.magamochi.catalog.model.enumeration.MangaStatus; +import com.magamochi.catalog.util.DoubleUtil; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.jspecify.annotations.NonNull; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class AniListService { + private final AniListClient aniListClient; + private final RateLimiter aniListRateLimiter; + + public Map searchMangaByTitle(String title) { + var request = getSearchGraphQLRequest(title); + + aniListRateLimiter.acquire(); + var response = aniListClient.searchManga(request); + + if (nonNull(response) && nonNull(response.data()) && nonNull(response.data().page())) { + return response.data().page().media().stream() + .flatMap( + manga -> + Stream.of( + manga.title().romaji(), + manga.title().english(), + manga.title().nativeTitle()) + .filter(Objects::nonNull) + .filter(t -> !t.isBlank()) + .map(titleString -> Map.entry(titleString, manga))) + .collect( + Collectors.toMap( + Map.Entry::getKey, Map.Entry::getValue, (existingManga, manga) -> existingManga)); + } + + return Map.of(); + } + + public MangaDataDTO getMangaDataById(Long aniListId) { + var request = getGraphQLRequest(aniListId); + + aniListRateLimiter.acquire(); + var media = aniListClient.getManga(request).data().Media(); + + var authors = + media.staff().edges().stream() + .filter(edge -> isAuthorRole(edge.role())) + .map(edge -> edge.node().name().full()) + .distinct() + .toList(); + + boolean hasRomajiTitle = nonNull(media.title().romaji()); + return MangaDataDTO.builder() + .title(hasRomajiTitle ? media.title().romaji() : media.title().english()) + .score( + nonNull(media.averageScore()) + ? DoubleUtil.round((double) media.averageScore() / 10, 2) + : 0) + .synopsis(media.description()) + .chapterCount(media.chapters()) + .publishedFrom(convertFuzzyDate(media.startDate())) + .publishedTo(convertFuzzyDate(media.endDate())) + .authors(authors) + .genres(media.genres()) + // TODO: improve this + .alternativeTitles( + hasRomajiTitle + ? nonNull(media.title().english()) + ? List.of(media.title().english(), media.title().nativeTitle()) + : List.of(media.title().nativeTitle()) + : List.of(media.title().nativeTitle())) + .coverImageUrl(media.coverImage().large()) + .status(mapStatus(media.status())) + .build(); + } + + private static AniListClient.@NonNull GraphQLRequest getGraphQLRequest(Long aniListId) { + var query = + """ + query ($id: Int) { + Media (id: $id, type: MANGA) { + title { + romaji + english + native + } + startDate { year month day } + endDate { year month day } + description + status + averageScore + chapters + coverImage { large } + genres + staff { + edges { + role + node { + name { + full + } + } + } + } + } + } + """; + return new AniListClient.GraphQLRequest( + query, new AniListClient.GraphQLRequest.Variables(aniListId)); + } + + private static AniListClient.@NonNull SearchGraphQLRequest getSearchGraphQLRequest(String title) { + var query = + """ + query ($search: String) { + Page(page: 1, perPage: 10) { + media(search: $search, type: MANGA) { + id + idMal + title { + romaji + english + native + } + status + } + } + } + """; + + var variables = new AniListClient.SearchGraphQLRequest.SearchVariables(title); + return new AniListClient.SearchGraphQLRequest(query, variables); + } + + private OffsetDateTime convertFuzzyDate(AniListClient.Manga.FuzzyDate date) { + if (isNull(date) || isNull(date.year())) { + return null; + } + return OffsetDateTime.of( + date.year(), + isNull(date.month()) ? 1 : date.month(), + isNull(date.day()) ? 1 : date.day(), + 0, + 0, + 0, + 0, + ZoneOffset.UTC); + } + + private MangaStatus mapStatus(String aniListStatus) { + return switch (aniListStatus.toLowerCase()) { + case "releasing" -> MangaStatus.ONGOING; + case "finished" -> MangaStatus.COMPLETED; + default -> MangaStatus.UNKNOWN; + }; + } + + private boolean isAuthorRole(String role) { + return role.equalsIgnoreCase("Story & Art") + || role.equalsIgnoreCase("Story") + || role.equalsIgnoreCase("Art"); + } +} diff --git a/src/main/java/com/magamochi/service/LanguageService.java b/src/main/java/com/magamochi/catalog/service/LanguageService.java similarity index 75% rename from src/main/java/com/magamochi/service/LanguageService.java rename to src/main/java/com/magamochi/catalog/service/LanguageService.java index b37ff9d..83833eb 100644 --- a/src/main/java/com/magamochi/service/LanguageService.java +++ b/src/main/java/com/magamochi/catalog/service/LanguageService.java @@ -1,8 +1,8 @@ -package com.magamochi.service; +package com.magamochi.catalog.service; +import com.magamochi.catalog.model.entity.Language; +import com.magamochi.catalog.model.repository.LanguageRepository; import com.magamochi.common.exception.NotFoundException; -import com.magamochi.model.entity.Language; -import com.magamochi.model.repository.LanguageRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/catalog/service/MangaIngestReviewService.java b/src/main/java/com/magamochi/catalog/service/MangaIngestReviewService.java new file mode 100644 index 0000000..24d188f --- /dev/null +++ b/src/main/java/com/magamochi/catalog/service/MangaIngestReviewService.java @@ -0,0 +1,68 @@ +package com.magamochi.catalog.service; + +import com.magamochi.catalog.model.dto.MangaIngestReviewDTO; +import com.magamochi.catalog.model.entity.MangaIngestReview; +import com.magamochi.catalog.model.repository.MangaIngestReviewRepository; +import com.magamochi.common.exception.NotFoundException; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.NotImplementedException; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class MangaIngestReviewService { + private final MangaIngestReviewRepository mangaIngestReviewRepository; + + public List get() { + return mangaIngestReviewRepository.findAll().stream().map(MangaIngestReviewDTO::from).toList(); + } + + public void deleteIngestReview(Long id) { + var importReview = getImportReviewThrowIfNotFound(id); + + mangaIngestReviewRepository.delete(importReview); + } + + public void resolveImportReview(Long id, String malId) { + throw new NotImplementedException(); + // var importReview = getImportReviewThrowIfNotFound(id); + // + // jikanRateLimiter.acquire(); + // var jikanResult = jikanClient.getMangaById(Long.parseLong(malId)).data(); + // + // if (isNull(jikanResult)) { + // throw new NotFoundException("MyAnimeList manga not found for ID: " + id); + // } + // + // var manga = + // mangaRepository + // .findByTitleIgnoreCase(jikanResult.title()) + // .orElseGet( + // () -> + // mangaRepository.save( + // Manga.builder() + // .title(jikanResult.title()) + // .malId(Long.parseLong(malId)) + // .build())); + // + // if (!mangaContentProviderRepository.existsByMangaAndContentProviderAndUrlIgnoreCase( + // manga, importReview.getContentProvider(), importReview.getUrl())) { + // mangaContentProviderRepository.save( + // MangaContentProvider.builder() + // .manga(manga) + // .mangaTitle(importReview.getMangaTitle()) + // .contentProvider(importReview.getContentProvider()) + // .url(importReview.getUrl()) + // .build()); + // } + // + // mangaIngestReviewRepository.delete(importReview); + } + + private MangaIngestReview getImportReviewThrowIfNotFound(Long id) { + return mangaIngestReviewRepository + .findById(id) + .orElseThrow(() -> new NotFoundException("Import review not found for ID: " + id)); + } +} diff --git a/src/main/java/com/magamochi/catalog/service/MangaIngestService.java b/src/main/java/com/magamochi/catalog/service/MangaIngestService.java new file mode 100644 index 0000000..3d26e35 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/service/MangaIngestService.java @@ -0,0 +1,101 @@ +package com.magamochi.catalog.service; + +import static java.util.Objects.isNull; + +import com.magamochi.catalog.model.entity.MangaIngestReview; +import com.magamochi.catalog.model.repository.MangaContentProviderRepository; +import com.magamochi.catalog.model.repository.MangaIngestReviewRepository; +import com.magamochi.ingestion.service.ContentProviderService; +import com.magamochi.model.entity.MangaContentProvider; +import jakarta.validation.constraints.NotBlank; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaIngestService { + private final ContentProviderService contentProviderService; + private final MangaResolutionService mangaResolutionService; + + private final MangaIngestReviewRepository mangaIngestReviewRepository; + private final MangaContentProviderRepository mangaContentProviderRepository; + + public void ingestManga( + long contentProviderId, @NotBlank String mangaTitle, @NotBlank String url) { + log.info( + "Ingesting manga with mangaTitle '{}' from provider {}", mangaTitle, contentProviderId); + + if (mangaContentProviderRepository.existsByMangaTitleIgnoreCaseAndContentProvider_Id( + mangaTitle, contentProviderId)) { + log.info( + "Manga with mangaTitle '{}' already exists for provider '{}', skipping ingest", + mangaTitle, + contentProviderId); + return; + } + + var manga = mangaResolutionService.findOrCreateManga(mangaTitle); + if (isNull(manga)) { + createMangaIngestReview(mangaTitle, url, contentProviderId); + return; + } + + try { + var contentProvider = contentProviderService.find(contentProviderId); + + mangaContentProviderRepository.save( + MangaContentProvider.builder() + .manga(manga) + .mangaTitle(mangaTitle) + .contentProvider(contentProvider) + .url(url) + .build()); + } catch (Exception e) { + log.error( + "Failed to ingest manga with mangaTitle '{}' from provider '{}'", + mangaTitle, + contentProviderId, + e); + } + + log.info( + "Successfully ingested manga with mangaTitle '{}' from provider {}", + mangaTitle, + contentProviderId); + } + + private void createMangaIngestReview(String mangaTitle, String url, long contentProviderId) { + log.info( + "Creating manga ingest review for manga with mangaTitle '{}' from provider {}", + mangaTitle, + contentProviderId); + + if (mangaIngestReviewRepository.existsByMangaTitleIgnoreCaseAndContentProvider_Id( + mangaTitle, contentProviderId)) { + log.info( + "Manga ingest review already exists for manga with mangaTitle '{}' and provider {}, skipping review creation", + mangaTitle, + contentProviderId); + return; + } + + try { + var contentProvider = contentProviderService.find(contentProviderId); + + mangaIngestReviewRepository.save( + MangaIngestReview.builder() + .mangaTitle(mangaTitle) + .url(url) + .contentProvider(contentProvider) + .build()); + } catch (Exception e) { + log.error( + "Failed to create manga ingest review for manga with mangaTitle '{}' from provider {}", + mangaTitle, + contentProviderId, + e); + } + } +} diff --git a/src/main/java/com/magamochi/catalog/service/MangaResolutionService.java b/src/main/java/com/magamochi/catalog/service/MangaResolutionService.java new file mode 100644 index 0000000..51a6fcb --- /dev/null +++ b/src/main/java/com/magamochi/catalog/service/MangaResolutionService.java @@ -0,0 +1,133 @@ +package com.magamochi.catalog.service; + +import static java.util.Objects.nonNull; + +import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.catalog.model.repository.MangaRepository; +import com.magamochi.catalog.queue.command.MangaUpdateCommand; +import com.magamochi.catalog.queue.producer.MangaUpdateProducer; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaResolutionService { + private final AniListService aniListService; + private final MyAnimeListService myAnimeListService; + private final TitleMatcherService titleMatcherService; + + private final MangaUpdateProducer mangaUpdateProducer; + + private final MangaRepository mangaRepository; + + public Manga findOrCreateManga(String searchTitle) { + var existingManga = mangaRepository.findByTitleIgnoreCase(searchTitle); + if (existingManga.isPresent()) { + return existingManga.get(); + } + + var aniListResult = searchMangaOnAniList(searchTitle); + var malResult = searchMangaOnMyAnimeList(searchTitle); + + if (aniListResult.isEmpty() && malResult.isEmpty()) { + return null; + } + + var canonicalTitle = + aniListResult + .map(ProviderResult::title) + .orElseGet(() -> malResult.map(ProviderResult::title).orElse(searchTitle)); + + var aniListId = aniListResult.map(ProviderResult::externalId).orElse(null); + var malId = malResult.map(ProviderResult::externalId).orElse(null); + + return findOrCreateManga(canonicalTitle, aniListId, malId); + } + + private Optional searchMangaOnAniList(String title) { + var searchResults = aniListService.searchMangaByTitle(title); + if (searchResults.isEmpty()) { + return Optional.empty(); + } + + var matchResponse = + titleMatcherService.findBestMatch( + TitleMatcherService.TitleMatchRequest.builder() + .title(title) + .options(searchResults.keySet()) + .build()); + + if (!matchResponse.matchFound()) { + log.warn("No title match found for manga with title {} on AniList", title); + return Optional.empty(); + } + + var matchedManga = searchResults.get(matchResponse.bestMatch()); + + var bestTitle = + nonNull(matchedManga.title().romaji()) + ? matchedManga.title().romaji() + : matchedManga.title().english(); + + return Optional.of(new ProviderResult(bestTitle, matchedManga.id())); + } + + private Optional searchMangaOnMyAnimeList(String title) { + var searchResults = myAnimeListService.searchMangaByTitle(title); + if (searchResults.isEmpty()) { + return Optional.empty(); + } + + var matchResponse = + titleMatcherService.findBestMatch( + TitleMatcherService.TitleMatchRequest.builder() + .title(title) + .options(searchResults.keySet()) + .build()); + if (!matchResponse.matchFound()) { + log.warn("No title match found for manga with title {} on MyAnimeList", title); + return Optional.empty(); + } + + var bestTitle = matchResponse.bestMatch(); + var malId = searchResults.get(bestTitle); + + return Optional.of(new ProviderResult(bestTitle, malId)); + } + + private Manga findOrCreateManga(String canonicalTitle, Long aniListId, Long malId) { + if (nonNull(aniListId)) { + var existingByAniList = mangaRepository.findByAniListId(aniListId); + if (existingByAniList.isPresent()) { + return existingByAniList.get(); + } + } + + if (nonNull(malId)) { + var existingByMalId = mangaRepository.findByMalId(malId); + if (existingByMalId.isPresent()) { + return existingByMalId.get(); + } + } + + return mangaRepository + .findByTitleIgnoreCase(canonicalTitle) + .orElseGet( + () -> { + var newManga = + Manga.builder().title(canonicalTitle).malId(malId).aniListId(aniListId).build(); + + var savedManga = mangaRepository.save(newManga); + + mangaUpdateProducer.sendMangaUpdateCommand( + new MangaUpdateCommand(savedManga.getId())); + + return savedManga; + }); + } + + private record ProviderResult(String title, Long externalId) {} +} diff --git a/src/main/java/com/magamochi/catalog/service/MangaService.java b/src/main/java/com/magamochi/catalog/service/MangaService.java new file mode 100644 index 0000000..fbce832 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/service/MangaService.java @@ -0,0 +1,81 @@ +package com.magamochi.catalog.service; + +import static java.util.Objects.nonNull; + +import com.magamochi.catalog.model.dto.MangaDTO; +import com.magamochi.catalog.model.dto.MangaListDTO; +import com.magamochi.catalog.model.dto.MangaListFilterDTO; +import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.catalog.model.repository.MangaRepository; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.model.repository.UserFavoriteMangaRepository; +import com.magamochi.model.repository.UserMangaFollowRepository; +import com.magamochi.model.specification.MangaSpecification; +import com.magamochi.service.UserService; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class MangaService { + private final UserService userService; + + private final MangaRepository mangaRepository; + private final UserFavoriteMangaRepository userFavoriteMangaRepository; + private final UserMangaFollowRepository userMangaFollowRepository; + + public Manga find(long mangaId) { + return mangaRepository + .findById(mangaId) + .orElseThrow(() -> new NotFoundException("Manga with ID " + mangaId + " not found")); + } + + public Page get(MangaListFilterDTO filterDTO, Pageable pageable) { + var user = userService.getLoggedUser(); + + var specification = MangaSpecification.getMangaListSpecification(filterDTO, user); + + var favoriteMangasIds = + nonNull(user) + ? userFavoriteMangaRepository.findByUser(user).stream() + .map(ufm -> ufm.getManga().getId()) + .collect(Collectors.toSet()) + : Set.of(); + + return mangaRepository + .findAll(specification, pageable) + .map( + manga -> { + var favorite = favoriteMangasIds.contains(manga.getId()); + return MangaListDTO.from(manga, favorite); + }); + } + + public MangaDTO get(Long mangaId) { + var manga = find(mangaId); + var user = userService.getLoggedUser(); + + var favoriteMangasIds = + nonNull(user) + ? userFavoriteMangaRepository.findByUser(user).stream() + .map(ufm -> ufm.getManga().getId()) + .collect(Collectors.toSet()) + : Set.of(); + + var followingMangaIds = + nonNull(user) + ? userMangaFollowRepository.findByUser(user).stream() + .map(umf -> umf.getManga().getId()) + .collect(Collectors.toSet()) + : Set.of(); + + return MangaDTO.from( + manga, + favoriteMangasIds.contains(manga.getId()), + followingMangaIds.contains(manga.getId())); + } +} diff --git a/src/main/java/com/magamochi/catalog/service/MangaUpdateService.java b/src/main/java/com/magamochi/catalog/service/MangaUpdateService.java new file mode 100644 index 0000000..200ac27 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/service/MangaUpdateService.java @@ -0,0 +1,91 @@ +package com.magamochi.catalog.service; + +import static java.util.Objects.nonNull; + +import com.magamochi.catalog.model.dto.MangaDataDTO; +import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.catalog.model.enumeration.MangaState; +import com.magamochi.common.model.enumeration.ContentType; +import com.magamochi.common.queue.command.ImageFetchCommand; +import com.magamochi.common.queue.producer.ImageFetchProducer; +import jakarta.transaction.Transactional; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaUpdateService { + private final AniListService aniListService; + private final MyAnimeListService myAnimeListService; + private final MangaService mangaService; + + private final ImageFetchProducer imageFetchProducer; + + @Transactional + public void update(long mangaId) { + log.info("Updating manga with ID {}", mangaId); + + var manga = mangaService.find(mangaId); + var mangaData = fetchExternalMangaData(manga); + applyUpdatesToDatabase(manga, mangaData); + + imageFetchProducer.sendImageFetchCommand( + new ImageFetchCommand(manga.getId(), ContentType.MANGA_COVER, mangaData.coverImageUrl())); + + log.info("Manga with ID {} updated successfully", mangaId); + } + + @Transactional + public void updateMangaCoverImage(long mangaId, UUID imageId) { + log.info("Updating cover image for manga with ID {}", mangaId); + + var manga = mangaService.find(mangaId); + // manga.setCoverImageId(imageId); + // + // log.info("Manga with ID {} cover image updated successfully", mangaId); + } + + private MangaDataDTO fetchExternalMangaData(Manga manga) { + if (nonNull(manga.getAniListId())) { + return aniListService.getMangaDataById(manga.getAniListId()); + } + + if (nonNull(manga.getMalId())) { + return myAnimeListService.getMangaDataById(manga.getMalId()); + } + + throw new IllegalStateException( + "Cannot update manga: No external provider IDs found for Manga " + manga.getId()); + } + + private void applyUpdatesToDatabase(Manga manga, MangaDataDTO mangaData) { + manga.setTitle(mangaData.title()); + manga.setSynopsis(mangaData.synopsis()); + manga.setScore(mangaData.score()); + manga.setStatus(mangaData.status()); + manga.setPublishedFrom(mangaData.publishedFrom()); + manga.setPublishedTo(mangaData.publishedTo()); + manga.setChapterCount(mangaData.chapterCount()); + manga.setState(MangaState.AVAILABLE); + + // TODO: properly save these + // + // mangaAlternativeTitleService.saveOrUpdateMangaAlternativeTitles( + // manga.getId(), mangaData.alternativeTitles()); + // + // var genreIds = + // mangaData.genres().stream() + // .map(genreService::findOrCreateGenre) + // .collect(Collectors.toSet()); + // mangaGenreService.saveOrUpdateMangaGenres(manga.getId(), genreIds); + // + // var authorIds = + // mangaData.authors().stream() + // .map(authorService::findOrCreateAuthor) + // .collect(Collectors.toSet()); + // mangaAuthorService.saveOrUpdateMangaAuthors(manga.getId(), authorIds); + } +} diff --git a/src/main/java/com/magamochi/catalog/service/MyAnimeListService.java b/src/main/java/com/magamochi/catalog/service/MyAnimeListService.java new file mode 100644 index 0000000..530745f --- /dev/null +++ b/src/main/java/com/magamochi/catalog/service/MyAnimeListService.java @@ -0,0 +1,88 @@ +package com.magamochi.catalog.service; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; + +import com.google.common.util.concurrent.RateLimiter; +import com.magamochi.catalog.client.JikanClient; +import com.magamochi.catalog.model.dto.MangaDataDTO; +import com.magamochi.catalog.model.enumeration.MangaStatus; +import com.magamochi.catalog.util.DoubleUtil; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MyAnimeListService { + private final JikanClient jikanClient; + private final RateLimiter jikanRateLimiter; + + /// Searches for manga titles on MyAnimeList using the Jikan API and returns a map of title to MAL + // ID. + public Map searchMangaByTitle(String titleToSearch) { + jikanRateLimiter.acquire(); + var results = jikanClient.mangaSearch(titleToSearch).data(); + if (results.isEmpty()) { + log.warn("No manga found with title {}", titleToSearch); + return Map.of(); + } + + return results.stream() + .collect( + Collectors.toMap( + JikanClient.SearchResponse.MangaData::title, + JikanClient.SearchResponse.MangaData::mal_id, + (existing, second) -> existing)); + } + + public MangaDataDTO getMangaDataById(Long malId) { + jikanRateLimiter.acquire(); + var response = jikanClient.getMangaById(malId); + if (isNull(response) || isNull(response.data())) { + log.warn("No manga found with MAL ID {}", malId); + return null; + } + + var responseData = response.data(); + + var authors = + responseData.authors().stream() + .map(JikanClient.MangaResponse.MangaData.AuthorData::name) + .toList(); + + var genres = + responseData.genres().stream() + .map(JikanClient.MangaResponse.MangaData.GenreData::name) + .toList(); + + var alternativeTitles = responseData.title_synonyms(); + + return MangaDataDTO.builder() + .title(responseData.title()) + .score(nonNull(responseData.score()) ? DoubleUtil.round(responseData.score(), 2) : 0) + .synopsis(responseData.synopsis()) + .chapterCount(responseData.chapters()) + .publishedFrom(responseData.published().from()) + .publishedTo(responseData.published().to()) + .authors(authors) + .genres(genres) + .alternativeTitles(alternativeTitles) + .coverImageUrl(responseData.images().jpg().large_image_url()) + .status(mapStatus(responseData.status())) + .build(); + } + + private MangaStatus mapStatus(String malStatus) { + return switch (malStatus) { + case "finished" -> MangaStatus.COMPLETED; + case "publishing" -> MangaStatus.ONGOING; + case "on hiatus" -> MangaStatus.HIATUS; + case "discontinued" -> MangaStatus.CANCELLED; + default -> MangaStatus.UNKNOWN; + }; + } +} diff --git a/src/main/java/com/magamochi/service/TitleMatcherService.java b/src/main/java/com/magamochi/catalog/service/TitleMatcherService.java similarity index 55% rename from src/main/java/com/magamochi/service/TitleMatcherService.java rename to src/main/java/com/magamochi/catalog/service/TitleMatcherService.java index fc85034..1a13d5e 100644 --- a/src/main/java/com/magamochi/service/TitleMatcherService.java +++ b/src/main/java/com/magamochi/catalog/service/TitleMatcherService.java @@ -1,10 +1,9 @@ -package com.magamochi.service; +package com.magamochi.catalog.service; +import static java.util.Objects.isNull; import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.springframework.util.CollectionUtils.isEmpty; -import com.magamochi.model.dto.TitleMatchRequestDTO; -import com.magamochi.model.dto.TitleMatchResponseDTO; +import lombok.Builder; import lombok.extern.log4j.Log4j2; import org.apache.commons.text.similarity.LevenshteinDistance; import org.springframework.stereotype.Service; @@ -14,18 +13,24 @@ import org.springframework.stereotype.Service; public class TitleMatcherService { private final LevenshteinDistance levenshteinDistance = LevenshteinDistance.getDefaultInstance(); - public TitleMatchResponseDTO findBestMatch(TitleMatchRequestDTO request) { - if (isBlank(request.getTitle()) || isEmpty(request.getOptions())) { + public TitleMatchResponse findBestMatch(TitleMatchRequest request) { + if (isBlank(request.title()) || !request.options().iterator().hasNext()) { throw new IllegalArgumentException("Title and options are required"); } - log.info("Finding best match for {}. Options: {}", request.getTitle(), request.getOptions()); + // Set the default threshold if not specified + var threshold = request.threshold(); + if (isNull(threshold) || threshold == 0) { + threshold = 85; + } + + log.info("Finding best match for {}. Options: {}", request.title(), request.options()); String bestMatch = null; double bestScore = 0.0; - for (var option : request.getOptions()) { - var score = calculateSimilarityScore(request.getTitle(), option); + for (var option : request.options()) { + var score = calculateSimilarityScore(request.title(), option); if (score > bestScore) { bestScore = score; @@ -33,20 +38,20 @@ public class TitleMatcherService { } } - if (bestScore >= request.getThreshold()) { + if (bestScore >= threshold) { log.info( - "Found best match for {}: {}. Similarity: {}", request.getTitle(), bestMatch, bestScore); + "Found best match for {}: {}. Similarity: {}", request.title(), bestMatch, bestScore); - return TitleMatchResponseDTO.builder() + return TitleMatchResponse.builder() .matchFound(true) .bestMatch(bestMatch) .similarity(bestScore) .build(); } - log.info("No match found for {}. Threshold: {}", request.getTitle(), request.getThreshold()); + log.info("No match found for {}. Threshold: {}", request.title(), threshold); - return TitleMatchResponseDTO.builder().matchFound(false).build(); + return TitleMatchResponse.builder().matchFound(false).build(); } private double calculateSimilarityScore(String title, String option) { @@ -64,4 +69,10 @@ public class TitleMatcherService { // Format to two decimal places for a cleaner result return Math.round(similarity * 100.0) / 100.0; } + + @Builder + public record TitleMatchRequest(String title, Iterable options, Integer threshold) {} + + @Builder + public record TitleMatchResponse(boolean matchFound, String bestMatch, Double similarity) {} } diff --git a/src/main/java/com/magamochi/util/DoubleUtil.java b/src/main/java/com/magamochi/catalog/util/DoubleUtil.java similarity index 92% rename from src/main/java/com/magamochi/util/DoubleUtil.java rename to src/main/java/com/magamochi/catalog/util/DoubleUtil.java index 5452721..cba0e28 100644 --- a/src/main/java/com/magamochi/util/DoubleUtil.java +++ b/src/main/java/com/magamochi/catalog/util/DoubleUtil.java @@ -1,4 +1,4 @@ -package com.magamochi.util; +package com.magamochi.catalog.util; import java.math.BigDecimal; import java.math.RoundingMode; diff --git a/src/main/java/com/magamochi/client/AniListClient.java b/src/main/java/com/magamochi/client/AniListClient.java deleted file mode 100644 index 5baea6e..0000000 --- a/src/main/java/com/magamochi/client/AniListClient.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.magamochi.client; - -import io.github.resilience4j.retry.annotation.Retry; -import java.util.List; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; - -@FeignClient(name = "aniList", url = "https://graphql.anilist.co") -@Retry(name = "AniListRetry") -public interface AniListClient { - - @PostMapping - MangaResponse getManga(@RequestBody GraphQLRequest request); - - record GraphQLRequest(String query, Variables variables) { - public record Variables(Long id) {} - } - - record MangaResponse(Data data) { - public record Data(Manga Media) {} - - public record Manga( - Long id, - Long idMal, - Title title, - String status, - String description, // synopsis - int chapters, - int averageScore, // score (0-100) - CoverImage coverImage, - List genres, - FuzzyDate startDate, - FuzzyDate endDate, - StaffConnection staff) { - public record Title(String romaji, String english, String nativeTitle) {} - - public record CoverImage(String large) {} - - public record FuzzyDate(Integer year, Integer month, Integer day) {} - - public record StaffConnection(List edges) { - public record StaffEdge(String role, Staff node) { - public record Staff(Name name) { - public record Name(String full) {} - } - } - } - } - } -} diff --git a/src/main/java/com/magamochi/common/config/RabbitConfig.java b/src/main/java/com/magamochi/common/config/RabbitConfig.java index f9c991e..948098d 100644 --- a/src/main/java/com/magamochi/common/config/RabbitConfig.java +++ b/src/main/java/com/magamochi/common/config/RabbitConfig.java @@ -16,6 +16,22 @@ public class RabbitConfig { @Value("${queues.provider-page-ingest}") private String providerPageIngestQueue; + @Value("${queues.manga-update}") + private String mangaUpdateQueue; + + @Value("${queues.image-fetch}") + private String imageFetchQueue; + + @Bean + public Queue imageFetchQueue() { + return new Queue(imageFetchQueue, false); + } + + @Bean + public Queue mangaUpdateQueue() { + return new Queue(mangaUpdateQueue, false); + } + @Bean public Queue mangaIngestQueue() { return new Queue(mangaIngestQueue, false); diff --git a/src/main/java/com/magamochi/common/exception/GlobalExceptionHandler.java b/src/main/java/com/magamochi/common/exception/GlobalExceptionHandler.java index a53d7ee..093cd65 100644 --- a/src/main/java/com/magamochi/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/magamochi/common/exception/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ package com.magamochi.common.exception; -import com.magamochi.common.dto.ErrorResponseDTO; +import com.magamochi.common.model.dto.ErrorResponseDTO; import java.time.Instant; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/com/magamochi/common/dto/DefaultResponseDTO.java b/src/main/java/com/magamochi/common/model/dto/DefaultResponseDTO.java similarity index 94% rename from src/main/java/com/magamochi/common/dto/DefaultResponseDTO.java rename to src/main/java/com/magamochi/common/model/dto/DefaultResponseDTO.java index 923a457..0c7b8a4 100644 --- a/src/main/java/com/magamochi/common/dto/DefaultResponseDTO.java +++ b/src/main/java/com/magamochi/common/model/dto/DefaultResponseDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.common.dto; +package com.magamochi.common.model.dto; import jakarta.annotation.Nullable; import java.time.Instant; diff --git a/src/main/java/com/magamochi/common/dto/ErrorResponseDTO.java b/src/main/java/com/magamochi/common/model/dto/ErrorResponseDTO.java similarity index 84% rename from src/main/java/com/magamochi/common/dto/ErrorResponseDTO.java rename to src/main/java/com/magamochi/common/model/dto/ErrorResponseDTO.java index c7d5580..d20979b 100644 --- a/src/main/java/com/magamochi/common/dto/ErrorResponseDTO.java +++ b/src/main/java/com/magamochi/common/model/dto/ErrorResponseDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.common.dto; +package com.magamochi.common.model.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/common/model/enumeration/ContentType.java b/src/main/java/com/magamochi/common/model/enumeration/ContentType.java new file mode 100644 index 0000000..79726ce --- /dev/null +++ b/src/main/java/com/magamochi/common/model/enumeration/ContentType.java @@ -0,0 +1,5 @@ +package com.magamochi.common.model.enumeration; + +public enum ContentType { + MANGA_COVER +} diff --git a/src/main/java/com/magamochi/common/queue/command/ImageFetchCommand.java b/src/main/java/com/magamochi/common/queue/command/ImageFetchCommand.java new file mode 100644 index 0000000..5f470c8 --- /dev/null +++ b/src/main/java/com/magamochi/common/queue/command/ImageFetchCommand.java @@ -0,0 +1,5 @@ +package com.magamochi.common.queue.command; + +import com.magamochi.common.model.enumeration.ContentType; + +public record ImageFetchCommand(long entityId, ContentType contentType, String url) {} diff --git a/src/main/java/com/magamochi/common/queue/producer/ImageFetchProducer.java b/src/main/java/com/magamochi/common/queue/producer/ImageFetchProducer.java new file mode 100644 index 0000000..9674a70 --- /dev/null +++ b/src/main/java/com/magamochi/common/queue/producer/ImageFetchProducer.java @@ -0,0 +1,23 @@ +package com.magamochi.common.queue.producer; + +import com.magamochi.common.queue.command.ImageFetchCommand; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class ImageFetchProducer { + private final RabbitTemplate rabbitTemplate; + + @Value("${queues.image-fetch}") + private String imageFetchQueue; + + public void sendImageFetchCommand(ImageFetchCommand command) { + rabbitTemplate.convertAndSend(imageFetchQueue, command); + log.info("Sent image fetch command: {}", command); + } +} diff --git a/src/main/java/com/magamochi/config/RateLimiterConfig.java b/src/main/java/com/magamochi/config/RateLimiterConfig.java index 2e8288b..112ed2e 100644 --- a/src/main/java/com/magamochi/config/RateLimiterConfig.java +++ b/src/main/java/com/magamochi/config/RateLimiterConfig.java @@ -16,6 +16,11 @@ public class RateLimiterConfig { return RateLimiter.create(1); } + @Bean + public RateLimiter aniListRateLimiter() { + return RateLimiter.create(0.5); + } + @Bean public RateLimiter imageDownloadRateLimiter() { return RateLimiter.create(10); diff --git a/src/main/java/com/magamochi/controller/AuthenticationController.java b/src/main/java/com/magamochi/controller/AuthenticationController.java index 502601b..a723e44 100644 --- a/src/main/java/com/magamochi/controller/AuthenticationController.java +++ b/src/main/java/com/magamochi/controller/AuthenticationController.java @@ -1,6 +1,6 @@ package com.magamochi.controller; -import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.common.model.dto.DefaultResponseDTO; import com.magamochi.model.dto.AuthenticationRequestDTO; import com.magamochi.model.dto.AuthenticationResponseDTO; import com.magamochi.model.dto.RefreshTokenRequestDTO; diff --git a/src/main/java/com/magamochi/controller/ManagementController.java b/src/main/java/com/magamochi/controller/ManagementController.java index 4d8b0c6..f7cd841 100644 --- a/src/main/java/com/magamochi/controller/ManagementController.java +++ b/src/main/java/com/magamochi/controller/ManagementController.java @@ -1,11 +1,9 @@ package com.magamochi.controller; import com.magamochi.client.NtfyClient; -import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.common.model.dto.DefaultResponseDTO; import com.magamochi.ingestion.task.IngestFromContentProvidersTask; -import com.magamochi.model.dto.UpdateMangaDataCommand; import com.magamochi.model.repository.UserRepository; -import com.magamochi.queue.UpdateMangaDataProducer; import com.magamochi.task.ImageCleanupTask; import com.magamochi.task.MangaFollowUpdateTask; import io.swagger.v3.oas.annotations.Operation; @@ -21,19 +19,6 @@ public class ManagementController { private final MangaFollowUpdateTask mangaFollowUpdateTask; private final UserRepository userRepository; private final NtfyClient ntfyClient; - private final UpdateMangaDataProducer updateMangaDataProducer; - - @Operation( - summary = "Trigger manga data update", - description = "Triggers the update of the metadata for a manga by its ID", - tags = {"Management"}, - operationId = "triggerUpdateMangaData") - @PostMapping("update-manga-data/{mangaId}") - public DefaultResponseDTO triggerUpdateMangaData(@PathVariable Long mangaId) { - updateMangaDataProducer.sendUpdateMangaDataCommand(new UpdateMangaDataCommand(mangaId)); - - return DefaultResponseDTO.ok().build(); - } @Operation( summary = "Cleanup unused S3 images", diff --git a/src/main/java/com/magamochi/controller/MangaChapterController.java b/src/main/java/com/magamochi/controller/MangaChapterController.java index 1160d35..1635b2d 100644 --- a/src/main/java/com/magamochi/controller/MangaChapterController.java +++ b/src/main/java/com/magamochi/controller/MangaChapterController.java @@ -1,6 +1,6 @@ package com.magamochi.controller; -import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.common.model.dto.DefaultResponseDTO; import com.magamochi.model.dto.MangaChapterImagesDTO; import com.magamochi.model.enumeration.ArchiveFileType; import com.magamochi.service.MangaChapterService; diff --git a/src/main/java/com/magamochi/controller/MangaController.java b/src/main/java/com/magamochi/controller/MangaController.java index 48241bb..3ffdd3d 100644 --- a/src/main/java/com/magamochi/controller/MangaController.java +++ b/src/main/java/com/magamochi/controller/MangaController.java @@ -1,48 +1,18 @@ package com.magamochi.controller; -import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.common.model.dto.DefaultResponseDTO; import com.magamochi.model.dto.MangaChapterDTO; -import com.magamochi.model.dto.MangaDTO; -import com.magamochi.model.dto.MangaListDTO; -import com.magamochi.model.dto.MangaListFilterDTO; -import com.magamochi.service.MangaService; +import com.magamochi.service.OldMangaService; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; import java.util.List; import lombok.RequiredArgsConstructor; -import org.springdoc.core.annotations.ParameterObject; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/mangas") @RequiredArgsConstructor public class MangaController { - private final MangaService mangaService; - - @Operation( - summary = "Get a list of mangas", - description = "Retrieve a list of mangas with their details.", - tags = {"Manga"}, - operationId = "getMangas") - @GetMapping - public DefaultResponseDTO> getMangas( - @ParameterObject MangaListFilterDTO filterDTO, - @Parameter(hidden = true) @ParameterObject @PageableDefault Pageable pageable) { - return DefaultResponseDTO.ok(mangaService.getMangas(filterDTO, pageable)); - } - - @Operation( - summary = "Get the details of a manga", - description = "Get the details of a manga by its ID", - tags = {"Manga"}, - operationId = "getManga") - @GetMapping("/{mangaId}") - public DefaultResponseDTO getManga(@PathVariable Long mangaId) { - return DefaultResponseDTO.ok(mangaService.getManga(mangaId)); - } + private final OldMangaService oldMangaService; @Operation( summary = "Get the available chapters for a specific manga/provider combination", @@ -52,7 +22,7 @@ public class MangaController { @GetMapping("/{mangaProviderId}/chapters") public DefaultResponseDTO> getMangaChapters( @PathVariable Long mangaProviderId) { - return DefaultResponseDTO.ok(mangaService.getMangaChapters(mangaProviderId)); + return DefaultResponseDTO.ok(oldMangaService.getMangaChapters(mangaProviderId)); } @Operation( @@ -62,7 +32,7 @@ public class MangaController { operationId = "fetchAllChapters") @PostMapping(value = "/{mangaProviderId}/fetch-all-chapters") public DefaultResponseDTO fetchAllChapters(@PathVariable Long mangaProviderId) { - mangaService.fetchAllNotDownloadedChapters(mangaProviderId); + oldMangaService.fetchAllNotDownloadedChapters(mangaProviderId); return DefaultResponseDTO.ok().build(); } @@ -74,7 +44,7 @@ public class MangaController { operationId = "fetchMangaChapters") @PostMapping("/{mangaProviderId}/fetch-chapters") public DefaultResponseDTO fetchMangaChapters(@PathVariable Long mangaProviderId) { - mangaService.fetchMangaChapters(mangaProviderId); + oldMangaService.fetchMangaChapters(mangaProviderId); return DefaultResponseDTO.ok().build(); } @@ -86,7 +56,7 @@ public class MangaController { operationId = "followManga") @PostMapping("/{mangaId}/followManga") public DefaultResponseDTO followManga(@PathVariable Long mangaId) { - mangaService.follow(mangaId); + oldMangaService.follow(mangaId); return DefaultResponseDTO.ok().build(); } @@ -98,7 +68,7 @@ public class MangaController { operationId = "unfollowManga") @PostMapping("/{mangaId}/unfollowManga") public DefaultResponseDTO unfollowManga(@PathVariable Long mangaId) { - mangaService.unfollow(mangaId); + oldMangaService.unfollow(mangaId); return DefaultResponseDTO.ok().build(); } diff --git a/src/main/java/com/magamochi/controller/MangaImportController.java b/src/main/java/com/magamochi/controller/MangaImportController.java index a4c72a8..06c9e19 100644 --- a/src/main/java/com/magamochi/controller/MangaImportController.java +++ b/src/main/java/com/magamochi/controller/MangaImportController.java @@ -1,9 +1,9 @@ package com.magamochi.controller; -import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.common.model.dto.DefaultResponseDTO; import com.magamochi.model.dto.ImportMangaResponseDTO; import com.magamochi.model.dto.ImportRequestDTO; -import com.magamochi.service.MangaImportService; +// import com.magamochi.service.MangaImportService; import com.magamochi.service.ProviderManualMangaImportService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -20,7 +20,7 @@ import org.springframework.web.multipart.MultipartFile; @RequestMapping("/manga/import") @RequiredArgsConstructor public class MangaImportController { - private final MangaImportService mangaImportService; + // private final MangaImportService mangaImportService; private final ProviderManualMangaImportService providerManualMangaImportService; @Operation( @@ -55,7 +55,7 @@ public class MangaImportController { @RequestPart("files") @NotNull List files) { - mangaImportService.importMangaFiles(malId, files); + // mangaImportService.importMangaFiles(malId, files); return DefaultResponseDTO.ok().build(); } diff --git a/src/main/java/com/magamochi/controller/MangaImportReviewController.java b/src/main/java/com/magamochi/controller/MangaImportReviewController.java deleted file mode 100644 index 733943c..0000000 --- a/src/main/java/com/magamochi/controller/MangaImportReviewController.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.magamochi.controller; - -import com.magamochi.common.dto.DefaultResponseDTO; -import com.magamochi.model.dto.ImportReviewDTO; -import com.magamochi.service.MangaImportReviewService; -import io.swagger.v3.oas.annotations.Operation; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/manga/import/review") -@RequiredArgsConstructor -public class MangaImportReviewController { - private final MangaImportReviewService mangaImportReviewService; - - @Operation( - summary = "Get list of pending import reviews", - description = "Get list of pending import reviews.", - tags = {"Manga Import Review"}, - operationId = "getImportReviews") - @GetMapping - public DefaultResponseDTO> getImportReviews() { - return DefaultResponseDTO.ok(mangaImportReviewService.getImportReviews()); - } - - @Operation( - summary = "Delete pending import review", - description = "Delete pending import review by ID.", - tags = {"Manga Import Review"}, - operationId = "deleteImportReview") - @DeleteMapping("/{id}") - public DefaultResponseDTO deleteImportReview(@PathVariable Long id) { - mangaImportReviewService.deleteImportReview(id); - - return DefaultResponseDTO.ok().build(); - } - - @Operation( - summary = "Resolve import review", - description = "Resolve import review by ID.", - tags = {"Manga Import Review"}, - operationId = "resolveImportReview") - @PostMapping - public DefaultResponseDTO resolveImportReview( - @RequestParam Long importReviewId, @RequestParam String malId) { - mangaImportReviewService.resolveImportReview(importReviewId, malId); - - return DefaultResponseDTO.ok().build(); - } -} diff --git a/src/main/java/com/magamochi/controller/UserFavoriteMangaController.java b/src/main/java/com/magamochi/controller/UserFavoriteMangaController.java index 7e254d1..1c45802 100644 --- a/src/main/java/com/magamochi/controller/UserFavoriteMangaController.java +++ b/src/main/java/com/magamochi/controller/UserFavoriteMangaController.java @@ -1,6 +1,6 @@ package com.magamochi.controller; -import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.common.model.dto.DefaultResponseDTO; import com.magamochi.service.UserFavoriteMangaService; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/ingestion/controller/IngestionController.java b/src/main/java/com/magamochi/ingestion/controller/IngestionController.java index c4539e4..7b46f07 100644 --- a/src/main/java/com/magamochi/ingestion/controller/IngestionController.java +++ b/src/main/java/com/magamochi/ingestion/controller/IngestionController.java @@ -1,6 +1,6 @@ package com.magamochi.ingestion.controller; -import com.magamochi.common.dto.DefaultResponseDTO; +import com.magamochi.common.model.dto.DefaultResponseDTO; import com.magamochi.ingestion.model.dto.ContentProviderListDTO; import com.magamochi.ingestion.service.ContentProviderService; import com.magamochi.ingestion.service.IngestionService; diff --git a/src/main/java/com/magamochi/ingestion/model/dto/MangaInfoDTO.java b/src/main/java/com/magamochi/ingestion/model/dto/MangaInfoDTO.java index 192fb3e..609e160 100644 --- a/src/main/java/com/magamochi/ingestion/model/dto/MangaInfoDTO.java +++ b/src/main/java/com/magamochi/ingestion/model/dto/MangaInfoDTO.java @@ -1,6 +1,5 @@ package com.magamochi.ingestion.model.dto; -import com.magamochi.model.enumeration.MangaStatus; import jakarta.validation.constraints.NotBlank; -public record MangaInfoDTO(@NotBlank String title, @NotBlank String url, MangaStatus status) {} +public record MangaInfoDTO(@NotBlank String title, @NotBlank String url) {} diff --git a/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java index f97bdd3..431b480 100644 --- a/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java @@ -6,7 +6,6 @@ import com.magamochi.ingestion.providers.ContentProviders; import com.magamochi.ingestion.providers.PagedContentProvider; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.model.entity.MangaContentProvider; -import com.magamochi.model.enumeration.MangaStatus; import java.io.IOException; import java.util.*; import java.util.regex.Pattern; @@ -116,25 +115,13 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv try { var linkElement = element.getElementsByTag("a").getFirst(); - var imageContainer = - linkElement.getElementsByClass("manga-card-image").getFirst(); var contentContainer = linkElement.getElementsByClass("manga-card-content").getFirst(); var title = contentContainer.getElementsByTag("h3").text(); var url = linkElement.attr("href"); - var status = - switch (imageContainer - .getElementsByClass("manga-status") - .text() - .toLowerCase()) { - case "em andamento" -> MangaStatus.ONGOING; - case "completo" -> MangaStatus.COMPLETED; - case "hiato" -> MangaStatus.HIATUS; - default -> MangaStatus.UNKNOWN; - }; - return new MangaInfoDTO(title, url, status); + return new MangaInfoDTO(title, url); } catch (Exception e) { return null; } diff --git a/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java index e3dbd57..5f0be16 100644 --- a/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java @@ -9,7 +9,6 @@ import com.magamochi.ingestion.providers.PagedContentProvider; import com.magamochi.ingestion.service.FlareService; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.model.entity.MangaContentProvider; -import com.magamochi.model.enumeration.MangaStatus; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -116,7 +115,7 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider } } - return new MangaInfoDTO(title.trim(), url.trim(), MangaStatus.UNKNOWN); + return new MangaInfoDTO(title.trim(), url.trim()); }) .toList(); } catch (NoSuchElementException e) { diff --git a/src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java index c9e7af1..d631095 100644 --- a/src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java @@ -10,7 +10,6 @@ import com.magamochi.ingestion.providers.PagedContentProvider; import com.magamochi.ingestion.service.FlareService; import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; import com.magamochi.model.entity.MangaContentProvider; -import com.magamochi.model.enumeration.MangaStatus; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -142,7 +141,7 @@ public class PinkRosaScanProvider implements ContentProvider, PagedContentProvid var textElement = linkElement.getElementsByTag("h3"); var title = textElement.text().trim(); - return new MangaInfoDTO(title, url, MangaStatus.UNKNOWN); + return new MangaInfoDTO(title, url); }) .toList(); } catch (NoSuchElementException e) { diff --git a/src/main/java/com/magamochi/model/dto/LanguageDTO.java b/src/main/java/com/magamochi/model/dto/LanguageDTO.java index 438264f..32c409d 100644 --- a/src/main/java/com/magamochi/model/dto/LanguageDTO.java +++ b/src/main/java/com/magamochi/model/dto/LanguageDTO.java @@ -2,7 +2,7 @@ package com.magamochi.model.dto; import static java.util.Objects.isNull; -import com.magamochi.model.entity.Language; +import com.magamochi.catalog.model.entity.Language; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/model/dto/MangaListUpdateCommand.java b/src/main/java/com/magamochi/model/dto/MangaListUpdateCommand.java deleted file mode 100644 index 3e15233..0000000 --- a/src/main/java/com/magamochi/model/dto/MangaListUpdateCommand.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.magamochi.model.dto; - -public record MangaListUpdateCommand(String contentProviderName, Integer page) {} diff --git a/src/main/java/com/magamochi/model/dto/MangaMessageDTO.java b/src/main/java/com/magamochi/model/dto/MangaMessageDTO.java deleted file mode 100644 index acda1c3..0000000 --- a/src/main/java/com/magamochi/model/dto/MangaMessageDTO.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.magamochi.model.dto; - -import com.magamochi.ingestion.model.dto.MangaInfoDTO; -import java.util.List; - -public record MangaMessageDTO( - String contentProviderName, List mangaInfoResponseDTOs) {} diff --git a/src/main/java/com/magamochi/model/dto/TitleMatchRequestDTO.java b/src/main/java/com/magamochi/model/dto/TitleMatchRequestDTO.java deleted file mode 100644 index 835f1c0..0000000 --- a/src/main/java/com/magamochi/model/dto/TitleMatchRequestDTO.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.magamochi.model.dto; - -import java.util.List; -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class TitleMatchRequestDTO { - private String title; - - private List options; - - @Builder.Default private int threshold = 85; -} diff --git a/src/main/java/com/magamochi/model/dto/TitleMatchResponseDTO.java b/src/main/java/com/magamochi/model/dto/TitleMatchResponseDTO.java deleted file mode 100644 index d5d9f0d..0000000 --- a/src/main/java/com/magamochi/model/dto/TitleMatchResponseDTO.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.magamochi.model.dto; - -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class TitleMatchResponseDTO { - boolean matchFound; - String bestMatch; - Double similarity; -} diff --git a/src/main/java/com/magamochi/model/dto/UpdateMangaDataCommand.java b/src/main/java/com/magamochi/model/dto/UpdateMangaDataCommand.java deleted file mode 100644 index bca7526..0000000 --- a/src/main/java/com/magamochi/model/dto/UpdateMangaDataCommand.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.magamochi.model.dto; - -public record UpdateMangaDataCommand(Long mangaId) {} diff --git a/src/main/java/com/magamochi/model/entity/MangaChapter.java b/src/main/java/com/magamochi/model/entity/MangaChapter.java index 71f48b2..1e6ed63 100644 --- a/src/main/java/com/magamochi/model/entity/MangaChapter.java +++ b/src/main/java/com/magamochi/model/entity/MangaChapter.java @@ -1,5 +1,6 @@ package com.magamochi.model.entity; +import com.magamochi.catalog.model.entity.Language; import jakarta.persistence.*; import java.time.Instant; import java.util.List; diff --git a/src/main/java/com/magamochi/model/entity/MangaContentProvider.java b/src/main/java/com/magamochi/model/entity/MangaContentProvider.java index eddf5aa..9bfcc2c 100644 --- a/src/main/java/com/magamochi/model/entity/MangaContentProvider.java +++ b/src/main/java/com/magamochi/model/entity/MangaContentProvider.java @@ -1,5 +1,6 @@ package com.magamochi.model.entity; +import com.magamochi.catalog.model.entity.Manga; import com.magamochi.ingestion.model.entity.ContentProvider; import jakarta.persistence.*; import java.time.Instant; diff --git a/src/main/java/com/magamochi/model/entity/UserFavoriteManga.java b/src/main/java/com/magamochi/model/entity/UserFavoriteManga.java index 102f50d..e091b63 100644 --- a/src/main/java/com/magamochi/model/entity/UserFavoriteManga.java +++ b/src/main/java/com/magamochi/model/entity/UserFavoriteManga.java @@ -1,5 +1,6 @@ package com.magamochi.model.entity; +import com.magamochi.catalog.model.entity.Manga; import jakarta.persistence.*; import java.time.Instant; import lombok.*; diff --git a/src/main/java/com/magamochi/model/entity/UserMangaFollow.java b/src/main/java/com/magamochi/model/entity/UserMangaFollow.java index c62932f..8535eaf 100644 --- a/src/main/java/com/magamochi/model/entity/UserMangaFollow.java +++ b/src/main/java/com/magamochi/model/entity/UserMangaFollow.java @@ -1,5 +1,6 @@ package com.magamochi.model.entity; +import com.magamochi.catalog.model.entity.Manga; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/model/repository/AuthorRepository.java b/src/main/java/com/magamochi/model/repository/AuthorRepository.java deleted file mode 100644 index 52e4465..0000000 --- a/src/main/java/com/magamochi/model/repository/AuthorRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.magamochi.model.repository; - -import com.magamochi.model.entity.Author; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface AuthorRepository extends JpaRepository { - Optional findByMalId(Long aLong); - - Optional findByName(String name); -} diff --git a/src/main/java/com/magamochi/model/repository/MangaContentProviderRepository.java b/src/main/java/com/magamochi/model/repository/MangaContentProviderRepository.java deleted file mode 100644 index 066f3b4..0000000 --- a/src/main/java/com/magamochi/model/repository/MangaContentProviderRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.magamochi.model.repository; - -import com.magamochi.ingestion.model.entity.ContentProvider; -import com.magamochi.model.entity.Manga; -import com.magamochi.model.entity.MangaContentProvider; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface MangaContentProviderRepository extends JpaRepository { - boolean existsByMangaAndContentProviderAndUrlIgnoreCase( - Manga manga, ContentProvider contentProvider, String url); - - Optional findByMangaTitleIgnoreCaseAndContentProvider( - String mangaTitle, ContentProvider contentProvider); -} diff --git a/src/main/java/com/magamochi/model/repository/MangaImportReviewRepository.java b/src/main/java/com/magamochi/model/repository/MangaImportReviewRepository.java deleted file mode 100644 index 023006f..0000000 --- a/src/main/java/com/magamochi/model/repository/MangaImportReviewRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.magamochi.model.repository; - -import com.magamochi.model.entity.MangaImportReview; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface MangaImportReviewRepository extends JpaRepository { - boolean existsByTitleIgnoreCaseAndUrlIgnoreCase(String title, String url); -} diff --git a/src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java b/src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java index 365c5ba..be50c56 100644 --- a/src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java +++ b/src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java @@ -1,6 +1,6 @@ package com.magamochi.model.repository; -import com.magamochi.model.entity.Manga; +import com.magamochi.catalog.model.entity.Manga; import com.magamochi.model.entity.User; import com.magamochi.model.entity.UserFavoriteManga; import java.util.Optional; diff --git a/src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java b/src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java index 154e532..d1ace6a 100644 --- a/src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java +++ b/src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java @@ -1,6 +1,6 @@ package com.magamochi.model.repository; -import com.magamochi.model.entity.Manga; +import com.magamochi.catalog.model.entity.Manga; import com.magamochi.model.entity.User; import com.magamochi.model.entity.UserMangaFollow; import java.util.List; diff --git a/src/main/java/com/magamochi/model/specification/MangaSpecification.java b/src/main/java/com/magamochi/model/specification/MangaSpecification.java index 4720847..bc653e5 100644 --- a/src/main/java/com/magamochi/model/specification/MangaSpecification.java +++ b/src/main/java/com/magamochi/model/specification/MangaSpecification.java @@ -2,9 +2,9 @@ package com.magamochi.model.specification; import static java.util.Objects.nonNull; -import com.magamochi.model.dto.MangaListFilterDTO; -import com.magamochi.model.entity.Author; -import com.magamochi.model.entity.Manga; +import com.magamochi.catalog.model.dto.MangaListFilterDTO; +import com.magamochi.catalog.model.entity.Author; +import com.magamochi.catalog.model.entity.Manga; import com.magamochi.model.entity.User; import jakarta.persistence.criteria.*; import java.util.ArrayList; diff --git a/src/main/java/com/magamochi/queue/UpdateMangaDataConsumer.java b/src/main/java/com/magamochi/queue/UpdateMangaDataConsumer.java deleted file mode 100644 index 2aa0169..0000000 --- a/src/main/java/com/magamochi/queue/UpdateMangaDataConsumer.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.magamochi.queue; - -import com.magamochi.model.dto.UpdateMangaDataCommand; -import com.magamochi.service.MangaImportService; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.amqp.rabbit.annotation.RabbitListener; -import org.springframework.stereotype.Service; - -@Log4j2 -@Service -@RequiredArgsConstructor -public class UpdateMangaDataConsumer { - private final MangaImportService mangaImportService; - - @RabbitListener(queues = "${rabbit-mq.queues.manga-data-update}") - public void receiveUpdateMangaDataCommand(UpdateMangaDataCommand command) { - log.info("Received update manga data command: {}", command); - mangaImportService.updateMangaData(command.mangaId()); - } -} diff --git a/src/main/java/com/magamochi/queue/UpdateMangaDataProducer.java b/src/main/java/com/magamochi/queue/UpdateMangaDataProducer.java deleted file mode 100644 index 8516218..0000000 --- a/src/main/java/com/magamochi/queue/UpdateMangaDataProducer.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.magamochi.queue; - -import com.magamochi.model.dto.UpdateMangaDataCommand; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -@Log4j2 -@Service -@RequiredArgsConstructor -public class UpdateMangaDataProducer { - private final RabbitTemplate rabbitTemplate; - - @Value("${rabbit-mq.queues.manga-data-update}") - private String mangaDataUpdateQueue; - - public void sendUpdateMangaDataCommand(UpdateMangaDataCommand command) { - rabbitTemplate.convertAndSend(mangaDataUpdateQueue, command); - log.info("Sent update manga data command: {}", command); - } -} diff --git a/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java b/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java index 64b40f2..d6d9168 100644 --- a/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java +++ b/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java @@ -1,7 +1,7 @@ package com.magamochi.queue; import com.magamochi.model.dto.UpdateMangaFollowChapterListCommand; -import com.magamochi.service.MangaService; +import com.magamochi.service.OldMangaService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.rabbit.annotation.RabbitListener; @@ -11,11 +11,11 @@ import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class UpdateMangaFollowChapterListConsumer { - private final MangaService mangaService; + private final OldMangaService oldMangaService; @RabbitListener(queues = "${rabbit-mq.queues.manga-follow-update-chapter}") public void receiveMangaFollowUpdateChapterCommand(UpdateMangaFollowChapterListCommand command) { log.info("Received update followed manga chapter list command: {}", command); - mangaService.fetchFollowedMangaChapters(command.mangaProviderId()); + oldMangaService.fetchFollowedMangaChapters(command.mangaProviderId()); } } diff --git a/src/main/java/com/magamochi/queue/UpdateMangaListConsumer.java b/src/main/java/com/magamochi/queue/UpdateMangaListConsumer.java deleted file mode 100644 index 6a04c3c..0000000 --- a/src/main/java/com/magamochi/queue/UpdateMangaListConsumer.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.magamochi.queue; - -import com.magamochi.model.dto.MangaListUpdateCommand; -import com.magamochi.service.MangaListService; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.amqp.rabbit.annotation.RabbitListener; -import org.springframework.stereotype.Service; - -@Log4j2 -@Service -@RequiredArgsConstructor -public class UpdateMangaListConsumer { - private final MangaListService mangaListService; - - @RabbitListener(queues = "${rabbit-mq.queues.manga-list-update}") - public void receiveUpdateMangaListCommand(MangaListUpdateCommand command) { - log.info("Received update manga list command: {}", command); - mangaListService.updateMangaList(command.contentProviderName(), command.page()); - } -} diff --git a/src/main/java/com/magamochi/queue/UpdateMangaListProducer.java b/src/main/java/com/magamochi/queue/UpdateMangaListProducer.java deleted file mode 100644 index 7d74af5..0000000 --- a/src/main/java/com/magamochi/queue/UpdateMangaListProducer.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.magamochi.queue; - -import com.magamochi.model.dto.MangaListUpdateCommand; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -@Log4j2 -@Service -@RequiredArgsConstructor -public class UpdateMangaListProducer { - private final RabbitTemplate rabbitTemplate; - - @Value("${rabbit-mq.queues.manga-list-update}") - private String mangaListUpdateQueue; - - public void sendUpdateMangaListCommand(MangaListUpdateCommand command) { - rabbitTemplate.convertAndSend(mangaListUpdateQueue, command); - log.info("Sent update manga list command: {}", command); - } -} diff --git a/src/main/java/com/magamochi/service/MangaCreationService.java b/src/main/java/com/magamochi/service/MangaCreationService.java deleted file mode 100644 index 9e4f642..0000000 --- a/src/main/java/com/magamochi/service/MangaCreationService.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.magamochi.service; - -import static java.util.Objects.nonNull; - -import com.google.common.util.concurrent.RateLimiter; -import com.magamochi.client.AniListClient; -import com.magamochi.client.JikanClient; -import com.magamochi.ingestion.model.entity.ContentProvider; -import com.magamochi.model.dto.TitleMatchRequestDTO; -import com.magamochi.model.dto.UpdateMangaDataCommand; -import com.magamochi.model.entity.Manga; -import com.magamochi.model.entity.MangaImportReview; -import com.magamochi.model.repository.MangaImportReviewRepository; -import com.magamochi.model.repository.MangaRepository; -import com.magamochi.queue.UpdateMangaDataProducer; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.stereotype.Service; - -@Log4j2 -@Service -@RequiredArgsConstructor -public class MangaCreationService { - private final MangaRepository mangaRepository; - private final MangaImportReviewRepository mangaImportReviewRepository; - - private final TitleMatcherService titleMatcherService; - - private final JikanClient jikanClient; - private final AniListClient aniListClient; - - private final RateLimiter jikanRateLimiter; - - private final UpdateMangaDataProducer updateMangaDataProducer; - - public Manga getOrCreateManga(String title, String url, ContentProvider contentProvider) { - var existingManga = mangaRepository.findByTitleIgnoreCase(title); - if (existingManga.isPresent()) { - return existingManga.get(); - } - - jikanRateLimiter.acquire(); - var jikanResults = jikanClient.mangaSearch(title).data(); - if (jikanResults.isEmpty()) { - createMangaImportReview(title, url, contentProvider); - log.warn("No manga found with title {}", title); - return null; - } - - var titleMatchResponse = - titleMatcherService.findBestMatch( - TitleMatchRequestDTO.builder() - .title(title) - .options( - jikanResults.stream() - .flatMap( - results -> - results.titles().stream() - .map(JikanClient.SearchResponse.MangaData.TitleData::title)) - .toList()) - .build()); - - if (!titleMatchResponse.isMatchFound()) { - createMangaImportReview(title, url, contentProvider); - log.warn("No match found for manga with title {}", title); - return null; - } - - var resultOptional = - jikanResults.stream() - .filter( - results -> - results.titles().stream() - .map(JikanClient.SearchResponse.MangaData.TitleData::title) - .toList() - .contains(titleMatchResponse.getBestMatch())) - .findFirst(); - if (resultOptional.isEmpty()) { - createMangaImportReview(title, url, contentProvider); - log.warn("No match found for manga with title {}", title); - return null; - } - - var result = resultOptional.get(); - return getOrCreateManga(result.mal_id(), null, result.title()); - } - - public Manga getOrCreateManga(Long malId, Long aniListId) { - if (nonNull(malId)) { - try { - jikanRateLimiter.acquire(); - var data = jikanClient.getMangaById(malId); - return getOrCreateManga(data.data().mal_id(), aniListId, data.data().title()); - } catch (feign.FeignException.NotFound e) { - log.warn("Manga not found on MyAnimeList for ID: {}", malId); - } - } - - if (nonNull(aniListId)) { - try { - var query = - """ - query ($id: Int) { - Media (id: $id, type: MANGA) { - id - idMal - title { - romaji - english - native - } - } - } - """; - var request = - new AniListClient.GraphQLRequest( - query, new AniListClient.GraphQLRequest.Variables(aniListId)); - var data = aniListClient.getManga(request).data().Media(); - - String title = - nonNull(data.title().english()) ? data.title().english() : data.title().romaji(); - return getOrCreateManga(data.idMal(), data.id(), title); - } catch (feign.FeignException.NotFound e) { - log.warn("Manga not found on AniList for ID: {}", aniListId); - } - } - - throw new RuntimeException("Could not find manga on any provider"); - } - - private Manga getOrCreateManga(Long malId, Long aniListId, String title) { - var mangaOptional = java.util.Optional.empty(); - - if (nonNull(malId)) { - mangaOptional = mangaRepository.findByMalId(malId); - } - - if (mangaOptional.isEmpty() && nonNull(aniListId)) { - mangaOptional = mangaRepository.findByAniListId(aniListId); - } - - return mangaOptional.orElseGet( - () -> { - var manga = - mangaRepository.save( - Manga.builder().title(title).malId(malId).aniListId(aniListId).build()); - - updateMangaDataProducer.sendUpdateMangaDataCommand( - new UpdateMangaDataCommand(manga.getId())); - - return manga; - }); - } - - private void createMangaImportReview(String title, String url, ContentProvider contentProvider) { - if (mangaImportReviewRepository.existsByTitleIgnoreCaseAndUrlIgnoreCase(title, url)) { - return; - } - - mangaImportReviewRepository.save( - MangaImportReview.builder().title(title).url(url).contentProvider(contentProvider).build()); - } -} diff --git a/src/main/java/com/magamochi/service/MangaImportReviewService.java b/src/main/java/com/magamochi/service/MangaImportReviewService.java deleted file mode 100644 index 56e49c3..0000000 --- a/src/main/java/com/magamochi/service/MangaImportReviewService.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.magamochi.service; - -import static java.util.Objects.isNull; - -import com.google.common.util.concurrent.RateLimiter; -import com.magamochi.client.JikanClient; -import com.magamochi.common.exception.NotFoundException; -import com.magamochi.model.dto.ImportReviewDTO; -import com.magamochi.model.dto.UpdateMangaDataCommand; -import com.magamochi.model.entity.Manga; -import com.magamochi.model.entity.MangaContentProvider; -import com.magamochi.model.entity.MangaImportReview; -import com.magamochi.model.repository.MangaContentProviderRepository; -import com.magamochi.model.repository.MangaImportReviewRepository; -import com.magamochi.model.repository.MangaRepository; -import com.magamochi.queue.UpdateMangaDataProducer; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class MangaImportReviewService { - private final MangaImportReviewRepository mangaImportReviewRepository; - private final MangaRepository mangaRepository; - private final MangaContentProviderRepository mangaContentProviderRepository; - - private final JikanClient jikanClient; - - private final RateLimiter jikanRateLimiter; - - private final UpdateMangaDataProducer updateMangaDataProducer; - - public List getImportReviews() { - return mangaImportReviewRepository.findAll().stream().map(ImportReviewDTO::from).toList(); - } - - public void deleteImportReview(Long id) { - var importReview = getImportReviewThrowIfNotFound(id); - - mangaImportReviewRepository.delete(importReview); - } - - public void resolveImportReview(Long id, String malId) { - var importReview = getImportReviewThrowIfNotFound(id); - - jikanRateLimiter.acquire(); - var jikanResult = jikanClient.getMangaById(Long.parseLong(malId)).data(); - - if (isNull(jikanResult)) { - throw new NotFoundException("MyAnimeList manga not found for ID: " + id); - } - - var manga = - mangaRepository - .findByTitleIgnoreCase(jikanResult.title()) - .orElseGet( - () -> - mangaRepository.save( - Manga.builder() - .title(jikanResult.title()) - .malId(Long.parseLong(malId)) - .build())); - - if (!mangaContentProviderRepository.existsByMangaAndContentProviderAndUrlIgnoreCase( - manga, importReview.getContentProvider(), importReview.getUrl())) { - mangaContentProviderRepository.save( - MangaContentProvider.builder() - .manga(manga) - .mangaTitle(importReview.getTitle()) - .contentProvider(importReview.getContentProvider()) - .url(importReview.getUrl()) - .build()); - } - - mangaImportReviewRepository.delete(importReview); - - updateMangaDataProducer.sendUpdateMangaDataCommand(new UpdateMangaDataCommand(manga.getId())); - } - - private MangaImportReview getImportReviewThrowIfNotFound(Long id) { - return mangaImportReviewRepository - .findById(id) - .orElseThrow(() -> new NotFoundException("Import review not found for ID: " + id)); - } -} diff --git a/src/main/java/com/magamochi/service/MangaImportService.java b/src/main/java/com/magamochi/service/MangaImportService.java index 737f5fb..ea64e30 100644 --- a/src/main/java/com/magamochi/service/MangaImportService.java +++ b/src/main/java/com/magamochi/service/MangaImportService.java @@ -1,434 +1,438 @@ -package com.magamochi.service; - -import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; - -import com.google.common.util.concurrent.RateLimiter; -import com.magamochi.catalog.model.entity.Genre; -import com.magamochi.catalog.model.repository.GenreRepository; -import com.magamochi.client.AniListClient; -import com.magamochi.client.JikanClient; -import com.magamochi.common.exception.NotFoundException; -import com.magamochi.ingestion.model.entity.ContentProvider; -import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.entity.*; -import com.magamochi.model.repository.*; -import com.magamochi.util.DoubleUtil; -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.stream.IntStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -@Log4j2 -@Service -@RequiredArgsConstructor -public class MangaImportService { - private final ProviderService providerService; - private final MangaCreationService mangaCreationService; - private final ImageService imageService; - private final LanguageService languageService; - - private final GenreRepository genreRepository; - private final MangaGenreRepository mangaGenreRepository; - private final MangaContentProviderRepository mangaContentProviderRepository; - private final AuthorRepository authorRepository; - private final MangaAuthorRepository mangaAuthorRepository; - private final MangaChapterRepository mangaChapterRepository; - private final MangaRepository mangaRepository; - - private final JikanClient jikanClient; - private final AniListClient aniListClient; - private final MangaChapterImageRepository mangaChapterImageRepository; - private final MangaAlternativeTitlesRepository mangaAlternativeTitlesRepository; - - private final RateLimiter jikanRateLimiter; - - public void importMangaFiles(String malId, List files) { - log.info("Importing manga files for MAL ID {}", malId); - var provider = providerService.getOrCreateProvider("Manual Import", false); - - jikanRateLimiter.acquire(); - var mangaData = jikanClient.getMangaById(Long.parseLong(malId)); - - var mangaProvider = getOrCreateMangaProvider(mangaData.data().title(), provider); - - var sortedFiles = files.stream().sorted(Comparator.comparing(MultipartFile::getName)).toList(); - - IntStream.rangeClosed(1, sortedFiles.size()) - .forEach( - fileIndex -> { - var file = sortedFiles.get(fileIndex - 1); - log.info( - "Importing file {}/{}: {}, for Mangá {}", - fileIndex, - sortedFiles.size(), - file.getOriginalFilename(), - mangaProvider.getManga().getTitle()); - - var chapter = - persistMangaChapter( - mangaProvider, - new ContentProviderMangaChapterResponseDTO( - removeFileExtension(file.getOriginalFilename()), - "manual_" + file.getOriginalFilename(), - file.getOriginalFilename(), - "en-US")); - - List allChapterImages = new ArrayList<>(); - try (InputStream is = file.getInputStream(); - ZipInputStream zis = new ZipInputStream(is)) { - ZipEntry entry; - var position = 0; - - while ((entry = zis.getNextEntry()) != null) { - if (entry.isDirectory()) { - continue; - } - - var os = new ByteArrayOutputStream(); - zis.transferTo(os); - var bytes = os.toByteArray(); - - var image = - imageService.uploadImage(bytes, "image/jpeg", "chapter/" + chapter.getId()); - - var chapterImage = - MangaChapterImage.builder() - .position(position++) - .image(image) - .mangaChapter(chapter) - .build(); - - allChapterImages.add(chapterImage); - zis.closeEntry(); - } - - log.info("Chapter images added for chapter {}", chapter.getTitle()); - } catch (IOException e) { - throw new RuntimeException(e); - } - - mangaChapterImageRepository.saveAll(allChapterImages); - chapter.setDownloaded(true); - mangaChapterRepository.save(chapter); - }); - - log.info("Import manga files for MAL ID {} completed.", malId); - } - - public void updateMangaData(Long mangaId) { - var manga = - mangaRepository - .findById(mangaId) - .orElseThrow(() -> new NotFoundException("Manga not found for ID: " + mangaId)); - - updateMangaData(manga); - } - - public void updateMangaData(Manga manga) { - log.info("Updating manga {}", manga.getTitle()); - - if (nonNull(manga.getMalId())) { - try { - updateFromJikan(manga); - return; - } catch (Exception e) { - log.warn( - "Error updating manga data from Jikan for manga {}. Trying AniList... Error: {}", - manga.getTitle(), - e.getMessage()); - } - } - - if (nonNull(manga.getAniListId())) { - try { - updateFromAniList(manga); - return; - } catch (Exception e) { - log.warn( - "Error updating manga data from AniList for manga {}. Error: {}", - manga.getTitle(), - e.getMessage()); - } - } - - log.warn( - "Could not update manga data for {}. No provider data available/found.", manga.getTitle()); - } - - private void updateFromJikan(Manga manga) throws IOException, URISyntaxException { - jikanRateLimiter.acquire(); - var mangaData = jikanClient.getMangaById(manga.getMalId()); - - manga.setSynopsis(mangaData.data().synopsis()); - manga.setStatus(mangaData.data().status()); - manga.setScore(DoubleUtil.round((double) mangaData.data().score(), 2)); - manga.setPublishedFrom(mangaData.data().published().from()); - manga.setPublishedTo(mangaData.data().published().to()); - manga.setChapterCount(mangaData.data().chapters()); - - var authors = - mangaData.data().authors().stream() - .map( - authorData -> - authorRepository - .findByMalId(authorData.mal_id()) - .orElseGet( - () -> - authorRepository.save( - Author.builder() - .malId(authorData.mal_id()) - .name(authorData.name()) - .build()))) - .toList(); - - updateMangaAuthors(manga, authors); - - var genres = - mangaData.data().genres().stream() - .map( - genreData -> - genreRepository - .findByMalId(genreData.mal_id()) - .orElseGet( - () -> - genreRepository.save( - Genre.builder() - .malId(genreData.mal_id()) - .name(genreData.name()) - .build()))) - .toList(); - - updateMangaGenres(manga, genres); - - if (isNull(manga.getCoverImage())) { - downloadCoverImage(manga, mangaData.data().images().jpg().large_image_url()); - } - - var mangaEntity = mangaRepository.save(manga); - var alternativeTitles = - mangaData.data().title_synonyms().stream() - .map(at -> MangaAlternativeTitle.builder().manga(mangaEntity).title(at).build()) - .toList(); - mangaAlternativeTitlesRepository.saveAll(alternativeTitles); - } - - private void updateFromAniList(Manga manga) throws IOException, URISyntaxException { - var query = - """ - query ($id: Int) { - Media (id: $id, type: MANGA) { - startDate { year month day } - endDate { year month day } - description - status - averageScore - chapters - coverImage { large } - genres - staff { - edges { - role - node { - name { - full - } - } - } - } - } - } - """; - var request = - new AniListClient.GraphQLRequest( - query, new AniListClient.GraphQLRequest.Variables(manga.getAniListId())); - var media = aniListClient.getManga(request).data().Media(); - - manga.setSynopsis(media.description()); - manga.setStatus(mapAniListStatus(media.status())); - manga.setScore(DoubleUtil.round((double) media.averageScore() / 10, 2)); // 0-100 -> 0-10 - manga.setPublishedFrom(convertFuzzyDate(media.startDate())); - manga.setPublishedTo(convertFuzzyDate(media.endDate())); - manga.setChapterCount(media.chapters()); - - var authors = - media.staff().edges().stream() - .filter(edge -> isAuthorRole(edge.role())) - .map(edge -> edge.node().name().full()) - .distinct() - .map( - name -> - authorRepository - .findByName(name) - .orElseGet( - () -> authorRepository.save(Author.builder().name(name).build()))) - .toList(); - - updateMangaAuthors(manga, authors); - - var genres = - media.genres().stream() - .map( - name -> - genreRepository - .findByName(name) - .orElseGet(() -> genreRepository.save(Genre.builder().name(name).build()))) - .toList(); - - updateMangaGenres(manga, genres); - - if (isNull(manga.getCoverImage())) { - downloadCoverImage(manga, media.coverImage().large()); - } - - mangaRepository.save(manga); - } - - private boolean isAuthorRole(String role) { - return role.equalsIgnoreCase("Story & Art") - || role.equalsIgnoreCase("Story") - || role.equalsIgnoreCase("Art"); - } - - private String mapAniListStatus(String status) { - return switch (status) { - case "RELEASING" -> "Publishing"; - case "FINISHED" -> "Finished"; - case "NOT_YET_RELEASED" -> "Not yet published"; - default -> "Unknown"; - }; - } - - private OffsetDateTime convertFuzzyDate(AniListClient.MangaResponse.Manga.FuzzyDate date) { - if (isNull(date) || isNull(date.year())) { - return null; - } - return OffsetDateTime.of( - date.year(), - isNull(date.month()) ? 1 : date.month(), - isNull(date.day()) ? 1 : date.day(), - 0, - 0, - 0, - 0, - ZoneOffset.UTC); - } - - private void updateMangaAuthors(Manga manga, List authors) { - var mangaAuthors = - authors.stream() - .map( - author -> - mangaAuthorRepository - .findByMangaAndAuthor(manga, author) - .orElseGet( - () -> - mangaAuthorRepository.save( - MangaAuthor.builder().manga(manga).author(author).build()))) - .toList(); - manga.setMangaAuthors(mangaAuthors); - } - - private void updateMangaGenres(Manga manga, List genres) { - var mangaGenres = - genres.stream() - .map( - genre -> - mangaGenreRepository - .findByMangaAndGenre(manga, genre) - .orElseGet( - () -> - mangaGenreRepository.save( - MangaGenre.builder().manga(manga).genre(genre).build()))) - .toList(); - manga.setMangaGenres(mangaGenres); - } - - private void downloadCoverImage(Manga manga, String imageUrl) - throws IOException, URISyntaxException { - var inputStream = - new BufferedInputStream(new URL(new URI(imageUrl).toASCIIString()).openStream()); - - var bytes = inputStream.readAllBytes(); - - inputStream.close(); - var image = imageService.uploadImage(bytes, "image/jpeg", "cover"); - - manga.setCoverImage(image); - } - - public MangaChapter persistMangaChapter( - MangaContentProvider mangaContentProvider, ContentProviderMangaChapterResponseDTO chapter) { - var mangaChapter = - mangaChapterRepository - .findByMangaContentProviderAndUrlIgnoreCase(mangaContentProvider, chapter.chapterUrl()) - .orElseGet(MangaChapter::new); - - mangaChapter.setMangaContentProvider(mangaContentProvider); - mangaChapter.setTitle(chapter.chapterTitle()); - mangaChapter.setUrl(chapter.chapterUrl()); - - var language = languageService.getOrThrow(chapter.languageCode()); - mangaChapter.setLanguage(language); - - if (nonNull(chapter.chapter())) { - try { - mangaChapter.setChapterNumber(Integer.parseInt(chapter.chapter())); - } catch (NumberFormatException e) { - log.warn( - "Could not parse chapter number {} from manga {}", - chapter.chapter(), - mangaContentProvider.getManga().getTitle()); - } - } - - return mangaChapterRepository.save(mangaChapter); - } - - private MangaContentProvider getOrCreateMangaProvider( - String title, ContentProvider contentProvider) { - return mangaContentProviderRepository - .findByMangaTitleIgnoreCaseAndContentProvider(title, contentProvider) - .orElseGet( - () -> { - jikanRateLimiter.acquire(); - var manga = mangaCreationService.getOrCreateManga(title, "manual", contentProvider); - - return mangaContentProviderRepository.save( - MangaContentProvider.builder() - .manga(manga) - .mangaTitle(manga.getTitle()) - .contentProvider(contentProvider) - .url("manual") - .build()); - }); - } - - private String removeFileExtension(String filename) { - if (StringUtils.isBlank(filename)) { - return filename; - } - - int lastDotIndex = filename.lastIndexOf('.'); - - // No dot, or dot is the first character (like .gitignore) - if (lastDotIndex <= 0) { - return filename; - } - - return filename.substring(0, lastDotIndex); - } -} +// package com.magamochi.service; +// +// import static java.util.Objects.isNull; +// import static java.util.Objects.nonNull; +// +// import com.google.common.util.concurrent.RateLimiter; +// import com.magamochi.catalog.model.entity.Genre; +// import com.magamochi.catalog.model.repository.GenreRepository; +// import com.magamochi.catalog.client.AniListClient; +// import com.magamochi.catalog.client.JikanClient; +// import com.magamochi.common.exception.NotFoundException; +// import com.magamochi.ingestion.model.entity.ContentProvider; +// import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; +// import com.magamochi.model.entity.*; +// import com.magamochi.model.repository.*; +// import com.magamochi.catalog.util.DoubleUtil; +// import java.io.*; +// import java.net.URI; +// import java.net.URISyntaxException; +// import java.net.URL; +// import java.time.OffsetDateTime; +// import java.time.ZoneOffset; +// import java.util.ArrayList; +// import java.util.Comparator; +// import java.util.List; +// import java.util.stream.IntStream; +// import java.util.zip.ZipEntry; +// import java.util.zip.ZipInputStream; +// import lombok.RequiredArgsConstructor; +// import lombok.extern.log4j.Log4j2; +// import org.apache.commons.lang3.StringUtils; +// import org.springframework.stereotype.Service; +// import org.springframework.web.multipart.MultipartFile; +// +// @Log4j2 +//// @Service +// @RequiredArgsConstructor +// public class MangaImportService { +// private final ProviderService providerService; +// private final MangaCreationService mangaCreationService; +// private final ImageService imageService; +// private final LanguageService languageService; +// +// private final GenreRepository genreRepository; +// private final MangaGenreRepository mangaGenreRepository; +// private final MangaContentProviderRepository mangaContentProviderRepository; +// private final AuthorRepository authorRepository; +// private final MangaAuthorRepository mangaAuthorRepository; +// private final MangaChapterRepository mangaChapterRepository; +// private final MangaRepository mangaRepository; +// +// private final JikanClient jikanClient; +// private final AniListClient aniListClient; +// private final MangaChapterImageRepository mangaChapterImageRepository; +// private final MangaAlternativeTitlesRepository mangaAlternativeTitlesRepository; +// +// private final RateLimiter jikanRateLimiter; +// +// public void importMangaFiles(String malId, List files) { +// log.info("Importing manga files for MAL ID {}", malId); +// var provider = providerService.getOrCreateProvider("Manual Import", false); +// +// jikanRateLimiter.acquire(); +// var mangaData = jikanClient.getMangaById(Long.parseLong(malId)); +// +// var mangaProvider = getOrCreateMangaProvider(mangaData.data().title(), provider); +// +// var sortedFiles = +// files.stream().sorted(Comparator.comparing(MultipartFile::getName)).toList(); +// +// IntStream.rangeClosed(1, sortedFiles.size()) +// .forEach( +// fileIndex -> { +// var file = sortedFiles.get(fileIndex - 1); +// log.info( +// "Importing file {}/{}: {}, for Mangá {}", +// fileIndex, +// sortedFiles.size(), +// file.getOriginalFilename(), +// mangaProvider.getManga().getTitle()); +// +// var chapter = +// persistMangaChapter( +// mangaProvider, +// new ContentProviderMangaChapterResponseDTO( +// removeFileExtension(file.getOriginalFilename()), +// "manual_" + file.getOriginalFilename(), +// file.getOriginalFilename(), +// "en-US")); +// +// List allChapterImages = new ArrayList<>(); +// try (InputStream is = file.getInputStream(); +// ZipInputStream zis = new ZipInputStream(is)) { +// ZipEntry entry; +// var position = 0; +// +// while ((entry = zis.getNextEntry()) != null) { +// if (entry.isDirectory()) { +// continue; +// } +// +// var os = new ByteArrayOutputStream(); +// zis.transferTo(os); +// var bytes = os.toByteArray(); +// +// var image = +// imageService.uploadImage(bytes, "image/jpeg", "chapter/" + chapter.getId()); +// +// var chapterImage = +// MangaChapterImage.builder() +// .position(position++) +// .image(image) +// .mangaChapter(chapter) +// .build(); +// +// allChapterImages.add(chapterImage); +// zis.closeEntry(); +// } +// +// log.info("Chapter images added for chapter {}", chapter.getTitle()); +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// +// mangaChapterImageRepository.saveAll(allChapterImages); +// chapter.setDownloaded(true); +// mangaChapterRepository.save(chapter); +// }); +// +// log.info("Import manga files for MAL ID {} completed.", malId); +// } +// +// public void updateMangaData(Long mangaId) { +// var manga = +// mangaRepository +// .findById(mangaId) +// .orElseThrow(() -> new NotFoundException("Manga not found for ID: " + mangaId)); +// +// updateMangaData(manga); +// } +// +// public void updateMangaData(Manga manga) { +// log.info("Updating manga {}", manga.getTitle()); +// +// if (nonNull(manga.getMalId())) { +// try { +// updateFromJikan(manga); +// return; +// } catch (Exception e) { +// log.warn( +// "Error updating manga data from Jikan for manga {}. Trying AniList... Error: {}", +// manga.getTitle(), +// e.getMessage()); +// } +// } +// +// if (nonNull(manga.getAniListId())) { +// try { +// updateFromAniList(manga); +// return; +// } catch (Exception e) { +// log.warn( +// "Error updating manga data from AniList for manga {}. Error: {}", +// manga.getTitle(), +// e.getMessage()); +// } +// } +// +// log.warn( +// "Could not update manga data for {}. No provider data available/found.", +// manga.getTitle()); +// } +// +// private void updateFromJikan(Manga manga) throws IOException, URISyntaxException { +// jikanRateLimiter.acquire(); +// var mangaData = jikanClient.getMangaById(manga.getMalId()); +// +// manga.setSynopsis(mangaData.data().synopsis()); +// manga.setStatus(mangaData.data().status()); +// manga.setScore(DoubleUtil.round((double) mangaData.data().score(), 2)); +// manga.setPublishedFrom(mangaData.data().published().from()); +// manga.setPublishedTo(mangaData.data().published().to()); +// manga.setChapterCount(mangaData.data().chapters()); +// +// var authors = +// mangaData.data().authors().stream() +// .map( +// authorData -> +// authorRepository +// .findByMalId(authorData.mal_id()) +// .orElseGet( +// () -> +// authorRepository.save( +// Author.builder() +// .malId(authorData.mal_id()) +// .name(authorData.name()) +// .build()))) +// .toList(); +// +// updateMangaAuthors(manga, authors); +// +// var genres = +// mangaData.data().genres().stream() +// .map( +// genreData -> +// genreRepository +// .findByMalId(genreData.mal_id()) +// .orElseGet( +// () -> +// genreRepository.save( +// Genre.builder() +// .malId(genreData.mal_id()) +// .name(genreData.name()) +// .build()))) +// .toList(); +// +// updateMangaGenres(manga, genres); +// +// if (isNull(manga.getCoverImage())) { +// downloadCoverImage(manga, mangaData.data().images().jpg().large_image_url()); +// } +// +// var mangaEntity = mangaRepository.save(manga); +// var alternativeTitles = +// mangaData.data().title_synonyms().stream() +// .map(at -> MangaAlternativeTitle.builder().manga(mangaEntity).title(at).build()) +// .toList(); +// mangaAlternativeTitlesRepository.saveAll(alternativeTitles); +// } +// +// private void updateFromAniList(Manga manga) throws IOException, URISyntaxException { +// var query = +// """ +// query ($id: Int) { +// Media (id: $id, type: MANGA) { +// startDate { year month day } +// endDate { year month day } +// description +// status +// averageScore +// chapters +// coverImage { large } +// genres +// staff { +// edges { +// role +// node { +// name { +// full +// } +// } +// } +// } +// } +// } +// """; +// var request = +// new AniListClient.GraphQLRequest( +// query, new AniListClient.GraphQLRequest.Variables(manga.getAniListId())); +// var media = aniListClient.getManga(request).data().Media(); +// +// manga.setSynopsis(media.description()); +// manga.setStatus(mapAniListStatus(media.status())); +// manga.setScore(DoubleUtil.round((double) media.averageScore() / 10, 2)); // 0-100 -> 0-10 +// manga.setPublishedFrom(convertFuzzyDate(media.startDate())); +// manga.setPublishedTo(convertFuzzyDate(media.endDate())); +// manga.setChapterCount(media.chapters()); +// +// var authors = +// media.staff().edges().stream() +// .filter(edge -> isAuthorRole(edge.role())) +// .map(edge -> edge.node().name().full()) +// .distinct() +// .map( +// name -> +// authorRepository +// .findByName(name) +// .orElseGet( +// () -> authorRepository.save(Author.builder().name(name).build()))) +// .toList(); +// +// updateMangaAuthors(manga, authors); +// +// var genres = +// media.genres().stream() +// .map( +// name -> +// genreRepository +// .findByName(name) +// .orElseGet(() -> +// genreRepository.save(Genre.builder().name(name).build()))) +// .toList(); +// +// updateMangaGenres(manga, genres); +// +// if (isNull(manga.getCoverImage())) { +// downloadCoverImage(manga, media.coverImage().large()); +// } +// +// mangaRepository.save(manga); +// } +// +// private boolean isAuthorRole(String role) { +// return role.equalsIgnoreCase("Story & Art") +// || role.equalsIgnoreCase("Story") +// || role.equalsIgnoreCase("Art"); +// } +// +// private String mapAniListStatus(String status) { +// return switch (status) { +// case "RELEASING" -> "Publishing"; +// case "FINISHED" -> "Finished"; +// case "NOT_YET_RELEASED" -> "Not yet published"; +// default -> "Unknown"; +// }; +// } +// +// private OffsetDateTime convertFuzzyDate(AniListClient.MangaResponse.Manga.FuzzyDate date) { +// if (isNull(date) || isNull(date.year())) { +// return null; +// } +// return OffsetDateTime.of( +// date.year(), +// isNull(date.month()) ? 1 : date.month(), +// isNull(date.day()) ? 1 : date.day(), +// 0, +// 0, +// 0, +// 0, +// ZoneOffset.UTC); +// } +// +// private void updateMangaAuthors(Manga manga, List authors) { +// var mangaAuthors = +// authors.stream() +// .map( +// author -> +// mangaAuthorRepository +// .findByMangaAndAuthor(manga, author) +// .orElseGet( +// () -> +// mangaAuthorRepository.save( +// MangaAuthor.builder().manga(manga).author(author).build()))) +// .toList(); +// manga.setMangaAuthors(mangaAuthors); +// } +// +// private void updateMangaGenres(Manga manga, List genres) { +// var mangaGenres = +// genres.stream() +// .map( +// genre -> +// mangaGenreRepository +// .findByMangaAndGenre(manga, genre) +// .orElseGet( +// () -> +// mangaGenreRepository.save( +// MangaGenre.builder().manga(manga).genre(genre).build()))) +// .toList(); +// manga.setMangaGenres(mangaGenres); +// } +// +// private void downloadCoverImage(Manga manga, String imageUrl) +// throws IOException, URISyntaxException { +// var inputStream = +// new BufferedInputStream(new URL(new URI(imageUrl).toASCIIString()).openStream()); +// +// var bytes = inputStream.readAllBytes(); +// +// inputStream.close(); +// var image = imageService.uploadImage(bytes, "image/jpeg", "cover"); +// +// manga.setCoverImage(image); +// } +// +// public MangaChapter persistMangaChapter( +// MangaContentProvider mangaContentProvider, ContentProviderMangaChapterResponseDTO chapter) { +// var mangaChapter = +// mangaChapterRepository +// .findByMangaContentProviderAndUrlIgnoreCase(mangaContentProvider, +// chapter.chapterUrl()) +// .orElseGet(MangaChapter::new); +// +// mangaChapter.setMangaContentProvider(mangaContentProvider); +// mangaChapter.setTitle(chapter.chapterTitle()); +// mangaChapter.setUrl(chapter.chapterUrl()); +// +// var language = languageService.getOrThrow(chapter.languageCode()); +// mangaChapter.setLanguage(language); +// +// if (nonNull(chapter.chapter())) { +// try { +// mangaChapter.setChapterNumber(Integer.parseInt(chapter.chapter())); +// } catch (NumberFormatException e) { +// log.warn( +// "Could not parse chapter number {} from manga {}", +// chapter.chapter(), +// mangaContentProvider.getManga().getTitle()); +// } +// } +// +// return mangaChapterRepository.save(mangaChapter); +// } +// +// private MangaContentProvider getOrCreateMangaProvider( +// String title, ContentProvider contentProvider) { +// return mangaContentProviderRepository +// .findByMangaTitleIgnoreCaseAndContentProvider(title, contentProvider) +// .orElseGet( +// () -> { +// jikanRateLimiter.acquire(); +// var manga = mangaCreationService.getOrCreateManga(title, "manual", contentProvider); +// +// return mangaContentProviderRepository.save( +// MangaContentProvider.builder() +// .manga(manga) +// .mangaTitle(manga.getTitle()) +// .contentProvider(contentProvider) +// .url("manual") +// .build()); +// }); +// } +// +// private String removeFileExtension(String filename) { +// if (StringUtils.isBlank(filename)) { +// return filename; +// } +// +// int lastDotIndex = filename.lastIndexOf('.'); +// +// // No dot, or dot is the first character (like .gitignore) +// if (lastDotIndex <= 0) { +// return filename; +// } +// +// return filename.substring(0, lastDotIndex); +// } +// } diff --git a/src/main/java/com/magamochi/service/MangaListService.java b/src/main/java/com/magamochi/service/MangaListService.java deleted file mode 100644 index faf5bc8..0000000 --- a/src/main/java/com/magamochi/service/MangaListService.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.magamochi.service; - -import static java.util.Objects.isNull; - -import com.magamochi.ingestion.providers.PagedContentProviderFactory; -import com.magamochi.model.entity.MangaContentProvider; -import com.magamochi.model.repository.MangaContentProviderRepository; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.stereotype.Service; - -@Log4j2 -@Service -@RequiredArgsConstructor -public class MangaListService { - private final ProviderService providerService; - private final MangaCreationService mangaCreationService; - private final PagedContentProviderFactory pagedContentProviderFactory; - - private final MangaContentProviderRepository mangaContentProviderRepository; - - public void updateMangaList(String contentProviderName, Integer page) { - var contentProvider = pagedContentProviderFactory.getPagedContentProvider(contentProviderName); - var provider = providerService.getOrCreateProvider(contentProviderName); - - var mangas = contentProvider.getMangasFromPage(page); - - mangas.forEach( - mangaResponse -> { - var mangaProvider = - mangaContentProviderRepository.findByMangaTitleIgnoreCaseAndContentProvider( - mangaResponse.title(), provider); - - if (mangaProvider.isPresent()) { - return; - } - - var manga = - mangaCreationService.getOrCreateManga( - mangaResponse.title(), mangaResponse.url(), provider); - - if (isNull(manga)) { - return; - } - - if (!mangaContentProviderRepository.existsByMangaAndContentProviderAndUrlIgnoreCase( - manga, provider, mangaResponse.url())) { - mangaContentProviderRepository.save( - MangaContentProvider.builder() - .manga(manga) - .mangaTitle(mangaResponse.title()) - .contentProvider(provider) - .url(mangaResponse.url()) - .build()); - } - }); - } -} diff --git a/src/main/java/com/magamochi/service/MangaService.java b/src/main/java/com/magamochi/service/OldMangaService.java similarity index 71% rename from src/main/java/com/magamochi/service/MangaService.java rename to src/main/java/com/magamochi/service/OldMangaService.java index 8d36731..ec106b2 100644 --- a/src/main/java/com/magamochi/service/MangaService.java +++ b/src/main/java/com/magamochi/service/OldMangaService.java @@ -1,40 +1,35 @@ package com.magamochi.service; -import static java.util.Objects.nonNull; - +import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.catalog.model.repository.MangaContentProviderRepository; +import com.magamochi.catalog.model.repository.MangaRepository; import com.magamochi.client.NtfyClient; import com.magamochi.common.exception.NotFoundException; import com.magamochi.ingestion.providers.ContentProviderFactory; import com.magamochi.model.dto.*; -import com.magamochi.model.entity.Manga; import com.magamochi.model.entity.MangaChapter; import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.entity.UserMangaFollow; import com.magamochi.model.repository.*; -import com.magamochi.model.specification.MangaSpecification; import com.magamochi.queue.MangaChapterDownloadProducer; import java.util.Comparator; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Log4j2 @Service @RequiredArgsConstructor -public class MangaService { - private final MangaImportService mangaImportService; +public class OldMangaService { + // private final MangaImportService mangaImportService; private final UserService userService; private final MangaRepository mangaRepository; private final MangaContentProviderRepository mangaContentProviderRepository; private final ContentProviderFactory contentProviderFactory; - private final UserFavoriteMangaRepository userFavoriteMangaRepository; private final UserMangaFollowRepository userMangaFollowRepository; @@ -62,27 +57,6 @@ public class MangaService { new MangaChapterDownloadCommand(chapterId))); } - public Page getMangas(MangaListFilterDTO filterDTO, Pageable pageable) { - var user = userService.getLoggedUser(); - - var specification = MangaSpecification.getMangaListSpecification(filterDTO, user); - - var favoriteMangasIds = - nonNull(user) - ? userFavoriteMangaRepository.findByUser(user).stream() - .map(ufm -> ufm.getManga().getId()) - .collect(Collectors.toSet()) - : Set.of(); - - return mangaRepository - .findAll(specification, pageable) - .map( - manga -> { - var favorite = favoriteMangasIds.contains(manga.getId()); - return MangaListDTO.from(manga, favorite); - }); - } - public List getMangaChapters(Long mangaProviderId) { var mangaProvider = getMangaProviderThrowIfNotFound(mangaProviderId); @@ -92,30 +66,6 @@ public class MangaService { .toList(); } - public MangaDTO getManga(Long mangaId) { - var manga = findMangaByIdThrowIfNotFound(mangaId); - var user = userService.getLoggedUser(); - - var favoriteMangasIds = - nonNull(user) - ? userFavoriteMangaRepository.findByUser(user).stream() - .map(ufm -> ufm.getManga().getId()) - .collect(Collectors.toSet()) - : Set.of(); - - var followingMangaIds = - nonNull(user) - ? userMangaFollowRepository.findByUser(user).stream() - .map(umf -> umf.getManga().getId()) - .collect(Collectors.toSet()) - : Set.of(); - - return MangaDTO.from( - manga, - favoriteMangasIds.contains(manga.getId()), - followingMangaIds.contains(manga.getId())); - } - public void fetchFollowedMangaChapters(Long mangaProviderId) { var mangaProvider = mangaContentProviderRepository @@ -155,8 +105,8 @@ public class MangaService { contentProviderFactory.getContentProvider(mangaProvider.getContentProvider().getName()); var availableChapters = contentProvider.getAvailableChapters(mangaProvider); - availableChapters.forEach( - chapter -> mangaImportService.persistMangaChapter(mangaProvider, chapter)); + // availableChapters.forEach( + // chapter -> mangaImportService.persistMangaChapter(mangaProvider, chapter)); } public Manga findMangaByIdThrowIfNotFound(Long mangaId) { diff --git a/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java b/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java index b23e908..eff4cd9 100644 --- a/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java +++ b/src/main/java/com/magamochi/service/ProviderManualMangaImportService.java @@ -1,25 +1,21 @@ package com.magamochi.service; -import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; - +import com.magamochi.catalog.model.repository.MangaContentProviderRepository; import com.magamochi.common.exception.NotFoundException; import com.magamochi.ingestion.model.entity.ContentProvider; import com.magamochi.ingestion.model.repository.ContentProviderRepository; import com.magamochi.ingestion.providers.ManualImportContentProviderFactory; import com.magamochi.model.dto.ImportMangaResponseDTO; import com.magamochi.model.dto.ImportRequestDTO; -import com.magamochi.model.entity.MangaContentProvider; -import com.magamochi.model.repository.MangaContentProviderRepository; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.NotImplementedException; import org.springframework.stereotype.Service; @Log4j2 @Service @RequiredArgsConstructor public class ProviderManualMangaImportService { - private final MangaCreationService mangaCreationService; private final ManualImportContentProviderFactory contentProviderFactory; @@ -27,35 +23,40 @@ public class ProviderManualMangaImportService { private final MangaContentProviderRepository mangaContentProviderRepository; public ImportMangaResponseDTO importFromProvider(Long providerId, ImportRequestDTO requestDTO) { - var provider = getProvider(providerId); - var contentProvider = contentProviderFactory.getManualImportContentProvider(provider.getName()); - - var title = contentProvider.getMangaTitle(requestDTO.id()); - - var malId = nonNull(requestDTO.metadataId()) ? Long.parseLong(requestDTO.metadataId()) : null; - var aniListId = nonNull(requestDTO.aniListId()) ? Long.parseLong(requestDTO.aniListId()) : null; - - var manga = - nonNull(malId) || nonNull(aniListId) - ? mangaCreationService.getOrCreateManga(malId, aniListId) - : mangaCreationService.getOrCreateManga(title, requestDTO.id(), provider); - - if (isNull(manga)) { - throw new NotFoundException("Manga could not be found or created for ID: " + requestDTO.id()); - } - - if (!mangaContentProviderRepository.existsByMangaAndContentProviderAndUrlIgnoreCase( - manga, provider, requestDTO.id())) { - mangaContentProviderRepository.save( - MangaContentProvider.builder() - .manga(manga) - .mangaTitle(title) - .contentProvider(provider) - .url(requestDTO.id()) - .build()); - } - - return new ImportMangaResponseDTO(manga.getId()); + throw new NotImplementedException(); + // var provider = getProvider(providerId); + // var contentProvider = + // contentProviderFactory.getManualImportContentProvider(provider.getName()); + // + // var title = contentProvider.getMangaTitle(requestDTO.id()); + // + // var malId = nonNull(requestDTO.metadataId()) ? Long.parseLong(requestDTO.metadataId()) : + // null; + // var aniListId = nonNull(requestDTO.aniListId()) ? Long.parseLong(requestDTO.aniListId()) : + // null; + // + // var manga = + // nonNull(malId) || nonNull(aniListId) + // ? mangaCreationService.getOrCreateManga(malId, aniListId) + // : mangaCreationService.getOrCreateManga(title, requestDTO.id(), provider); + // + // if (isNull(manga)) { + // throw new NotFoundException("Manga could not be found or created for ID: " + + // requestDTO.id()); + // } + // + // if (!mangaContentProviderRepository.existsByMangaAndContentProviderAndUrlIgnoreCase( + // manga, provider, requestDTO.id())) { + // mangaContentProviderRepository.save( + // MangaContentProvider.builder() + // .manga(manga) + // .mangaTitle(title) + // .contentProvider(provider) + // .url(requestDTO.id()) + // .build()); + // } + // + // return new ImportMangaResponseDTO(manga.getId()); } public ContentProvider getProvider(Long providerId) { diff --git a/src/main/java/com/magamochi/service/UserFavoriteMangaService.java b/src/main/java/com/magamochi/service/UserFavoriteMangaService.java index cb160d7..828182f 100644 --- a/src/main/java/com/magamochi/service/UserFavoriteMangaService.java +++ b/src/main/java/com/magamochi/service/UserFavoriteMangaService.java @@ -1,9 +1,9 @@ package com.magamochi.service; +import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.catalog.model.repository.MangaRepository; import com.magamochi.common.exception.NotFoundException; -import com.magamochi.model.entity.Manga; import com.magamochi.model.entity.UserFavoriteManga; -import com.magamochi.model.repository.MangaRepository; import com.magamochi.model.repository.UserFavoriteMangaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/task/MangaFollowUpdateTask.java b/src/main/java/com/magamochi/task/MangaFollowUpdateTask.java index 926c036..9099c23 100644 --- a/src/main/java/com/magamochi/task/MangaFollowUpdateTask.java +++ b/src/main/java/com/magamochi/task/MangaFollowUpdateTask.java @@ -1,8 +1,8 @@ package com.magamochi.task; +import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.catalog.model.repository.MangaRepository; import com.magamochi.model.dto.UpdateMangaFollowChapterListCommand; -import com.magamochi.model.entity.Manga; -import com.magamochi.model.repository.MangaRepository; import com.magamochi.queue.UpdateMangaFollowChapterListProducer; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f9395d2..47829e9 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -91,6 +91,8 @@ resilience4j: queues: manga-ingest: ${MANGA_INGEST_QUEUE:mangaIngest} provider-page-ingest: ${PROVIDER_PAGE_INGEST_QUEUE:providerPageIngest} + manga-update: ${MANGA_UPDATE_QUEUE:mangaUpdate} + image-fetch: ${IMAGE_FETCH_QUEUE:imageFetch} rabbit-mq: queues: diff --git a/src/main/resources/db/migration/V0001__CREATE_TABLES.sql b/src/main/resources/db/migration/V0001__CREATE_TABLES.sql index df0130f..9093ea5 100644 --- a/src/main/resources/db/migration/V0001__CREATE_TABLES.sql +++ b/src/main/resources/db/migration/V0001__CREATE_TABLES.sql @@ -34,6 +34,7 @@ CREATE TABLE mangas published_to TIMESTAMPTZ, chapter_count INT DEFAULT 0, follow BOOLEAN NOT NULL DEFAULT FALSE, + state VARCHAR NOT NULL DEFAULT 'PENDING', created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -70,6 +71,8 @@ CREATE TABLE manga_chapters url VARCHAR NOT NULL, chapter_number INTEGER, language_id BIGINT REFERENCES languages (id), + downloaded BOOLEAN DEFAULT FALSE, + read BOOLEAN DEFAULT FALSE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -80,17 +83,15 @@ CREATE TABLE manga_chapter_images manga_chapter_id BIGINT NOT NULL REFERENCES manga_chapters (id) ON DELETE CASCADE, image_id UUID REFERENCES images (id) ON DELETE CASCADE, position INT NOT NULL, - downloaded BOOLEAN DEFAULT FALSE, - read BOOLEAN DEFAULT FALSE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE TABLE manga_import_reviews +CREATE TABLE manga_ingest_reviews ( id BIGSERIAL NOT NULL PRIMARY KEY, content_provider_id BIGINT NOT NULL REFERENCES content_providers (id) ON DELETE CASCADE, - title VARCHAR NOT NULL, + manga_title VARCHAR NOT NULL, url VARCHAR NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -98,7 +99,6 @@ CREATE TABLE manga_import_reviews CREATE TABLE authors ( id BIGSERIAL NOT NULL PRIMARY KEY, - mal_id BIGINT UNIQUE, name VARCHAR, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP @@ -114,9 +114,8 @@ CREATE TABLE manga_author CREATE TABLE genres ( - id BIGSERIAL NOT NULL PRIMARY KEY, - mal_id BIGINT UNIQUE, - name VARCHAR + id BIGSERIAL NOT NULL PRIMARY KEY, + name VARCHAR ); CREATE TABLE manga_genre -- 2.49.1 From abde5950e8062245e9aba7c057dbbde8752aad30 Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Wed, 18 Mar 2026 10:17:56 -0300 Subject: [PATCH 08/11] feat: image service --- pom.xml | 6 ++ .../magamochi/catalog/model/dto/MangaDTO.java | 2 +- .../catalog/model/dto/MangaListDTO.java | 2 +- .../magamochi/catalog/model/entity/Manga.java | 2 +- .../consumer/MangaCoverUpdateConsumer.java | 21 +++++ .../catalog/service/MangaUpdateService.java | 10 ++- .../magamochi/common/config/RabbitConfig.java | 30 +++++++ .../queue/command/ImageUpdateCommand.java | 5 ++ .../controller/ManagementController.java | 2 +- .../{ => image}/config/S3ClientConfig.java | 2 +- .../{ => image}/model/entity/Image.java | 6 +- .../model/repository/ImageRepository.java | 10 +++ .../queue/consumer/ImageFetchConsumer.java | 28 +++++++ .../queue/producer/ImageUpdateProducer.java | 25 ++++++ .../image/service/ImageFetchService.java | 79 +++++++++++++++++++ .../magamochi/image/service/ImageService.java | 54 +++++++++++++ .../{ => image}/service/S3Service.java | 60 +++++++------- .../{ => image}/task/ImageCleanupTask.java | 14 ++-- .../model/dto/MangaChapterImagesDTO.java | 2 +- .../model/entity/MangaChapterImage.java | 1 + .../model/repository/ImageRepository.java | 7 -- .../service/MangaChapterService.java | 6 +- ...ImageService.java => OldImageService.java} | 14 ++-- .../com/magamochi/service/OldS3Service.java | 36 +++++++++ src/main/resources/application.yml | 9 ++- .../db/migration/V0001__CREATE_TABLES.sql | 3 +- 26 files changed, 366 insertions(+), 70 deletions(-) create mode 100644 src/main/java/com/magamochi/catalog/queue/consumer/MangaCoverUpdateConsumer.java create mode 100644 src/main/java/com/magamochi/common/queue/command/ImageUpdateCommand.java rename src/main/java/com/magamochi/{ => image}/config/S3ClientConfig.java (97%) rename src/main/java/com/magamochi/{ => image}/model/entity/Image.java (83%) create mode 100644 src/main/java/com/magamochi/image/model/repository/ImageRepository.java create mode 100644 src/main/java/com/magamochi/image/queue/consumer/ImageFetchConsumer.java create mode 100644 src/main/java/com/magamochi/image/queue/producer/ImageUpdateProducer.java create mode 100644 src/main/java/com/magamochi/image/service/ImageFetchService.java create mode 100644 src/main/java/com/magamochi/image/service/ImageService.java rename src/main/java/com/magamochi/{ => image}/service/S3Service.java (88%) rename src/main/java/com/magamochi/{ => image}/task/ImageCleanupTask.java (80%) delete mode 100644 src/main/java/com/magamochi/model/repository/ImageRepository.java rename src/main/java/com/magamochi/service/{ImageService.java => OldImageService.java} (54%) create mode 100644 src/main/java/com/magamochi/service/OldS3Service.java diff --git a/pom.xml b/pom.xml index 799c7d7..d6f8822 100644 --- a/pom.xml +++ b/pom.xml @@ -131,6 +131,12 @@ resilience4j-spring-boot3 2.3.0 + + org.apache.tika + tika-core + 3.2.3 + compile + diff --git a/src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java b/src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java index 0b8a6af..4cdfe51 100644 --- a/src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java +++ b/src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java @@ -33,7 +33,7 @@ public record MangaDTO( return new MangaDTO( manga.getId(), manga.getTitle(), - isNull(manga.getCoverImage()) ? null : manga.getCoverImage().getFileKey(), + isNull(manga.getCoverImage()) ? null : manga.getCoverImage().getObjectKey(), manga.getStatus(), manga.getPublishedFrom(), manga.getPublishedTo(), diff --git a/src/main/java/com/magamochi/catalog/model/dto/MangaListDTO.java b/src/main/java/com/magamochi/catalog/model/dto/MangaListDTO.java index e68b8fc..15943e1 100644 --- a/src/main/java/com/magamochi/catalog/model/dto/MangaListDTO.java +++ b/src/main/java/com/magamochi/catalog/model/dto/MangaListDTO.java @@ -25,7 +25,7 @@ public record MangaListDTO( return new MangaListDTO( manga.getId(), manga.getTitle(), - nonNull(manga.getCoverImage()) ? manga.getCoverImage().getFileKey() : null, + nonNull(manga.getCoverImage()) ? manga.getCoverImage().getObjectKey() : null, manga.getStatus(), manga.getPublishedFrom(), manga.getPublishedTo(), diff --git a/src/main/java/com/magamochi/catalog/model/entity/Manga.java b/src/main/java/com/magamochi/catalog/model/entity/Manga.java index 1202f18..8a307ed 100644 --- a/src/main/java/com/magamochi/catalog/model/entity/Manga.java +++ b/src/main/java/com/magamochi/catalog/model/entity/Manga.java @@ -2,7 +2,7 @@ package com.magamochi.catalog.model.entity; import com.magamochi.catalog.model.enumeration.MangaState; import com.magamochi.catalog.model.enumeration.MangaStatus; -import com.magamochi.model.entity.Image; +import com.magamochi.image.model.entity.Image; import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.entity.UserFavoriteManga; import jakarta.persistence.*; diff --git a/src/main/java/com/magamochi/catalog/queue/consumer/MangaCoverUpdateConsumer.java b/src/main/java/com/magamochi/catalog/queue/consumer/MangaCoverUpdateConsumer.java new file mode 100644 index 0000000..bd3b12c --- /dev/null +++ b/src/main/java/com/magamochi/catalog/queue/consumer/MangaCoverUpdateConsumer.java @@ -0,0 +1,21 @@ +package com.magamochi.catalog.queue.consumer; + +import com.magamochi.catalog.service.MangaUpdateService; +import com.magamochi.common.queue.command.ImageUpdateCommand; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaCoverUpdateConsumer { + private final MangaUpdateService mangaUpdateService; + + @RabbitListener(queues = "${queues.manga-cover-update}") + public void receiveImageUpdateCommand(ImageUpdateCommand command) { + log.info("Received manga cover image update command: {}", command); + mangaUpdateService.updateMangaCoverImage(command.entityId(), command.imageId()); + } +} diff --git a/src/main/java/com/magamochi/catalog/service/MangaUpdateService.java b/src/main/java/com/magamochi/catalog/service/MangaUpdateService.java index 200ac27..a133a3d 100644 --- a/src/main/java/com/magamochi/catalog/service/MangaUpdateService.java +++ b/src/main/java/com/magamochi/catalog/service/MangaUpdateService.java @@ -8,6 +8,7 @@ import com.magamochi.catalog.model.enumeration.MangaState; import com.magamochi.common.model.enumeration.ContentType; import com.magamochi.common.queue.command.ImageFetchCommand; import com.magamochi.common.queue.producer.ImageFetchProducer; +import com.magamochi.image.service.ImageService; import jakarta.transaction.Transactional; import java.util.UUID; import lombok.RequiredArgsConstructor; @@ -21,6 +22,7 @@ public class MangaUpdateService { private final AniListService aniListService; private final MyAnimeListService myAnimeListService; private final MangaService mangaService; + private final ImageService imageService; private final ImageFetchProducer imageFetchProducer; @@ -43,9 +45,11 @@ public class MangaUpdateService { log.info("Updating cover image for manga with ID {}", mangaId); var manga = mangaService.find(mangaId); - // manga.setCoverImageId(imageId); - // - // log.info("Manga with ID {} cover image updated successfully", mangaId); + var image = imageService.find(imageId); + + manga.setCoverImage(image); + + log.info("Manga with ID {} cover image updated successfully (Image ID {})", mangaId, imageId); } private MangaDataDTO fetchExternalMangaData(Manga manga) { diff --git a/src/main/java/com/magamochi/common/config/RabbitConfig.java b/src/main/java/com/magamochi/common/config/RabbitConfig.java index 948098d..9a3ba42 100644 --- a/src/main/java/com/magamochi/common/config/RabbitConfig.java +++ b/src/main/java/com/magamochi/common/config/RabbitConfig.java @@ -1,6 +1,9 @@ package com.magamochi.common.config; +import com.magamochi.common.model.enumeration.ContentType; +import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.Queue; +import org.springframework.amqp.core.TopicExchange; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; @@ -19,9 +22,20 @@ public class RabbitConfig { @Value("${queues.manga-update}") private String mangaUpdateQueue; + @Value("${queues.manga-cover-update}") + private String mangaCoverUpdateQueue; + @Value("${queues.image-fetch}") private String imageFetchQueue; + @Value("${topics.image-updates}") + private String imageUpdatesTopic; + + @Bean + public TopicExchange imageUpdatesExchange() { + return new TopicExchange(imageUpdatesTopic); + } + @Bean public Queue imageFetchQueue() { return new Queue(imageFetchQueue, false); @@ -32,6 +46,22 @@ public class RabbitConfig { return new Queue(mangaUpdateQueue, false); } + @Bean + public Queue mangaCoverUpdateQueue() { + return new Queue(mangaCoverUpdateQueue, false); + } + + @Bean + public Binding bindingMangaCoverUpdateQueue( + Queue mangaCoverUpdateQueue, TopicExchange imageUpdatesExchange) { + return new Binding( + mangaCoverUpdateQueue.getName(), + Binding.DestinationType.QUEUE, + imageUpdatesExchange.getName(), + String.format("image.update.%s", ContentType.MANGA_COVER.name().toLowerCase()), + null); + } + @Bean public Queue mangaIngestQueue() { return new Queue(mangaIngestQueue, false); diff --git a/src/main/java/com/magamochi/common/queue/command/ImageUpdateCommand.java b/src/main/java/com/magamochi/common/queue/command/ImageUpdateCommand.java new file mode 100644 index 0000000..e5cf71a --- /dev/null +++ b/src/main/java/com/magamochi/common/queue/command/ImageUpdateCommand.java @@ -0,0 +1,5 @@ +package com.magamochi.common.queue.command; + +import java.util.UUID; + +public record ImageUpdateCommand(long entityId, UUID imageId) {} diff --git a/src/main/java/com/magamochi/controller/ManagementController.java b/src/main/java/com/magamochi/controller/ManagementController.java index f7cd841..eedfcc2 100644 --- a/src/main/java/com/magamochi/controller/ManagementController.java +++ b/src/main/java/com/magamochi/controller/ManagementController.java @@ -2,9 +2,9 @@ package com.magamochi.controller; import com.magamochi.client.NtfyClient; import com.magamochi.common.model.dto.DefaultResponseDTO; +import com.magamochi.image.task.ImageCleanupTask; import com.magamochi.ingestion.task.IngestFromContentProvidersTask; import com.magamochi.model.repository.UserRepository; -import com.magamochi.task.ImageCleanupTask; import com.magamochi.task.MangaFollowUpdateTask; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/config/S3ClientConfig.java b/src/main/java/com/magamochi/image/config/S3ClientConfig.java similarity index 97% rename from src/main/java/com/magamochi/config/S3ClientConfig.java rename to src/main/java/com/magamochi/image/config/S3ClientConfig.java index a86ccd5..64dbb19 100644 --- a/src/main/java/com/magamochi/config/S3ClientConfig.java +++ b/src/main/java/com/magamochi/image/config/S3ClientConfig.java @@ -1,4 +1,4 @@ -package com.magamochi.config; +package com.magamochi.image.config; import java.net.URI; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/magamochi/model/entity/Image.java b/src/main/java/com/magamochi/image/model/entity/Image.java similarity index 83% rename from src/main/java/com/magamochi/model/entity/Image.java rename to src/main/java/com/magamochi/image/model/entity/Image.java index ec636cc..3829341 100644 --- a/src/main/java/com/magamochi/model/entity/Image.java +++ b/src/main/java/com/magamochi/image/model/entity/Image.java @@ -1,4 +1,4 @@ -package com.magamochi.model.entity; +package com.magamochi.image.model.entity; import jakarta.persistence.*; import java.time.Instant; @@ -19,7 +19,9 @@ public class Image { @GeneratedValue(strategy = GenerationType.UUID) private UUID id; - private String fileKey; + private String objectKey; + + private String fileHash; @CreationTimestamp private Instant createdAt; diff --git a/src/main/java/com/magamochi/image/model/repository/ImageRepository.java b/src/main/java/com/magamochi/image/model/repository/ImageRepository.java new file mode 100644 index 0000000..342a464 --- /dev/null +++ b/src/main/java/com/magamochi/image/model/repository/ImageRepository.java @@ -0,0 +1,10 @@ +package com.magamochi.image.model.repository; + +import com.magamochi.image.model.entity.Image; +import java.util.Optional; +import java.util.UUID; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ImageRepository extends JpaRepository { + Optional findByFileHash(String fileHash); +} diff --git a/src/main/java/com/magamochi/image/queue/consumer/ImageFetchConsumer.java b/src/main/java/com/magamochi/image/queue/consumer/ImageFetchConsumer.java new file mode 100644 index 0000000..7a17062 --- /dev/null +++ b/src/main/java/com/magamochi/image/queue/consumer/ImageFetchConsumer.java @@ -0,0 +1,28 @@ +package com.magamochi.image.queue.consumer; + +import com.magamochi.common.queue.command.ImageFetchCommand; +import com.magamochi.common.queue.command.ImageUpdateCommand; +import com.magamochi.image.queue.producer.ImageUpdateProducer; +import com.magamochi.image.service.ImageFetchService; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class ImageFetchConsumer { + private final ImageFetchService imageFetchService; + private final ImageUpdateProducer imageUpdateProducer; + + @RabbitListener(queues = "${queues.image-fetch}") + public void receiveImageFetchCommand(ImageFetchCommand command) { + log.info("Received image fetch command: {}", command); + + var imageId = imageFetchService.fetchImage(command.url(), command.contentType()); + + imageUpdateProducer.publishImageUpdateCommand( + new ImageUpdateCommand(command.entityId(), imageId), command.contentType()); + } +} diff --git a/src/main/java/com/magamochi/image/queue/producer/ImageUpdateProducer.java b/src/main/java/com/magamochi/image/queue/producer/ImageUpdateProducer.java new file mode 100644 index 0000000..89964f1 --- /dev/null +++ b/src/main/java/com/magamochi/image/queue/producer/ImageUpdateProducer.java @@ -0,0 +1,25 @@ +package com.magamochi.image.queue.producer; + +import com.magamochi.common.model.enumeration.ContentType; +import com.magamochi.common.queue.command.ImageUpdateCommand; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class ImageUpdateProducer { + private final RabbitTemplate rabbitTemplate; + + @Value("${topics.image-updates}") + private String imageUpdatesTopic; + + public void publishImageUpdateCommand(ImageUpdateCommand command, ContentType contentType) { + var routingKey = String.format("image.update.%s", contentType.name().toLowerCase()); + + rabbitTemplate.convertAndSend(imageUpdatesTopic, routingKey, command); + } +} diff --git a/src/main/java/com/magamochi/image/service/ImageFetchService.java b/src/main/java/com/magamochi/image/service/ImageFetchService.java new file mode 100644 index 0000000..843b806 --- /dev/null +++ b/src/main/java/com/magamochi/image/service/ImageFetchService.java @@ -0,0 +1,79 @@ +package com.magamochi.image.service; + +import static java.util.Objects.nonNull; + +import com.magamochi.common.model.enumeration.ContentType; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.apache.tika.Tika; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class ImageFetchService { + private final ImageService imageManagerService; + + private final HttpClient httpClient = + HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build(); + private final Tika tika = new Tika(); + + public UUID fetchImage(String imageUrl, ContentType contentType) { + try { + var request = HttpRequest.newBuilder(URI.create(imageUrl)).GET().build(); + var response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()); + + var imageBytes = response.body(); + + var fileContentType = resolveContentType(response, imageBytes); + + var fileHash = computeHash(imageBytes); + + return imageManagerService.upload( + imageBytes, fileContentType, contentType.name().toLowerCase(), fileHash); + } catch (Exception e) { + log.error("Failed to fetch image from URL: {}", imageUrl, e); + return null; + } + } + + private String resolveContentType(HttpResponse response, byte[] fileBytes) { + var headerType = + response + .headers() + .firstValue("Content-Type") + .map(val -> val.split(";")[0].trim().toLowerCase()) + .orElse(null); + + if (nonNull(headerType) && headerType.startsWith("image/")) { + return headerType; + } + + return tika.detect(fileBytes); + } + + private String computeHash(byte[] content) throws NoSuchAlgorithmException { + var digest = MessageDigest.getInstance("SHA-256"); + var hashBytes = digest.digest(content); + var hexString = new StringBuilder(2 * hashBytes.length); + + for (byte b : hashBytes) { + var hex = Integer.toHexString(0xff & b); + + if (hex.length() == 1) { + hexString.append('0'); + } + + hexString.append(hex); + } + + return hexString.toString(); + } +} diff --git a/src/main/java/com/magamochi/image/service/ImageService.java b/src/main/java/com/magamochi/image/service/ImageService.java new file mode 100644 index 0000000..e3c19a8 --- /dev/null +++ b/src/main/java/com/magamochi/image/service/ImageService.java @@ -0,0 +1,54 @@ +package com.magamochi.image.service; + +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.image.model.entity.Image; +import com.magamochi.image.model.repository.ImageRepository; +import java.util.List; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.apache.tika.mime.MimeTypeException; +import org.apache.tika.mime.MimeTypes; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class ImageService { + private final S3Service s3Service; + private final ImageRepository imageRepository; + + public UUID upload(byte[] data, String contentType, String path, String fileHash) { + var existingImage = imageRepository.findByFileHash(fileHash); + if (existingImage.isPresent()) { + log.info("Image already exists with hash {}, returning existing ID", fileHash); + return existingImage.get().getId(); + } + + log.info("Uploading new image {} to S3", path); + + String extension = ""; + try { + extension = MimeTypes.getDefaultMimeTypes().forName(contentType).getExtension(); + } catch (MimeTypeException e) { + log.warn("Could not determine extension for content type: {}", contentType); + } + + var filename = "manga/" + path + "/" + UUID.randomUUID() + extension; + var objectKey = s3Service.uploadFile(data, contentType, filename); + + return imageRepository + .save(Image.builder().objectKey(objectKey).fileHash(fileHash).build()) + .getId(); + } + + public Image find(UUID id) { + return imageRepository + .findById(id) + .orElseThrow(() -> new NotFoundException("Image not found with ID " + id)); + } + + public List findAll() { + return imageRepository.findAll(); + } +} diff --git a/src/main/java/com/magamochi/service/S3Service.java b/src/main/java/com/magamochi/image/service/S3Service.java similarity index 88% rename from src/main/java/com/magamochi/service/S3Service.java rename to src/main/java/com/magamochi/image/service/S3Service.java index 77eb28c..9cebd2f 100644 --- a/src/main/java/com/magamochi/service/S3Service.java +++ b/src/main/java/com/magamochi/image/service/S3Service.java @@ -1,12 +1,11 @@ -package com.magamochi.service; +package com.magamochi.image.service; import static java.util.Objects.nonNull; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Set; -import java.util.UUID; +import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -21,11 +20,13 @@ public class S3Service { @Value("${minio.bucket}") private String bucket; + @Value("${storage.base-url}") + @Getter + private String baseUrl; + private final S3Client s3Client; - public String uploadFile(byte[] data, String contentType, String path) { - var filename = "manga/" + path + "/" + UUID.randomUUID(); - + public String uploadFile(byte[] data, String contentType, String filename) { var request = PutObjectRequest.builder().bucket(bucket).key(filename).contentType(contentType).build(); @@ -34,10 +35,26 @@ public class S3Service { return filename; } - public InputStream getFile(String key) { - var request = GetObjectRequest.builder().bucket(bucket).key(key).build(); + public List listAllObjectKeys() { + var keys = new ArrayList(); + String continuationToken = null; - return s3Client.getObject(request); + do { + var requestBuilder = ListObjectsV2Request.builder().bucket(bucket).maxKeys(1000); + + if (nonNull(continuationToken)) { + requestBuilder.continuationToken(continuationToken); + } + + var response = s3Client.listObjectsV2(requestBuilder.build()); + + response.contents().forEach(s3Object -> keys.add(s3Object.key())); + + continuationToken = response.isTruncated() ? response.nextContinuationToken() : null; + + } while (nonNull(continuationToken)); + + return keys; } public void deleteObjects(Set objectKeys) { @@ -50,7 +67,7 @@ public class S3Service { objectKeys.stream().map(key -> ObjectIdentifier.builder().key(key).build()).toList(); for (int i = 0; i < allObjects.size(); i += BATCH_SIZE) { - int end = Math.min(i + BATCH_SIZE, allObjects.size()); + var end = Math.min(i + BATCH_SIZE, allObjects.size()); List batch = allObjects.subList(i, end); DeleteObjectsRequest deleteRequest = @@ -77,7 +94,6 @@ public class S3Service { + (i / BATCH_SIZE + 1) + ")"); } - } catch (S3Exception e) { System.err.println( "Failed to delete batch starting at index " @@ -87,26 +103,4 @@ public class S3Service { } } } - - public List listAllObjectKeys() { - var keys = new ArrayList(); - String continuationToken = null; - - do { - var requestBuilder = ListObjectsV2Request.builder().bucket(bucket).maxKeys(1000); - - if (nonNull(continuationToken)) { - requestBuilder.continuationToken(continuationToken); - } - - var response = s3Client.listObjectsV2(requestBuilder.build()); - - response.contents().forEach(s3Object -> keys.add(s3Object.key())); - - continuationToken = response.isTruncated() ? response.nextContinuationToken() : null; - - } while (nonNull(continuationToken)); - - return keys; - } } diff --git a/src/main/java/com/magamochi/task/ImageCleanupTask.java b/src/main/java/com/magamochi/image/task/ImageCleanupTask.java similarity index 80% rename from src/main/java/com/magamochi/task/ImageCleanupTask.java rename to src/main/java/com/magamochi/image/task/ImageCleanupTask.java index 6bbac36..6360ea4 100644 --- a/src/main/java/com/magamochi/task/ImageCleanupTask.java +++ b/src/main/java/com/magamochi/image/task/ImageCleanupTask.java @@ -1,8 +1,8 @@ -package com.magamochi.task; +package com.magamochi.image.task; -import com.magamochi.model.entity.Image; -import com.magamochi.model.repository.ImageRepository; -import com.magamochi.service.S3Service; +import com.magamochi.image.model.entity.Image; +import com.magamochi.image.service.ImageService; +import com.magamochi.image.service.S3Service; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -18,7 +18,7 @@ public class ImageCleanupTask { private Boolean cleanUpEnabled; private final S3Service s3Service; - private final ImageRepository imageRepository; + private final ImageService imageService; @Scheduled(cron = "${image-service.cron-expression}") public void cleanUpImagesScheduled() { @@ -36,8 +36,8 @@ public class ImageCleanupTask { var imageKeys = s3Service.listAllObjectKeys(); var existingImages = - imageRepository.findAll().parallelStream() - .map(Image::getFileKey) + imageService.findAll().parallelStream() + .map(Image::getObjectKey) .collect(Collectors.toSet()); var keysToRemove = diff --git a/src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java b/src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java index 35b3d3b..ea6bcce 100644 --- a/src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java +++ b/src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java @@ -21,7 +21,7 @@ public record MangaChapterImagesDTO( nextId, mangaChapter.getMangaChapterImages().stream() .sorted(Comparator.comparing(MangaChapterImage::getPosition)) - .map(mangaChapterImage -> mangaChapterImage.getImage().getFileKey()) + .map(mangaChapterImage -> mangaChapterImage.getImage().getObjectKey()) .toList()); } } diff --git a/src/main/java/com/magamochi/model/entity/MangaChapterImage.java b/src/main/java/com/magamochi/model/entity/MangaChapterImage.java index 25da2ba..33cf78b 100644 --- a/src/main/java/com/magamochi/model/entity/MangaChapterImage.java +++ b/src/main/java/com/magamochi/model/entity/MangaChapterImage.java @@ -1,5 +1,6 @@ package com.magamochi.model.entity; +import com.magamochi.image.model.entity.Image; import jakarta.persistence.*; import java.time.Instant; import lombok.*; diff --git a/src/main/java/com/magamochi/model/repository/ImageRepository.java b/src/main/java/com/magamochi/model/repository/ImageRepository.java deleted file mode 100644 index 9decd54..0000000 --- a/src/main/java/com/magamochi/model/repository/ImageRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.magamochi.model.repository; - -import com.magamochi.model.entity.Image; -import java.util.UUID; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ImageRepository extends JpaRepository {} diff --git a/src/main/java/com/magamochi/service/MangaChapterService.java b/src/main/java/com/magamochi/service/MangaChapterService.java index 099973b..ccd1209 100644 --- a/src/main/java/com/magamochi/service/MangaChapterService.java +++ b/src/main/java/com/magamochi/service/MangaChapterService.java @@ -35,7 +35,7 @@ public class MangaChapterService { private final MangaChapterRepository mangaChapterRepository; private final MangaChapterImageRepository mangaChapterImageRepository; - private final ImageService imageService; + private final OldImageService oldImageService; private final ContentProviderFactory contentProviderFactory; @@ -93,7 +93,7 @@ public class MangaChapterService { var bytes = inputStream.readAllBytes(); var image = - imageService.uploadImage( + oldImageService.uploadImage( bytes, "image/jpeg", "chapter/" + chapterId); log.info( @@ -192,7 +192,7 @@ public class MangaChapterService { var paddedFileName = String.format("%0" + paddingLength + "d.jpg", imgSrc.getPosition()); zipOutputStream.putNextEntry(new ZipEntry(paddedFileName)); - IOUtils.copy(imageService.getImageStream(imgSrc.getImage()), zipOutputStream); + IOUtils.copy(oldImageService.getImageStream(imgSrc.getImage()), zipOutputStream); zipOutputStream.closeEntry(); } diff --git a/src/main/java/com/magamochi/service/ImageService.java b/src/main/java/com/magamochi/service/OldImageService.java similarity index 54% rename from src/main/java/com/magamochi/service/ImageService.java rename to src/main/java/com/magamochi/service/OldImageService.java index da41ced..79d3ef5 100644 --- a/src/main/java/com/magamochi/service/ImageService.java +++ b/src/main/java/com/magamochi/service/OldImageService.java @@ -1,7 +1,7 @@ package com.magamochi.service; -import com.magamochi.model.entity.Image; -import com.magamochi.model.repository.ImageRepository; +import com.magamochi.image.model.entity.Image; +import com.magamochi.image.model.repository.ImageRepository; import java.io.InputStream; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -10,18 +10,18 @@ import org.springframework.stereotype.Service; @Log4j2 @Service @RequiredArgsConstructor -public class ImageService { - private final S3Service s3Service; +public class OldImageService { + private final OldS3Service oldS3Service; private final ImageRepository imageRepository; public Image uploadImage(byte[] data, String contentType, String path) { log.info("Uploading image {} to S3", path); - var fileKey = s3Service.uploadFile(data, contentType, path); + var fileKey = oldS3Service.uploadFile(data, contentType, path); - return imageRepository.save(Image.builder().fileKey(fileKey).build()); + return imageRepository.save(Image.builder().objectKey(fileKey).build()); } public InputStream getImageStream(Image image) { - return s3Service.getFile(image.getFileKey()); + return oldS3Service.getFile(image.getObjectKey()); } } diff --git a/src/main/java/com/magamochi/service/OldS3Service.java b/src/main/java/com/magamochi/service/OldS3Service.java new file mode 100644 index 0000000..69fe6c5 --- /dev/null +++ b/src/main/java/com/magamochi/service/OldS3Service.java @@ -0,0 +1,36 @@ +package com.magamochi.service; + +import java.io.InputStream; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.*; + +@Service +@RequiredArgsConstructor +public class OldS3Service { + @Value("${minio.bucket}") + private String bucket; + + private final S3Client s3Client; + + public String uploadFile(byte[] data, String contentType, String path) { + var filename = "manga/" + path + "/" + UUID.randomUUID(); + + var request = + PutObjectRequest.builder().bucket(bucket).key(filename).contentType(contentType).build(); + + s3Client.putObject(request, RequestBody.fromBytes(data)); + + return filename; + } + + public InputStream getFile(String key) { + var request = GetObjectRequest.builder().bucket(bucket).key(key).build(); + + return s3Client.getObject(request); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 47829e9..99de633 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -42,7 +42,10 @@ minio: endpoint: ${MINIO_ENDPOINT} accessKey: ${MINIO_USER} secretKey: ${MINIO_PASS} - bucket: mangamochi + bucket: ${MINIO_BUCKET} + +storage: + base-url: ${minio.endpoint}/${minio.bucket} ntfy: endpoint: ${NTFY_ENDPOINT:https://ntfy.badger-pirarucu.ts.net} @@ -88,11 +91,15 @@ resilience4j: - java.io.IOException - java.net.SocketTimeoutException +topics: + image-updates: ${IMAGE_UPDATES_TOPIC:imageUpdates} + queues: manga-ingest: ${MANGA_INGEST_QUEUE:mangaIngest} provider-page-ingest: ${PROVIDER_PAGE_INGEST_QUEUE:providerPageIngest} manga-update: ${MANGA_UPDATE_QUEUE:mangaUpdate} image-fetch: ${IMAGE_FETCH_QUEUE:imageFetch} + manga-cover-update: ${MANGA_COVER_UDPATE_QUEUE:mangaCoverUpdate} rabbit-mq: queues: diff --git a/src/main/resources/db/migration/V0001__CREATE_TABLES.sql b/src/main/resources/db/migration/V0001__CREATE_TABLES.sql index 9093ea5..05db389 100644 --- a/src/main/resources/db/migration/V0001__CREATE_TABLES.sql +++ b/src/main/resources/db/migration/V0001__CREATE_TABLES.sql @@ -15,7 +15,8 @@ ON CONFLICT DO NOTHING; CREATE TABLE images ( id UUID NOT NULL PRIMARY KEY, - file_key VARCHAR NOT NULL, + object_key VARCHAR NOT NULL, + file_hash VARCHAR(64) UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 2.49.1 From 7cb571939e20ee9feff30d18df5cafa8e07c02d7 Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Wed, 18 Mar 2026 10:45:24 -0300 Subject: [PATCH 09/11] refactor: user and authentication service --- .../controller/AuthenticationController.java | 40 ++++++ .../model/dto/AuthenticationRequestDTO.java | 2 +- .../model/dto/AuthenticationResponseDTO.java | 4 +- .../model/dto/RefreshTokenRequestDTO.java | 2 +- .../security/JwtRequestFilter.java | 5 +- .../security}/JwtUtil.java | 2 +- .../security}/SecurityConfig.java | 13 +- .../service/AuthenticationService.java | 68 ++++++++++ .../catalog/service/MangaService.java | 2 +- .../config/RateLimiterConfig.java | 2 +- .../{ => common}/config/WebConfig.java | 2 +- .../controller/AuthenticationController.java | 54 -------- .../controller/ManagementController.java | 2 +- .../model/entity/UserFavoriteManga.java | 1 + .../model/entity/UserMangaFollow.java | 1 + .../magamochi/model/enumeration/UserRole.java | 5 - .../UserFavoriteMangaRepository.java | 2 +- .../repository/UserMangaFollowRepository.java | 2 +- .../specification/MangaSpecification.java | 2 +- .../magamochi/service/OldMangaService.java | 1 + .../magamochi/service/ProviderService.java | 29 ----- .../service/UserFavoriteMangaService.java | 1 + .../com/magamochi/service/UserService.java | 116 ------------------ .../user/controller/UserController.java | 29 +++++ .../model/dto/RegistrationRequestDTO.java | 2 +- .../{ => user}/model/entity/User.java | 5 +- .../user/model/enumeration/UserRole.java | 5 + .../repository/UserRepository.java | 4 +- .../service/CustomUserDetailsService.java | 21 ++-- .../magamochi/user/service/UserService.java | 61 +++++++++ 30 files changed, 244 insertions(+), 241 deletions(-) create mode 100644 src/main/java/com/magamochi/authentication/controller/AuthenticationController.java rename src/main/java/com/magamochi/{ => authentication}/model/dto/AuthenticationRequestDTO.java (74%) rename src/main/java/com/magamochi/{ => authentication}/model/dto/AuthenticationResponseDTO.java (72%) rename src/main/java/com/magamochi/{ => authentication}/model/dto/RefreshTokenRequestDTO.java (71%) rename src/main/java/com/magamochi/{ => authentication}/security/JwtRequestFilter.java (95%) rename src/main/java/com/magamochi/{util => authentication/security}/JwtUtil.java (98%) rename src/main/java/com/magamochi/{config => authentication/security}/SecurityConfig.java (89%) create mode 100644 src/main/java/com/magamochi/authentication/service/AuthenticationService.java rename src/main/java/com/magamochi/{ => common}/config/RateLimiterConfig.java (94%) rename src/main/java/com/magamochi/{ => common}/config/WebConfig.java (92%) delete mode 100644 src/main/java/com/magamochi/controller/AuthenticationController.java delete mode 100644 src/main/java/com/magamochi/model/enumeration/UserRole.java delete mode 100644 src/main/java/com/magamochi/service/ProviderService.java delete mode 100644 src/main/java/com/magamochi/service/UserService.java create mode 100644 src/main/java/com/magamochi/user/controller/UserController.java rename src/main/java/com/magamochi/{ => user}/model/dto/RegistrationRequestDTO.java (69%) rename src/main/java/com/magamochi/{ => user}/model/entity/User.java (80%) create mode 100644 src/main/java/com/magamochi/user/model/enumeration/UserRole.java rename src/main/java/com/magamochi/{model => user}/repository/UserRepository.java (74%) rename src/main/java/com/magamochi/{ => user}/service/CustomUserDetailsService.java (63%) create mode 100644 src/main/java/com/magamochi/user/service/UserService.java diff --git a/src/main/java/com/magamochi/authentication/controller/AuthenticationController.java b/src/main/java/com/magamochi/authentication/controller/AuthenticationController.java new file mode 100644 index 0000000..565e89e --- /dev/null +++ b/src/main/java/com/magamochi/authentication/controller/AuthenticationController.java @@ -0,0 +1,40 @@ +package com.magamochi.authentication.controller; + +import com.magamochi.authentication.model.dto.AuthenticationRequestDTO; +import com.magamochi.authentication.model.dto.AuthenticationResponseDTO; +import com.magamochi.authentication.model.dto.RefreshTokenRequestDTO; +import com.magamochi.authentication.service.AuthenticationService; +import com.magamochi.common.model.dto.DefaultResponseDTO; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/authentication") +@CrossOrigin(origins = "*") +@RequiredArgsConstructor +public class AuthenticationController { + private final AuthenticationService authenticationService; + + @Operation( + summary = "Authenticate an user", + description = "Authenticate an user with email and password.", + tags = {"Authentication"}, + operationId = "login") + @PostMapping + public DefaultResponseDTO login( + @RequestBody AuthenticationRequestDTO authenticationRequestDTO) { + return DefaultResponseDTO.ok(authenticationService.authenticate(authenticationRequestDTO)); + } + + @Operation( + summary = "Refresh authentication token", + description = "Refresh the authentication token", + tags = {"Authentication"}, + operationId = "refreshAuthToken") + @PostMapping("/refresh") + public DefaultResponseDTO refreshAuthToken( + @RequestBody RefreshTokenRequestDTO authenticationRequestDTO) { + return DefaultResponseDTO.ok(authenticationService.refreshAuthToken(authenticationRequestDTO)); + } +} diff --git a/src/main/java/com/magamochi/model/dto/AuthenticationRequestDTO.java b/src/main/java/com/magamochi/authentication/model/dto/AuthenticationRequestDTO.java similarity index 74% rename from src/main/java/com/magamochi/model/dto/AuthenticationRequestDTO.java rename to src/main/java/com/magamochi/authentication/model/dto/AuthenticationRequestDTO.java index d45bbbe..de343e9 100644 --- a/src/main/java/com/magamochi/model/dto/AuthenticationRequestDTO.java +++ b/src/main/java/com/magamochi/authentication/model/dto/AuthenticationRequestDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.model.dto; +package com.magamochi.authentication.model.dto; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/model/dto/AuthenticationResponseDTO.java b/src/main/java/com/magamochi/authentication/model/dto/AuthenticationResponseDTO.java similarity index 72% rename from src/main/java/com/magamochi/model/dto/AuthenticationResponseDTO.java rename to src/main/java/com/magamochi/authentication/model/dto/AuthenticationResponseDTO.java index dae2b22..f9c863d 100644 --- a/src/main/java/com/magamochi/model/dto/AuthenticationResponseDTO.java +++ b/src/main/java/com/magamochi/authentication/model/dto/AuthenticationResponseDTO.java @@ -1,6 +1,6 @@ -package com.magamochi.model.dto; +package com.magamochi.authentication.model.dto; -import com.magamochi.model.enumeration.UserRole; +import com.magamochi.user.model.enumeration.UserRole; import jakarta.validation.constraints.NotNull; public record AuthenticationResponseDTO( diff --git a/src/main/java/com/magamochi/model/dto/RefreshTokenRequestDTO.java b/src/main/java/com/magamochi/authentication/model/dto/RefreshTokenRequestDTO.java similarity index 71% rename from src/main/java/com/magamochi/model/dto/RefreshTokenRequestDTO.java rename to src/main/java/com/magamochi/authentication/model/dto/RefreshTokenRequestDTO.java index 0d78a49..5cb57e4 100644 --- a/src/main/java/com/magamochi/model/dto/RefreshTokenRequestDTO.java +++ b/src/main/java/com/magamochi/authentication/model/dto/RefreshTokenRequestDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.model.dto; +package com.magamochi.authentication.model.dto; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/magamochi/security/JwtRequestFilter.java b/src/main/java/com/magamochi/authentication/security/JwtRequestFilter.java similarity index 95% rename from src/main/java/com/magamochi/security/JwtRequestFilter.java rename to src/main/java/com/magamochi/authentication/security/JwtRequestFilter.java index a41c07e..994433a 100644 --- a/src/main/java/com/magamochi/security/JwtRequestFilter.java +++ b/src/main/java/com/magamochi/authentication/security/JwtRequestFilter.java @@ -1,10 +1,9 @@ -package com.magamochi.security; +package com.magamochi.authentication.security; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import com.magamochi.service.CustomUserDetailsService; -import com.magamochi.util.JwtUtil; +import com.magamochi.user.service.CustomUserDetailsService; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/com/magamochi/util/JwtUtil.java b/src/main/java/com/magamochi/authentication/security/JwtUtil.java similarity index 98% rename from src/main/java/com/magamochi/util/JwtUtil.java rename to src/main/java/com/magamochi/authentication/security/JwtUtil.java index 1b9964c..bb7c228 100644 --- a/src/main/java/com/magamochi/util/JwtUtil.java +++ b/src/main/java/com/magamochi/authentication/security/JwtUtil.java @@ -1,4 +1,4 @@ -package com.magamochi.util; +package com.magamochi.authentication.security; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; diff --git a/src/main/java/com/magamochi/config/SecurityConfig.java b/src/main/java/com/magamochi/authentication/security/SecurityConfig.java similarity index 89% rename from src/main/java/com/magamochi/config/SecurityConfig.java rename to src/main/java/com/magamochi/authentication/security/SecurityConfig.java index d873c90..89943bf 100644 --- a/src/main/java/com/magamochi/config/SecurityConfig.java +++ b/src/main/java/com/magamochi/authentication/security/SecurityConfig.java @@ -1,8 +1,6 @@ -package com.magamochi.config; +package com.magamochi.authentication.security; -import com.magamochi.security.JwtRequestFilter; -import com.magamochi.service.CustomUserDetailsService; -import com.magamochi.util.JwtUtil; +import com.magamochi.user.service.CustomUserDetailsService; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; @@ -27,18 +25,17 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @EnableMethodSecurity @RequiredArgsConstructor public class SecurityConfig { - - private final CustomUserDetailsService userDetailsService; + private final CustomUserDetailsService customUserDetailsService; private final JwtUtil jwtUtil; @Bean public JwtRequestFilter jwtRequestFilter() { - return new JwtRequestFilter(jwtUtil, userDetailsService); + return new JwtRequestFilter(jwtUtil, customUserDetailsService); } @Bean public DaoAuthenticationProvider authenticationProvider() { - var authProvider = new DaoAuthenticationProvider(userDetailsService); + var authProvider = new DaoAuthenticationProvider(customUserDetailsService); authProvider.setPasswordEncoder(passwordEncoder()); return authProvider; } diff --git a/src/main/java/com/magamochi/authentication/service/AuthenticationService.java b/src/main/java/com/magamochi/authentication/service/AuthenticationService.java new file mode 100644 index 0000000..ec6defa --- /dev/null +++ b/src/main/java/com/magamochi/authentication/service/AuthenticationService.java @@ -0,0 +1,68 @@ +package com.magamochi.authentication.service; + +import com.magamochi.authentication.model.dto.AuthenticationRequestDTO; +import com.magamochi.authentication.model.dto.AuthenticationResponseDTO; +import com.magamochi.authentication.model.dto.RefreshTokenRequestDTO; +import com.magamochi.authentication.security.JwtUtil; +import com.magamochi.user.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class AuthenticationService { + private final AuthenticationManager authenticationManager; + + private final UserDetailsService userDetailsService; + private final UserService userService; + + private final JwtUtil jwtUtil; + + public AuthenticationResponseDTO authenticate(AuthenticationRequestDTO request) { + authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(request.email(), request.password())); + + var userDetails = userDetailsService.loadUserByUsername(request.email()); + + var accessToken = jwtUtil.generateAccessToken(userDetails); + var refreshToken = jwtUtil.generateRefreshToken(userDetails); + + var user = userService.find(userDetails.getUsername()); + + return new AuthenticationResponseDTO( + user.getId(), + accessToken, + refreshToken, + userDetails.getUsername(), + user.getName(), + user.getRole()); + } + + public AuthenticationResponseDTO refreshAuthToken( + RefreshTokenRequestDTO authenticationRequestDTO) { + var username = jwtUtil.extractUsernameFromRefreshToken(authenticationRequestDTO.refreshToken()); + + var userDetails = userDetailsService.loadUserByUsername(username); + + if (!jwtUtil.validateRefreshToken(authenticationRequestDTO.refreshToken(), userDetails)) { + throw new BadCredentialsException("Invalid refresh token"); + } + + var newAccessToken = jwtUtil.generateAccessToken(userDetails); + var newRefreshToken = jwtUtil.generateRefreshToken(userDetails); + + var user = userService.find(userDetails.getUsername()); + + return new AuthenticationResponseDTO( + user.getId(), + newAccessToken, + newRefreshToken, + userDetails.getUsername(), + user.getName(), + user.getRole()); + } +} diff --git a/src/main/java/com/magamochi/catalog/service/MangaService.java b/src/main/java/com/magamochi/catalog/service/MangaService.java index fbce832..0439c43 100644 --- a/src/main/java/com/magamochi/catalog/service/MangaService.java +++ b/src/main/java/com/magamochi/catalog/service/MangaService.java @@ -11,7 +11,7 @@ import com.magamochi.common.exception.NotFoundException; import com.magamochi.model.repository.UserFavoriteMangaRepository; import com.magamochi.model.repository.UserMangaFollowRepository; import com.magamochi.model.specification.MangaSpecification; -import com.magamochi.service.UserService; +import com.magamochi.user.service.UserService; import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/magamochi/config/RateLimiterConfig.java b/src/main/java/com/magamochi/common/config/RateLimiterConfig.java similarity index 94% rename from src/main/java/com/magamochi/config/RateLimiterConfig.java rename to src/main/java/com/magamochi/common/config/RateLimiterConfig.java index 112ed2e..ade59f9 100644 --- a/src/main/java/com/magamochi/config/RateLimiterConfig.java +++ b/src/main/java/com/magamochi/common/config/RateLimiterConfig.java @@ -1,4 +1,4 @@ -package com.magamochi.config; +package com.magamochi.common.config; import com.google.common.util.concurrent.RateLimiter; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/com/magamochi/config/WebConfig.java b/src/main/java/com/magamochi/common/config/WebConfig.java similarity index 92% rename from src/main/java/com/magamochi/config/WebConfig.java rename to src/main/java/com/magamochi/common/config/WebConfig.java index 4221d89..06666d7 100644 --- a/src/main/java/com/magamochi/config/WebConfig.java +++ b/src/main/java/com/magamochi/common/config/WebConfig.java @@ -1,4 +1,4 @@ -package com.magamochi.config; +package com.magamochi.common.config; import lombok.NonNull; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/com/magamochi/controller/AuthenticationController.java b/src/main/java/com/magamochi/controller/AuthenticationController.java deleted file mode 100644 index a723e44..0000000 --- a/src/main/java/com/magamochi/controller/AuthenticationController.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.magamochi.controller; - -import com.magamochi.common.model.dto.DefaultResponseDTO; -import com.magamochi.model.dto.AuthenticationRequestDTO; -import com.magamochi.model.dto.AuthenticationResponseDTO; -import com.magamochi.model.dto.RefreshTokenRequestDTO; -import com.magamochi.model.dto.RegistrationRequestDTO; -import com.magamochi.service.UserService; -import io.swagger.v3.oas.annotations.Operation; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/auth") -@CrossOrigin(origins = "*") -@RequiredArgsConstructor -public class AuthenticationController { - private final UserService userService; - - @Operation( - summary = "Authenticate user", - description = "Authenticate user with email and password.", - tags = {"Auth"}, - operationId = "authenticateUser") - @PostMapping("/login") - public DefaultResponseDTO authenticateUser( - @RequestBody AuthenticationRequestDTO authenticationRequestDTO) { - return DefaultResponseDTO.ok(userService.authenticate(authenticationRequestDTO)); - } - - @Operation( - summary = "Refresh authentication token", - description = "Refresh the authentication token", - tags = {"Auth"}, - operationId = "refreshAuthToken") - @PostMapping("/refresh") - public DefaultResponseDTO refreshAuthToken( - @RequestBody RefreshTokenRequestDTO authenticationRequestDTO) { - return DefaultResponseDTO.ok(userService.refreshAuthToken(authenticationRequestDTO)); - } - - @Operation( - summary = "Register user", - description = "Register a new user.", - tags = {"Auth"}, - operationId = "registerUser") - @PostMapping("/register") - public DefaultResponseDTO registerUser( - @RequestBody RegistrationRequestDTO registrationRequestDTO) { - userService.register(registrationRequestDTO); - - return DefaultResponseDTO.ok().build(); - } -} diff --git a/src/main/java/com/magamochi/controller/ManagementController.java b/src/main/java/com/magamochi/controller/ManagementController.java index eedfcc2..ef17ac8 100644 --- a/src/main/java/com/magamochi/controller/ManagementController.java +++ b/src/main/java/com/magamochi/controller/ManagementController.java @@ -4,8 +4,8 @@ import com.magamochi.client.NtfyClient; import com.magamochi.common.model.dto.DefaultResponseDTO; import com.magamochi.image.task.ImageCleanupTask; import com.magamochi.ingestion.task.IngestFromContentProvidersTask; -import com.magamochi.model.repository.UserRepository; import com.magamochi.task.MangaFollowUpdateTask; +import com.magamochi.user.repository.UserRepository; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/magamochi/model/entity/UserFavoriteManga.java b/src/main/java/com/magamochi/model/entity/UserFavoriteManga.java index e091b63..7788116 100644 --- a/src/main/java/com/magamochi/model/entity/UserFavoriteManga.java +++ b/src/main/java/com/magamochi/model/entity/UserFavoriteManga.java @@ -1,6 +1,7 @@ package com.magamochi.model.entity; import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.user.model.entity.User; import jakarta.persistence.*; import java.time.Instant; import lombok.*; diff --git a/src/main/java/com/magamochi/model/entity/UserMangaFollow.java b/src/main/java/com/magamochi/model/entity/UserMangaFollow.java index 8535eaf..36a7d63 100644 --- a/src/main/java/com/magamochi/model/entity/UserMangaFollow.java +++ b/src/main/java/com/magamochi/model/entity/UserMangaFollow.java @@ -1,6 +1,7 @@ package com.magamochi.model.entity; import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.user.model.entity.User; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/magamochi/model/enumeration/UserRole.java b/src/main/java/com/magamochi/model/enumeration/UserRole.java deleted file mode 100644 index dc9b57e..0000000 --- a/src/main/java/com/magamochi/model/enumeration/UserRole.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.magamochi.model.enumeration; - -public enum UserRole { - USER -} diff --git a/src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java b/src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java index be50c56..f5c478e 100644 --- a/src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java +++ b/src/main/java/com/magamochi/model/repository/UserFavoriteMangaRepository.java @@ -1,8 +1,8 @@ package com.magamochi.model.repository; import com.magamochi.catalog.model.entity.Manga; -import com.magamochi.model.entity.User; import com.magamochi.model.entity.UserFavoriteManga; +import com.magamochi.user.model.entity.User; import java.util.Optional; import java.util.Set; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java b/src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java index d1ace6a..db84305 100644 --- a/src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java +++ b/src/main/java/com/magamochi/model/repository/UserMangaFollowRepository.java @@ -1,8 +1,8 @@ package com.magamochi.model.repository; import com.magamochi.catalog.model.entity.Manga; -import com.magamochi.model.entity.User; import com.magamochi.model.entity.UserMangaFollow; +import com.magamochi.user.model.entity.User; import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/model/specification/MangaSpecification.java b/src/main/java/com/magamochi/model/specification/MangaSpecification.java index bc653e5..6ade1e1 100644 --- a/src/main/java/com/magamochi/model/specification/MangaSpecification.java +++ b/src/main/java/com/magamochi/model/specification/MangaSpecification.java @@ -5,7 +5,7 @@ import static java.util.Objects.nonNull; import com.magamochi.catalog.model.dto.MangaListFilterDTO; import com.magamochi.catalog.model.entity.Author; import com.magamochi.catalog.model.entity.Manga; -import com.magamochi.model.entity.User; +import com.magamochi.user.model.entity.User; import jakarta.persistence.criteria.*; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/magamochi/service/OldMangaService.java b/src/main/java/com/magamochi/service/OldMangaService.java index ec106b2..b0de84c 100644 --- a/src/main/java/com/magamochi/service/OldMangaService.java +++ b/src/main/java/com/magamochi/service/OldMangaService.java @@ -12,6 +12,7 @@ import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.entity.UserMangaFollow; import com.magamochi.model.repository.*; import com.magamochi.queue.MangaChapterDownloadProducer; +import com.magamochi.user.service.UserService; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; diff --git a/src/main/java/com/magamochi/service/ProviderService.java b/src/main/java/com/magamochi/service/ProviderService.java deleted file mode 100644 index 88a1467..0000000 --- a/src/main/java/com/magamochi/service/ProviderService.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.magamochi.service; - -import com.magamochi.ingestion.model.entity.ContentProvider; -import com.magamochi.ingestion.model.repository.ContentProviderRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class ProviderService { - private final ContentProviderRepository contentProviderRepository; - - public ContentProvider getOrCreateProvider(String providerName) { - return getOrCreateProvider(providerName, true); - } - - public ContentProvider getOrCreateProvider(String providerName, Boolean supportsChapterFetch) { - return contentProviderRepository - .findByNameIgnoreCase(providerName) - .orElseGet( - () -> - contentProviderRepository.save( - ContentProvider.builder() - .name(providerName) - .active(true) - .supportsChapterFetch(supportsChapterFetch) - .build())); - } -} diff --git a/src/main/java/com/magamochi/service/UserFavoriteMangaService.java b/src/main/java/com/magamochi/service/UserFavoriteMangaService.java index 828182f..f4b3d8a 100644 --- a/src/main/java/com/magamochi/service/UserFavoriteMangaService.java +++ b/src/main/java/com/magamochi/service/UserFavoriteMangaService.java @@ -5,6 +5,7 @@ import com.magamochi.catalog.model.repository.MangaRepository; import com.magamochi.common.exception.NotFoundException; import com.magamochi.model.entity.UserFavoriteManga; import com.magamochi.model.repository.UserFavoriteMangaRepository; +import com.magamochi.user.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/magamochi/service/UserService.java b/src/main/java/com/magamochi/service/UserService.java deleted file mode 100644 index 32ad556..0000000 --- a/src/main/java/com/magamochi/service/UserService.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.magamochi.service; - -import static java.util.Objects.isNull; - -import com.magamochi.common.exception.ConflictException; -import com.magamochi.common.exception.NotFoundException; -import com.magamochi.model.dto.AuthenticationRequestDTO; -import com.magamochi.model.dto.AuthenticationResponseDTO; -import com.magamochi.model.dto.RefreshTokenRequestDTO; -import com.magamochi.model.dto.RegistrationRequestDTO; -import com.magamochi.model.entity.User; -import com.magamochi.model.enumeration.UserRole; -import com.magamochi.model.repository.UserRepository; -import com.magamochi.util.JwtUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; - -@Log4j2 -@Service -@RequiredArgsConstructor -public class UserService { - private final AuthenticationManager authenticationManager; - private final UserDetailsService userDetailsService; - private final JwtUtil jwtUtil; - private final UserRepository userRepository; - private final PasswordEncoder passwordEncoder; - - public AuthenticationResponseDTO authenticate(AuthenticationRequestDTO request) { - authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(request.email(), request.password())); - - var userDetails = userDetailsService.loadUserByUsername(request.email()); - - var accessToken = jwtUtil.generateAccessToken(userDetails); - var refreshToken = jwtUtil.generateRefreshToken(userDetails); - - var user = findUserByEmailThrowIfNotFound(userDetails.getUsername()); - - return new AuthenticationResponseDTO( - user.getId(), - accessToken, - refreshToken, - userDetails.getUsername(), - user.getName(), - user.getRole()); - } - - public AuthenticationResponseDTO refreshAuthToken( - RefreshTokenRequestDTO authenticationRequestDTO) { - var username = jwtUtil.extractUsernameFromRefreshToken(authenticationRequestDTO.refreshToken()); - - var userDetails = userDetailsService.loadUserByUsername(username); - - if (!jwtUtil.validateRefreshToken(authenticationRequestDTO.refreshToken(), userDetails)) { - throw new BadCredentialsException("Invalid refresh token"); - } - - var newAccessToken = jwtUtil.generateAccessToken(userDetails); - var newRefreshToken = jwtUtil.generateRefreshToken(userDetails); - - var user = findUserByEmailThrowIfNotFound(userDetails.getUsername()); - - return new AuthenticationResponseDTO( - user.getId(), - newAccessToken, - newRefreshToken, - userDetails.getUsername(), - user.getName(), - user.getRole()); - } - - public void register(RegistrationRequestDTO request) { - if (userRepository.existsByEmail(request.email())) { - throw new ConflictException("An user with this email already exists."); - } - - userRepository.save( - User.builder() - .name(request.name()) - .email(request.email()) - .password(passwordEncoder.encode(request.password())) - .role(UserRole.USER) - .build()); - } - - public User getLoggedUserThrowIfNotFound() { - var authentication = SecurityContextHolder.getContext().getAuthentication(); - if (isNull(authentication) || authentication.getName().equals("anonymousUser")) { - throw new NotFoundException("User not found."); - } - - return findUserByEmailThrowIfNotFound(authentication.getName()); - } - - public User getLoggedUser() { - var authentication = SecurityContextHolder.getContext().getAuthentication(); - if (isNull(authentication) || authentication.getName().equals("anonymousUser")) { - return null; - } - - return userRepository.findByEmail(authentication.getName()).orElse(null); - } - - private User findUserByEmailThrowIfNotFound(String email) { - return userRepository - .findByEmail(email) - .orElseThrow(() -> new NotFoundException("User not found.")); - } -} diff --git a/src/main/java/com/magamochi/user/controller/UserController.java b/src/main/java/com/magamochi/user/controller/UserController.java new file mode 100644 index 0000000..30555a2 --- /dev/null +++ b/src/main/java/com/magamochi/user/controller/UserController.java @@ -0,0 +1,29 @@ +package com.magamochi.user.controller; + +import com.magamochi.common.model.dto.DefaultResponseDTO; +import com.magamochi.user.model.dto.RegistrationRequestDTO; +import com.magamochi.user.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/user") +@CrossOrigin(origins = "*") +@RequiredArgsConstructor +public class UserController { + private final UserService userService; + + @Operation( + summary = "Register an user", + description = "Register a new user.", + tags = {"User"}, + operationId = "registerUser") + @PostMapping("/register") + public DefaultResponseDTO registerUser( + @RequestBody RegistrationRequestDTO registrationRequestDTO) { + userService.register(registrationRequestDTO); + + return DefaultResponseDTO.ok().build(); + } +} diff --git a/src/main/java/com/magamochi/model/dto/RegistrationRequestDTO.java b/src/main/java/com/magamochi/user/model/dto/RegistrationRequestDTO.java similarity index 69% rename from src/main/java/com/magamochi/model/dto/RegistrationRequestDTO.java rename to src/main/java/com/magamochi/user/model/dto/RegistrationRequestDTO.java index f3d544f..f73d8a7 100644 --- a/src/main/java/com/magamochi/model/dto/RegistrationRequestDTO.java +++ b/src/main/java/com/magamochi/user/model/dto/RegistrationRequestDTO.java @@ -1,3 +1,3 @@ -package com.magamochi.model.dto; +package com.magamochi.user.model.dto; public record RegistrationRequestDTO(String name, String email, String password) {} diff --git a/src/main/java/com/magamochi/model/entity/User.java b/src/main/java/com/magamochi/user/model/entity/User.java similarity index 80% rename from src/main/java/com/magamochi/model/entity/User.java rename to src/main/java/com/magamochi/user/model/entity/User.java index dd52061..a9b7f2c 100644 --- a/src/main/java/com/magamochi/model/entity/User.java +++ b/src/main/java/com/magamochi/user/model/entity/User.java @@ -1,6 +1,7 @@ -package com.magamochi.model.entity; +package com.magamochi.user.model.entity; -import com.magamochi.model.enumeration.UserRole; +import com.magamochi.model.entity.UserFavoriteManga; +import com.magamochi.user.model.enumeration.UserRole; import jakarta.persistence.*; import java.util.Set; import lombok.*; diff --git a/src/main/java/com/magamochi/user/model/enumeration/UserRole.java b/src/main/java/com/magamochi/user/model/enumeration/UserRole.java new file mode 100644 index 0000000..21b7e54 --- /dev/null +++ b/src/main/java/com/magamochi/user/model/enumeration/UserRole.java @@ -0,0 +1,5 @@ +package com.magamochi.user.model.enumeration; + +public enum UserRole { + USER +} diff --git a/src/main/java/com/magamochi/model/repository/UserRepository.java b/src/main/java/com/magamochi/user/repository/UserRepository.java similarity index 74% rename from src/main/java/com/magamochi/model/repository/UserRepository.java rename to src/main/java/com/magamochi/user/repository/UserRepository.java index f083182..b41596d 100644 --- a/src/main/java/com/magamochi/model/repository/UserRepository.java +++ b/src/main/java/com/magamochi/user/repository/UserRepository.java @@ -1,6 +1,6 @@ -package com.magamochi.model.repository; +package com.magamochi.user.repository; -import com.magamochi.model.entity.User; +import com.magamochi.user.model.entity.User; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/service/CustomUserDetailsService.java b/src/main/java/com/magamochi/user/service/CustomUserDetailsService.java similarity index 63% rename from src/main/java/com/magamochi/service/CustomUserDetailsService.java rename to src/main/java/com/magamochi/user/service/CustomUserDetailsService.java index c15b00a..6351076 100644 --- a/src/main/java/com/magamochi/service/CustomUserDetailsService.java +++ b/src/main/java/com/magamochi/user/service/CustomUserDetailsService.java @@ -1,10 +1,11 @@ -package com.magamochi.service; +package com.magamochi.user.service; -import com.magamochi.model.repository.UserRepository; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.user.model.entity.User; +import com.magamochi.user.repository.UserRepository; import java.util.Collections; import lombok.RequiredArgsConstructor; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -17,15 +18,17 @@ public class CustomUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - var user = - userRepository - .findByEmail(username) - .orElseThrow( - () -> new UsernameNotFoundException("User not found with email: " + username)); + var user = find(username); - return new User( + return new org.springframework.security.core.userdetails.User( user.getEmail(), user.getPassword(), Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + user.getRole()))); } + + private User find(String email) { + return userRepository + .findByEmail(email) + .orElseThrow(() -> new NotFoundException("User not found with email " + email + ".")); + } } diff --git a/src/main/java/com/magamochi/user/service/UserService.java b/src/main/java/com/magamochi/user/service/UserService.java new file mode 100644 index 0000000..019d5d2 --- /dev/null +++ b/src/main/java/com/magamochi/user/service/UserService.java @@ -0,0 +1,61 @@ +package com.magamochi.user.service; + +import static java.util.Objects.isNull; + +import com.magamochi.common.exception.ConflictException; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.user.model.dto.RegistrationRequestDTO; +import com.magamochi.user.model.entity.User; +import com.magamochi.user.model.enumeration.UserRole; +import com.magamochi.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class UserService { + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + + public void register(RegistrationRequestDTO request) { + if (userRepository.existsByEmail(request.email())) { + throw new ConflictException("An user with this email already exists."); + } + + userRepository.save( + User.builder() + .name(request.name()) + .email(request.email()) + .password(passwordEncoder.encode(request.password())) + .role(UserRole.USER) + .build()); + } + + public User getLoggedUserThrowIfNotFound() { + var authentication = SecurityContextHolder.getContext().getAuthentication(); + if (isNull(authentication) || authentication.getName().equals("anonymousUser")) { + throw new NotFoundException("User not found."); + } + + return find(authentication.getName()); + } + + public User getLoggedUser() { + var authentication = SecurityContextHolder.getContext().getAuthentication(); + if (isNull(authentication) || authentication.getName().equals("anonymousUser")) { + return null; + } + + return userRepository.findByEmail(authentication.getName()).orElse(null); + } + + public User find(String email) { + return userRepository + .findByEmail(email) + .orElseThrow(() -> new NotFoundException("User not found with email " + email + ".")); + } +} -- 2.49.1 From f955671b8a7e1a8c4ad440f57fdc7eb25f65d680 Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Wed, 18 Mar 2026 11:11:24 -0300 Subject: [PATCH 10/11] feat: content service --- .../magamochi/catalog/model/dto/MangaDTO.java | 10 +- .../magamochi/catalog/model/entity/Manga.java | 1 - .../model/entity/MangaContentProvider.java | 6 +- .../MangaContentProviderRepository.java | 2 +- .../catalog/service/LanguageService.java | 2 +- .../service/MangaContentProviderService.java | 22 +++ .../catalog/service/MangaIngestService.java | 2 +- .../magamochi/common/config/RabbitConfig.java | 51 ++++-- .../common/model/enumeration/ContentType.java | 5 +- .../MangaContentImageIngestCommand.java | 6 + .../command/MangaContentIngestCommand.java | 9 + .../content/controller/ContentController.java | 44 +++++ .../{ => content}/model/dto/LanguageDTO.java | 2 +- .../content/model/dto/MangaContentDTO.java | 21 +++ .../model/entity/MangaContent.java} | 18 +- .../model/entity/MangaContentImage.java} | 10 +- .../MangaContentImageRepository.java | 12 ++ .../repository/MangaContentRepository.java | 8 + .../MangaContentImageIngestConsumer.java | 22 +++ .../MangaContentImageUpdateConsumer.java | 22 +++ .../consumer/MangaContentIngestConsumer.java | 22 +++ .../content/service/ContentIngestService.java | 113 +++++++++++++ .../content/service/ContentService.java | 64 ++++++++ .../controller/MangaChapterController.java | 25 --- .../magamochi/controller/MangaController.java | 25 --- .../image/service/ImageFetchService.java | 7 +- .../controller/IngestionController.java | 26 +++ .../model/dto/ContentImageInfoDTO.java | 5 + .../ingestion/model/dto/ContentInfoDTO.java | 6 + .../model/entity/ContentProvider.java | 4 +- .../repository/ContentProviderRepository.java | 5 +- .../ingestion/providers/ContentProvider.java | 10 +- .../providers/impl/MangaDexProvider.java | 22 +-- .../impl/MangaLivreBlogProvider.java | 22 ++- .../providers/impl/MangaLivreProvider.java | 21 +-- .../providers/impl/PinkRosaScanProvider.java | 25 ++- .../MangaContentImageIngestProducer.java | 23 +++ .../producer/MangaContentIngestProducer.java | 23 +++ .../ingestion/service/IngestionService.java | 55 +++++++ ...ontentProviderMangaChapterResponseDTO.java | 9 - .../magamochi/model/dto/MangaChapterDTO.java | 21 --- .../model/dto/MangaChapterImagesDTO.java | 27 --- .../model/dto/MangaChapterResponseDTO.java | 10 -- .../model/dto/MangaContentImagesDTO.java | 27 +++ .../MangaChapterImageRepository.java | 10 -- .../repository/MangaChapterRepository.java | 15 -- .../queue/MangaChapterDownloadConsumer.java | 26 --- .../UpdateMangaFollowChapterListConsumer.java | 2 +- .../service/MangaChapterService.java | 154 ++---------------- .../magamochi/service/MangaImportService.java | 6 +- .../magamochi/service/OldImageService.java | 9 - .../magamochi/service/OldMangaService.java | 108 ++++-------- .../com/magamochi/service/OldS3Service.java | 13 -- src/main/resources/application.yml | 3 + .../db/migration/V0001__CREATE_TABLES.sql | 29 ++-- .../db/migration/V0002__CONTENT_PROVIDERS.sql | 2 +- 56 files changed, 719 insertions(+), 530 deletions(-) rename src/main/java/com/magamochi/{ => catalog}/model/entity/MangaContentProvider.java (86%) create mode 100644 src/main/java/com/magamochi/catalog/service/MangaContentProviderService.java create mode 100644 src/main/java/com/magamochi/common/queue/command/MangaContentImageIngestCommand.java create mode 100644 src/main/java/com/magamochi/common/queue/command/MangaContentIngestCommand.java create mode 100644 src/main/java/com/magamochi/content/controller/ContentController.java rename src/main/java/com/magamochi/{ => content}/model/dto/LanguageDTO.java (92%) create mode 100644 src/main/java/com/magamochi/content/model/dto/MangaContentDTO.java rename src/main/java/com/magamochi/{model/entity/MangaChapter.java => content/model/entity/MangaContent.java} (67%) rename src/main/java/com/magamochi/{model/entity/MangaChapterImage.java => content/model/entity/MangaContentImage.java} (75%) create mode 100644 src/main/java/com/magamochi/content/model/repository/MangaContentImageRepository.java create mode 100644 src/main/java/com/magamochi/content/model/repository/MangaContentRepository.java create mode 100644 src/main/java/com/magamochi/content/queue/consumer/MangaContentImageIngestConsumer.java create mode 100644 src/main/java/com/magamochi/content/queue/consumer/MangaContentImageUpdateConsumer.java create mode 100644 src/main/java/com/magamochi/content/queue/consumer/MangaContentIngestConsumer.java create mode 100644 src/main/java/com/magamochi/content/service/ContentIngestService.java create mode 100644 src/main/java/com/magamochi/content/service/ContentService.java create mode 100644 src/main/java/com/magamochi/ingestion/model/dto/ContentImageInfoDTO.java create mode 100644 src/main/java/com/magamochi/ingestion/model/dto/ContentInfoDTO.java create mode 100644 src/main/java/com/magamochi/ingestion/queue/producer/MangaContentImageIngestProducer.java create mode 100644 src/main/java/com/magamochi/ingestion/queue/producer/MangaContentIngestProducer.java delete mode 100644 src/main/java/com/magamochi/model/dto/ContentProviderMangaChapterResponseDTO.java delete mode 100644 src/main/java/com/magamochi/model/dto/MangaChapterDTO.java delete mode 100644 src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java delete mode 100644 src/main/java/com/magamochi/model/dto/MangaChapterResponseDTO.java create mode 100644 src/main/java/com/magamochi/model/dto/MangaContentImagesDTO.java delete mode 100644 src/main/java/com/magamochi/model/repository/MangaChapterImageRepository.java delete mode 100644 src/main/java/com/magamochi/model/repository/MangaChapterRepository.java delete mode 100644 src/main/java/com/magamochi/queue/MangaChapterDownloadConsumer.java diff --git a/src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java b/src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java index 4cdfe51..091186a 100644 --- a/src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java +++ b/src/main/java/com/magamochi/catalog/model/dto/MangaDTO.java @@ -4,9 +4,9 @@ import static java.util.Objects.isNull; import com.magamochi.catalog.model.entity.Manga; import com.magamochi.catalog.model.entity.MangaAlternativeTitle; +import com.magamochi.catalog.model.entity.MangaContentProvider; import com.magamochi.catalog.model.enumeration.MangaStatus; -import com.magamochi.model.entity.MangaChapter; -import com.magamochi.model.entity.MangaContentProvider; +import com.magamochi.content.model.entity.MangaContent; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.time.OffsetDateTime; @@ -59,9 +59,9 @@ public record MangaDTO( @NotNull Integer chaptersDownloaded, @NotNull Boolean supportsChapterFetch) { public static MangaProviderDTO from(MangaContentProvider mangaContentProvider) { - var chapters = mangaContentProvider.getMangaChapters(); + var chapters = mangaContentProvider.getMangaContents(); var chaptersAvailable = chapters.size(); - var chaptersDownloaded = (int) chapters.stream().filter(MangaChapter::getDownloaded).count(); + var chaptersDownloaded = (int) chapters.stream().filter(MangaContent::getDownloaded).count(); return new MangaProviderDTO( mangaContentProvider.getId(), @@ -69,7 +69,7 @@ public record MangaDTO( mangaContentProvider.getContentProvider().isActive(), chaptersAvailable, chaptersDownloaded, - mangaContentProvider.getContentProvider().getSupportsChapterFetch()); + mangaContentProvider.getContentProvider().getSupportsContentFetch()); } } } diff --git a/src/main/java/com/magamochi/catalog/model/entity/Manga.java b/src/main/java/com/magamochi/catalog/model/entity/Manga.java index 8a307ed..8662417 100644 --- a/src/main/java/com/magamochi/catalog/model/entity/Manga.java +++ b/src/main/java/com/magamochi/catalog/model/entity/Manga.java @@ -3,7 +3,6 @@ package com.magamochi.catalog.model.entity; import com.magamochi.catalog.model.enumeration.MangaState; import com.magamochi.catalog.model.enumeration.MangaStatus; import com.magamochi.image.model.entity.Image; -import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.entity.UserFavoriteManga; import jakarta.persistence.*; import java.time.Instant; diff --git a/src/main/java/com/magamochi/model/entity/MangaContentProvider.java b/src/main/java/com/magamochi/catalog/model/entity/MangaContentProvider.java similarity index 86% rename from src/main/java/com/magamochi/model/entity/MangaContentProvider.java rename to src/main/java/com/magamochi/catalog/model/entity/MangaContentProvider.java index 9bfcc2c..cd4e150 100644 --- a/src/main/java/com/magamochi/model/entity/MangaContentProvider.java +++ b/src/main/java/com/magamochi/catalog/model/entity/MangaContentProvider.java @@ -1,6 +1,6 @@ -package com.magamochi.model.entity; +package com.magamochi.catalog.model.entity; -import com.magamochi.catalog.model.entity.Manga; +import com.magamochi.content.model.entity.MangaContent; import com.magamochi.ingestion.model.entity.ContentProvider; import jakarta.persistence.*; import java.time.Instant; @@ -34,7 +34,7 @@ public class MangaContentProvider { private String url; @OneToMany(mappedBy = "mangaContentProvider") - List mangaChapters; + List mangaContents; @CreationTimestamp private Instant createdAt; diff --git a/src/main/java/com/magamochi/catalog/model/repository/MangaContentProviderRepository.java b/src/main/java/com/magamochi/catalog/model/repository/MangaContentProviderRepository.java index de3f1bd..7632849 100644 --- a/src/main/java/com/magamochi/catalog/model/repository/MangaContentProviderRepository.java +++ b/src/main/java/com/magamochi/catalog/model/repository/MangaContentProviderRepository.java @@ -1,6 +1,6 @@ package com.magamochi.catalog.model.repository; -import com.magamochi.model.entity.MangaContentProvider; +import com.magamochi.catalog.model.entity.MangaContentProvider; import jakarta.validation.constraints.NotBlank; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/magamochi/catalog/service/LanguageService.java b/src/main/java/com/magamochi/catalog/service/LanguageService.java index 83833eb..fe8af57 100644 --- a/src/main/java/com/magamochi/catalog/service/LanguageService.java +++ b/src/main/java/com/magamochi/catalog/service/LanguageService.java @@ -11,7 +11,7 @@ import org.springframework.stereotype.Service; public class LanguageService { public final LanguageRepository languageRepository; - public Language getOrThrow(String code) { + public Language find(String code) { return languageRepository .findByCodeIgnoreCase(code) .orElseThrow(() -> new NotFoundException("Language with code " + code + " not found")); diff --git a/src/main/java/com/magamochi/catalog/service/MangaContentProviderService.java b/src/main/java/com/magamochi/catalog/service/MangaContentProviderService.java new file mode 100644 index 0000000..85aa949 --- /dev/null +++ b/src/main/java/com/magamochi/catalog/service/MangaContentProviderService.java @@ -0,0 +1,22 @@ +package com.magamochi.catalog.service; + +import com.magamochi.catalog.model.entity.MangaContentProvider; +import com.magamochi.catalog.model.repository.MangaContentProviderRepository; +import com.magamochi.common.exception.NotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class MangaContentProviderService { + private final MangaContentProviderRepository mangaContentProviderRepository; + + public MangaContentProvider find(long mangaContentProviderId) { + return mangaContentProviderRepository + .findById(mangaContentProviderId) + .orElseThrow( + () -> + new NotFoundException( + "MangaContentProvider not found - ID: " + mangaContentProviderId)); + } +} diff --git a/src/main/java/com/magamochi/catalog/service/MangaIngestService.java b/src/main/java/com/magamochi/catalog/service/MangaIngestService.java index 3d26e35..5f3d919 100644 --- a/src/main/java/com/magamochi/catalog/service/MangaIngestService.java +++ b/src/main/java/com/magamochi/catalog/service/MangaIngestService.java @@ -2,11 +2,11 @@ package com.magamochi.catalog.service; import static java.util.Objects.isNull; +import com.magamochi.catalog.model.entity.MangaContentProvider; import com.magamochi.catalog.model.entity.MangaIngestReview; import com.magamochi.catalog.model.repository.MangaContentProviderRepository; import com.magamochi.catalog.model.repository.MangaIngestReviewRepository; import com.magamochi.ingestion.service.ContentProviderService; -import com.magamochi.model.entity.MangaContentProvider; import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/magamochi/common/config/RabbitConfig.java b/src/main/java/com/magamochi/common/config/RabbitConfig.java index 9a3ba42..912b9ba 100644 --- a/src/main/java/com/magamochi/common/config/RabbitConfig.java +++ b/src/main/java/com/magamochi/common/config/RabbitConfig.java @@ -16,6 +16,12 @@ public class RabbitConfig { @Value("${queues.manga-ingest}") private String mangaIngestQueue; + @Value("${queues.manga-content-ingest}") + private String mangaContentIngestQueue; + + @Value("${queues.manga-content-image-ingest}") + private String mangaContentImageIngestQueue; + @Value("${queues.provider-page-ingest}") private String providerPageIngestQueue; @@ -25,6 +31,9 @@ public class RabbitConfig { @Value("${queues.manga-cover-update}") private String mangaCoverUpdateQueue; + @Value("${queues.manga-content-image-update}") + private String mangaContentImageUpdateQueue; + @Value("${queues.image-fetch}") private String imageFetchQueue; @@ -46,6 +55,11 @@ public class RabbitConfig { return new Queue(mangaUpdateQueue, false); } + @Bean + public Queue mangaContentImageUpdateQueue() { + return new Queue(mangaContentImageUpdateQueue, false); + } + @Bean public Queue mangaCoverUpdateQueue() { return new Queue(mangaCoverUpdateQueue, false); @@ -62,6 +76,27 @@ public class RabbitConfig { null); } + @Bean + public Binding bindingMangaContentImageUpdateQueue( + Queue mangaContentImageUpdateQueue, TopicExchange imageUpdatesExchange) { + return new Binding( + mangaContentImageUpdateQueue.getName(), + Binding.DestinationType.QUEUE, + imageUpdatesExchange.getName(), + String.format("image.update.%s", ContentType.CONTENT_IMAGE.name().toLowerCase()), + null); + } + + @Bean + public Queue mangaContentIngestQueue() { + return new Queue(mangaContentIngestQueue, false); + } + + @Bean + public Queue mangaContentImageIngestQueue() { + return new Queue(mangaContentImageIngestQueue, false); + } + @Bean public Queue mangaIngestQueue() { return new Queue(mangaIngestQueue, false); @@ -74,33 +109,17 @@ public class RabbitConfig { // TODO: remove unused queues - @Value("${rabbit-mq.queues.manga-data-update}") - private String mangaDataUpdateQueue; - @Value("${rabbit-mq.queues.manga-chapter-download}") private String mangaChapterDownloadQueue; - @Value("${rabbit-mq.queues.manga-list-update}") - private String mangaListUpdateQueue; - @Value("${rabbit-mq.queues.manga-follow-update-chapter}") private String mangaFollowUpdateChapterQueue; - @Bean - public Queue mangaDataUpdateQueue() { - return new Queue(mangaDataUpdateQueue, false); - } - @Bean public Queue mangaChapterDownloadQueue() { return new Queue(mangaChapterDownloadQueue, false); } - @Bean - public Queue mangaListUpdateQueue() { - return new Queue(mangaListUpdateQueue, false); - } - @Bean public Queue mangaFollowUpdateChapterQueue() { return new Queue(mangaFollowUpdateChapterQueue, false); diff --git a/src/main/java/com/magamochi/common/model/enumeration/ContentType.java b/src/main/java/com/magamochi/common/model/enumeration/ContentType.java index 79726ce..a4ce0d7 100644 --- a/src/main/java/com/magamochi/common/model/enumeration/ContentType.java +++ b/src/main/java/com/magamochi/common/model/enumeration/ContentType.java @@ -1,5 +1,8 @@ package com.magamochi.common.model.enumeration; public enum ContentType { - MANGA_COVER + MANGA_COVER, + CHAPTER, + VOLUME, + CONTENT_IMAGE, } diff --git a/src/main/java/com/magamochi/common/queue/command/MangaContentImageIngestCommand.java b/src/main/java/com/magamochi/common/queue/command/MangaContentImageIngestCommand.java new file mode 100644 index 0000000..42ffb0b --- /dev/null +++ b/src/main/java/com/magamochi/common/queue/command/MangaContentImageIngestCommand.java @@ -0,0 +1,6 @@ +package com.magamochi.common.queue.command; + +import jakarta.validation.constraints.NotBlank; + +public record MangaContentImageIngestCommand( + long mangaContentId, @NotBlank String url, int position, boolean isLast) {} diff --git a/src/main/java/com/magamochi/common/queue/command/MangaContentIngestCommand.java b/src/main/java/com/magamochi/common/queue/command/MangaContentIngestCommand.java new file mode 100644 index 0000000..b04bd82 --- /dev/null +++ b/src/main/java/com/magamochi/common/queue/command/MangaContentIngestCommand.java @@ -0,0 +1,9 @@ +package com.magamochi.common.queue.command; + +import jakarta.validation.constraints.NotBlank; + +public record MangaContentIngestCommand( + long mangaContentProviderId, + @NotBlank String title, + @NotBlank String url, + @NotBlank String languageCode) {} diff --git a/src/main/java/com/magamochi/content/controller/ContentController.java b/src/main/java/com/magamochi/content/controller/ContentController.java new file mode 100644 index 0000000..c035ddf --- /dev/null +++ b/src/main/java/com/magamochi/content/controller/ContentController.java @@ -0,0 +1,44 @@ +package com.magamochi.content.controller; + +import com.magamochi.common.model.dto.DefaultResponseDTO; +import com.magamochi.content.model.dto.MangaContentDTO; +import com.magamochi.content.service.ContentService; +import com.magamochi.model.dto.MangaContentImagesDTO; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.constraints.NotNull; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/content") +@RequiredArgsConstructor +public class ContentController { + private final ContentService contentService; + + @Operation( + summary = "Get the content for a specific manga/content provider combination", + description = "Retrieve the content for a specific manga/content provider combination.", + tags = {"Content"}, + operationId = "getMangaProviderContent") + @GetMapping("/{mangaContentProviderId}") + public DefaultResponseDTO> getMangaProviderContent( + @PathVariable @NotNull Long mangaContentProviderId) { + return DefaultResponseDTO.ok(contentService.getContent(mangaContentProviderId)); + } + + @Operation( + summary = "Get the content images for a specific manga/provider combination", + description = + "Retrieve a list of manga content images for a specific manga/provider combination.", + tags = {"Content"}, + operationId = "getMangaContentImages") + @GetMapping("/{mangaContentId}/images") + public DefaultResponseDTO getMangaContentImages( + @PathVariable Long mangaContentId) { + return DefaultResponseDTO.ok(contentService.getContentImages(mangaContentId)); + } +} diff --git a/src/main/java/com/magamochi/model/dto/LanguageDTO.java b/src/main/java/com/magamochi/content/model/dto/LanguageDTO.java similarity index 92% rename from src/main/java/com/magamochi/model/dto/LanguageDTO.java rename to src/main/java/com/magamochi/content/model/dto/LanguageDTO.java index 32c409d..3f70990 100644 --- a/src/main/java/com/magamochi/model/dto/LanguageDTO.java +++ b/src/main/java/com/magamochi/content/model/dto/LanguageDTO.java @@ -1,4 +1,4 @@ -package com.magamochi.model.dto; +package com.magamochi.content.model.dto; import static java.util.Objects.isNull; diff --git a/src/main/java/com/magamochi/content/model/dto/MangaContentDTO.java b/src/main/java/com/magamochi/content/model/dto/MangaContentDTO.java new file mode 100644 index 0000000..86a52ca --- /dev/null +++ b/src/main/java/com/magamochi/content/model/dto/MangaContentDTO.java @@ -0,0 +1,21 @@ +package com.magamochi.content.model.dto; + +import com.magamochi.content.model.entity.MangaContent; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record MangaContentDTO( + @NotNull Long id, + @NotBlank String title, + @NotNull Boolean downloaded, + @NotNull Boolean isRead, + LanguageDTO language) { + public static MangaContentDTO from(MangaContent mangaContent) { + return new MangaContentDTO( + mangaContent.getId(), + mangaContent.getTitle(), + mangaContent.getDownloaded(), + false, + LanguageDTO.from(mangaContent.getLanguage())); + } +} diff --git a/src/main/java/com/magamochi/model/entity/MangaChapter.java b/src/main/java/com/magamochi/content/model/entity/MangaContent.java similarity index 67% rename from src/main/java/com/magamochi/model/entity/MangaChapter.java rename to src/main/java/com/magamochi/content/model/entity/MangaContent.java index 1e6ed63..000683e 100644 --- a/src/main/java/com/magamochi/model/entity/MangaChapter.java +++ b/src/main/java/com/magamochi/content/model/entity/MangaContent.java @@ -1,6 +1,8 @@ -package com.magamochi.model.entity; +package com.magamochi.content.model.entity; import com.magamochi.catalog.model.entity.Language; +import com.magamochi.catalog.model.entity.MangaContentProvider; +import com.magamochi.common.model.enumeration.ContentType; import jakarta.persistence.*; import java.time.Instant; import java.util.List; @@ -9,13 +11,13 @@ import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; @Entity -@Table(name = "manga_chapters") +@Table(name = "manga_contents") @Builder @NoArgsConstructor @AllArgsConstructor @Getter @Setter -public class MangaChapter { +public class MangaContent { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -24,22 +26,20 @@ public class MangaChapter { @JoinColumn(name = "manga_content_provider_id") private MangaContentProvider mangaContentProvider; + @Builder.Default private ContentType type = ContentType.CHAPTER; + private String title; private String url; @Builder.Default private Boolean downloaded = false; - @Builder.Default private Boolean read = false; - @CreationTimestamp private Instant createdAt; @UpdateTimestamp private Instant updatedAt; - @OneToMany(mappedBy = "mangaChapter") - private List mangaChapterImages; - - private Integer chapterNumber; + @OneToMany(mappedBy = "mangaContent") + private List mangaContentImages; @ManyToOne @JoinColumn(name = "language_id") diff --git a/src/main/java/com/magamochi/model/entity/MangaChapterImage.java b/src/main/java/com/magamochi/content/model/entity/MangaContentImage.java similarity index 75% rename from src/main/java/com/magamochi/model/entity/MangaChapterImage.java rename to src/main/java/com/magamochi/content/model/entity/MangaContentImage.java index 33cf78b..f0fc463 100644 --- a/src/main/java/com/magamochi/model/entity/MangaChapterImage.java +++ b/src/main/java/com/magamochi/content/model/entity/MangaContentImage.java @@ -1,4 +1,4 @@ -package com.magamochi.model.entity; +package com.magamochi.content.model.entity; import com.magamochi.image.model.entity.Image; import jakarta.persistence.*; @@ -8,20 +8,20 @@ import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; @Entity -@Table(name = "manga_chapter_images") +@Table(name = "manga_content_images") @Builder @NoArgsConstructor @AllArgsConstructor @Getter @Setter -public class MangaChapterImage { +public class MangaContentImage { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne - @JoinColumn(name = "manga_chapter_id") - private MangaChapter mangaChapter; + @JoinColumn(name = "manga_content_id") + private MangaContent mangaContent; @OneToOne @JoinColumn(name = "image_id") diff --git a/src/main/java/com/magamochi/content/model/repository/MangaContentImageRepository.java b/src/main/java/com/magamochi/content/model/repository/MangaContentImageRepository.java new file mode 100644 index 0000000..5ba885f --- /dev/null +++ b/src/main/java/com/magamochi/content/model/repository/MangaContentImageRepository.java @@ -0,0 +1,12 @@ +package com.magamochi.content.model.repository; + +import com.magamochi.content.model.entity.MangaContent; +import com.magamochi.content.model.entity.MangaContentImage; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MangaContentImageRepository extends JpaRepository { + List findAllByMangaContent(MangaContent mangaContent); + + boolean existsByMangaContent_IdAndPosition(Long mangaContentId, int position); +} diff --git a/src/main/java/com/magamochi/content/model/repository/MangaContentRepository.java b/src/main/java/com/magamochi/content/model/repository/MangaContentRepository.java new file mode 100644 index 0000000..2aadd67 --- /dev/null +++ b/src/main/java/com/magamochi/content/model/repository/MangaContentRepository.java @@ -0,0 +1,8 @@ +package com.magamochi.content.model.repository; + +import com.magamochi.content.model.entity.MangaContent; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MangaContentRepository extends JpaRepository { + boolean existsByMangaContentProvider_IdAndUrlIgnoreCase(Long mangaContentProviderId, String url); +} diff --git a/src/main/java/com/magamochi/content/queue/consumer/MangaContentImageIngestConsumer.java b/src/main/java/com/magamochi/content/queue/consumer/MangaContentImageIngestConsumer.java new file mode 100644 index 0000000..feedc13 --- /dev/null +++ b/src/main/java/com/magamochi/content/queue/consumer/MangaContentImageIngestConsumer.java @@ -0,0 +1,22 @@ +package com.magamochi.content.queue.consumer; + +import com.magamochi.common.queue.command.MangaContentImageIngestCommand; +import com.magamochi.content.service.ContentIngestService; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaContentImageIngestConsumer { + private final ContentIngestService contentIngestService; + + @RabbitListener(queues = "${queues.manga-content-image-ingest}") + public void receiveMangaContentImageIngestCommand(MangaContentImageIngestCommand command) { + log.info("Received manga content ingest command: {}", command); + contentIngestService.ingestImages( + command.mangaContentId(), command.url(), command.position(), command.isLast()); + } +} diff --git a/src/main/java/com/magamochi/content/queue/consumer/MangaContentImageUpdateConsumer.java b/src/main/java/com/magamochi/content/queue/consumer/MangaContentImageUpdateConsumer.java new file mode 100644 index 0000000..96a0e6d --- /dev/null +++ b/src/main/java/com/magamochi/content/queue/consumer/MangaContentImageUpdateConsumer.java @@ -0,0 +1,22 @@ +package com.magamochi.content.queue.consumer; + +import com.magamochi.common.queue.command.ImageUpdateCommand; +import com.magamochi.content.service.ContentIngestService; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaContentImageUpdateConsumer { + private final ContentIngestService contentIngestService; + + @RabbitListener(queues = "${queues.manga-content-image-update}") + public void receiveMangaContentImageUpdateCommand(ImageUpdateCommand command) { + log.info("Received manga content image update command: {}", command); + + contentIngestService.updateMangaContentImage(command.entityId(), command.imageId()); + } +} diff --git a/src/main/java/com/magamochi/content/queue/consumer/MangaContentIngestConsumer.java b/src/main/java/com/magamochi/content/queue/consumer/MangaContentIngestConsumer.java new file mode 100644 index 0000000..17df5bd --- /dev/null +++ b/src/main/java/com/magamochi/content/queue/consumer/MangaContentIngestConsumer.java @@ -0,0 +1,22 @@ +package com.magamochi.content.queue.consumer; + +import com.magamochi.common.queue.command.MangaContentIngestCommand; +import com.magamochi.content.service.ContentIngestService; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaContentIngestConsumer { + private final ContentIngestService contentIngestService; + + @RabbitListener(queues = "${queues.manga-content-ingest}") + public void receiveMangaContentIngestCommand(MangaContentIngestCommand command) { + log.info("Received manga content ingest command: {}", command); + contentIngestService.ingest( + command.mangaContentProviderId(), command.title(), command.url(), command.languageCode()); + } +} diff --git a/src/main/java/com/magamochi/content/service/ContentIngestService.java b/src/main/java/com/magamochi/content/service/ContentIngestService.java new file mode 100644 index 0000000..3d2f13b --- /dev/null +++ b/src/main/java/com/magamochi/content/service/ContentIngestService.java @@ -0,0 +1,113 @@ +package com.magamochi.content.service; + +import static java.util.Objects.isNull; + +import com.magamochi.catalog.service.LanguageService; +import com.magamochi.catalog.service.MangaContentProviderService; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.common.model.enumeration.ContentType; +import com.magamochi.common.queue.command.ImageFetchCommand; +import com.magamochi.common.queue.producer.ImageFetchProducer; +import com.magamochi.content.model.entity.MangaContent; +import com.magamochi.content.model.entity.MangaContentImage; +import com.magamochi.content.model.repository.MangaContentImageRepository; +import com.magamochi.content.model.repository.MangaContentRepository; +import com.magamochi.image.service.ImageService; +import jakarta.validation.constraints.NotBlank; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class ContentIngestService { + private final ContentService contentService; + private final MangaContentProviderService mangaContentProviderService; + private final LanguageService languageService; + + private final MangaContentRepository mangaContentRepository; + private final MangaContentImageRepository mangaContentImageRepository; + + private final ImageFetchProducer imageFetchProducer; + private final ImageService imageService; + + public void ingest( + long mangaContentProviderId, + @NotBlank String title, + @NotBlank String url, + @NotBlank String languageCode) { + log.info("Ingesting Manga Content ({}) for provider {}", title, mangaContentProviderId); + + var mangaContentProvider = mangaContentProviderService.find(mangaContentProviderId); + + if (mangaContentRepository.existsByMangaContentProvider_IdAndUrlIgnoreCase( + mangaContentProvider.getId(), url)) { + log.info( + "Manga Content ({}) for provider {} already exists. Skipped.", + title, + mangaContentProviderId); + return; + } + + var language = languageService.find(languageCode); + + var mangaContent = + mangaContentRepository.save( + MangaContent.builder() + .mangaContentProvider(mangaContentProvider) + .title(title) + .url(url) + .language(language) + .build()); + + log.info( + "Ingested Manga Content ({}) for provider {}: {}", + title, + mangaContentProviderId, + mangaContent.getId()); + } + + @Transactional + public void ingestImages( + long mangaContentId, @NotBlank String url, int position, boolean isLast) { + log.info( + "Ingesting Manga Content Image for MangaContent {}, position {}", mangaContentId, position); + + var mangaContent = contentService.find(mangaContentId); + + if (mangaContentImageRepository.existsByMangaContent_IdAndPosition(mangaContentId, position)) { + return; + } + + var mangaContentImage = + mangaContentImageRepository.save( + MangaContentImage.builder().mangaContent(mangaContent).position(position).build()); + + imageFetchProducer.sendImageFetchCommand( + new ImageFetchCommand(mangaContentImage.getId(), ContentType.CONTENT_IMAGE, url)); + + if (isLast) { + mangaContent.setDownloaded(true); + } + } + + @Transactional + public void updateMangaContentImage(long mangaContentImageId, UUID imageId) { + if (isNull(imageId)) { + log.error("Null imageID received!"); + return; + } + + var mangaContentImage = + mangaContentImageRepository + .findById(mangaContentImageId) + .orElseThrow( + () -> new NotFoundException("Image not found for ID: " + mangaContentImageId)); + + var image = imageService.find(imageId); + mangaContentImage.setImage(image); + } +} diff --git a/src/main/java/com/magamochi/content/service/ContentService.java b/src/main/java/com/magamochi/content/service/ContentService.java new file mode 100644 index 0000000..0cd4e79 --- /dev/null +++ b/src/main/java/com/magamochi/content/service/ContentService.java @@ -0,0 +1,64 @@ +package com.magamochi.content.service; + +import com.magamochi.catalog.service.MangaContentProviderService; +import com.magamochi.common.exception.NotFoundException; +import com.magamochi.content.model.dto.MangaContentDTO; +import com.magamochi.content.model.entity.MangaContent; +import com.magamochi.content.model.repository.MangaContentRepository; +import com.magamochi.model.dto.MangaContentImagesDTO; +import jakarta.validation.constraints.NotNull; +import java.util.Comparator; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ContentService { + private final MangaContentProviderService mangaContentProviderService; + + private final MangaContentRepository mangaContentRepository; + + public List getContent(@NotNull Long mangaContentProviderId) { + var mangaContentProvider = mangaContentProviderService.find(mangaContentProviderId); + + return mangaContentProvider.getMangaContents().stream() + .sorted(Comparator.comparing(MangaContent::getId)) + .map(MangaContentDTO::from) + .toList(); + } + + public MangaContent find(Long id) { + return mangaContentRepository + .findById(id) + .orElseThrow(() -> new NotFoundException("MangaContent not found for ID: " + id)); + } + + public MangaContentImagesDTO getContentImages(Long mangaContentId) { + var mangaContent = find(mangaContentId); + + var chapters = + mangaContent.getMangaContentProvider().getMangaContents().stream() + .sorted(Comparator.comparing(MangaContent::getId)) + .toList(); + Long prevId = null; + Long nextId = null; + + // TODO: this doesn't perform well for large datasets + for (var i = 0; i < chapters.size(); i++) { + if (chapters.get(i).getId().equals(mangaContent.getId())) { + if (i > 0) { + prevId = chapters.get(i - 1).getId(); + } + + if (i < chapters.size() - 1) { + nextId = chapters.get(i + 1).getId(); + } + + break; + } + } + + return MangaContentImagesDTO.from(mangaContent, prevId, nextId); + } +} diff --git a/src/main/java/com/magamochi/controller/MangaChapterController.java b/src/main/java/com/magamochi/controller/MangaChapterController.java index 1635b2d..4d1cdf9 100644 --- a/src/main/java/com/magamochi/controller/MangaChapterController.java +++ b/src/main/java/com/magamochi/controller/MangaChapterController.java @@ -1,7 +1,6 @@ package com.magamochi.controller; import com.magamochi.common.model.dto.DefaultResponseDTO; -import com.magamochi.model.dto.MangaChapterImagesDTO; import com.magamochi.model.enumeration.ArchiveFileType; import com.magamochi.service.MangaChapterService; import io.swagger.v3.oas.annotations.Operation; @@ -21,30 +20,6 @@ import org.springframework.web.bind.annotation.*; public class MangaChapterController { private final MangaChapterService mangaChapterService; - @Operation( - summary = "Fetch chapter", - description = "Fetch the chapter from the provider", - tags = {"Manga Chapter"}, - operationId = "fetchChapter") - @PostMapping(value = "/{chapterId}/fetch") - public DefaultResponseDTO fetchChapter(@PathVariable Long chapterId) { - mangaChapterService.fetchChapter(chapterId); - - return DefaultResponseDTO.ok().build(); - } - - @Operation( - summary = "Get the images for a specific manga/provider combination", - description = - "Retrieve a list of manga chapter images for a specific manga/provider combination.", - tags = {"Manga Chapter"}, - operationId = "getMangaChapterImages") - @GetMapping("/{chapterId}/images") - public DefaultResponseDTO getMangaChapterImages( - @PathVariable Long chapterId) { - return DefaultResponseDTO.ok(mangaChapterService.getMangaChapterImages(chapterId)); - } - @Operation( summary = "Mark a chapter as read", description = "Mark a chapter as read by its ID.", diff --git a/src/main/java/com/magamochi/controller/MangaController.java b/src/main/java/com/magamochi/controller/MangaController.java index 3ffdd3d..5c17b85 100644 --- a/src/main/java/com/magamochi/controller/MangaController.java +++ b/src/main/java/com/magamochi/controller/MangaController.java @@ -1,10 +1,8 @@ package com.magamochi.controller; import com.magamochi.common.model.dto.DefaultResponseDTO; -import com.magamochi.model.dto.MangaChapterDTO; import com.magamochi.service.OldMangaService; import io.swagger.v3.oas.annotations.Operation; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -14,17 +12,6 @@ import org.springframework.web.bind.annotation.*; public class MangaController { private final OldMangaService oldMangaService; - @Operation( - summary = "Get the available chapters for a specific manga/provider combination", - description = "Retrieve a list of manga chapters for a specific manga/provider combination.", - tags = {"Manga"}, - operationId = "getMangaChapters") - @GetMapping("/{mangaProviderId}/chapters") - public DefaultResponseDTO> getMangaChapters( - @PathVariable Long mangaProviderId) { - return DefaultResponseDTO.ok(oldMangaService.getMangaChapters(mangaProviderId)); - } - @Operation( summary = "Fetch all chapters", description = "Fetch all not yet downloaded chapters from the provider", @@ -37,18 +24,6 @@ public class MangaController { return DefaultResponseDTO.ok().build(); } - @Operation( - summary = "Fetch the available chapters for a specific manga/provider combination", - description = "Fetch a list of manga chapters for a specific manga/provider combination.", - tags = {"Manga"}, - operationId = "fetchMangaChapters") - @PostMapping("/{mangaProviderId}/fetch-chapters") - public DefaultResponseDTO fetchMangaChapters(@PathVariable Long mangaProviderId) { - oldMangaService.fetchMangaChapters(mangaProviderId); - - return DefaultResponseDTO.ok().build(); - } - @Operation( summary = "Follow the manga specified by its ID", description = "Follow the manga specified by its ID.", diff --git a/src/main/java/com/magamochi/image/service/ImageFetchService.java b/src/main/java/com/magamochi/image/service/ImageFetchService.java index 843b806..0b7c7eb 100644 --- a/src/main/java/com/magamochi/image/service/ImageFetchService.java +++ b/src/main/java/com/magamochi/image/service/ImageFetchService.java @@ -2,6 +2,7 @@ package com.magamochi.image.service; import static java.util.Objects.nonNull; +import com.google.common.util.concurrent.RateLimiter; import com.magamochi.common.model.enumeration.ContentType; import java.net.URI; import java.net.http.HttpClient; @@ -21,13 +22,17 @@ import org.springframework.stereotype.Service; public class ImageFetchService { private final ImageService imageManagerService; + private final RateLimiter imageDownloadRateLimiter; + private final HttpClient httpClient = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build(); private final Tika tika = new Tika(); public UUID fetchImage(String imageUrl, ContentType contentType) { try { - var request = HttpRequest.newBuilder(URI.create(imageUrl)).GET().build(); + var request = HttpRequest.newBuilder(URI.create(imageUrl.trim())).GET().build(); + + imageDownloadRateLimiter.acquire(); var response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()); var imageBytes = response.body(); diff --git a/src/main/java/com/magamochi/ingestion/controller/IngestionController.java b/src/main/java/com/magamochi/ingestion/controller/IngestionController.java index 7b46f07..f8304bf 100644 --- a/src/main/java/com/magamochi/ingestion/controller/IngestionController.java +++ b/src/main/java/com/magamochi/ingestion/controller/IngestionController.java @@ -51,4 +51,30 @@ public class IngestionController { return DefaultResponseDTO.ok().build(); } + + @Operation( + summary = "Fetch content list from a content provider", + description = + "Triggers the ingestion process for a specific content provider, fetching content list and queuing it for processing.", + tags = {"Ingestion"}, + operationId = "fetchContentProviderContentList") + @PostMapping("/manga-content-providers/{mangaContentProviderId}/fetch") + public DefaultResponseDTO fetchContentProviderContentList( + @PathVariable Long mangaContentProviderId) { + ingestionService.fetchMangaContentProviderContentList(mangaContentProviderId); + + return DefaultResponseDTO.ok().build(); + } + + @Operation( + summary = "Fetch content from a content provider", + description = "Fetch the content (images) from the content provider", + tags = {"Ingestion"}, + operationId = "fetchContentProviderContent") + @PostMapping(value = "/manga-content/{mangaContentId}/fetch") + public DefaultResponseDTO fetchContentProviderContent(@PathVariable Long mangaContentId) { + ingestionService.fetchContent(mangaContentId); + + return DefaultResponseDTO.ok().build(); + } } diff --git a/src/main/java/com/magamochi/ingestion/model/dto/ContentImageInfoDTO.java b/src/main/java/com/magamochi/ingestion/model/dto/ContentImageInfoDTO.java new file mode 100644 index 0000000..33ea729 --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/model/dto/ContentImageInfoDTO.java @@ -0,0 +1,5 @@ +package com.magamochi.ingestion.model.dto; + +import jakarta.validation.constraints.NotBlank; + +public record ContentImageInfoDTO(int position, @NotBlank String url) {} diff --git a/src/main/java/com/magamochi/ingestion/model/dto/ContentInfoDTO.java b/src/main/java/com/magamochi/ingestion/model/dto/ContentInfoDTO.java new file mode 100644 index 0000000..d967cbf --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/model/dto/ContentInfoDTO.java @@ -0,0 +1,6 @@ +package com.magamochi.ingestion.model.dto; + +import jakarta.validation.constraints.NotBlank; + +public record ContentInfoDTO( + @NotBlank String title, @NotBlank String url, @NotBlank String languageCode) {} diff --git a/src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java b/src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java index 85baedf..26ab447 100644 --- a/src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java +++ b/src/main/java/com/magamochi/ingestion/model/entity/ContentProvider.java @@ -1,6 +1,6 @@ package com.magamochi.ingestion.model.entity; -import com.magamochi.model.entity.MangaContentProvider; +import com.magamochi.catalog.model.entity.MangaContentProvider; import jakarta.persistence.*; import java.time.Instant; import java.util.List; @@ -24,7 +24,7 @@ public class ContentProvider { private boolean active; - private Boolean supportsChapterFetch; + private Boolean supportsContentFetch; private Boolean manualImport; diff --git a/src/main/java/com/magamochi/ingestion/model/repository/ContentProviderRepository.java b/src/main/java/com/magamochi/ingestion/model/repository/ContentProviderRepository.java index 8232dec..8f1e612 100644 --- a/src/main/java/com/magamochi/ingestion/model/repository/ContentProviderRepository.java +++ b/src/main/java/com/magamochi/ingestion/model/repository/ContentProviderRepository.java @@ -1,9 +1,6 @@ package com.magamochi.ingestion.model.repository; import com.magamochi.ingestion.model.entity.ContentProvider; -import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -public interface ContentProviderRepository extends JpaRepository { - Optional findByNameIgnoreCase(String name); -} +public interface ContentProviderRepository extends JpaRepository {} diff --git a/src/main/java/com/magamochi/ingestion/providers/ContentProvider.java b/src/main/java/com/magamochi/ingestion/providers/ContentProvider.java index 17eb162..aec1b8d 100644 --- a/src/main/java/com/magamochi/ingestion/providers/ContentProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/ContentProvider.java @@ -1,12 +1,12 @@ package com.magamochi.ingestion.providers; -import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.entity.MangaContentProvider; +import com.magamochi.catalog.model.entity.MangaContentProvider; +import com.magamochi.ingestion.model.dto.ContentImageInfoDTO; +import com.magamochi.ingestion.model.dto.ContentInfoDTO; import java.util.List; -import java.util.Map; public interface ContentProvider { - List getAvailableChapters(MangaContentProvider provider); + List getAvailableChapters(MangaContentProvider provider); - Map getChapterImagesUrls(String chapterUrl); + List getContentImages(String chapterUrl); } diff --git a/src/main/java/com/magamochi/ingestion/providers/impl/MangaDexProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/MangaDexProvider.java index 5a46e81..bdc7765 100644 --- a/src/main/java/com/magamochi/ingestion/providers/impl/MangaDexProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/MangaDexProvider.java @@ -3,15 +3,15 @@ package com.magamochi.ingestion.providers.impl; import static java.util.Objects.isNull; import com.google.common.util.concurrent.RateLimiter; +import com.magamochi.catalog.model.entity.MangaContentProvider; import com.magamochi.client.MangaDexClient; import com.magamochi.common.exception.UnprocessableException; +import com.magamochi.ingestion.model.dto.ContentImageInfoDTO; +import com.magamochi.ingestion.model.dto.ContentInfoDTO; import com.magamochi.ingestion.providers.ContentProvider; import com.magamochi.ingestion.providers.ContentProviders; import com.magamochi.ingestion.providers.ManualImportContentProvider; -import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.entity.MangaContentProvider; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -26,8 +26,7 @@ public class MangaDexProvider implements ContentProvider, ManualImportContentPro private final RateLimiter mangaDexRateLimiter; @Override - public List getAvailableChapters( - MangaContentProvider provider) { + public List getAvailableChapters(MangaContentProvider provider) { try { mangaDexRateLimiter.acquire(); var response = @@ -85,10 +84,9 @@ public class MangaDexProvider implements ContentProvider, ManualImportContentPro }) .map( c -> - new ContentProviderMangaChapterResponseDTO( + new ContentInfoDTO( c.attributes().chapter() + " - " + c.attributes().title(), c.id().toString(), - c.attributes().chapter(), languagesToImport.get(c.attributes().translatedLanguage()))) .toList(); } catch (Exception e) { @@ -98,7 +96,7 @@ public class MangaDexProvider implements ContentProvider, ManualImportContentPro } @Override - public Map getChapterImagesUrls(String chapterUrl) { + public List getContentImages(String chapterUrl) { mangaDexRateLimiter.acquire(); var chapter = mangaDexClient.getMangaChapter(UUID.fromString(chapterUrl)); @@ -109,12 +107,8 @@ public class MangaDexProvider implements ContentProvider, ManualImportContentPro return IntStream.range(0, chapterImageHashes.size()) .boxed() - .collect( - Collectors.toMap( - i -> i, - chapterImageHashes::get, - (existing, replacement) -> existing, - LinkedHashMap::new)); + .map(position -> new ContentImageInfoDTO(position, chapterImageHashes.get(position))) + .toList(); } @Override diff --git a/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java index 431b480..3c566de 100644 --- a/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreBlogProvider.java @@ -1,15 +1,15 @@ package com.magamochi.ingestion.providers.impl; +import com.magamochi.catalog.model.entity.MangaContentProvider; +import com.magamochi.ingestion.model.dto.ContentImageInfoDTO; +import com.magamochi.ingestion.model.dto.ContentInfoDTO; import com.magamochi.ingestion.model.dto.MangaInfoDTO; import com.magamochi.ingestion.providers.ContentProvider; import com.magamochi.ingestion.providers.ContentProviders; import com.magamochi.ingestion.providers.PagedContentProvider; -import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.entity.MangaContentProvider; import java.io.IOException; import java.util.*; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -27,8 +27,7 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv private final String url = "https://mangalivre.blog/manga/"; @Override - public List getAvailableChapters( - MangaContentProvider mangaContentProvider) { + public List getAvailableChapters(MangaContentProvider mangaContentProvider) { log.info( "Getting available chapters from {}, manga {}", ContentProviders.MANGA_LIVRE_BLOG, @@ -49,8 +48,8 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv var chapterNumberElement = linkElement.getElementsByClass("chapter-number").getFirst(); - return new ContentProviderMangaChapterResponseDTO( - chapterNumberElement.text(), linkElement.attr("href"), null, "pt-BR"); + return new ContentInfoDTO( + chapterNumberElement.text(), linkElement.attr("href"), "pt-BR"); }) .toList(); } catch (IOException | NoSuchElementException e) { @@ -60,7 +59,7 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv } @Override - public Map getChapterImagesUrls(String chapterUrl) { + public List getContentImages(String chapterUrl) { log.info("Getting images from {}, url {}", ContentProviders.MANGA_LIVRE_BLOG, chapterUrl); try { @@ -90,12 +89,11 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv return IntStream.range(0, imageUrls.size()) .boxed() - .collect( - Collectors.toMap( - i -> i, imageUrls::get, (existing, replacement) -> existing, LinkedHashMap::new)); + .map(position -> new ContentImageInfoDTO(position, imageUrls.get(position))) + .toList(); } catch (IOException | NoSuchElementException e) { log.error("Error fetching mangas from MangaLivre", e); - return Map.of(); + return List.of(); } } diff --git a/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java index 5f0be16..3ec9263 100644 --- a/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/MangaLivreProvider.java @@ -2,15 +2,15 @@ package com.magamochi.ingestion.providers.impl; import static java.util.Objects.nonNull; +import com.magamochi.catalog.model.entity.MangaContentProvider; +import com.magamochi.ingestion.model.dto.ContentImageInfoDTO; +import com.magamochi.ingestion.model.dto.ContentInfoDTO; import com.magamochi.ingestion.model.dto.MangaInfoDTO; import com.magamochi.ingestion.providers.ContentProvider; import com.magamochi.ingestion.providers.ContentProviders; import com.magamochi.ingestion.providers.PagedContentProvider; import com.magamochi.ingestion.service.FlareService; -import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.entity.MangaContentProvider; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -26,8 +26,7 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider private final FlareService flareService; @Override - public List getAvailableChapters( - MangaContentProvider provider) { + public List getAvailableChapters(MangaContentProvider provider) { log.info( "Getting available chapters from {}, manga {}", ContentProviders.MANGA_LIVRE_TO, @@ -49,8 +48,7 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider var url = linkElement.attr("href"); var title = linkElement.text(); - return new ContentProviderMangaChapterResponseDTO( - title.trim(), url.trim(), null, "pt-BR"); + return new ContentInfoDTO(title.trim(), url.trim(), "pt-BR"); }) .toList(); } catch (NoSuchElementException e) { @@ -60,7 +58,7 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider } @Override - public Map getChapterImagesUrls(String chapterUrl) { + public List getContentImages(String chapterUrl) { log.info("Getting images from {}, url {}", ContentProviders.MANGA_LIVRE_TO, chapterUrl); try { @@ -79,12 +77,11 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider return IntStream.range(0, imageUrls.size()) .boxed() - .collect( - Collectors.toMap( - i -> i, imageUrls::get, (existing, replacement) -> existing, LinkedHashMap::new)); + .map(position -> new ContentImageInfoDTO(position, imageUrls.get(position))) + .toList(); } catch (NoSuchElementException e) { log.error("Error parsing manga images from MangaLivre", e); - return Map.of(); + return List.of(); } } diff --git a/src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java b/src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java index d631095..2e4ef56 100644 --- a/src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java +++ b/src/main/java/com/magamochi/ingestion/providers/impl/PinkRosaScanProvider.java @@ -3,15 +3,15 @@ package com.magamochi.ingestion.providers.impl; import static java.util.Objects.isNull; import static org.apache.commons.lang3.StringUtils.isBlank; +import com.magamochi.catalog.model.entity.MangaContentProvider; +import com.magamochi.ingestion.model.dto.ContentImageInfoDTO; +import com.magamochi.ingestion.model.dto.ContentInfoDTO; import com.magamochi.ingestion.model.dto.MangaInfoDTO; import com.magamochi.ingestion.providers.ContentProvider; import com.magamochi.ingestion.providers.ContentProviders; import com.magamochi.ingestion.providers.PagedContentProvider; import com.magamochi.ingestion.service.FlareService; -import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; -import com.magamochi.model.entity.MangaContentProvider; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -27,8 +27,7 @@ public class PinkRosaScanProvider implements ContentProvider, PagedContentProvid private final FlareService flareService; @Override - public List getAvailableChapters( - MangaContentProvider provider) { + public List getAvailableChapters(MangaContentProvider provider) { log.info( "Getting available chapters from {}, manga {}", ContentProviders.PINK_ROSA_SCAN, @@ -57,11 +56,8 @@ public class PinkRosaScanProvider implements ContentProvider, PagedContentProvid .getFirst() .getElementsByClass("text-sm truncate") .getFirst(); - return new ContentProviderMangaChapterResponseDTO( - chapterTitleElement.text().trim(), - chapterItemElement.attr("href"), - null, - "pt-BR"); + return new ContentInfoDTO( + chapterTitleElement.text().trim(), chapterItemElement.attr("href"), "pt-BR"); }) .toList(); } catch (NoSuchElementException e) { @@ -71,7 +67,7 @@ public class PinkRosaScanProvider implements ContentProvider, PagedContentProvid } @Override - public Map getChapterImagesUrls(String chapterUrl) { + public List getContentImages(String chapterUrl) { log.info("Getting images from {}, url {}", ContentProviders.PINK_ROSA_SCAN, chapterUrl); try { @@ -99,12 +95,11 @@ public class PinkRosaScanProvider implements ContentProvider, PagedContentProvid return IntStream.range(0, imageUrls.size()) .boxed() - .collect( - Collectors.toMap( - i -> i, imageUrls::get, (existing, replacement) -> existing, LinkedHashMap::new)); + .map(position -> new ContentImageInfoDTO(position, imageUrls.get(position))) + .toList(); } catch (NoSuchElementException e) { log.error("Error parsing mangas from Pink Rosa Scan", e); - return Map.of(); + return List.of(); } } diff --git a/src/main/java/com/magamochi/ingestion/queue/producer/MangaContentImageIngestProducer.java b/src/main/java/com/magamochi/ingestion/queue/producer/MangaContentImageIngestProducer.java new file mode 100644 index 0000000..38d16df --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/queue/producer/MangaContentImageIngestProducer.java @@ -0,0 +1,23 @@ +package com.magamochi.ingestion.queue.producer; + +import com.magamochi.common.queue.command.MangaContentImageIngestCommand; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaContentImageIngestProducer { + private final RabbitTemplate rabbitTemplate; + + @Value("${queues.manga-content-image-ingest}") + private String mangaContentImageIngestQueue; + + public void sendMangaContentImageIngestCommand(MangaContentImageIngestCommand command) { + rabbitTemplate.convertAndSend(mangaContentImageIngestQueue, command); + log.info("Sent manga content image ingest command: {}", command); + } +} diff --git a/src/main/java/com/magamochi/ingestion/queue/producer/MangaContentIngestProducer.java b/src/main/java/com/magamochi/ingestion/queue/producer/MangaContentIngestProducer.java new file mode 100644 index 0000000..68702c8 --- /dev/null +++ b/src/main/java/com/magamochi/ingestion/queue/producer/MangaContentIngestProducer.java @@ -0,0 +1,23 @@ +package com.magamochi.ingestion.queue.producer; + +import com.magamochi.common.queue.command.MangaContentIngestCommand; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +@RequiredArgsConstructor +public class MangaContentIngestProducer { + private final RabbitTemplate rabbitTemplate; + + @Value("${queues.manga-content-ingest}") + private String mangaContentIngestQueue; + + public void sendMangaContentIngestCommand(MangaContentIngestCommand command) { + rabbitTemplate.convertAndSend(mangaContentIngestQueue, command); + log.info("Sent manga content ingest command: {}", command); + } +} diff --git a/src/main/java/com/magamochi/ingestion/service/IngestionService.java b/src/main/java/com/magamochi/ingestion/service/IngestionService.java index fed47cf..4591499 100644 --- a/src/main/java/com/magamochi/ingestion/service/IngestionService.java +++ b/src/main/java/com/magamochi/ingestion/service/IngestionService.java @@ -1,8 +1,15 @@ package com.magamochi.ingestion.service; +import com.magamochi.catalog.service.MangaContentProviderService; +import com.magamochi.common.queue.command.MangaContentImageIngestCommand; +import com.magamochi.common.queue.command.MangaContentIngestCommand; import com.magamochi.common.queue.command.MangaIngestCommand; +import com.magamochi.content.service.ContentService; +import com.magamochi.ingestion.providers.ContentProviderFactory; import com.magamochi.ingestion.providers.PagedContentProviderFactory; import com.magamochi.ingestion.queue.command.ProviderPageIngestCommand; +import com.magamochi.ingestion.queue.producer.MangaContentImageIngestProducer; +import com.magamochi.ingestion.queue.producer.MangaContentIngestProducer; import com.magamochi.ingestion.queue.producer.MangaIngestProducer; import com.magamochi.ingestion.queue.producer.ProviderPageIngestProducer; import java.util.stream.IntStream; @@ -13,10 +20,16 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class IngestionService { private final ContentProviderService contentProviderService; + private final ContentService contentService; + private final MangaContentProviderService mangaContentProviderService; + + private final ContentProviderFactory contentProviderFactory; private final PagedContentProviderFactory pagedContentProviderFactory; private final ProviderPageIngestProducer providerPageIngestProducer; private final MangaIngestProducer mangaIngestProducer; + private final MangaContentIngestProducer mangaContentIngestProducer; + private final MangaContentImageIngestProducer mangaContentImageIngestProducer; public void fetchContentProviderMangas(long contentProviderId) { var contentProvider = contentProviderService.find(contentProviderId); @@ -48,4 +61,46 @@ public class IngestionService { var contentProviders = contentProviderService.getProviders(null); contentProviders.providers().forEach(dto -> fetchContentProviderMangas(dto.id())); } + + public void fetchMangaContentProviderContentList(Long mangaContentProviderId) { + var mangaContentProvider = mangaContentProviderService.find(mangaContentProviderId); + + var contentProvider = + contentProviderFactory.getContentProvider( + mangaContentProvider.getContentProvider().getName()); + + var availableChapters = contentProvider.getAvailableChapters(mangaContentProvider); + + availableChapters.forEach( + content -> + mangaContentIngestProducer.sendMangaContentIngestCommand( + new MangaContentIngestCommand( + mangaContentProvider.getId(), + content.title(), + content.url(), + content.languageCode()))); + } + + public void fetchContent(Long mangaContentId) { + var mangaContent = contentService.find(mangaContentId); + var mangaContentProvider = mangaContent.getMangaContentProvider(); + + var contentProvider = + contentProviderFactory.getContentProvider( + mangaContentProvider.getContentProvider().getName()); + + var chapterImagesUrl = contentProvider.getContentImages(mangaContent.getUrl()); + + IntStream.range(0, chapterImagesUrl.size()) + .forEach( + i -> { + var item = chapterImagesUrl.get(i); + + var isLast = i == chapterImagesUrl.size() - 1; + + mangaContentImageIngestProducer.sendMangaContentImageIngestCommand( + new MangaContentImageIngestCommand( + mangaContent.getId(), item.url(), item.position(), isLast)); + }); + } } diff --git a/src/main/java/com/magamochi/model/dto/ContentProviderMangaChapterResponseDTO.java b/src/main/java/com/magamochi/model/dto/ContentProviderMangaChapterResponseDTO.java deleted file mode 100644 index 0ea052c..0000000 --- a/src/main/java/com/magamochi/model/dto/ContentProviderMangaChapterResponseDTO.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.magamochi.model.dto; - -import jakarta.validation.constraints.NotBlank; - -public record ContentProviderMangaChapterResponseDTO( - @NotBlank String chapterTitle, - @NotBlank String chapterUrl, - String chapter, - String languageCode) {} diff --git a/src/main/java/com/magamochi/model/dto/MangaChapterDTO.java b/src/main/java/com/magamochi/model/dto/MangaChapterDTO.java deleted file mode 100644 index 9e008b1..0000000 --- a/src/main/java/com/magamochi/model/dto/MangaChapterDTO.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.magamochi.model.dto; - -import com.magamochi.model.entity.MangaChapter; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; - -public record MangaChapterDTO( - @NotNull Long id, - @NotBlank String title, - @NotNull Boolean downloaded, - @NotNull Boolean isRead, - LanguageDTO language) { - public static MangaChapterDTO from(MangaChapter mangaChapter) { - return new MangaChapterDTO( - mangaChapter.getId(), - mangaChapter.getTitle(), - mangaChapter.getDownloaded(), - mangaChapter.getRead(), - LanguageDTO.from(mangaChapter.getLanguage())); - } -} diff --git a/src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java b/src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java deleted file mode 100644 index ea6bcce..0000000 --- a/src/main/java/com/magamochi/model/dto/MangaChapterImagesDTO.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.magamochi.model.dto; - -import com.magamochi.model.entity.MangaChapter; -import com.magamochi.model.entity.MangaChapterImage; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import java.util.Comparator; -import java.util.List; - -public record MangaChapterImagesDTO( - @NotNull Long id, - @NotBlank String mangaTitle, - Long previousChapterId, - Long nextChapterId, - @NotNull List<@NotBlank String> chapterImageKeys) { - public static MangaChapterImagesDTO from(MangaChapter mangaChapter, Long prevId, Long nextId) { - return new MangaChapterImagesDTO( - mangaChapter.getId(), - mangaChapter.getTitle(), - prevId, - nextId, - mangaChapter.getMangaChapterImages().stream() - .sorted(Comparator.comparing(MangaChapterImage::getPosition)) - .map(mangaChapterImage -> mangaChapterImage.getImage().getObjectKey()) - .toList()); - } -} diff --git a/src/main/java/com/magamochi/model/dto/MangaChapterResponseDTO.java b/src/main/java/com/magamochi/model/dto/MangaChapterResponseDTO.java deleted file mode 100644 index 46194d9..0000000 --- a/src/main/java/com/magamochi/model/dto/MangaChapterResponseDTO.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.magamochi.model.dto; - -import jakarta.validation.constraints.NotNull; -import java.util.UUID; - -public record MangaChapterResponseDTO( - @NotNull UUID id, - // @NotNull Long mangaProviderId, - @NotNull String chapterTitle, - @NotNull String chapterUrl) {} diff --git a/src/main/java/com/magamochi/model/dto/MangaContentImagesDTO.java b/src/main/java/com/magamochi/model/dto/MangaContentImagesDTO.java new file mode 100644 index 0000000..72dd26a --- /dev/null +++ b/src/main/java/com/magamochi/model/dto/MangaContentImagesDTO.java @@ -0,0 +1,27 @@ +package com.magamochi.model.dto; + +import com.magamochi.content.model.entity.MangaContent; +import com.magamochi.content.model.entity.MangaContentImage; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.Comparator; +import java.util.List; + +public record MangaContentImagesDTO( + @NotNull Long id, + @NotBlank String mangaTitle, + Long previousContentId, + Long nextContentId, + @NotNull List<@NotBlank String> contentImageKeys) { + public static MangaContentImagesDTO from(MangaContent mangaContent, Long prevId, Long nextId) { + return new MangaContentImagesDTO( + mangaContent.getId(), + mangaContent.getTitle(), + prevId, + nextId, + mangaContent.getMangaContentImages().stream() + .sorted(Comparator.comparing(MangaContentImage::getPosition)) + .map(mangaContentImage -> mangaContentImage.getImage().getObjectKey()) + .toList()); + } +} diff --git a/src/main/java/com/magamochi/model/repository/MangaChapterImageRepository.java b/src/main/java/com/magamochi/model/repository/MangaChapterImageRepository.java deleted file mode 100644 index 0dd8574..0000000 --- a/src/main/java/com/magamochi/model/repository/MangaChapterImageRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.magamochi.model.repository; - -import com.magamochi.model.entity.MangaChapter; -import com.magamochi.model.entity.MangaChapterImage; -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface MangaChapterImageRepository extends JpaRepository { - List findAllByMangaChapter(MangaChapter mangaChapter); -} diff --git a/src/main/java/com/magamochi/model/repository/MangaChapterRepository.java b/src/main/java/com/magamochi/model/repository/MangaChapterRepository.java deleted file mode 100644 index 3b1b9c2..0000000 --- a/src/main/java/com/magamochi/model/repository/MangaChapterRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.magamochi.model.repository; - -import com.magamochi.model.entity.MangaChapter; -import com.magamochi.model.entity.MangaContentProvider; -import jakarta.validation.constraints.NotBlank; -import java.util.List; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface MangaChapterRepository extends JpaRepository { - Optional findByMangaContentProviderAndUrlIgnoreCase( - MangaContentProvider mangaContentProvider, @NotBlank String url); - - List findByMangaContentProviderId(Long mangaProvider_id); -} diff --git a/src/main/java/com/magamochi/queue/MangaChapterDownloadConsumer.java b/src/main/java/com/magamochi/queue/MangaChapterDownloadConsumer.java deleted file mode 100644 index 43da465..0000000 --- a/src/main/java/com/magamochi/queue/MangaChapterDownloadConsumer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.magamochi.queue; - -import com.magamochi.model.dto.MangaChapterDownloadCommand; -import com.magamochi.service.MangaChapterService; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.amqp.rabbit.annotation.RabbitListener; -import org.springframework.stereotype.Service; - -@Log4j2 -@Service -@RequiredArgsConstructor -public class MangaChapterDownloadConsumer { - private final MangaChapterService mangaChapterService; - - @RabbitListener(queues = "${rabbit-mq.queues.manga-chapter-download}") - public void receiveMangaChapterDownloadCommand(MangaChapterDownloadCommand command) { - log.info("Received manga chapter download command: {}", command); - - try { - mangaChapterService.fetchChapter(command.chapterId()); - } catch (Exception e) { - log.error("Couldn't download chapter {}. {}", command.chapterId(), e.getMessage()); - } - } -} diff --git a/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java b/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java index d6d9168..75efa22 100644 --- a/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java +++ b/src/main/java/com/magamochi/queue/UpdateMangaFollowChapterListConsumer.java @@ -16,6 +16,6 @@ public class UpdateMangaFollowChapterListConsumer { @RabbitListener(queues = "${rabbit-mq.queues.manga-follow-update-chapter}") public void receiveMangaFollowUpdateChapterCommand(UpdateMangaFollowChapterListCommand command) { log.info("Received update followed manga chapter list command: {}", command); - oldMangaService.fetchFollowedMangaChapters(command.mangaProviderId()); + // oldMangaService.fetchFollowedMangaChapters(command.mangaProviderId()); } } diff --git a/src/main/java/com/magamochi/service/MangaChapterService.java b/src/main/java/com/magamochi/service/MangaChapterService.java index ccd1209..01ac56e 100644 --- a/src/main/java/com/magamochi/service/MangaChapterService.java +++ b/src/main/java/com/magamochi/service/MangaChapterService.java @@ -1,24 +1,15 @@ package com.magamochi.service; -import com.google.common.util.concurrent.RateLimiter; import com.magamochi.common.exception.UnprocessableException; -import com.magamochi.ingestion.providers.ContentProviderFactory; +import com.magamochi.content.model.entity.MangaContent; +import com.magamochi.content.model.entity.MangaContentImage; +import com.magamochi.content.model.repository.MangaContentImageRepository; +import com.magamochi.content.model.repository.MangaContentRepository; import com.magamochi.model.dto.MangaChapterArchiveDTO; -import com.magamochi.model.dto.MangaChapterImagesDTO; -import com.magamochi.model.entity.MangaChapter; -import com.magamochi.model.entity.MangaChapterImage; import com.magamochi.model.enumeration.ArchiveFileType; -import com.magamochi.model.repository.MangaChapterImageRepository; -import com.magamochi.model.repository.MangaChapterRepository; -import io.github.resilience4j.retry.Retry; -import io.github.resilience4j.retry.RetryRegistry; -import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.net.URI; -import java.net.URL; -import java.util.Comparator; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -26,144 +17,29 @@ import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; @Log4j2 @Service @RequiredArgsConstructor public class MangaChapterService { - private final MangaChapterRepository mangaChapterRepository; - private final MangaChapterImageRepository mangaChapterImageRepository; + private final MangaContentRepository mangaContentRepository; + private final MangaContentImageRepository mangaContentImageRepository; private final OldImageService oldImageService; - private final ContentProviderFactory contentProviderFactory; - - private final RateLimiter imageDownloadRateLimiter; - private final RetryRegistry retryRegistry; - - @Transactional - public void fetchChapter(Long chapterId) { - var chapter = getMangaChapterThrowIfNotFound(chapterId); - - var mangaProvider = chapter.getMangaContentProvider(); - var provider = - contentProviderFactory.getContentProvider(mangaProvider.getContentProvider().getName()); - - var chapterImagesUrls = provider.getChapterImagesUrls(chapter.getUrl()); - if (chapterImagesUrls.isEmpty()) { - throw new UnprocessableException( - "No images found on provider for Manga Chapter ID: " + chapterId); - } - - var retryConfig = retryRegistry.retry("ImageDownloadRetry").getRetryConfig(); - - var chapterImages = - chapterImagesUrls.entrySet().parallelStream() - .map( - entry -> { - imageDownloadRateLimiter.acquire(); - - try { - var finalUrl = new URI(entry.getValue().trim()).toASCIIString().trim(); - var retry = - Retry.of("image-download-" + chapterId + "-" + entry.getKey(), retryConfig); - - retry - .getEventPublisher() - .onRetry( - event -> - log.warn( - "Retrying image download {}/{} for chapter {}. Attempt #{}. Error: {}", - entry.getKey() + 1, - chapterImagesUrls.size(), - chapterId, - event.getNumberOfRetryAttempts(), - event.getLastThrowable().getMessage())); - - return retry.executeCheckedSupplier( - () -> { - var url = new URL(finalUrl); - var connection = url.openConnection(); - connection.setConnectTimeout(5000); - connection.setReadTimeout(5000); - - try (var inputStream = - new BufferedInputStream(connection.getInputStream())) { - var bytes = inputStream.readAllBytes(); - - var image = - oldImageService.uploadImage( - bytes, "image/jpeg", "chapter/" + chapterId); - - log.info( - "Downloaded image {}/{} for manga {} chapter {}: {}", - entry.getKey() + 1, - chapterImagesUrls.size(), - chapter.getMangaContentProvider().getManga().getTitle(), - chapterId, - entry.getValue()); - - return MangaChapterImage.builder() - .mangaChapter(chapter) - .position(entry.getKey()) - .image(image) - .build(); - } - }); - } catch (Throwable e) { - throw new UnprocessableException( - "Could not download image for chapter ID: " + chapterId, e); - } - }) - .toList(); - - mangaChapterImageRepository.saveAll(chapterImages); - - chapter.setDownloaded(true); - mangaChapterRepository.save(chapter); - } - - public MangaChapterImagesDTO getMangaChapterImages(Long chapterId) { - var chapter = getMangaChapterThrowIfNotFound(chapterId); - - var chapters = - chapter.getMangaContentProvider().getMangaChapters().stream() - .sorted(Comparator.comparing(MangaChapter::getId)) - .toList(); - Long prevId = null; - Long nextId = null; - - // TODO: this doesn't perform well for large datasets - for (var i = 0; i < chapters.size(); i++) { - if (chapters.get(i).getId().equals(chapterId)) { - if (i > 0) { - prevId = chapters.get(i - 1).getId(); - } - - if (i < chapters.size() - 1) { - nextId = chapters.get(i + 1).getId(); - } - - break; - } - } - - return MangaChapterImagesDTO.from(chapter, prevId, nextId); - } - public void markAsRead(Long chapterId) { - var chapter = getMangaChapterThrowIfNotFound(chapterId); - chapter.setRead(true); - - mangaChapterRepository.save(chapter); + // TODO: implement this + // var chapter = getMangaChapterThrowIfNotFound(chapterId); + // chapter.setRead(true); + // + // mangaChapterRepository.save(chapter); } public MangaChapterArchiveDTO downloadChapter(Long chapterId, ArchiveFileType archiveFileType) throws IOException { var chapter = getMangaChapterThrowIfNotFound(chapterId); - var chapterImages = mangaChapterImageRepository.findAllByMangaChapter(chapter); + var chapterImages = mangaContentImageRepository.findAllByMangaContent(chapter); var byteArrayOutputStream = switch (archiveFileType) { @@ -177,7 +53,7 @@ public class MangaChapterService { chapter.getTitle() + ".cbz", byteArrayOutputStream.toByteArray()); } - private ByteArrayOutputStream getChapterCbzArchive(List chapterImages) + private ByteArrayOutputStream getChapterCbzArchive(List chapterImages) throws IOException { var byteArrayOutputStream = new ByteArrayOutputStream(); var bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream); @@ -202,8 +78,8 @@ public class MangaChapterService { return byteArrayOutputStream; } - private MangaChapter getMangaChapterThrowIfNotFound(Long chapterId) { - return mangaChapterRepository + private MangaContent getMangaChapterThrowIfNotFound(Long chapterId) { + return mangaContentRepository .findById(chapterId) .orElseThrow(() -> new RuntimeException("Manga Chapter not found for ID: " + chapterId)); } diff --git a/src/main/java/com/magamochi/service/MangaImportService.java b/src/main/java/com/magamochi/service/MangaImportService.java index ea64e30..510f9c2 100644 --- a/src/main/java/com/magamochi/service/MangaImportService.java +++ b/src/main/java/com/magamochi/service/MangaImportService.java @@ -378,12 +378,12 @@ // var mangaChapter = // mangaChapterRepository // .findByMangaContentProviderAndUrlIgnoreCase(mangaContentProvider, -// chapter.chapterUrl()) +// chapter.url()) // .orElseGet(MangaChapter::new); // // mangaChapter.setMangaContentProvider(mangaContentProvider); -// mangaChapter.setTitle(chapter.chapterTitle()); -// mangaChapter.setUrl(chapter.chapterUrl()); +// mangaChapter.setTitle(chapter.title()); +// mangaChapter.setUrl(chapter.url()); // // var language = languageService.getOrThrow(chapter.languageCode()); // mangaChapter.setLanguage(language); diff --git a/src/main/java/com/magamochi/service/OldImageService.java b/src/main/java/com/magamochi/service/OldImageService.java index 79d3ef5..fe7ba46 100644 --- a/src/main/java/com/magamochi/service/OldImageService.java +++ b/src/main/java/com/magamochi/service/OldImageService.java @@ -1,7 +1,6 @@ package com.magamochi.service; import com.magamochi.image.model.entity.Image; -import com.magamochi.image.model.repository.ImageRepository; import java.io.InputStream; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -12,14 +11,6 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class OldImageService { private final OldS3Service oldS3Service; - private final ImageRepository imageRepository; - - public Image uploadImage(byte[] data, String contentType, String path) { - log.info("Uploading image {} to S3", path); - var fileKey = oldS3Service.uploadFile(data, contentType, path); - - return imageRepository.save(Image.builder().objectKey(fileKey).build()); - } public InputStream getImageStream(Image image) { return oldS3Service.getFile(image.getObjectKey()); diff --git a/src/main/java/com/magamochi/service/OldMangaService.java b/src/main/java/com/magamochi/service/OldMangaService.java index b0de84c..412cdb8 100644 --- a/src/main/java/com/magamochi/service/OldMangaService.java +++ b/src/main/java/com/magamochi/service/OldMangaService.java @@ -3,18 +3,13 @@ package com.magamochi.service; import com.magamochi.catalog.model.entity.Manga; import com.magamochi.catalog.model.repository.MangaContentProviderRepository; import com.magamochi.catalog.model.repository.MangaRepository; -import com.magamochi.client.NtfyClient; import com.magamochi.common.exception.NotFoundException; -import com.magamochi.ingestion.providers.ContentProviderFactory; +import com.magamochi.content.model.entity.MangaContent; import com.magamochi.model.dto.*; -import com.magamochi.model.entity.MangaChapter; -import com.magamochi.model.entity.MangaContentProvider; import com.magamochi.model.entity.UserMangaFollow; import com.magamochi.model.repository.*; import com.magamochi.queue.MangaChapterDownloadProducer; import com.magamochi.user.service.UserService; -import java.util.Comparator; -import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -25,19 +20,13 @@ import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor public class OldMangaService { - // private final MangaImportService mangaImportService; private final UserService userService; private final MangaRepository mangaRepository; private final MangaContentProviderRepository mangaContentProviderRepository; - private final ContentProviderFactory contentProviderFactory; - private final UserMangaFollowRepository userMangaFollowRepository; private final MangaChapterDownloadProducer mangaChapterDownloadProducer; - private final MangaChapterRepository mangaChapterRepository; - - private final NtfyClient ntfyClient; public void fetchAllNotDownloadedChapters(Long mangaProviderId) { var mangaProvider = @@ -47,9 +36,9 @@ public class OldMangaService { () -> new NotFoundException("Manga Provider not found for ID: " + mangaProviderId)); var chapterIds = - mangaProvider.getMangaChapters().stream() + mangaProvider.getMangaContents().stream() .filter(mangaChapter -> !mangaChapter.getDownloaded()) - .map(MangaChapter::getId) + .map(MangaContent::getId) .collect(Collectors.toSet()); chapterIds.forEach( @@ -58,57 +47,39 @@ public class OldMangaService { new MangaChapterDownloadCommand(chapterId))); } - public List getMangaChapters(Long mangaProviderId) { - var mangaProvider = getMangaProviderThrowIfNotFound(mangaProviderId); - - return mangaProvider.getMangaChapters().stream() - .sorted(Comparator.comparing(MangaChapter::getId)) - .map(MangaChapterDTO::from) - .toList(); - } - - public void fetchFollowedMangaChapters(Long mangaProviderId) { - var mangaProvider = - mangaContentProviderRepository - .findById(mangaProviderId) - .orElseThrow( - () -> new NotFoundException("Manga Provider not found for ID: " + mangaProviderId)); - - var currentAvailableChapterCount = - mangaChapterRepository.findByMangaContentProviderId(mangaProviderId).size(); - - fetchMangaChapters(mangaProviderId); - mangaChapterRepository.flush(); - - var availableChapterCount = - mangaChapterRepository.findByMangaContentProviderId(mangaProviderId).size(); - - if (availableChapterCount <= currentAvailableChapterCount) { - return; - } - - log.info("New chapters found for Manga Provider {}", mangaProviderId); - - var userMangaFollows = userMangaFollowRepository.findByManga(mangaProvider.getManga()); - userMangaFollows.forEach( - umf -> - ntfyClient.notify( - new NtfyClient.Request( - "mangamochi-" + umf.getUser().getId().toString(), - umf.getManga().getTitle(), - "New chapter available on " + mangaProvider.getContentProvider().getName()))); - } - - public void fetchMangaChapters(Long mangaProviderId) { - var mangaProvider = getMangaProviderThrowIfNotFound(mangaProviderId); - - var contentProvider = - contentProviderFactory.getContentProvider(mangaProvider.getContentProvider().getName()); - var availableChapters = contentProvider.getAvailableChapters(mangaProvider); - - // availableChapters.forEach( - // chapter -> mangaImportService.persistMangaChapter(mangaProvider, chapter)); - } + // public void fetchFollowedMangaChapters(Long mangaProviderId) { + // var mangaProvider = + // mangaContentProviderRepository + // .findById(mangaProviderId) + // .orElseThrow( + // () -> new NotFoundException("Manga Provider not found for ID: " + + // mangaProviderId)); + // + // var currentAvailableChapterCount = + // mangaChapterRepository.findByMangaContentProviderId(mangaProviderId).size(); + // + // fetchMangaChapters(mangaProviderId); + // mangaChapterRepository.flush(); + // + // var availableChapterCount = + // mangaChapterRepository.findByMangaContentProviderId(mangaProviderId).size(); + // + // if (availableChapterCount <= currentAvailableChapterCount) { + // return; + // } + // + // log.info("New chapters found for Manga Provider {}", mangaProviderId); + // + // var userMangaFollows = userMangaFollowRepository.findByManga(mangaProvider.getManga()); + // userMangaFollows.forEach( + // umf -> + // ntfyClient.notify( + // new NtfyClient.Request( + // "mangamochi-" + umf.getUser().getId().toString(), + // umf.getManga().getTitle(), + // "New chapter available on " + + // mangaProvider.getContentProvider().getName()))); + // } public Manga findMangaByIdThrowIfNotFound(Long mangaId) { return mangaRepository @@ -116,13 +87,6 @@ public class OldMangaService { .orElseThrow(() -> new NotFoundException("Manga not found for ID: " + mangaId)); } - private MangaContentProvider getMangaProviderThrowIfNotFound(Long mangaProviderId) { - return mangaContentProviderRepository - .findById(mangaProviderId) - .orElseThrow( - () -> new NotFoundException("Manga Provider not found for ID: " + mangaProviderId)); - } - @Transactional public void follow(Long mangaId) { var user = userService.getLoggedUserThrowIfNotFound(); diff --git a/src/main/java/com/magamochi/service/OldS3Service.java b/src/main/java/com/magamochi/service/OldS3Service.java index 69fe6c5..d8cdc8d 100644 --- a/src/main/java/com/magamochi/service/OldS3Service.java +++ b/src/main/java/com/magamochi/service/OldS3Service.java @@ -1,11 +1,9 @@ package com.magamochi.service; import java.io.InputStream; -import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.*; @@ -17,17 +15,6 @@ public class OldS3Service { private final S3Client s3Client; - public String uploadFile(byte[] data, String contentType, String path) { - var filename = "manga/" + path + "/" + UUID.randomUUID(); - - var request = - PutObjectRequest.builder().bucket(bucket).key(filename).contentType(contentType).build(); - - s3Client.putObject(request, RequestBody.fromBytes(data)); - - return filename; - } - public InputStream getFile(String key) { var request = GetObjectRequest.builder().bucket(bucket).key(key).build(); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 99de633..765c34a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -96,8 +96,11 @@ topics: queues: manga-ingest: ${MANGA_INGEST_QUEUE:mangaIngest} + manga-content-ingest: ${MANGA_CONTENT_INGEST_QUEUE:mangaContentIngest} + manga-content-image-ingest: ${MANGA_CONTENT_IMAGE_INGEST_QUEUE:mangaContentImageIngest} provider-page-ingest: ${PROVIDER_PAGE_INGEST_QUEUE:providerPageIngest} manga-update: ${MANGA_UPDATE_QUEUE:mangaUpdate} + manga-content-image-update: ${MANGA_CONTENT_IMAGE_UPDATE_QUEUE:mangaContentImageUpdate} image-fetch: ${IMAGE_FETCH_QUEUE:imageFetch} manga-cover-update: ${MANGA_COVER_UDPATE_QUEUE:mangaCoverUpdate} diff --git a/src/main/resources/db/migration/V0001__CREATE_TABLES.sql b/src/main/resources/db/migration/V0001__CREATE_TABLES.sql index 05db389..ca96fe7 100644 --- a/src/main/resources/db/migration/V0001__CREATE_TABLES.sql +++ b/src/main/resources/db/migration/V0001__CREATE_TABLES.sql @@ -15,8 +15,8 @@ ON CONFLICT DO NOTHING; CREATE TABLE images ( id UUID NOT NULL PRIMARY KEY, - object_key VARCHAR NOT NULL, - file_hash VARCHAR(64) UNIQUE, + object_key VARCHAR NOT NULL, + file_hash VARCHAR(64) UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -46,7 +46,7 @@ CREATE TABLE content_providers name VARCHAR NOT NULL, url VARCHAR NOT NULL, active BOOLEAN NOT NULL DEFAULT TRUE, - supports_chapter_fetch BOOLEAN NOT NULL DEFAULT TRUE, + supports_content_fetch BOOLEAN NOT NULL DEFAULT TRUE, manual_import BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP @@ -64,24 +64,23 @@ CREATE TABLE manga_content_provider UNIQUE (manga_id, content_provider_id) ); -CREATE TABLE manga_chapters +CREATE TABLE manga_contents ( - id BIGSERIAL NOT NULL PRIMARY KEY, - manga_content_provider_id BIGINT NOT NULL REFERENCES manga_content_provider (id) ON DELETE CASCADE, - title VARCHAR NOT NULL, - url VARCHAR NOT NULL, - chapter_number INTEGER, + id BIGSERIAL NOT NULL PRIMARY KEY, + manga_content_provider_id BIGINT NOT NULL REFERENCES manga_content_provider (id) ON DELETE CASCADE, + type VARCHAR(50) NOT NULL, + title VARCHAR NOT NULL, + url VARCHAR NOT NULL, language_id BIGINT REFERENCES languages (id), - downloaded BOOLEAN DEFAULT FALSE, - read BOOLEAN DEFAULT FALSE, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + downloaded BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE TABLE manga_chapter_images +CREATE TABLE manga_content_images ( id BIGSERIAL NOT NULL PRIMARY KEY, - manga_chapter_id BIGINT NOT NULL REFERENCES manga_chapters (id) ON DELETE CASCADE, + manga_content_id BIGINT NOT NULL REFERENCES manga_contents (id) ON DELETE CASCADE, image_id UUID REFERENCES images (id) ON DELETE CASCADE, position INT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, diff --git a/src/main/resources/db/migration/V0002__CONTENT_PROVIDERS.sql b/src/main/resources/db/migration/V0002__CONTENT_PROVIDERS.sql index dce63ff..45cfb40 100644 --- a/src/main/resources/db/migration/V0002__CONTENT_PROVIDERS.sql +++ b/src/main/resources/db/migration/V0002__CONTENT_PROVIDERS.sql @@ -1,4 +1,4 @@ -INSERT INTO content_providers(name, url, active, supports_chapter_fetch, manual_import) +INSERT INTO content_providers(name, url, active, supports_content_fetch, manual_import) VALUES ('Manga Livre Blog', 'https://mangalivre.blog', TRUE, TRUE, FALSE), ('Manga Livre.to', 'https://mangalivre.to', TRUE, TRUE, FALSE), ('Pink Rosa Scan', 'https://scanpinkrosa.blogspot.com', TRUE, TRUE, FALSE); \ No newline at end of file -- 2.49.1 From 135725ab3ca267144d559141db4fa4f79e2013fe Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Wed, 18 Mar 2026 16:54:18 -0300 Subject: [PATCH 11/11] refactor: update configuration to use environment variables and bump versions --- pom.xml | 35 +++------- .../security/JwtRequestFilter.java | 4 +- .../com/magamochi/client/MangaDexClient.java | 2 - .../java/com/magamochi/client/NtfyClient.java | 2 - .../ingestion/client/FlareClient.java | 2 - src/main/resources/application.yml | 67 +++++-------------- 6 files changed, 27 insertions(+), 85 deletions(-) diff --git a/pom.xml b/pom.xml index d6f8822..3df6343 100644 --- a/pom.xml +++ b/pom.xml @@ -5,14 +5,14 @@ org.springframework.boot spring-boot-starter-parent - 3.5.6 + 4.0.3 - com.magamochi + com.mangamochi mangamochi 0.0.1-SNAPSHOT mangamochi - Demo project for Spring Boot + @@ -27,7 +27,7 @@ - 21 + 25 @@ -65,56 +65,49 @@ software.amazon.awssdk s3 - 2.34.5 + 2.42.14 compile org.springdoc springdoc-openapi-starter-webmvc-ui - 2.8.13 + 3.0.2 org.springframework.cloud spring-cloud-starter-openfeign - 4.3.0 + 5.0.1 org.jsoup jsoup - 1.21.2 + 1.22.1 io.hypersistence - hypersistence-utils-hibernate-63 - 3.11.0 + hypersistence-utils-hibernate-73 + 3.15.2 - - com.google.guava guava 33.5.0-jre - org.springframework.boot spring-boot-starter-security - - io.jsonwebtoken jjwt-api 0.13.0 - io.jsonwebtoken jjwt-impl 0.13.0 runtime - io.jsonwebtoken jjwt-jackson @@ -124,12 +117,6 @@ org.springframework.boot spring-boot-starter-amqp - - - - io.github.resilience4j - resilience4j-spring-boot3 - 2.3.0 org.apache.tika @@ -151,8 +138,6 @@ lombok - 22 - 22 diff --git a/src/main/java/com/magamochi/authentication/security/JwtRequestFilter.java b/src/main/java/com/magamochi/authentication/security/JwtRequestFilter.java index 994433a..7c8f717 100644 --- a/src/main/java/com/magamochi/authentication/security/JwtRequestFilter.java +++ b/src/main/java/com/magamochi/authentication/security/JwtRequestFilter.java @@ -10,7 +10,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; @@ -25,7 +25,7 @@ public class JwtRequestFilter extends OncePerRequestFilter { @Override protected void doFilterInternal( - HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain chain) + HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain chain) throws ServletException, IOException { final String authorizationHeader = request.getHeader("Authorization"); diff --git a/src/main/java/com/magamochi/client/MangaDexClient.java b/src/main/java/com/magamochi/client/MangaDexClient.java index 8bf0056..57ca8c5 100644 --- a/src/main/java/com/magamochi/client/MangaDexClient.java +++ b/src/main/java/com/magamochi/client/MangaDexClient.java @@ -1,14 +1,12 @@ package com.magamochi.client; import com.magamochi.model.dto.MangaDexMangaDTO; -import io.github.resilience4j.retry.annotation.Retry; import java.util.List; import java.util.UUID; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.*; @FeignClient(name = "mangaDex", url = "https://api.mangadex.org") -@Retry(name = "MangaDexRetry") public interface MangaDexClient { @GetMapping("/manga/{id}") MangaDexMangaDTO getManga(@PathVariable UUID id); diff --git a/src/main/java/com/magamochi/client/NtfyClient.java b/src/main/java/com/magamochi/client/NtfyClient.java index 76bf158..af6618b 100644 --- a/src/main/java/com/magamochi/client/NtfyClient.java +++ b/src/main/java/com/magamochi/client/NtfyClient.java @@ -1,12 +1,10 @@ package com.magamochi.client; -import io.github.resilience4j.retry.annotation.Retry; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; @FeignClient(name = "ntfy", url = "${ntfy.endpoint}") -@Retry(name = "JikanRetry") public interface NtfyClient { @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) void notify(@RequestBody Request dto); diff --git a/src/main/java/com/magamochi/ingestion/client/FlareClient.java b/src/main/java/com/magamochi/ingestion/client/FlareClient.java index 42e52de..5756b26 100644 --- a/src/main/java/com/magamochi/ingestion/client/FlareClient.java +++ b/src/main/java/com/magamochi/ingestion/client/FlareClient.java @@ -1,6 +1,5 @@ package com.magamochi.ingestion.client; -import io.github.resilience4j.retry.annotation.Retry; import java.util.List; import lombok.Builder; import lombok.Getter; @@ -10,7 +9,6 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @FeignClient(name = "flare-solverr", url = "${flare-solverr.endpoint}/v1") -@Retry(name = "FlareSolverrRetry") public interface FlareClient { @PostMapping( consumes = MediaType.APPLICATION_JSON_VALUE, diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 765c34a..7613222 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,12 +8,12 @@ spring: jpa: properties: hibernate: - default_schema: mangamochi + default_schema: ${DB_SCHEMA:mangamochi} flyway: enabled: true schemas: - - mangamochi - default-schema: mangamochi + - ${DB_SCHEMA:mangamochi} + default-schema: ${DB_SCHEMA:mangamochi} servlet: multipart: max-file-size: 2GB @@ -48,67 +48,30 @@ storage: base-url: ${minio.endpoint}/${minio.bucket} ntfy: - endpoint: ${NTFY_ENDPOINT:https://ntfy.badger-pirarucu.ts.net} + endpoint: ${NTFY_ENDPOINT} jwt: - secret: /JcSdxjeyeuMGoK5GD9w7OfqK/j+nvHR7uVUY12pNis= + secret: ${JWT_SECRET} expiration: 3600000 - refresh-secret: MIV9ctIwrImmrZBjh9QueNEcDOLLVv9Rephii+0DKbk= + refresh-secret: ${JWT_REFRESH_SECRET} refresh-expiration: 2629746000 -resilience4j: - retry: - instances: - FlareSolverrRetry: - max-attempts: 2 - wait-duration: - seconds: 5 - retry-exceptions: - - feign.FeignException - MangaDexRetry: - max-attempts: 5 - wait-duration: - seconds: 5 - retry-exceptions: - - feign.FeignException - JikanRetry: - max-attempts: 5 - wait-duration: - seconds: 5 - retry-exceptions: - - feign.FeignException - AniListRetry: - max-attempts: 5 - wait-duration: - seconds: 5 - retry-exceptions: - - feign.FeignException - ImageDownloadRetry: - max-attempts: 3 - wait-duration: - seconds: 5 - retry-exceptions: - - java.io.IOException - - java.net.SocketTimeoutException - topics: - image-updates: ${IMAGE_UPDATES_TOPIC:imageUpdates} + image-updates: ${IMAGE_UPDATES_TOPIC:mangamochi.image.updates} queues: - manga-ingest: ${MANGA_INGEST_QUEUE:mangaIngest} - manga-content-ingest: ${MANGA_CONTENT_INGEST_QUEUE:mangaContentIngest} - manga-content-image-ingest: ${MANGA_CONTENT_IMAGE_INGEST_QUEUE:mangaContentImageIngest} - provider-page-ingest: ${PROVIDER_PAGE_INGEST_QUEUE:providerPageIngest} - manga-update: ${MANGA_UPDATE_QUEUE:mangaUpdate} - manga-content-image-update: ${MANGA_CONTENT_IMAGE_UPDATE_QUEUE:mangaContentImageUpdate} - image-fetch: ${IMAGE_FETCH_QUEUE:imageFetch} - manga-cover-update: ${MANGA_COVER_UDPATE_QUEUE:mangaCoverUpdate} + manga-ingest: ${MANGA_INGEST_QUEUE:mangamochi.manga.ingest} + manga-update: ${MANGA_UPDATE_QUEUE:mangamochi.manga.update} + manga-content-ingest: ${MANGA_CONTENT_INGEST_QUEUE:mangamochi.manga.content.ingest} + manga-content-image-ingest: ${MANGA_CONTENT_IMAGE_INGEST_QUEUE:mangamochi.manga.content.image.ingest} + manga-content-image-update: ${MANGA_CONTENT_IMAGE_UPDATE_QUEUE:mangamochi.manga.content.image.update} + provider-page-ingest: ${PROVIDER_PAGE_INGEST_QUEUE:mangamochi.provider.page.ingest} + image-fetch: ${IMAGE_FETCH_QUEUE:mangamochi.image.fetch} + manga-cover-update: ${MANGA_COVER_UDPATE_QUEUE:mangamochi.manga.cover.update} rabbit-mq: queues: - manga-data-update: ${MANGA_DATA_UPDATE_QUEUE:mangaDataUpdateQueue} manga-chapter-download: ${MANGA_CHAPTER_DOWNLOAD_QUEUE:mangaChapterDownloadQueue} - manga-list-update: ${MANGA_LIST_UPDATE_QUEUE:mangaListUpdateQueue} manga-follow-update-chapter: ${MANGA_FOLLOW_UPDATE_CHAPTER_QUEUE:mangaFollowUpdateChapterQueue} image-service: -- 2.49.1