Files
RadioPlayerWeb/src/radio/loadManagedStations.ts

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);
}