diff --git a/assets/svg/upload-content-icon.svg b/assets/svg/upload-content-icon.svg new file mode 100644 index 00000000..4f4904b7 --- /dev/null +++ b/assets/svg/upload-content-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/upload-modal.html b/components/upload-modal.html index 4de81d6e..02c49fb6 100644 --- a/components/upload-modal.html +++ b/components/upload-modal.html @@ -124,7 +124,13 @@

Cloudflare R2 Credentials

Stored locally -

Required for direct file uploads.

+
+

Required for direct file uploads.

+ + + Learn How + +
diff --git a/content/upload-content.md b/content/upload-content.md new file mode 100644 index 00000000..01d3cfcd --- /dev/null +++ b/content/upload-content.md @@ -0,0 +1,44 @@ +# How to Upload Content + +bitvid is a decentralized platform, but for larger video files, we currently support **Cloudflare R2** as a storage backend. This allows you to upload files directly from your browser to a high-performance, affordable storage bucket. + +## Prerequisites + +- A **Cloudflare Account** (free to create). +- An activated **R2 Plan** (there is a generous free tier, but you may need to add a payment method). + +## Step-by-Step Guide + +### 1. Create an API Token + +To let bitvid upload files to your bucket, you need an API Token with permission to edit R2 storage. + +1. Log in to your [Cloudflare Dashboard](https://dash.cloudflare.com/). +2. Navigate to **R2** in the sidebar. +3. On the right side of the R2 Overview page, look for **"Manage R2 API Tokens"** and click it. +4. Click the **"Create API Token"** button. +5. **Configure the token:** + - **Token name**: Enter something recognizable, e.g., "bitvid-upload". + - **Permissions**: Select **"Admin Read & Write"** (easiest) or select the specific **"Workers R2 Storage"** permission with **"Edit"** access. + - **Specific Bucket(s)**: You can grant access to "All buckets" or a specific one if you already created it. + - **TTL**: Set this to "Forever" or a duration of your choice. +6. Click **"Create API Token"**. + +### 2. Copy Your Credentials + +Once created, you will see your credentials. **Do not close this page yet!** You cannot see the Secret Access Key again. + +Copy the following values: + +- **Access Key ID**: Usually starts with a random string. +- **Secret Access Key**: A long string of characters (this is your password). +- **Account ID**: This is found in the URL of your dashboard or on the R2 Overview page (e.g., `https://dash.cloudflare.com//r2`). + +### 3. Configure bitvid + +1. Return to the **Upload Video** modal in bitvid. +2. Click **"Configure R2 Storage"**. +3. Enter your **Account ID**, **Access Key ID**, and **Secret Access Key**. +4. Click **"Save Credentials"**. + +bitvid will automatically attempt to create a private bucket for your videos if one doesn't exist, or you can use the "Advanced Options" to specify a custom bucket or domain. diff --git a/verification/upload_content_page.png b/verification/upload_content_page.png new file mode 100644 index 00000000..6d4227d9 Binary files /dev/null and b/verification/upload_content_page.png differ diff --git a/verification/upload_modal_link.png b/verification/upload_modal_link.png new file mode 100644 index 00000000..4970503a Binary files /dev/null and b/verification/upload_modal_link.png differ diff --git a/verification/verify_upload.py b/verification/verify_upload.py new file mode 100644 index 00000000..305414bd --- /dev/null +++ b/verification/verify_upload.py @@ -0,0 +1,69 @@ + +from playwright.sync_api import sync_playwright, expect + +def run(playwright): + browser = playwright.chromium.launch(headless=True) + page = browser.new_page() + + # Navigate to the app + page.goto("http://localhost:8080") + + # Wait for app + page.wait_for_selector("#app") + + # Dismiss disclaimer if present + try: + if page.locator("#disclaimerModal").is_visible(): + page.locator("#disclaimerModal").get_by_role("button", name="I understand").click(timeout=2000) + except: + pass + + # 1. Verify the "Upload Content" Page + print("Navigating to Upload Content view...") + page.goto("http://localhost:8080/#view=upload-content") + page.wait_for_timeout(2000) + + expect(page.locator("h2#uploadContentHeading")).to_have_text("Upload Content") + page_icon = page.locator("#viewContainer img[src='assets/svg/upload-content-icon.svg']") + expect(page_icon).to_be_visible() + page.screenshot(path="verification/upload_content_page.png") + print("Screenshot of page saved.") + + # 2. Verify the Link in Upload Modal + # Force remove 'hidden' class from uploadButton + page.evaluate("document.getElementById('uploadButton').classList.remove('hidden')") + + # Wait for it to be visible? Playwright waits for visibility. + # Sometimes removing class isn't enough if parent is hidden, but parent is visible header. + # The previous error said "element is not visible". + # Let's force click via JS if UI click fails, or force visible style. + + page.evaluate("document.getElementById('uploadButton').style.display = 'block'") + page.wait_for_selector("#uploadButton", state="visible") + page.locator("#uploadButton").click() + + # Verify modal visible + modal = page.locator("#uploadModal") + # Modal also has 'hidden' class toggled by JS. + # Wait for it. + expect(modal).to_be_visible() + + # Open storage settings + page.locator("#btn-storage-settings").click() + + # Verify link + link = page.locator("#section-storage-settings").get_by_role("link", name="Learn How") + expect(link).to_be_visible() + expect(link).to_have_attribute("href", "#view=upload-content") + + # Verify link icon + link_icon = link.locator("img") + expect(link_icon).to_have_attribute("src", "assets/svg/upload-content-icon.svg") + + page.screenshot(path="verification/upload_modal_link.png") + print("Screenshot of modal saved.") + + browser.close() + +with sync_playwright() as playwright: + run(playwright) diff --git a/views/upload-content.html b/views/upload-content.html new file mode 100644 index 00000000..7b5fce87 --- /dev/null +++ b/views/upload-content.html @@ -0,0 +1,77 @@ + +
+
+
+
+ +

+ Upload Content +

+
+
+

Loading content...

+
+
+
+
+ +