From 3f759bce2e8fe5e9717ee8675c93489963bd925d Mon Sep 17 00:00:00 2001 From: Jeena Date: Sat, 21 Mar 2026 02:37:06 +0000 Subject: [PATCH] image-cache: rewrite URLs eagerly, download lazily via scheme handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit process() was downloading all images before returning, blocking the article list update for potentially minutes on a first run or after a cache wipe. Move all network I/O out of process(): - process() now only rewrites src="https://..." to the custom feedthemonkey-img:/// scheme — it is synchronous and instant. - The URI scheme handler already downloads and caches on demand, so images are fetched the first time the WebView requests them and served from disk on every subsequent view. This means the article list appears immediately after a server fetch regardless of how many images need caching. --- src/image_cache.rs | 58 +++++++++++++++------------------------------- src/window.rs | 2 +- 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/src/image_cache.rs b/src/image_cache.rs index 2d87899..1d59ab5 100644 --- a/src/image_cache.rs +++ b/src/image_cache.rs @@ -134,47 +134,27 @@ fn serve_file(request: webkit6::URISchemeRequest, path: PathBuf) { } } -/// Download all remote images in every article and rewrite their src attributes -/// to feedthemonkey-img:// URIs so images are served through the cache handler, -/// which re-downloads automatically on a cache miss. -pub async fn process(articles: Vec
) -> Vec
{ - let dir = images_dir(); - std::fs::create_dir_all(&dir).ok(); - - let client = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(30)) - .build() - .unwrap_or_default(); - +/// Rewrite all remote image src attributes to feedthemonkey-img:// URIs. +/// No network requests are made here — images are downloaded lazily by the +/// URI scheme handler the first time the WebView requests them, then cached. +pub fn process(articles: Vec
) -> Vec
{ let re = regex::Regex::new(r#"src="(https?://[^"]+)""#).unwrap(); - - let mut out = Vec::with_capacity(articles.len()); - for mut article in articles { - let content = article.content.clone(); - let mut rewritten = content.clone(); - - for cap in re.captures_iter(&content) { - let url = &cap[1]; - let path = dir.join(url_to_filename(url)); - if !path.exists() { - if let Ok(resp) = client.get(url).send().await { - if let Ok(bytes) = resp.bytes().await { - std::fs::write(&path, &bytes).ok(); - } - } + articles + .into_iter() + .map(|mut article| { + let content = article.content.clone(); + let mut rewritten = content.clone(); + for cap in re.captures_iter(&content) { + let url = &cap[1]; + rewritten = rewritten.replace( + &format!("src=\"{}\"", url), + &format!("src=\"{}\"", original_url_to_scheme_uri(url)), + ); } - // Always rewrite to the scheme URI so the handler can re-download - // if the cache directory is ever deleted. - rewritten = rewritten.replace( - &format!("src=\"{}\"", url), - &format!("src=\"{}\"", original_url_to_scheme_uri(url)), - ); - } - - article.content = rewritten; - out.push(article); - } - out + article.content = rewritten; + article + }) + .collect() } /// Remove cached image files no longer referenced by any article. diff --git a/src/window.rs b/src/window.rs index 95ba8ea..15e6ed8 100644 --- a/src/window.rs +++ b/src/window.rs @@ -597,7 +597,7 @@ pub mod imp { async move { let articles = api.fetch_unread().await?; let articles = if cache_images { - crate::image_cache::process(articles).await + crate::image_cache::process(articles) } else { articles };