chore: refactor fetchVideos to use subscribeVideos

Replaced the deprecated `fetchVideos` implementation with a wrapper around the newer `subscribeVideos` method. This reduces code duplication and ensures consistent behavior for video fetching logic. The wrapper maintains the original Promise-based API and ensures NIP-71 metadata is populated before resolving.

Added tests in `tests/nostr/client.test.mjs` to verify `fetchVideos` behavior.

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-08 03:22:50 +00:00
1 parent 1fbee4219b
commit 911c030437
2 files changed
+65 -75

No files matched your search

+18 -75
View File
@@ -8064,85 +8064,28 @@ export class NostrClient {
* @deprecated Use `subscribeVideos` for the main feed to support buffering and streaming.
*/
async fetchVideos(options = {}) {
const requestedLimit = Number(options?.limit);
const resolvedLimit = this.clampVideoRequestLimit(requestedLimit, DEFAULT_VIDEO_REQUEST_LIMIT);
const filter = {
kinds: [30078],
"#t": ["video"],
limit: resolvedLimit,
since: 0,
};
const localAll = new Map();
// NEW: track invalid
const invalidNotes = [];
try {
await Promise.all(
this.getHealthyRelays(this.relays).map(async (url) => {
const events = await this.pool.list([url], [filter]);
for (const evt of events) {
if (evt && evt.id) {
this.rawEvents.set(evt.id, evt);
}
const vid = convertEventToVideo(evt);
if (vid.invalid) {
// Accumulate if invalid
invalidNotes.push({ id: vid.id, reason: vid.reason });
} else {
// Only add if good
this.applyRootCreatedAt(vid);
const activeKey = getActiveKey(vid);
if (vid.deleted) {
this.recordTombstone(activeKey, vid.created_at);
} else {
this.applyTombstoneGuard(vid);
}
localAll.set(evt.id, vid);
}
}
})
);
// Merge into allEvents
for (const [id, vid] of localAll.entries()) {
this.allEvents.set(id, vid);
this.dirtyEventIds.add(id);
this.applyRootCreatedAt(vid);
return new Promise((resolve) => {
// Default to since: 0 to force full fetch if not specified
const effectiveOptions = { ...options };
if (effectiveOptions.since === undefined) {
effectiveOptions.since = 0;
}
// Rebuild activeMap
this.activeMap.clear();
for (const [id, video] of this.allEvents.entries()) {
if (video.deleted) continue;
const activeKey = getActiveKey(video);
const existing = this.activeMap.get(activeKey);
const sub = this.subscribeVideos(() => {}, effectiveOptions);
if (!existing || video.created_at > existing.created_at) {
this.activeMap.set(activeKey, video);
this.applyRootCreatedAt(video);
sub.on("eose", async () => {
sub.unsub();
try {
const activeVideos = this.getActiveVideos();
await this.populateNip71MetadataForVideos(activeVideos);
activeVideos.forEach((video) => this.applyRootCreatedAt(video));
resolve(activeVideos);
} catch (err) {
userLogger.error("fetchVideos error:", err);
resolve([]);
}
}
// OPTIONAL: Log invalid stats
if (invalidNotes.length > 0 && isDevMode) {
userLogger.warn(
`Skipped ${invalidNotes.length} invalid video notes:\n`,
invalidNotes.map((n) => `${n.id.slice(0, 8)}.. => ${n.reason}`)
);
}
const activeVideos = Array.from(this.activeMap.values()).sort(
(a, b) => b.created_at - a.created_at
);
await this.populateNip71MetadataForVideos(activeVideos);
activeVideos.forEach((video) => this.applyRootCreatedAt(video));
return activeVideos;
} catch (err) {
userLogger.error("fetchVideos error:", err);
return [];
}
});
});
}
/**
+47
View File
@@ -165,6 +165,53 @@ describe("NostrClient", () => {
});
});
describe("fetchVideos", () => {
it("should delegate to subscribeVideos and return result on eose", async () => {
let subOpts = null;
// Override subscribeVideos to verify delegation
client.subscribeVideos = (cb, opts) => {
subOpts = opts;
return {
on: (evt, handler) => {
if (evt === "eose") setTimeout(handler, 0);
},
unsub: () => {},
};
};
// Mock helpers
client.getActiveVideos = () => [{ id: "v1" }];
client.populateNip71MetadataForVideos = async () => {};
client.applyRootCreatedAt = () => {};
const result = await client.fetchVideos({ limit: 5 });
assert.equal(result[0].id, "v1");
assert.equal(subOpts.limit, 5);
// Ensure we force full fetch by default
assert.equal(subOpts.since, 0);
});
it("should respect since parameter if provided", async () => {
let subOpts = null;
client.subscribeVideos = (cb, opts) => {
subOpts = opts;
return {
on: (evt, handler) => {
if (evt === "eose") setTimeout(handler, 0);
},
unsub: () => {},
};
};
client.getActiveVideos = () => [];
client.populateNip71MetadataForVideos = async () => {};
client.applyRootCreatedAt = () => {};
await client.fetchVideos({ since: 12345 });
assert.equal(subOpts.since, 12345);
});
});
describe("publishVideo", () => {
it("should throw if not logged in", async () => {
await assert.rejects(