97 lines
2.8 KiB
TypeScript
97 lines
2.8 KiB
TypeScript
const MANAGED_CATALOG_CACHE_PREFIX = 'radioplayer-managed-catalog-';
|
|
const MANAGED_CATALOG_SOURCE_HEADER = 'x-radioplayer-managed-source';
|
|
|
|
export type ManagedCatalogSource = 'remote' | 'cached-remote' | 'bundled' | 'unknown';
|
|
|
|
let lastManagedCatalogSource: ManagedCatalogSource = 'unknown';
|
|
|
|
type ManagedCatalogEnvelope = {
|
|
stations?: unknown;
|
|
};
|
|
|
|
async function loadManagedCatalogFromCache(catalogUrl: string): Promise<Response | null> {
|
|
if (typeof window === 'undefined' || !('caches' in window)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const cacheKeys = await caches.keys();
|
|
const managedCacheName = cacheKeys.find((cacheName) => cacheName.startsWith(MANAGED_CATALOG_CACHE_PREFIX));
|
|
if (!managedCacheName) {
|
|
return null;
|
|
}
|
|
|
|
const managedCache = await caches.open(managedCacheName);
|
|
return await managedCache.match(catalogUrl) ?? null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function normalizeManagedStationsPayload(payload: unknown): unknown[] {
|
|
if (Array.isArray(payload)) {
|
|
return payload;
|
|
}
|
|
|
|
if (payload && typeof payload === 'object' && Array.isArray((payload as ManagedCatalogEnvelope).stations)) {
|
|
return (payload as ManagedCatalogEnvelope).stations as unknown[];
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
function setLastManagedCatalogSource(source: ManagedCatalogSource) {
|
|
lastManagedCatalogSource = source;
|
|
}
|
|
|
|
export function getLastManagedCatalogSource(): ManagedCatalogSource {
|
|
return lastManagedCatalogSource;
|
|
}
|
|
|
|
export async function loadManagedStations(): Promise<unknown[]> {
|
|
const remoteCatalogUrl = `${import.meta.env.BASE_URL}api/managed-stations.json`;
|
|
const bundledCatalogUrl = `${import.meta.env.BASE_URL}stations.json`;
|
|
const hasServiceWorkerController = typeof navigator !== 'undefined'
|
|
&& 'serviceWorker' in navigator
|
|
&& Boolean(navigator.serviceWorker.controller);
|
|
|
|
let response = null;
|
|
|
|
if (hasServiceWorkerController) {
|
|
response = await loadManagedCatalogFromCache(remoteCatalogUrl);
|
|
if (response?.ok) {
|
|
setLastManagedCatalogSource('cached-remote');
|
|
}
|
|
}
|
|
|
|
if (!response?.ok) {
|
|
try {
|
|
response = await fetch(remoteCatalogUrl);
|
|
if (response?.ok) {
|
|
const responseSource = response.headers.get(MANAGED_CATALOG_SOURCE_HEADER);
|
|
setLastManagedCatalogSource(
|
|
responseSource === 'remote' || responseSource === 'cached-remote' || responseSource === 'bundled'
|
|
? responseSource
|
|
: 'remote',
|
|
);
|
|
}
|
|
} catch {
|
|
response = null;
|
|
}
|
|
}
|
|
|
|
if (!response?.ok) {
|
|
response = await fetch(bundledCatalogUrl);
|
|
if (response?.ok) {
|
|
setLastManagedCatalogSource('bundled');
|
|
}
|
|
}
|
|
|
|
if (!response.ok) {
|
|
setLastManagedCatalogSource('unknown');
|
|
throw new Error(`Failed to load managed stations: ${response.status}`);
|
|
}
|
|
|
|
const stations = await response.json();
|
|
return normalizeManagedStationsPayload(stations);
|
|
} |