mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-16 16:20:04 +00:00
Added game file size
This commit is contained in:
@@ -80,6 +80,9 @@ public class DetectedGame {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private long diskSize;
|
||||||
|
|
||||||
@Column(columnDefinition = "boolean default false")
|
@Column(columnDefinition = "boolean default false")
|
||||||
private boolean confirmedMatch;
|
private boolean confirmedMatch;
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,21 @@ package de.grimsi.gameyfin.mapper;
|
|||||||
import com.igdb.proto.Igdb;
|
import com.igdb.proto.Igdb;
|
||||||
import de.grimsi.gameyfin.dto.GameOverviewDto;
|
import de.grimsi.gameyfin.dto.GameOverviewDto;
|
||||||
import de.grimsi.gameyfin.entities.DetectedGame;
|
import de.grimsi.gameyfin.entities.DetectedGame;
|
||||||
|
import de.grimsi.gameyfin.service.LibraryService;
|
||||||
import de.grimsi.gameyfin.util.ProtobufUtil;
|
import de.grimsi.gameyfin.util.ProtobufUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class GameMapper {
|
public class GameMapper {
|
||||||
|
|
||||||
public static DetectedGame toDetectedGame(Igdb.Game g, Path path) {
|
public static DetectedGame toDetectedGame(Igdb.Game g, Path path) {
|
||||||
@@ -37,6 +47,7 @@ public class GameMapper {
|
|||||||
.themes(ThemeMapper.toThemes(g.getThemesList()))
|
.themes(ThemeMapper.toThemes(g.getThemesList()))
|
||||||
.playerPerspectives(PlayerPerspectiveMapper.toPlayerPerspectives(g.getPlayerPerspectivesList()))
|
.playerPerspectives(PlayerPerspectiveMapper.toPlayerPerspectives(g.getPlayerPerspectivesList()))
|
||||||
.path(path.toString())
|
.path(path.toString())
|
||||||
|
.diskSize(calculateDiskSize(path))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,4 +74,16 @@ public class GameMapper {
|
|||||||
private static int getMaxPlayers(List<Igdb.MultiplayerMode> modes) {
|
private static int getMaxPlayers(List<Igdb.MultiplayerMode> modes) {
|
||||||
return modes.stream().mapToInt(Igdb.MultiplayerMode::getOnlinecoopmax).max().orElse(0);
|
return modes.stream().mapToInt(Igdb.MultiplayerMode::getOnlinecoopmax).max().orElse(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long calculateDiskSize(Path path) {
|
||||||
|
try(Stream<Path> pathStream = Files.walk(path)) {
|
||||||
|
return pathStream
|
||||||
|
.filter(p -> p.toFile().isFile())
|
||||||
|
.mapToLong(p -> p.toFile().length())
|
||||||
|
.sum();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Unable to calculate size for path '{}'.", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.springframework.core.io.ByteArrayResource;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||||
|
import org.springframework.data.repository.core.RepositoryCreationException;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
@@ -157,6 +158,7 @@ public class DownloadService {
|
|||||||
Files.copy(path, outputStream);
|
Files.copy(path, outputStream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error while downloading file:", e);
|
log.error("Error while downloading file:", e);
|
||||||
|
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Could not load file '%s'.".formatted(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {MatGridListModule} from "@angular/material/grid-list";
|
|||||||
import {GameScreenshotComponent} from './components/game-screenshot/game-screenshot.component';
|
import {GameScreenshotComponent} from './components/game-screenshot/game-screenshot.component';
|
||||||
import {YouTubePlayerModule} from "@angular/youtube-player";
|
import {YouTubePlayerModule} from "@angular/youtube-player";
|
||||||
import { GameVideoComponent } from './components/game-video/game-video.component';
|
import { GameVideoComponent } from './components/game-video/game-video.component';
|
||||||
|
import {MatChipsModule} from "@angular/material/chips";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -52,34 +53,35 @@ import { GameVideoComponent } from './components/game-video/game-video.component
|
|||||||
GameScreenshotComponent,
|
GameScreenshotComponent,
|
||||||
GameVideoComponent
|
GameVideoComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatTabsModule,
|
MatTabsModule,
|
||||||
MatToolbarModule,
|
MatToolbarModule,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
FlexModule,
|
FlexModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
MatTableModule,
|
MatTableModule,
|
||||||
MatPaginatorModule,
|
MatPaginatorModule,
|
||||||
MatSortModule,
|
MatSortModule,
|
||||||
MatSnackBarModule,
|
MatSnackBarModule,
|
||||||
MatGridListModule,
|
MatGridListModule,
|
||||||
FlexLayoutModule,
|
FlexLayoutModule,
|
||||||
GridModule,
|
GridModule,
|
||||||
YouTubePlayerModule
|
YouTubePlayerModule,
|
||||||
],
|
MatChipsModule
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
|||||||
@@ -1,15 +1,37 @@
|
|||||||
<div fxLayout="row" fxLayoutAlign="center" style="margin-top: 16px;">
|
<div fxLayout="row" fxLayoutAlign="center" style="margin-top: 16px;">
|
||||||
<div fxLayout="column" fxFlex="0 1 70" fxLayoutGap="16px" fxFlex.lt-xl="95">
|
<div fxLayout="column" fxFlex="0 1 70" fxLayoutGap="16px" fxFlex.lt-xl="95">
|
||||||
<div fxLayout="row" fxLayoutGap="16px">
|
<div fxLayout="row" fxLayoutGap="16px">
|
||||||
|
|
||||||
<img src="v1/images/{{game.coverId}}" alt="Game cover">
|
<img src="v1/images/{{game.coverId}}" alt="Game cover">
|
||||||
<div fxFlex="30" fxLayout="column" id="game-details">
|
|
||||||
<h2>{{game.title}}</h2>
|
<div fxFlex="40" fxLayout="column" id="game-details">
|
||||||
|
<h1>{{game.title}}</h1>
|
||||||
<p id="game-summary">{{game.summary}}</p>
|
<p id="game-summary">{{game.summary}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div fxLayout="column" fxFlex>
|
|
||||||
<button mat-raised-button (click)="downloadGame()">
|
<div fxFlex><!-- Spacer --></div>
|
||||||
Download
|
|
||||||
|
<div fxLayout="column" fxFlex="40" fxLayoutGap="16px">
|
||||||
|
|
||||||
|
<button mat-raised-button color="primary" (click)="downloadGame()">
|
||||||
|
Download ({{bytesAsHumanReadableString(game.diskSize)}})
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div fxLayout="column" fxLayoutGap="24px">
|
||||||
|
<div *ngIf="game.genres !== undefined && game.genres.length > 0">
|
||||||
|
<h2>Genres</h2>
|
||||||
|
<mat-chip-list>
|
||||||
|
<mat-chip *ngFor="let genre of game.genres">{{genre.name}}</mat-chip>
|
||||||
|
</mat-chip-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="game.themes !== undefined && game.themes.length > 0">
|
||||||
|
<h2>Themes</h2>
|
||||||
|
<mat-chip-list>
|
||||||
|
<mat-chip *ngFor="let theme of game.themes">{{theme.name}}</mat-chip>
|
||||||
|
</mat-chip-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div fxLayout="column" fxLayoutGap="16px">
|
<div fxLayout="column" fxLayoutGap="16px">
|
||||||
|
|||||||
@@ -34,8 +34,28 @@ export class GameDetailViewComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadGame(): void {
|
public downloadGame(): void {
|
||||||
this.gamesService.downloadGame(this.game.slug);
|
this.gamesService.downloadGame(this.game.slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bytesAsHumanReadableString(bytes: number): string {
|
||||||
|
const thresh = 1024;
|
||||||
|
|
||||||
|
if (Math.abs(bytes) < thresh) {
|
||||||
|
return bytes + ' B';
|
||||||
|
}
|
||||||
|
|
||||||
|
const units = ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
|
const dp = 1;
|
||||||
|
let u = -1;
|
||||||
|
const r = 10**dp;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bytes /= thresh;
|
||||||
|
++u;
|
||||||
|
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
|
||||||
|
|
||||||
|
return bytes.toFixed(dp) + ' ' + units[u];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,6 @@ export class DetectedGameDto {
|
|||||||
playerPerspectives?: PlayerPerspectiveDto[];
|
playerPerspectives?: PlayerPerspectiveDto[];
|
||||||
|
|
||||||
path!: string;
|
path!: string;
|
||||||
isFolder!: boolean;
|
diskSize!: number;
|
||||||
confirmedMatch!: boolean;
|
confirmedMatch!: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export class GamesService implements GamesApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
downloadGame(slug: String): void {
|
downloadGame(slug: String): void {
|
||||||
window.open(`v1/${this.apiPath}/game/${slug}/download`, '_top');
|
window.open(`v1${this.apiPath}/game/${slug}/download`, '_top');
|
||||||
}
|
}
|
||||||
|
|
||||||
getGameOverviews(): Observable<GameOverviewDto[]> {
|
getGameOverviews(): Observable<GameOverviewDto[]> {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
@import "~@angular/material/theming";
|
@import "@angular/material/theming";
|
||||||
|
|
||||||
@include mat.core();
|
@include mat.core();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user