radiko-rs 0.5.0

An unofficial Rust library for accessing Japan's radiko radio streaming service
Documentation
use std::sync::Arc;

use crate::models::search::SearchCondition;
use crate::{dto::program_xml::RadikoProgramXml, models::program::Programs};
use anyhow::{Result, anyhow};
use reqwest::Client;

use super::endpoint::RadikoEndpoint;

pub struct RadikoProgram {
    inner: Arc<RadikoProgramRef>,
}

struct RadikoProgramRef {
    client: Client,
}

impl RadikoProgram {
    pub fn new() -> Self {
        Self {
            inner: Arc::new(RadikoProgramRef {
                client: Client::new(),
            }),
        }
    }

    pub async fn now_on_air_programs(&self, area_id: &str) -> Result<Programs> {
        let res = self
            .inner
            .client
            .get(RadikoEndpoint::now_on_air_programs(area_id))
            .send()
            .await?
            .text()
            .await?;

        let radiko_program: RadikoProgramXml = quick_xml::de::from_str(&res)?;

        Ok(Programs::from(radiko_program))
    }

    pub async fn find_program(&self, condition: &SearchCondition) -> Result<Programs> {
        if condition.key.is_empty() {
            return Err(anyhow!("condition key required."));
        }

        let res = self
            .inner
            .client
            .get(RadikoEndpoint::search_endpoint())
            .query(&condition.to_query_params())
            .send()
            .await?
            .text()
            .await?;

        Ok(serde_json::from_str(&res)?)
    }

    pub async fn weekly_programs_from_station(&self, station_id: &str) -> Result<Programs> {
        let res = self
            .inner
            .client
            .get(RadikoEndpoint::weekly_programs_endpoint(station_id))
            .send()
            .await?
            .text()
            .await?;

        let radiko_program: RadikoProgramXml = quick_xml::de::from_str(&res)?;

        Ok(Programs::from(radiko_program))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::radiko::Radiko;

    #[tokio::test]
    async fn get_now_on_air_programs_test() -> Result<()> {
        let area_id = "JP13";
        let radiko = Radiko::new().await;
        let programs = radiko.now_on_air_programs(area_id).await?;

        println!("{}_now_on_air_programs: {:#?}", area_id, programs);

        assert!(!programs.data.is_empty());

        Ok(())
    }

    #[tokio::test]
    async fn find_program_from_condition_test() -> Result<()> {
        let search_condition = SearchCondition {
            key: vec!["オールナイトニッポン".to_string(), "".to_string()],
            station_id: Some(vec!["LFR".to_string()]),
            ..Default::default()
        };
        let radiko = Radiko::new().await;
        let result = radiko.find_program(&search_condition).await?;

        println!("{:#?}", result);

        assert!(!result.data.is_empty());
        Ok(())
    }

    #[tokio::test]
    async fn find_weekly_programs_from_station_test() -> Result<()> {
        let station_id = "LFR";
        let radiko = Radiko::new().await;
        let programs = radiko.weekly_programs_from_station(station_id).await?;

        println!("{}_weekly_programs: {:#?}", station_id, programs);

        assert!(!programs.data.is_empty());

        Ok(())
    }

    #[tokio::test]
    async fn program_duration_methods_test() -> Result<()> {
        let station_id = "LFR";
        let radiko = Radiko::new().await;
        let programs = radiko.weekly_programs_from_station(station_id).await?;
        let program_len = programs.data.len();
        let target_program = programs.data[program_len - 150].clone();

        println!("program: {:#?}", target_program);

        println!(
            "now2start_time num_secs: {:#?}",
            target_program.now_to_start_duration(None)
        );

        println!(
            "start2end secs: {:#?}",
            target_program.start_to_end_duration()
        );

        assert!(!programs.data.is_empty());

        Ok(())
    }
}