commit bcf83b51ea326c00850f78a1b54960a725a481d7 Author: synapz-org Date: Sun Oct 26 09:25:17 2025 -0400 Add design document for X Space downloader - Python CLI tool using yt-dlp as core engine - Cookie-based authentication - MP3 output format - Recordings only (not live Spaces) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude diff --git a/docs/plans/2025-10-26-x-space-downloader-design.md b/docs/plans/2025-10-26-x-space-downloader-design.md new file mode 100644 index 0000000..13194a4 --- /dev/null +++ b/docs/plans/2025-10-26-x-space-downloader-design.md @@ -0,0 +1,188 @@ +# X Space Downloader - Design Document + +**Date:** 2025-10-26 +**Status:** Approved + +## Overview + +A Python CLI tool that downloads X (Twitter) Space recordings and outputs them as MP3 files. Takes a Space URL as input and produces an audio file. + +## Requirements + +- **Input:** X Space recording URL (e.g., `https://x.com/i/spaces/1ZkKzZWnVRRKv`) +- **Output:** MP3 audio file +- **Interface:** Command-line tool +- **Authentication:** Supports X account credentials via cookies +- **Scope:** Recordings only (not live Spaces) +- **Error Handling:** Clear error messages for invalid or non-recording URLs + +## Architecture Overview + +### Technology Choices + +- **Language:** Python +- **Core Engine:** yt-dlp (handles X authentication and media extraction) +- **CLI Framework:** Click (argument parsing and user interface) +- **Pattern:** Thin wrapper around yt-dlp + +### Project Structure + +``` +x-space-downloader/ +├── src/ +│ ├── __init__.py +│ ├── main.py # CLI entry point with argument parsing +│ ├── downloader.py # Wraps yt-dlp functionality +│ └── validator.py # URL validation and error handling +├── tests/ +│ ├── test_validator.py +│ └── test_downloader.py +├── requirements.txt # Dependencies: yt-dlp, click +├── setup.py # Makes it installable +└── README.md +``` + +### User Workflow + +```bash +x-space-dl --cookies cookies.txt https://x.com/i/spaces/1ZkKzZWnVRRKv +# Output: space_1ZkKzZWnVRRKv.mp3 +``` + +## Component Design + +### validator.py + +**Responsibilities:** +- Validate URL format (must match X Spaces pattern: `https://x.com/i/spaces/{SPACE_ID}`) +- Check if URL points to a recording vs live Space +- Return clear error messages for invalid inputs + +**Key Interface:** +```python +def validate_space_url(url: str) -> Tuple[bool, Optional[str], Optional[str]]: + """ + Validates X Space URL. + + Returns: + (is_valid, space_id, error_message) + """ +``` + +### downloader.py + +**Responsibilities:** +- Configure yt-dlp options (format: mp3, quality, output template) +- Pass cookie file to yt-dlp for authentication +- Translate yt-dlp output/errors to user-friendly messages +- Show download progress + +**Key Interface:** +```python +def download_space( + space_url: str, + output_dir: str, + cookies_file: Optional[str] +) -> str: + """ + Downloads Space recording using yt-dlp. + + Returns: + Path to downloaded MP3 file + """ +``` + +**yt-dlp Configuration:** +- Output format: mp3 (audio only) +- Output template: `space_{space_id}.mp3` +- Post-processing: convert to mp3 if needed +- Progress hooks for user feedback + +### main.py + +**Responsibilities:** +- CLI argument parsing with Click +- Orchestrate validation → download flow +- Handle and display errors appropriately +- Exit with appropriate status codes + +**CLI Arguments:** +- `URL` (required): X Space URL +- `--cookies` (optional): Path to cookies file +- `--output` (optional): Output directory (default: current directory) + +**Exit Codes:** +- `0`: Success +- `1`: Invalid URL +- `2`: Download failed +- `3`: Authentication required + +## Data Flow + +1. User provides URL + optional cookies file +2. **Validator** checks URL format → extracts space_id +3. **Downloader** configures yt-dlp with: + - Output format: mp3 (audio only) + - Output template: `space_{space_id}.mp3` + - Cookies file if provided + - Post-processing: convert to mp3 if needed +4. yt-dlp fetches metadata, downloads audio stream, converts to mp3 +5. Return file path to user + +## Error Handling + +| Error Scenario | Detection | User Message | +|----------------|-----------|--------------| +| Invalid URL format | Validator regex check | "Invalid URL format. Expected: https://x.com/i/spaces/{SPACE_ID}" | +| Space doesn't exist | yt-dlp 404 error | "Recording not found. Check URL or Space may be deleted." | +| Authentication required | yt-dlp auth error | "Authentication required. Please provide cookies file with --cookies" | +| Network errors | yt-dlp timeout/connection | "Network error. Check connection and try again." | +| Space is live | Validator metadata check | "Error: This is a live Space, not a recording." | +| Disk space/permissions | File system errors | "Cannot write to output directory. Check permissions and disk space." | + +## Authentication + +**Cookie-based Authentication:** +- User exports cookies from logged-in X session +- Provides cookie file path via `--cookies` flag +- yt-dlp uses cookies for authenticated requests + +**Cookie Export Instructions:** +- Browser extensions: "Get cookies.txt", "cookies.txt" +- Manual: Export via browser DevTools +- Instructions included in README + +## Testing Strategy + +**Unit Tests:** +- Validator: Test various URL formats, valid/invalid patterns +- Error message generation + +**Integration Tests:** +- Full download flow with yt-dlp +- Use test Space URLs or mock yt-dlp calls +- Cookie authentication flow + +**Error Scenario Tests:** +- Invalid URLs +- Missing cookies +- Network failures +- Live Space detection + +## Dependencies + +**Core:** +- `yt-dlp`: Media downloading and extraction +- `click`: CLI framework + +**Development:** +- `pytest`: Testing framework +- `pytest-mock`: Mocking for tests + +## Future Considerations (Out of Scope) + +- Live Space recording +- Batch downloading multiple Spaces +- GUI interface +- Format options beyond MP3 +- Metadata extraction (speakers, title, description)