diff --git a/backend/pom.xml b/backend/pom.xml index 66bbe98..1fc2007 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -7,7 +7,7 @@ gameyfin de.grimsi - 1.1.4-RC1 + 1.2.0 gameyfin-backend diff --git a/backend/src/main/java/de/grimsi/gameyfin/dto/ImageDownloadResultDto.java b/backend/src/main/java/de/grimsi/gameyfin/dto/ImageDownloadResultDto.java new file mode 100644 index 0000000..7e0d103 --- /dev/null +++ b/backend/src/main/java/de/grimsi/gameyfin/dto/ImageDownloadResultDto.java @@ -0,0 +1,10 @@ +package de.grimsi.gameyfin.dto; + +import lombok.Data; + +@Data +public class ImageDownloadResultDto { + private int coverDownloads; + private int screenshotDownloads; + private int companyLogoDownloads; +} diff --git a/backend/src/main/java/de/grimsi/gameyfin/dto/LibraryScanResult.java b/backend/src/main/java/de/grimsi/gameyfin/dto/LibraryScanResult.java new file mode 100644 index 0000000..dd0fbe8 --- /dev/null +++ b/backend/src/main/java/de/grimsi/gameyfin/dto/LibraryScanResult.java @@ -0,0 +1,13 @@ +package de.grimsi.gameyfin.dto; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class LibraryScanResult { + private int newGames; + private int deletedGames; + private int newUnmappableFiles; + private int totalGames; +} diff --git a/backend/src/main/java/de/grimsi/gameyfin/dto/LibraryScanResultDto.java b/backend/src/main/java/de/grimsi/gameyfin/dto/LibraryScanResultDto.java new file mode 100644 index 0000000..48d7307 --- /dev/null +++ b/backend/src/main/java/de/grimsi/gameyfin/dto/LibraryScanResultDto.java @@ -0,0 +1,21 @@ +package de.grimsi.gameyfin.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LibraryScanResultDto { + private int newGames; + private int deletedGames; + private int newUnmappableFiles; + private int totalGames; + private int coverDownloads; + private int screenshotDownloads; + private int companyLogoDownloads; + private int scanDuration; +} diff --git a/backend/src/main/java/de/grimsi/gameyfin/rest/LibraryController.java b/backend/src/main/java/de/grimsi/gameyfin/rest/LibraryController.java index a0a14b0..0643817 100644 --- a/backend/src/main/java/de/grimsi/gameyfin/rest/LibraryController.java +++ b/backend/src/main/java/de/grimsi/gameyfin/rest/LibraryController.java @@ -1,5 +1,8 @@ package de.grimsi.gameyfin.rest; +import de.grimsi.gameyfin.dto.ImageDownloadResultDto; +import de.grimsi.gameyfin.dto.LibraryScanResult; +import de.grimsi.gameyfin.dto.LibraryScanResultDto; import de.grimsi.gameyfin.service.DownloadService; import de.grimsi.gameyfin.service.ImageService; import de.grimsi.gameyfin.service.LibraryService; @@ -7,6 +10,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.StopWatch; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -29,19 +33,45 @@ public class LibraryController { private final ImageService imageService; @GetMapping(value = "/scan", produces = MediaType.APPLICATION_JSON_VALUE) - public void scanLibrary(@RequestParam(value = "download_images", defaultValue = "true") boolean downloadImages) { - libraryService.scanGameLibrary(); + public LibraryScanResultDto scanLibrary(@RequestParam(value = "download_images", defaultValue = "true") boolean downloadImages) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); - if(downloadImages) downloadImages(); + LibraryScanResultDto lscDto = new LibraryScanResultDto(); + + LibraryScanResult lsc = libraryService.scanGameLibrary(); + lscDto.setNewGames(lsc.getNewGames()); + lscDto.setDeletedGames(lsc.getDeletedGames()); + lscDto.setNewUnmappableFiles(lsc.getNewUnmappableFiles()); + lscDto.setTotalGames(lsc.getTotalGames()); + + if(downloadImages) { + ImageDownloadResultDto idrDto = downloadImages(); + + lscDto.setCoverDownloads(idrDto.getCoverDownloads()); + lscDto.setScreenshotDownloads(idrDto.getScreenshotDownloads()); + lscDto.setCompanyLogoDownloads(idrDto.getCompanyLogoDownloads()); + } + + stopWatch.stop(); + lscDto.setScanDuration((int) stopWatch.getTotalTimeSeconds()); + + log.info("Library scan completed in {} seconds.", (int) stopWatch.getTotalTimeSeconds()); + + return lscDto; } @GetMapping(value = "/download-images") - public void downloadImages() { - imageService.downloadGameCoversFromIgdb(); - imageService.downloadGameScreenshotsFromIgdb(); - imageService.downloadCompanyLogosFromIgdb(); + public ImageDownloadResultDto downloadImages() { + ImageDownloadResultDto idrDto = new ImageDownloadResultDto(); + + idrDto.setCoverDownloads(imageService.downloadGameCoversFromIgdb()); + idrDto.setScreenshotDownloads(imageService.downloadGameScreenshotsFromIgdb()); + idrDto.setCompanyLogoDownloads(imageService.downloadCompanyLogosFromIgdb()); log.info("Downloading images completed."); + + return idrDto; } @GetMapping(value = "/files", produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/backend/src/main/java/de/grimsi/gameyfin/service/ImageService.java b/backend/src/main/java/de/grimsi/gameyfin/service/ImageService.java index 292f764..b492125 100644 --- a/backend/src/main/java/de/grimsi/gameyfin/service/ImageService.java +++ b/backend/src/main/java/de/grimsi/gameyfin/service/ImageService.java @@ -42,7 +42,7 @@ public class ImageService { igdbImageClient = webclientBuilder.baseUrl(IgdbApiProperties.IMAGES_BASE_URL).build(); } - public void downloadGameCoversFromIgdb() { + public int downloadGameCoversFromIgdb() { StopWatch stopWatch = new StopWatch(); log.info("Starting game cover download..."); @@ -57,9 +57,10 @@ public class ImageService { stopWatch.stop(); log.info("Downloaded {} covers in {} seconds.", downloadCount, (int) stopWatch.getTotalTimeSeconds()); + return downloadCount; } - public void downloadGameScreenshotsFromIgdb() { + public int downloadGameScreenshotsFromIgdb() { StopWatch stopWatch = new StopWatch(); log.info("Starting game screenshot download..."); @@ -74,9 +75,10 @@ public class ImageService { stopWatch.stop(); log.info("Downloaded {} screenshots in {} seconds.", downloadCount, (int) stopWatch.getTotalTimeSeconds()); + return downloadCount; } - public void downloadCompanyLogosFromIgdb() { + public int downloadCompanyLogosFromIgdb() { StopWatch stopWatch = new StopWatch(); log.info("Starting company logo download..."); @@ -93,6 +95,7 @@ public class ImageService { stopWatch.stop(); log.info("Downloaded {} company logos in {} seconds.", downloadCount, (int) stopWatch.getTotalTimeSeconds()); + return downloadCount; } private int saveImagesIntoCache(MultiValueMap entityToImageIds, String imageSize, String imageType, String entityType) { diff --git a/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java b/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java index 9930bc3..a171e0e 100644 --- a/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java +++ b/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java @@ -2,6 +2,7 @@ package de.grimsi.gameyfin.service; import com.igdb.proto.Igdb; import de.grimsi.gameyfin.dto.AutocompleteSuggestionDto; +import de.grimsi.gameyfin.dto.LibraryScanResult; import de.grimsi.gameyfin.entities.DetectedGame; import de.grimsi.gameyfin.entities.UnmappableFile; import de.grimsi.gameyfin.igdb.IgdbWrapper; @@ -57,7 +58,7 @@ public class LibraryService { return gamefiles; } - public void scanGameLibrary() { + public LibraryScanResult scanGameLibrary() { StopWatch stopWatch = new StopWatch(); log.info("Starting scan..."); @@ -120,6 +121,13 @@ public class LibraryService { log.info("Scan finished in {} seconds: Found {} new games, deleted {} games, could not map {} files/folders, {} games total.", (int) stopWatch.getTotalTimeSeconds(), newDetectedGames.size(), deletedGames.size() + deletedUnmappableFiles.size(), newUnmappedFilesCounter.get(), detectedGameRepository.count()); + + return LibraryScanResult.builder() + .newGames(newDetectedGames.size()) + .deletedGames(deletedGames.size() + deletedUnmappableFiles.size()) + .newUnmappableFiles(newUnmappedFilesCounter.get()) + .totalGames((int) detectedGameRepository.count()) + .build(); } public List getAutocompleteSuggestions(String searchTerm, int limit) { diff --git a/frontend/pom.xml b/frontend/pom.xml index 01da9ac..b7da3fa 100644 --- a/frontend/pom.xml +++ b/frontend/pom.xml @@ -5,7 +5,7 @@ gameyfin de.grimsi - 1.1.4-RC1 + 1.2.0 4.0.0 diff --git a/frontend/src/app/api/LibraryApi.ts b/frontend/src/app/api/LibraryApi.ts index fb8df25..9c3eda4 100644 --- a/frontend/src/app/api/LibraryApi.ts +++ b/frontend/src/app/api/LibraryApi.ts @@ -1,8 +1,11 @@ import {Observable} from "rxjs"; -import {HttpResponse} from "@angular/common/http"; +import {LibraryScanResultDto} from "../models/dtos/LibraryScanResultDto"; +import {ImageDownloadResultDto} from "../models/dtos/ImageDownloadResultDto"; export interface LibraryApi { - scanLibrary(): Observable>; - downloadImages(): Observable>; + scanLibrary(): Observable; + + downloadImages(): Observable; + getFiles(): Observable; } diff --git a/frontend/src/app/components/game-detail-view/game-detail-view.component.html b/frontend/src/app/components/game-detail-view/game-detail-view.component.html index e564261..8d2d39b 100644 --- a/frontend/src/app/components/game-detail-view/game-detail-view.component.html +++ b/frontend/src/app/components/game-detail-view/game-detail-view.component.html @@ -17,13 +17,11 @@

{{game.summary}}

-
+

Developed by

-
- {{company.name}} - {{company.name}} +
+ {{company.name}}
diff --git a/frontend/src/app/components/game-detail-view/game-detail-view.component.ts b/frontend/src/app/components/game-detail-view/game-detail-view.component.ts index 8366c89..444f1b0 100644 --- a/frontend/src/app/components/game-detail-view/game-detail-view.component.ts +++ b/frontend/src/app/components/game-detail-view/game-detail-view.component.ts @@ -2,6 +2,7 @@ import {Component, HostListener} from '@angular/core'; import {ActivatedRoute, Params, Router} from "@angular/router"; import {DetectedGameDto} from "../../models/dtos/DetectedGameDto"; import {GamesService} from "../../services/games.service"; +import {CompanyDto} from "../../models/dtos/CompanyDto"; @Component({ selector: 'app-game-detail-view', @@ -12,13 +13,20 @@ export class GameDetailViewComponent { game!: DetectedGameDto; + companiesWithLogo: CompanyDto[]= []; + gridColumnCount: number; constructor(private route: ActivatedRoute, private router: Router, private gamesService: GamesService) { this.gamesService.getGame(this.route.snapshot.params['slug']).subscribe({ - next: game => this.game = game, + next: game => { + this.game = game; + if(game.companies !== undefined) { + this.companiesWithLogo = game.companies.filter(c => c.logoId !== undefined && c.logoId.length > 0); + } + }, error: error => { if (error.status === 404) { this.router.navigate(['/library']); diff --git a/frontend/src/app/components/header/header.component.ts b/frontend/src/app/components/header/header.component.ts index 71b017a..eb3e802 100644 --- a/frontend/src/app/components/header/header.component.ts +++ b/frontend/src/app/components/header/header.component.ts @@ -1,7 +1,6 @@ import {Component} from '@angular/core'; import {LibraryService} from "../../services/library.service"; import {MatSnackBar} from '@angular/material/snack-bar'; -import {timeInterval} from "rxjs"; import {Router} from "@angular/router"; import {GamesService} from "../../services/games.service"; import {ThemingService} from "../../services/theming.service"; @@ -26,11 +25,27 @@ export class HeaderComponent { } scanLibrary(): void { - this.libraryService.scanLibrary().pipe(timeInterval()).subscribe({ - next: value => { + this.libraryService.scanLibrary().subscribe({ + next: result => { // Refresh the current page "angular style" - this.router.navigate([this.router.url]).then(() => - this.snackBar.open(`Library scan completed in ${Math.trunc(value.interval / 1000)} seconds.`, undefined, {duration: 5000}) + this.router.navigate([this.router.url]).then(() => { + const snackBarDuration: number = 10000; + + let snackbarContent: string = 'Library scan completed in ' + result.scanDuration + ' seconds:\n' + + '- ' + result.newGames + ' new games\n' + + '- ' + result.deletedGames + ' games removed\n' + + '- ' + result.newUnmappableFiles + ' files/folders could not be mapped\n' + + '- ' + result.totalGames + ' games currently in your library'; + + if (result.companyLogoDownloads !== undefined && result.coverDownloads !== undefined && result.screenshotDownloads !== undefined) { + snackbarContent = snackbarContent.concat('\n' + + '- ' + result.coverDownloads + ' covers downloaded\n' + + '- ' + result.screenshotDownloads + ' screenshots downloaded\n' + + '- ' + result.companyLogoDownloads + ' company logos downloaded'); + } + + this.snackBar.open(snackbarContent, undefined, {duration: snackBarDuration}); + } ) }, error: error => this.snackBar.open(`Error while scanning library: ${error.error.message}`, undefined, {duration: 5000}) diff --git a/frontend/src/app/models/dtos/ImageDownloadResultDto.ts b/frontend/src/app/models/dtos/ImageDownloadResultDto.ts new file mode 100644 index 0000000..a7048c0 --- /dev/null +++ b/frontend/src/app/models/dtos/ImageDownloadResultDto.ts @@ -0,0 +1,5 @@ +export class ImageDownloadResultDto { + coverDownloads!: number; + screenshotDownloads!: number; + companyLogoDownloads!: number; +} diff --git a/frontend/src/app/models/dtos/LibraryScanResultDto.ts b/frontend/src/app/models/dtos/LibraryScanResultDto.ts new file mode 100644 index 0000000..f34977a --- /dev/null +++ b/frontend/src/app/models/dtos/LibraryScanResultDto.ts @@ -0,0 +1,10 @@ +export class LibraryScanResultDto { + newGames!: number; + deletedGames!: number; + newUnmappableFiles!: number; + totalGames!: number; + coverDownloads!: number; + screenshotDownloads!: number; + companyLogoDownloads!: number; + scanDuration!: number; +} diff --git a/frontend/src/app/services/library.service.ts b/frontend/src/app/services/library.service.ts index e0cd2b5..3fd7d82 100644 --- a/frontend/src/app/services/library.service.ts +++ b/frontend/src/app/services/library.service.ts @@ -1,7 +1,9 @@ import {Injectable} from '@angular/core'; -import {HttpClient, HttpResponse} from "@angular/common/http"; +import {HttpClient} from "@angular/common/http"; import {Observable} from "rxjs"; import {LibraryApi} from "../api/LibraryApi"; +import {LibraryScanResultDto} from "../models/dtos/LibraryScanResultDto"; +import {ImageDownloadResultDto} from "../models/dtos/ImageDownloadResultDto"; @Injectable({ providedIn: 'root' @@ -13,12 +15,12 @@ export class LibraryService implements LibraryApi { constructor(private http: HttpClient) { } - scanLibrary(): Observable> { - return this.http.get>(`${this.apiPath}/scan`); + scanLibrary(): Observable { + return this.http.get(`${this.apiPath}/scan`); } - downloadImages(): Observable> { - return this.http.get>(`${this.apiPath}/download-images`); + downloadImages(): Observable { + return this.http.get(`${this.apiPath}/download-images`); } getFiles(): Observable { diff --git a/pom.xml b/pom.xml index 1959753..e34baa5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ de.grimsi gameyfin - 1.1.4-RC1 + 1.2.0 gameyfin gameyfin