From 78f004ff4f382e8837008c06ce36d9bfae5613c3 Mon Sep 17 00:00:00 2001 From: lashman Date: Fri, 27 Feb 2026 23:40:18 +0200 Subject: [PATCH] Add source URL tracking and display for AppImages --- src/core/database.rs | 10 ++++++++++ src/core/updater.rs | 23 +++++++++++++++++++++++ src/ui/detail_view.rs | 7 +++++-- src/window.rs | 3 +++ 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/core/database.rs b/src/core/database.rs index d7836cc..173d440 100644 --- a/src/core/database.rs +++ b/src/core/database.rs @@ -1744,6 +1744,16 @@ impl Database { Ok(self.conn.last_insert_rowid()) } + // --- Source URL --- + + pub fn set_source_url(&self, id: i64, url: Option<&str>) -> SqlResult<()> { + self.conn.execute( + "UPDATE appimages SET source_url = ?2 WHERE id = ?1", + params![id, url], + )?; + Ok(()) + } + // --- Version rollback --- pub fn set_previous_version(&self, id: i64, path: Option<&str>) -> SqlResult<()> { diff --git a/src/core/updater.rs b/src/core/updater.rs index 005c02d..79039a7 100644 --- a/src/core/updater.rs +++ b/src/core/updater.rs @@ -62,6 +62,29 @@ pub struct UpdateCheckResult { pub file_size: Option, } +/// Detect the source URL for an AppImage from its update_info string. +pub fn detect_source_url(update_info: Option<&str>) -> Option { + let info = update_info?; + let parts: Vec<&str> = info.split('|').collect(); + if info.starts_with("gh-releases-zsync|") && parts.len() >= 3 { + return Some(format!("https://github.com/{}/{}", parts[1], parts[2])); + } + if info.starts_with("gl-releases-zsync|") && parts.len() >= 4 { + return Some(format!("https://{}/{}/{}", parts[1], parts[2], parts[3])); + } + if info.starts_with("zsync|") { + if let Some(url_part) = parts.get(1) { + // Extract the base URL (up to the hostname) + if let Some(idx) = url_part.find("://") { + if let Some(slash) = url_part[idx + 3..].find('/') { + return Some(url_part[..idx + 3 + slash].to_string()); + } + } + } + } + None +} + /// Parse the raw update info string from an AppImage's ELF section. pub fn parse_update_info(raw: &str) -> Option { let raw = raw.trim().trim_matches('\0'); diff --git a/src/ui/detail_view.rs b/src/ui/detail_view.rs index 454d1c5..d4e3738 100644 --- a/src/ui/detail_view.rs +++ b/src/ui/detail_view.rs @@ -517,11 +517,14 @@ fn build_overview_tab(record: &AppImageRecord, db: &Rc) -> gtk::Box { // ----------------------------------------------------------------------- // Links section // ----------------------------------------------------------------------- + // Use source_url as fallback for vcs_url (auto-detected from update_info) + let source_code_url = record.vcs_url.clone().or_else(|| record.source_url.clone()); + let has_links = record.homepage_url.is_some() || record.bugtracker_url.is_some() || record.donation_url.is_some() || record.help_url.is_some() - || record.vcs_url.is_some(); + || source_code_url.is_some(); if has_links { let links_group = adw::PreferencesGroup::builder() @@ -531,7 +534,7 @@ fn build_overview_tab(record: &AppImageRecord, db: &Rc) -> gtk::Box { let link_entries: &[(&str, &str, &Option)] = &[ ("Homepage", "web-browser-symbolic", &record.homepage_url), ("Report a problem", "bug-symbolic", &record.bugtracker_url), - ("Source code", "code-symbolic", &record.vcs_url), + ("Source code", "code-symbolic", &source_code_url), ("Documentation", "help-browser-symbolic", &record.help_url), ("Donate", "emblem-favorite-symbolic", &record.donation_url), ]; diff --git a/src/window.rs b/src/window.rs index 192b888..9b63653 100644 --- a/src/window.rs +++ b/src/window.rs @@ -660,6 +660,9 @@ impl DriftwoodWindow { ); if raw_info.is_some() { bg_db.update_update_info(record_id, raw_info.as_deref(), None).ok(); + if let Some(source) = updater::detect_source_url(raw_info.as_deref()) { + bg_db.set_source_url(record_id, Some(&source)).ok(); + } } if let Some(result) = check_result { if result.update_available {