My personal site cherry.computer
htmx tailwind axum askama
at main 91 lines 2.9 kB view raw
1use std::{ 2 sync::{Arc, LazyLock}, 3 time::Duration, 4}; 5 6use anyhow::Context; 7use reqwest::Url; 8use scraper::{Html, Selector}; 9use tokio::sync::RwLock; 10use tracing::instrument; 11 12use super::{ 13 Media, 14 cached::{MediaCache, cache_or_fetch, try_cache_or_fetch}, 15}; 16 17pub async fn fetch() -> anyhow::Result<Media> { 18 static FIRST_ENTRY_SEL: LazyLock<Selector> = 19 LazyLock::new(|| Selector::parse(".journal_entry:first-child").unwrap()); 20 static NAME_SEL: LazyLock<Selector> = 21 LazyLock::new(|| Selector::parse(".game-name a").unwrap()); 22 static IMAGE_SEL: LazyLock<Selector> = LazyLock::new(|| Selector::parse(".card-img").unwrap()); 23 static PLATFORM_SEL: LazyLock<Selector> = 24 LazyLock::new(|| Selector::parse(".journal-platform").unwrap()); 25 static URL_SEL: LazyLock<Selector> = 26 LazyLock::new(|| Selector::parse("a:has(.fa-arrow-right)").unwrap()); 27 28 let page_url = Url::parse("https://backloggd.com/u/cherryfunk/journal") 29 .context("wrote invalid Backloggd URL")?; 30 let html = reqwest::get(page_url.clone()) 31 .await 32 .context("failed to fetch Backloggd page")? 33 .text() 34 .await 35 .context("failed to get HTML text")?; 36 let document = Html::parse_document(&html); 37 38 let first_entry = document 39 .select(&FIRST_ENTRY_SEL) 40 .next() 41 .context("couldn't find any journal entries")?; 42 let name = first_entry 43 .select(&NAME_SEL) 44 .next() 45 .context("couldn't find name element")? 46 .text() 47 .next() 48 .context("name element didn't have any text")? 49 .to_owned(); 50 let image = first_entry 51 .select(&IMAGE_SEL) 52 .next() 53 .context("couldn't find image element")? 54 .attr("src") 55 .context("image element didn't have src attribute")? 56 .to_owned(); 57 let platform = first_entry 58 .select(&PLATFORM_SEL) 59 .next() 60 .context("couldn't find platform element")? 61 .text() 62 .next() 63 .context("platform element didn't have any text")? 64 .to_owned(); 65 let url = first_entry 66 .select(&URL_SEL) 67 .next() 68 .context("couldn't find log URL element")? 69 .attr("href") 70 .context("log anchor didn't have a URL")? 71 .to_owned(); 72 let url = page_url.join(&url).context("log URL was invalid")?; 73 74 Ok(Media { 75 name, 76 image, 77 context: platform, 78 url: url.into(), 79 }) 80} 81 82static CACHE: LazyLock<MediaCache> = LazyLock::new(|| Arc::new(RwLock::new(None))); 83static TTL: Duration = Duration::from_secs(300); 84#[instrument(name = "backlogged_try_cached_fetch")] 85pub fn try_cached_fetch() -> Option<Media> { 86 try_cache_or_fetch(&CACHE, TTL, fetch) 87} 88#[instrument(name = "backlogged_cached_fetch")] 89pub async fn cached_fetch() -> Option<Media> { 90 cache_or_fetch(&CACHE, TTL, fetch).await 91}