fixing stuff after splitting jsons
This commit is contained in:
@@ -430,7 +430,7 @@
|
||||
<div class="flex flex-col gap-1">
|
||||
{#each tooltipActivities as a}
|
||||
<a
|
||||
href="{import.meta.env.BASE_URL}activity/{a.id}/"
|
||||
href={a.detail_url ? `${import.meta.env.BASE_URL}activity/${a.id}/` : `${import.meta.env.BASE_URL}activity/local/?id=${a.id}`}
|
||||
class="flex flex-col gap-0.5 rounded-lg px-2 py-1.5 hover:bg-zinc-800 transition-colors"
|
||||
>
|
||||
<span class="text-sm font-medium text-white truncate">
|
||||
|
||||
@@ -186,17 +186,24 @@ export async function loadTimeseries(
|
||||
): Promise<Timeseries | null> {
|
||||
try {
|
||||
let url: string;
|
||||
// Strip the leading "activities/" from timeseriesUrl so we can append it
|
||||
// to whatever directory the detail JSON lives in.
|
||||
const filename = timeseriesUrl.replace(/^activities\//, '');
|
||||
|
||||
if (timeseriesUrl.startsWith('http')) {
|
||||
url = timeseriesUrl;
|
||||
} else if (detailUrl.startsWith('http')) {
|
||||
// detailUrl is absolute — resolve timeseries relative to its directory
|
||||
// absolute detailUrl (browser shard resolution) → same directory
|
||||
const dir = detailUrl.substring(0, detailUrl.lastIndexOf('/') + 1);
|
||||
// timeseriesUrl is "activities/id.timeseries.json" — strip leading "activities/"
|
||||
// because dir already ends with "activities/"
|
||||
const filename = timeseriesUrl.replace(/^activities\//, '');
|
||||
url = `${dir}${filename}`;
|
||||
} else {
|
||||
url = `${baseUrl}data/${timeseriesUrl}`;
|
||||
// relative detailUrl — may be plain ("activities/{id}.json", single-user)
|
||||
// or prefixed ("dave/_merged/activities/{id}.json", multi-user SSG prop).
|
||||
// In both cases, resolve the timeseries file from the same directory.
|
||||
const dir = detailUrl.includes('/')
|
||||
? detailUrl.substring(0, detailUrl.lastIndexOf('/') + 1)
|
||||
: '';
|
||||
url = `${baseUrl}data/${dir}${filename}`;
|
||||
}
|
||||
return await fetchJSON<Timeseries>(url);
|
||||
} catch {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
||||
import { join, resolve } from 'node:path';
|
||||
import Base from '../../layouts/Base.astro';
|
||||
import ActivityDetail from '../../components/ActivityDetail.svelte';
|
||||
@@ -49,12 +49,73 @@ export async function getStaticPaths() {
|
||||
const activities = readActivities(join(dataDir, 'index.json'));
|
||||
const athlete = root.owner?.athlete ?? null;
|
||||
|
||||
return activities
|
||||
.filter(a => a.privacy !== 'private' && a.id)
|
||||
.map(a => ({
|
||||
params: { id: a.id },
|
||||
props: { activity: a, athlete },
|
||||
}));
|
||||
// Build the map from the index first
|
||||
const byId = new Map(
|
||||
activities
|
||||
.filter(a => a.privacy !== 'private' && a.id)
|
||||
.map(a => [a.id, { activity: a, athlete }])
|
||||
);
|
||||
|
||||
// Fallback: scan _merged/activities/ directories for any JSON files not yet
|
||||
// covered by the index (e.g. shard read failures, recently added activities).
|
||||
try {
|
||||
const userDirs = readdirSync(dataDir, { withFileTypes: true })
|
||||
.filter(d => d.isDirectory() && !d.name.startsWith('_') && !d.name.startsWith('.'))
|
||||
.map(d => d.name);
|
||||
|
||||
for (const handle of userDirs) {
|
||||
// Prefer _merged, fall back to plain activities dir
|
||||
const mergedActs = join(dataDir, handle, '_merged', 'activities');
|
||||
const plainActs = join(dataDir, handle, 'activities');
|
||||
const actsDir = existsSync(mergedActs) ? mergedActs : (existsSync(plainActs) ? plainActs : null);
|
||||
if (!actsDir) continue;
|
||||
|
||||
const urlPrefix = existsSync(mergedActs)
|
||||
? `${handle}/_merged/`
|
||||
: `${handle}/`;
|
||||
|
||||
for (const file of readdirSync(actsDir)) {
|
||||
if (!file.endsWith('.json') || file.endsWith('.timeseries.json')) continue;
|
||||
const id = file.slice(0, -5); // strip .json
|
||||
if (byId.has(id)) continue; // already covered by the index
|
||||
try {
|
||||
const detail = JSON.parse(readFileSync(join(actsDir, file), 'utf-8'));
|
||||
if (detail.privacy === 'private') continue;
|
||||
// Build a minimal ActivitySummary from the detail file
|
||||
const a: ActivitySummary = {
|
||||
id,
|
||||
title: detail.title ?? id,
|
||||
sport: detail.sport ?? 'other',
|
||||
sub_sport: detail.sub_sport ?? null,
|
||||
started_at: detail.started_at ?? '',
|
||||
distance_m: detail.distance_m ?? null,
|
||||
duration_s: detail.duration_s ?? null,
|
||||
moving_time_s: detail.moving_time_s ?? null,
|
||||
elevation_gain_m: detail.elevation_gain_m ?? null,
|
||||
avg_speed_kmh: detail.avg_speed_kmh ?? null,
|
||||
max_speed_kmh: detail.max_speed_kmh ?? null,
|
||||
avg_hr_bpm: detail.avg_hr_bpm ?? null,
|
||||
max_hr_bpm: detail.max_hr_bpm ?? null,
|
||||
avg_cadence_rpm: detail.avg_cadence_rpm ?? null,
|
||||
avg_power_w: detail.avg_power_w ?? null,
|
||||
mmp: detail.mmp ?? null,
|
||||
source: detail.source ?? null,
|
||||
privacy: detail.privacy ?? 'public',
|
||||
detail_url: `${urlPrefix}activities/${file}`,
|
||||
track_url: detail.bbox ? `${urlPrefix}activities/${id}.geojson` : null,
|
||||
preview_coords: null,
|
||||
handle,
|
||||
};
|
||||
byId.set(id, { activity: a, athlete });
|
||||
} catch { /* skip malformed files */ }
|
||||
}
|
||||
}
|
||||
} catch { /* ignore scan errors */ }
|
||||
|
||||
return [...byId.values()].map(({ activity: a, athlete: ath }) => ({
|
||||
params: { id: a.id },
|
||||
props: { activity: a, athlete: ath },
|
||||
}));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user