8.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Development Commands
# Install dependencies
npm run install:all
# Development (run in separate terminals)
npm run dev:backend # Starts on :3001 with tsx watch
npm run dev:frontend # Starts on :3000 with Vite HMR
# Within backend/ directory
cd backend
npm run dev # Development with tsx watch
npm run build # TypeScript compilation
npm start # Run compiled version
# Within frontend/ directory
cd frontend
npm run dev # Vite dev server
npm run build # TypeScript + Vite build
npm run preview # Preview production build
Architecture Overview
VidRip is a monorepo with separate backend (Express/SQLite) and frontend (React) apps.
Core Scheduler Logic
The scheduler in backend/src/services/scheduler.ts operates on two separate timers:
- Channel Checker: Cron job (
0 * * * *) runs every hour to check active channels for new videos - Download Cycle: Self-scheduling recursive setTimeout that:
- Downloads one pending video
- Calculates next run:
intervalHours * 60 ± random(varianceMinutes) - Schedules itself again with the calculated delay
The variance prevents predictable download patterns. The scheduler uses a single global isDownloading flag to prevent concurrent downloads.
Database Layer
SQLite database (backend/data.db) with three tables managed via better-sqlite3:
channels: YouTube channels to monitorvideos: Individual videos with status tracking (pending/downloading/completed/failed)config: Key-value store for app settings
All database operations are in backend/src/db/database.ts using synchronous better-sqlite3 API. The database auto-initializes on first run.
yt-dlp Integration
backend/src/services/ytdlp.ts wraps the yt-dlp CLI (must be installed on system):
- Uses
child_process.spawnto call yt-dlp --dump-jsonextracts metadata without downloading--flat-playlistlists channel videos efficiently- Progress tracking via stdout parsing (looks for percentage patterns)
- Downloads merge best video+audio to MP4 format
Frontend Proxy Setup
Vite development server (port 3000) proxies to backend (port 3001):
/api/*→ Backend API endpoints/downloads/*→ Static video files
This is configured in frontend/vite.config.ts. Production deployments need a reverse proxy (nginx, etc.).
API Structure
Routes are split by domain:
backend/src/routes/channels.ts: Channel CRUD + refreshbackend/src/routes/videos.ts: Video CRUD + retry + streamingbackend/src/routes/config.ts: Settings + scheduler control + WebDAV test
Video files are served via /api/videos/:id/stream endpoint which handles both local storage and WebDAV proxying.
Key Constraints
- Only 1 concurrent download supported (hardcoded in scheduler)
- Download progress is ephemeral (not persisted to DB, only in-memory)
- No authentication/authorization
- yt-dlp must be in PATH (external dependency)
- Database uses WAL mode by default for better concurrency
Important Implementation Notes
Scheduler Restart Behavior
When config changes (e.g., interval or variance), call restartScheduler() from backend/src/services/scheduler.ts. This stops the cron task and clears the setTimeout chain, then restarts with new settings.
Video Status State Machine
Videos progress through states: pending → downloading → completed/failed
- Retry functionality resets
failedback topending - Deleting a channel cascades to delete all its videos and files
Channel Refresh
Adding a channel fetches ALL videos from that channel using yt-dlp's --flat-playlist. Large channels (1000+ videos) may take time. Videos are created in pending state and downloaded according to the schedule.
File Locations
- Database:
backend/data.db - Downloaded videos (local mode):
backend/downloads/{videoId}.mp4 - Downloaded videos (WebDAV mode): Stored on WebDAV server, proxied through backend
- Environment: Optional
backend/.env(PORT, NODE_ENV)
System Requirements
- Node.js 18+
- yt-dlp installed and in PATH (
pip install yt-dlporbrew install yt-dlp)
Troubleshooting 403 Errors
If you encounter 403 Forbidden errors when downloading videos:
Built-in Mitigations
The app automatically includes these anti-403 measures:
- Browser-like User-Agent headers
- YouTube referer
- Multiple retry attempts (5 extractor retries, 10 connection retries)
- Request throttling (1-5 second delays between requests)
- Fragment retries for long videos
Additional Solution: Browser Cookies
For persistent 403 errors, export cookies from your browser:
-
Install browser extension:
- Chrome/Edge: "Get cookies.txt LOCALLY"
- Firefox: "cookies.txt"
-
Export YouTube cookies:
- Visit youtube.com while logged in
- Click extension icon and export cookies
- Save as
cookies.txt
-
Add to VidRip:
- Place
cookies.txtinbackend/directory - VidRip will automatically use it if present
- No restart needed
- Place
-
Keep cookies fresh:
- YouTube cookies expire periodically
- Re-export if 403 errors return
- Consider using an account with YouTube Premium to avoid restrictions
Other Tips
- Update yt-dlp:
pip install --upgrade yt-dlp(YouTube changes frequently) - Increase download interval to avoid rate limiting
- Use variance to randomize download times
- Avoid downloading too many videos from the same channel quickly
WebDAV Storage
VidRip supports storing videos on a WebDAV server instead of local storage. This is useful for:
- Network-attached storage (NAS) devices
- Cloud storage with WebDAV support (Nextcloud, ownCloud)
- Remote servers with WebDAV access
Enabling WebDAV
- Go to Settings page in the UI
- Enable "WebDAV Storage"
- Configure connection details:
- Server URL: Full WebDAV server URL (e.g.,
https://nextcloud.example.com/remote.php/dav/files/username) - Username: WebDAV authentication username
- Password: WebDAV authentication password
- Directory Path: Subdirectory on server (default:
/vidrip/)
- Server URL: Full WebDAV server URL (e.g.,
- Click "Test Connection" to verify settings
- Save Settings
How WebDAV Works
Download Flow:
- Video is downloaded to
backend/downloads/temporarily - After successful download, video is uploaded to WebDAV server
- Local temporary file is deleted after successful upload
- Database stores path as
webdav://{videoId}.mp4
Playback Flow:
- Frontend requests
/api/videos/:id/stream - Backend detects WebDAV storage (path starts with
webdav://) - Backend creates stream from WebDAV server
- Stream is proxied to frontend (transparent to user)
Deletion Flow:
- When deleting a video, backend automatically removes from WebDAV server
- Database record is also deleted
Supported WebDAV Servers
Tested with:
- Nextcloud: Use WebDAV URL format:
https://your-nextcloud.com/remote.php/dav/files/username - ownCloud: Similar to Nextcloud
- Generic WebDAV: Any RFC-compliant WebDAV server
Configuration Details
WebDAV settings are stored in the config table:
webdavEnabled: 'true' or 'false'webdavUrl: Server URLwebdavUsername: UsernamewebdavPassword: Password (stored in plaintext in database)webdavPath: Directory path on server
Troubleshooting WebDAV
Connection Test Fails:
- Verify server URL is correct and accessible from backend server
- Check username/password are correct
- Ensure WebDAV path exists or backend has permission to create it
- Check firewall rules if server is remote
Upload Fails:
- Check available disk space on WebDAV server
- Verify write permissions on directory
- Check WebDAV server logs for detailed errors
- Ensure network is stable for large uploads
Playback Issues:
- Verify WebDAV server is accessible from backend
- Check network bandwidth between backend and WebDAV
- Browser console may show streaming errors
- Try downloading video directly from WebDAV to verify file integrity
Migration:
- Existing local videos remain in
backend/downloads/ - New videos go to WebDAV when enabled
- To migrate existing videos, manually upload to WebDAV and update database
filePath - Or delete and re-download videos
Security Considerations
- Credentials stored in local database (plaintext)
- Backend must have network access to WebDAV server
- Use HTTPS for WebDAV URL to encrypt credentials in transit
- Consider firewall rules to restrict WebDAV access to backend IP only
- WebDAV server should require authentication