From 4a20483e6cbfdf6e39e6778b20ccc54003d191a3 Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Tue, 28 Jul 2026 07:26:25 +0200 Subject: [PATCH 1/5] fix(web-radio): try the round-robin host, and retry, before failing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported in #452: on Windows, the first category click after reopening WaveFlow often failed with a server error, and simply leaving the Web Radio page and coming back made it work. The plugin pinned a single host, `de1.api.radio-browser.info`, with no fallback and no retry — so any hiccup on the first HTTP call of a session surfaced straight to the user, and the "leave and come back" workaround was just a second attempt. The pin rested on the comment's assumption that "any regional prefix serves the same catalogue, so the chosen one doesn't matter". Checked against reality: of the prefixes that comment names, `at1`, `nl1` and `fi1` no longer resolve at all. A hard-coded node list rots on its own. So the first host is now `all.api.radio-browser.info`, the project's official round-robin DNS entry, which resolves only to nodes that are alive — no list to maintain. `de1` follows as a second attempt, both because it does answer and because having a second entry means a failed first request is retried at all, which is what the report comes down to. A 4xx short-circuits: that's the API rejecting the request itself, and every node would say the same. Both hosts are already covered by the manifest allowlist (`https://*.api.radio-browser.info/**`), so permissions are unchanged. Plugin bumped to 0.1.1 (the version rides in the User-Agent radio-browser asks for) and plugin.wasm rebuilt in both places it lives: the bundled resource and the core test fixture. Refs #452 --- .../tests/fixtures/web-radio/manifest.toml | 2 +- .../core/tests/fixtures/web-radio/plugin.wasm | Bin 142210 -> 130644 bytes src-tauri/plugins/web-radio/Cargo.lock | 2 +- src-tauri/plugins/web-radio/Cargo.toml | 2 +- src-tauri/plugins/web-radio/manifest.toml | 2 +- src-tauri/plugins/web-radio/src/lib.rs | 115 +++++++++++++----- .../resources/plugins/web-radio/manifest.toml | 2 +- .../resources/plugins/web-radio/plugin.wasm | Bin 142210 -> 130644 bytes 8 files changed, 87 insertions(+), 38 deletions(-) diff --git a/src-tauri/crates/core/tests/fixtures/web-radio/manifest.toml b/src-tauri/crates/core/tests/fixtures/web-radio/manifest.toml index a714975e..90dd79be 100644 --- a/src-tauri/crates/core/tests/fixtures/web-radio/manifest.toml +++ b/src-tauri/crates/core/tests/fixtures/web-radio/manifest.toml @@ -9,7 +9,7 @@ schema_version = 1 [plugin] id = "web-radio" name = "Web Radio" -version = "0.1.0" +version = "0.1.1" author = "InstaZDLL" description = "Internet radio backed by radio-browser.info — 30 000+ stations searchable by country, language, tag, or codec." homepage = "https://github.com/InstaZDLL/WaveFlow" diff --git a/src-tauri/crates/core/tests/fixtures/web-radio/plugin.wasm b/src-tauri/crates/core/tests/fixtures/web-radio/plugin.wasm index 70413c37bb18badf41ebb9d8df9717f06ba2facd..c4c81a75b911c31667fb1b4ed1c8b8c6990991ec 100644 GIT binary patch delta 39881 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 z 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 Result, String> { log::emit(Level::Debug, &format!("web-radio resolve: {query}")); - let url = build_url(&query)?; - let body = fetch_json(&url)?; + let path = build_path(&query)?; + let body = fetch_json(&path)?; let stations: Vec = serde_json::from_slice(&body) .map_err(|e| format!("radio-browser response parse: {e}"))?; Ok(stations.into_iter().filter_map(to_track).collect()) @@ -100,7 +118,9 @@ fn entry(label: &str, query: &str) -> Entry { } } -/// Decode the entry / search token into a radio-browser API URL. +/// Decode the entry / search token into a **host-relative** +/// radio-browser API path — [`fetch_json`] prefixes whichever host in +/// [`HOSTS`] answers, so the path must not carry one. /// Tokens we understand: /// /// - `top` → top-voted stations @@ -110,19 +130,19 @@ fn entry(label: &str, query: &str) -> Entry { /// - anything else non-empty → treated as a free-text search term /// (the host's search box hands its raw input here and expects /// matches against station names) -fn build_url(query: &str) -> Result { +fn build_path(query: &str) -> Result { let trimmed = query.trim(); if trimmed.is_empty() { return Err("empty query".into()); } if trimmed.eq_ignore_ascii_case("top") { return Ok(format!( - "{MIRROR}/json/stations/topvote/{PAGE_LIMIT}?hidebroken=true" + "/json/stations/topvote/{PAGE_LIMIT}?hidebroken=true" )); } if trimmed.eq_ignore_ascii_case("trending") { return Ok(format!( - "{MIRROR}/json/stations/lastchange/{PAGE_LIMIT}?hidebroken=true" + "/json/stations/lastchange/{PAGE_LIMIT}?hidebroken=true" )); } if let Some(tag) = trimmed.strip_prefix("tag:") { @@ -131,7 +151,7 @@ fn build_url(query: &str) -> Result { return Err("empty tag".into()); } return Ok(format!( - "{MIRROR}/json/stations/bytag/{tag}?limit={PAGE_LIMIT}&order=votes&reverse=true&hidebroken=true" + "/json/stations/bytag/{tag}?limit={PAGE_LIMIT}&order=votes&reverse=true&hidebroken=true" )); } // `country:` → stations in one country, by ISO 3166-1 @@ -148,7 +168,7 @@ fn build_url(query: &str) -> Result { } let code = code.to_ascii_uppercase(); return Ok(format!( - "{MIRROR}/json/stations/bycountrycodeexact/{code}?limit={PAGE_LIMIT}&order=votes&reverse=true&hidebroken=true" + "/json/stations/bycountrycodeexact/{code}?limit={PAGE_LIMIT}&order=votes&reverse=true&hidebroken=true" )); } // Both the explicit `search:` form and the free-text fallback @@ -161,29 +181,58 @@ fn build_url(query: &str) -> Result { return Err("empty search term".into()); } Ok(format!( - "{MIRROR}/json/stations/search?name={term}&limit={PAGE_LIMIT}&order=votes&reverse=true&hidebroken=true" + "/json/stations/search?name={term}&limit={PAGE_LIMIT}&order=votes&reverse=true&hidebroken=true" )) } -/// Issue a GET, surface the body bytes. The host enforces the -/// manifest's HTTP allowlist + the 10 MB response cap + the offline -/// short-circuit, so failure modes here are limited to network -/// errors and non-2xx HTTP statuses (which we propagate verbatim). -fn fetch_json(url: &str) -> Result, String> { - let req = Request { - method: "GET".into(), - url: url.into(), - headers: vec![ - ("User-Agent".into(), USER_AGENT.into()), - ("Accept".into(), "application/json".into()), - ], - body: None, - }; - let resp = http::send(&req).map_err(|e| format!("http: {e}"))?; - if !(200..300).contains(&resp.status) { - return Err(format!("http status {}", resp.status)); +/// Issue a GET for `path` against each host in [`HOSTS`] until one +/// answers, and surface the body bytes. +/// +/// The host enforces the manifest's HTTP allowlist + the 10 MB +/// response cap + the offline short-circuit, so failure modes here +/// are limited to network errors and non-2xx HTTP statuses. +/// +/// **What is and isn't retried.** A transport failure or a 5xx means +/// "this node couldn't serve it" — worth asking the next one. A 4xx +/// is the API rejecting the request itself (bad tag, malformed +/// query); every node would answer the same way, so it returns +/// immediately rather than burning a second round-trip to be told +/// the same thing twice. +/// +/// Offline mode surfaces as the host's `503` sentinel. It is left in +/// the retry path deliberately: it costs one extra no-op call (the +/// host short-circuits before touching the network), and special- +/// casing the value here would couple the guest to a host +/// implementation detail it has no contract for. +fn fetch_json(path: &str) -> Result, String> { + let mut last_err = String::from("no radio-browser host reachable"); + for host in HOSTS { + let req = Request { + method: "GET".into(), + url: format!("{host}{path}"), + headers: vec![ + ("User-Agent".into(), USER_AGENT.into()), + ("Accept".into(), "application/json".into()), + ], + body: None, + }; + match http::send(&req) { + Ok(resp) if (200..300).contains(&resp.status) => return Ok(resp.body), + Ok(resp) if resp.status < 500 => { + // Client-side rejection: the next node says the same. + return Err(format!("http status {}", resp.status)); + } + Ok(resp) => { + last_err = format!("{host}: http status {}", resp.status); + log::emit(Level::Warn, &format!("radio-browser {last_err}")); + } + Err(e) => { + last_err = format!("{host}: {e}"); + log::emit(Level::Warn, &format!("radio-browser {last_err}")); + } + } } - Ok(resp.body) + Err(format!("http: {last_err}")) } /// Minimal `application/x-www-form-urlencoded`-style encoder for diff --git a/src-tauri/resources/plugins/web-radio/manifest.toml b/src-tauri/resources/plugins/web-radio/manifest.toml index a714975e..90dd79be 100644 --- a/src-tauri/resources/plugins/web-radio/manifest.toml +++ b/src-tauri/resources/plugins/web-radio/manifest.toml @@ -9,7 +9,7 @@ schema_version = 1 [plugin] id = "web-radio" name = "Web Radio" -version = "0.1.0" +version = "0.1.1" author = "InstaZDLL" description = "Internet radio backed by radio-browser.info — 30 000+ stations searchable by country, language, tag, or codec." homepage = "https://github.com/InstaZDLL/WaveFlow" diff --git a/src-tauri/resources/plugins/web-radio/plugin.wasm b/src-tauri/resources/plugins/web-radio/plugin.wasm index 70413c37bb18badf41ebb9d8df9717f06ba2facd..c4c81a75b911c31667fb1b4ed1c8b8c6990991ec 100644 GIT binary patch delta 39881 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 z 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 Date: Wed, 29 Jul 2026 07:13:01 +0200 Subject: [PATCH 2/5] fix(plugin-host): surface the cause chain behind an http error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The report on #452 came with this, and nothing more: plugin web-radio: plugin: http: error sending request for url (https://de1.api.radio-browser.info/json/stations/bycountrycodeexact/IL?…) That is `reqwest::Error`'s Display, which stops at the outermost layer. It says the transport failed but never says why — DNS lookup, connection refused, timeout and TLS handshake all render identically, and the frontend shows this string verbatim. So the one question worth answering ("which of those was it?") is exactly the one the message drops, and the next report would have been just as blind. Walk `source()` and append each distinct cause. Layers that restate their parent are skipped (reqwest and hyper both do this, and "timed out: timed out" reads worse than the line it repeats), and the walk is depth-capped because `source()` is author-controlled. Refs #452 --- src-tauri/crates/core/src/plugin/host_impl.rs | 129 +++++++++++++++++- 1 file changed, 127 insertions(+), 2 deletions(-) diff --git a/src-tauri/crates/core/src/plugin/host_impl.rs b/src-tauri/crates/core/src/plugin/host_impl.rs index 8fc867bd..06812dd8 100644 --- a/src-tauri/crates/core/src/plugin/host_impl.rs +++ b/src-tauri/crates/core/src/plugin/host_impl.rs @@ -332,6 +332,42 @@ fn sum_state_dir_bytes( // ----- waveflow:host/http ------------------------------------------------- +/// Render an error *with its cause chain* for a guest to surface. +/// +/// `reqwest::Error`'s `Display` stops at the outermost layer — a +/// failed request reads only "error sending request for url (…)", +/// which says the transport failed but never says why. The reason +/// (DNS lookup, connection refused, timeout, TLS handshake) lives in +/// `source()`, and the frontend shows this string verbatim, so +/// without unwinding it a bug report arrives with no way to tell +/// those cases apart. That is exactly what happened in issue #452. +/// +/// The chain is depth-capped: `source()` is author-controlled and +/// nothing forbids a cyclic or absurdly deep implementation. +fn describe_error(err: &dyn std::error::Error) -> String { + const MAX_DEPTH: usize = 8; + let mut out = err.to_string(); + let mut source = err.source(); + let mut depth = 0; + while let Some(cause) = source { + if depth >= MAX_DEPTH { + out.push_str(": …"); + break; + } + // Skip a layer that just restates its parent — `reqwest` and + // `hyper` both do this, and "timed out: timed out" reads + // worse than the single line it repeats. + let text = cause.to_string(); + if !out.ends_with(&text) { + out.push_str(": "); + out.push_str(&text); + } + source = cause.source(); + depth += 1; + } + out +} + impl wit_host::http::Host for HostCtx { fn send( &mut self, @@ -385,7 +421,7 @@ impl wit_host::http::Host for HostCtx { } let resp = match builder.send() { Ok(r) => r, - Err(e) => return Ok(Err(e.to_string())), + Err(e) => return Ok(Err(describe_error(&e))), }; let status = resp.status().as_u16(); let headers = resp @@ -408,7 +444,7 @@ impl wit_host::http::Host for HostCtx { let mut body = Vec::new(); let read = match resp.take(MAX_BODY_BYTES + 1).read_to_end(&mut body) { Ok(n) => n as u64, - Err(e) => return Ok(Err(e.to_string())), + Err(e) => return Ok(Err(describe_error(&e))), }; if read > MAX_BODY_BYTES { return Ok(Err("response body too large".to_string())); @@ -787,4 +823,93 @@ mod tests { Err(err) => panic!("expected InvalidStateKey, got {err:?}"), } } + + // ----- describe_error ------------------------------------------------- + + /// Minimal error whose `source()` we control, so the cause-chain + /// walk can be tested without conjuring a real `reqwest::Error`. + #[derive(Debug)] + struct Chained { + msg: &'static str, + source: Option>, + } + + impl std::fmt::Display for Chained { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.msg) + } + } + + impl std::error::Error for Chained { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.source + .as_deref() + .map(|s| s as &(dyn std::error::Error + 'static)) + } + } + + fn chain(msgs: &[&'static str]) -> Chained { + let mut it = msgs.iter().rev(); + let last = Chained { + msg: it.next().expect("at least one"), + source: None, + }; + it.fold(last, |acc, msg| Chained { + msg, + source: Some(Box::new(acc)), + }) + } + + /// The whole point: the root cause has to survive into the string + /// the user pastes into a bug report (issue #452). + #[test] + fn describe_error_keeps_the_root_cause() { + let err = chain(&[ + "error sending request for url (https://de1.api.radio-browser.info/json/…)", + "client error (Connect)", + "dns error", + "failed to lookup address information", + ]); + let out = super::describe_error(&err); + assert!( + out.contains("failed to lookup address information"), + "root cause lost: {out}" + ); + assert!(out.starts_with("error sending request for url")); + } + + #[test] + fn describe_error_handles_a_lone_error() { + let err = chain(&["timed out"]); + assert_eq!(super::describe_error(&err), "timed out"); + } + + /// `reqwest`/`hyper` often wrap an error in one that Displays + /// identically; echoing it would read "timed out: timed out". + #[test] + fn describe_error_skips_a_layer_that_restates_its_parent() { + let err = chain(&["timed out", "timed out"]); + assert_eq!(super::describe_error(&err), "timed out"); + } + + /// `source()` is author-controlled — a pathological chain must not + /// build an unbounded string. + #[test] + fn describe_error_caps_a_very_deep_chain() { + let deep = vec!["layer"; 64]; + // Distinct messages, otherwise the dedup above hides the cap. + let mut err = Chained { + msg: "root", + source: None, + }; + for _ in 0..deep.len() { + err = Chained { + msg: "wrap", + source: Some(Box::new(err)), + }; + } + let out = super::describe_error(&err); + assert!(out.ends_with('\u{2026}'), "chain not capped: {out}"); + assert!(out.len() < 200, "chain not capped: {out}"); + } } From cb8a31c4da733bb380fc59bfa4979ea376cf8926 Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Wed, 29 Jul 2026 07:31:13 +0200 Subject: [PATCH 3/5] fix(web-radio): let a 429 fall through to the next host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review catch on #455. `resp.status < 500` fails fast on 429 Too Many Requests, but a rate limit is per-node state rather than a verdict on the request — routing around a node that won't serve us right now is exactly what the second host is for. The same condition also swallowed 3xx (the host client disables redirects, so those arrive verbatim), which are likewise not a rejection of the request. Narrow the fail-fast to genuine 4xx minus 429; everything else moves on to the next host. Refs #452 --- .../core/tests/fixtures/web-radio/plugin.wasm | Bin 130644 -> 130655 bytes src-tauri/plugins/web-radio/src/lib.rs | 11 +++++++++-- .../resources/plugins/web-radio/plugin.wasm | Bin 130644 -> 130655 bytes 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src-tauri/crates/core/tests/fixtures/web-radio/plugin.wasm b/src-tauri/crates/core/tests/fixtures/web-radio/plugin.wasm index c4c81a75b911c31667fb1b4ed1c8b8c6990991ec..a1ef16d12d10b68cdaee328e2bb83fc5fa054457 100644 GIT binary patch delta 878 zcma)4O-NKx6n^)<8K2Xemov_N{?tV0J}N|-3ZY>z!Y(n+j8jyaR9Kdcq7Ev}gnu;Z zKo}Sy(Tjqjg$pD5LwJO%piNMeL8UEPWQ(#N42oKs(|HOlTDAG^J?DPsJAZd(QJGs* zCWl?*w{^}HaL|#T)&tE~L6)qiRh=$B(17%dYLbw+477}9Wy33GZ_k#;wbkaq*oH$4 zUlcb{g#!spI6T*~*{ozZ!&(72IRh_ORG>l%(n$&wSdiYePbElip~ana7kI?ap)@OW>L-MP_;(DCuRnchDYRba!Op!$#KnrvAz}io?NX-XnfBkR~#Rt znC4NfFL6JiZfaRIg~W30z2GFed8Kv-;;Y&?VWy~K1$Oa->w@wUG01J-L!w)`x&~6j z#9+0dtmSLK59Q#mj|*SNka}&wp@(WcKowUn0X+v4{kMEb z9s!j{!OdgX-N~+x6yKw%Z-~M4DB|PvCM;?p9iUzYp0Gy20q#nP;K9uD?)~_0s!}35 zl;#XmEA|HCn0%RJbD8R%s+o-H-?mFLWqgXPQ>m!v;Uxe7 delta 843 zcmZ`%-%C?b9KYw>>2=;++}t^CnbJ99hz~7{a;$~eDdwEBUo)Z5GEp(tls54f&TS9{ zL0E9=rI6?$D3UO);7k1hL4uG-NzqeLL=UaP7s&gb(vpYy%v{ie3M zsV!WwqoR4$9`}ww5(hGIdnNM2QOqq?XOcdr*xFLl;_j9j2a0>U`k}@QL-rm!EbHRy zHcM!dqt{Cik@ph^0pBI!ek$@@hfxWm9EL~axZAn{;zVHXg_6qlj^~J&<)^w(6&1M_ zpSF`%vl>>*;G;(58I{wW1z>D;zEWXYIdczz{iUu!z}H>9fZ^^nLL!ETl|oqhB?~vNkvq4_ZzA34i3=HDplM%5@gw4%Wtre0pID=sg#`_6mmU@iWy& zWcyGbqUOTTc?|Zhl&1`Gwtx?j4XM-r=x?VwLH{Kc1;xJM0NC>=(|Id%@EF`_c;o|y z{f_ikK#`dRT*=fyl#NUPyC`!;;2+tTBdj2psB9cN0lAygqL9zPWp0d7i12xQ7odHj zLH-<@+!k5vzoOiD{Qsg>kR~RsLAqN7^Vv z_%?k6B#qe+;7GO!a6Y@cnmA{z2MkYh2XrFL4s6#t2#VQs6}(}Eg~UrG$wLlRXBuy} Twx(vnh+JNrSz4Syj|;y6CzRO8 diff --git a/src-tauri/plugins/web-radio/src/lib.rs b/src-tauri/plugins/web-radio/src/lib.rs index f1d60642..6784ce2a 100644 --- a/src-tauri/plugins/web-radio/src/lib.rs +++ b/src-tauri/plugins/web-radio/src/lib.rs @@ -218,8 +218,15 @@ fn fetch_json(path: &str) -> Result, String> { }; match http::send(&req) { Ok(resp) if (200..300).contains(&resp.status) => return Ok(resp.body), - Ok(resp) if resp.status < 500 => { - // Client-side rejection: the next node says the same. + // Client-side rejection: the request itself is wrong (bad + // tag, malformed query), so the next node answers the same + // and asking it again just wastes a round-trip. + // + // 429 is the exception: a rate limit is per-node state, not + // a verdict on the request, and the whole point of having a + // second host is to route around a node that won't serve us + // right now. + Ok(resp) if (400..500).contains(&resp.status) && resp.status != 429 => { return Err(format!("http status {}", resp.status)); } Ok(resp) => { diff --git a/src-tauri/resources/plugins/web-radio/plugin.wasm b/src-tauri/resources/plugins/web-radio/plugin.wasm index c4c81a75b911c31667fb1b4ed1c8b8c6990991ec..a1ef16d12d10b68cdaee328e2bb83fc5fa054457 100644 GIT binary patch delta 878 zcma)4O-NKx6n^)<8K2Xemov_N{?tV0J}N|-3ZY>z!Y(n+j8jyaR9Kdcq7Ev}gnu;Z zKo}Sy(Tjqjg$pD5LwJO%piNMeL8UEPWQ(#N42oKs(|HOlTDAG^J?DPsJAZd(QJGs* zCWl?*w{^}HaL|#T)&tE~L6)qiRh=$B(17%dYLbw+477}9Wy33GZ_k#;wbkaq*oH$4 zUlcb{g#!spI6T*~*{ozZ!&(72IRh_ORG>l%(n$&wSdiYePbElip~ana7kI?ap)@OW>L-MP_;(DCuRnchDYRba!Op!$#KnrvAz}io?NX-XnfBkR~#Rt znC4NfFL6JiZfaRIg~W30z2GFed8Kv-;;Y&?VWy~K1$Oa->w@wUG01J-L!w)`x&~6j z#9+0dtmSLK59Q#mj|*SNka}&wp@(WcKowUn0X+v4{kMEb z9s!j{!OdgX-N~+x6yKw%Z-~M4DB|PvCM;?p9iUzYp0Gy20q#nP;K9uD?)~_0s!}35 zl;#XmEA|HCn0%RJbD8R%s+o-H-?mFLWqgXPQ>m!v;Uxe7 delta 843 zcmZ`%-%C?b9KYw>>2=;++}t^CnbJ99hz~7{a;$~eDdwEBUo)Z5GEp(tls54f&TS9{ zL0E9=rI6?$D3UO);7k1hL4uG-NzqeLL=UaP7s&gb(vpYy%v{ie3M zsV!WwqoR4$9`}ww5(hGIdnNM2QOqq?XOcdr*xFLl;_j9j2a0>U`k}@QL-rm!EbHRy zHcM!dqt{Cik@ph^0pBI!ek$@@hfxWm9EL~axZAn{;zVHXg_6qlj^~J&<)^w(6&1M_ zpSF`%vl>>*;G;(58I{wW1z>D;zEWXYIdczz{iUu!z}H>9fZ^^nLL!ETl|oqhB?~vNkvq4_ZzA34i3=HDplM%5@gw4%Wtre0pID=sg#`_6mmU@iWy& zWcyGbqUOTTc?|Zhl&1`Gwtx?j4XM-r=x?VwLH{Kc1;xJM0NC>=(|Id%@EF`_c;o|y z{f_ikK#`dRT*=fyl#NUPyC`!;;2+tTBdj2psB9cN0lAygqL9zPWp0d7i12xQ7odHj zLH-<@+!k5vzoOiD{Qsg>kR~RsLAqN7^Vv z_%?k6B#qe+;7GO!a6Y@cnmA{z2MkYh2XrFL4s6#t2#VQs6}(}Eg~UrG$wLlRXBuy} Twx(vnh+JNrSz4Syj|;y6CzRO8 From 9c2a55ff0f8cbdd5a30d212f25509489cb8c211d Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Wed, 29 Jul 2026 07:44:27 +0200 Subject: [PATCH 4/5] refactor(web-radio): one constant for the shared list query params `order=votes&reverse=true&hidebroken=true` was spelled out identically in the tag, country and search branches. The three category kinds have to rank the same way, so a future tweak would have had to be remembered in three places. `topvote` / `lastchange` deliberately stay out of it: they carry their ordering in the endpoint name and take the limit as a path segment. Outside-diff review note on #455. Refs #452 --- .../core/tests/fixtures/web-radio/plugin.wasm | Bin 130655 -> 130663 bytes src-tauri/plugins/web-radio/src/lib.rs | 18 +++++++++++++++--- .../resources/plugins/web-radio/plugin.wasm | Bin 130655 -> 130663 bytes 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src-tauri/crates/core/tests/fixtures/web-radio/plugin.wasm b/src-tauri/crates/core/tests/fixtures/web-radio/plugin.wasm index a1ef16d12d10b68cdaee328e2bb83fc5fa054457..6b37df1cc9f7af12963e8e1c273b776261a66cb7 100644 GIT binary patch delta 9606 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 Occ7jB;trVc$o~QWY(+W% delta 9352 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 AwEzGB diff --git a/src-tauri/plugins/web-radio/src/lib.rs b/src-tauri/plugins/web-radio/src/lib.rs index 6784ce2a..ffa9d812 100644 --- a/src-tauri/plugins/web-radio/src/lib.rs +++ b/src-tauri/plugins/web-radio/src/lib.rs @@ -55,6 +55,18 @@ const USER_AGENT: &str = concat!("WaveFlow/Web-Radio/", env!("CARGO_PKG_VERSION" /// to drop stations the federated bot has flagged as unreachable. const PAGE_LIMIT: u32 = 50; +/// Query params shared by every "list stations" endpoint (`bytag`, +/// `bycountrycodeexact`, `search`): most-voted first, and let the +/// federated bot's reachability flag filter out dead streams. +/// +/// One constant rather than three copies — they must stay identical +/// for the three category kinds to rank consistently, and a future +/// tweak (a different `order`, say) would otherwise have to be +/// remembered in three places. `topvote` / `lastchange` are NOT in +/// this set: they carry their ordering in the endpoint name and take +/// the limit as a path segment. +const LIST_PARAMS: &str = "order=votes&reverse=true&hidebroken=true"; + struct WebRadio; impl Guest for WebRadio { @@ -151,7 +163,7 @@ fn build_path(query: &str) -> Result { return Err("empty tag".into()); } return Ok(format!( - "/json/stations/bytag/{tag}?limit={PAGE_LIMIT}&order=votes&reverse=true&hidebroken=true" + "/json/stations/bytag/{tag}?limit={PAGE_LIMIT}&{LIST_PARAMS}" )); } // `country:` → stations in one country, by ISO 3166-1 @@ -168,7 +180,7 @@ fn build_path(query: &str) -> Result { } let code = code.to_ascii_uppercase(); return Ok(format!( - "/json/stations/bycountrycodeexact/{code}?limit={PAGE_LIMIT}&order=votes&reverse=true&hidebroken=true" + "/json/stations/bycountrycodeexact/{code}?limit={PAGE_LIMIT}&{LIST_PARAMS}" )); } // Both the explicit `search:` form and the free-text fallback @@ -181,7 +193,7 @@ fn build_path(query: &str) -> Result { return Err("empty search term".into()); } Ok(format!( - "/json/stations/search?name={term}&limit={PAGE_LIMIT}&order=votes&reverse=true&hidebroken=true" + "/json/stations/search?name={term}&limit={PAGE_LIMIT}&{LIST_PARAMS}" )) } diff --git a/src-tauri/resources/plugins/web-radio/plugin.wasm b/src-tauri/resources/plugins/web-radio/plugin.wasm index a1ef16d12d10b68cdaee328e2bb83fc5fa054457..6b37df1cc9f7af12963e8e1c273b776261a66cb7 100644 GIT binary patch delta 9606 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 Occ7jB;trVc$o~QWY(+W% delta 9352 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 AwEzGB From 5a7bff7be30e2e1014d5a9ce27378ffa9850d5ad Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Wed, 29 Jul 2026 08:05:36 +0200 Subject: [PATCH 5/5] fix(plugin-host): dedup a cause against its parent, not the whole buffer Review catch on #455. `out.ends_with(&text)` asked "does this cause end the string built so far", not "does it repeat the layer above it". A distinct cause that happens to be a suffix of the accumulated text was therefore dropped: "failed to connect" followed by its own "connect" layer collapsed to just the first, losing information the whole function exists to preserve. Track the last appended message separately and require an exact match. The new test fails against the previous logic, which is the point. Refs #452 --- src-tauri/crates/core/src/plugin/host_impl.rs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src-tauri/crates/core/src/plugin/host_impl.rs b/src-tauri/crates/core/src/plugin/host_impl.rs index 06812dd8..248bfbff 100644 --- a/src-tauri/crates/core/src/plugin/host_impl.rs +++ b/src-tauri/crates/core/src/plugin/host_impl.rs @@ -347,6 +347,12 @@ fn sum_state_dir_bytes( fn describe_error(err: &dyn std::error::Error) -> String { const MAX_DEPTH: usize = 8; let mut out = err.to_string(); + // The last message actually appended, tracked separately from + // `out`: testing against the accumulated string would compare a + // cause to the tail of *everything* written so far, so a distinct + // cause that happens to end the buffer ("failed to connect" then + // "connect") would be dropped as a repeat. + let mut last = out.clone(); let mut source = err.source(); let mut depth = 0; while let Some(cause) = source { @@ -356,11 +362,13 @@ fn describe_error(err: &dyn std::error::Error) -> String { } // Skip a layer that just restates its parent — `reqwest` and // `hyper` both do this, and "timed out: timed out" reads - // worse than the single line it repeats. + // worse than the single line it repeats. Only an exact repeat + // of the previous layer counts. let text = cause.to_string(); - if !out.ends_with(&text) { + if text != last { out.push_str(": "); out.push_str(&text); + last = text; } source = cause.source(); depth += 1; @@ -892,6 +900,17 @@ mod tests { assert_eq!(super::describe_error(&err), "timed out"); } + /// The dedup must fire on an exact repeat of the previous layer + /// and nothing else. Comparing against the accumulated buffer + /// instead would drop a distinct cause that merely happens to end + /// it — here "connect" is a suffix of "failed to connect", but it + /// is its own layer and has to survive. + #[test] + fn describe_error_only_dedups_an_exact_repeat_of_the_previous_layer() { + let err = chain(&["failed to connect", "connect"]); + assert_eq!(super::describe_error(&err), "failed to connect: connect"); + } + /// `source()` is author-controlled — a pathological chain must not /// build an unbounded string. #[test]