guard around potential crashes

This commit is contained in:
2026-09-02 18:38:38 +02:00
parent 9c3fd4c1a2
commit e98293fa7c
5 changed files with 32 additions and 16 deletions
+2 -2
View File
@@ -18,7 +18,7 @@ export function getStreams(): StreamSummary[] {
stream_date: Date.parse(stream.stream_date as string),
filename: stream.filename as string,
title: stream.title as string | null,
tags: JSON.parse(stream.tags as string) as string[],
tags: (JSON.parse(stream.tags as string) as string[]) ?? [],
length_seconds: stream.length_seconds as number
}));
}
@@ -40,7 +40,7 @@ export function getStreamInfo(streamId: string): Stream | null {
format: streamData.format as string,
title: streamData.title as string | null,
description: streamData.description as string | null,
tags: JSON.parse(streamData.tags as string) as string[],
tags: (JSON.parse(streamData.tags as string) as string[]) ?? [],
length_seconds: streamData.length_seconds as number,
tracks: JSON.parse(streamData.tracks as string)
};
+3 -5
View File
@@ -1,12 +1,10 @@
import { browser } from '$app/environment';
import { SvelteSet } from 'svelte/reactivity';
import { readStored, writeStored } from './utils.ts';
export const favoritedStreams = new SvelteSet<string>(
JSON.parse((browser && localStorage.getItem('favoritedStreams')) || '[]')
);
export const favoritedStreams = new SvelteSet<string>(readStored('favoritedStreams', []));
$effect.root(() => {
$effect(() => {
localStorage.setItem('favoritedStreams', JSON.stringify(Array.from(favoritedStreams)));
writeStored('favoritedStreams', Array.from(favoritedStreams));
});
});
+5 -6
View File
@@ -1,5 +1,6 @@
import { browser } from '$app/environment';
import { SvelteSet, SvelteMap } from 'svelte/reactivity';
import { readStored, writeStored } from './utils.ts';
import type { StreamSummary, PartialDownload } from './types.ts';
const STORAGE_KEY = 'cachedStreams';
@@ -16,23 +17,21 @@ const CHUNK_SIZE = 8 * 1024 * 1024;
// as the intent is to catch socket breakage during cellular network changes for example
const CHUNK_TIMEOUT = 60_000;
export const cached = new SvelteSet<string>(
browser ? JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]') : []
);
export const cached = new SvelteSet<string>(readStored<string[]>(STORAGE_KEY, []));
export const downloading = new SvelteMap<string, number>();
// bytes already written to OPFS for each incomplete download
export const partials = new SvelteMap<string, PartialDownload>(
browser ? Object.entries(JSON.parse(localStorage.getItem(PARTIALS_KEY) || '{}')) : []
Object.entries(readStored<Record<string, PartialDownload>>(PARTIALS_KEY, {}))
);
if (browser) {
$effect.root(() => {
$effect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify([...cached]));
writeStored(STORAGE_KEY, [...cached]);
});
$effect(() => {
localStorage.setItem(PARTIALS_KEY, JSON.stringify(Object.fromEntries(partials)));
writeStored(PARTIALS_KEY, Object.fromEntries(partials));
});
});
}
+19
View File
@@ -1,7 +1,26 @@
import { browser } from '$app/environment';
import Sqids from 'sqids';
const sqids = new Sqids({ minLength: 6, alphabet: 'abcdefghijklmnopqrstuvwxyz0123456789' });
// browser blocks site data => localStorage throws
// so we're wrapping our calls to ensure things running at module init
// don't bring down the site render entirely
export function readStored<T>(key: string, fallback: T): T {
if (!browser) return fallback;
try {
return (JSON.parse(localStorage.getItem(key) ?? '') as T) ?? fallback;
} catch {
return fallback;
}
}
export function writeStored(key: string, value: unknown) {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch {}
}
export function hashcode(str: string): number {
let h = 9;
for (let i = 0; i < str.length; ) h = Math.imul(h ^ str.charCodeAt(i++), 9 ** 9);
+3 -3
View File
@@ -16,6 +16,7 @@
import { onMount, untrack } from 'svelte';
import { fade } from 'svelte/transition';
import { getStreamContext } from '$lib/streamContext.svelte.ts';
import { readStored, writeStored } from '$lib/utils.ts';
import type { Track } from '$lib/types';
const ctx = getStreamContext();
@@ -87,8 +88,7 @@
isSafari = isIOS || isDesktopSafari;
// default volume
const volumeData = localStorage.getItem('volume');
volume = volumeData ? parseFloat(volumeData) : 0.67;
volume = readStored('volume', 0.67);
setVolume(volume);
// set actions for previous/next track media buttons
@@ -181,7 +181,7 @@
if (!volumeBar) return;
volume = seek(event, volumeBar.getBoundingClientRect());
setVolume(volume);
localStorage.setItem('volume', volume.toString());
writeStored('volume', volume);
muted = false;
}