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 }