feat: Implemented download page for downloading roms from various sources using plugins. Added support for internet archive external plugin. feat: Added tasks page to track running tasks/downloads feat: Added tanstack caching feat: Added quick play action Fixes #6 feat: Added quick emulator launch action fix: Made task queue only support 1 task per group and task ID should now be unique
710 lines
No EOL
20 KiB
TypeScript
710 lines
No EOL
20 KiB
TypeScript
import * as z from "zod";
|
|
|
|
export const settingRegistry = z.registry<{
|
|
dev?: boolean;
|
|
}>();
|
|
|
|
export const SettingsSchema = z.object({
|
|
rommAddress: z.url().optional(),
|
|
rommUser: z.string().default('admin').optional(),
|
|
windowSize: z.object({ width: z.number(), height: z.number() }).optional(),
|
|
windowPosition: z.object({ x: z.number(), y: z.number() }).optional(),
|
|
downloadPath: z.string(),
|
|
launchInFullscreen: z.boolean().default(true),
|
|
disabledPlugins: z.array(z.string()).default([]),
|
|
emulatorResolution: z.enum(['720p', '1080p', '1440p', '4k']).default('720p'),
|
|
emulatorWidescreen: z.boolean().default(true)
|
|
}); export const LocalSettingsSchema = z.object({
|
|
backgroundBlur: z.boolean().default(true).meta({ title: "Background Blur" }),
|
|
backgroundAnimation: z.boolean().default(true).meta({ title: "Background Animation" }),
|
|
theme: z.enum(['dark', 'light', 'auto']).default('auto').meta({ title: "Theme" }),
|
|
soundEffects: z.boolean().default(true).meta({ title: "Sounds" }),
|
|
soundEffectsVolume: z.number().min(0).max(100).default(50).meta({ title: "Sound Volume" }),
|
|
hapticsEffects: z.boolean().default(true).meta({ title: "Haptics" }),
|
|
showRouterDevOptions: z.boolean().default(false).meta({ title: "Show Router Options" }).register(settingRegistry, { dev: true }),
|
|
showQueryDevOptions: z.boolean().default(false).meta({ title: "Show Query Options" }).register(settingRegistry, { dev: true }),
|
|
useGameflowKeyboard: z.boolean().default(true).describe("Show the gameflow on screen keyboard when using a controller").meta({ title: "Use Gameflow Keyboard" }),
|
|
autoKeybaord: z.boolean().default(true).describe("Open on screen keybaord automatically").meta({ title: "Auto Keyboard" })
|
|
});
|
|
export const GameListFilterSchema = z.object({
|
|
platform_source: z.string().optional(),
|
|
platform_slug: z.string().optional(),
|
|
platform_id: z.coerce.number().optional(),
|
|
collection_id: z.coerce.number().optional(),
|
|
collection_source: z.string().optional(),
|
|
limit: z.coerce.number().optional(),
|
|
search: z.string().optional(),
|
|
offset: z.coerce.number().optional(),
|
|
source: z.string().optional(),
|
|
localOnly: z.coerce.boolean().optional(),
|
|
orderBy: z.literal(['added', 'activity', 'name', 'release']).optional(),
|
|
age_ratings: z.union([z.string().array(), z.string().transform(v => [v])]).optional(),
|
|
genres: z.union([z.string().array(), z.string().transform(v => [v])]).optional(),
|
|
keywords: z.union([z.string().array(), z.string().transform(v => [v])]).optional(),
|
|
});
|
|
export const DownloadSourceSchema = z.object({
|
|
id: z.string(),
|
|
name: z.string()
|
|
});
|
|
export const RommLoginDataSchema = z.object({ hostname: z.url(), username: z.string(), password: z.string() });
|
|
export type GameListFilterType = z.infer<typeof GameListFilterSchema>;
|
|
export const DirSchema = z.object({ name: z.string(), parentPath: z.string(), isDirectory: z.boolean() });
|
|
export type DirType = z.infer<typeof DirSchema>;
|
|
export const CustomEmulatorSchema = z.record(z.string(), z.string());
|
|
export const GithubManifestSchema = z.object({
|
|
sha: z.hash('sha1'),
|
|
url: z.url(),
|
|
tree: z.array(z.object({
|
|
path: z.string(),
|
|
mode: z.string(),
|
|
type: z.enum(['blob', 'tree']),
|
|
sha: z.hash('sha1'),
|
|
url: z.url()
|
|
}))
|
|
});
|
|
export const StoreGameSaveSchema = z.object({
|
|
cwd: z.string(),
|
|
globs: z.string().array()
|
|
});
|
|
export const StoreDownloadSchema = z.discriminatedUnion('type', [
|
|
z.object({
|
|
type: z.literal('direct'),
|
|
url: z.url(),
|
|
name: z.string().optional(),
|
|
system: z.string(),
|
|
main: z.string().optional(),
|
|
saves: z.record(z.string(), StoreGameSaveSchema).optional()
|
|
}),
|
|
z.object({
|
|
type: z.literal("itch"),
|
|
path: z.string(),
|
|
name: z.string().optional(),
|
|
system: z.string(),
|
|
saves: z.record(z.string(), StoreGameSaveSchema).optional()
|
|
})
|
|
]);
|
|
export const NewGameSchema = z.object({
|
|
name: z.string(),
|
|
summary: z.string(),
|
|
genres: z.string().regex(/^$|^(\s*\S[^,]*)(\s*,\s*\S[^,]*)*\s*$/, {
|
|
message: "Must be a comma-separated list",
|
|
})
|
|
});
|
|
export const StoreGameSchema = z.object({
|
|
name: z.string(),
|
|
description: z.string(),
|
|
version: z.string(),
|
|
homepage: z.string().optional(),
|
|
keywords: z.string().array().optional(),
|
|
genres: z.string().array().optional(),
|
|
companies: z.string().array().optional(),
|
|
screenshots: z.string().array().optional(),
|
|
covers: z.string().array().optional(),
|
|
igdb_id: z.number().optional(),
|
|
ra_id: z.number().optional(),
|
|
sgdb_id: z.number().optional(),
|
|
first_release_date: z.union([z.number(), z.date()]).optional(),
|
|
player_count: z.string().optional(),
|
|
saves: z.record(z.string(), z.record(z.string(), StoreGameSaveSchema)).optional(),
|
|
downloads: z.record(z.string(), StoreDownloadSchema)
|
|
});
|
|
export const EmulatorPackageSchema = z.object({
|
|
name: z.string(),
|
|
description: z.string(),
|
|
homepage: z.url(),
|
|
logo: z.url(),
|
|
type: z.enum(['emulator']),
|
|
os: z.array(z.enum(['darwin', 'linux', 'win32', 'android'])),
|
|
keywords: z.array(z.string()).optional(),
|
|
downloads: z.record(z.string(), z.array(z.discriminatedUnion('type', [
|
|
z.object({
|
|
type: z.literal(['github', 'gitlab']),
|
|
pattern: z.string(),
|
|
path: z.string(),
|
|
bin: z.string().optional()
|
|
}),
|
|
z.object({
|
|
type: z.literal('direct'),
|
|
url: z.url(),
|
|
bin: z.string().optional()
|
|
}),
|
|
z.object({
|
|
type: z.literal('scoop'),
|
|
url: z.url(),
|
|
bin: z.string().optional()
|
|
})
|
|
]))).optional(),
|
|
systems: z.array(z.string()),
|
|
bios: z.literal(["required", "optional"]).optional()
|
|
});
|
|
export const ScoopPackageSchema = z.object({
|
|
version: z.string(),
|
|
url: z.url().optional(),
|
|
description: z.string(),
|
|
bin: z.string().optional(),
|
|
architecture: z.record(z.string(), z.object({
|
|
url: z.url(),
|
|
hash: z.string().optional(),
|
|
extract_dir: z.string().optional()
|
|
})).optional()
|
|
});
|
|
export const SystemInfoSchema = z.object({
|
|
battery: z.object({
|
|
percent: z.number(),
|
|
isCharging: z.boolean(),
|
|
acConnected: z.boolean(),
|
|
hasBattery: z.boolean()
|
|
}),
|
|
wifiConnections: z.array(z.object({ signalLevel: z.number() })),
|
|
bluetoothDevices: z.array(z.object({ connected: z.boolean() }))
|
|
});
|
|
export const GithubReleaseSchema = z.object({
|
|
id: z.number(),
|
|
tag_name: z.string().optional(),
|
|
url: z.url(),
|
|
body: z.string(),
|
|
assets: z.array(z.object({
|
|
name: z.string(),
|
|
browser_download_url: z.url(),
|
|
content_type: z.string().optional()
|
|
}))
|
|
});
|
|
export const EmulatorDownloadInfoSchema = z.object({
|
|
id: z.string(),
|
|
version: z.string().optional(),
|
|
url: z.url().optional(),
|
|
description: z.string().optional(),
|
|
downloadDate: z.coerce.date(),
|
|
type: z.string()
|
|
});
|
|
export const PluginEntrySchema = z.object({
|
|
downloads: z.object({
|
|
monthly: z.number(),
|
|
weekly: z.number()
|
|
}),
|
|
searchScore: z.number(),
|
|
installed: z.boolean(),
|
|
update: z.object({ from: z.string() }).optional(),
|
|
package: z.object({
|
|
name: z.string(),
|
|
keywords: z.string().array(),
|
|
version: z.string(),
|
|
description: z.string().optional(),
|
|
sanitized_name: z.string(),
|
|
license: z.string().optional(),
|
|
publisher: z.object({
|
|
email: z.string(),
|
|
username: z.string(),
|
|
trustedPublisher: z.object({
|
|
id: z.string(),
|
|
oidcConfigId: z.string()
|
|
}).optional()
|
|
}),
|
|
date: z.coerce.date(),
|
|
links: z.object({
|
|
homepage: z.string().optional(),
|
|
repository: z.string().optional(),
|
|
bugs: z.string().optional(),
|
|
npm: z.url()
|
|
})
|
|
})
|
|
});
|
|
export const PluginBunDetailsSchema = z.object({
|
|
name: z.string(),
|
|
keywords: z.string().array(),
|
|
version: z.string(),
|
|
author: z.object({ name: z.string().optional() }).optional(),
|
|
license: z.string().optional(),
|
|
devDependencies: z.record(z.string(), z.string()).optional(),
|
|
dependencies: z.record(z.string(), z.string()).optional(),
|
|
maintainers: z.object({ name: z.string() }).array().optional(),
|
|
dist: z.object({ unpackedSize: z.number() }),
|
|
description: z.string().optional(),
|
|
_npmUser: z.object({ name: z.string() }).optional()
|
|
});
|
|
export type EmulatorPackageType = z.infer<typeof EmulatorPackageSchema>;
|
|
export type StoreGameType = z.infer<typeof StoreGameSchema>;
|
|
export type StoreDownloadType = z.infer<typeof StoreDownloadSchema>;
|
|
export type SettingsType = z.infer<typeof SettingsSchema>;
|
|
export type LocalSettingsType = z.infer<typeof LocalSettingsSchema>;
|
|
export const PlatformSchema = z.object({ slug: z.string() });
|
|
export type SystemInfoType = z.infer<typeof SystemInfoSchema>;
|
|
export type EmulatorDownloadInfoType = z.infer<typeof EmulatorDownloadInfoSchema>;
|
|
export type DownloadSourceType = z.infer<typeof DownloadSourceSchema>;
|
|
export type PluginEntryType = z.infer<typeof PluginEntrySchema>;
|
|
export type PluginBunDetailsType = z.infer<typeof PluginBunDetailsSchema>;
|
|
|
|
export interface SaveFileChange
|
|
{
|
|
subPath: string | string[];
|
|
isGlob?: true;
|
|
cwd: string;
|
|
shared: boolean;
|
|
fixedSize?: boolean;
|
|
}
|
|
|
|
export type EmulatorSourceType = 'custom' | 'store' | 'registry' | 'system' | 'static' | 'embedded';
|
|
|
|
export interface EmulatorSourceEntryType
|
|
{
|
|
binPath: string;
|
|
rootPath?: string;
|
|
type: EmulatorSourceType;
|
|
/** Does the emulator exist in the file system */
|
|
exists: boolean;
|
|
}
|
|
|
|
export interface FrontEndEmulator
|
|
{
|
|
name: string;
|
|
source: string;
|
|
logo: string;
|
|
systems: EmulatorSystem[];
|
|
description?: string;
|
|
gameCount: number;
|
|
validSources: EmulatorSourceEntryType[];
|
|
integrations: EmulatorSupport[];
|
|
}
|
|
|
|
export interface EmulatorSystem { id: string, romm_slug?: string, name: string, iconUrl: string; }
|
|
|
|
export interface FrontEndEmulatorDetailedDownload
|
|
{
|
|
name: string;
|
|
type: string | undefined;
|
|
version?: string;
|
|
}
|
|
|
|
export interface FrontEndEmulatorDetailed extends FrontEndEmulator
|
|
{
|
|
homepage: string;
|
|
description: string;
|
|
downloads: FrontEndEmulatorDetailedDownload[];
|
|
keywords?: string[];
|
|
screenshots: string[];
|
|
biosRequirement?: "required" | "optional";
|
|
bios?: string[];
|
|
storeDownloadInfo?: { hasUpdate: boolean; version?: string, type: string; description?: string; };
|
|
}
|
|
|
|
export interface FrontEndGameTypeDetailedAchievement
|
|
{
|
|
id: string;
|
|
title: string;
|
|
description?: string;
|
|
date?: Date;
|
|
date_hardcode?: Date;
|
|
badge_url?: string;
|
|
display_order: number;
|
|
type?: string;
|
|
}
|
|
|
|
export interface FrontEndGameTypeDetailedEmulator extends FrontEndEmulator
|
|
{
|
|
|
|
}
|
|
|
|
export interface FrontEndGameTypeDetailed extends Exclude<FrontEndGameTypeWithIds, "metadata">
|
|
{
|
|
summary: string | null;
|
|
fs_size_bytes: number | null;
|
|
missing: boolean;
|
|
local: boolean;
|
|
version?: string | null;
|
|
version_system?: string | null;
|
|
version_source?: string | null;
|
|
metadata: FrontEndGameMetadataDetailed,
|
|
emulators?: FrontEndGameTypeDetailedEmulator[],
|
|
achievements?: {
|
|
unlocked: number;
|
|
total: number;
|
|
entires: FrontEndGameTypeDetailedAchievement[];
|
|
};
|
|
};
|
|
|
|
export interface Drive
|
|
{
|
|
parent: string | null;
|
|
device: string;
|
|
label: string;
|
|
mountPoint: string | null;
|
|
type: string;
|
|
size: number;
|
|
used: number;
|
|
isRemovable: boolean;
|
|
interfaceType: string | null;
|
|
hasWriteAccess: boolean;
|
|
hasReadAccess: boolean;
|
|
}
|
|
|
|
export interface DownloadsDrive
|
|
{
|
|
device: string;
|
|
label: string;
|
|
mountPoint: string | null;
|
|
isRemovable: boolean;
|
|
size: number;
|
|
used: number;
|
|
isCurrentlyUsed: boolean;
|
|
unusableReason: 'not_enough_space' | 'already_used' | null;
|
|
}
|
|
|
|
export interface FrontendNotification
|
|
{
|
|
title?: string;
|
|
message: string;
|
|
type: 'success' | 'error' | 'info' | 'custom';
|
|
icon?: "save" | "upload" | "clock";
|
|
duration?: number;
|
|
}
|
|
|
|
export interface CommandEntry
|
|
{
|
|
/** The ID of the command. Could be just an index or a string */
|
|
id: string | number;
|
|
/** The front end label for the command. Mainly gotten from ES-DE list */
|
|
label?: string;
|
|
/** Compiled command to be executed */
|
|
command: string | string[];
|
|
/** Environment variables */
|
|
env?: Record<string, string>,
|
|
/** The path the spawned process will start at */
|
|
startDir?: string;
|
|
/** Is the command valid, for example does the executable exists */
|
|
valid: boolean;
|
|
/** Run the command as shell. Defaults is true */
|
|
shell?: boolean;
|
|
/** For what emulator is the command */
|
|
emulator?: string;
|
|
/** Where the emulator came from */
|
|
emulatorSource?: EmulatorSourceType;
|
|
/** Metadata for the command */
|
|
metadata: {
|
|
romPath?: string;
|
|
emulatorBin?: string;
|
|
/** The root directory of the emulator */
|
|
emulatorDir?: string;
|
|
};
|
|
}
|
|
|
|
export interface FrontEndId
|
|
{
|
|
id: string;
|
|
source: string;
|
|
}
|
|
|
|
// Stuff stored in the local sqlite metadata field
|
|
export interface LocalGameMetadata
|
|
{
|
|
genres?: string[],
|
|
companies?: string[],
|
|
game_modes?: string[],
|
|
age_ratings?: string[];
|
|
player_count?: string;
|
|
first_release_date?: number;
|
|
average_rating?: number;
|
|
}
|
|
|
|
export interface FrontEndPlatformType
|
|
{
|
|
id: FrontEndId;
|
|
slug: string;
|
|
name: string;
|
|
family_name?: string | null;
|
|
path_cover: string | null;
|
|
game_count: number;
|
|
updated_at: Date;
|
|
hasLocal: boolean;
|
|
paths_screenshots: string[];
|
|
}
|
|
|
|
export interface FrontEndGameTypeWithIds extends FrontEndGameType
|
|
{
|
|
igdb_id: number | null;
|
|
ra_id: number | null;
|
|
}
|
|
|
|
export interface FrontEndFilterSets
|
|
{
|
|
age_ratings: Set<string>,
|
|
player_counts: Set<string>,
|
|
languages: Set<string>,
|
|
companies: Set<string>,
|
|
genres: Set<string>;
|
|
}
|
|
|
|
export interface FrontEndFilterLists
|
|
{
|
|
age_ratings: string[],
|
|
player_counts: string[],
|
|
languages: string[],
|
|
companies: string[],
|
|
genres: string[];
|
|
}
|
|
|
|
export interface FrontEndGameMetadata
|
|
{
|
|
first_release_date: Date | null;
|
|
}
|
|
|
|
export interface FrontEndGameMetadataDetailed extends FrontEndGameMetadata
|
|
{
|
|
genres: string[],
|
|
companies: string[],
|
|
game_modes: string[],
|
|
age_ratings: string[];
|
|
player_count: string | null;
|
|
average_rating: number | null;
|
|
}
|
|
|
|
export interface FrontEndGameType
|
|
{
|
|
platform_display_name: string | null,
|
|
path_platform_cover: string | null;
|
|
id: FrontEndId,
|
|
source: string | null,
|
|
source_id: string | null,
|
|
path_fs: string | null,
|
|
path_covers: string[],
|
|
last_played: Date | null,
|
|
updated_at: Date,
|
|
metadata: FrontEndGameMetadata,
|
|
slug: string | null,
|
|
name: string | null,
|
|
platform_id: number | null,
|
|
platform_slug: string | null,
|
|
paths_screenshots: string[];
|
|
};
|
|
|
|
export type GameStatusType = 'installed' | 'missing-emulator' | 'error' | 'install' | 'download' | 'extract' | 'playing' | 'queued';
|
|
|
|
export interface GameInstallProgress
|
|
{
|
|
progress?: number;
|
|
status?: GameStatusType;
|
|
details?: string;
|
|
commands?: CommandEntry[];
|
|
error?: any;
|
|
}
|
|
|
|
export type JobStatus = 'completed' | 'error' | 'running' | 'queued' | 'aborted';
|
|
export type GameInstallProgressEvent = 'refresh';
|
|
|
|
export interface FrontEndJob
|
|
{
|
|
id: string;
|
|
data: any;
|
|
progress: number;
|
|
state?: string;
|
|
status: string;
|
|
}
|
|
|
|
export interface FrontendPlugin
|
|
{
|
|
name: string;
|
|
displayName?: string;
|
|
description?: string;
|
|
category: string;
|
|
enabled: boolean;
|
|
canDisable: boolean;
|
|
canUninstall: boolean;
|
|
source: PluginSourceType;
|
|
hasSettings: boolean;
|
|
version: string;
|
|
icon?: string;
|
|
update?: PluginUpdateCheck;
|
|
}
|
|
|
|
export interface PluginUpdateCheck
|
|
{
|
|
current: string;
|
|
new: string;
|
|
}
|
|
|
|
export type PluginSourceType = "builtin" | "store";
|
|
|
|
export type KeysWithValueAssignableTo<T, Value> = {
|
|
[K in keyof T]: Exclude<T[K], undefined> extends Value ? K : never;
|
|
}[keyof T];
|
|
|
|
export interface DownloadInfo
|
|
{
|
|
id: string;
|
|
screenshotUrls: string[];
|
|
coverUrl: string;
|
|
platform?: DownloadPlatform;
|
|
slug?: string;
|
|
path_fs?: string;
|
|
main_glob?: string;
|
|
summary?: string;
|
|
name: string;
|
|
last_played?: Date;
|
|
igdb_id?: number;
|
|
ra_id?: number;
|
|
source_id: string;
|
|
system_slug: string;
|
|
extract_path?: string;
|
|
metadata?: any;
|
|
files: DownloadFileEntry[];
|
|
auth?: string;
|
|
version?: string;
|
|
version_source?: string;
|
|
version_system?: string;
|
|
}
|
|
|
|
export interface DownloadPlatform
|
|
{
|
|
id: string;
|
|
source: string;
|
|
igdb_id?: number;
|
|
igdb_slug?: string;
|
|
ra_id?: number;
|
|
moby_id?: number;
|
|
slug: string;
|
|
name: string;
|
|
/** Like Sony or Nintendo */
|
|
family_name?: string;
|
|
}
|
|
|
|
export interface DownloadFileEntry
|
|
{
|
|
url: URL;
|
|
/** The path of the file, excluding the name */
|
|
file_path: string;
|
|
/** Just the name of the file including the extension */
|
|
file_name: string;
|
|
/** Checksum of the file */
|
|
sha1?: string;
|
|
/** Size in bytes */
|
|
size?: number;
|
|
}
|
|
|
|
export interface LocalDownloadFileEntry extends DownloadFileEntry
|
|
{
|
|
/** Exists on the file system */
|
|
exists: boolean;
|
|
/** Matches the checksum */
|
|
matches: boolean;
|
|
}
|
|
|
|
export interface FrontEndCollection
|
|
{
|
|
id: FrontEndId;
|
|
name: string;
|
|
description: string;
|
|
path_platform_cover: string | null;
|
|
game_count: number;
|
|
}
|
|
|
|
export type EmulatorCapabilities = "saves" | "fullscreen" | "resolution" | "batch" | "states" | "config";
|
|
|
|
export interface EmulatorSupport
|
|
{
|
|
id: string;
|
|
source?: EmulatorSourceEntryType;
|
|
supportLevel?: "partial" | "full";
|
|
capabilities?: EmulatorCapabilities[];
|
|
}
|
|
|
|
export interface GameLookup
|
|
{
|
|
source: string;
|
|
id: string;
|
|
coverUrl: string | null | undefined;
|
|
slug: string | null | undefined;
|
|
screenshotUrls: string[];
|
|
name: string;
|
|
summary: string | null | undefined;
|
|
genres: string[];
|
|
companies: string[];
|
|
game_modes: string[];
|
|
age_ratings: string[];
|
|
player_count: string | undefined;
|
|
first_release_date: number | undefined;
|
|
average_rating: number | undefined;
|
|
keywords: string[];
|
|
igdb_id: number | undefined;
|
|
platforms: {
|
|
id: number;
|
|
name?: string | null;
|
|
displayName: string;
|
|
slug: string;
|
|
}[];
|
|
}
|
|
|
|
export interface DownloadLookupEntry
|
|
{
|
|
source: string;
|
|
id: string;
|
|
cover_url: string | null | undefined;
|
|
name: string;
|
|
summary: string | null | undefined;
|
|
size: number | null | undefined;
|
|
date: Date | null | undefined;
|
|
rating: number | null | undefined;
|
|
view_count: number | null | undefined;
|
|
download_count: number | null | undefined;
|
|
comment_count: number | null | undefined;
|
|
}
|
|
|
|
export interface DownloadLookupDetailsFile
|
|
{
|
|
id: string;
|
|
format: string | null | undefined;
|
|
mtime: Date | null | undefined;
|
|
size: number | null | undefined;
|
|
download_url: string;
|
|
}
|
|
|
|
export interface DownloadLookupDetails
|
|
{
|
|
source: string;
|
|
id: string;
|
|
cover_url: string | null | undefined;
|
|
name: string;
|
|
summary: string | null | undefined;
|
|
date: Date | null | undefined;
|
|
files: DownloadLookupDetailsFile[];
|
|
}
|
|
|
|
export interface AutoSaveChange
|
|
{
|
|
subPath: string;
|
|
cwd: string;
|
|
}
|
|
|
|
export interface AppInfoContext
|
|
{
|
|
activeTaskProgress: number | null;
|
|
}
|
|
|
|
export type SaveSlots = Record<string, { cwd: string; }>;
|
|
|
|
/** Jobs that are downloading stuff can implement this data interface to show up in the downloads screen */
|
|
export interface DownloadJobData extends Partial<Omit<ProgressStats, 'progress'>>
|
|
{
|
|
preview_url?: string | null;
|
|
name?: string;
|
|
}
|
|
|
|
export interface ProgressStats
|
|
{
|
|
progress: number;
|
|
speed: number;
|
|
total: number;
|
|
downloaded: number;
|
|
}
|
|
|
|
export interface DownloadsLookupFilter
|
|
{
|
|
source?: string,
|
|
orderBy?: string,
|
|
search?: string;
|
|
sortDirection?: "desc" | "asc";
|
|
}
|
|
|
|
export interface DownloadsLookupFilterValues
|
|
{
|
|
orderBy: string[],
|
|
source: string[];
|
|
} |