From d53ce6742a51d0065e6a0da9dc55f5725f4f088e Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Wed, 8 Oct 2025 09:41:38 -0400 Subject: [PATCH] Hide private videos from non-owners --- js/channelProfile.js | 38 ++++++++++++++++++++++++++++++++++-- js/services/nostrService.js | 24 +++++++++++++++++++++++ js/ui/views/VideoListView.js | 29 +++++++++++++++------------ 3 files changed, 76 insertions(+), 15 deletions(-) diff --git a/js/channelProfile.js b/js/channelProfile.js index 60480634..336fda23 100644 --- a/js/channelProfile.js +++ b/js/channelProfile.js @@ -1689,6 +1689,17 @@ function renderChannelVideosFromList({ ? app.getUnsupportedBtihMessage() : "This magnet link is missing a compatible BitTorrent v1 info hash."; + const fallbackNormalizePubkey = (value) => + typeof value === "string" && value + ? value.trim().toLowerCase() + : ""; + const normalizePubkey = + typeof app?.normalizeHexPubkey === "function" + ? (value) => app.normalizeHexPubkey(value) || fallbackNormalizePubkey(value) + : fallbackNormalizePubkey; + const normalizedViewerPubkey = normalizePubkey(app?.pubkey); + let renderIndex = 0; + const extractPlaybackDetail = (trigger, video) => { if ( app?.videoListView && @@ -1768,10 +1779,24 @@ function renderChannelVideosFromList({ } }; - videos.forEach((video, index) => { + videos.forEach((video) => { + if (!video || !video.id || !video.title) { + return; + } + + const normalizedVideoPubkey = normalizePubkey(video.pubkey); + const canEdit = + Boolean(normalizedVideoPubkey) && + Boolean(normalizedViewerPubkey) && + normalizedVideoPubkey === normalizedViewerPubkey; + + if (video.isPrivate && !canEdit) { + return; + } + app?.videosMap?.set(video.id, video); - const canEdit = video.pubkey === app?.pubkey; + const index = renderIndex++; let hasOlder = false; if (canEdit && video.videoRootId) { hasOlder = app?.hasOlderVersion?.(video, allKnownEventsArray) || false; @@ -1882,6 +1907,15 @@ function renderChannelVideosFromList({ } }); + if (renderIndex === 0) { + if (allowEmptyMessage) { + container.dataset.hasChannelVideos = "false"; + container.innerHTML = `
No videos to display.
`; + return true; + } + return false; + } + container.appendChild(fragment); attachHealthBadges(container); diff --git a/js/services/nostrService.js b/js/services/nostrService.js index 05269cc7..d167df88 100644 --- a/js/services/nostrService.js +++ b/js/services/nostrService.js @@ -80,6 +80,15 @@ function ensureSet(value) { return new Set(); } +function normalizeHexPubkey(pubkey) { + if (typeof pubkey !== "string") { + return ""; + } + + const trimmed = pubkey.trim(); + return trimmed ? trimmed.toLowerCase() : ""; +} + function normalizeUntil(timestamp) { const value = Number(timestamp); if (!Number.isFinite(value) || value <= 0) { @@ -197,6 +206,21 @@ export class NostrService { } } + if (video.isPrivate === true) { + const normalizedVideoPubkey = normalizeHexPubkey(video.pubkey); + const normalizedViewerPubkey = normalizeHexPubkey( + this.nostrClient?.pubkey + ); + + if ( + !normalizedViewerPubkey || + !normalizedVideoPubkey || + normalizedVideoPubkey !== normalizedViewerPubkey + ) { + return false; + } + } + if (this.accessControl && typeof this.accessControl.canAccess === "function") { try { if (!this.accessControl.canAccess(video)) { diff --git a/js/ui/views/VideoListView.js b/js/ui/views/VideoListView.js index 53ddcd49..91ce4050 100644 --- a/js/ui/views/VideoListView.js +++ b/js/ui/views/VideoListView.js @@ -254,28 +254,31 @@ export class VideoListView { const source = Array.isArray(videos) ? videos : []; const dedupedVideos = this.utils.dedupeVideos(source); - this.currentVideos = dedupedVideos.slice(); + const displayVideos = dedupedVideos.filter( + (video) => this.utils.canEditVideo(video) || !video?.isPrivate + ); - this.syncViewCountSubscriptions(dedupedVideos); - this.cleanupThumbnailCache(dedupedVideos); + this.syncViewCountSubscriptions(displayVideos); + this.cleanupThumbnailCache(displayVideos); - if (!dedupedVideos.length) { + if (!displayVideos.length) { this.renderedVideoIds.clear(); this.videoCardInstances = []; if (this.lastRenderedVideoSignature === EMPTY_VIDEO_LIST_SIGNATURE) { - return dedupedVideos; + return displayVideos; } this.lastRenderedVideoSignature = EMPTY_VIDEO_LIST_SIGNATURE; this.container.innerHTML = `No public videos available yet. Be the first to upload one!
`; - return dedupedVideos; + return displayVideos; } - dedupedVideos.sort((a, b) => (b?.created_at || 0) - (a?.created_at || 0)); + displayVideos.sort((a, b) => (b?.created_at || 0) - (a?.created_at || 0)); + this.currentVideos = displayVideos.slice(); - const signaturePayload = dedupedVideos.map((video) => ({ + const signaturePayload = displayVideos.map((video) => ({ id: typeof video?.id === "string" ? video.id : "", createdAt: Number.isFinite(video?.created_at) ? video.created_at @@ -290,7 +293,7 @@ export class VideoListView { const signature = JSON.stringify(signaturePayload); if (signature === this.lastRenderedVideoSignature) { - return dedupedVideos; + return displayVideos; } this.lastRenderedVideoSignature = signature; @@ -300,7 +303,7 @@ export class VideoListView { const allEvents = this.utils.getAllEvents(); const fragment = this.document?.createDocumentFragment?.(); if (!fragment) { - return dedupedVideos; + return displayVideos; } const authorSet = new Set(); @@ -308,7 +311,7 @@ export class VideoListView { const canManageBlacklist = this.utils.canManageBlacklist(); this.videoCardInstances = []; - dedupedVideos.forEach((video, index) => { + displayVideos.forEach((video, index) => { if (!video || !video.id || !video.title) { return; } @@ -457,10 +460,10 @@ export class VideoListView { if (authorSet.size) { this.utils.batchFetchProfiles(authorSet); } - this.utils.refreshDiscussionCounts(dedupedVideos); + this.utils.refreshDiscussionCounts(displayVideos); this.pruneDetachedViewCountElements(); - return dedupedVideos; + return displayVideos; } updateVideo(videoId, patch) {