From ff07ed8e01b6fb6533bd6fb7cbb64db776d159dc Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:11:41 -0500 Subject: [PATCH] Add paging and limits to nostr video subscription --- js/nostr/client.js | 70 ++++++++++++++++++++++++++++++++++--- js/services/nostrService.js | 22 +++++++++--- 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/js/nostr/client.js b/js/nostr/client.js index 8289536d..b2f7074f 100644 --- a/js/nostr/client.js +++ b/js/nostr/client.js @@ -301,6 +301,8 @@ function shouldRequestExtensionPermissions(signer) { const EVENTS_CACHE_STORAGE_KEY = "bitvid:eventsCache:v1"; const LEGACY_EVENTS_STORAGE_KEY = "bitvidEvents"; const EVENTS_CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes +const DEFAULT_VIDEO_REQUEST_LIMIT = 150; +const MAX_VIDEO_REQUEST_LIMIT = 500; // To limit error spam let errorLogCount = 0; @@ -5148,19 +5150,79 @@ export class NostrClient { } } + clampVideoRequestLimit(limit, fallback = DEFAULT_VIDEO_REQUEST_LIMIT) { + const normalizedFallback = + Number.isFinite(fallback) && fallback > 0 + ? Math.floor(fallback) + : DEFAULT_VIDEO_REQUEST_LIMIT; + + if (!Number.isFinite(limit) || limit <= 0) { + return normalizedFallback; + } + + const floored = Math.floor(limit); + const clamped = Math.min(MAX_VIDEO_REQUEST_LIMIT, Math.max(1, floored)); + + if (clamped !== floored && isDevMode) { + devLogger.log( + `[nostr] Clamped video request limit from ${floored} to ${clamped}`, + ); + } + + return clamped; + } + + getLatestCachedCreatedAt() { + let latest = 0; + + for (const video of this.allEvents.values()) { + const createdAt = Number.isFinite(video?.created_at) + ? Math.floor(video.created_at) + : 0; + if (createdAt > latest) { + latest = createdAt; + } + } + + return latest; + } + /** * Subscribe to *all* videos (old and new) with a single subscription, * buffering incoming events to avoid excessive DOM updates. + * + * @param {Function} onVideo + * @param {{ since?: number, until?: number, limit?: number }} [options] + * `since` defaults to the latest cached created_at to avoid replaying + * the full history on every load. `limit` is clamped to + * MAX_VIDEO_REQUEST_LIMIT. */ - subscribeVideos(onVideo) { + subscribeVideos(onVideo, options = {}) { + const { since, until, limit } = options; + const latestCachedCreatedAt = this.getLatestCachedCreatedAt(); + + const resolvedLimit = this.clampVideoRequestLimit(limit); + const resolvedSince = Number.isFinite(since) + ? Math.floor(since) + : latestCachedCreatedAt > 0 + ? latestCachedCreatedAt + : undefined; + const resolvedUntil = Number.isFinite(until) ? Math.floor(until) : undefined; + const filter = { kinds: [30078], "#t": ["video"], - // Adjust limit/time as desired - limit: 500, - since: 0, + limit: resolvedLimit, }; + if (resolvedSince !== undefined) { + filter.since = resolvedSince; + } + + if (resolvedUntil !== undefined) { + filter.until = resolvedUntil; + } + devLogger.log("[subscribeVideos] Subscribing with filter:", filter); const sub = this.pool.sub(this.relays, [filter]); diff --git a/js/services/nostrService.js b/js/services/nostrService.js index 341f7251..64a71a55 100644 --- a/js/services/nostrService.js +++ b/js/services/nostrService.js @@ -1122,10 +1122,19 @@ export class NostrService { this.resolveInitialLoad({ videos: initial, reason: "cache" }); if (!getStoredVideoSubscription()) { - const subscription = this.nostrClient.subscribeVideos(() => { - const updated = this.nostrClient.getActiveVideos(); - applyAndNotify(updated, "subscription"); - }); + const subscriptionOptions = { + // Start from the freshest cached timestamp to avoid replaying the full history; + // use loadOlderVideos when the user scrolls for backfill. + since: this.nostrClient.getLatestCachedCreatedAt() || undefined, + limit: this.nostrClient.clampVideoRequestLimit(200), + }; + const subscription = this.nostrClient.subscribeVideos( + () => { + const updated = this.nostrClient.getActiveVideos(); + applyAndNotify(updated, "subscription"); + }, + subscriptionOptions, + ); this.setVideoSubscription(subscription); this.emit("subscription:started", { subscription }); } @@ -1160,11 +1169,14 @@ export class NostrService { return []; } + const clampedLimit = + this.nostrClient?.clampVideoRequestLimit(limit) ?? limit ?? 0; + const filter = { kinds: [VIDEO_KIND], "#t": ["video"], until, - limit, + limit: clampedLimit, }; const collected = new Map();