82 lines
2.7 KiB
TypeScript
82 lines
2.7 KiB
TypeScript
// T5DE's client releases, from git.t5de.dev's API (the t5de repository's releases, which its CI publishes). Pages
|
|
// using them are regenerated every few minutes; if Gitea can't be reached, they link to its releases page instead.
|
|
|
|
const repo = "https://git.t5de.dev/api/v1/repos/t5de/t5de";
|
|
export const releasesPage = "https://git.t5de.dev/t5de/t5de/releases";
|
|
const revalidate = 300;
|
|
|
|
export type Release = {
|
|
tag: string;
|
|
version: string;
|
|
date: string;
|
|
nightly: boolean;
|
|
url: string;
|
|
body: string;
|
|
setup: { url: string; size: number } | null;
|
|
sha256: string | null;
|
|
imvu: string | null;
|
|
};
|
|
|
|
type GiteaRelease = {
|
|
tag_name: string;
|
|
name: string;
|
|
body: string;
|
|
prerelease: boolean;
|
|
draft: boolean;
|
|
published_at: string;
|
|
html_url: string;
|
|
assets: { name: string; size: number; browser_download_url: string }[];
|
|
};
|
|
|
|
function parse(r: GiteaRelease): Release {
|
|
const nightly = r.tag_name === "nightly";
|
|
const setup = r.assets.find((a) => a.name === "T5DE-Setup.exe");
|
|
return {
|
|
tag: r.tag_name,
|
|
// Releases are tagged vX.Y.Z; the nightly's version is in its name ("Nightly <version>").
|
|
version: nightly ? r.name.replace(/^Nightly\s+/, "") : r.tag_name.replace(/^v/, ""),
|
|
date: r.published_at,
|
|
nightly,
|
|
url: r.html_url,
|
|
body: r.body,
|
|
setup: setup ? { url: setup.browser_download_url, size: setup.size } : null,
|
|
sha256: /`T5DE-Setup\.exe` sha256: `([0-9a-f]{64})`/.exec(r.body)?.[1] ?? null,
|
|
imvu: /IMVU Classic \*\*([\d.]+)\*\*/.exec(r.body)?.[1] ?? null,
|
|
};
|
|
}
|
|
|
|
async function get<T>(path: string): Promise<T | null> {
|
|
try {
|
|
const res = await fetch(repo + path, { next: { revalidate } });
|
|
return res.ok ? ((await res.json()) as T) : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** The latest release (not the nightly), or null. */
|
|
export async function latestRelease(): Promise<Release | null> {
|
|
const r = await get<GiteaRelease>("/releases/latest");
|
|
return r ? parse(r) : null;
|
|
}
|
|
|
|
/** The current nightly build, or null. */
|
|
export async function nightlyRelease(): Promise<Release | null> {
|
|
const r = await get<GiteaRelease>("/releases/tags/nightly");
|
|
return r ? parse(r) : null;
|
|
}
|
|
|
|
/** Every release, newest first, without the nightly; null if Gitea can't be reached. */
|
|
export async function allReleases(): Promise<Release[] | null> {
|
|
const rs = await get<GiteaRelease[]>("/releases?limit=50&pre-release=false&draft=false");
|
|
return rs ? rs.filter((r) => r.tag_name !== "nightly" && !r.draft).map(parse) : null;
|
|
}
|
|
|
|
export function formatSize(bytes: number): string {
|
|
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
}
|
|
|
|
export function formatDate(iso: string): string {
|
|
return new Date(iso).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", timeZone: "UTC" });
|
|
}
|