This commit is contained in:
Julian Freeman
2025-12-02 09:11:59 -04:00
parent f4e264708a
commit a18065de93
23 changed files with 2909 additions and 183 deletions

142
spec/prd.md Normal file
View File

@@ -0,0 +1,142 @@
# Project: StreamCapture (Tauri + Vue 3 YouTube Downloader)
## 1. Context & Objective
You are an expert Full-Stack Rust/TypeScript developer specializing in Tauri v2 application development.
Your task is to build a cross-platform (Windows & macOS) desktop application named "StreamCapture" in the current directory.
The app is a GUI wrapper for `yt-dlp`, but unlike standard sidecar implementations, it must manage the `yt-dlp` binary externally (in the user's AppData directory) to allow for frequent updates without rebuilding the app.
**Tech Stack:**
- **Core:** Tauri v2 (Rust)
- **Frontend:** Vue 3 (Composition API, `<script setup>`)
- **Build Tool:** Vite
- **Styling:** Tailwind CSS (Mobile-first, Modern UI)
- **State Management:** Pinia
- **Icons:** Lucide-vue-next
- **HTTP Client (Rust):** `reqwest` (for downloading yt-dlp)
---
## 2. Core Architecture & Workflow
### 2.1. External Binary Management (Crucial)
Since `yt-dlp` updates frequently, we **do NOT** bundle it inside the binary.
1. **Location:** The app must utilize the system's local data directory (e.g., `%APPDATA%/StreamCapture/bin` on Windows, `~/Library/Application Support/StreamCapture/bin` on macOS).
2. **Initialization:** On app launch, the Rust backend must check if `yt-dlp` exists in that directory.
3. **Auto-Download:** If missing, download the correct binary from the official GitHub releases (`yt-dlp.exe` for Win, `yt-dlp_macos` for Mac).
4. **Permissions:** On macOS, apply `chmod +x` (0o755) to the downloaded binary immediately.
5. **Execution:** Use Rust's `std::process::Command` to execute this specific binary using its absolute path.
### 2.2. Configuration Persistence
- File: `settings.json` in the app data directory.
- Fields:
- `download_path`: string (Default: System Download Dir)
- `theme`: 'light' | 'dark' | 'system' (Default: system)
- `last_updated`: timestamp (for yt-dlp check)
### 2.3. History Persistence
- File: `history.json`.
- Stores a list of completed or failed downloads (metadata, status, timestamp, file path).
---
## 3. Rust Backend Specifications (`src-tauri/src/lib.rs` & Modules)
Create specific commands to handle logic securely. Avoid exposing raw shell execution to the frontend.
### Module: `ytdlp_manager`
- **`init_ytdlp()`**: Checks existence. If missing, downloads it. Returns status.
- **`update_ytdlp()`**: Runs `yt-dlp -U`.
- **`get_version()`**: Returns current version string.
### Module: `downloader`
- **`fetch_metadata(url: String)`**:
- Runs `yt-dlp --dump-single-json --flat-playlist [url]`.
- **Logic:** If it's a playlist, return a list of video objects (id, title, thumbnail, duration). If single video, return one object.
- **Important:** Do NOT download media yet.
- **`download_video(url: String, options: DownloadOptions)`**:
- **Options Struct:** `{ is_audio_only: bool, quality: String, output_path: String }`
- **Process:** Spawns a command. Emits Tauri Events (`download-progress`) to frontend with percentage and speed.
### Module: `storage`
- Helper functions to read/write `settings.json` and `history.json`.
---
## 4. Frontend Specifications (Vue 3 + Tailwind)
### 4.1. UI Layout
- **Sidebar:** Navigation (Home/Download, History, Settings).
- **Header:** Theme toggle, App status (checking yt-dlp...).
- **Main Content:** Dynamic view based on route.
### 4.2. Views
#### A. Home View (The Downloader)
1. **Input Area:** Large, modern input box for URL. "Analyze" button.
2. **Selection Area (Conditional):**
- Appears after "Analyze" succeeds.
- Shows thumbnail(s) and title(s).
- If Playlist: Show list of items with checkboxes (Select All / Select None).
3. **Options Panel:**
- **Format:** Dropdown (Best Video+Audio, 1080p, 720p, 480p).
- **Mode:** Toggle Switch (Video / Audio Only).
4. **Action:** Big "Download" button.
5. **Progress:** Progress bars for active downloads.
#### B. History View
- Table/List of past downloads.
- Columns: Thumbnail (small), Title, Date, Format, Status (Success/Fail).
- Actions: "Open File Location", "Delete Record" (Icon), "Clear All" (Button at top).
#### C. Settings View
- **Download Path:** Input field + "Browse" button (use Tauri `dialog` API).
- **Yt-dlp:** Show current version. Button "Check for Updates".
- **Theme:** Radio buttons (Light/Dark/System).
### 4.3. State Management (Pinia stores)
- `useSettingsStore`: Loads/saves config. Handles theme applying (adding `.dark` class to `html`).
- `useQueueStore`: Manages active downloads and progress events.
---
## 5. Visual Design Guidelines (Tailwind)
- **Theme:**
- **Light Mode:** White/Gray-50 background, Zinc-900 text, Primary Blue-600.
- **Dark Mode:** Zinc-950 background, Gray-100 text, Primary Blue-500.
- **Components:**
- Rounded corners (`rounded-xl`).
- Subtle shadows (`shadow-sm`, `shadow-md`).
- Input fields with focus rings.
- Transitions for hover states and page switches.
---
## 6. Implementation Steps for Gemini
Please generate the code in the following order. **Ensure all files include `// filepath: ...` comments.**
1. **Rust Setup:**
- `Cargo.toml` (dependencies: reqwest, serde, serde_json, tokio, etc.).
- `src-tauri/src/lib.rs`: Main entry with command registration.
- `src-tauri/src/ytdlp.rs`: The download/update logic.
- `src-tauri/src/commands.rs`: The Tauri commands exposed to frontend.
2. **Frontend Setup:**
- `package.json`: deps (pinia, vue-router, lucide-vue-next, tailwindcss, autoprefixer, postcss).
- `src/style.css`: Tailwind directives.
- `src/stores/...`: Pinia stores.
- `src/components/...`: Reusable UI components (Input, Button, Card).
- `src/views/...`: The main pages.
- `src/App.vue`: Main layout.
3. **Configuration:**
- `tailwind.config.js`: Dark mode config.
- `src-tauri/capabilities/default.json`: **CRITICAL**. Configure permissions to allow accessing `app_data_dir` and the network scope.
## 7. Testing Requirements
- Create a basic Rust test in `src-tauri/src/ytdlp.rs` that verifies it can construct the correct path for the binary based on OS.
- Ensure Vue components handle "Loading" states gracefully (skeletons or spinners).
---
**IMPORTANT NOTE ON PERMISSIONS:**
Since we are using `std::process::Command` directly in Rust, we do not need to configure the strict `shell` scope in `tauri.conf.json`, but we MUST ensure the App Data directory is writable.
Start by generating the Rust backend logic first, as the frontend depends on these commands.