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                        }
88                        _ => {}
89                    },
90                    _ => {}
91                };
92            }
93        }
94        retries += 1;
95        if retries > max_retries {
96            break;
97        }
98        thread::sleep(Duration::from_secs(1));
99    }
100    return StreamCheckResult::new(url, Err(StreamCheckError::NoResult()));
101}
102
103/// Check url for audio/video stream.
104/// # Example
105/// ```ignore
106/// let item = av_stream_info_rust::check_tree_async("https://example.com/test.m3u", 10, 3, 3).await?;
107/// println!("{:#?}", item);
108/// ```
109/// # Arguments
110/// * `url` - The url to check
111/// * `timeout` - TCP timeout for connect and read in seconds
112/// * `max_depth` - How many layers of http redirects or playlists should be followed
113/// * `retries` - Retry how many times to find at least one working stream
114/// * `early_exit_on_first_ok` - return from checking as early as 1 working stream has been found
115pub async fn check_tree_async(
116    url: &str,
117    timeout: u32,
118    max_depth: u8,
119    max_retries: u8,
120) -> StreamCheckResult {
121    let mut retries: u8 = 0;
122    loop {
123        let mut urllist = vec![url.to_string()];
124        debug!("Check retry {}", retries);
125        for depth in 1..max_depth {
126            debug!("Check depth {}", depth);
127            for url in urllist.clone() {
128                debug!("Check url '{}'", url);
129                let result = streamcheck::check(&url, timeout).await;
130                if has_ok_result(&result) {
131                    return result;
132                }
133                match &result.info {
134                    Ok(info) => match info {
135                        UrlType::Playlist(list) => {
136                            urllist = list.clone();
137                        }
138                        _ => {}
139                    },
140                    _ => {}
141                };
142            }
143        }
144        retries += 1;
145        if retries > max_retries {
146            break;
147        }
148        thread::sleep(Duration::from_secs(1));
149    }
150    return StreamCheckResult::new(url, Err(StreamCheckError::NoResult()));
151}
152
153fn has_ok_result(result: &StreamCheckResult) -> bool {
154    match &result.info {
155        Ok(info) => match info {
156            UrlType::Stream(_) => true,
157            UrlType::Playlist(_) => false,
158        },
159        Err(_) => false,
160    }
161}