diff --git a/src/lib.rs b/src/lib.rs index 18d99e7..74be9fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,23 @@ +use chrono::NaiveDate; use serde_json::Value; -pub async fn newest_morning_journal_streaming_url() -> Result> { - let url = get_newest_morning_journal().await?; - get_streaming_url(url).await +#[derive(Clone)] +pub struct Episode { + pub url: String, + pub date: NaiveDate, +} + +pub async fn newest_morning_journal_streaming_url() -> Result> { + let (date, url) = get_newest_morning_journal().await?; + let url = get_streaming_url(url).await?; + + Ok(Episode { url, date }) } // List of broadcasts: https://audioapi.orf.at/oe1/api/json/current/broadcasts // // ^ contains link, e.g. https://audioapi.orf.at/oe1/api/json/4.0/broadcast/797577/20250611 -async fn get_newest_morning_journal() -> Result> { +async fn get_newest_morning_journal() -> Result<(NaiveDate, String), Box> { let url = "https://audioapi.orf.at/oe1/api/json/current/broadcasts"; let data: Value = reqwest::get(url).await?.json().await?; @@ -19,7 +28,11 @@ async fn get_newest_morning_journal() -> Result>, - pub last_download_on_day: RwLock>, + pub episodes: RwLock>, } impl AppState { pub fn new() -> Self { Self { - urls: RwLock::new(Vec::new()), - last_download_on_day: RwLock::new(None), + episodes: RwLock::new(Vec::new()), } } pub async fn check_update(self: Arc) { - let today = Local::now().date_naive(); - if let Some(downloaded_on_day) = *self.last_download_on_day.read().await - && today == downloaded_on_day - { + if self.already_downloaded_today().await { return; } - *self.last_download_on_day.write().await = Some(today); - - let latest_url = player::newest_morning_journal_streaming_url() + let latest_episode = player::newest_morning_journal_streaming_url() .await .unwrap(); - let mut old = self.urls.read().await.clone(); - old.push(latest_url); + if self.already_downloaded_url(&latest_episode.url).await { + return; + } + + let mut old = self.episodes.read().await.clone(); + old.push(latest_episode); let new = old.into_iter().rev().take(10).collect(); // only keep last 10 - *self.urls.write().await = new; + *self.episodes.write().await = new; + } + + async fn already_downloaded_today(self: &Arc) -> bool { + let today = Local::now().date_naive(); + self.episodes.read().await.iter().any(|x| x.date == today) + } + + async fn already_downloaded_url(self: &Arc, url: &str) -> bool { + self.episodes.read().await.iter().any(|x| x.url == url) } } diff --git a/src/streamer.rs b/src/streamer.rs index e4f66e0..5edfca6 100644 --- a/src/streamer.rs +++ b/src/streamer.rs @@ -1,19 +1,20 @@ use crate::state::AppState; use axum::{extract::State, http::HeaderMap, response::IntoResponse}; +use player::Episode; use reqwest::header; use std::sync::Arc; pub async fn stream_handler(State(state): State>) -> impl IntoResponse { state.clone().check_update().await; - let content = feed(&state.urls.read().await.to_vec()); + let content = feed(&state.episodes.read().await.to_vec()); let mut headers = HeaderMap::new(); headers.insert(header::CONTENT_TYPE, "application/rss+xml".parse().unwrap()); (headers, content) } -fn feed(urls: &Vec) -> String { +fn feed(episodes: &Vec) -> String { let mut ret = String::new(); ret.push_str(r#""#); ret.push_str(r#""#); @@ -22,15 +23,17 @@ fn feed(urls: &Vec) -> String { ret.push_str("https://news.hofer.link"); ret.push_str("Feed für Ö1 Morgenjournal. Live."); - for url in urls { + for episode in episodes { ret.push_str(""); - ret.push_str(&format!("Morgenjournal")); - ret.push_str(&format!("{}", quick_xml::escape::escape(url))); + ret.push_str(&format!( + "Morgenjournal {}", + &episode.date.format("%d. %m.") + )); ret.push_str(&format!( "\n", - quick_xml::escape::escape(url) + quick_xml::escape::escape(&episode.url) )); - ret.push_str(&format!("Morgenjournal")); + ret.push_str("Morgenjournal"); ret.push_str(""); }