From 505d574c93ea5887ff2ec3fc8e630493df22d88a Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 20 Apr 2022 21:51:14 +0200 Subject: [PATCH] random server select --- Cargo.toml | 6 +- src/bin/test.rs | 13 ++-- src/lib.rs | 176 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 154 insertions(+), 41 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b314558..efcdbb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" [dependencies] async-std = { version = "*", features = ["attributes", "tokio1"] } +async-std-resolver = "0.21.2" +chrono = { version = "0.4.19", features = ["serde"] } +rand = { version = "0.8.5" } reqwest = { version = "0.11.10", features = ["json"] } -serde = { version = "1.0.136", features = ["derive"] } -async-std-resolver = "0.21.2" \ No newline at end of file +serde = { version = "1.0.136", features = ["derive"] } \ No newline at end of file diff --git a/src/bin/test.rs b/src/bin/test.rs index c4a90c2..49cdae9 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -1,13 +1,12 @@ -use radiobrowser_lib_rust::ApiConfig; +use radiobrowser_lib_rust::RadioBrowserAPI; use std::error::Error; #[async_std::main] async fn main() -> Result<(), Box> { - let servers = radiobrowser_lib_rust::get_servers().await?; - println!("Servers: {:?}", servers); - for server in servers { - let config: ApiConfig = radiobrowser_lib_rust::get_server_config(server).await?; - println!("{:#?}", config); - } + let mut api = RadioBrowserAPI::new().await?; + let config = api.get_server_config().await?; + println!("{:#?}", config); + let countries = api.get_countries().await?; + println!("{:?}", countries); Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 4bba390..692c7cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,81 @@ use reqwest; +use serde::de::DeserializeOwned; use serde::Deserialize; use std::error::Error; +use rand::seq::SliceRandom; +use rand::thread_rng; + use async_std_resolver::proto::rr::RecordType; use async_std_resolver::proto::xfer::DnsRequestOptions; use async_std_resolver::{config, resolver}; +use chrono::DateTime; +use chrono::Utc; + +#[derive(PartialEq, Deserialize, Debug)] +pub struct Station { + pub changeuuid: String, + pub stationuuid: String, + pub serveruuid: Option, + pub name: String, + pub url: String, + pub url_resolved: String, + pub homepage: String, + pub favicon: String, + pub tags: String, + pub country: String, + pub countrycode: String, + pub iso_3166_2: Option, + pub state: String, + pub language: String, + pub languagecodes: Option, + pub votes: i32, + pub lastchangetime: String, + pub lastchangetime_iso8601: Option>, + pub codec: String, + pub bitrate: u32, + pub hls: i8, + pub lastcheckok: i8, + pub lastchecktime: String, + pub lastchecktime_iso8601: Option>, + pub lastcheckoktime: String, + pub lastcheckoktime_iso8601: Option>, + pub lastlocalchecktime: String, + pub lastlocalchecktime_iso8601: Option>, + pub clicktimestamp: String, + pub clicktimestamp_iso8601: Option>, + pub clickcount: u32, + pub clicktrend: i32, + pub ssl_error: Option, + pub geo_lat: Option, + pub geo_long: Option, + pub has_extended_info: Option, +} + +#[derive(PartialEq, Eq, Deserialize, Debug)] +pub struct ApiCountry { + pub name: String, + pub iso_3166_1: String, + pub stationcount: u32, +} + +#[derive(PartialEq, Eq, Deserialize, Debug)] +pub struct ApiLanguage { + pub name: String, + pub iso_639: Option, + pub stationcount: u32, +} + +#[derive(PartialEq, Deserialize, Debug)] +pub struct ApiStreamingServer { + pub uuid: String, + pub url: String, + pub statusurl: Option, + pub status: Option, + pub error: Option, +} + #[derive(Debug, Deserialize)] pub struct ApiConfig { pub check_enabled: bool, @@ -32,42 +102,84 @@ pub struct ApiConfig { pub language_to_code_filepath: String, } -pub async fn get_server_config>(server: P) -> Result> { - let client = reqwest::Client::new(); - let res = client - .post(format!("https://{}/json/config", server.as_ref())) - .send() - .await? - .json::() - .await?; - Ok(res) +pub struct RadioBrowserAPI { + servers: Vec, + current: usize, } -pub async fn get_servers() -> Result, Box> { - // Construct a new Resolver with default configuration options - let resolver = resolver( - config::ResolverConfig::default(), - config::ResolverOpts::default(), - ) - .await?; +impl RadioBrowserAPI { + pub async fn new() -> Result> { + Ok(RadioBrowserAPI { + servers: RadioBrowserAPI::get_servers().await?, + current: 0, + }) + } - // Lookup the IP addresses associated with a name. - // This returns a future that will lookup the IP addresses, it must be run in the Core to - // to get the actual result. - let response = resolver - .lookup( - "_api._tcp.radio-browser.info", - RecordType::SRV, - DnsRequestOptions::default(), + pub fn get_current_server(&mut self) -> String { + if self.servers.len() > 0 { + self.current = self.current % self.servers.len(); + format!("https://{}", self.servers[self.current]) + } else { + String::from("https://de1.api.radio-browser.info") + } + } + + pub async fn post_api>( + &mut self, + endpoint: O, + ) -> Result> { + let client = reqwest::Client::new(); + let res = client + .post(format!( + "{}{}", + self.get_current_server(), + endpoint.as_ref() + )) + .send() + .await? + .json::

() + .await?; + Ok(res) + } + + pub async fn get_server_config(&mut self) -> Result> { + Ok(self.post_api("/json/config").await?) + } + + pub async fn get_countries(&mut self) -> Result, Box> { + Ok(self.post_api("/json/countries").await?) + } + + pub async fn get_countries_filtered>( + &mut self, + filter: P, + ) -> Result, Box> { + Ok(self + .post_api(format!("/json/countries/{}", filter.as_ref())) + .await?) + } + + pub async fn get_servers() -> Result, Box> { + let resolver = resolver( + config::ResolverConfig::default(), + config::ResolverOpts::default(), ) .await?; + let response = resolver + .lookup( + "_api._tcp.radio-browser.info", + RecordType::SRV, + DnsRequestOptions::default(), + ) + .await?; + let mut list: Vec = response + .iter() + .filter_map(|entry| entry.as_srv()) + .map(|entry| entry.target().to_string().trim_matches('.').to_string()) + .collect(); - // There can be many addresses associated with the name, - // this can return IPv4 and/or IPv6 addresses - let list = response - .iter() - .filter_map(|entry| entry.as_srv()) - .map(|entry| entry.target().to_string().trim_matches('.').to_string()) - .collect(); - Ok(list) + list.shuffle(&mut thread_rng()); + println!("Servers: {:?}", list); + Ok(list) + } }