music on atproto
plyr.fm
1// platform stats cache - prevents refetching on navigation
2import { browser } from '$app/environment';
3import { API_URL } from '$lib/config';
4
5export interface PlatformStats {
6 total_plays: number;
7 total_tracks: number;
8 total_artists: number;
9 total_duration_seconds: number;
10}
11
12/**
13 * format seconds into human-readable duration string.
14 * examples: "25h 32m", "3h 45m", "45m", "1h"
15 */
16export function formatDuration(totalSeconds: number): string {
17 const hours = Math.floor(totalSeconds / 3600);
18 const minutes = Math.floor((totalSeconds % 3600) / 60);
19
20 if (hours === 0) {
21 return `${minutes}m`;
22 }
23 if (minutes === 0) {
24 return `${hours}h`;
25 }
26 return `${hours}h ${minutes}m`;
27}
28
29class StatsCache {
30 data = $state<PlatformStats | null>(null);
31 loading = $state(false);
32 private lastFetch = 0;
33 private readonly CACHE_DURATION = 60_000; // 1 minute
34
35 get stats(): PlatformStats | null {
36 return this.data;
37 }
38
39 async fetch(force = false): Promise<void> {
40 if (!browser) return;
41
42 const now = Date.now();
43 const isCacheValid = this.data && now - this.lastFetch < this.CACHE_DURATION;
44
45 if (!force && isCacheValid) return;
46 if (this.loading) return;
47
48 this.loading = true;
49 try {
50 const response = await fetch(`${API_URL}/stats`);
51 if (response.ok) {
52 this.data = await response.json();
53 this.lastFetch = now;
54 }
55 } catch (e) {
56 console.error('failed to load platform stats:', e);
57 } finally {
58 this.loading = false;
59 }
60 }
61}
62
63export const statsCache = new StatsCache();