Merge branch 'feat/new-catalogue' into feat/achievements-points

# Conflicts:
#	src/preload/index.ts
#	src/renderer/src/app.tsx
#	src/renderer/src/features/index.ts
#	src/renderer/src/store.ts
#	yarn.lock
This commit is contained in:
Zamitto
2024-12-22 21:22:52 -03:00
54 changed files with 1129 additions and 926 deletions

View File

@@ -1,10 +1,8 @@
import { DataSource } from "typeorm";
import {
DownloadQueue,
DownloadSource,
Game,
GameShopCache,
Repack,
UserPreferences,
UserAuth,
GameAchievement,
@@ -17,12 +15,10 @@ export const dataSource = new DataSource({
type: "better-sqlite3",
entities: [
Game,
Repack,
UserAuth,
UserPreferences,
UserSubscription,
GameShopCache,
DownloadSource,
DownloadQueue,
GameAchievement,
],

View File

@@ -1,41 +0,0 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
OneToMany,
} from "typeorm";
import type { Repack } from "./repack.entity";
import { DownloadSourceStatus } from "@shared";
@Entity("download_source")
export class DownloadSource {
@PrimaryGeneratedColumn()
id: number;
@Column("text", { nullable: true, unique: true })
url: string;
@Column("text")
name: string;
@Column("text", { nullable: true })
etag: string | null;
@Column("int", { default: 0 })
downloadCount: number;
@Column("text", { default: DownloadSourceStatus.UpToDate })
status: DownloadSourceStatus;
@OneToMany("Repack", "downloadSource", { cascade: true })
repacks: Repack[];
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@@ -5,9 +5,7 @@ import {
CreateDateColumn,
UpdateDateColumn,
OneToOne,
JoinColumn,
} from "typeorm";
import { Repack } from "./repack.entity";
import type { GameShop, GameStatus } from "@types";
import { Downloader } from "@shared";
@@ -72,13 +70,6 @@ export class Game {
@Column("text", { nullable: true })
uri: string | null;
/**
* @deprecated
*/
@OneToOne("Repack", "game", { nullable: true })
@JoinColumn()
repack: Repack;
@OneToOne("DownloadQueue", "game")
downloadQueue: DownloadQueue;

View File

@@ -1,10 +1,8 @@
export * from "./game.entity";
export * from "./repack.entity";
export * from "./user-auth.entity";
export * from "./user-preferences.entity";
export * from "./user-subscription.entity";
export * from "./game-shop-cache.entity";
export * from "./game.entity";
export * from "./game-achievements.entity";
export * from "./download-source.entity";
export * from "./download-queue.entity";

View File

@@ -1,45 +0,0 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
} from "typeorm";
import { DownloadSource } from "./download-source.entity";
@Entity("repack")
export class Repack {
@PrimaryGeneratedColumn()
id: number;
@Column("text", { unique: true })
title: string;
/**
* @deprecated Use uris instead
*/
@Column("text", { unique: true })
magnet: string;
@Column("text")
repacker: string;
@Column("text")
fileSize: string;
@Column("datetime")
uploadDate: Date | string;
@ManyToOne(() => DownloadSource, { nullable: true, onDelete: "CASCADE" })
downloadSource: DownloadSource;
@Column("text", { default: "[]" })
uris: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@@ -1,9 +1,6 @@
import type { GameShop } from "@types";
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { CatalogueCategory, steamUrlBuilder } from "@shared";
import { steamGamesWorker } from "@main/workers";
import { CatalogueCategory } from "@shared";
const getCatalogue = async (
_event: Electron.IpcMainInvokeEvent,
@@ -14,26 +11,11 @@ const getCatalogue = async (
skip: "0",
});
const response = await HydraApi.get<{ objectId: string; shop: GameShop }[]>(
return HydraApi.get(
`/catalogue/${category}?${params.toString()}`,
{},
{ needsAuth: false }
);
return Promise.all(
response.map(async (game) => {
const steamGame = await steamGamesWorker.run(Number(game.objectId), {
name: "getById",
});
return {
title: steamGame.name,
shop: game.shop,
cover: steamUrlBuilder.library(game.objectId),
objectId: game.objectId,
};
})
);
};
registerEvent("getCatalogue", getCatalogue);

View File

@@ -0,0 +1,10 @@
import { HydraApi } from "@main/services";
import { registerEvent } from "../register-event";
const getDevelopers = async (_event: Electron.IpcMainInvokeEvent) => {
return HydraApi.get<string[]>(`/catalogue/developers`, null, {
needsAuth: false,
});
};
registerEvent("getDevelopers", getDevelopers);

View File

@@ -1,29 +0,0 @@
import type { CatalogueEntry } from "@types";
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { steamUrlBuilder } from "@shared";
const getGames = async (
_event: Electron.IpcMainInvokeEvent,
take = 12,
skip = 0
): Promise<CatalogueEntry[]> => {
const searchParams = new URLSearchParams({
take: take.toString(),
skip: skip.toString(),
});
const games = await HydraApi.get<CatalogueEntry[]>(
`/games/catalogue?${searchParams.toString()}`,
undefined,
{ needsAuth: false }
);
return games.map((game) => ({
...game,
cover: steamUrlBuilder.library(game.objectId),
}));
};
registerEvent("getGames", getGames);

View File

@@ -0,0 +1,10 @@
import { HydraApi } from "@main/services";
import { registerEvent } from "../register-event";
const getPublishers = async (_event: Electron.IpcMainInvokeEvent) => {
return HydraApi.get<string[]>(`/catalogue/publishers`, null, {
needsAuth: false,
});
};
registerEvent("getPublishers", getPublishers);

View File

@@ -1,23 +1,16 @@
import type { CatalogueSearchPayload } from "@types";
import { registerEvent } from "../register-event";
import { convertSteamGameToCatalogueEntry } from "../helpers/search-games";
import type { CatalogueEntry } from "@types";
import { HydraApi } from "@main/services";
const searchGamesEvent = async (
const searchGames = async (
_event: Electron.IpcMainInvokeEvent,
query: string
): Promise<CatalogueEntry[]> => {
const games = await HydraApi.get<
{ objectId: string; title: string; shop: string }[]
>("/games/search", { title: query, take: 12, skip: 0 }, { needsAuth: false });
return games.map((game) => {
return convertSteamGameToCatalogueEntry({
id: Number(game.objectId),
name: game.title,
clientIcon: null,
});
});
payload: CatalogueSearchPayload
) => {
return HydraApi.post(
"/catalogue/search",
{ ...payload, take: 24, skip: 0 },
{ needsAuth: false }
);
};
registerEvent("searchGames", searchGamesEvent);
registerEvent("searchGames", searchGames);

View File

@@ -1,9 +0,0 @@
import { registerEvent } from "../register-event";
import { knexClient } from "@main/knex-client";
const deleteDownloadSource = async (
_event: Electron.IpcMainInvokeEvent,
id: number
) => knexClient("download_source").where({ id }).delete();
registerEvent("deleteDownloadSource", deleteDownloadSource);

View File

@@ -1,7 +0,0 @@
import { registerEvent } from "../register-event";
import { knexClient } from "@main/knex-client";
const getDownloadSources = async (_event: Electron.IpcMainInvokeEvent) =>
knexClient.select("*").from("download_source");
registerEvent("getDownloadSources", getDownloadSources);

View File

@@ -0,0 +1,17 @@
import { HydraApi } from "@main/services";
import { registerEvent } from "../register-event";
const putDownloadSource = async (
_event: Electron.IpcMainInvokeEvent,
objectIds: string[]
) => {
return HydraApi.put<{ fingerprint: string }>(
"/download-sources",
{
objectIds,
},
{ needsAuth: false }
);
};
registerEvent("putDownloadSource", putDownloadSource);

View File

@@ -1,31 +0,0 @@
import type { GameShop, CatalogueEntry, SteamGame } from "@types";
import { steamGamesWorker } from "@main/workers";
import { steamUrlBuilder } from "@shared";
export interface SearchGamesArgs {
query?: string;
take?: number;
skip?: number;
}
export const convertSteamGameToCatalogueEntry = (
game: SteamGame
): CatalogueEntry => ({
objectId: String(game.id),
title: game.name,
shop: "steam" as GameShop,
cover: steamUrlBuilder.library(String(game.id)),
});
export const getSteamGameById = async (
objectId: string
): Promise<CatalogueEntry | null> => {
const steamGame = await steamGamesWorker.run(Number(objectId), {
name: "getById",
});
if (!steamGame) return null;
return convertSteamGameToCatalogueEntry(steamGame);
};

View File

@@ -3,12 +3,13 @@ import { ipcMain } from "electron";
import "./catalogue/get-catalogue";
import "./catalogue/get-game-shop-details";
import "./catalogue/get-games";
import "./catalogue/get-how-long-to-beat";
import "./catalogue/get-random-game";
import "./catalogue/search-games";
import "./catalogue/get-game-stats";
import "./catalogue/get-trending-games";
import "./catalogue/get-publishers";
import "./catalogue/get-developers";
import "./hardware/get-disk-free-space";
import "./library/add-game-to-library";
import "./library/create-game-shortcut";
@@ -40,8 +41,7 @@ import "./user-preferences/auto-launch";
import "./autoupdater/check-for-updates";
import "./autoupdater/restart-and-install-update";
import "./user-preferences/authenticate-real-debrid";
import "./download-sources/delete-download-source";
import "./download-sources/get-download-sources";
import "./download-sources/put-download-source";
import "./auth/sign-out";
import "./auth/open-auth-window";
import "./auth/get-session-hash";

View File

@@ -1,10 +1,8 @@
import { dataSource } from "./data-source";
import {
DownloadQueue,
DownloadSource,
Game,
GameShopCache,
Repack,
UserPreferences,
UserAuth,
GameAchievement,
@@ -13,16 +11,11 @@ import {
export const gameRepository = dataSource.getRepository(Game);
export const repackRepository = dataSource.getRepository(Repack);
export const userPreferencesRepository =
dataSource.getRepository(UserPreferences);
export const gameShopCacheRepository = dataSource.getRepository(GameShopCache);
export const downloadSourceRepository =
dataSource.getRepository(DownloadSource);
export const downloadQueueRepository = dataSource.getRepository(DownloadQueue);
export const userAuthRepository = dataSource.getRepository(UserAuth);