av_stream_info_rust/
lib.rs

1//! This library can analyze a http/https address and check if leads to an audio or a video stream
2//! If so, then it will extract information about the stream from its metadata or in case of HLS streams
3//! from its master playlist file.
4//!
5//! # Example
6//! ```ignore
7//! let list = av_stream_info_rust::check("https://example.com/test.m3u", 10, 3, 3);
8//! for item in list {
9//!     println!("{:?}", item);
10//! }
11//! ```
12
13extern crate hls_m3u8;
14#[macro_use]
15extern crate log;
16extern crate playlist_decoder;
17extern crate reqwest;
18extern crate url;
19
20extern crate serde;
21extern crate serde_json;
22
23mod decodeerror;
24mod lat_long;
25mod request;
26mod request_error;
27mod streamcheck;
28mod streamcheckerror;
29mod streamcheckresult;
30mod streaminfo;
31mod external_check_result;
32
33mod http_config;
34
35use std::thread;
36use std::time::Duration;
37
38pub use decodeerror::DecodeError;
39pub use external_check_result::StreamResult;
40pub use http_config::extract_from_homepage_async;
41#[cfg(feature = "blocking")]
42pub use http_config::extract_from_homepage;
43pub use http_config::MetaInfoFile;
44pub use lat_long::LatLong;
45use streamcheckerror::StreamCheckError;
46use streamcheckresult::StreamCheckResult;
47pub use streamcheckresult::UrlType;
48pub use streaminfo::StreamInfo;
49
50/// Check url for audio/video stream.
51/// # Example
52/// ```ignore
53/// let item = av_stream_info_rust::check("https://example.com/test.m3u", 10, 3, 3);
54/// println!("{:#?}", item);
55/// ```
56/// # Arguments
57/// * `url` - The url to check
58/// * `timeout` - TCP timeout for connect and read in seconds
59/// * `max_depth` - How many layers of http redirects or playlists should be followed
60/// * `retries` - Retry how many times to find at least one working stream
61#[cfg(feature = "blocking")]
62pub fn check(
63    url: &str,
64    timeout: u32,
65    max_depth: u8,
66    max_retries: u8,
67) -> StreamResult {
68    use async_std::task;
69    task::block_on(async {
70        check_async(&url, timeout, max_depth, max_retries).await
71    })
72}
73
74/// Check url for audio/video stream.
75/// # Example
76/// ```ignore
77/// let item = av_stream_info_rust::check_async("https://example.com/test.m3u", 10, 3, 3).await?;
78/// println!("{:#?}", item);
79/// ```
80/// # Arguments
81/// * `url` - The url to check
82/// * `timeout` - TCP timeout for connect and read in seconds
83/// * `max_depth` - How many layers of http redirects or playlists should be followed
84/// * `retries` - Retry how many times to find at least one working stream
85pub async fn check_async(
86    url: &str,
87    timeout: u32,
88    max_depth: u8,
89    max_retries: u8,
90) -> StreamResult {
91    let mut retries: u8 = 0;
92    let mut log_lines = vec![];
93    loop {
94        let mut urllist = vec![url.to_string()];
95        let mut already_checked = vec![];
96        debug!("Check retry {}", retries);
97        for depth in 1..max_depth {
98            debug!("Check depth {}", depth);
99            for url in urllist.clone() {
100                if already_checked.contains(&url) {
101                    continue;
102                }
103                debug!("Check url '{}'", url);
104                log_lines.push(format!("Checking '{}' (retry {})", url, retries));
105                already_checked.push(url.clone());
106                let result = streamcheck::check(&mut log_lines, &url, timeout).await;
107                match &result.info {
108                    Ok(info) => match info {
109                        UrlType::Stream(info) => {
110                            return StreamResult::new(result.url(), Some(info.to_owned()), log_lines);
111                        },
112                        _ => {}
113                    },
114                    _ => {}
115                };
116                match &result.info {
117                    Ok(info) => match info {
118                        UrlType::Playlist(list) => {
119                            log_lines.push(format!("Found playlist at '{}'", url));
120                            for url in list.iter() {
121                                log_lines.push(format!("Adding url '{}' to check list", url));
122                            }
123                            urllist.extend(list.clone());
124                            debug!("Urllist updated: {:?}", urllist);
125                        }
126                        _ => {}
127                    },
128                    _ => {}
129                };
130            }
131        }
132        retries += 1;
133        if retries > max_retries {
134            break;
135        }
136        thread::sleep(Duration::from_secs(1));
137    }
138    return StreamResult::new(url, None, log_lines);
139}