v2 download API and Admin UI fixes (#177)
* fix: small ui fixes * feat: #171 * fix: improvements to library scanning on admin UI * feat: v2 download API * fix: add download context cleanup * fix: lint
This commit is contained in:
@@ -1,9 +1,68 @@
|
||||
/*
|
||||
The download co-ordinator's job is to keep track of all the currently online clients.
|
||||
import prisma from "../db/database";
|
||||
import type { DropManifest } from "./manifest";
|
||||
|
||||
When a client signs on and registers itself as a peer
|
||||
const TIMEOUT = 1000 * 60 * 60 * 1; // 1 hour
|
||||
|
||||
*/
|
||||
class DownloadContextManager {
|
||||
private contexts: Map<
|
||||
string,
|
||||
{
|
||||
timeout: Date;
|
||||
manifest: DropManifest;
|
||||
versionName: string;
|
||||
libraryId: string;
|
||||
libraryPath: string;
|
||||
}
|
||||
> = new Map();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class, @typescript-eslint/no-unused-vars
|
||||
class DownloadCoordinator {}
|
||||
async createContext(game: string, versionName: string) {
|
||||
const version = await prisma.gameVersion.findUnique({
|
||||
where: {
|
||||
gameId_versionName: {
|
||||
gameId: game,
|
||||
versionName,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
game: {
|
||||
select: {
|
||||
libraryId: true,
|
||||
libraryPath: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!version) return undefined;
|
||||
|
||||
const contextId = crypto.randomUUID();
|
||||
this.contexts.set(contextId, {
|
||||
timeout: new Date(),
|
||||
manifest: JSON.parse(version.dropletManifest as string) as DropManifest,
|
||||
versionName,
|
||||
libraryId: version.game.libraryId!,
|
||||
libraryPath: version.game.libraryPath,
|
||||
});
|
||||
|
||||
return contextId;
|
||||
}
|
||||
|
||||
async fetchContext(contextId: string) {
|
||||
const context = this.contexts.get(contextId);
|
||||
if (!context) return undefined;
|
||||
context.timeout = new Date();
|
||||
this.contexts.set(contextId, context);
|
||||
return context;
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
for (const key of this.contexts.keys()) {
|
||||
const context = this.contexts.get(key)!;
|
||||
if (context.timeout.getDate() + TIMEOUT < Date.now()) {
|
||||
this.contexts.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const contextManager = new DownloadContextManager();
|
||||
export default contextManager;
|
||||
|
||||
@@ -5,7 +5,7 @@ export type DropChunk = {
|
||||
permissions: number;
|
||||
ids: string[];
|
||||
checksums: string[];
|
||||
lengths: string[];
|
||||
lengths: number[];
|
||||
};
|
||||
|
||||
export type DropManifest = {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { parsePlatform } from "../utils/parseplatform";
|
||||
import notificationSystem from "../notifications";
|
||||
import { GameNotFoundError, type LibraryProvider } from "./provider";
|
||||
import { logger } from "../logging";
|
||||
import type { GameModel } from "~/prisma/client/models";
|
||||
|
||||
class LibraryManager {
|
||||
private libraries: Map<string, LibraryProvider<unknown>> = new Map();
|
||||
@@ -37,24 +38,32 @@ class LibraryManager {
|
||||
return libraryWithMetadata;
|
||||
}
|
||||
|
||||
async fetchGamesByLibrary() {
|
||||
const results: { [key: string]: { [key: string]: GameModel } } = {};
|
||||
const games = await prisma.game.findMany({});
|
||||
for (const game of games) {
|
||||
const libraryId = game.libraryId!;
|
||||
const libraryPath = game.libraryPath!;
|
||||
|
||||
results[libraryId] ??= {};
|
||||
results[libraryId][libraryPath] = game;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
async fetchUnimportedGames() {
|
||||
const unimportedGames: { [key: string]: string[] } = {};
|
||||
const instanceGames = await this.fetchGamesByLibrary();
|
||||
|
||||
for (const [id, library] of this.libraries.entries()) {
|
||||
const games = await library.listGames();
|
||||
const validGames = await prisma.game.findMany({
|
||||
where: {
|
||||
libraryId: id,
|
||||
libraryPath: { in: games },
|
||||
},
|
||||
select: {
|
||||
libraryPath: true,
|
||||
},
|
||||
});
|
||||
const providerUnimportedGames = games.filter(
|
||||
(e) =>
|
||||
validGames.findIndex((v) => v.libraryPath == e) == -1 &&
|
||||
!(this.gameImportLocks.get(id) ?? []).includes(e),
|
||||
const providerGames = await library.listGames();
|
||||
const locks = this.gameImportLocks.get(id) ?? [];
|
||||
const providerUnimportedGames = providerGames.filter(
|
||||
(libraryPath) =>
|
||||
instanceGames[id] &&
|
||||
!instanceGames[id][libraryPath] &&
|
||||
!locks.includes(libraryPath),
|
||||
);
|
||||
unimportedGames[id] = providerUnimportedGames;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user