diff --git a/web/i18n/en/save.json b/web/i18n/en/save.json
index 83d260d2..71b6f4a4 100644
--- a/web/i18n/en/save.json
+++ b/web/i18n/en/save.json
@@ -6,5 +6,7 @@
"mute": "mute",
"input.placeholder": "paste the link here",
"terms.note.agreement": "by continuing, you agree to",
- "terms.note.link": "terms and ethics of use"
+ "terms.note.link": "terms and ethics of use",
+ "services.title": "supported services",
+ "services.disclaimer": "cobalt is not affiliated with any of the supported services listed above."
}
diff --git a/web/src/components/save/SupportedServices.svelte b/web/src/components/save/SupportedServices.svelte
new file mode 100644
index 00000000..ed64953b
--- /dev/null
+++ b/web/src/components/save/SupportedServices.svelte
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
+ {#if loaded}
+ {#each services as service}
+
{service}
+ {/each}
+ {:else}
+ {#each { length: 17 } as _}
+
+ {/each}
+ {/if}
+
+
+ {$t("save.services.disclaimer")}
+
+
+
+
+
diff --git a/web/src/lib/api/server-info.ts b/web/src/lib/api/server-info.ts
new file mode 100644
index 00000000..0c67f22d
--- /dev/null
+++ b/web/src/lib/api/server-info.ts
@@ -0,0 +1,46 @@
+import { get, writable } from "svelte/store";
+import { currentApiURL } from "$lib/api/api-url";
+
+import type { CobaltServerInfoResponse, CobaltErrorResponse, CobaltServerInfo } from "$lib/types/api";
+
+export const cachedInfo = writable();
+
+const request = async () => {
+ const apiEndpoint = `${currentApiURL()}/`;
+
+ const response: CobaltServerInfoResponse = await fetch(apiEndpoint, {
+ redirect: "manual",
+ signal: AbortSignal.timeout(10000),
+ })
+ .then(r => r.json())
+ .catch((e) => {
+ if (e?.message?.includes("timed out")) {
+ return {
+ status: "error",
+ error: {
+ code: "error.api.timed_out"
+ }
+ } as CobaltErrorResponse
+ }
+ });
+
+ return response;
+}
+
+export const getServerInfo = async () => {
+ const cache = get(cachedInfo);
+ if (cache) return true;
+
+ const freshInfo = await request();
+
+ if (!freshInfo || !("cobalt" in freshInfo)) {
+ return false;
+ }
+
+ if (!("status" in freshInfo)) {
+ cachedInfo.set(freshInfo);
+ return true;
+ }
+
+ return false;
+}
diff --git a/web/src/lib/types/api.ts b/web/src/lib/types/api.ts
index 08bb3148..7ffae099 100644
--- a/web/src/lib/types/api.ts
+++ b/web/src/lib/types/api.ts
@@ -43,7 +43,23 @@ export type CobaltSession = {
exp: number,
}
+export type CobaltServerInfo = {
+ cobalt: {
+ version: string,
+ url: string,
+ startTime: string,
+ durationLimit: number,
+ services: string[]
+ },
+ git: {
+ branch: string,
+ commit: string,
+ remote: string,
+ }
+}
+
export type CobaltSessionResponse = CobaltSession | CobaltErrorResponse;
+export type CobaltServerInfoResponse = CobaltServerInfo | CobaltErrorResponse;
export type CobaltAPIResponse = CobaltErrorResponse
| CobaltPickerResponse
diff --git a/web/src/routes/+page.svelte b/web/src/routes/+page.svelte
index eebcdec3..4e348f3a 100644
--- a/web/src/routes/+page.svelte
+++ b/web/src/routes/+page.svelte
@@ -3,6 +3,7 @@
import Omnibox from "$components/save/Omnibox.svelte";
import Meowbalt from "$components/misc/Meowbalt.svelte";
+ import SupportedServices from "$components/save/SupportedServices.svelte";
@@ -10,6 +11,7 @@
+