use bytes::Bytes; use chrono::{Local, NaiveDate}; use std::{io::Read, sync::Arc, time::Duration}; use stream_download::{ source::DecodeError, storage::temp::TempStorageProvider, Settings, StreamDownload, }; use tokio::sync::{mpsc, Notify, RwLock}; pub struct AppState { pub chunks: RwLock>, pub complete: RwLock, pub notify: Notify, pub downloaded_on_day: RwLock>, } impl AppState { pub fn new() -> Self { Self { chunks: RwLock::new(Vec::new()), complete: RwLock::new(false), notify: Notify::new(), downloaded_on_day: RwLock::new(None), } } pub async fn check_update(self: Arc) { let today = Local::now().date_naive(); if let Some(downloaded_on_day) = *self.downloaded_on_day.read().await { if today == downloaded_on_day { return; } } self.reset().await; *self.downloaded_on_day.write().await = Some(today); let latest_url = player::newest_morning_journal_streaming_url() .await .unwrap(); let self_clone = Arc::clone(&self); tokio::spawn(async move { if let Err(e) = self_clone.download_stream(&latest_url).await { eprintln!("Download failed: {e:?}"); } }); } async fn download_stream(&self, url: &str) -> Result<(), Box> { println!("Starting download from: {url}"); let reader = match StreamDownload::new_http( url.parse()?, TempStorageProvider::new(), Settings::default(), ) .await { Ok(reader) => reader, Err(e) => return Err(e.decode_error().await.into()), }; let (tx, mut rx) = mpsc::unbounded_channel::(); // Spawn blocking task to read from the stream let read_handle = tokio::task::spawn_blocking( move || -> Result<(), Box> { let mut reader = reader; let mut buffer = vec![0u8; 8192]; // 8KB buffer loop { match reader.read(&mut buffer) { Ok(0) => { // End of stream println!("End of stream reached"); break; } Ok(n) => { let chunk = bytes::Bytes::copy_from_slice(&buffer[..n]); if tx.send(chunk).is_err() { // Receiver dropped, exit break; } } Err(e) => { println!("Error reading from stream: {e:?}"); // Short delay before retrying std::thread::sleep(Duration::from_millis(100)); } } } Ok(()) }, ); // Process received chunks while let Some(bytes) = rx.recv().await { self.add_chunk(bytes).await; } // Wait for the reading task to complete match read_handle.await { Ok(result) => result.unwrap(), Err(e) => return Err(e.into()), } self.mark_complete().await; println!("Download complete!"); Ok(()) } async fn reset(&self) { // Clear all chunks self.chunks.write().await.clear(); // Reset completion status *self.complete.write().await = false; *self.downloaded_on_day.write().await = None; // Notify any waiting tasks about the reset self.notify.notify_waiters(); } async fn add_chunk(&self, chunk: Bytes) { self.chunks.write().await.push(chunk); self.notify.notify_waiters(); } async fn mark_complete(&self) { *self.complete.write().await = true; self.notify.notify_waiters(); } pub async fn is_complete(&self) -> bool { *self.complete.read().await } pub async fn chunk_count(&self) -> usize { self.chunks.read().await.len() } }