Spaces:
Sleeping
Sleeping
| import { useState, useEffect, useCallback } from 'react' | |
| import { isCached, cacheStem } from '../utils/presetCache' | |
| const STEM_NAMES = ['guitar', 'drums', 'bass', 'synth', 'click_record'] | |
| /** | |
| * Checks IndexedDB for cached presets on mount, then background-fetches | |
| * any that are missing using the stateless /api/preset-stem endpoint. | |
| * | |
| * Returns cacheStatus: { [presetName]: 'cached' | 'loading' | 'uncached' } | |
| */ | |
| export function usePresetCache() { | |
| const [cacheStatus, setCacheStatus] = useState({}) | |
| const prefetchPreset = useCallback(async (presetName) => { | |
| setCacheStatus(prev => ({ ...prev, [presetName]: 'loading' })) | |
| try { | |
| const results = await Promise.all( | |
| STEM_NAMES.map(async (stemName) => { | |
| const response = await fetch(`/api/preset-stem/${presetName}/${stemName}`) | |
| if (!response.ok) return false | |
| const sampleRate = parseInt(response.headers.get('X-Sample-Rate')) | |
| const numChannels = parseInt(response.headers.get('X-Channels')) | |
| const numFrames = parseInt(response.headers.get('X-Frames')) | |
| const bytes = await response.arrayBuffer() | |
| await cacheStem(presetName, stemName, { bytes, sampleRate, numChannels, numFrames }) | |
| return true | |
| }) | |
| ) | |
| const allOk = results.every(Boolean) | |
| setCacheStatus(prev => ({ ...prev, [presetName]: allOk ? 'cached' : 'uncached' })) | |
| } catch { | |
| setCacheStatus(prev => ({ ...prev, [presetName]: 'uncached' })) | |
| } | |
| }, []) | |
| useEffect(() => { | |
| let cancelled = false | |
| async function init() { | |
| try { | |
| const res = await fetch('/api/presets') | |
| const { presets } = await res.json() | |
| if (cancelled || !presets?.length) return | |
| // Check which presets are already in IndexedDB | |
| const statuses = {} | |
| await Promise.all( | |
| presets.map(async (name) => { | |
| statuses[name] = (await isCached(name, STEM_NAMES)) ? 'cached' : 'uncached' | |
| }) | |
| ) | |
| if (cancelled) return | |
| setCacheStatus(statuses) | |
| // Background-fetch uncached presets one at a time to avoid saturating bandwidth | |
| for (const name of presets) { | |
| if (cancelled) break | |
| if (statuses[name] === 'uncached') { | |
| await prefetchPreset(name) | |
| } | |
| } | |
| } catch { | |
| // Prefetch is best-effort — silently fail | |
| } | |
| } | |
| init() | |
| return () => { cancelled = true } | |
| }, [prefetchPreset]) | |
| return { cacheStatus } | |
| } | |