From a2a3bcd4f874ebe4d529524392b8e6d09ea0b0c5 Mon Sep 17 00:00:00 2001 From: O2theC <25ocarter@gmail.com> Date: Thu, 12 Dec 2024 17:56:40 -0500 Subject: [PATCH] adjusted function input for getting latest mod version and adding chunking to not hit request limits --- packages/lib/src/ModStore.ts | 153 +++++++++++++++-------------------- 1 file changed, 64 insertions(+), 89 deletions(-) diff --git a/packages/lib/src/ModStore.ts b/packages/lib/src/ModStore.ts index 979df95d..c552690f 100644 --- a/packages/lib/src/ModStore.ts +++ b/packages/lib/src/ModStore.ts @@ -2,7 +2,7 @@ import fs from "fs-extra"; import path from "path"; import { Static, StringContentEncodingOption, Type } from "@sinclair/typebox"; import { logger } from "./logging"; -import { ModInfo } from "./data"; +import { integerModVersion, ModInfo } from "./data"; import * as libSchema from "./schema"; import TypedEventEmitter from "./TypedEventEmitter"; import { safeOutputFile } from "./file_ops"; @@ -12,56 +12,36 @@ export interface ModStoreEvents { change: (mod: ModInfo) => void; } +interface ModDownloadRequest { + name: string; + version?: string; + sha1?: string; +} + interface ModRelease { download_url: string, - // /download/maraxsis/670de301df21001f6147fd1c -- append to mods.factorio.com to download file, auth required file_name: string, - // maraxsis_1.8.0.zip -- the file name of the downloaded file ^^ - info_json: { - dependencies?: Array, - factorio_version: string; - }; - released_at: string, - // 2024-10-15T03:35:29.892000Z -- (ISO 8601) - sha1: string, - // e227ee560d93485c7f215c9451eaa9cc38d81c98 - version: string; // 1.8.0 -- just a normal version string + info_json: { factorio_version: string; }, + releasted_at: string, // ISO 8601 + version: string, + sha1: string; } -interface ModImage { - id: string, - thumbnail: string, - url: string; -} - -interface ModLicense { - description: string, - id: string, - name: string, - title: string, - url: string; -} interface ModDetails { - category: string, // content -- what mod catogory is this apart of, a mod can only be apart of 1 category - download_count: number, // 3653 -- how many downloads does the mod have as a number - name: string, // maraxsis -- the name of the mod - owner: string, // notnotmelon -- the name of the author of the mod - releases: Array, // an array of all releases and version of the mod - score: number, // 1054.8333333333333 -- it's prob used for sorting purposes - summary: string, // the mod description - thumbnail: string, // append path to assets-mods.factorio.com to get full url to thumbnail file - title: string, // the full title of the mod, shown on the mod page - changelog?: string, - created_at?: string, - description?: string, - homepage?: string, - images?: Array, - license?: ModLicense, - source_url?: string, - tags?: Array, - updated_at?: string, - deprecated?: boolean; + name: string, + title: string, + owner: string, + summary: string, + downloads_count: number, + category: string, + score: number, + releases: Array, +} + +interface ModsInfoResponse { + pagination: any, + results: Array, } export default class ModStore extends TypedEventEmitter { @@ -188,54 +168,49 @@ export default class ModStore extends TypedEventEmitter { - const modDetails: ModDetails = await ( - await fetch(`https://mods.factorio.com/api/mods/${modName}`) - ).json() as ModDetails; - return modDetails.releases[modDetails.releases.length].version; + static getLatestVersionFromReleases(releases: Array): string | undefined { + let latestVersion = "0.0.0"; + releases.forEach((release) => { + if (integerModVersion(latestVersion) < integerModVersion(release.version)) { + latestVersion = release.version; + } + }); + if (latestVersion === "0.0.0") { + return undefined; + } + return latestVersion; } - // gets the latest version of the mod provided as a string - static async fetchLatestVersionModInfo(modName: string): Promise { - const modDetails: ModDetails = await ( - await fetch(`https://mods.factorio.com/api/mods/${modName}/full`) - ).json() as ModDetails; - const latestRelease: ModRelease = modDetails.releases[modDetails.releases.length]; - let modInfo = new ModInfo(); - const info_json = latestRelease.info_json; - - // info.json fields - if (modDetails.name) { modInfo.name = modDetails.name; } - if (latestRelease.version) { modInfo.version = latestRelease.version; } - if (modDetails.title) { modInfo.title = modDetails.title; } - if (modDetails.owner) { modInfo.author = modDetails.owner; } - if (modDetails.homepage) { modInfo.homepage = modDetails.homepage; } - if (modDetails.description) { modInfo.description = modDetails.description; } - if (info_json.factorio_version) { modInfo.factorioVersion = info_json.factorio_version; } - if (info_json.dependencies) { modInfo.dependencies = info_json.dependencies; } - - const infoJsonSchema = Type.Object({ - "name": Type.String(), - "version": Type.String(), - "title": Type.String(), - "author": Type.String(), - "contact": Type.Optional(Type.String()), - "homepage": Type.Optional(Type.String()), - "description": Type.Optional(Type.String()), - "factorio_version": Type.Optional(Type.String()), - "dependencies": Type.Optional(Type.Array(Type.String())), + static async getLatestVersionsChunk(modNames: Array): Promise<{ [key: string]: string | undefined; }> { + const url = new URL("https://mods.factorio.com/api/mods"); + url.searchParams.set("page_size", "max"); + const response = await fetch(url, { + method: "POST", + body: new URLSearchParams({ namelist: modNames }), + }); + if (response.status !== 200) { + throw Error(`Fetch: ${url} returned ${response.status} ${response.statusText}`); + } + const mods = (await response.json() as ModsInfoResponse).results; + const versions: { [key: string]: string | undefined; } = {}; + mods.forEach((mod) => { + const modName = mod.name; + let latestVersion = ModStore.getLatestVersionFromReleases(mod.releases); + versions[modName] = latestVersion; }); - const validateInfo = libSchema.compile>(infoJsonSchema as any); - - const valid = validateInfo(modInfo); - - if (valid) { - return modInfo; - } - - return modInfo; - + return versions; } + + static async getLatestVersions(modNames: Array): Promise<{ [key: string]: string | undefined; }> { + const chunkSize = 500; + const chunks: Array<{ [key: string]: string | undefined; }> = []; + for (let i = 0; i < modNames.length; i += chunkSize) { + chunks.push(await ModStore.getLatestVersionsChunk(modNames.slice(i, i + chunkSize))); + } + const versions = chunks.reduce((acc, curr) => ({ ...acc, ...curr }), {}); + return versions; + } + + }