rust-yt-uploader 0.2.13

High-performance YouTube video uploader in Rust with OAuth 2.0, concurrent uploads, and comprehensive validation
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# Rust YouTube Uploader

A high-performance, memory-safe Rust library for YouTube video uploading with OAuth 2.0 authentication, featuring both programmatic API and command-line interface.

## Features

- **OAuth 2.0 Authentication**: Secure authentication with YouTube API using OAuth 2.0 flow with PKCE support
- **Dual Configuration Formats**: Support for both individual and batch YAML configuration formats
- **Concurrent Uploads**: Async upload mode with configurable concurrency (default: 3)
- **Resumable Uploads**: Robust upload handling with automatic retry and resumption
- **Progress Tracking**: Real-time upload progress bars for sequential uploads
- **MTS File Support**: Special handling for MTS files with correct MIME type
- **Comprehensive Validation**: Input validation for all configuration parameters
- **Retry Logic**: Exponential backoff with jitter for handling transient failures
- **Memory Safety**: Zero-cost abstractions with compile-time safety guarantees

## Installation

### As a Library Dependency

Add to your `Cargo.toml`:

```toml
[dependencies]
rust-yt-uploader = "0.2.8"
```

### As a CLI Tool

#### Prerequisites

- A Google Cloud project with YouTube Data API v3 enabled
- OAuth 2.0 client credentials (`client_secret.json`)

1. Go to the [Google Cloud Console]https://console.cloud.google.com/
2. Create a new project or select an existing one
3. Enable the YouTube Data API v3
4. Create OAuth 2.0 credentials (Desktop application)
5. Download the credentials as `client_secret.json`
6. Place the file in the parent directory of the Rust project

The first time you run the uploader, it will:

1. Display an authorization URL
2. Open your browser for authentication
3. Ask you to paste the authorization code
4. Save the tokens to `youtube-oauth2.json`

#### Build from Source

```bash
git clone https://github.com/leafyoung/rust-yt-uploader
cd rust-yt-uploader
cargo build --release --bin yt-upload
cargo build --release --bin yt-list
cargo build --release --bin yt-update-lang
```

The binaries will be available after building.

## Usage

### As a Library

```rust
use rust_yt_uploader::{YouTubeClient, ConfigFormat, BatchConfigRoot};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load configuration
    let config = BatchConfigRoot::from_file("config.yaml")?;

    // Create authenticated client
    let client = YouTubeClient::new(
        "client_secret.json",
        "youtube-oauth2.json"
    ).await?;

    // Upload videos
    client.upload_batch(&config).await?;

    Ok(())
}
```

#### Using GoogleOAuth Directly

For more advanced use cases requiring direct API access:

```rust
use rust_yt_uploader::google_oauth::{GoogleOAuth, Credentials};
use rust_yt_uploader::youtube_client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create OAuth client with custom scopes
    let oauth_client = GoogleOAuth::new(
        "client_secret.json",
        "youtube-oauth2.json",
        youtube_client::default_youtube_scopes(),
        youtube_client::build_youtube_base_url(),
    ).await?;

    // Use the authenticated client for custom API calls
    // The client handles token refresh automatically
    let response = oauth_client
        .http_client
        .get("https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true")
        .bearer_auth(&oauth_client.access_token)
        .send()
        .await?;

    println!("API Response: {:?}", response.json::<serde_json::Value>().await?);

    Ok(())
}
```

## YAML Configuration Formats

#### Batch Format (Recommended)

```yaml
common:
    prefix: "My Video Series"
    keywords: "rust,youtube,programming"
    category: ScienceTechnology
    privacyStatus: "private"
    playlistId: "PL1234567890123456"

titles:
    - "Episode 1: Introduction"
    - "Episode 2: Getting Started"

files:
    - "/path/to/video1.mp4"
    - "/path/to/video2.mp4"
    # Each entry can also contain multiple files separated by comma, semicolon, or space:
    # - "/path/to/part1.mp4;/path/to/part2.mp4"
    # - "/path/to/video3.mp4, /path/to/video3_extra.mp4"
```

#### Individual Format

```yaml
videos:
    - title: "My First Video"
      description: "This is my first video"
      keywords: "rust,youtube"
      file: "/path/to/video1.mp4"
      category: ScienceTechnology
      privacyStatus: "private"
      playlistId: "PL1234567890123456"
```

### Configuration Reference

#### Video Categories

| ID  | Category             |
| --- | -------------------- |
| 1   | Film & Animation     |
| 2   | Autos & Vehicles     |
| 10  | Music                |
| 15  | Pets & Animals       |
| 17  | Sports               |
| 20  | Gaming               |
| 22  | People & Blogs       |
| 23  | Comedy               |
| 24  | Entertainment        |
| 25  | News & Politics      |
| 26  | Howto & Style        |
| 27  | Education            |
| 28  | Science & Technology |

#### Privacy Status Options

- `public`: Video is visible to everyone
- `private`: Video is only visible to you
- `unlisted`: Video is visible to anyone with the link

#### Playlist ID Format

Playlist IDs must match the pattern: `^PL[a-zA-Z0-9_-]{16,33}$`

Example: `PL1234567890123456`

### As a CLI Tool

#### yt-upload: Upload Videos

```bash
# Sequential upload (default)
yt-upload --file config.yaml

# Sequential upload with progress bars
yt-upload --file config.yaml --progress

# Concurrent upload (3 concurrent by default)
yt-upload --file config.yaml --async

# Custom concurrency level
yt-upload --file config.yaml --async --concurrent 5
```

#### yt-list: List and Export Videos

The `yt-list` tool lists all videos from your YouTube channel with comprehensive metadata. This is useful for:

1. **Downloading videos** - Get video IDs for download tools
2. **Updating metadata** - Export video info to update recording date, language, and audio language

**Basic Usage:**

```bash
# List all videos in table format (default)
yt-list

# Export as JSON (for programmatic access)
yt-list --format json

# Export as JSONL (one video per line, useful for piping)
yt-list --format jsonl

# Save to file instead of stdout
yt-list --format json --output videos.json

# Show only video IDs (one per line)
yt-list --ids-only

# Filter by privacy status
yt-list --status private
yt-list --status public
yt-list --status unlisted
```

**Output Formats:**

- **table** (default): Human-readable table with video ID, title, status, and recording date
- **json**: Single JSON array containing all videos
- **jsonl**: JSON Lines format - one JSON object per line (great for piping to other tools)

**Video Details Exported:**

Each video includes:

- `id`: YouTube video ID (needed for downloading)
- `title`: Video title
- `description`: Video description
- `status`: Privacy status (public, private, unlisted)
- `upload_date`: When the video was uploaded
- `recording_date`: When the video was recorded (if set)
- `category_id`: YouTube category ID
- `tags`: Video tags/keywords
- `default_language`: Default language for the video
- `default_audio_language`: Default audio language for the video

**Examples:**

```bash
# Export public videos to JSON and pipe to jq for further processing
yt-list --format json --status public | jq '.[].id'

# Extract video IDs and titles for batch download
yt-list --format jsonl | jq -r '[.id, .title] | join(": ")'

# Save all video metadata for backup
yt-list --format json --output my_videos_backup.json
```

#### yt-update-lang: Update Language Metadata

The `yt-update-lang` tool automatically updates language metadata for all public videos in your channel. It sets:

- `defaultLanguage`: zh (Chinese)
- `defaultAudioLanguage`: zh-Hans (Simplified Chinese)

For any videos that don't already have these values set.

**Basic Usage:**

```bash
# Show what would be updated (dry run)
yt-update-lang --dry-run

# Update all public videos with language metadata
yt-update-lang

# Verbose mode - show each video being processed
yt-update-lang --verbose

# Only update videos with no language metadata at all
yt-update-lang --only-empty
```

**Features:**

- **Dry Run Mode** (`--dry-run`): Preview which videos would be updated without making changes
- **Verbose Output** (`--verbose`): Show detailed information about each video being updated
- **Smart Filtering** (`--only-empty`): Only update videos with completely empty language fields
- **Rate Limiting**: Automatically adds small delays between updates to avoid API rate limits
- **Progress Tracking**: Shows progress and summary of successful/failed updates

**Examples:**

```bash
# Preview changes before applying
yt-update-lang --dry-run

# Update with verbose output to see what's happening
yt-update-lang --verbose

# Combine with yt-list to verify your videos first
yt-list --status public --format json | jq '.[] | {id, title, default_language, default_audio_language}'

# Then update them
yt-update-lang
```

**Use Cases:**

1. **Batch Language Setup** - Set correct language metadata after bulk uploads
2. **Content Localization** - Ensure Chinese content is properly marked as such
3. **Accessibility** - Help YouTube properly display captions and audio tracks
4. **Content Organization** - Maintain consistent metadata across your channel

### Key Dependencies

- `tokio`: Async runtime for high-performance I/O
- `reqwest`: HTTP client for API calls
- `serde`/`serde_yaml`: Configuration parsing and serialization
- `clap`: Command-line argument parsing
- `validator`: Input validation with custom validators
- `tracing`: Structured logging
- `anyhow`/`thiserror`: Comprehensive error handling

### Security Notes

- Never commit `client_secret.json` or token files to version control
- Store credentials securely with appropriate file permissions (600)
- Regularly rotate OAuth tokens if needed
- Use private/unlisted privacy settings for sensitive content

## Development

### Prerequisites

- Rust 2024 edition or later
- `pre-commit` for git hooks (optional but recommended)

### Setting Up Pre-commit Hooks

Pre-commit hooks automatically run linters, formatters, and tests before each commit:

```bash
# Install pre-commit (if not already installed)
pip install pre-commit

# Install the git hooks
pre-commit install

# Run hooks manually on all files
pre-commit run --all-files

# Update hooks to the latest version
pre-commit autoupdate
```

The pre-commit configuration includes:

- **cargo fmt** - Checks code formatting
- **cargo clippy** - Runs the Rust linter
- **cargo test** - Runs all tests
- **Generic checks** - YAML/JSON syntax, trailing whitespace, merge conflicts, etc.

### Manual Testing

```bash
# Format code
cargo fmt

# Check formatting without making changes
cargo fmt -- --check

# Run linter
cargo clippy --all-targets --all-features

# Run tests
cargo test --all-features

# Run tests with output
cargo test --all-features -- --nocapture

# Run specific test
cargo test test_name
```

## License

This project is licensed under the MIT License - see the LICENSE file for details.

## Acknowledgments

- Built with the Tokio async runtime for high-performance I/O
- Use yup_oauth2 as an alternative