From 2ca8eb31dc4943aea7ec94b0402f5441f402edc3 Mon Sep 17 00:00:00 2001 From: nrobi144 Date: Tue, 2 Jun 2026 10:30:36 +0300 Subject: [PATCH] fix: address root causes of 6 runtime log noise issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. LocalRelayStore: use batchInsert() with per-row savepoints instead of manual transaction — UNIQUE constraint violations skip that row instead of failing the whole batch 2. Robohash empty hex: guard blank input in CachedRobohash.get() with a fallback all-zeros hex key instead of passing empty string to assembler 3. GiftWrapEvent decrypt: downgrade from WARN to DEBUG — expected when gift wraps from local relay cache aren't addressed to current user (subscription filter is correct, but hydration doesn't filter by p-tag) 4. Relay URL %20: decode percent-encoded spaces before rejection check in RelayUrlNormalizer.fix() — wss://relay.example.com/%20 now normalizes to wss://relay.example.com/ instead of being rejected 5. NIP19 Parser: downgrade from ERROR/WARN to DEBUG — malformed bech32 from relay content is expected in the wild, catch+log is correct 6. VLC macOS: add --avcodec-hw=none (disables VideoToolbox that causes CVPN chroma failures) and --reset-plugins-cache (rebuilds stale cache on startup instead of logging hundreds of stale-cache errors) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../amethyst/commons/robohash/CachedRobohash.kt | 9 ++++++--- .../amethyst/desktop/relay/LocalRelayStore.kt | 10 +++------- .../desktop/service/media/VlcjPlayerPool.kt | 7 ++++++- .../relay/normalizer/RelayUrlNormalizer.kt | 15 +++++++++++++-- .../quartz/nip19Bech32/Nip19Parser.kt | 6 +++--- .../quartz/nip59Giftwrap/wraps/GiftWrapEvent.kt | 2 +- 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/robohash/CachedRobohash.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/robohash/CachedRobohash.kt index bf30d63ffa..1adca4fd6a 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/robohash/CachedRobohash.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/robohash/CachedRobohash.kt @@ -44,6 +44,9 @@ object CachedRobohash { msg: String, isLightTheme: Boolean, ): ImageVector { + // Guard: empty/blank keys produce meaningless robohash; use a fallback hex + val key = msg.ifBlank { "0000000000000000000000000000000000000000000000000000000000000000" } + // resets cache on theme change if (cacheIsForLightTheme == null) { cacheIsForLightTheme = isLightTheme @@ -52,13 +55,13 @@ object CachedRobohash { cache.evictAll() } - cache[msg]?.let { + cache[key]?.let { return it } - val vector = assembler.build(msg, isLightTheme) + val vector = assembler.build(key, isLightTheme) - cache.put(msg, vector) + cache.put(key, vector) return vector } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/relay/LocalRelayStore.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/relay/LocalRelayStore.kt index cf3eb5029f..64189fede1 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/relay/LocalRelayStore.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/relay/LocalRelayStore.kt @@ -106,13 +106,9 @@ class LocalRelayStore( if (!_enabled.value || _writesDisabled.value) return val s = store ?: return writeBundler.invalidateList(event) { batch -> - try { - s.transaction { - batch.forEach { insert(it) } - } - } catch (e: Exception) { - Log.w("LocalRelayStore") { "Batch insert failed: ${e.message}" } - } + // batchInsert uses per-row savepoints — UNIQUE constraint + // violations skip that row instead of failing the whole batch + s.batchInsert(batch.toList()) } } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/service/media/VlcjPlayerPool.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/service/media/VlcjPlayerPool.kt index 50ea042e58..4c73e918cd 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/service/media/VlcjPlayerPool.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/service/media/VlcjPlayerPool.kt @@ -115,7 +115,12 @@ object VlcjPlayerPool { } // Build factory args — add --plugin-path fallback if env var wasn't set - val factoryArgs = mutableListOf("--no-xlib") + val factoryArgs = + mutableListOf( + "--no-xlib", + "--avcodec-hw=none", // Disable VideoToolbox — avoids CVPN chroma failures on macOS + "--reset-plugins-cache", // Rebuild stale plugins cache on startup + ) if (!macOsDiscoverer.envVarSet) { val pluginPath = macOsDiscoverer.discoveredPluginPath diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/normalizer/RelayUrlNormalizer.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/normalizer/RelayUrlNormalizer.kt index ffd1da4d63..81b08a9666 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/normalizer/RelayUrlNormalizer.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/normalizer/RelayUrlNormalizer.kt @@ -85,9 +85,20 @@ class RelayUrlNormalizer { private fun norm(url: String) = NormalizedRelayUrl(Rfc3986.normalize(url)) @OptIn(ExperimentalContracts::class) - fun fix(url: String): String? { + fun fix(rawUrl: String): String? { + if (rawUrl.length < 4) return null + if (rawUrl.contains("%00")) return null + + // Trim trailing %20 (percent-encoded spaces from malformed event data) + val url = + rawUrl.trimEnd('%', '2', '0').let { trimmed -> + // Only accept if we actually removed a trailing %20 pattern + if (trimmed.length < rawUrl.length && rawUrl.endsWith("%20")) trimmed else rawUrl + } if (url.length < 4) return null - if (url.contains("%00") || url.contains("%20")) return null + + // Reject URLs with %20 in the middle — these are garbage + if (url.contains("%20")) return null if (url.length > 50) { // removes multiple urls in the same line diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip19Bech32/Nip19Parser.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip19Bech32/Nip19Parser.kt index 5fd03a9054..866ee0869e 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip19Bech32/Nip19Parser.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip19Bech32/Nip19Parser.kt @@ -78,7 +78,7 @@ object Nip19Parser { return type!! + key } catch (e: Throwable) { - Log.e("NIP19 Parser") { "Issue trying to Decode NIP19 $uri: ${e.message}" } + Log.d("NIP19 Parser") { "Issue trying to Decode NIP19 $uri: ${e.message}" } } return null @@ -98,7 +98,7 @@ object Nip19Parser { return parseComponents(type, key, additionalChars?.ifEmpty { null }) } catch (e: Throwable) { - Log.e("NIP19 Parser") { "Issue trying to Decode NIP19 $uri: ${e.message}" } + Log.d("NIP19 Parser") { "Issue trying to Decode NIP19 $uri: ${e.message}" } } return null @@ -127,7 +127,7 @@ object Nip19Parser { ParseReturn(it, nip19, additionalChars) } } catch (e: Throwable) { - Log.w("NIP19 Parser") { "Issue trying to Decode NIP19 $key: ${e.message}" } + Log.d("NIP19 Parser") { "Issue trying to Decode NIP19 $key: ${e.message}" } null } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/wraps/GiftWrapEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/wraps/GiftWrapEvent.kt index 3b3fbfa371..af7298adf8 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/wraps/GiftWrapEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/wraps/GiftWrapEvent.kt @@ -85,7 +85,7 @@ open class GiftWrapEvent( try { unwrapThrowing(signer) } catch (_: Exception) { - Log.w("GiftWrapEvent", "Couldn't Decrypt the content " + this.toNostrUri()) + Log.d("GiftWrapEvent") { "Couldn't Decrypt the content " + this.toNostrUri() } null }