- Replaced the button with a simple text link (`<a id="link-r2-help">`) inside the R2 credentials section.
- Added a click event listener in `UploadModal.js` to call `this.close()` when the link is clicked, allowing users to navigate to the setup guide without obstruction.
- Modified `js/ui/components/UploadModal.js` to hide the thumbnail browse button when in 'External Link' mode.
- Ensured the browse button is visible when switching back to 'Upload File' mode.
- Added logic to clear the thumbnail file input when resetting the form.
- Added file input and browse button for thumbnails in `upload-modal.html`.
- Updated `UploadModal.js` to handle thumbnail file selection and UI toggling.
- Updated `r2Service.js` to upload thumbnail file (if present) to R2 and update metadata.
- Ensured thumbnail keys are derived from video keys for consistent naming.
- Add new "Setup Guide" section to `docs/storage-r2.md` detailing the dual-credential requirement (S3 Keys + Cloudflare API Token) for full automation.
- Remove unsupported "manual/S3-only" workflow documentation to prevent user confusion.
- Update `js/services/r2Service.js` error message to explicitly prompt users for an API Token when the public domain is missing, instead of suggesting manual configuration.
- Refactored `SimilarContentCard.js` to use Flexbox layout.
- Updated `css/tailwind.source.css` to remove grid layout and use standard flex utilities.
- Improved thumbnail sizing (16:9, fixed width) and responsiveness.
- Added hover effects (background color, thumbnail zoom).
- Cleaned up typography and metadata hierarchy.
- Replaces split "Custom" vs "Cloudflare" forms with a single unified form
- Implements source switching between "Upload File" and "External Link"
- Consolidates NIP-71 advanced fields into a single collapsible section
- Moves R2 storage configuration to a dedicated, collapsible settings block
- Improves responsiveness and modernizes the UI layout
- Updates internal JS logic to handle unified form submission and validation
Integrates WebTorrent client-side seeding into the Cloudflare R2 upload path.
- Adds `uploadFile` to `r2Service` for generic file uploads.
- Updates `UploadModal` to offer an optional "Create torrent & seed" flow.
- Generates magnet links and .torrent files in the browser after R2 upload.
- Uploads the .torrent file to R2 and populates `xs` tag.
- Populates `ws` (Web Seed) with the R2 public URL.
- Adds UI progress indicators for hashing and torrent upload.
- Enforces a 2GB file size limit for browser seeding to prevent crashes.
- Adds a mandatory legal rights confirmation checkbox for R2 uploads.
- Rename "Legacy description" to "Video description"
- Group technical NIP-71 fields (Event Type, IMETA, Tracks, Segments) into a hidden "Advanced Options" section
- Automate "Summary" field to mirror "Video description" by default (with unlock toggle)
- Automate "Web seed" field to mirror "Hosted video URL"
- Automate "Content Warning" based on NSFW toggle status
- Maintain visibility of Hashtags in the main form flow
- Apply changes consistently to both Custom and Cloudflare R2 upload modes
- Fix cleanup handler registration for advanced toggle listener
- Rename "Legacy description" to "Video description"
- Group technical NIP-71 fields (Event Type, IMETA, Tracks, Segments) into a hidden "Advanced Options" section
- Automate "Summary" field to mirror "Video description" by default (with unlock toggle)
- Automate "Web seed" field to mirror "Hosted video URL"
- Automate "Content Warning" based on NSFW toggle status
- Maintain visibility of Hashtags in the main form flow
- Apply changes consistently to both Custom and Cloudflare R2 upload modes
- Add a confirmation dialog in `UploadModal.js` when a user uploads a video with only a magnet link to warn about potential data loss.
- Replace the hardcoded limit of 300 in `NostrClient.fetchVideos` with a configurable limit that respects `DEFAULT_VIDEO_REQUEST_LIMIT` and `MAX_VIDEO_REQUEST_LIMIT`, improving client-side scalability.
- Implements `hasDisinterestedTag` helper in `js/feedEngine/utils.js` to check for disinterested tags in both raw Nostr tags and NIP-71 metadata.
- Updates `createBlacklistFilterStage` in `js/feedEngine/stages.js` to filter out videos matching `context.runtime.tagPreferences` disinterests.
- Ensures existing blacklist logic remains undisturbed.
- Verified with new unit test `tests/feed-engine-tag-pref.test.mjs` and existing feed engine tests.
Removes the fixed height and overflow from the watch history container in the profile modal, allowing it to expand fully. Updates the profile modal controller to use the main modal panes container as the scroll root for infinite scrolling.
This change introduces `createWatchHistoryProfileHydrationStage` to the
watch history feed pipeline. The stage resolves author profiles for
hydrated videos by:
1. Checking the global `getProfileCacheEntry` for cached profiles.
2. Batching missing profiles into a single Kind 0 relay request.
3. Updating the global cache (`setProfileCacheEntry`) with fetched data.
4. Attaching the resolved profile to `item.metadata.profile`.
This ensures that channel images populate correctly in the watch history
UI (both sidebar and profile modal).
- Shorten npubs in history view cards.
- Add Refresh, Privacy Banner, and Info to Profile Modal Watch History.
- Wire up Profile Modal Watch History controls.
Replaces unreliable esm.sh imports with cdn.jsdelivr.net to prevent site crashes due to CORS and timeout errors.
Updates imports for `nostr-tools`, `buffer`, and `@aws-sdk/client-s3` in `js/nostrToolsBootstrap.js`, `js/bufferPolyfill.js`, and `js/storage/r2-s3.js`.
Adds jsdelivr as a prioritized remote source in `nostrToolsBootstrap` while retaining esm.sh as a fallback.
When relays fail to connect or do not send EOSE, the legacy `pool.list` shim
would hang indefinitely, blocking watch history hydration.
This change adds a timeout (default 7000ms) to ensure it returns whatever events
it has collected, preventing the UI from getting stuck.
This commit addresses issues with the watch history not updating dynamically and adds a manual refresh mechanism.
Changes:
- Added a "Refresh" button to the watch history view in `views/history.html` and `js/historyView.js`.
- Updated `watchHistoryService.js` to accept a `forceRefresh` option in `loadLatest`, which bypasses the cache freshness check and triggers a relay fetch.
- Updated `js/feedEngine/watchHistoryFeed.js` to propagate the `forceRefresh` flag from the runtime context to the service.
- Added user feedback (toast messages and logs) for "Clear Local History" and "Republish Now" actions.
- Updated `tests/watch-history.test.mjs` to align with the new `fetchHistory` signature.
This patch updates `js/payments/zapSplit.js` and `js/payments/nwcClient.js` to replace legacy manual event signing (which relied on `nostr-tools` v1 methods `getEventHash` and `signEvent` that are missing in v2) with the centralized `signEventWithPrivateKey` helper from `js/nostr/publishHelpers.js`. This ensures compatibility with the installed `nostr-tools` version and resolves the error preventing zaps.
This addresses an issue where comments would not display on the first load of the video modal if the DOM elements were not fully bound. The `open()` method now checks if the `commentsList` is missing or disconnected and re-initializes the `CommentsController` if necessary.
Also updated `tests/video-modal-accessibility.test.mjs` to fix a JSDOM environment issue with `navigator` mocking.
- Updated `hydratePointer` in `js/viewCounter.js` to only update local view counts if the network count is strictly greater (or if local state is uninitialized).
- This prevents regressions where a partial or stale network response would overwrite a higher, accurate local count.
- Updated `tests/view-counter.test.mjs` to reflect that fresh view events are additive to the cached high watermark (fixing a test expectation that relied on the old non-monotonic behavior).
- Updated `tests/view-counter.test.mjs` to match `resolveVideoPointer` behavior which prioritizes `dTag` (NIP-33) over `videoRootId`, fixing a legacy test failure.
- Enforce descending month order in `js/nostr/watchHistory.js` by explicitly sorting keys before flattening buckets.
- Update `js/feedEngine/watchHistoryFeed.js` and `js/historyView.js` to use `video.created_at` as a fallback sort key when `watchedAt` is missing or equal.
- Verified logic with `tests/verify_sort.mjs`.
Replace sessionStorage with localStorage to ensure pending watch history updates persist across tab closures and sessions. Update `loadLatest` to always restore queue state on initialization to catch any pending uploads.
Removed the `ACCEPT_LEGACY_V1` flag and associated logic that supported parsing magnet links from unstructured text notes or tags, as well as support for video events with version < 2.
This simplifies `js/videoEventUtils.js` and `js/nostr/nip71.js`, and removes legacy regex scanning.
Deleted `tests/legacy-infohash.test.mjs` which tested this deprecated functionality.
Guards the `fetch("/webtorrent/cancel/")` call in `js/app.js` with a check for `navigator.serviceWorker.controller` and adds a `try...catch` block. This prevents the application from making a network request to a virtual route that doesn't exist on the server when the Service Worker is not active or controlling the page, eliminating "404 Not Found" errors in the console during cleanup.
- Removed `WATCH_HISTORY_LEGACY_KIND` (30078) support from watch history fetch and config.
- Removed `WATCH_HISTORY_LEGACY_LIST_IDENTIFIERS` from config.
- Removed `LEGACY_EVENTS_STORAGE_KEY` (bitvidEvents) migration logic from client.
- Removed `loadLegacyAdminState` and `LEGACY_STORAGE_KEYS` from admin list store.
- Updated tests to remove references to legacy kinds.
Migrates watch history persistence from Kind 30078 (Application Data) to Kind 30003 (Bookmark List) to resolve blocking issues on some relays. Updates fetch logic to query both kinds for backward compatibility.
Updates `shouldUseExtensionForHistory` in `js/watchHistoryService.js` to check for `signEvent` instead of `nip04.decrypt`. This ensures that logged-in users can sync their watch history (which is now plaintext) even if they haven't granted the legacy encryption permission.
When creating address pointers (`a` tags) for watch history, `resolveVideoPointer` was incorrectly prioritizing `videoRootId` over the actual NIP-33 `d` tag. If these values differed (common in newly published videos), the resulting pointer would reference a non-existent identifier, causing subsequent hydration to fail.
This change swaps the order to prioritize the `d` tag, ensuring watch history entries use the correct addressable identifier.