download_players/
download_players.rs

1//! Download YouTube player files for testing
2//!
3//! Usage: cargo run --example download_players
4
5use ytdlp_ejs::test_data::{ALL_VARIANTS, TEST_CASES, get_cache_path, get_player_paths};
6use std::fs;
7use std::path::Path;
8
9fn main() {
10    let player_paths = get_player_paths();
11    let mut downloaded = 0;
12    let mut skipped = 0;
13    let mut failed = 0;
14
15    for test_case in TEST_CASES {
16        let variants = test_case.variants.unwrap_or(ALL_VARIANTS);
17
18        for variant in variants {
19            let cache_path = get_cache_path(test_case.player, variant);
20            let path = Path::new(&cache_path);
21
22            if path.exists() {
23                skipped += 1;
24                continue;
25            }
26
27            // Create parent directory if it doesn't exist
28            if let Some(parent) = path.parent()
29                && !parent.exists()
30                && let Err(e) = fs::create_dir_all(parent)
31            {
32                eprintln!("Failed to create directory {:?}: {}", parent, e);
33                failed += 1;
34                continue;
35            }
36
37            let Some(player_path) = player_paths.get(variant) else {
38                eprintln!("Unknown variant: {}", variant);
39                failed += 1;
40                continue;
41            };
42
43            let url = format!(
44                "https://www.youtube.com/s/player/{}/{}",
45                test_case.player, player_path
46            );
47
48            println!("Downloading: {}", url);
49
50            match download_file(&url, &cache_path) {
51                Ok(_) => {
52                    println!("  -> Saved to {}", cache_path);
53                    downloaded += 1;
54                }
55                Err(e) => {
56                    eprintln!("  -> Failed: {}", e);
57                    failed += 1;
58                }
59            }
60        }
61    }
62
63    println!();
64    println!("Summary:");
65    println!("  Downloaded: {}", downloaded);
66    println!("  Skipped (already exists): {}", skipped);
67    println!("  Failed: {}", failed);
68}
69
70fn download_file(url: &str, path: &str) -> Result<(), String> {
71    // Use curl with built-in retry mechanism
72    let output = std::process::Command::new("curl")
73        .args([
74            "-sL",
75            "--connect-timeout",
76            "10",
77            "--max-time",
78            "120",
79            "--retry",
80            "3",
81            "--retry-delay",
82            "1",
83            "--retry-all-errors",
84            "-o",
85            path,
86            url,
87        ])
88        .output()
89        .map_err(|e| format!("Failed to run curl: {}", e))?;
90
91    if !output.status.success() {
92        // Clean up partial download
93        fs::remove_file(path).ok();
94        return Err(format!(
95            "curl failed with exit code {}: {}",
96            output.status.code().unwrap_or(-1),
97            String::from_utf8_lossy(&output.stderr)
98        ));
99    }
100
101    // Check if file was created and has content
102    let metadata = fs::metadata(path).map_err(|e| {
103        fs::remove_file(path).ok();
104        format!("Failed to read file: {}", e)
105    })?;
106
107    if metadata.len() == 0 {
108        fs::remove_file(path).ok();
109        return Err("Downloaded file is empty".to_string());
110    }
111
112    Ok(())
113}