mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Add "Upload Content" guide and link in Upload Modal
- Create `assets/svg/upload-content-icon.svg` - Create `content/upload-content.md` with Cloudflare R2 setup instructions - Create `views/upload-content.html` to render the guide - Add "Learn How" link to `components/upload-modal.html` in the R2 credentials section
This commit is contained in:
1 parent
c19421f9a5
commit
cd38fd0c2a
7 files changed
+198
-1
No files matched your search
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/></svg>
|
||||
|
After Width: | Height: | Size: 301 B |
@@ -124,7 +124,13 @@
|
||||
<h4 class="text-sm font-semibold text-text">Cloudflare R2 Credentials</h4>
|
||||
<span id="storage-status" class="text-xs text-muted">Stored locally</span>
|
||||
</div>
|
||||
<p class="text-xs text-muted">Required for direct file uploads.</p>
|
||||
<div class="flex items-center justify-between">
|
||||
<p class="text-xs text-muted">Required for direct file uploads.</p>
|
||||
<a href="#view=upload-content" target="_blank" class="flex items-center gap-1 text-xs text-accent hover:underline">
|
||||
<img src="assets/svg/upload-content-icon.svg" alt="" class="h-3 w-3" />
|
||||
Learn How
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-2">
|
||||
<div class="space-y-1">
|
||||
|
||||
@@ -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/<ACCOUNT_ID>/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.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -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)
|
||||
@@ -0,0 +1,77 @@
|
||||
<!-- views/upload-content.html -->
|
||||
<section class="bv-stack" data-ds="new" aria-labelledby="uploadContentHeading">
|
||||
<article class="card p-lg">
|
||||
<div class="bv-stack bv-stack--tight">
|
||||
<div class="flex items-center gap-3">
|
||||
<img src="assets/svg/upload-content-icon.svg" alt="" class="h-8 w-8 text-accent" aria-hidden="true" />
|
||||
<h2 id="uploadContentHeading" class="text-2xl font-semibold text-text-strong">
|
||||
Upload Content
|
||||
</h2>
|
||||
</div>
|
||||
<div id="markdown-container" class="markdown-prose">
|
||||
<p class="text-muted-strong">Loading content...</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
// Provide no-op fallback loggers so embeds stay functional if bitvidLogger is unavailable.
|
||||
const noopLogger = {
|
||||
log: () => {},
|
||||
info: () => {},
|
||||
warn: () => {},
|
||||
error: () => {},
|
||||
};
|
||||
const dev = window.bitvidLogger?.dev || noopLogger;
|
||||
const user = window.bitvidLogger?.user || noopLogger;
|
||||
|
||||
const loadMarkdown = async () => {
|
||||
try {
|
||||
const response = await fetch("content/upload-content.md");
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to load upload-content.md");
|
||||
}
|
||||
const markdownText = await response.text();
|
||||
|
||||
// Convert the markdown to HTML (requires "marked" loaded globally)
|
||||
const html = marked.parse(markdownText);
|
||||
|
||||
// Insert the HTML into the container
|
||||
const container = document.getElementById("markdown-container");
|
||||
if (container) {
|
||||
container.innerHTML = html;
|
||||
|
||||
const highlighter = window.hljs;
|
||||
const highlight =
|
||||
highlighter && typeof highlighter.highlightElement === "function"
|
||||
? (block) => highlighter.highlightElement(block)
|
||||
: highlighter && typeof highlighter.highlightBlock === "function"
|
||||
? (block) => highlighter.highlightBlock(block)
|
||||
: null;
|
||||
|
||||
if (highlight) {
|
||||
container.querySelectorAll("pre code").forEach((block) => {
|
||||
try {
|
||||
highlight(block);
|
||||
} catch (highlightError) {
|
||||
dev.warn("Failed to highlight code block.", highlightError);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
user.error("Error loading Upload Content guide:", err);
|
||||
const container = document.getElementById("markdown-container");
|
||||
if (container) {
|
||||
container.innerHTML = `
|
||||
<p class="text-critical-strong">Error loading content. Please try again later.</p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loadMarkdown();
|
||||
})();
|
||||
</script>
|
||||
Reference in new issue
Block a user