From 6e42d2c97c71a06b00c69b2439db587cb09b3cd3 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:48:37 +0000 Subject: [PATCH] perf: Optimize nostrService video subscription updates Optimizes the `subscribeVideos` callback in `js/services/nostrService.js` to handle video updates incrementally, avoiding O(N*M) complexity during high-volume updates (e.g., initial load). Changes: - Modified `js/nostr/videoEventBuffer.js` to include deleted videos in the flushed batch, allowing consumers to handle deletions incrementally. - Updated `js/services/nostrService.js` to process `updatedVideos` incrementally: - Separates updates into active and deleted. - Updates `this.videosMap` directly (removing deleted, adding/updating active). - Filters and caches only the active updates (O(M)). - Emits `videos:updated` with the full sorted list (derived from the updated map) to maintain API contract with consumers. - Persists the full sorted list to localStorage. This reduces the per-batch overhead significantly by avoiding O(N) filtering and map reconstruction for every batch of updates. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- js/nostr/videoEventBuffer.js | 1 + js/services/nostrService.js | 56 ++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/js/nostr/videoEventBuffer.js b/js/nostr/videoEventBuffer.js index df6c87a5..0af08a73 100644 --- a/js/nostr/videoEventBuffer.js +++ b/js/nostr/videoEventBuffer.js @@ -130,6 +130,7 @@ export class VideoEventBuffer { } } } + updatedVideos.push(video); continue; } diff --git a/js/services/nostrService.js b/js/services/nostrService.js index ea2fb0c7..ff1d9dc5 100644 --- a/js/services/nostrService.js +++ b/js/services/nostrService.js @@ -1743,9 +1743,59 @@ export class NostrService { limit: this.nostrClient.clampVideoRequestLimit(200), }; const subscription = this.nostrClient.subscribeVideos( - () => { - const updated = this.nostrClient.getActiveVideos(); - applyAndNotify(updated, "subscription"); + (updatedVideos) => { + if (!Array.isArray(updatedVideos) || !updatedVideos.length) { + return; + } + + const activeUpdates = []; + const deletedUpdates = []; + + for (const v of updatedVideos) { + if (v.deleted) { + deletedUpdates.push(v); + } else { + activeUpdates.push(v); + } + } + + if (deletedUpdates.length > 0) { + const map = this.ensureVideosMap(); + for (const v of deletedUpdates) { + if (v.id) { + map.delete(v.id); + } + } + this.markAuthorIndexDirty(); + } + + const filtered = this.filterVideos(activeUpdates, { + blacklistedEventIds, + isAuthorBlocked, + }); + + if (filtered.length > 0) { + this.cacheVideos(filtered); + } + + if (filtered.length > 0 || deletedUpdates.length > 0) { + // Persist the full list (latest N) from the now-updated cache + const map = this.ensureVideosMap(); + const fullList = Array.from(map.values()); + fullList.sort((a, b) => { + const aCreated = Number(a?.created_at) || 0; + const bCreated = Number(b?.created_at) || 0; + return bCreated - aCreated; + }); + + this.emit("videos:updated", { + videos: fullList, + deleted: deletedUpdates, + reason: "subscription", + }); + + persistFilteredVideos(fullList); + } }, subscriptionOptions, );