playlist_dl/
playlist_dl.rs1use std::{io::Write, sync::Arc};
8use vsd::{
9 Error, Muxer, PlaylistDownloader, Result,
10 playlist::MediaType,
11 progress::{ByteSize, Eta, ProgressCallback, ProgressState},
12 reqwest::Client,
13 tokio,
14 tokio_util::sync::CancellationToken,
15};
16
17struct Progress;
18
19impl ProgressCallback for Progress {
20 fn on_progress(&self, state: &ProgressState) {
21 let stderr = std::io::stderr();
22 let mut handle = stderr.lock();
23 write!(
24 handle,
25 "\r\x1B[2K[{}/~{}({:.0}%) PT:{}/{} DL:{} ETA:{}]",
26 ByteSize(state.downloaded_bytes),
27 ByteSize(state.estimated_bytes),
28 state.percent,
29 state.downloaded_parts,
30 state.total_parts,
31 ByteSize(state.speed_bps as usize),
32 Eta(state.eta_seconds)
33 )
34 .unwrap();
35 handle.flush().unwrap();
36 }
37
38 fn on_finish(&self, state: &ProgressState) {
39 self.on_progress(state);
40 eprintln!();
41 }
42}
43
44#[tokio::main(flavor = "multi_thread")]
45async fn main() -> Result<()> {
46 let client = Client::new();
47
48 let dl = PlaylistDownloader::new(&client).directory("target");
50 let config = dl.get_config();
51
52 let mp = dl
53 .parse(
54 "https://media.axprod.net/TestVectors/Dash/not_protected_dash_1080p_h264/manifest.mpd",
55 false,
56 )
57 .await?;
58
59 let token = CancellationToken::new();
61 let mut muxer = Muxer::new();
62
63 for stream in mp.streams {
65 if stream.media_type == MediaType::Subtitles {
66 println!(
67 "Downloading {} subtitles",
68 stream.language.as_deref().unwrap_or("unknown")
69 );
70
71 let dl_info = match stream.download(config, Arc::new(Progress), &token).await {
74 Ok(info) => info,
75 Err(Error::MissingSegments) => {
76 println!("Stream has no segments");
77 continue;
78 }
79 Err(Error::UnsupportedEncryption(e)) => {
80 println!("Unsupported encryption {}", e);
81 continue;
82 }
83 Err(Error::MissingKey(key_id)) => {
84 println!("Missing decryption key for {key_id}");
85 continue;
86 }
87 Err(Error::DownloadInterrupted) => {
88 println!("Download paused");
89 std::process::exit(0);
90 }
91 Err(e) => return Err(e),
92 };
93
94 println!("Downloaded {}", dl_info.path.to_string_lossy());
95 muxer.push(dl_info);
96
97 break;
98 }
99 }
100
101 println!("Muxing to target/output.srt");
102 muxer
103 .mux(&vsd::find_ffmpeg().unwrap(), "target/output.srt", "srt")
104 .await?;
105 muxer.clean(config.directory.as_deref()).await?;
106
107 Ok(())
108}