Skip to main content

playlist/
playlist.rs

1use baidu_netdisk_sdk::playlist::VideoQuality;
2use baidu_netdisk_sdk::BaiduNetDiskClient;
3use log::info;
4use std::io::{self, BufRead};
5
6#[tokio::main]
7async fn main() -> Result<(), Box<dyn std::error::Error>> {
8    // Initialize logger
9    env_logger::init();
10
11    println!("=== Baidu NetDisk Playlist Test ===\n");
12
13    // Create client - will automatically load from .env file
14    let client = BaiduNetDiskClient::builder().build()?;
15    info!("Client created successfully");
16
17    // Load token from environment variables
18    client.load_token_from_env()?;
19    info!("Token loaded successfully");
20
21    // === Part 1: Get Playlist List ===
22    println!("=== Part 1: Get Playlist List ===");
23    println!("This will fetch all playlists from your Baidu NetDisk");
24    wait_for_enter();
25
26    let playlists = match client.playlist().get_playlist_list().await {
27        Ok(pl) => {
28            println!("✓ Successfully retrieved playlist list!");
29            println!(
30                "  Has more: {}",
31                if pl.has_more == 1 { "Yes" } else { "No" }
32            );
33            println!("  Found {} playlist(s):", pl.list.len());
34            for (idx, playlist) in pl.list.iter().enumerate() {
35                println!("    {}. {}", idx + 1, playlist.name);
36                println!("       mb_id: {}", playlist.mb_id);
37                println!("       file_count: {}", playlist.file_count);
38                println!("       btype: {}", playlist.btype);
39                println!("       bstype: {}", playlist.bstype);
40                println!("       ctime: {}", playlist.ctime);
41                println!("       mtime: {}", playlist.mtime);
42            }
43            Some(pl)
44        }
45        Err(e) => {
46            println!("! Failed to get playlist list: {}", e);
47            None
48        }
49    };
50
51    // === Part 2: Get Playlist File List ===
52    println!("\n=== Part 2: Get Playlist File List ===");
53    println!("mb_id is the unique identifier for a playlist (numeric).");
54    if playlists.is_some() && !playlists.as_ref().unwrap().list.is_empty() {
55        println!("Available playlists, please select one's mb_id:");
56        for (idx, playlist) in playlists.as_ref().unwrap().list.iter().enumerate() {
57            println!(
58                "  {}. {} - mb_id: {}",
59                idx + 1,
60                playlist.name,
61                playlist.mb_id
62            );
63        }
64    }
65    println!("Please enter mb_id, or press Enter to skip:");
66    let mut input = String::new();
67    std::io::stdin().read_line(&mut input)?;
68    let input_trimmed = input.trim();
69
70    let playlist_mb_id = if input_trimmed.is_empty() && playlists.is_some() {
71        // If user just pressed enter and we have playlists, use the first one
72        println!("  No input provided, using the first playlist...");
73        playlists
74            .as_ref()
75            .and_then(|pl| pl.list.first())
76            .map(|p| p.mb_id)
77    } else {
78        // Try to parse as number
79        match input_trimmed.parse::<u64>() {
80            Ok(mb_id) => Some(mb_id),
81            Err(_) => {
82                println!("  Invalid input, please enter a valid mb_id");
83                None
84            }
85        }
86    };
87
88    let playlist_files = if let Some(mb_id) = playlist_mb_id {
89        println!("\nGetting files from playlist with mb_id: {}", mb_id);
90        wait_for_enter();
91
92        match client.playlist().get_playlist_file_list(mb_id).await {
93            Ok(pl_files) => {
94                println!("✓ Successfully retrieved playlist file list!");
95                println!(
96                    "  Has more: {}",
97                    if pl_files.has_more == 1 { "Yes" } else { "No" }
98                );
99                println!("  Found {} file(s) in playlist:", pl_files.list.len());
100                for (idx, file) in pl_files.list.iter().enumerate() {
101                    println!(
102                        "    {}. {}",
103                        idx + 1,
104                        file.server_filename.as_deref().unwrap_or("(no name)")
105                    );
106                    println!("       fs_id: {}", file.fs_id);
107                    println!("       path: {}", file.path);
108                    if let Some(size) = &file.size {
109                        println!("       size: {}", size);
110                    }
111                    if let Some(category) = &file.category {
112                        println!("       category: {}", category);
113                    }
114                    if let Some(isdir) = &file.isdir {
115                        println!("       isdir: {}", isdir);
116                    }
117                }
118                Some(pl_files)
119            }
120            Err(e) => {
121                println!("! Failed to get playlist file list: {}", e);
122                None
123            }
124        }
125    } else {
126        println!("  Skipping playlist file list as no valid mb_id was provided");
127        None
128    };
129
130    // === Part 3-4: Get Media and Check M3U8 Generation ===
131    println!("\n=== Part 3-4: Get Media and Check M3U8 Generation ===");
132    println!("Enter the path of the media file to get playback info:");
133    let mut input = String::new();
134    std::io::stdin().read_line(&mut input)?;
135    let media_path = input.trim();
136
137    println!("\nEnter media type (e.g., M3U8_MP3_128, M3U8_AUTO_720, M3U8_AUTO_1080) or press Enter for default:");
138    let mut media_type_input = String::new();
139    std::io::stdin().read_line(&mut media_type_input)?;
140    let media_type = if media_type_input.trim().is_empty() {
141        "M3U8_AUTO_1080".to_string()
142    } else {
143        media_type_input.trim().to_string()
144    };
145
146    let (_selected_fs_id, selected_path) = if media_path.is_empty() && playlist_files.is_some() {
147        // If user just pressed enter and we have files, use the first one
148        println!("  No input provided, using the first file in playlist...");
149        let file = playlist_files.as_ref().and_then(|pf| pf.list.first());
150        (
151            file.and_then(|f| f.fs_id.parse::<u64>().ok()),
152            file.map(|f| f.path.as_str()),
153        )
154    } else {
155        (None, Some(media_path))
156    };
157
158    let target_path = selected_path.unwrap_or_default().to_string();
159    if !target_path.is_empty() {
160        println!("\nStarting m3u8 check process with type: {}", media_type);
161        println!("Path: {}", target_path);
162        println!("Press Enter to check, any other key + Enter to skip");
163
164        let mut attempts = 0;
165        let max_attempts = 100;
166        let mut last_content_len = 0;
167
168        loop {
169            // Wait for user to press enter
170            let mut user_input = String::new();
171            std::io::stdin().read_line(&mut user_input)?;
172            if !user_input.trim().is_empty() && user_input.trim() != "y" && user_input.trim() != "Y"
173            {
174                println!("  Skipping further checks");
175                break;
176            }
177
178            attempts += 1;
179            println!("\n=== Check attempt {}/{} ===", attempts, max_attempts);
180
181            // Try to get m3u8 content directly
182            match client
183                .playlist()
184                .get_media_m3u8_content(&target_path, &media_type)
185                .await
186            {
187                Ok(content) => {
188                    let len = content.len();
189                    let has_end_list = content.contains("#EXT-X-ENDLIST");
190
191                    println!("✓ Got m3u8 content!");
192                    println!("  Length: {} bytes", len);
193                    println!(
194                        "  Has #EXT-X-ENDLIST: {}",
195                        if has_end_list { "Yes" } else { "No" }
196                    );
197
198                    if len > last_content_len {
199                        println!("  Content is growing (was {} bytes)", last_content_len);
200                        last_content_len = len;
201                    } else if len > 0 && len == last_content_len {
202                        println!("  Content size unchanged");
203                    }
204
205                    // Show preview
206                    let preview_lines = content.lines().take(20);
207                    println!("\n  Preview:");
208                    for line in preview_lines {
209                        println!("    {}", line);
210                    }
211                    if content.lines().count() > 20 {
212                        println!("    ... ({} more lines)", content.lines().count() - 20);
213                    }
214
215                    if has_end_list {
216                        println!("\n✅ Media is fully transcoded! Done.");
217                        break;
218                    } else {
219                        println!("\n⏳ Media still transcoding... Press Enter to check again");
220                    }
221                }
222                Err(e) => {
223                    println!("❌ Error getting m3u8: {}", e);
224                    println!("  Stopping check process");
225                    break;
226                }
227            }
228
229            if attempts >= max_attempts {
230                println!("\n⚠️ Max attempts reached. Giving up.");
231                break;
232            }
233        }
234    } else {
235        println!("  Skipping as no valid path was provided");
236    }
237
238    // === Part 5: Using Quality Enums ===
239    println!("\n=== Part 5: Using Quality Enums ===");
240    println!("Test the new VideoQuality and AudioQuality enums? (Y/n):");
241    let mut enum_test_input = String::new();
242    std::io::stdin().read_line(&mut enum_test_input)?;
243
244    if enum_test_input.trim().to_lowercase() != "n" {
245        println!("\nAvailable video qualities for different VIP levels:");
246        println!("  VIP 0-1: {:?}", VideoQuality::available_for_vip_level(0));
247        println!("  VIP 2+: {:?}", VideoQuality::available_for_vip_level(2));
248
249        println!("\nHighest quality for each level:");
250        println!(
251            "  VIP 0: {:?} ({})",
252            VideoQuality::highest_for_vip_level(0),
253            VideoQuality::highest_for_vip_level(0).to_media_type()
254        );
255        println!(
256            "  VIP 2: {:?} ({})",
257            VideoQuality::highest_for_vip_level(2),
258            VideoQuality::highest_for_vip_level(2).to_media_type()
259        );
260
261        // Ask user to test enum methods
262        println!("\nTest enum-based m3u8 methods? (Y/n):");
263        let mut enum_m3u8_input = String::new();
264        std::io::stdin().read_line(&mut enum_m3u8_input)?;
265
266        if enum_m3u8_input.trim().to_lowercase() != "n" {
267            println!("\nEnter media file path:");
268            let mut enum_path_input = String::new();
269            std::io::stdin().read_line(&mut enum_path_input)?;
270            let enum_path = enum_path_input.trim();
271
272            if !enum_path.is_empty() {
273                println!("\nSelect video quality (1=480P, 2=720P, 3=1080P, or press Enter for highest VIP 2):");
274                let mut quality_input = String::new();
275                std::io::stdin().read_line(&mut quality_input)?;
276
277                let quality = match quality_input.trim() {
278                    "1" => VideoQuality::Quality480P,
279                    "2" => VideoQuality::Quality720P,
280                    "3" => VideoQuality::Quality1080P,
281                    _ => VideoQuality::highest_for_vip_level(2), // Default to highest for VIP 2
282                };
283
284                println!(
285                    "\nUsing quality: {:?} ({})",
286                    quality,
287                    quality.to_media_type()
288                );
289                println!("Getting m3u8...");
290                wait_for_enter();
291
292                match client.playlist().get_video_m3u8(enum_path, quality).await {
293                    Ok(content) => {
294                        println!("✓ Successfully got m3u8 content!");
295                        println!("  Length: {} bytes", content.len());
296                        println!(
297                            "  Has #EXT-X-ENDLIST: {}",
298                            content.contains("#EXT-X-ENDLIST")
299                        );
300                    }
301                    Err(e) => {
302                        println!("! Failed to get m3u8: {}", e);
303                    }
304                }
305            }
306        }
307    }
308
309    println!("\n=== All playlist tests completed! ===");
310
311    Ok(())
312}
313
314/// Wait for user to press Enter before continuing
315fn wait_for_enter() {
316    println!("Press Enter to continue...");
317    let stdin = io::stdin();
318    let mut lines = stdin.lock().lines();
319    let _ = lines.next();
320}