guard around potential crashes
This commit is contained in:
+2
-2
@@ -18,7 +18,7 @@ export function getStreams(): StreamSummary[] {
|
|||||||
stream_date: Date.parse(stream.stream_date as string),
|
stream_date: Date.parse(stream.stream_date as string),
|
||||||
filename: stream.filename as string,
|
filename: stream.filename as string,
|
||||||
title: stream.title as string | null,
|
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
|
length_seconds: stream.length_seconds as number
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ export function getStreamInfo(streamId: string): Stream | null {
|
|||||||
format: streamData.format as string,
|
format: streamData.format as string,
|
||||||
title: streamData.title as string | null,
|
title: streamData.title as string | null,
|
||||||
description: streamData.description 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,
|
length_seconds: streamData.length_seconds as number,
|
||||||
tracks: JSON.parse(streamData.tracks as string)
|
tracks: JSON.parse(streamData.tracks as string)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { browser } from '$app/environment';
|
|
||||||
import { SvelteSet } from 'svelte/reactivity';
|
import { SvelteSet } from 'svelte/reactivity';
|
||||||
|
import { readStored, writeStored } from './utils.ts';
|
||||||
|
|
||||||
export const favoritedStreams = new SvelteSet<string>(
|
export const favoritedStreams = new SvelteSet<string>(readStored('favoritedStreams', []));
|
||||||
JSON.parse((browser && localStorage.getItem('favoritedStreams')) || '[]')
|
|
||||||
);
|
|
||||||
$effect.root(() => {
|
$effect.root(() => {
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
localStorage.setItem('favoritedStreams', JSON.stringify(Array.from(favoritedStreams)));
|
writeStored('favoritedStreams', Array.from(favoritedStreams));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { SvelteSet, SvelteMap } from 'svelte/reactivity';
|
import { SvelteSet, SvelteMap } from 'svelte/reactivity';
|
||||||
|
import { readStored, writeStored } from './utils.ts';
|
||||||
import type { StreamSummary, PartialDownload } from './types.ts';
|
import type { StreamSummary, PartialDownload } from './types.ts';
|
||||||
|
|
||||||
const STORAGE_KEY = 'cachedStreams';
|
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
|
// as the intent is to catch socket breakage during cellular network changes for example
|
||||||
const CHUNK_TIMEOUT = 60_000;
|
const CHUNK_TIMEOUT = 60_000;
|
||||||
|
|
||||||
export const cached = new SvelteSet<string>(
|
export const cached = new SvelteSet<string>(readStored<string[]>(STORAGE_KEY, []));
|
||||||
browser ? JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]') : []
|
|
||||||
);
|
|
||||||
export const downloading = new SvelteMap<string, number>();
|
export const downloading = new SvelteMap<string, number>();
|
||||||
|
|
||||||
// bytes already written to OPFS for each incomplete download
|
// bytes already written to OPFS for each incomplete download
|
||||||
export const partials = new SvelteMap<string, PartialDownload>(
|
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) {
|
if (browser) {
|
||||||
$effect.root(() => {
|
$effect.root(() => {
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify([...cached]));
|
writeStored(STORAGE_KEY, [...cached]);
|
||||||
});
|
});
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
localStorage.setItem(PARTIALS_KEY, JSON.stringify(Object.fromEntries(partials)));
|
writeStored(PARTIALS_KEY, Object.fromEntries(partials));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,26 @@
|
|||||||
|
import { browser } from '$app/environment';
|
||||||
import Sqids from 'sqids';
|
import Sqids from 'sqids';
|
||||||
|
|
||||||
const sqids = new Sqids({ minLength: 6, alphabet: 'abcdefghijklmnopqrstuvwxyz0123456789' });
|
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 {
|
export function hashcode(str: string): number {
|
||||||
let h = 9;
|
let h = 9;
|
||||||
for (let i = 0; i < str.length; ) h = Math.imul(h ^ str.charCodeAt(i++), 9 ** 9);
|
for (let i = 0; i < str.length; ) h = Math.imul(h ^ str.charCodeAt(i++), 9 ** 9);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
import { onMount, untrack } from 'svelte';
|
import { onMount, untrack } from 'svelte';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
import { getStreamContext } from '$lib/streamContext.svelte.ts';
|
import { getStreamContext } from '$lib/streamContext.svelte.ts';
|
||||||
|
import { readStored, writeStored } from '$lib/utils.ts';
|
||||||
import type { Track } from '$lib/types';
|
import type { Track } from '$lib/types';
|
||||||
|
|
||||||
const ctx = getStreamContext();
|
const ctx = getStreamContext();
|
||||||
@@ -87,8 +88,7 @@
|
|||||||
isSafari = isIOS || isDesktopSafari;
|
isSafari = isIOS || isDesktopSafari;
|
||||||
|
|
||||||
// default volume
|
// default volume
|
||||||
const volumeData = localStorage.getItem('volume');
|
volume = readStored('volume', 0.67);
|
||||||
volume = volumeData ? parseFloat(volumeData) : 0.67;
|
|
||||||
setVolume(volume);
|
setVolume(volume);
|
||||||
|
|
||||||
// set actions for previous/next track media buttons
|
// set actions for previous/next track media buttons
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
if (!volumeBar) return;
|
if (!volumeBar) return;
|
||||||
volume = seek(event, volumeBar.getBoundingClientRect());
|
volume = seek(event, volumeBar.getBoundingClientRect());
|
||||||
setVolume(volume);
|
setVolume(volume);
|
||||||
localStorage.setItem('volume', volume.toString());
|
writeStored('volume', volume);
|
||||||
muted = false;
|
muted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user