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 env_logger::init();
10
11 println!("=== Baidu NetDisk Playlist Test ===\n");
12
13 let client = BaiduNetDiskClient::builder().build()?;
15 info!("Client created successfully");
16
17 client.load_token_from_env()?;
19 info!("Token loaded successfully");
20
21 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 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 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 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 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 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 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 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 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 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 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), };
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
314fn 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}