mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2025-09-09 07:28:44 +00:00
it go faster now
time to sleep...
This commit is contained in:
BIN
src/assets/jpg/default-profile.jpg
Normal file
BIN
src/assets/jpg/default-profile.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
1073
src/js/app.js
1073
src/js/app.js
File diff suppressed because it is too large
Load Diff
207
src/js/nostr.js
207
src/js/nostr.js
@@ -46,6 +46,9 @@ class NostrClient {
|
|||||||
this.pool = null;
|
this.pool = null;
|
||||||
this.pubkey = null;
|
this.pubkey = null;
|
||||||
this.relays = RELAY_URLS;
|
this.relays = RELAY_URLS;
|
||||||
|
|
||||||
|
// We keep a Map of subscribed videos for quick lookups by event.id
|
||||||
|
this.subscribedVideos = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -207,7 +210,7 @@ class NostrClient {
|
|||||||
const event = {
|
const event = {
|
||||||
kind: 30078,
|
kind: 30078,
|
||||||
pubkey,
|
pubkey,
|
||||||
created_at: Math.floor(Date.now() / 1000),
|
created_at: Math.floor(Date.now() / 100),
|
||||||
tags: [
|
tags: [
|
||||||
["t", "video"],
|
["t", "video"],
|
||||||
["d", uniqueD],
|
["d", uniqueD],
|
||||||
@@ -254,7 +257,6 @@ class NostrClient {
|
|||||||
* Edits an existing video event by reusing the same "d" tag.
|
* Edits an existing video event by reusing the same "d" tag.
|
||||||
* Allows toggling isPrivate on/off and re-encrypting or decrypting the magnet.
|
* Allows toggling isPrivate on/off and re-encrypting or decrypting the magnet.
|
||||||
*/
|
*/
|
||||||
// Minimal fix: ensures we only ever encrypt once per edit operation
|
|
||||||
async editVideo(originalEvent, updatedVideoData, pubkey) {
|
async editVideo(originalEvent, updatedVideoData, pubkey) {
|
||||||
if (!pubkey) {
|
if (!pubkey) {
|
||||||
throw new Error("User is not logged in.");
|
throw new Error("User is not logged in.");
|
||||||
@@ -382,7 +384,7 @@ class NostrClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Soft-delete or hide an existing video by marking content as "deleted: true"
|
* Soft-delete or hide an existing video by marking content as "deleted: true"
|
||||||
* and republishing with same (kind=30078, pubkey, d) address.
|
* and republishing with the same (kind=30078, pubkey, d) address.
|
||||||
*/
|
*/
|
||||||
async deleteVideo(originalEvent, pubkey) {
|
async deleteVideo(originalEvent, pubkey) {
|
||||||
if (!pubkey) {
|
if (!pubkey) {
|
||||||
@@ -407,6 +409,7 @@ class NostrClient {
|
|||||||
const oldContent = JSON.parse(originalEvent.content || "{}");
|
const oldContent = JSON.parse(originalEvent.content || "{}");
|
||||||
const oldVersion = oldContent.version ?? 1;
|
const oldVersion = oldContent.version ?? 1;
|
||||||
|
|
||||||
|
// Mark it "deleted" and clear out magnet, thumbnail, etc.
|
||||||
const contentObject = {
|
const contentObject = {
|
||||||
version: oldVersion,
|
version: oldVersion,
|
||||||
deleted: true,
|
deleted: true,
|
||||||
@@ -418,6 +421,7 @@ class NostrClient {
|
|||||||
isPrivate: oldContent.isPrivate || false,
|
isPrivate: oldContent.isPrivate || false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Reuse the same d-tag for an addressable edit
|
||||||
const event = {
|
const event = {
|
||||||
kind: 30078,
|
kind: 30078,
|
||||||
pubkey,
|
pubkey,
|
||||||
@@ -451,10 +455,7 @@ class NostrClient {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isDevMode) {
|
if (isDevMode) {
|
||||||
console.error(
|
console.error(`Failed to publish deleted event to ${url}:`, err);
|
||||||
`Failed to publish deleted event to ${url}:`,
|
|
||||||
err.message
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -463,117 +464,141 @@ class NostrClient {
|
|||||||
return signedEvent;
|
return signedEvent;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isDevMode) {
|
if (isDevMode) {
|
||||||
console.error("Failed to sign deleted event:", error.message);
|
console.error("Failed to sign deleted event:", error);
|
||||||
}
|
}
|
||||||
throw new Error("Failed to sign deleted event.");
|
throw new Error("Failed to sign deleted event.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches videos from all configured relays.
|
* Subscribes to video events from all configured relays, storing them in a Map.
|
||||||
|
*
|
||||||
|
* @param {Function} onVideo - Callback fired for each new/updated video
|
||||||
|
*/
|
||||||
|
subscribeVideos(onVideo) {
|
||||||
|
const filter = {
|
||||||
|
kinds: [30078],
|
||||||
|
"#t": ["video"],
|
||||||
|
limit: 500, // Adjust as needed
|
||||||
|
since: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isDevMode) {
|
||||||
|
console.log("[subscribeVideos] Subscribing with filter:", filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create subscription across all relays
|
||||||
|
const sub = this.pool.sub(this.relays, [filter]);
|
||||||
|
|
||||||
|
sub.on("event", (event) => {
|
||||||
|
try {
|
||||||
|
const content = JSON.parse(event.content);
|
||||||
|
|
||||||
|
// If marked deleted
|
||||||
|
if (content.deleted === true) {
|
||||||
|
// Remove it from our Map if we had it
|
||||||
|
if (this.subscribedVideos.has(event.id)) {
|
||||||
|
this.subscribedVideos.delete(event.id);
|
||||||
|
// Optionally notify the callback so UI can remove it
|
||||||
|
// onVideo(null, { deletedId: event.id });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a video object
|
||||||
|
const video = {
|
||||||
|
id: event.id,
|
||||||
|
version: content.version ?? 1,
|
||||||
|
isPrivate: content.isPrivate ?? false,
|
||||||
|
title: content.title || "",
|
||||||
|
magnet: content.magnet || "",
|
||||||
|
thumbnail: content.thumbnail || "",
|
||||||
|
description: content.description || "",
|
||||||
|
mode: content.mode || "live",
|
||||||
|
pubkey: event.pubkey,
|
||||||
|
created_at: event.created_at,
|
||||||
|
tags: event.tags,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if we already have it in our Map
|
||||||
|
if (!this.subscribedVideos.has(event.id)) {
|
||||||
|
// It's new, so store it
|
||||||
|
this.subscribedVideos.set(event.id, video);
|
||||||
|
// Then notify the callback that a new video arrived
|
||||||
|
onVideo(video);
|
||||||
|
} else {
|
||||||
|
// Optional: if you want to detect edits, compare the new vs. old and update
|
||||||
|
// this.subscribedVideos.set(event.id, video);
|
||||||
|
// onVideo(video) to re-render, etc.
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (isDevMode) {
|
||||||
|
console.error("[subscribeVideos] Error parsing event:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.on("eose", () => {
|
||||||
|
if (isDevMode) {
|
||||||
|
console.log("[subscribeVideos] Reached EOSE for all relays");
|
||||||
|
}
|
||||||
|
// Optionally: onVideo(null, { eose: true }) to signal initial load done
|
||||||
|
});
|
||||||
|
|
||||||
|
return sub; // so you can unsub later if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A one-time, bulk fetch of videos from all configured relays.
|
||||||
|
* (Limit has been reduced to 300 for better performance.)
|
||||||
*/
|
*/
|
||||||
async fetchVideos() {
|
async fetchVideos() {
|
||||||
const filter = {
|
const filter = {
|
||||||
kinds: [30078],
|
kinds: [30078],
|
||||||
"#t": ["video"],
|
"#t": ["video"],
|
||||||
limit: 1000,
|
limit: 300, // Reduced from 1000 for quicker fetches
|
||||||
since: 0,
|
since: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const videoEvents = new Map();
|
const videoEvents = new Map();
|
||||||
|
|
||||||
if (isDevMode) {
|
|
||||||
console.log("[fetchVideos] Starting fetch from all relays...");
|
|
||||||
console.log("[fetchVideos] Filter:", filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Query each relay in parallel
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
this.relays.map(async (url) => {
|
this.relays.map(async (url) => {
|
||||||
if (isDevMode) console.log(`[fetchVideos] Querying relay: ${url}`);
|
const events = await this.pool.list([url], [filter]);
|
||||||
|
for (const evt of events) {
|
||||||
try {
|
try {
|
||||||
const events = await this.pool.list([url], [filter]);
|
const content = JSON.parse(evt.content);
|
||||||
|
if (content.deleted) {
|
||||||
if (isDevMode) {
|
videoEvents.delete(evt.id);
|
||||||
console.log(`Events from ${url}:`, events.length);
|
} else {
|
||||||
if (events.length > 0) {
|
videoEvents.set(evt.id, {
|
||||||
events.forEach((evt, idx) => {
|
id: evt.id,
|
||||||
console.log(
|
pubkey: evt.pubkey,
|
||||||
`[fetchVideos] [${url}] Event[${idx}] ID: ${evt.id} | pubkey: ${evt.pubkey} | created_at: ${evt.created_at}`
|
created_at: evt.created_at,
|
||||||
);
|
title: content.title || "",
|
||||||
|
magnet: content.magnet || "",
|
||||||
|
thumbnail: content.thumbnail || "",
|
||||||
|
description: content.description || "",
|
||||||
|
mode: content.mode || "live",
|
||||||
|
isPrivate: content.isPrivate || false,
|
||||||
|
tags: evt.tags,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
} catch (e) {
|
||||||
|
console.error("Error parsing event content:", e);
|
||||||
events.forEach((event) => {
|
|
||||||
try {
|
|
||||||
const content = JSON.parse(event.content);
|
|
||||||
|
|
||||||
// If deleted == true, it overrides older notes
|
|
||||||
if (content.deleted === true) {
|
|
||||||
videoEvents.delete(event.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we haven't seen this event.id before, store it
|
|
||||||
if (!videoEvents.has(event.id)) {
|
|
||||||
videoEvents.set(event.id, {
|
|
||||||
id: event.id,
|
|
||||||
version: content.version ?? 1,
|
|
||||||
isPrivate: content.isPrivate ?? false,
|
|
||||||
title: content.title || "",
|
|
||||||
magnet: content.magnet || "",
|
|
||||||
thumbnail: content.thumbnail || "",
|
|
||||||
description: content.description || "",
|
|
||||||
mode: content.mode || "live",
|
|
||||||
pubkey: event.pubkey,
|
|
||||||
created_at: event.created_at,
|
|
||||||
tags: event.tags,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (parseError) {
|
|
||||||
if (isDevMode) {
|
|
||||||
console.error(
|
|
||||||
"[fetchVideos] Event parsing error:",
|
|
||||||
parseError
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (relayError) {
|
|
||||||
if (isDevMode) {
|
|
||||||
console.error(
|
|
||||||
`[fetchVideos] Error fetching from ${url}:`,
|
|
||||||
relayError
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const videos = Array.from(videoEvents.values()).sort(
|
// Turn the Map into a sorted array
|
||||||
|
const allVideos = Array.from(videoEvents.values()).sort(
|
||||||
(a, b) => b.created_at - a.created_at
|
(a, b) => b.created_at - a.created_at
|
||||||
);
|
);
|
||||||
|
return allVideos;
|
||||||
// Apply access control filtering
|
} catch (err) {
|
||||||
const filteredVideos = accessControl.filterVideos(videos);
|
console.error("fetchVideos error:", err);
|
||||||
|
|
||||||
if (isDevMode) {
|
|
||||||
console.log("[fetchVideos] All relays have responded.");
|
|
||||||
console.log(
|
|
||||||
`[fetchVideos] Total unique video events: ${videoEvents.size}`
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`[fetchVideos] Videos after filtering: ${filteredVideos.length}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return filteredVideos;
|
|
||||||
} catch (error) {
|
|
||||||
if (isDevMode) {
|
|
||||||
console.error("FETCH VIDEOS ERROR:", error);
|
|
||||||
}
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user