Skip to content
This repository was archived by the owner on May 21, 2026. It is now read-only.

Commit 6aa038c

Browse files
Refactor media search & persistence logic
Unify and improve media search/persistence behavior and fix external reference strategy. - GamesAndVNsApiImpl: switch to Game proto responses, adjust requested fields (name, summary, cover, artworks, genres, alternative names), add genre mapping, and simplify VN filter logic. - ExternalReferenceModel: change ID generation to GenerationType.IDENTITY. - MediaServiceImpl: extract shared logic into findGenericMediaByIdOrName and updateIfStale; centralize paging, fallback to external search when local results are missing, and refresh stale records before returning. - Tests: update MediaServiceImplTest (mocks, setup, and new unit tests for external fallback and stale-refresh behavior) and add MediaServicePersistenceIntegrationTest to verify persistence and reuse of fetched media. These changes reduce duplicated code, ensure stale data is refreshed automatically, fix ID generation for external references, and add coverage for the new behaviors.
1 parent 1e798b9 commit 6aa038c

5 files changed

Lines changed: 362 additions & 68 deletions

File tree

src/main/java/com/espacogeek/geek/data/api/impl/GamesAndVNsApiImpl.java

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131

3232
import jakarta.annotation.PostConstruct;
3333
import proto.Game;
34-
import proto.Search;
3534

3635
import static com.espacogeek.geek.data.api.MediaApi.ApiKey.*;
3736

@@ -128,29 +127,39 @@ public MediaModel getDetails(Integer id) {
128127
@Override
129128
@Retryable(maxAttempts = 2, backoff = @Backoff(delay = 2000), retryFor = com.espacogeek.geek.exception.RequestException.class)
130129
public List<MediaModel> doSearch(String search, MediaCategoryModel mediaCategoryModel) {
131-
var apicalypse = new APICalypse().search(search).fields("game.age_ratings, game.aggregated_rating, game.alternative_names.name, game.artworks.image_id, game.cover.image_id, game.name").where("game.genres " + (mediaCategoryModel.getId() == MediaDataController.MediaType.GAME.getId() ? "!=" : "=") + " [" + VN_ID_IGDB + "]").limit(10);
130+
String vnFilterOperator = mediaCategoryModel.getId() == MediaDataController.MediaType.GAME.getId() ? "!=" : "=";
131+
var apicalypse = new APICalypse()
132+
.search(search)
133+
.fields("name, summary, alternative_names.name, artworks.image_id, cover.image_id, genres.name")
134+
.where("genres " + vnFilterOperator + " [" + VN_ID_IGDB + "]")
135+
.limit(10);
132136
List<MediaModel> medias = new ArrayList<>();
133137

134138
try {
135-
var searchGames = ProtoRequestKt.search(wrapper, apicalypse);
139+
var searchGames = ProtoRequestKt.games(wrapper, apicalypse);
136140

137-
for (Search result : searchGames) {
138-
if ((long) result.getGame().getId() != (long) 0L) {
141+
for (Game result : searchGames) {
142+
if ((long) result.getId() != (long) 0L) {
139143
var media = new MediaModel();
140-
var reference = new ExternalReferenceModel(null, String.valueOf(result.getGame().getId()), media, typeReference);
144+
var reference = new ExternalReferenceModel(null, String.valueOf(result.getId()), media, typeReference);
141145

142-
media.setName(result.getGame().getName());
146+
media.setName(result.getName());
147+
media.setAbout(result.getSummary());
143148
media.setCover(
144-
!result.getGame().getCover().getImageId().isEmpty()
145-
? ImageBuilderKt.imageBuilder(result.getGame().getCover().getImageId(),
149+
!result.getCover().getImageId().isEmpty()
150+
? ImageBuilderKt.imageBuilder(result.getCover().getImageId(),
146151
ImageSize.COVER_BIG, ImageType.PNG)
147152
: null);
148-
media.setBanner(result.getGame().getArtworksList().isEmpty() ? null
149-
: ImageBuilderKt.imageBuilder(result.getGame().getArtworksList().getFirst().getImageId(),
153+
media.setBanner(result.getArtworksList().isEmpty() ? null
154+
: ImageBuilderKt.imageBuilder(result.getArtworksList().getFirst().getImageId(),
150155
ImageSize.SCREENSHOT_HUGE, ImageType.PNG));
151156

157+
List<String> genresName = new ArrayList<>();
158+
result.getGenresList().forEach(genre -> genresName.add(genre.getName()));
159+
media.setGenre(new java.util.LinkedHashSet<>(genreService.findAllByNames(genresName)));
160+
152161
var alternativeTitles = new ArrayList<AlternativeTitleModel>();
153-
for (proto.AlternativeName title : result.getGame().getAlternativeNamesList()) {
162+
for (proto.AlternativeName title : result.getAlternativeNamesList()) {
154163
if (!title.getName().isEmpty()) alternativeTitles.add(new AlternativeTitleModel(null, title.getName(), media));
155164
}
156165
media.setAlternativeTitles(new java.util.LinkedHashSet<>(alternativeTitles));

src/main/java/com/espacogeek/geek/models/ExternalReferenceModel.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
@AllArgsConstructor
2020
public class ExternalReferenceModel implements Serializable {
2121
@Id
22-
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "gen_externals_references")
23-
@SequenceGenerator(name = "gen_externals_references", sequenceName = "seq_externals_references", allocationSize = 50)
22+
@GeneratedValue(strategy = GenerationType.IDENTITY)
2423
@Column(name = "id_external_reference")
2524
private Integer id;
2625

src/main/java/com/espacogeek/geek/services/impl/MediaServiceImpl.java

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -146,32 +146,13 @@ public MediaPage findSerieByIdOrName(Integer id, String name, Pageable pageable)
146146
@SuppressWarnings("unchecked")
147147
@Override
148148
public MediaPage findGameByIdOrName(Integer id, String name, Pageable pageable) {
149-
Page<MediaModel> results;
150-
if (id != null) {
151-
List<MediaModel> medias = new ArrayList<MediaModel>();
152-
this.mediaRepository.findById(id).ifPresent(media -> medias.add((MediaModel) media));
153-
Pageable safePageable = pageable != null ? pageable : Pageable.unpaged();
154-
results = new PageImpl<>(medias, safePageable, medias.size());
155-
} else {
156-
results = this.mediaRepository.findMediaByNameOrAlternativeTitleAndMediaCategory(name, name, mediaCategoryService.findById(MediaDataController.MediaType.GAME.getId()).get().getId(), pageable);
157-
}
158-
return mountMediaPage(results);
149+
return findGenericMediaByIdOrName(id, name, pageable, MediaDataController.MediaType.GAME);
159150
}
160151

161152
@Override
162153
@SuppressWarnings("unchecked")
163154
public MediaPage findVisualNovelByIdOrName(Integer id, String name, Pageable pageable) {
164-
Page<MediaModel> results;
165-
166-
if(id != null) {
167-
List<MediaModel> medias = new ArrayList<MediaModel>();
168-
this.mediaRepository.findById(id).ifPresent(media -> medias.add((MediaModel) media));
169-
Pageable safePageable = pageable != null ? pageable : Pageable.unpaged();
170-
results = new PageImpl<>(medias, safePageable, medias.size());
171-
} else {
172-
results = this.mediaRepository.findMediaByNameOrAlternativeTitleAndMediaCategory(name, name, mediaCategoryService.findById(MediaDataController.MediaType.VN.getId()).get().getId(), pageable);
173-
}
174-
return mountMediaPage(results);
155+
return findGenericMediaByIdOrName(id, name, pageable, MediaDataController.MediaType.VN);
175156
}
176157

177158
/**
@@ -322,6 +303,59 @@ private MediaModel update(MediaModel media) {
322303

323304
}
324305

306+
private MediaPage findGenericMediaByIdOrName(Integer id, String name, Pageable pageable, MediaDataController.MediaType mediaType) {
307+
Pageable safePageable = pageable != null ? pageable : Pageable.unpaged();
308+
var mediaCategory = mediaCategoryService.findById(mediaType.getId()).orElseThrow();
309+
Page<MediaModel> results;
310+
311+
if (id != null) {
312+
List<MediaModel> medias = new ArrayList<>();
313+
this.mediaRepository.findById(id)
314+
.map(media -> updateIfStale((MediaModel) media))
315+
.ifPresent(medias::add);
316+
results = new PageImpl<>(medias, safePageable, medias.size());
317+
return mountMediaPage(results);
318+
}
319+
320+
results = this.mediaRepository.findMediaByNameOrAlternativeTitleAndMediaCategory(name, name, mediaCategory.getId(), safePageable);
321+
322+
if (results.hasContent()) {
323+
List<MediaModel> refreshed = results.getContent().stream()
324+
.map(this::updateIfStale)
325+
.toList();
326+
return mountMediaPage(new PageImpl<>(refreshed, safePageable, results.getTotalElements()));
327+
}
328+
329+
if (name == null || name.isBlank()) {
330+
return mountMediaPage(Page.empty(safePageable));
331+
}
332+
333+
var igdbTypeReference = typeReferenceService.findById(MediaDataController.ExternalReferenceType.IGDB.getId()).orElseThrow();
334+
List<MediaModel> fetched = genericMediaDataController.searchMedia(name, gamesAndVNsAPI, igdbTypeReference, mediaCategory);
335+
336+
if (fetched.isEmpty()) {
337+
return mountMediaPage(Page.empty(safePageable));
338+
}
339+
340+
int fromIndex = Math.min((int) safePageable.getOffset(), fetched.size());
341+
int toIndex = safePageable.isPaged()
342+
? Math.min(fromIndex + safePageable.getPageSize(), fetched.size())
343+
: fetched.size();
344+
List<MediaModel> pagedContent = fetched.subList(fromIndex, toIndex);
345+
346+
return mountMediaPage(new PageImpl<>(pagedContent, safePageable, fetched.size()));
347+
}
348+
349+
private MediaModel updateIfStale(MediaModel media) {
350+
if (media == null) {
351+
return null;
352+
}
353+
354+
return MediaUtils.updateMediaWhenLastTimeUpdateMoreThanOneDay(media)
355+
? update(media)
356+
: media;
357+
}
358+
325359
private MediaPage mountMediaPage(Page<MediaModel> medias) {
326360
MediaPage response = new MediaPage();
327361

0 commit comments

Comments
 (0)