feat(node-player): use Service Worker for auth, enable streaming playback
Add a Service Worker that intercepts /api/* requests and injects the Bearer token. This allows <audio> and <img> elements to use direct URLs instead of downloading entire files as blobs first. - Audio now streams progressively (no full download before playback) - Cover art loads via regular <img src> (SW adds auth header) - Remove blob-based preloadStream, fetchCoverBlob, useCoverUrl hook - Register SW in main.tsx, token synced via postMessage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,8 +8,17 @@ export const furumiApi = axios.create({
|
||||
baseURL: API_ROOT,
|
||||
})
|
||||
|
||||
function sendTokenToSW(token: string) {
|
||||
navigator.serviceWorker?.controller?.postMessage({ type: 'SET_TOKEN', token })
|
||||
// Also send to waiting/installing SW
|
||||
navigator.serviceWorker?.ready.then((reg) => {
|
||||
reg.active?.postMessage({ type: 'SET_TOKEN', token })
|
||||
})
|
||||
}
|
||||
|
||||
export function setAuthToken(token: string) {
|
||||
furumiApi.defaults.headers.common['Authorization'] = `Bearer ${token}`
|
||||
sendTokenToSW(token)
|
||||
}
|
||||
|
||||
export function clearAuthToken() {
|
||||
@@ -37,7 +46,6 @@ furumiApi.interceptors.response.use(
|
||||
const original = error.config
|
||||
if (error.response?.status === 401 && !original._retried) {
|
||||
original._retried = true
|
||||
// Deduplicate concurrent refresh attempts
|
||||
if (!refreshPromise) {
|
||||
refreshPromise = refreshToken().finally(() => { refreshPromise = null })
|
||||
}
|
||||
@@ -96,14 +104,3 @@ export async function getRecentPlays(): Promise<RecentPlay[] | null> {
|
||||
export async function recordPlay(trackSlug: string): Promise<void> {
|
||||
await furumiApi.post(`/tracks/${trackSlug}/play`).catch(() => null)
|
||||
}
|
||||
|
||||
export async function preloadStream(trackSlug: string) {
|
||||
return await furumiApi.get(`/stream/${trackSlug}`, { responseType: 'blob' }).catch(() => null)
|
||||
}
|
||||
|
||||
export async function fetchCoverBlob(trackSlug: string): Promise<string | null> {
|
||||
const res = await furumiApi.get(`/tracks/${trackSlug}/cover`, { responseType: 'blob' }).catch(() => null)
|
||||
if (!res?.data) return null
|
||||
return URL.createObjectURL(res.data)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user