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_tree("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 native_tls;
17extern crate playlist_decoder;
18extern crate reqwest;
19extern crate url;
20
21extern crate serde;
22extern crate serde_json;
23
24mod decodeerror;
25mod lat_long;
26mod request;
27mod request_error;
28mod streamcheck;
29mod streamcheckerror;
30mod streamcheckresult;
31mod streaminfo;
32
33mod http_config;
34
35use std::thread;
36use std::time::Duration;
37
38pub use decodeerror::DecodeError;
39pub use http_config::extract_from_homepage_async;
40#[cfg(feature = "blocking")]
41pub use http_config::extract_from_homepage;
42pub use http_config::MetaInfoFile;
43pub use lat_long::LatLong;
44pub use streamcheckerror::StreamCheckError;
45pub use streamcheckresult::StreamCheckResult;
46pub use streamcheckresult::UrlType;
47pub use streaminfo::StreamInfo;
48
49/// Check url for audio/video stream.
50/// # Example
51/// ```ignore
52/// let item = av_stream_info_rust::check_tree("https://example.com/test.m3u", 10, 3, 3);
53/// println!("{:#?}", item);
54/// ```
55/// # Arguments
56/// * `url` - The url to check
57/// * `timeout` - TCP timeout for connect and read in seconds
58/// * `max_depth` - How many layers of http redirects or playlists should be followed
59/// * `retries` - Retry how many times to find at least one working stream
60/// * `early_exit_on_first_ok` - return from checking as early as 1 working stream has been found
61#[cfg(feature = "blocking")]
62pub fn check_tree(
63    url: &str,
64    timeout: u32,
65    max_depth: u8,
66    max_retries: u8,
67) -> StreamCheckResult {
68    use async_std::task;
69    let mut retries: u8 = 0;
70    loop {
71        let mut urllist = vec![url.to_string()];
72        debug!("Check retry {}", retries);
73        for depth in 1..max_depth {
74            debug!("Check depth {}", depth);
75            for url in urllist.clone() {
76                debug!("Check url '{}'", url);
77                let result = task::block_on(async {
78                    streamcheck::check(&url, timeout).await
79                });
80                if has_ok_result(&result) {
81                    return result;
82                }
83                match &result.info {
84                    Ok(info) => match info {
85                        UrlType::Playlist(list) => {
86                            urllist = list.clone();
87                            debug!("Urllist updated: {:?}", urllist);
88                        }
89                        _ => {}
90                    },
91                    _ => {}
92                };
93            }
94        }
95        retries += 1;
96        if retries > max_retries {
97            break;
98        }
99        thread::sleep(Duration::from_secs(1));
100    }
101    return StreamCheckResult::new(url, Err(StreamCheckError::NoResult()));
102}
103
104/// Check url for audio/video stream.
105/// # Example
106/// ```ignore
107/// let item = av_stream_info_rust::check_tree_async("https://example.com/test.m3u", 10, 3, 3).await?;
108/// println!("{:#?}", item);
109/// ```
110/// # Arguments
111/// * `url` - The url to check
112/// * `timeout` - TCP timeout for connect and read in seconds
113/// * `max_depth` - How many layers of http redirects or playlists should be followed
114/// * `retries` - Retry how many times to find at least one working stream
115/// * `early_exit_on_first_ok` - return from checking as early as 1 working stream has been found
116pub async fn check_tree_async(
117    url: &str,
118    timeout: u32,
119    max_depth: u8,
120    max_retries: u8,
121) -> StreamCheckResult {
122    let mut retries: u8 = 0;
123    loop {
124        let mut urllist = vec![url.to_string()];
125        debug!("Check retry {}", retries);
126        for depth in 1..max_depth {
127            debug!("Check depth {}", depth);
128            for url in urllist.clone() {
129                debug!("Check url '{}'", url);
130                let result = streamcheck::check(&url, timeout).await;
131                if has_ok_result(&result) {
132                    return result;
133                }
134                match &result.info {
135                    Ok(info) => match info {
136                        UrlType::Playlist(list) => {
137                            urllist = list.clone();
138                            debug!("Urllist updated: {:?}", urllist);
139                        }
140                        _ => {}
141                    },
142                    _ => {}
143                };
144            }
145        }
146        retries += 1;
147        if retries > max_retries {
148            break;
149        }
150        thread::sleep(Duration::from_secs(1));
151    }
152    return StreamCheckResult::new(url, Err(StreamCheckError::NoResult()));
153}
154
155fn has_ok_result(result: &StreamCheckResult) -> bool {
156    match &result.info {
157        Ok(info) => match info {
158            UrlType::Stream(_) => true,
159            UrlType::Playlist(_) => false,
160        },
161        Err(_) => false,
162    }
163}