mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-02 16:23:48 +03:00
fix: fixing download sources migration
This commit is contained in:
parent
f37b92261f
commit
32032e1c8a
@ -279,8 +279,11 @@
|
|||||||
"instructions": "Verifique a forma correta de instalar algum deles no seu distro Linux, garantindo assim a execução normal do jogo"
|
"instructions": "Verifique a forma correta de instalar algum deles no seu distro Linux, garantindo assim a execução normal do jogo"
|
||||||
},
|
},
|
||||||
"catalogue": {
|
"catalogue": {
|
||||||
"next_page": "Próxima página",
|
"search": "Pesquisar…",
|
||||||
"previous_page": "Página anterior"
|
"developers": "Desenvolvedores",
|
||||||
|
"genres": "Gêneros",
|
||||||
|
"tags": "Tags",
|
||||||
|
"download_sources": "Fontes de download"
|
||||||
},
|
},
|
||||||
"modal": {
|
"modal": {
|
||||||
"close": "Botão de fechar"
|
"close": "Botão de fechar"
|
||||||
|
@ -74,9 +74,18 @@ export const search = recipe({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const searchButton = style({
|
export const searchButton = recipe({
|
||||||
WebkitAppRegion: "no-drag",
|
base: {
|
||||||
} as ComplexStyleRule);
|
WebkitAppRegion: "no-drag",
|
||||||
|
} as ComplexStyleRule,
|
||||||
|
variants: {
|
||||||
|
hidden: {
|
||||||
|
true: {
|
||||||
|
visibility: "hidden",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const section = style({
|
export const section = style({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -35,6 +35,10 @@ export function Header() {
|
|||||||
return t(pathTitle[location.pathname]);
|
return t(pathTitle[location.pathname]);
|
||||||
}, [location.pathname, headerTitle, t]);
|
}, [location.pathname, headerTitle, t]);
|
||||||
|
|
||||||
|
const showSearchButton = useMemo(() => {
|
||||||
|
return location.pathname.startsWith("/catalogue");
|
||||||
|
}, [location.pathname]);
|
||||||
|
|
||||||
const handleBackButtonClick = () => {
|
const handleBackButtonClick = () => {
|
||||||
navigate(-1);
|
navigate(-1);
|
||||||
};
|
};
|
||||||
@ -71,7 +75,7 @@ export function Header() {
|
|||||||
<section className={styles.section}>
|
<section className={styles.section}>
|
||||||
<Button
|
<Button
|
||||||
theme="outline"
|
theme="outline"
|
||||||
className={styles.searchButton}
|
className={styles.searchButton({ hidden: showSearchButton })}
|
||||||
onClick={() => navigate("/catalogue?search=true")}
|
onClick={() => navigate("/catalogue?search=true")}
|
||||||
>
|
>
|
||||||
<SearchIcon />
|
<SearchIcon />
|
||||||
|
@ -93,23 +93,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__filters-container {
|
&__filters-container {
|
||||||
width: 250px;
|
width: 270px;
|
||||||
min-width: 250px;
|
min-width: 270px;
|
||||||
max-width: 250px;
|
max-width: 270px;
|
||||||
background-color: globals.$dark-background-color;
|
background-color: globals.$dark-background-color;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border: 1px solid globals.$border-color;
|
border: 1px solid globals.$border-color;
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__ai-recommendations-button-text {
|
|
||||||
font-weight: 400;
|
|
||||||
color: #fff;
|
|
||||||
background: linear-gradient(0deg, #16b195 50%, #3e62c0 100%);
|
|
||||||
background-clip: text;
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Badge, Button } from "@renderer/components";
|
import { Badge } from "@renderer/components";
|
||||||
|
|
||||||
import type { DownloadSource } from "@types";
|
import type { DownloadSource } from "@types";
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ import cn from "classnames";
|
|||||||
|
|
||||||
import { useAppDispatch, useAppSelector, useRepacks } from "@renderer/hooks";
|
import { useAppDispatch, useAppSelector, useRepacks } from "@renderer/hooks";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { LightBulbIcon, SearchIcon, XIcon } from "@primer/octicons-react";
|
import { SearchIcon, XIcon } from "@primer/octicons-react";
|
||||||
|
|
||||||
import "./catalogue.scss";
|
import "./catalogue.scss";
|
||||||
|
|
||||||
@ -18,7 +18,8 @@ import { useNavigate, useSearchParams } from "react-router-dom";
|
|||||||
import { FilterSection } from "./filter-section";
|
import { FilterSection } from "./filter-section";
|
||||||
import { setSearch } from "@renderer/features";
|
import { setSearch } from "@renderer/features";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { steamUserTags } from "./steam-user-tags";
|
import axios from "axios";
|
||||||
|
import Skeleton, { SkeletonTheme } from "react-loading-skeleton";
|
||||||
|
|
||||||
const filterCategoryColors = {
|
const filterCategoryColors = {
|
||||||
genres: "hsl(262deg 50% 47%)",
|
genres: "hsl(262deg 50% 47%)",
|
||||||
@ -35,6 +36,8 @@ export default function Catalogue() {
|
|||||||
|
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
|
|
||||||
|
const [steamUserTags, setSteamUserTags] = useState<any>({});
|
||||||
|
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const search = searchParams.get("search");
|
const search = searchParams.get("search");
|
||||||
|
|
||||||
@ -42,6 +45,7 @@ export default function Catalogue() {
|
|||||||
|
|
||||||
const [downloadSources, setDownloadSources] = useState<DownloadSource[]>([]);
|
const [downloadSources, setDownloadSources] = useState<DownloadSource[]>([]);
|
||||||
const [games, setGames] = useState<any[]>([]);
|
const [games, setGames] = useState<any[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [publishers, setPublishers] = useState<string[]>([]);
|
const [publishers, setPublishers] = useState<string[]>([]);
|
||||||
const [developers, setDevelopers] = useState<string[]>([]);
|
const [developers, setDevelopers] = useState<string[]>([]);
|
||||||
|
|
||||||
@ -49,24 +53,30 @@ export default function Catalogue() {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { t } = useTranslation("catalogue");
|
const { t, i18n } = useTranslation("catalogue");
|
||||||
|
|
||||||
const { getRepacksForObjectId } = useRepacks();
|
const { getRepacksForObjectId } = useRepacks();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setGames([]);
|
setGames([]);
|
||||||
|
setIsLoading(true);
|
||||||
abortControllerRef.current?.abort();
|
abortControllerRef.current?.abort();
|
||||||
|
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
abortControllerRef.current = abortController;
|
abortControllerRef.current = abortController;
|
||||||
|
|
||||||
window.electron.searchGames(filters).then((games) => {
|
window.electron
|
||||||
if (abortController.signal.aborted) {
|
.searchGames(filters)
|
||||||
return;
|
.then((games) => {
|
||||||
}
|
if (abortController.signal.aborted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setGames(games);
|
setGames(games);
|
||||||
});
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
}, [filters]);
|
}, [filters]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -91,7 +101,7 @@ export default function Catalogue() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
downloadSourcesTable.toArray().then((sources) => {
|
downloadSourcesTable.toArray().then((sources) => {
|
||||||
setDownloadSources(sources);
|
setDownloadSources(sources.filter((source) => !!source.fingerprint));
|
||||||
});
|
});
|
||||||
}, [getRepacksForObjectId]);
|
}, [getRepacksForObjectId]);
|
||||||
|
|
||||||
@ -107,6 +117,22 @@ export default function Catalogue() {
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
axios
|
||||||
|
.get(
|
||||||
|
`${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}/steam-user-tags.json`
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
const language = i18n.language.split("-")[0];
|
||||||
|
|
||||||
|
if (response.data[language]) {
|
||||||
|
setSteamUserTags(response.data[language]);
|
||||||
|
} else {
|
||||||
|
setSteamUserTags(response.data["en"]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [i18n.language]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (search) {
|
if (search) {
|
||||||
focusInput();
|
focusInput();
|
||||||
@ -124,7 +150,8 @@ export default function Catalogue() {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={cn("catalogue__search-container", {
|
className={cn("catalogue__search-container", {
|
||||||
["catalogue__search-container--focused"]: focused,
|
["catalogue__search-container--focused"]:
|
||||||
|
focused || !!filters.title,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@ -185,35 +212,42 @@ export default function Catalogue() {
|
|||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{filters.tags.map((tag) => (
|
<li
|
||||||
<Badge key={tag}>
|
style={{
|
||||||
<div style={{ display: "flex", gap: 4, alignItems: "center" }}>
|
display: "flex",
|
||||||
{tag}
|
alignItems: "center",
|
||||||
</div>
|
color: vars.color.body,
|
||||||
</Badge>
|
backgroundColor: vars.color.darkBackground,
|
||||||
))}
|
padding: "6px 12px",
|
||||||
|
borderRadius: 4,
|
||||||
{filters.downloadSourceFingerprints.map((fingerprint) => (
|
border: `solid 1px ${vars.color.border}`,
|
||||||
<Badge key={fingerprint}>
|
fontSize: 12,
|
||||||
<div style={{ display: "flex", gap: 4, alignItems: "center" }}>
|
}}
|
||||||
<div
|
>
|
||||||
style={{
|
<div
|
||||||
width: 10,
|
style={{
|
||||||
height: 10,
|
width: 10,
|
||||||
backgroundColor:
|
height: 10,
|
||||||
filterCategoryColors.downloadSourceFingerprints,
|
backgroundColor: filterCategoryColors.genres,
|
||||||
borderRadius: "50%",
|
borderRadius: "50%",
|
||||||
}}
|
marginRight: 8,
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
{
|
Action
|
||||||
downloadSources.find(
|
<button
|
||||||
(source) => source.fingerprint === fingerprint
|
type="button"
|
||||||
)?.name
|
style={{
|
||||||
}
|
color: vars.color.body,
|
||||||
</div>
|
marginLeft: 4,
|
||||||
</Badge>
|
display: "flex",
|
||||||
))}
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<XIcon size={13} />
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <Button theme="outline">
|
{/* <Button theme="outline">
|
||||||
@ -237,74 +271,81 @@ export default function Catalogue() {
|
|||||||
gap: 8,
|
gap: 8,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{gamesWithRepacks.map((game, i) => (
|
{isLoading ? (
|
||||||
<button
|
<SkeletonTheme
|
||||||
type="button"
|
baseColor={vars.color.darkBackground}
|
||||||
key={i}
|
highlightColor={vars.color.background}
|
||||||
className="catalogue__game-item"
|
|
||||||
onClick={() => navigate(buildGameDetailsPath(game))}
|
|
||||||
>
|
>
|
||||||
<img
|
{Array.from({ length: 24 }).map((_, i) => (
|
||||||
style={{
|
<Skeleton
|
||||||
width: 200,
|
key={i}
|
||||||
height: "100%",
|
|
||||||
objectFit: "cover",
|
|
||||||
}}
|
|
||||||
src={steamUrlBuilder.library(game.objectId)}
|
|
||||||
alt={game.title}
|
|
||||||
loading="lazy"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
gap: 4,
|
|
||||||
padding: "16px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>{game.title}</span>
|
|
||||||
<span
|
|
||||||
style={{
|
style={{
|
||||||
color: vars.color.body,
|
height: 105,
|
||||||
marginBottom: 4,
|
borderRadius: 4,
|
||||||
fontSize: 12,
|
border: `solid 1px ${vars.color.border}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SkeletonTheme>
|
||||||
|
) : (
|
||||||
|
gamesWithRepacks.map((game, i) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
key={i}
|
||||||
|
className="catalogue__game-item"
|
||||||
|
onClick={() => navigate(buildGameDetailsPath(game))}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{
|
||||||
|
width: 200,
|
||||||
|
height: "100%",
|
||||||
|
objectFit: "cover",
|
||||||
|
}}
|
||||||
|
src={steamUrlBuilder.library(game.objectId)}
|
||||||
|
alt={game.title}
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
gap: 4,
|
||||||
|
padding: "16px 0",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{game.genres?.join(", ")}
|
<span>{game.title}</span>
|
||||||
</span>
|
<span
|
||||||
|
style={{
|
||||||
|
color: vars.color.body,
|
||||||
|
marginBottom: 4,
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{game.genres?.join(", ")}
|
||||||
|
</span>
|
||||||
|
|
||||||
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
|
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
|
||||||
{game.repacks.map((repack) => (
|
{game.repacks.map((repack) => (
|
||||||
<Badge key={repack}>{repack}</Badge>
|
<Badge key={repack}>{repack}</Badge>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
</button>
|
))
|
||||||
))}
|
)}
|
||||||
|
|
||||||
<div style={{ display: "flex", gap: 8 }}>
|
{/* <div style={{ display: "flex", gap: 8 }}>
|
||||||
<Button theme="outline">1</Button>
|
<Button theme="outline">1</Button>
|
||||||
<Button theme="outline">2</Button>
|
<Button theme="outline">2</Button>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="catalogue__filters-container">
|
<div className="catalogue__filters-container">
|
||||||
<Button
|
|
||||||
style={{ width: "100%", marginBottom: 16 }}
|
|
||||||
theme="outline"
|
|
||||||
className="catalogue__ai-recommendations-button"
|
|
||||||
>
|
|
||||||
<span className="catalogue__ai-recommendations-button-text">
|
|
||||||
<LightBulbIcon size={14} />
|
|
||||||
Recomendações por AI
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
||||||
<FilterSection
|
<FilterSection
|
||||||
title="Genres"
|
title={t("genres")}
|
||||||
onClear={() => dispatch(setSearch({ genres: [] }))}
|
onClear={() => dispatch(setSearch({ genres: [] }))}
|
||||||
color={filterCategoryColors.genres}
|
color={filterCategoryColors.genres}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
@ -358,7 +399,7 @@ export default function Catalogue() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<FilterSection
|
<FilterSection
|
||||||
title="User tags"
|
title={t("tags")}
|
||||||
color={filterCategoryColors.tags}
|
color={filterCategoryColors.tags}
|
||||||
onClear={() => dispatch(setSearch({ tags: [] }))}
|
onClear={() => dispatch(setSearch({ tags: [] }))}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
@ -382,7 +423,7 @@ export default function Catalogue() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<FilterSection
|
<FilterSection
|
||||||
title="Download sources"
|
title={t("download_sources")}
|
||||||
color={filterCategoryColors.downloadSourceFingerprints}
|
color={filterCategoryColors.downloadSourceFingerprints}
|
||||||
onClear={() =>
|
onClear={() =>
|
||||||
dispatch(setSearch({ downloadSourceFingerprints: [] }))
|
dispatch(setSearch({ downloadSourceFingerprints: [] }))
|
||||||
@ -418,7 +459,7 @@ export default function Catalogue() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<FilterSection
|
<FilterSection
|
||||||
title="Developers"
|
title={t("developers")}
|
||||||
color={filterCategoryColors.developers}
|
color={filterCategoryColors.developers}
|
||||||
onClear={() => dispatch(setSearch({ developers: [] }))}
|
onClear={() => dispatch(setSearch({ developers: [] }))}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
@ -444,7 +485,7 @@ export default function Catalogue() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<FilterSection
|
<FilterSection
|
||||||
title="Publishers"
|
title={t("publishers")}
|
||||||
color={filterCategoryColors.publishers}
|
color={filterCategoryColors.publishers}
|
||||||
onClear={() => dispatch(setSearch({ publishers: [] }))}
|
onClear={() => dispatch(setSearch({ publishers: [] }))}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
|
@ -1,452 +0,0 @@
|
|||||||
export const steamUserTags = {
|
|
||||||
Atmospheric: 4166,
|
|
||||||
Fantasy: 1684,
|
|
||||||
Relaxing: 1654,
|
|
||||||
Funny: 4136,
|
|
||||||
"Sci-fi": 3942,
|
|
||||||
Horror: 1667,
|
|
||||||
"Family Friendly": 5350,
|
|
||||||
Retro: 4004,
|
|
||||||
Survival: 1662,
|
|
||||||
Mystery: 5716,
|
|
||||||
Dark: 4342,
|
|
||||||
Comedy: 1719,
|
|
||||||
"Psychological Horror": 1721,
|
|
||||||
Sports: 701,
|
|
||||||
"Old School": 3916,
|
|
||||||
Medieval: 4172,
|
|
||||||
Magic: 4057,
|
|
||||||
Racing: 699,
|
|
||||||
Building: 1643,
|
|
||||||
Management: 12472,
|
|
||||||
Space: 1755,
|
|
||||||
Tactical: 1708,
|
|
||||||
Drama: 5984,
|
|
||||||
Futuristic: 4295,
|
|
||||||
Logic: 6129,
|
|
||||||
Romance: 4947,
|
|
||||||
Crafting: 1702,
|
|
||||||
"Dark Fantasy": 4604,
|
|
||||||
Emotional: 5608,
|
|
||||||
"Survival Horror": 3978,
|
|
||||||
"1980s": 7743,
|
|
||||||
Nature: 30358,
|
|
||||||
Education: 1036,
|
|
||||||
"1990's": 6691,
|
|
||||||
Surreal: 1710,
|
|
||||||
"Post-apocalyptic": 3835,
|
|
||||||
War: 1678,
|
|
||||||
Zombies: 1659,
|
|
||||||
Historical: 3987,
|
|
||||||
Stealth: 1687,
|
|
||||||
Investigation: 8369,
|
|
||||||
Military: 4168,
|
|
||||||
"LGBTQ+": 44868,
|
|
||||||
Cyberpunk: 4115,
|
|
||||||
"Lore-Rich": 3854,
|
|
||||||
Detective: 5613,
|
|
||||||
Aliens: 1673,
|
|
||||||
Thriller: 4064,
|
|
||||||
Economy: 4695,
|
|
||||||
Robots: 5752,
|
|
||||||
Demons: 9541,
|
|
||||||
"Dark Humor": 5923,
|
|
||||||
Psychological: 5186,
|
|
||||||
Driving: 1644,
|
|
||||||
Supernatural: 10808,
|
|
||||||
"Comic Book": 1751,
|
|
||||||
Modern: 5673,
|
|
||||||
Psychedelic: 1714,
|
|
||||||
Dystopian: 5030,
|
|
||||||
Flight: 15045,
|
|
||||||
"Artificial Intelligence": 7926,
|
|
||||||
Loot: 4236,
|
|
||||||
Memes: 10397,
|
|
||||||
"Alternate History": 4598,
|
|
||||||
Cats: 17894,
|
|
||||||
Parkour: 4036,
|
|
||||||
Mythology: 16094,
|
|
||||||
Crime: 6378,
|
|
||||||
"Game Development": 13906,
|
|
||||||
Destruction: 5363,
|
|
||||||
Philosophical: 15277,
|
|
||||||
Capitalism: 4845,
|
|
||||||
"Dark Comedy": 19995,
|
|
||||||
Automation: 255534,
|
|
||||||
Lovecraftian: 7432,
|
|
||||||
Noir: 6052,
|
|
||||||
Swordplay: 4608,
|
|
||||||
Science: 5794,
|
|
||||||
Cooking: 3920,
|
|
||||||
America: 13190,
|
|
||||||
Dragons: 4046,
|
|
||||||
"World War II": 4150,
|
|
||||||
Parody: 4878,
|
|
||||||
Agriculture: 22602,
|
|
||||||
Conspiracy: 5372,
|
|
||||||
Gothic: 3952,
|
|
||||||
"Martial Arts": 6915,
|
|
||||||
Mechs: 4821,
|
|
||||||
Underground: 21006,
|
|
||||||
Satire: 1651,
|
|
||||||
Pirates: 1681,
|
|
||||||
Steampunk: 1777,
|
|
||||||
Dog: 1638,
|
|
||||||
"Time Travel": 10679,
|
|
||||||
Mining: 5981,
|
|
||||||
Transportation: 10383,
|
|
||||||
Ninja: 1688,
|
|
||||||
Vampire: 4018,
|
|
||||||
Tanks: 13276,
|
|
||||||
Political: 4853,
|
|
||||||
Otome: 31579,
|
|
||||||
Underwater: 9157,
|
|
||||||
Hunting: 9564,
|
|
||||||
Fishing: 15564,
|
|
||||||
Trains: 1616,
|
|
||||||
Dinosaurs: 5160,
|
|
||||||
Western: 1647,
|
|
||||||
Hacking: 5502,
|
|
||||||
Faith: 180368,
|
|
||||||
Programming: 5432,
|
|
||||||
Superhero: 1671,
|
|
||||||
Politics: 4754,
|
|
||||||
Assassin: 4376,
|
|
||||||
Gambling: 16250,
|
|
||||||
Naval: 6910,
|
|
||||||
Diplomacy: 6310,
|
|
||||||
Heist: 1680,
|
|
||||||
Snow: 9803,
|
|
||||||
Archery: 13382,
|
|
||||||
"Cold War": 5179,
|
|
||||||
Sailing: 13577,
|
|
||||||
"Football (Soccer)": 1254546,
|
|
||||||
Foreign: 51306,
|
|
||||||
Offroad: 7622,
|
|
||||||
Horses: 6041,
|
|
||||||
Illuminati: 7478,
|
|
||||||
Sniper: 7423,
|
|
||||||
Transhumanism: 4137,
|
|
||||||
Werewolves: 17015,
|
|
||||||
Mars: 6702,
|
|
||||||
Boxing: 12190,
|
|
||||||
Jet: 92092,
|
|
||||||
Motorbike: 198913,
|
|
||||||
Golf: 7038,
|
|
||||||
Bikes: 123332,
|
|
||||||
"World War I": 5382,
|
|
||||||
Rome: 6948,
|
|
||||||
Submarine: 19780,
|
|
||||||
Basketball: 1746,
|
|
||||||
Baseball: 5727,
|
|
||||||
LEGO: 1736,
|
|
||||||
Skateboarding: 1753,
|
|
||||||
"Mini Golf": 22955,
|
|
||||||
Wrestling: 47827,
|
|
||||||
"Football (American)": 1254552,
|
|
||||||
Tennis: 5914,
|
|
||||||
Pool: 17927,
|
|
||||||
Skating: 96359,
|
|
||||||
Cycling: 19568,
|
|
||||||
Motocross: 15868,
|
|
||||||
"Warhammer 40K": 12286,
|
|
||||||
Lemmings: 17337,
|
|
||||||
Hockey: 324176,
|
|
||||||
Bowling: 7328,
|
|
||||||
Snowboarding: 28444,
|
|
||||||
Skiing: 7309,
|
|
||||||
BMX: 252854,
|
|
||||||
ATV: 129761,
|
|
||||||
Indie: 492,
|
|
||||||
Action: 19,
|
|
||||||
Casual: 597,
|
|
||||||
Adventure: 21,
|
|
||||||
Simulation: 599,
|
|
||||||
RPG: 122,
|
|
||||||
Strategy: 9,
|
|
||||||
"Action-Adventure": 4106,
|
|
||||||
Software: 8013,
|
|
||||||
"2D": 3871,
|
|
||||||
"3D": 4191,
|
|
||||||
Colorful: 4305,
|
|
||||||
"Pixel Graphics": 3964,
|
|
||||||
Cute: 4726,
|
|
||||||
"First-Person": 3839,
|
|
||||||
Anime: 4085,
|
|
||||||
"Third Person": 1697,
|
|
||||||
Stylized: 4252,
|
|
||||||
"Top-Down": 4791,
|
|
||||||
Realistic: 4175,
|
|
||||||
Cartoony: 4195,
|
|
||||||
Minimalist: 4094,
|
|
||||||
"Hand-drawn": 6815,
|
|
||||||
VR: 21978,
|
|
||||||
Cartoon: 4562,
|
|
||||||
"Text-Based": 31275,
|
|
||||||
Cinematic: 4145,
|
|
||||||
"2.5D": 4975,
|
|
||||||
Isometric: 5851,
|
|
||||||
Abstract: 4400,
|
|
||||||
"Split Screen": 10816,
|
|
||||||
"3D Vision": 29363,
|
|
||||||
Voxel: 1732,
|
|
||||||
Beautiful: 5411,
|
|
||||||
FMV: 18594,
|
|
||||||
"360 Video": 776177,
|
|
||||||
"Story Rich": 1742,
|
|
||||||
Combat: 3993,
|
|
||||||
Controller: 7481,
|
|
||||||
"Female Protagonist": 7208,
|
|
||||||
"Choices Matter": 6426,
|
|
||||||
"Open World": 1695,
|
|
||||||
PvP: 1775,
|
|
||||||
PvE: 6730,
|
|
||||||
Linear: 7250,
|
|
||||||
"Multiple Endings": 6971,
|
|
||||||
Physics: 3968,
|
|
||||||
"Character Customization": 4747,
|
|
||||||
"Procedural Generation": 5125,
|
|
||||||
Tabletop: 17389,
|
|
||||||
"Turn-Based Combat": 4325,
|
|
||||||
"Turn-Based Tactics": 14139,
|
|
||||||
"Hack and Slash": 1646,
|
|
||||||
"Resource Management": 8945,
|
|
||||||
"Base-Building": 7332,
|
|
||||||
"Score Attack": 5154,
|
|
||||||
Narration: 5094,
|
|
||||||
Conversation: 15172,
|
|
||||||
Nonlinear: 6869,
|
|
||||||
Tutorial: 12057,
|
|
||||||
"Perma Death": 1759,
|
|
||||||
"Team-Based": 5711,
|
|
||||||
Deckbuilding: 32322,
|
|
||||||
"Inventory Management": 6276,
|
|
||||||
"Level Editor": 8122,
|
|
||||||
"Grid-Based Movement": 7569,
|
|
||||||
Moddable: 1669,
|
|
||||||
"Class-Based": 4155,
|
|
||||||
"Vehicular Combat": 11104,
|
|
||||||
"Gun Customization": 5765,
|
|
||||||
"6DOF": 4835,
|
|
||||||
Trading: 4202,
|
|
||||||
"Bullet Time": 5796,
|
|
||||||
"Time Manipulation": 6625,
|
|
||||||
"Quick-Time Events": 4559,
|
|
||||||
"Dynamic Narration": 9592,
|
|
||||||
"Hex Grid": 1717,
|
|
||||||
"Naval Combat": 4994,
|
|
||||||
"Music-Based Procedural Generation": 8253,
|
|
||||||
"Asymmetric VR": 856791,
|
|
||||||
Puzzle: 1664,
|
|
||||||
Arcade: 1773,
|
|
||||||
Shooter: 1774,
|
|
||||||
Platformer: 1625,
|
|
||||||
"Visual Novel": 3799,
|
|
||||||
Sandbox: 3810,
|
|
||||||
"Rogue-like": 1716,
|
|
||||||
"Action RPG": 4231,
|
|
||||||
"Point & Click": 1698,
|
|
||||||
"Action Roguelike": 42804,
|
|
||||||
"Interactive Fiction": 11014,
|
|
||||||
"Turn-Based Strategy": 1741,
|
|
||||||
"Dating Sim": 9551,
|
|
||||||
JRPG: 4434,
|
|
||||||
"Party-Based RPG": 10695,
|
|
||||||
"Walking Simulator": 5900,
|
|
||||||
"Design & Illustration": 84,
|
|
||||||
"Card Game": 1666,
|
|
||||||
"Life Sim": 10235,
|
|
||||||
Utilities: 87,
|
|
||||||
"Strategy RPG": 17305,
|
|
||||||
"Board Game": 1770,
|
|
||||||
RTS: 1676,
|
|
||||||
"Tower Defense": 1645,
|
|
||||||
"Web Publishing": 1038,
|
|
||||||
"City Builder": 4328,
|
|
||||||
"Beat 'em up": 4158,
|
|
||||||
"Automobile Sim": 1100687,
|
|
||||||
"2D Fighter": 4736,
|
|
||||||
Rhythm: 1752,
|
|
||||||
"3D Fighter": 6506,
|
|
||||||
"Farming Sim": 87918,
|
|
||||||
"Animation & Modeling": 872,
|
|
||||||
"e-sports": 5055,
|
|
||||||
"Grand Strategy": 4364,
|
|
||||||
"Space Sim": 16598,
|
|
||||||
"Colony Sim": 220585,
|
|
||||||
"Word Game": 24003,
|
|
||||||
"Battle Royale": 176981,
|
|
||||||
MMORPG: 1754,
|
|
||||||
"Auto Battler": 1084988,
|
|
||||||
"Audio Production": 1027,
|
|
||||||
"Video Production": 784,
|
|
||||||
"God Game": 5300,
|
|
||||||
"4X": 1670,
|
|
||||||
MOBA: 1718,
|
|
||||||
"Photo Editing": 809,
|
|
||||||
Trivia: 10437,
|
|
||||||
"Immersive Sim": 9204,
|
|
||||||
"Political Sim": 26921,
|
|
||||||
"Outbreak Sim": 1100686,
|
|
||||||
"Medical Sim": 1100688,
|
|
||||||
Short: 4234,
|
|
||||||
Movie: 4700,
|
|
||||||
Episodic: 4242,
|
|
||||||
Gaming: 150626,
|
|
||||||
Documentary: 15339,
|
|
||||||
Exploration: 3834,
|
|
||||||
"2D Platformer": 5379,
|
|
||||||
FPS: 1663,
|
|
||||||
"Rogue-lite": 3959,
|
|
||||||
"Shoot 'Em Up": 4255,
|
|
||||||
"3D Platformer": 5395,
|
|
||||||
"Side Scroller": 3798,
|
|
||||||
"Choose Your Own Adventure": 4486,
|
|
||||||
"Puzzle-Platformer": 5537,
|
|
||||||
"Hidden Object": 1738,
|
|
||||||
"Bullet Hell": 4885,
|
|
||||||
"Dungeon Crawler": 1720,
|
|
||||||
"Top-Down Shooter": 4637,
|
|
||||||
Clicker: 379975,
|
|
||||||
"Third-Person Shooter": 3814,
|
|
||||||
"Precision Platformer": 3877,
|
|
||||||
"Time Management": 16689,
|
|
||||||
"Real Time Tactics": 3813,
|
|
||||||
"Arena Shooter": 5547,
|
|
||||||
Collectathon: 5652,
|
|
||||||
"Tactical RPG": 21725,
|
|
||||||
Idler: 615955,
|
|
||||||
Wargame: 4684,
|
|
||||||
Metroidvania: 1628,
|
|
||||||
Runner: 8666,
|
|
||||||
"Card Battler": 791774,
|
|
||||||
"Souls-like": 29482,
|
|
||||||
CRPG: 4474,
|
|
||||||
"Creature Collector": 916648,
|
|
||||||
"Twin Stick Shooter": 4758,
|
|
||||||
"Match 3": 1665,
|
|
||||||
"Mystery Dungeon": 198631,
|
|
||||||
"Hero Shooter": 620519,
|
|
||||||
"Spectacle fighter": 4777,
|
|
||||||
"Looter Shooter": 353880,
|
|
||||||
Solitaire: 13070,
|
|
||||||
"Combat Racing": 4102,
|
|
||||||
"Action RTS": 1723,
|
|
||||||
Sokoban: 1730,
|
|
||||||
"Trading Card Game": 9271,
|
|
||||||
Typing: 1674,
|
|
||||||
"Boomer Shooter": 1023537,
|
|
||||||
"Traditional Roguelike": 454187,
|
|
||||||
"On-Rails Shooter": 56690,
|
|
||||||
Spelling: 71389,
|
|
||||||
Roguevania: 922563,
|
|
||||||
Singleplayer: 4182,
|
|
||||||
Multiplayer: 3859,
|
|
||||||
"Co-op": 1685,
|
|
||||||
"Online Co-Op": 3843,
|
|
||||||
"Massively Multiplayer": 128,
|
|
||||||
"Local Multiplayer": 7368,
|
|
||||||
"Local Co-Op": 3841,
|
|
||||||
"4 Player Local": 4840,
|
|
||||||
"Co-op Campaign": 4508,
|
|
||||||
"Asynchronous Multiplayer": 17770,
|
|
||||||
"Profile Features Limited": 1003823,
|
|
||||||
"Demo Available": 21491,
|
|
||||||
"Adult Content": 65443,
|
|
||||||
Hentai: 9130,
|
|
||||||
Fighting: 1743,
|
|
||||||
Classic: 1693,
|
|
||||||
Cozy: 97376,
|
|
||||||
"Open World Survival Craft": 1100689,
|
|
||||||
Wholesome: 552282,
|
|
||||||
"Roguelike Deckbuilder": 1091588,
|
|
||||||
Narrative: 7702,
|
|
||||||
Immersive: 3934,
|
|
||||||
"Party Game": 7178,
|
|
||||||
Party: 7108,
|
|
||||||
"Escape Room": 769306,
|
|
||||||
Addictive: 4190,
|
|
||||||
Nostalgia: 14720,
|
|
||||||
Farming: 4520,
|
|
||||||
"Cult Classic": 7782,
|
|
||||||
Spaceships: 4291,
|
|
||||||
"Electronic Music": 61357,
|
|
||||||
Pinball: 6621,
|
|
||||||
"Social Deduction": 745697,
|
|
||||||
Ambient: 29855,
|
|
||||||
Dwarf: 7918,
|
|
||||||
"Job Simulator": 35079,
|
|
||||||
Epic: 3965,
|
|
||||||
"Instrumental Music": 189941,
|
|
||||||
"Jump Scare": 42089,
|
|
||||||
"Boss Rush": 11095,
|
|
||||||
"Rock Music": 337964,
|
|
||||||
"Tile-Matching": 176733,
|
|
||||||
Vikings: 11634,
|
|
||||||
"Extraction Shooter": 1199779,
|
|
||||||
"8-bit Music": 117648,
|
|
||||||
"Well-Written": 8461,
|
|
||||||
Mahjong: 33572,
|
|
||||||
"Shop Keeper": 91114,
|
|
||||||
Electronic: 143739,
|
|
||||||
Birds: 6214,
|
|
||||||
Dice: 7556,
|
|
||||||
Musou: 323922,
|
|
||||||
Fox: 30927,
|
|
||||||
Coding: 42329,
|
|
||||||
Elf: 102530,
|
|
||||||
"Hobby Sim": 1220528,
|
|
||||||
Cricket: 158638,
|
|
||||||
Rugby: 49213,
|
|
||||||
Volleyball: 847164,
|
|
||||||
Snooker: 363767,
|
|
||||||
Reboot: 5941,
|
|
||||||
"Based On A Novel": 3796,
|
|
||||||
"Free to Play": 113,
|
|
||||||
"Early Access": 493,
|
|
||||||
Experimental: 13782,
|
|
||||||
"Software Training": 1445,
|
|
||||||
Minigames: 8093,
|
|
||||||
Remake: 5708,
|
|
||||||
Sequel: 5230,
|
|
||||||
Experience: 9994,
|
|
||||||
Kickstarter: 5153,
|
|
||||||
Crowdfunded: 7113,
|
|
||||||
Benchmark: 5407,
|
|
||||||
"Feature Film": 233824,
|
|
||||||
Difficult: 4026,
|
|
||||||
Competitive: 3878,
|
|
||||||
Unforgiving: 1733,
|
|
||||||
"Turn-Based": 1677,
|
|
||||||
"Replay Value": 4711,
|
|
||||||
"Fast-Paced": 1734,
|
|
||||||
"Real-Time": 4161,
|
|
||||||
"Real-Time with Pause": 7107,
|
|
||||||
"Time Attack": 5390,
|
|
||||||
"Sexual Content": 12095,
|
|
||||||
Nudity: 6650,
|
|
||||||
Mature: 5611,
|
|
||||||
NSFW: 24904,
|
|
||||||
"Character Action Game": 3955,
|
|
||||||
"Villain Protagonist": 11333,
|
|
||||||
"Silent Protagonist": 15954,
|
|
||||||
Chess: 4184,
|
|
||||||
Violent: 4667,
|
|
||||||
Gore: 4345,
|
|
||||||
Blood: 5228,
|
|
||||||
Soundtrack: 7948,
|
|
||||||
"Great Soundtrack": 1756,
|
|
||||||
Music: 1621,
|
|
||||||
"Mouse only": 11123,
|
|
||||||
"Touch-Friendly": 25085,
|
|
||||||
"Intentionally Awkward Controls": 14906,
|
|
||||||
"Voice Control": 27758,
|
|
||||||
Mod: 5348,
|
|
||||||
RPGMaker: 5577,
|
|
||||||
GameMaker: 1649,
|
|
||||||
"Dungeons & Dragons": 14153,
|
|
||||||
"Games Workshop": 5310,
|
|
||||||
TrackIR: 8075,
|
|
||||||
Hardware: 603297,
|
|
||||||
"Steam Machine": 348922,
|
|
||||||
};
|
|
@ -83,6 +83,37 @@ const getSteamGames = async () => {
|
|||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const importDownloadSource = async (url: string) => {
|
||||||
|
const response = await axios.get<z.infer<typeof downloadSourceSchema>>(url);
|
||||||
|
|
||||||
|
const steamGames = await getSteamGames();
|
||||||
|
|
||||||
|
await db.transaction("rw", repacksTable, downloadSourcesTable, async () => {
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
const id = await downloadSourcesTable.add({
|
||||||
|
url,
|
||||||
|
name: response.data.name,
|
||||||
|
etag: response.headers["etag"],
|
||||||
|
status: DownloadSourceStatus.UpToDate,
|
||||||
|
downloadCount: response.data.downloads.length,
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
});
|
||||||
|
|
||||||
|
const downloadSource = await downloadSourcesTable.get(id);
|
||||||
|
|
||||||
|
await addNewDownloads(downloadSource, response.data.downloads, steamGames);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteDownloadSource = async (id: number) => {
|
||||||
|
await db.transaction("rw", repacksTable, downloadSourcesTable, async () => {
|
||||||
|
await repacksTable.where({ downloadSourceId: id }).delete();
|
||||||
|
await downloadSourcesTable.where({ id }).delete();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
self.onmessage = async (event: MessageEvent<Payload>) => {
|
self.onmessage = async (event: MessageEvent<Payload>) => {
|
||||||
const [type, data] = event.data;
|
const [type, data] = event.data;
|
||||||
|
|
||||||
@ -102,10 +133,7 @@ self.onmessage = async (event: MessageEvent<Payload>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type === "DELETE_DOWNLOAD_SOURCE") {
|
if (type === "DELETE_DOWNLOAD_SOURCE") {
|
||||||
await db.transaction("rw", repacksTable, downloadSourcesTable, async () => {
|
await deleteDownloadSource(data);
|
||||||
await repacksTable.where({ downloadSourceId: data }).delete();
|
|
||||||
await downloadSourcesTable.where({ id: data }).delete();
|
|
||||||
});
|
|
||||||
|
|
||||||
const channel = new BroadcastChannel(`download_sources:delete:${data}`);
|
const channel = new BroadcastChannel(`download_sources:delete:${data}`);
|
||||||
|
|
||||||
@ -113,32 +141,7 @@ self.onmessage = async (event: MessageEvent<Payload>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type === "IMPORT_DOWNLOAD_SOURCE") {
|
if (type === "IMPORT_DOWNLOAD_SOURCE") {
|
||||||
const response =
|
await importDownloadSource(data);
|
||||||
await axios.get<z.infer<typeof downloadSourceSchema>>(data);
|
|
||||||
|
|
||||||
const steamGames = await getSteamGames();
|
|
||||||
|
|
||||||
await db.transaction("rw", repacksTable, downloadSourcesTable, async () => {
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
const id = await downloadSourcesTable.add({
|
|
||||||
url: data,
|
|
||||||
name: response.data.name,
|
|
||||||
etag: response.headers["etag"],
|
|
||||||
status: DownloadSourceStatus.UpToDate,
|
|
||||||
downloadCount: response.data.downloads.length,
|
|
||||||
createdAt: now,
|
|
||||||
updatedAt: now,
|
|
||||||
});
|
|
||||||
|
|
||||||
const downloadSource = await downloadSourcesTable.get(id);
|
|
||||||
|
|
||||||
await addNewDownloads(
|
|
||||||
downloadSource,
|
|
||||||
response.data.downloads,
|
|
||||||
steamGames
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const channel = new BroadcastChannel(`download_sources:import:${data}`);
|
const channel = new BroadcastChannel(`download_sources:import:${data}`);
|
||||||
channel.postMessage(true);
|
channel.postMessage(true);
|
||||||
@ -153,6 +156,12 @@ self.onmessage = async (event: MessageEvent<Payload>) => {
|
|||||||
const existingRepacks = await repacksTable.toArray();
|
const existingRepacks = await repacksTable.toArray();
|
||||||
|
|
||||||
for (const downloadSource of downloadSources) {
|
for (const downloadSource of downloadSources) {
|
||||||
|
if (!downloadSource.fingerprint) {
|
||||||
|
await deleteDownloadSource(downloadSource.id);
|
||||||
|
await importDownloadSource(downloadSource.url);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const headers = new AxiosHeaders();
|
const headers = new AxiosHeaders();
|
||||||
|
|
||||||
if (downloadSource.etag) {
|
if (downloadSource.etag) {
|
||||||
|
Loading…
Reference in New Issue
Block a user