.unified status check

This commit is contained in:
afkarxyz
2026-04-14 07:14:18 +07:00
parent c0c1348c3f
commit f75081780e
5 changed files with 147 additions and 26 deletions
+1 -1
View File
@@ -11,6 +11,6 @@ export function useApiStatus() {
return {
...state,
sources: API_SOURCES,
refreshAll: checkAllApiStatuses,
refreshAll: () => checkAllApiStatuses(true),
};
}
+73 -25
View File
@@ -1,4 +1,4 @@
import { CheckAPIStatus } from "../../wailsjs/go/main/App";
import { CheckAPIStatus, FetchUnifiedAPIStatus } from "../../wailsjs/go/main/App";
import { CHECK_TIMEOUT_MS, withTimeout } from "@/lib/async-timeout";
export type ApiCheckStatus = "checking" | "online" | "offline" | "idle";
export interface ApiSource {
@@ -32,6 +32,15 @@ let apiStatusState: ApiStatusState = {
};
let activeCheckAll: Promise<void> | null = null;
const listeners = new Set<() => void>();
type SpotiFLACUnifiedStatusResponse = {
tidal?: string;
qobuz_a?: string;
qobuz_b?: string;
qobuz_c?: string;
amazon?: string;
lrclib?: string;
};
function emitApiStatusChange() {
for (const listener of listeners) {
listener();
@@ -41,32 +50,37 @@ function setApiStatusState(updater: (current: ApiStatusState) => ApiStatusState)
apiStatusState = updater(apiStatusState);
emitApiStatusChange();
}
async function checkSingleApiStatus(source: ApiSource): Promise<void> {
setApiStatusState((current) => ({
...current,
function statusFromUnifiedValue(value: string | undefined): ApiCheckStatus {
return value === "up" ? "online" : "offline";
}
async function fetchUnifiedStatuses(forceRefresh: boolean): Promise<Pick<ApiStatusState, "statuses">> {
const response = await FetchUnifiedAPIStatus(forceRefresh);
const payload = JSON.parse(response) as SpotiFLACUnifiedStatusResponse;
const tidalStatus = statusFromUnifiedValue(payload.tidal);
return {
statuses: {
...current.statuses,
[source.id]: "checking",
tidal1: tidalStatus,
tidal2: tidalStatus,
tidal3: tidalStatus,
tidal4: tidalStatus,
tidal5: tidalStatus,
tidal6: tidalStatus,
tidal7: tidalStatus,
qobuz1: statusFromUnifiedValue(payload.qobuz_a),
qobuz2: statusFromUnifiedValue(payload.qobuz_b),
qobuz3: statusFromUnifiedValue(payload.qobuz_c),
amazon1: statusFromUnifiedValue(payload.amazon),
lrclib: statusFromUnifiedValue(payload.lrclib),
},
}));
};
}
async function checkMusicBrainzStatus(): Promise<ApiCheckStatus> {
try {
const isOnline = await withTimeout(CheckAPIStatus(source.type, source.url), CHECK_TIMEOUT_MS, `API status check timed out after 10 seconds for ${source.url}`);
setApiStatusState((current) => ({
...current,
statuses: {
...current.statuses,
[source.id]: isOnline ? "online" : "offline",
},
}));
const isOnline = await withTimeout(CheckAPIStatus("musicbrainz", "https://musicbrainz.org"), CHECK_TIMEOUT_MS, "API status check timed out after 10 seconds for MusicBrainz");
return isOnline ? "online" : "offline";
}
catch {
setApiStatusState((current) => ({
...current,
statuses: {
...current.statuses,
[source.id]: "offline",
},
}));
return "offline";
}
}
export function getApiStatusState(): ApiStatusState {
@@ -86,20 +100,54 @@ export function hasApiStatusResults(): boolean {
}
export function ensureApiStatusCheckStarted(): void {
if (!activeCheckAll && !hasApiStatusResults()) {
void checkAllApiStatuses();
void checkAllApiStatuses(false);
}
}
export async function checkAllApiStatuses(): Promise<void> {
export async function checkAllApiStatuses(forceRefresh: boolean = false): Promise<void> {
if (activeCheckAll) {
return activeCheckAll;
}
activeCheckAll = (async () => {
const checkingStatuses = Object.fromEntries(API_SOURCES.map((source) => [source.id, "checking" as ApiCheckStatus]));
setApiStatusState((current) => ({
...current,
isCheckingAll: true,
statuses: {
...current.statuses,
...checkingStatuses,
},
}));
try {
await Promise.allSettled(API_SOURCES.map((source) => checkSingleApiStatus(source)));
const [unifiedResult, musicBrainzStatus] = await Promise.allSettled([
withTimeout(fetchUnifiedStatuses(forceRefresh), CHECK_TIMEOUT_MS, "Unified SpotiFLAC status check timed out after 10 seconds"),
checkMusicBrainzStatus(),
]);
setApiStatusState((current) => {
const nextStatuses = { ...current.statuses };
if (unifiedResult.status === "fulfilled") {
Object.assign(nextStatuses, unifiedResult.value.statuses);
}
else {
nextStatuses.tidal1 = "offline";
nextStatuses.tidal2 = "offline";
nextStatuses.tidal3 = "offline";
nextStatuses.tidal4 = "offline";
nextStatuses.tidal5 = "offline";
nextStatuses.tidal6 = "offline";
nextStatuses.tidal7 = "offline";
nextStatuses.qobuz1 = "offline";
nextStatuses.qobuz2 = "offline";
nextStatuses.qobuz3 = "offline";
nextStatuses.amazon1 = "offline";
nextStatuses.lrclib = "offline";
}
nextStatuses.musicbrainz =
musicBrainzStatus.status === "fulfilled" ? musicBrainzStatus.value : "offline";
return {
...current,
statuses: nextStatuses,
};
});
}
finally {
setApiStatusState((current) => ({
+2
View File
@@ -53,6 +53,8 @@ export function DownloadTrack(arg1:main.DownloadRequest):Promise<main.DownloadRe
export function ExportFailedDownloads():Promise<string>;
export function FetchUnifiedAPIStatus(arg1:boolean):Promise<string>;
export function GetBrewPath():Promise<string>;
export function GetConfigPath():Promise<string>;
+4
View File
@@ -102,6 +102,10 @@ export function ExportFailedDownloads() {
return window['go']['main']['App']['ExportFailedDownloads']();
}
export function FetchUnifiedAPIStatus(arg1) {
return window['go']['main']['App']['FetchUnifiedAPIStatus'](arg1);
}
export function GetBrewPath() {
return window['go']['main']['App']['GetBrewPath']();
}