1 — Timeseries split
- writer.py: timeseries is now written to {id}.timeseries.json as a separate file. The detail JSON gets a timeseries_url field instead. finalize_pending and cleanup_pending handle the extra file.
- merge.py (merge_one): symlinks the .timeseries.json file alongside the detail JSON. merge_all already handles it transparently (the .timeseries.json stem doesn't match any activity
ID in to_merge, so it falls through to the symlink branch).
- types.ts: timeseries is now timeseries?: Timeseries | null, and timeseries_url?: string | null added.
- dataloader.ts: new loadTimeseries(url, detailUrl, base) function that resolves paths correctly in both single- and multi-user modes (uses the fetched detail URL's directory as the base).
- ActivityDetail.svelte: loads timeseries separately after detail loads; uses detail.timeseries for IDB activities (embedded) or fetches via detail.timeseries_url for server activities. Charts show a pulse placeholder while loading.
2 — GZip
- GZipMiddleware (min 1 KB) added to both bincio/serve/server.py and bincio/edit/server.py — all API JSON responses are now gzip-compressed.
- For static files (the big timeseries JSONs), nginx should be configured with gzip on; gzip_types application/json application/geo+json; — no code change needed on the server side.
Net effect: opening an activity page now fetches ~1.4 KB (detail) instead of ~586 KB. The timeseries fetches ~60–150 KB gzip-compressed shortly after (it loads concurrently with the map rendering).
This commit is contained in:
@@ -2,12 +2,12 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import type { ActivitySummary, ActivityDetail, AthleteZones } from '../lib/types';
|
||||
import type { ActivitySummary, ActivityDetail, AthleteZones, Timeseries } from '../lib/types';
|
||||
import { formatDistance, formatDuration, formatElevation, formatSpeed, formatDate, formatTime, sportIcon, sportLabel, sportColor } from '../lib/format';
|
||||
import ActivityMap from './ActivityMap.svelte';
|
||||
import ActivityCharts from './ActivityCharts.svelte';
|
||||
import EditDrawer from './EditDrawer.svelte';
|
||||
import { loadActivity } from '../lib/dataloader';
|
||||
import { loadActivity, loadTimeseries } from '../lib/dataloader';
|
||||
|
||||
export let activity: ActivitySummary;
|
||||
export let base: string = '/';
|
||||
@@ -17,6 +17,8 @@
|
||||
const editEnabled = editUrl !== '' || import.meta.env.PUBLIC_EDIT_ENABLED === 'true';
|
||||
|
||||
let detail: ActivityDetail | null = null;
|
||||
let timeseries: Timeseries | null = null;
|
||||
let timeseriesLoading = false;
|
||||
let error = '';
|
||||
let hoveredIdx: number | null = null;
|
||||
let editOpen = false;
|
||||
@@ -31,8 +33,17 @@
|
||||
try {
|
||||
detail = await loadActivity(activity.id, activity.detail_url ?? '', base);
|
||||
if (!detail) throw new Error('Activity not found');
|
||||
// Use embedded timeseries (IDB activities) or lazy-fetch from URL
|
||||
if (detail.timeseries) {
|
||||
timeseries = detail.timeseries;
|
||||
} else if (detail.timeseries_url) {
|
||||
timeseriesLoading = true;
|
||||
timeseries = await loadTimeseries(detail.timeseries_url, activity.detail_url ?? '', base);
|
||||
timeseriesLoading = false;
|
||||
}
|
||||
} catch (e: any) {
|
||||
error = e.message;
|
||||
timeseriesLoading = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -231,7 +242,7 @@
|
||||
{#if trackUrl}
|
||||
<ActivityMap
|
||||
{trackUrl}
|
||||
timeseries={detail?.timeseries ?? null}
|
||||
{timeseries}
|
||||
bbox={detail?.bbox ?? null}
|
||||
accentColor={color}
|
||||
bind:hoveredIdx
|
||||
@@ -263,11 +274,11 @@
|
||||
<!-- Charts -->
|
||||
{#if error}
|
||||
<p class="text-red-400 text-sm mt-4">{error}</p>
|
||||
{:else if detail?.timeseries && detail.timeseries.t.length > 0}
|
||||
{:else if timeseries && timeseries.t.length > 0}
|
||||
<div class="bg-zinc-900 rounded-xl border border-zinc-800 p-4">
|
||||
<ActivityCharts timeseries={detail.timeseries} bind:hoveredIdx {athlete} />
|
||||
<ActivityCharts {timeseries} bind:hoveredIdx {athlete} />
|
||||
</div>
|
||||
{:else if !detail}
|
||||
{:else if !detail || timeseriesLoading}
|
||||
<div class="bg-zinc-900 rounded-xl border border-zinc-800 p-4 h-32 animate-pulse"></div>
|
||||
{/if}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user