|
1 | | -# CLAUDE.md |
| 1 | +# AGENTS.md |
2 | 2 |
|
3 | | -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 3 | +Guidance for agentic coding agents working with Rust YouTube Uploader. |
4 | 4 |
|
5 | | -## Project Overview |
| 5 | +## Quick Start |
6 | 6 |
|
7 | | -Rust YouTube Uploader is a high-performance YouTube video uploader library with CLI tools. It uses OAuth 2.0 (PKCE flow) for authentication and supports both sequential and concurrent uploads with retry logic and progress tracking. |
| 7 | +**Build directory**: `~/.cache/rust-build/` (cached, never mention in docs) |
| 8 | +**Edition**: Rust 2024 | **Error handling**: `anyhow::Result` | **Runtime**: Tokio async |
8 | 9 |
|
9 | | -**Build directory**: `~/.cache/rust-build/` (never mention this in documentation or code). |
10 | | - |
11 | | -**Key Design**: Async-first architecture using Tokio runtime. All operations are async and the library uses `anyhow` for error handling. |
12 | | - |
13 | | -## Development Commands |
14 | | - |
15 | | -### Building |
| 10 | +### Essential Commands |
16 | 11 |
|
17 | 12 | ```bash |
18 | | -# Build specific binaries (never run from target/ directly) |
19 | | -cargo build --release --bin yt-upload |
20 | | -cargo build --release --bin yt-list |
21 | | -cargo build --release --bin yt-update-lang |
22 | | - |
23 | | -# Quick check (faster than full build) |
24 | | -cargo check |
| 13 | +# Build & check |
| 14 | +cargo build --release --bin <name> # Build specific binary |
| 15 | +cargo check # Quick syntax check |
| 16 | +cargo fmt # Auto-format code |
| 17 | +cargo clippy --all-targets --all-features # Lint |
| 18 | + |
| 19 | +# Testing |
| 20 | +cargo test --all-features # Run all tests |
| 21 | +cargo test test_name -- --nocapture # Run specific test with output |
| 22 | +cargo test --lib # Library tests only |
25 | 23 | ``` |
26 | 24 |
|
27 | | -### Testing |
| 25 | +## Project Structure |
28 | 26 |
|
29 | | -```bash |
30 | | -# Run all tests |
31 | | -cargo test --all-features |
| 27 | +**Core Library** (`src/lib.rs`): `google_oauth`, `models`, `youtube_client`, `retry`, `progress_stream`, `video_process` |
32 | 28 |
|
33 | | -# Run specific test |
34 | | -cargo test test_name |
| 29 | +**CLI Binaries** (`src/bin/`): `yt-upload`, `yt-list`, `yt-append-description`, `yt-add-pin-comment`, `yt-add-tags`, `yt-update-lang`, `yt-update-date`, `yt-upload-subtitle` |
35 | 30 |
|
36 | | -# Run tests with output |
37 | | -cargo test --all-features -- --nocapture |
38 | | -``` |
| 31 | +**Tests**: `tests/integration.rs` (config parsing, validation); library tests in `src/**/*.rs` |
39 | 32 |
|
40 | | -### Code Quality |
| 33 | +## Code Style & Conventions |
41 | 34 |
|
42 | | -```bash |
43 | | -# Format code |
44 | | -cargo fmt |
| 35 | +### Imports & Organization |
45 | 36 |
|
46 | | -# Check formatting without changes |
47 | | -cargo fmt -- --check |
| 37 | +- **Standard library first**, then external crates, then internal modules (separated by blank lines) |
| 38 | +- Use glob imports sparingly; prefer explicit imports for clarity |
| 39 | +- Re-export public types in `lib.rs` for API simplicity |
| 40 | +- Use `use anyhow::Result` for all error-returning functions |
| 41 | +- Trait objects: `Arc<dyn Trait>` for sharing (e.g., `Arc<dyn ProgressReporter>`) |
48 | 42 |
|
49 | | -# Run linter |
50 | | -cargo clippy --all-targets --all-features |
| 43 | +### Naming & Types |
51 | 44 |
|
52 | | -# Pre-commit hooks (configured in .pre-commit-config.yaml) |
53 | | -pre-commit install # Install git hooks |
54 | | -pre-commit run --all-files # Run manually |
55 | | -pre-commit autoupdate # Update hooks to latest versions |
56 | | -``` |
| 45 | +- **Functions**: `snake_case`, methods starting with action verbs (`update_`, `post_`, `get_`) |
| 46 | +- **Types**: `PascalCase` for structs/enums/traits |
| 47 | +- **Constants**: `SCREAMING_SNAKE_CASE` |
| 48 | +- **Private helpers**: Prefix internal functions with underscore if needed |
| 49 | +- **Type annotations**: Always explicit for public functions and struct fields |
57 | 50 |
|
58 | | -## Architecture |
| 51 | +### Error Handling |
59 | 52 |
|
60 | | -### Module Structure |
| 53 | +- Use `anyhow::Result<T>` (not `std::result::Result`) everywhere |
| 54 | +- Use `anyhow::bail!()` for quick errors: `anyhow::bail!("Error: {}", context)` |
| 55 | +- Use `?` operator for propagation; wrap with `.map_err()` only if context needed |
| 56 | +- Log errors with `error!()` before returning (uses tracing crate) |
| 57 | +- Document retriable vs. non-retriable errors in comments |
61 | 58 |
|
62 | | -**Core Library** (`src/lib.rs`): |
| 59 | +### Async & Concurrency |
63 | 60 |
|
64 | | -- `google_oauth/`: OAuth 2.0 authentication with PKCE support |
65 | | - - `credentials.rs`: Client secret loading |
66 | | - - `google.rs`: `GoogleOAuth` main client (token management, auto-refresh) |
67 | | - - `oauth.rs`: Interactive OAuth flow with local HTTP server for code exchange |
68 | | -- `models.rs`: Configuration models with validation (individual/batch YAML formats) |
69 | | -- `youtube_client.rs`: Core upload logic, progress reporting, playlist management |
70 | | -- `retry.rs`: Exponential backoff with jitter for retriable HTTP errors |
71 | | -- `progress_stream.rs`: Stream wrapper for progress tracking + bandwidth throttling |
72 | | -- `video_process.rs`: FFmpeg integration for merging multiple video files |
| 61 | +- **All operations are async**: Use `async fn` and `.await` |
| 62 | +- Use `tokio::spawn()` for concurrent tasks; `Arc<Semaphore>` for rate limiting |
| 63 | +- Document which fields/methods require `Send + Sync` traits |
| 64 | +- Avoid blocking operations in async contexts |
73 | 65 |
|
74 | | -**CLI Binaries** (`src/bin/`): |
| 66 | +### Comments & Documentation |
75 | 67 |
|
76 | | -- `yt-upload`: Main uploader (sequential/concurrent modes) |
77 | | -- `yt-list`: List/export channel videos (table/JSON/JSONL formats) |
78 | | -- `yt-update-lang`: Update language metadata for videos |
79 | | -- `yt-update-date`: Update recording date metadata |
80 | | -- `yt-update-description`: Update video descriptions |
81 | | -- `yt-upload-subtitle`: Upload caption/subtitle files |
82 | | -- `yt-add-tags`: Add tags to existing videos |
| 68 | +- **Public items**: Full doc comments `///` with examples and panics |
| 69 | +- **Complex logic**: Explain the "why" not the "what" |
| 70 | +- **Inline comments**: Use `//` for clarification, keep minimal |
| 71 | +- **Tests**: Include usage examples in doc comments for public APIs |
83 | 72 |
|
84 | | -### Key Patterns |
| 73 | +### Formatting |
85 | 74 |
|
86 | | -**OAuth Flow**: |
| 75 | +- 4-space indentation (enforced by `cargo fmt`) |
| 76 | +- Max line length: ~100 characters (soft limit) |
| 77 | +- One statement per line; use temporary variables for readability |
| 78 | +- Format strings: Use `{}` interpolation, not `.to_string()` concatenation |
87 | 79 |
|
88 | | -1. First run: Interactive flow displays auth URL → opens browser → user pastes code → tokens saved to `youtube-oauth2.json` |
89 | | -2. Subsequent runs: Loads existing tokens, auto-refreshes on expiry |
90 | | -3. Credentials files (`client_secret.json`, token files) are **never** committed to git |
| 80 | +### API Design |
91 | 81 |
|
92 | | -**Configuration Formats**: |
| 82 | +- Return `Result<T>` for fallible operations |
| 83 | +- Trait bounds in function signatures: `<P: AsRef<Path>>` for flexibility |
| 84 | +- Builder pattern for complex construction (use `with_*` methods) |
| 85 | +- Private constructors with public factory methods (e.g., `YouTubeClient::new()`) |
93 | 86 |
|
94 | | -- **Batch format**: `common` section + separate `titles` and `files` arrays (recommended) |
95 | | -- **Individual format**: `videos` array with per-video configuration |
96 | | -- Both support comma/semicolon/space-separated file lists for merging multiple videos |
| 87 | +## Pre-commit Hooks |
97 | 88 |
|
98 | | -**Upload Modes**: |
| 89 | +All commits run: `cargo fmt` → `cargo clippy` → `cargo test` → trailing whitespace checks |
99 | 90 |
|
100 | | -- **Sequential**: One video at a time with progress bars (default) |
101 | | -- **Concurrent**: Configurable concurrency (default 3), uses `Semaphore` for throttling |
| 91 | +**To bypass** (never recommended): `git commit --no-verify` |
| 92 | +**To fix formatting failures**: Run `cargo fmt` then re-stage files |
102 | 93 |
|
103 | | -**Error Handling**: |
| 94 | +## Key Patterns in Codebase |
104 | 95 |
|
105 | | -- Library uses `anyhow::Result` for error propagation |
106 | | -- Retriable errors: HTTP 500-504, connection errors, timeouts, IO errors |
107 | | -- Non-retriable: Client errors (4xx), validation failures |
| 96 | +### Error Context |
108 | 97 |
|
109 | | -**Progress Tracking**: |
110 | | - |
111 | | -- `ProgressReporter` trait with implementations: `NoProgress`, `ProgressBarReporter` |
112 | | -- `ProgressStream` wraps upload streams for real-time tracking |
113 | | - |
114 | | -### File Handling |
| 98 | +```rust |
| 99 | +// ✅ Good: context before propagation |
| 100 | +if !response.status().is_success() { |
| 101 | + let status = response.status(); |
| 102 | + let text = response.text().await.unwrap_or_default(); |
| 103 | + return Err(anyhow!("Failed to X with status {}: {}", status, text)); |
| 104 | +} |
| 105 | +``` |
115 | 106 |
|
116 | | -**Video Merging**: |
| 107 | +### Async Patterns |
117 | 108 |
|
118 | | -- File entries can contain multiple paths separated by `,` or `;` |
119 | | -- Automatically calls `merge_videos_with_ffmpeg()` to concatenate before upload |
120 | | -- Uses concat demuxer (no re-encoding, fast operation) |
| 109 | +```rust |
| 110 | +// ✅ Good: explicit await, Arc for shared state |
| 111 | +let client = Arc::new(YouTubeClient::new().await?); |
| 112 | +let results = try_join_all(tasks).await?; |
| 113 | +``` |
121 | 114 |
|
122 | | -**MIME Types**: |
| 115 | +### Response Handling |
123 | 116 |
|
124 | | -- Uses `mime_guess` for auto-detection |
125 | | -- Special handling for `.mts` files → `video/mp2t` |
| 117 | +```rust |
| 118 | +// ✅ Good: deserialize then extract |
| 119 | +#[derive(Deserialize)] |
| 120 | +struct Response { items: Vec<Item> } |
| 121 | +let resp: Response = response.json().await?; |
| 122 | +``` |
126 | 123 |
|
127 | 124 | ## Versioning |
128 | 125 |
|
129 | | -**Version Bumping**: Each commit shall bump the minor version in `Cargo.toml`. For example: |
130 | | - |
131 | | -- After committing, the version changes from `0.2.9` → `0.2.10` → `0.2.11`, etc. |
132 | | -- This automatic versioning is managed during the commit process |
133 | | -- Patch version increments only; major/minor versions remain stable unless explicitly changed |
| 126 | +**Version bumping**: Each commit increments minor version (`0.2.X` → `0.2.X+1`) |
| 127 | +Update `Cargo.toml` version before committing; pre-commit hook validates changes. |
134 | 128 |
|
135 | 129 | ## Important Constraints |
136 | 130 |
|
137 | | -- **Never run `cargo clean`** (build directory is cached) |
138 | | -- **Never run binaries** from `target/debug/` or `target/release/` directly |
139 | | -- Always use `cargo run --bin <name>` for execution |
140 | | -- Rust 2024 edition required |
141 | | -- Build directory location is an implementation detail - never expose it in docs/code |
142 | | - |
143 | | -## Test |
| 131 | +- ❌ Never: `cargo clean` (breaks build cache) |
| 132 | +- ❌ Never: Run binaries from `target/` directly (use `cargo run --bin`) |
| 133 | +- ❌ Never: Commit `client_secret.json`, `youtube-oauth2.json` |
| 134 | +- ✅ Always: Use `cargo test` before committing |
| 135 | +- ✅ Always: Run `cargo fmt` on modified files |
| 136 | +- ✅ Always: Test both sequential and concurrent upload modes if modifying `YouTubeClient` |
144 | 137 |
|
145 | | -Integration tests are in `tests/integration.rs`. They test: |
| 138 | +## Testing Strategy |
146 | 139 |
|
147 | | -- Configuration parsing (individual/batch formats) |
148 | | -- Validation logic (playlist IDs, categories, privacy status) |
149 | | -- File format detection |
| 140 | +**Unit tests** (`#[test]`): Logic validation, mocking not needed |
| 141 | +**Integration tests** (`tests/integration.rs`): Configuration parsing, file handling |
| 142 | +**Ignored tests** (`#[ignore]`): Run explicitly with `cargo test -- --ignored` |
150 | 143 |
|
151 | | -Tests with `#[ignore]` are meant for explicit running (e.g., retry logic tests with delays). |
| 144 | +Example: `cargo test test_batch_config -- --nocapture` |
0 commit comments