refactor: rename Provider to ContentProvider and update related references

This commit is contained in:
Rodrigo Verdiani 2026-03-17 19:53:38 -03:00
parent 3ecd38ef2c
commit d845dc4d12
41 changed files with 244 additions and 229 deletions

View File

@ -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<ProviderListDTO> getMangas(
@RequestParam(name = "manualImport", required = false) Boolean manualImport) {
return DefaultResponseDTO.ok(providerService.getProviders(manualImport));
}
}

View File

@ -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<ContentProviderListDTO> getContentProviders(
@RequestParam(name = "manualImport", required = false) Boolean manualImport) {
return DefaultResponseDTO.ok(contentProviderService.getProviders(manualImport));
}
}

View File

@ -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<ContentProviderDTO> providers) {
public static ContentProviderListDTO from(List<ContentProvider> 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());
}
}
}

View File

@ -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<MangaProvider> mangaProviders;
@Builder.Default private Boolean supportsChapterFetch = true;
@Builder.Default private Boolean manualImport = false;
@OneToMany(mappedBy = "contentProvider")
private List<MangaContentProvider> mangaContentProviders;
}

View File

@ -1,4 +1,4 @@
package com.magamochi.model.entity;
package com.magamochi.ingestion.model.entity;
import java.time.Instant;
import lombok.Builder;

View File

@ -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<ContentProvider, Long> {
Optional<ContentProvider> findByNameIgnoreCase(String name);
}

View File

@ -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);
}
}

View File

@ -1,4 +1,4 @@
package com.magamochi.service;
package com.magamochi.ingestion.service;
import com.magamochi.client.FlareClient;
import lombok.RequiredArgsConstructor;

View File

@ -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;

View File

@ -1,4 +1,4 @@
package com.magamochi.task;
package com.magamochi.ingestion.task;
import com.magamochi.client.FlareClient;
import com.magamochi.registry.FlareSessionRegistry;

View File

@ -1,4 +1,4 @@
package com.magamochi.task;
package com.magamochi.ingestion.task;
import com.magamochi.client.FlareClient;
import lombok.RequiredArgsConstructor;

View File

@ -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());

View File

@ -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());
}
}
}

View File

@ -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())

View File

@ -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<ProviderDTO> providers) {
public static ProviderListDTO from(List<Provider> 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());
}
}
}

View File

@ -35,7 +35,7 @@ public class Manga {
@UpdateTimestamp private Instant updatedAt;
@OneToMany(mappedBy = "manga")
private List<MangaProvider> mangaProviders;
private List<MangaContentProvider> mangaContentProviders;
@ManyToOne
@JoinColumn(name = "cover_image_id")

View File

@ -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;

View File

@ -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<MangaChapter> mangaChapters;
@CreationTimestamp private Instant createdAt;

View File

@ -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;
}

View File

@ -1,6 +0,0 @@
package com.magamochi.model.enumeration;
public enum ProviderStatus {
ACTIVE,
INACTIVE
}

View File

@ -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<MangaChapter, Long> {
Optional<MangaChapter> findByMangaProviderAndUrlIgnoreCase(
MangaProvider mangaProvider, @NotBlank String url);
Optional<MangaChapter> findByMangaContentProviderAndUrlIgnoreCase(
MangaContentProvider mangaContentProvider, @NotBlank String url);
List<MangaChapter> findByMangaProviderId(Long mangaProvider_id);
List<MangaChapter> findByMangaContentProviderId(Long mangaProvider_id);
}

View File

@ -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<MangaContentProvider, Long> {
boolean existsByMangaAndContentProviderAndUrlIgnoreCase(
Manga manga, ContentProvider contentProvider, String url);
Optional<MangaContentProvider> findByMangaTitleIgnoreCaseAndContentProvider(
String mangaTitle, ContentProvider contentProvider);
}

View File

@ -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<MangaProvider, Long> {
boolean existsByMangaAndProviderAndUrlIgnoreCase(Manga manga, Provider provider, String url);
Optional<MangaProvider> findByMangaTitleIgnoreCaseAndProvider(
String mangaTitle, Provider provider);
}

View File

@ -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<Provider, Long> {
Optional<Provider> findByNameIgnoreCase(String name);
}

View File

@ -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;

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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());
}

View File

@ -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());
});

View File

@ -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());
}

View File

@ -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));

View File

@ -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");
}

View File

@ -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()));
}

View File

@ -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<ContentProviderMangaChapterResponseDTO> getAvailableChapters(MangaProvider provider);
List<ContentProviderMangaChapterResponseDTO> getAvailableChapters(MangaContentProvider provider);
Map<Integer, String> getChapterImagesUrls(String chapterUrl);
}

View File

@ -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<ContentProviderMangaChapterResponseDTO> getAvailableChapters(MangaProvider provider) {
public List<ContentProviderMangaChapterResponseDTO> getAvailableChapters(
MangaContentProvider provider) {
try {
var document =
flareService.getContentAsJsoupDocument(provider.getUrl(), ContentProviders.BATO);

View File

@ -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<ContentProviderMangaChapterResponseDTO> getAvailableChapters(MangaProvider provider) {
public List<ContentProviderMangaChapterResponseDTO> getAvailableChapters(
MangaContentProvider provider) {
try {
mangaDexRateLimiter.acquire();
var response =

View File

@ -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<ContentProviderMangaChapterResponseDTO> 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");

View File

@ -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<ContentProviderMangaChapterResponseDTO> getAvailableChapters(MangaProvider provider) {
public List<ContentProviderMangaChapterResponseDTO> getAvailableChapters(
MangaContentProvider provider) {
log.info(
"Getting available chapters from {}, manga {}",
ContentProviders.MANGA_LIVRE_TO,

View File

@ -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<ContentProviderMangaChapterResponseDTO> getAvailableChapters(MangaProvider provider) {
public List<ContentProviderMangaChapterResponseDTO> getAvailableChapters(
MangaContentProvider provider) {
log.info(
"Getting available chapters from {}, manga {}",
ContentProviders.PINK_ROSA_SCAN,

View File

@ -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(

View File

@ -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());