mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-07-26 17:54:44 +10:00
Depot API & v4 (#298)
* feat: nginx + torrential basics & services system * fix: lint + i18n * fix: update torrential to remove openssl * feat: add torrential to Docker build * feat: move to self hosted runner * fix: move off self-hosted runner * fix: update nginx.conf * feat: torrential cache invalidation * fix: update torrential for cache invalidation * feat: integrity check task * fix: lint * feat: move to version ids * fix: client fixes and client-side checks * feat: new depot apis and version id fixes * feat: update torrential * feat: droplet bump and remove unsafe update functions * fix: lint * feat: v4 featureset: emulators, multi-launch commands * fix: lint * fix: mobile ui for game editor * feat: launch options * fix: lint * fix: remove axios, use $fetch * feat: metadata and task api improvements * feat: task actions * fix: slight styling issue * feat: fix style and lints * feat: totp backend routes * feat: oidc groups * fix: update drop-base * feat: creation of passkeys & totp * feat: totp signin * feat: webauthn mfa/signin * feat: launch selecting ui * fix: manually running tasks * feat: update add company game modal to use new SelectorGame * feat: executor selector * fix(docker): update rust to rust nightly for torrential build (#305) * feat: new version ui * feat: move package lookup to build time to allow for deno dev * fix: lint * feat: localisation cleanup * feat: apply localisation cleanup * feat: potential i18n refactor logic * feat: remove args from commands * fix: lint * fix: lockfile --------- Co-authored-by: Aden Lindsay <140392385+AdenMGB@users.noreply.github.com>
This commit is contained in:
@@ -9,8 +9,8 @@ import type {
|
||||
GameMetadataRating,
|
||||
} from "./types";
|
||||
import type { TaskRunContext } from "../tasks";
|
||||
import axios from "axios";
|
||||
import * as jdenticon from "jdenticon";
|
||||
import { load } from "cheerio";
|
||||
|
||||
/**
|
||||
* Note: The Steam API is largely undocumented.
|
||||
@@ -188,19 +188,15 @@ export class SteamProvider implements MetadataProvider {
|
||||
}
|
||||
|
||||
async search(query: string): Promise<GameMetadataSearchResult[]> {
|
||||
const response = await axios.get<SteamSearchStub[]>(
|
||||
const response = await $fetch<SteamSearchStub[]>(
|
||||
`https://steamcommunity.com/actions/SearchApps/${query}`,
|
||||
);
|
||||
|
||||
if (
|
||||
response.status !== 200 ||
|
||||
!response.data ||
|
||||
response.data.length === 0
|
||||
) {
|
||||
if (!response || response.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result: GameMetadataSearchResult[] = response.data.map((item) => ({
|
||||
const result: GameMetadataSearchResult[] = response.map((item) => ({
|
||||
id: item.appid,
|
||||
name: item.name,
|
||||
icon: item.icon || "",
|
||||
@@ -208,7 +204,7 @@ export class SteamProvider implements MetadataProvider {
|
||||
year: 0,
|
||||
}));
|
||||
|
||||
const ids = response.data.map((i) => i.appid);
|
||||
const ids = response.map((i) => i.appid);
|
||||
|
||||
const detailsResponse = await this._fetchGameDetails(ids, {
|
||||
include_basic_info: true,
|
||||
@@ -235,7 +231,7 @@ export class SteamProvider implements MetadataProvider {
|
||||
}
|
||||
|
||||
async fetchGame(
|
||||
{ id, publisher, developer, createObject }: _FetchGameMetadataParams,
|
||||
{ id, company, createObject }: _FetchGameMetadataParams,
|
||||
context?: TaskRunContext,
|
||||
): Promise<GameMetadata> {
|
||||
context?.logger.info(`Starting Steam metadata fetch for game ID: ${id}`);
|
||||
@@ -294,38 +290,66 @@ export class SteamProvider implements MetadataProvider {
|
||||
context?.progress(70);
|
||||
|
||||
context?.logger.info("Processing publishers and developers...");
|
||||
const storePage = await $fetch<string>(
|
||||
`https://store.steampowered.com/app/${id}/`,
|
||||
);
|
||||
const $ = load(storePage);
|
||||
|
||||
const companyLinks = $("a")
|
||||
.toArray()
|
||||
.filter(
|
||||
(v) =>
|
||||
v.attribs["href"]?.startsWith(
|
||||
"https://store.steampowered.com/developer/",
|
||||
) ||
|
||||
v.attribs["href"]?.startsWith(
|
||||
"https://store.steampowered.com/publisher/",
|
||||
),
|
||||
)
|
||||
.map((v) => v.attribs.href);
|
||||
|
||||
const companies: {
|
||||
[key: string]: {
|
||||
pub: boolean;
|
||||
dev: boolean;
|
||||
};
|
||||
} = {};
|
||||
|
||||
companyLinks.forEach((v) => {
|
||||
const [type, name] = v
|
||||
.substring("https://store.steampowered.com/".length, v.indexOf("?"))
|
||||
.split("/");
|
||||
|
||||
companies[name] ??= { pub: false, dev: false };
|
||||
switch (type) {
|
||||
case "publisher":
|
||||
companies[name].pub = true;
|
||||
break;
|
||||
case "developer":
|
||||
companies[name].dev = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const publishers = [];
|
||||
const publisherNames = currentGame.basic_info.publishers || [];
|
||||
context?.logger.info(
|
||||
`Found ${publisherNames.length} publisher(s) to process`,
|
||||
);
|
||||
|
||||
for (const pub of publisherNames) {
|
||||
context?.logger.info(`Processing publisher: "${pub.name}"`);
|
||||
const comp = await publisher(pub.name);
|
||||
if (!comp) {
|
||||
context?.logger.warn(`Failed to import publisher "${pub.name}"`);
|
||||
continue;
|
||||
}
|
||||
publishers.push(comp);
|
||||
context?.logger.info(`Successfully imported publisher: "${pub.name}"`);
|
||||
}
|
||||
|
||||
const developers = [];
|
||||
const developerNames = currentGame.basic_info.developers || [];
|
||||
context?.logger.info(
|
||||
`Found ${developerNames.length} developer(s) to process`,
|
||||
);
|
||||
|
||||
for (const dev of developerNames) {
|
||||
context?.logger.info(`Processing developer: "${dev.name}"`);
|
||||
const comp = await developer(dev.name);
|
||||
if (!comp) {
|
||||
context?.logger.warn(`Failed to import developer "${dev.name}"`);
|
||||
continue;
|
||||
for (const [companyName, types] of Object.entries(companies)) {
|
||||
context?.logger.info(`Processing company: "${companyName}"`);
|
||||
const comp = await company(companyName);
|
||||
|
||||
if (types.dev) {
|
||||
developers.push(comp);
|
||||
context?.logger.info(
|
||||
`Successfully imported developer: "${companyName}"`,
|
||||
);
|
||||
}
|
||||
if (types.pub) {
|
||||
publishers.push(comp);
|
||||
context?.logger.info(
|
||||
`Successfully imported publisher: "${companyName}"`,
|
||||
);
|
||||
}
|
||||
developers.push(comp);
|
||||
context?.logger.info(`Successfully imported developer: "${dev.name}"`);
|
||||
}
|
||||
|
||||
context?.logger.info(
|
||||
@@ -425,23 +449,19 @@ export class SteamProvider implements MetadataProvider {
|
||||
l: "english",
|
||||
});
|
||||
|
||||
const response = await axios.get(
|
||||
`https://store.steampowered.com/developer/${query.replaceAll(" ", "")}/?${searchParams.toString()}`,
|
||||
{
|
||||
maxRedirects: 0,
|
||||
},
|
||||
);
|
||||
const url = `https://store.steampowered.com/developer/${encodeURIComponent(query)}/?${searchParams.toString()}`;
|
||||
const response = await $fetch<string>(url);
|
||||
|
||||
if (response.status !== 200 || !response.data) {
|
||||
if (!response) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const html = response.data;
|
||||
const html = response;
|
||||
|
||||
// Extract metadata from HTML meta tags
|
||||
const metadata = this._extractMetaTagsFromHtml(html);
|
||||
|
||||
if (!metadata.title) {
|
||||
if (!metadata.title || metadata.title == "Steam Search") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -623,14 +643,12 @@ export class SteamProvider implements MetadataProvider {
|
||||
}),
|
||||
});
|
||||
|
||||
const request = await axios.get<SteamAppDetailsPackage>(
|
||||
const request = await $fetch<SteamAppDetailsPackage>(
|
||||
`https://api.steampowered.com/IStoreBrowseService/GetItems/v1/?${searchParams.toString()}`,
|
||||
);
|
||||
|
||||
if (request.status !== 200) return [];
|
||||
|
||||
const result = [];
|
||||
const storeItems = request.data?.response?.store_items ?? [];
|
||||
const storeItems = request.response?.store_items ?? [];
|
||||
|
||||
for (const item of storeItems) {
|
||||
if (item.success !== 1) continue;
|
||||
@@ -723,14 +741,14 @@ export class SteamProvider implements MetadataProvider {
|
||||
language,
|
||||
});
|
||||
|
||||
const request = await axios.get<SteamTagsPackage>(
|
||||
const request = await $fetch<SteamTagsPackage>(
|
||||
`https://api.steampowered.com/IStoreService/GetTagList/v1/?${searchParams.toString()}`,
|
||||
);
|
||||
|
||||
if (request.status !== 200 || !request.data.response?.tags) return [];
|
||||
if (!request.response?.tags) return [];
|
||||
|
||||
const tagMap = new Map<number, string>();
|
||||
for (const tag of request.data.response.tags) {
|
||||
for (const tag of request.response.tags) {
|
||||
tagMap.set(tag.tagid, tag.name);
|
||||
}
|
||||
|
||||
@@ -756,15 +774,11 @@ export class SteamProvider implements MetadataProvider {
|
||||
l: language,
|
||||
});
|
||||
|
||||
const request = await axios.get<SteamWebAppDetailsPackage>(
|
||||
const request = await $fetch<SteamWebAppDetailsPackage>(
|
||||
`https://store.steampowered.com/api/appdetails?${searchParams.toString()}`,
|
||||
);
|
||||
|
||||
if (request.status !== 200) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const appData = request.data[appid]?.data;
|
||||
const appData = request[appid]?.data;
|
||||
if (!appData) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user