Compare commits

...

2 Commits

2 changed files with 99 additions and 9 deletions

View File

@ -27,6 +27,20 @@ public class MangaIngestService {
log.info( log.info(
"Ingesting manga with mangaTitle '{}' from provider {}", mangaTitle, contentProviderId); "Ingesting manga with mangaTitle '{}' from provider {}", mangaTitle, contentProviderId);
try {
doIngestManga(contentProviderId, mangaTitle, url);
} catch (Exception e) {
log.error(
"Unexpected error ingesting manga '{}' from provider {} at url '{}'",
mangaTitle,
contentProviderId,
url,
e);
throw e;
}
}
private void doIngestManga(long contentProviderId, String mangaTitle, String url) {
if (mangaContentProviderRepository.existsByMangaTitleIgnoreCaseAndContentProvider_Id( if (mangaContentProviderRepository.existsByMangaTitleIgnoreCaseAndContentProvider_Id(
mangaTitle, contentProviderId)) { mangaTitle, contentProviderId)) {
log.info( log.info(
@ -51,6 +65,17 @@ public class MangaIngestService {
return; return;
} }
if (mangaContentProviderRepository
.findByManga_IdAndContentProvider_Id(manga.getId(), contentProviderId)
.isPresent()) {
log.info(
"MangaContentProvider already exists for manga '{}' (ID: {}) and provider '{}', skipping ingest",
manga.getTitle(),
manga.getId(),
contentProviderId);
return;
}
try { try {
var contentProvider = contentProviderService.find(contentProviderId); var contentProvider = contentProviderService.find(contentProviderId);
@ -63,10 +88,11 @@ public class MangaIngestService {
.build()); .build());
} catch (Exception e) { } catch (Exception e) {
log.error( log.error(
"Failed to ingest manga with mangaTitle '{}' from provider '{}'", "Failed to save manga content provider for manga '{}' from provider '{}'",
mangaTitle, mangaTitle,
contentProviderId, contentProviderId,
e); e);
return;
} }
log.info( log.info(

View File

@ -1,5 +1,6 @@
package com.magamochi.catalog.service; package com.magamochi.catalog.service;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull; import static java.util.Objects.nonNull;
import com.magamochi.catalog.model.entity.Manga; import com.magamochi.catalog.model.entity.Manga;
@ -9,6 +10,7 @@ import com.magamochi.catalog.queue.producer.MangaUpdateProducer;
import java.util.Optional; import java.util.Optional;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Log4j2 @Log4j2
@ -116,21 +118,21 @@ public class MangaResolutionService {
if (nonNull(aniListId)) { if (nonNull(aniListId)) {
var existingByAniList = mangaRepository.findByAniListId(aniListId); var existingByAniList = mangaRepository.findByAniListId(aniListId);
if (existingByAniList.isPresent()) { if (existingByAniList.isPresent()) {
return existingByAniList.get(); return mergeExternalIds(existingByAniList.get(), canonicalTitle, aniListId, malId);
} }
} }
if (nonNull(malId)) { if (nonNull(malId)) {
var existingByMalId = mangaRepository.findByMalId(malId); var existingByMalId = mangaRepository.findByMalId(malId);
if (existingByMalId.isPresent()) { if (existingByMalId.isPresent()) {
return existingByMalId.get(); return mergeExternalIds(existingByMalId.get(), canonicalTitle, aniListId, malId);
} }
} }
if (nonNull(canonicalTitle)) { if (nonNull(canonicalTitle)) {
var existingByTitle = mangaRepository.findByTitleIgnoreCase(canonicalTitle); var existingByTitle = mangaRepository.findByTitleIgnoreCase(canonicalTitle);
if (existingByTitle.isPresent()) { if (existingByTitle.isPresent()) {
return existingByTitle.get(); return mergeExternalIds(existingByTitle.get(), canonicalTitle, aniListId, malId);
} }
} }
@ -138,13 +140,75 @@ public class MangaResolutionService {
} }
private Manga createAndNotifyManga(String title, Long aniListId, Long malId) { private Manga createAndNotifyManga(String title, Long aniListId, Long malId) {
var manga = try {
mangaRepository.save( var manga =
Manga.builder().title(title).aniListId(aniListId).malId(malId).build()); mangaRepository.save(
Manga.builder().title(title).aniListId(aniListId).malId(malId).build());
mangaUpdateProducer.sendMangaUpdateCommand(new MangaUpdateCommand(manga.getId())); try {
mangaUpdateProducer.sendMangaUpdateCommand(new MangaUpdateCommand(manga.getId()));
} catch (Exception e) {
log.error("Failed to send manga update command for manga '{}' (ID: {})", title, manga.getId(), e);
}
return manga; return manga;
} catch (DataIntegrityViolationException e) {
log.warn(
"Race condition while creating manga '{}' (aniListId={}, malId={}), re-querying existing",
title,
aniListId,
malId);
if (nonNull(aniListId)) {
var existing = mangaRepository.findByAniListId(aniListId);
if (existing.isPresent()) {
return mergeExternalIds(existing.get(), title, aniListId, malId);
}
}
if (nonNull(malId)) {
var existing = mangaRepository.findByMalId(malId);
if (existing.isPresent()) {
return mergeExternalIds(existing.get(), title, aniListId, malId);
}
}
if (nonNull(title)) {
var existing = mangaRepository.findByTitleIgnoreCase(title);
if (existing.isPresent()) {
return mergeExternalIds(existing.get(), title, aniListId, malId);
}
}
throw e;
}
}
private Manga mergeExternalIds(Manga existing, String canonicalTitle, Long aniListId, Long malId) {
boolean updated = false;
if (isNull(existing.getAniListId()) && nonNull(aniListId)) {
existing.setAniListId(aniListId);
updated = true;
}
if (isNull(existing.getMalId()) && nonNull(malId)) {
existing.setMalId(malId);
updated = true;
}
if (isNull(existing.getTitle()) && nonNull(canonicalTitle)) {
existing.setTitle(canonicalTitle);
updated = true;
}
if (updated) {
log.info(
"Merged external IDs into manga '{}' (ID: {}): aniListId={}, malId={}",
existing.getTitle(),
existing.getId(),
existing.getAniListId(),
existing.getMalId());
return mangaRepository.save(existing);
}
return existing;
} }
private record ProviderResult(String title, Long externalId) {} private record ProviderResult(String title, Long externalId) {}