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 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;
32mod external_check_result;
33
34mod http_config;
35
36use std::thread;
37use std::time::Duration;
38
39pub use decodeerror::DecodeError;
40pub use external_check_result::StreamResult;
41pub use http_config::extract_from_homepage_async;
42#[cfg(feature = "blocking")]
43pub use http_config::extract_from_homepage;
44pub use http_config::MetaInfoFile;
45pub use lat_long::LatLong;
46use streamcheckerror::StreamCheckError;
47use streamcheckresult::StreamCheckResult;
48pub use streamcheckresult::UrlType;
49pub use streaminfo::StreamInfo;
50
51/// Check url for audio/video stream.
52/// # Example
53/// ```ignore
54/// let item = av_stream_info_rust::check("https://example.com/test.m3u", 10, 3, 3);
55/// println!("{:#?}", item);
56/// ```
57/// # Arguments
58/// * `url` - The url to check
59/// * `timeout` - TCP timeout for connect and read in seconds
60/// * `max_depth` - How many layers of http redirects or playlists should be followed
61/// * `retries` - Retry how many times to find at least one working stream
62#[cfg(feature = "blocking")]
63pub fn check(
64    url: &str,
65    timeout: u32,
66    max_depth: u8,
67    max_retries: u8,
68) -> StreamResult {
69    use async_std::task;
70    task::block_on(async {
71        check_async(&url, timeout, max_depth, max_retries).await
72    })
73}
74
75/// Check url for audio/video stream.
76/// # Example
77/// ```ignore
78/// let item = av_stream_info_rust::check_async("https://example.com/test.m3u", 10, 3, 3).await?;
79/// println!("{:#?}", item);
80/// ```
81/// # Arguments
82/// * `url` - The url to check
83/// * `timeout` - TCP timeout for connect and read in seconds
84/// * `max_depth` - How many layers of http redirects or playlists should be followed
85/// * `retries` - Retry how many times to find at least one working stream
86pub async fn check_async(
87    url: &str,
88    timeout: u32,
89    max_depth: u8,
90    max_retries: u8,
91) -> StreamResult {
92    let mut retries: u8 = 0;
93    let mut log_lines = vec![];
94    let mut already_checked = vec![];
95    loop {
96        let mut urllist = vec![url.to_string()];
97        debug!("Check retry {}", retries);
98        for depth in 1..max_depth {
99            debug!("Check depth {}", depth);
100            for url in urllist.clone() {
101                if already_checked.contains(&url) {
102                    continue;
103                }
104                debug!("Check url '{}'", url);
105                log_lines.push(format!("Checking '{}' (retry {})", url, retries));
106                already_checked.push(url.clone());
107                let result = streamcheck::check(&mut log_lines, &url, timeout).await;
108                match &result.info {
109                    Ok(info) => match info {
110                        UrlType::Stream(info) => {
111                            return StreamResult::new(result.url(), Some(info.to_owned()), log_lines);
112                        },
113                        _ => {}
114                    },
115                    _ => {}
116                };
117                match &result.info {
118                    Ok(info) => match info {
119                        UrlType::Playlist(list) => {
120                            log_lines.push(format!("Found playlist at '{}'", url));
121                            for url in list.iter() {
122                                log_lines.push(format!("Adding url '{}' to check list", url));
123                            }
124                            urllist.extend(list.clone());
125                            debug!("Urllist updated: {:?}", urllist);
126                        }
127                        _ => {}
128                    },
129                    _ => {}
130                };
131            }
132        }
133        retries += 1;
134        if retries > max_retries {
135            break;
136        }
137        thread::sleep(Duration::from_secs(1));
138    }
139    return StreamResult::new(url, None, log_lines);
140}