Skip to content

Commit ca97b86

Browse files
lmanganicursoragent
andcommitted
Fix P2P upload queue updates and GitHub Pages asset paths.
Deliver broadcasts to the local peer (Trystero does not echo), prefix flags/kick with basePath, and only log NTP probes when drift exceeds tolerance. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent afce467 commit ca97b86

5 files changed

Lines changed: 31 additions & 15 deletions

File tree

apps/client/src/components/dashboard/Metronome.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
import { useBeatTiming } from "@/hooks/useBeatTiming";
44
import { audioContextManager } from "@/lib/audioContextManager";
5+
import { publicAssetPath } from "@/lib/paths";
56
import { cn } from "@/lib/utils";
67
import { useGlobalStore } from "@/store/global";
78
import { Metronome as MetronomeIcon } from "lucide-react";
89
import { useCallback, useEffect, useRef } from "react";
910

10-
const KICK_URL = "/kick.wav";
11+
const KICK_URL = publicAssetPath("/kick.wav");
1112

1213
/** Lazily fetch + decode the kick sample once, then cache it. Clears cache on failure so retries work. */
1314
let kickBufferPromise: Promise<AudioBuffer> | null = null;
@@ -52,9 +53,13 @@ export const MetronomeButton = () => {
5253
const ctx = audioContextManager.getContext();
5354
let cancelled = false;
5455

55-
getKickBuffer(ctx).then((buffer) => {
56-
if (!cancelled) kickBufferRef.current = buffer;
57-
});
56+
getKickBuffer(ctx)
57+
.then((buffer) => {
58+
if (!cancelled) kickBufferRef.current = buffer;
59+
})
60+
.catch(() => {
61+
// kick.wav is optional (may be absent on static deploy)
62+
});
5863

5964
return () => {
6065
cancelled = true;

apps/client/src/lib/api.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,17 @@ export const uploadAudioFile = async (data: { file: File; roomId: string }) => {
2626

2727
await saveLocalTrack(record);
2828

29-
useP2PConnectionStore.getState().sendRequest({
29+
const p2p = useP2PConnectionStore.getState();
30+
if (!p2p.isReady) {
31+
throw new Error("Room connection is still starting — try again in a moment");
32+
}
33+
34+
p2p.sendRequest({
3035
type: ClientActionEnum.enum.REGISTER_AUDIO_SOURCE,
3136
source: { url },
3237
});
3338

34-
const room = useP2PConnectionStore.getState().room;
39+
const room = p2p.room;
3540
if (room) {
3641
void broadcastLocalTrackToRoom(room, record);
3742
}

apps/client/src/lib/ip.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { countryCodeEmoji } from "@/lib/country/countryCode";
2+
import { publicAssetPath } from "@/lib/paths";
23
import { LocationSchema, MAX_LOCATION_QUALITY, sanitizeLocationFields, scoreLocationQuality } from "@beatsync/shared";
34
import { z } from "zod";
45
import { getCountryName } from "./country/codeToName";
@@ -62,7 +63,7 @@ const getFlagSvgURLFromCountryCode = (countryCode: string) => {
6263
throw new Error(`Country code must be exactly 2 characters, got: ${countryCode}`);
6364
}
6465

65-
return `/flags/${countryCode.toLowerCase()}.svg`;
66+
return publicAssetPath(`/flags/${countryCode.toLowerCase()}.svg`);
6667
};
6768

6869
// https://www.geojs.io — Cloudflare-backed, full CORS, no key, unlimited

apps/client/src/store/p2pConnection.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ export const useP2PConnectionStore = create<P2PConnectionState>()((set, get) =>
115115

116116
const broadcastEnvelope = (envelope: P2PEnvelope) => {
117117
void sendEnvelopeAction(envelope, null);
118+
// Trystero does not echo broadcasts to the initiator — apply room events locally.
119+
if (envelope.kind !== "broadcast") return;
120+
const payload = envelope.payload;
121+
if (payload.type === "ROOM_EVENT" && payload.event.type === "CLIENT_CHANGE") {
122+
return;
123+
}
124+
deliverLocalRoomMessage(payload);
118125
};
119126

120127
const deliverLocalRoomMessage = (message: WSResponseType) => {

apps/client/src/utils/ntp.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,20 +138,18 @@ export const validateProbePair = (data: {
138138

139139
const total = pureCount + impureCount;
140140
const pureRate = total > 0 ? ((pureCount / total) * 100).toFixed(0) : "0";
141+
const significantDrift = gapDrift > NTP_CONSTANTS.PROBE_GAP_TOLERANCE_MS;
141142

142-
if (!isPure) {
143-
console.log(
144-
`[CodedProbe] IMPURE #${probeGroupId} | clientGap=${clientGap.toFixed(1)}ms serverGap=${serverGap.toFixed(1)}ms drift=${gapDrift.toFixed(1)}ms | pure: ${pureCount}/${total} (${pureRate}%) | sent: ${probeGroupCounter}`
143+
if (!isPure || significantDrift) {
144+
const label = isPure ? "DRIFT" : "IMPURE";
145+
console.warn(
146+
`[NTP] ${label} probe #${probeGroupId} | clientGap=${clientGap.toFixed(1)}ms serverGap=${serverGap.toFixed(1)}ms drift=${gapDrift.toFixed(1)}ms | pure: ${pureCount}/${total} (${pureRate}%)`
145147
);
146-
return null;
148+
if (!isPure) return null;
147149
}
148150

149151
const best = first.roundTripDelay <= measurement.roundTripDelay ? first : measurement;
150152

151-
console.log(
152-
`[CodedProbe] PURE #${probeGroupId} | clientGap=${clientGap.toFixed(1)}ms serverGap=${serverGap.toFixed(1)}ms drift=${gapDrift.toFixed(1)}ms | bestRTT=${best.roundTripDelay.toFixed(1)}ms offset=${best.clockOffset.toFixed(1)}ms | pure: ${pureCount}/${total} (${pureRate}%) | sent: ${probeGroupCounter}`
153-
);
154-
155153
return best;
156154
};
157155

0 commit comments

Comments
 (0)