From 000127f88ceecade2bf7042d0614526f53574fe5 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:57:35 -0400 Subject: [PATCH] update --- README.md | 20 ++++++++++++-------- content/roadmap.md | 15 +++++++++++---- docs/design-system.md | 7 +++++++ docs/nostr-auth.md | 9 +++++---- js/services/watchHistoryTelemetry.js | 10 +++++----- js/ui/ambientBackground.js | 23 ++++++++++++++++++++--- js/utils/videoDeduper.js | 10 +++++++++- tests/utils/video-deduper.test.mjs | 23 +++++++++++++++++++++++ 8 files changed, 92 insertions(+), 25 deletions(-) create mode 100644 tests/utils/video-deduper.test.mjs diff --git a/README.md b/README.md index 2ca7f1a2..3d08eafb 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,15 @@ ## Features -- **Decentralized Sharing**: Video sharing without central servers. -- **Cloudflare R2 Uploads**: Publish directly from the Upload modal's Cloudflare flow with progress tracking and credential helpers. +- **Decentralized Sharing**: Publish and browse videos without a centralized server. +- **Channel profile pages**: The [channel view](views/channel-profile.html) and `js/channelProfile.js` render banners, playlists, links, and follow stats so every creator has a branded landing page. +- **Audience flags**: The [Upload](components/upload-modal.html) and [Edit Video](components/edit-video-modal.html) modals expose **NSFW** and **For Kids** toggles that map straight to note metadata for safer discovery. +- **Richer metadata repeaters**: Configure variants, captions, segments, participants, references, and hashtags directly in the Upload modal so posts ship with structured context. +- **Cloudflare R2 Uploads**: Publish through the modal’s Cloudflare flow with progress tracking and credential helpers. - **Encrypted Watch History**: Sync viewing activity privately through the NIP-04 encrypted pipeline with local fallbacks. - **Live View Counters**: Subscribe to view events and see totals update in real time on video cards and the video modal. - **Lightning Zaps**: Tip creators with Lightning payments via the Zap controls in the video modal. -- **Private Video Listings**: Hide cards from shared grids by flipping the visibility switch in the [Edit Video modal](components/edit-video-modal.html) after publishing. +- **Private Video Listings**: Hide cards from shared grids by flipping the visibility switch in the Edit Video modal after publishing. - **Nostr Integration**: Use Nostr keys for login and interaction. - **WebTorrent Streaming**: Stream videos directly through torrent technology. - **Developer-Friendly**: Open source and customizable for your needs. @@ -120,9 +123,10 @@ Tailwind utilities are generated from `css/tailwind.source.css` and themed via the shared tokens in `css/tokens.css`. Install dependencies once and lean on the package scripts to keep formatting, linting, and generated output consistent: -- **Token imports:** `css/tokens.css` feeds `css/style.css`, which in turn seeds - `tailwind.config.cjs` so utilities inherit the same palette, spacing, and - typography primitives across the stack. +- **Token imports:** `css/tailwind.source.css` imports `css/tokens.css`, and + Tailwind consumes that source file directly so utilities inherit the same + palette, spacing, and typography primitives across the stack—no + `css/style.css` intermediary. - **Core scripts:** ```bash @@ -140,7 +144,7 @@ package scripts to keep formatting, linting, and generated output consistent: ```bash npm install # install Prettier, Stylelint, and Tailwind toolchain npm run format # format CSS/HTML/JS/MD with Prettier + tailwindcss plugin -npm run lint # run CSS, hex color, and inline-style guards in one pass +npm run lint # run CSS, hex color, inline-style, design-token, and Tailwind color/bracket guards in one pass npm run lint:css # enforce token usage and forbid raw hex colors npm run lint:inline-styles # fail CI if inline style attributes or element.style usage slip in npm run build:css # rebuild css/tailwind.generated.css from tailwind.source.css @@ -313,7 +317,7 @@ placeholder at “—” and development builds log a warning—so mixed deploym ### Contribution Guidelines -- Follow the [MIT License](https://opensource.org/licenses/MIT). +- Follow the [GPL-3.0-or-later License](LICENSE). - Use clear, concise commit messages. - Respect the existing coding style and architecture. - Run the manual QA script (see below) and note results in PR descriptions for changes that affect upload or playback. diff --git a/content/roadmap.md b/content/roadmap.md index 57169255..29b9a21a 100644 --- a/content/roadmap.md +++ b/content/roadmap.md @@ -7,13 +7,16 @@ - **Zap controls** – Channel profiles manage zap visibility, login gating, retry flows, and receipt rendering via the Lightning controller on the page. - **Source visibility filtering** – Lists now hide cards whose hosted URLs are offline unless their WebTorrent fallback is healthy, reusing the shared guard in `js/utils/cardSourceVisibility.js`. - **Video modal comments** – The video modal now surfaces in-context comment threads so viewers can follow discussions without leaving the player. +- **Channel profile views** – `views/channel-profile.html` and `js/channelProfile.js` render dedicated channel landing pages with banners, follow stats, playlists, and contact links. +- **Audience targeting toggles** – The [Upload modal](components/upload-modal.html) and [Edit Video modal](components/edit-video-modal.html) ship **NSFW** and **For Kids** toggles that map to the note’s moderation flags. +- **Hashtag metadata repeater** – Upload metadata repeaters now support hashtag sets so creators can publish discovery tags alongside variants, captions, and participants. +- **Dev/Live publishing fix** – Publishing honors the selected mode, preventing Dev-mode uploads from leaking into Live feeds. ## Bug Fixes > ⚠️ **Note:** If you find a new bug thats not listed here. Please submit a [Bug Report](https://bitvid.network/?modal=bug). - Speed up loading in subscriptions. Save to local cache? -- Fix "Dev Mode" publishing "Live Mode" notes—add a flag for dev mode posts. - Fix slow back button issues on Firefox. - Add Amber login support for mobile. - Add support for "Playlist" lists and other custom lists (named whatever) and also a "Watch Later" list. @@ -21,6 +24,8 @@ - Add "add to watch later" option to edit button. - Fix autoplay broken on iPhone chrome. - Fix playback broken on Safari on iPhone. +- **[14]** Get Watch History working reliably so the view stays in sync across devices and sessions. +- **[15]** Fix Watch History bugs for various login methods to keep timelines aligned regardless of how users authenticate. - Update note spec v3 transition to include new fields that have been added like "has previous" or "videoRootID" or enentID if null. - Change "explore" view to "kids" view and add flag to all notes to see if they are for kids. - Add "seed" lists. @@ -33,12 +38,11 @@ - Allow users to set custom relay settings, stored in local cache. - Add a "Publish" step in the video editing process. -- Add Profile/Channel Views. [Click for more details](https://github.com/PR0M3TH3AN/bitvid/blob/main/content/roadmap/04_bitvid_Enhanced_Profile_Channel_Views_Specification.md) +- Add a relay note search surface with filters for post date, hashtags, title keywords, view counts, and the publishing account. +- Add a mobile search control with a floating icon that expands into a full search field on small screens. - Improve event spec for migration to new versions. [Click for more details](https://github.com/PR0M3TH3AN/bitvid/blob/main/content/roadmap/02_bitvid_Enhanced_Migration_of_Note_Spec_Logic.md) - Migrate event spec to v3 and add support for Audio/Podcast content. [Click for more details](https://github.com/PR0M3TH3AN/bitvid/blob/main/content/roadmap/03_bitvid_Enhanced_Nostr_Video_%26_Audio_Note_Specification_Version%203.md) - Add Block List, Subscription List, Playlist, and Reporting Specification. [Click for more details](https://github.com/PR0M3TH3AN/bitvid/blob/main/content/roadmap/05_bitvid_Enhanced_Block_Subscription_%26_Reporting_Specification.md) -- Implement an "Adult Content" flag for note submissions. -- Enable custom hashtags in the submission spec and form. (Use with future search system) - Allow multiple video resolutions with a selector in the video player. (v3 event spec needed) - Introduce a subscription mechanism with notifications. - Add zaps to videos, profiles, and comments. @@ -48,5 +52,8 @@ - Add multi-language support for content and filtration. (v4?) - Create a settings menu for local account preferences, including relay, adult content, theme, and language. - Better integrate with other Nostr/torrent ecosystem. (NIP-35 + WebRTC Check Integration) [Click for more details](https://github.com/PR0M3TH3AN/bitvid/blob/main/content/roadmap/09_bitvid_Enhanced_NIP-35_%2B_WebRTC_Check_Integration.md) +- **[16]** Add ability to write and send DMs so creators and viewers can coordinate privately. +- **[17]** Add a "Live" live stream view (view only for now) that imports Zapstream notes and displays the live streams within the bitvid environment. +- **[18]** Add ability to create and post livestreams to R2 storage so streamers can broadcast directly from bitvid. > ⚠️ **Note:** Your feedback helps us improve bitvid! Whether it’s a suggestion, a concern, or general thoughts on the platform, we’d love to hear from you. Please tell us what you think [with this form](https://bitvid.network/?modal=feedback). diff --git a/docs/design-system.md b/docs/design-system.md index 50c43107..8f6e44a7 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -326,6 +326,13 @@ Tailwind now exposes matching utilities for layouts that opt into the design sys Iconography and motion helpers rely on fresh tokens as well: `--icon-size-sm`, `--icon-size-md`, and `--icon-size-lg` standardise glyph sizing across nav links, collapse toggles, and dropup triggers, while `--space-sidebar-nav-translate` powers the translate offsets used when collapsing the rail. Thin overlays inherit `--scrollbar-thin-width` so modal and sidebar scroll containers feel identical. +### Search surfaces backlog + +Discovery work is queued behind two search affordances that will need close coordination with the navigation shell tokens: + +- **Relay note search filters** — Stand up a dedicated search surface that can index relay-published notes and expose filter chips for posted date ranges, hashtags, title keywords, view-count ranges, and the publishing account. Reuse `.menu`, `.input`, and `.chip` primitives for the filter controls so the surface stays aligned with existing command panels, and plan on routing results through the popover/filter pipeline described above. +- **Mobile floating search** — Introduce a floating search icon on small breakpoints that expands into a full-width search field when tapped. The trigger should inherit the circular icon tokens used by sidebar toggles, snap to the `z-overlay-nav` layer, and collapse back to the icon state once the user blurs or submits the field. + ### Menus `.menu` normalises list-based command menus with consistent padding. Pair it with `.menu__heading` for optional section labels, `.menu__separator` for dividers, and `.menu__item` for actionable rows. Each item supports hover, active (`data-state="active"`), and critical (`data-variant="critical"`) palettes plus disabled fallbacks through either `disabled` or `aria-disabled="true"`. diff --git a/docs/nostr-auth.md b/docs/nostr-auth.md index cc59e1b0..986e7031 100644 --- a/docs/nostr-auth.md +++ b/docs/nostr-auth.md @@ -50,10 +50,11 @@ Notes: `authType` describes how the profile authenticated: -- `"nip07"` – Browser extension flow (current default). -- `"nsec"` – Reserved for future direct key import or signer integrations. New - auth strategies should pick a distinct string and document how migration works - alongside any recovery tooling. +1. `"nip07"` – Browser extension flow that delegates signing to a NIP-07 compatible provider embedded in the browser. +2. `"nsec"` – Direct key import where the private key stays in the client and bitvid signs locally without an external signer. +3. `"nip46"` – Remote signer flow that relies on a NIP-46 capable relay or service to authorize requests on behalf of the user. + +When introducing additional providers, choose a unique string value and extend this section (plus any related migration notes) to explain how the new strategy authenticates and how existing data should transition. When the app reads stored entries it normalises unknown values back to `"nip07"` but keeps recognised alternatives intact. diff --git a/js/services/watchHistoryTelemetry.js b/js/services/watchHistoryTelemetry.js index 14d565f1..6c3fc5dd 100644 --- a/js/services/watchHistoryTelemetry.js +++ b/js/services/watchHistoryTelemetry.js @@ -16,6 +16,9 @@ function sanitizeVideoMetadata(video) { return null; } + // Transport identifiers (URL, magnet, hashes) are intentionally omitted so + // watch history storage never persists playback endpoints. Only surface the + // minimal fields needed to render the saved entry. const createdAtCandidates = [ video.rootCreatedAt, video.created_at, @@ -37,11 +40,6 @@ function sanitizeVideoMetadata(video) { thumbnail: typeof video.thumbnail === "string" ? video.thumbnail : "", pubkey: typeof video.pubkey === "string" ? video.pubkey : "", created_at: createdAt, - url: typeof video.url === "string" ? video.url : "", - magnet: typeof video.magnet === "string" ? video.magnet : "", - infoHash: typeof video.infoHash === "string" ? video.infoHash : "", - legacyInfoHash: - typeof video.legacyInfoHash === "string" ? video.legacyInfoHash : "", }; } @@ -192,6 +190,8 @@ export default class WatchHistoryTelemetry { } const metadata = { + // Persist only sanitized metadata so local history never stores playback + // transports or magnet fingerprints. video: sanitizeVideoMetadata(video), }; diff --git a/js/ui/ambientBackground.js b/js/ui/ambientBackground.js index 305952b3..e51c42ac 100644 --- a/js/ui/ambientBackground.js +++ b/js/ui/ambientBackground.js @@ -1,5 +1,7 @@ // js/ui/ambientBackground.js +import { devLogger as defaultLogger } from "../utils/logger.js"; + const DEFAULT_THROTTLE_MS = 66; function getGlobalFromCanvas(canvas) { @@ -100,14 +102,29 @@ export function attachAmbientBackground(videoElement, canvasElement, options = { return () => {}; } - const ctx = canvasElement.getContext("2d", { alpha: false }); + const normalizedOptions = + options && typeof options === "object" ? options : {}; + const logger = normalizedOptions.logger || defaultLogger; + + let ctx = null; + try { + ctx = canvasElement.getContext("2d", { alpha: false }); + } catch (error) { + if (logger && typeof logger.warn === "function") { + logger.warn("[ambientBackground] Canvas context unavailable:", error); + } + return () => {}; + } if (!ctx) { + if (logger && typeof logger.warn === "function") { + logger.warn("[ambientBackground] Canvas context unavailable."); + } return () => {}; } const { doc, win } = getGlobalFromCanvas(canvasElement); - const throttleMs = Number.isFinite(options.throttleMs) - ? Math.max(0, options.throttleMs) + const throttleMs = Number.isFinite(normalizedOptions.throttleMs) + ? Math.max(0, normalizedOptions.throttleMs) : DEFAULT_THROTTLE_MS; const requestFrame = win?.requestAnimationFrame?.bind(win) || ((cb) => setTimeout(() => cb(Date.now()), throttleMs)); diff --git a/js/utils/videoDeduper.js b/js/utils/videoDeduper.js index 76575c42..bfbe8c05 100644 --- a/js/utils/videoDeduper.js +++ b/js/utils/videoDeduper.js @@ -8,6 +8,11 @@ export function dedupeToNewestByRoot(videos) { const map = new Map(); + const normalizeTimestamp = (value) => { + const numeric = Number(value); + return Number.isFinite(numeric) ? numeric : -Infinity; + }; + for (const vid of videos || []) { if (!vid) { continue; @@ -18,7 +23,10 @@ export function dedupeToNewestByRoot(videos) { } const existing = map.get(rootId); - if (!existing || vid.created_at > existing.created_at) { + if ( + !existing || + normalizeTimestamp(vid.created_at) > normalizeTimestamp(existing.created_at) + ) { map.set(rootId, vid); } } diff --git a/tests/utils/video-deduper.test.mjs b/tests/utils/video-deduper.test.mjs new file mode 100644 index 00000000..5c149584 --- /dev/null +++ b/tests/utils/video-deduper.test.mjs @@ -0,0 +1,23 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +import { dedupeToNewestByRoot } from "../../js/utils/videoDeduper.js"; + +test("dedupeToNewestByRoot replaces entries with missing timestamps", () => { + const original = { id: "abc", created_at: undefined }; + const newer = { id: "abc", created_at: 123 }; + + const [result] = dedupeToNewestByRoot([original, newer]); + + assert.equal(result, newer); +}); + +test("dedupeToNewestByRoot replaces entries with non-numeric timestamps", () => { + const rootId = "root-1"; + const invalid = { id: "old", videoRootId: rootId, created_at: "not-a-number" }; + const newer = { id: "new", videoRootId: rootId, created_at: "456" }; + + const [result] = dedupeToNewestByRoot([invalid, newer]); + + assert.equal(result, newer); +});