diff --git a/polylan_submitter/games/migrations/0004_game_image_alter_game_steam_app_id.py b/polylan_submitter/games/migrations/0004_game_image_alter_game_steam_app_id.py new file mode 100644 index 0000000..0c71c84 --- /dev/null +++ b/polylan_submitter/games/migrations/0004_game_image_alter_game_steam_app_id.py @@ -0,0 +1,22 @@ +# Generated by Django 6.0.5 on 2026-05-24 22:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("games", "0003_game_path"), + ] + + operations = [ + migrations.AddField( + model_name="game", + name="image", + field=models.URLField(blank=True, null=True), + ), + migrations.AlterField( + model_name="game", + name="steam_app_id", + field=models.PositiveIntegerField(blank=True, null=True), + ), + ] diff --git a/polylan_submitter/games/models.py b/polylan_submitter/games/models.py index 42f1284..2794c56 100644 --- a/polylan_submitter/games/models.py +++ b/polylan_submitter/games/models.py @@ -2,9 +2,10 @@ from django.db import models class Game(models.Model): - steam_app_id = models.PositiveIntegerField(unique=True) + steam_app_id = models.PositiveIntegerField(null=True, blank=True) name = models.CharField(max_length=255) path = models.CharField(max_length=100) + image = models.URLField(null=True, blank=True) enabled = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) @@ -14,4 +15,6 @@ class Game(models.Model): ordering = ["name"] def __str__(self) -> str: - return f"{self.name} ({self.steam_app_id})" + if self.steam_app_id: + return f"{self.name} ({self.steam_app_id})" + return self.name diff --git a/polylan_submitter/games/schemas.py b/polylan_submitter/games/schemas.py index 8da0f75..cd15419 100644 --- a/polylan_submitter/games/schemas.py +++ b/polylan_submitter/games/schemas.py @@ -1,7 +1,11 @@ +from typing import Optional + from ninja import Schema class GameOut(Schema): - steam_app_id: int + id: int + steam_app_id: Optional[int] = None name: str path: str + image: Optional[str] = None diff --git a/polylan_submitter/src/Home.vue b/polylan_submitter/src/Home.vue index f3d44b0..8d4a37f 100644 --- a/polylan_submitter/src/Home.vue +++ b/polylan_submitter/src/Home.vue @@ -15,12 +15,18 @@ const games = ref(); const loading = ref(true); const imageErrors = ref>(new Set()); -const getHeaderImage = (appId: number) => { - return `https://cdn.akamai.steamstatic.com/steam/apps/${appId}/header.jpg`; +const getHeaderImage = (game: GamesApiListGamesResponse[number]) => { + if (game.image) { + return game.image; + } + if (game.steam_app_id) { + return `https://cdn.akamai.steamstatic.com/steam/apps/${game.steam_app_id}/header.jpg`; + } + return null; }; -const onImageError = (appId: number) => { - imageErrors.value.add(appId); +const onImageError = (gameId: number) => { + imageErrors.value.add(gameId); }; const navigate = (path: string) => { @@ -82,11 +88,11 @@ onMounted(async () => { -
- +
diff --git a/polylan_submitter/src/api/index.ts b/polylan_submitter/src/api/index.ts index bd39d97..534f9ad 100644 --- a/polylan_submitter/src/api/index.ts +++ b/polylan_submitter/src/api/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { animationsApiPuzzleResults, animationsApiResults, animationsApiTopSubmissions, gamesApiListGames, marketApiCloseMarket, marketApiCreateBet, marketApiListMarkets, marketApiListUserBets, marketApiResolveMarket, noitaApiGetLeaderboard, noitaApiGetResults, noitaApiSubmitLogFile, type Options, opusMagnumApiCreateSubmission, opusMagnumApiDeleteSubmission, opusMagnumApiGetCollection, opusMagnumApiGetStats, opusMagnumApiGetSubmission, opusMagnumApiListPuzzles, opusMagnumApiListResponsesNeedingValidation, opusMagnumApiListSubmissions, opusMagnumApiValidateAuto, opusMagnumApiValidateResponse, opusMagnumApiValidateSubmission, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo, polylanSubmitterApiHealthCheck } from './sdk.gen'; -export type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, AnimationsApiTopSubmissionsResponses, ClientOptions, GameOut, GamesApiListGamesData, GamesApiListGamesResponse, GamesApiListGamesResponses, Input, LeaderboardEntryOut, LeaderboardOut, MarketApiCloseMarketData, MarketApiCloseMarketResponses, MarketApiCreateBetData, MarketApiCreateBetResponse, MarketApiCreateBetResponses, MarketApiListMarketsData, MarketApiListMarketsResponse, MarketApiListMarketsResponses, MarketApiListUserBetsData, MarketApiListUserBetsResponse, MarketApiListUserBetsResponses, MarketApiResolveMarketData, MarketApiResolveMarketResponse, MarketApiResolveMarketResponses, MarketListSchema, MarketOptionSchema, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponse, NoitaApiSubmitLogFileResponses, NoitaSubmissionOut, ObjectivResultOut, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionResponse, OpusMagnumApiValidateSubmissionResponses, PagedSubmissionOut, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponses, PuzzlePointsFactorOut, PuzzleResponseIn, PuzzleResponseOut, PuzzleResponseRankingOut, PuzzleResultsOut, PuzzleSubmissionsOut, PuzzleSubmissionWithRankOut, RankingSchema, ResolveMarketSchema, ResultsOut, SteamCollectionItemOut, SteamCollectionOut, SubmissionFileOut, SubmissionIn, SubmissionOut, TournamentPuzzleResultsOut, TournamentSubmissionsOut, UserBetCreateSchema, UserBetSchema, UserDisplayOut, UserInfoOut, ValidationIn, WinnerFileOut, WinnerResponseOut } from './types.gen'; +export { animationsApiPuzzleResults, animationsApiResults, animationsApiTopSubmissions, gamesApiListGames, noitaApiGetLeaderboard, noitaApiGetResults, noitaApiSubmitLogFile, type Options, opusMagnumApiCreateSubmission, opusMagnumApiDeleteSubmission, opusMagnumApiGetCollection, opusMagnumApiGetStats, opusMagnumApiGetSubmission, opusMagnumApiListPuzzles, opusMagnumApiListResponsesNeedingValidation, opusMagnumApiListSubmissions, opusMagnumApiValidateAuto, opusMagnumApiValidateResponse, opusMagnumApiValidateSubmission, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo, polylanSubmitterApiHealthCheck } from './sdk.gen'; +export type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, AnimationsApiTopSubmissionsResponses, ClientOptions, GameOut, GamesApiListGamesData, GamesApiListGamesResponse, GamesApiListGamesResponses, Input, LeaderboardEntryOut, LeaderboardOut, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponse, NoitaApiSubmitLogFileResponses, NoitaSubmissionOut, ObjectivResultOut, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionResponse, OpusMagnumApiValidateSubmissionResponses, PagedSubmissionOut, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponses, PuzzlePointsFactorOut, PuzzleResponseIn, PuzzleResponseOut, PuzzleResponseRankingOut, PuzzleResultsOut, PuzzleSubmissionsOut, PuzzleSubmissionWithRankOut, RankingSchema, ResultsOut, SteamCollectionItemOut, SteamCollectionOut, SubmissionFileOut, SubmissionIn, SubmissionOut, TournamentPuzzleResultsOut, TournamentSubmissionsOut, UserDisplayOut, UserInfoOut, ValidationIn, WinnerFileOut, WinnerResponseOut } from './types.gen'; diff --git a/polylan_submitter/src/api/sdk.gen.ts b/polylan_submitter/src/api/sdk.gen.ts index cc3bd1f..f186119 100644 --- a/polylan_submitter/src/api/sdk.gen.ts +++ b/polylan_submitter/src/api/sdk.gen.ts @@ -2,7 +2,7 @@ import { type Client, formDataBodySerializer, type Options as Options2, type TDataShape } from './client'; import { client } from './client.gen'; -import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponses, GamesApiListGamesData, GamesApiListGamesResponses, MarketApiCloseMarketData, MarketApiCloseMarketResponses, MarketApiCreateBetData, MarketApiCreateBetResponses, MarketApiListMarketsData, MarketApiListMarketsResponses, MarketApiListUserBetsData, MarketApiListUserBetsResponses, MarketApiResolveMarketData, MarketApiResolveMarketResponses, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponses, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionResponses, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponses } from './types.gen'; +import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponses, GamesApiListGamesData, GamesApiListGamesResponses, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponses, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionResponses, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponses } from './types.gen'; export type Options = Options2 & { /** @@ -190,52 +190,3 @@ export const noitaApiSubmitLogFile = (opti * List Games */ export const gamesApiListGames = (options?: Options) => (options?.client ?? client).get({ url: '/api/games/', ...options }); - -/** - * List Markets - * - * List all markets (excludes draft markets). - */ -export const marketApiListMarkets = (options?: Options) => (options?.client ?? client).get({ url: '/api/market/', ...options }); - -/** - * List User Bets - * - * List all bets placed by the current user. - */ -export const marketApiListUserBets = (options?: Options) => (options?.client ?? client).get({ url: '/api/market/user/bets', ...options }); - -/** - * Close Market - * - * Close a market. Admin only. - */ -export const marketApiCloseMarket = (options: Options) => (options.client ?? client).post({ url: '/api/market/{market_uuid}/actions/close', ...options }); - -/** - * Resolve Market - * - * Resolve a market with a winning option. Admin only. - */ -export const marketApiResolveMarket = (options: Options) => (options.client ?? client).post({ - url: '/api/market/{market_uuid}/actions/resolve', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options.headers - } -}); - -/** - * Create Bet - * - * Place a bet on a market option. - */ -export const marketApiCreateBet = (options: Options) => (options.client ?? client).post({ - url: '/api/market/{market_uuid}/bets', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options.headers - } -}); diff --git a/polylan_submitter/src/api/types.gen.ts b/polylan_submitter/src/api/types.gen.ts index cef94c1..306333f 100644 --- a/polylan_submitter/src/api/types.gen.ts +++ b/polylan_submitter/src/api/types.gen.ts @@ -952,10 +952,14 @@ export type NoitaSubmissionOut = { * GameOut */ export type GameOut = { + /** + * Id + */ + id: number; /** * Steam App Id */ - steam_app_id: number; + steam_app_id?: number | null; /** * Name */ @@ -964,107 +968,10 @@ export type GameOut = { * Path */ path: string; -}; - -/** - * MarketListSchema - */ -export type MarketListSchema = { /** - * Uuid + * Image */ - uuid: string; - /** - * Title - */ - title: string; - /** - * Description - */ - description: string; - /** - * Status - */ - status: string; - /** - * End Date - */ - end_date: string; - /** - * Multiplier - */ - multiplier?: number; - /** - * Created At - */ - created_at: string; - /** - * Options - */ - options: Array; - winning_option?: MarketOptionSchema | null; -}; - -/** - * MarketOptionSchema - */ -export type MarketOptionSchema = { - /** - * Uuid - */ - uuid: string; - /** - * Text - */ - text: string; - /** - * Total Bets - */ - total_bets?: number; -}; - -/** - * UserBetSchema - */ -export type UserBetSchema = { - /** - * Uuid - */ - uuid: string; - /** - * Amount - */ - amount: number; - /** - * Created At - */ - created_at: string; - option: MarketOptionSchema; - market?: MarketListSchema | null; -}; - -/** - * ResolveMarketSchema - */ -export type ResolveMarketSchema = { - /** - * Winning Option Uuid - */ - winning_option_uuid: string; -}; - -/** - * UserBetCreateSchema - */ -export type UserBetCreateSchema = { - /** - * Option Uuid - */ - option_uuid: string; - /** - * Amount - */ - amount: number; + image?: string | null; }; export type PolylanSubmitterApiHealthCheckData = { @@ -1474,100 +1381,3 @@ export type GamesApiListGamesResponses = { }; export type GamesApiListGamesResponse = GamesApiListGamesResponses[keyof GamesApiListGamesResponses]; - -export type MarketApiListMarketsData = { - body?: never; - path?: never; - query?: never; - url: '/api/market/'; -}; - -export type MarketApiListMarketsResponses = { - /** - * Response - * - * OK - */ - 200: Array; -}; - -export type MarketApiListMarketsResponse = MarketApiListMarketsResponses[keyof MarketApiListMarketsResponses]; - -export type MarketApiListUserBetsData = { - body?: never; - path?: never; - query?: never; - url: '/api/market/user/bets'; -}; - -export type MarketApiListUserBetsResponses = { - /** - * Response - * - * OK - */ - 200: Array; -}; - -export type MarketApiListUserBetsResponse = MarketApiListUserBetsResponses[keyof MarketApiListUserBetsResponses]; - -export type MarketApiCloseMarketData = { - body?: never; - path: { - /** - * Market Uuid - */ - market_uuid: string; - }; - query?: never; - url: '/api/market/{market_uuid}/actions/close'; -}; - -export type MarketApiCloseMarketResponses = { - /** - * OK - */ - 200: unknown; -}; - -export type MarketApiResolveMarketData = { - body: ResolveMarketSchema; - path: { - /** - * Market Uuid - */ - market_uuid: string; - }; - query?: never; - url: '/api/market/{market_uuid}/actions/resolve'; -}; - -export type MarketApiResolveMarketResponses = { - /** - * OK - */ - 200: MarketListSchema; -}; - -export type MarketApiResolveMarketResponse = MarketApiResolveMarketResponses[keyof MarketApiResolveMarketResponses]; - -export type MarketApiCreateBetData = { - body: UserBetCreateSchema; - path: { - /** - * Market Uuid - */ - market_uuid: string; - }; - query?: never; - url: '/api/market/{market_uuid}/bets'; -}; - -export type MarketApiCreateBetResponses = { - /** - * OK - */ - 200: UserBetSchema; -}; - -export type MarketApiCreateBetResponse = MarketApiCreateBetResponses[keyof MarketApiCreateBetResponses];