diff --git a/app/src/main/java/com/theveloper/pixelplay/data/navidrome/model/NavidromeCredentials.kt b/app/src/main/java/com/theveloper/pixelplay/data/navidrome/model/NavidromeCredentials.kt index 3a9fd667e..ef5123cc5 100644 --- a/app/src/main/java/com/theveloper/pixelplay/data/navidrome/model/NavidromeCredentials.kt +++ b/app/src/main/java/com/theveloper/pixelplay/data/navidrome/model/NavidromeCredentials.kt @@ -2,6 +2,7 @@ package com.theveloper.pixelplay.data.navidrome.model import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import com.theveloper.pixelplay.data.stream.CloudStreamSecurity /** * Represents authentication credentials for a Navidrome/Subsonic server. @@ -48,7 +49,18 @@ data class NavidromeCredentials( * Returns the parsed and normalized server URL, or null if it is invalid. */ val normalizedHttpUrlOrNull: HttpUrl? - get() = serverUrl.trim().trimEnd('/').toHttpUrlOrNull() + get() { + val trimmed = serverUrl.trim().trimEnd('/') + // Auto-prepend https:// if no scheme is provided + val withScheme = if (!trimmed.startsWith("http://", ignoreCase = true) && + !trimmed.startsWith("https://", ignoreCase = true) + ) { + "https://$trimmed" + } else { + trimmed + } + return withScheme.toHttpUrlOrNull() + } /** * Returns the normalized server URL (without trailing slash). @@ -65,7 +77,14 @@ data class NavidromeCredentials( return "Server URL must not include embedded credentials." } if (requireHttps && !httpUrl.isHttps) { - return "Use an https:// server URL for Navidrome/Subsonic." + val host = httpUrl.host + val isPrivate = host == "localhost" || + host == "127.0.0.1" || + host.endsWith(".local") || + CloudStreamSecurity.isPrivateIpv4Literal(host) + if (!isPrivate) { + return "Use an https:// server URL for remote Navidrome/Subsonic servers. HTTP is only allowed for local network addresses." + } } return null } diff --git a/app/src/test/java/com/theveloper/pixelplay/data/navidrome/model/NavidromeCredentialsTest.kt b/app/src/test/java/com/theveloper/pixelplay/data/navidrome/model/NavidromeCredentialsTest.kt index 1459983b1..b90560ac3 100644 --- a/app/src/test/java/com/theveloper/pixelplay/data/navidrome/model/NavidromeCredentialsTest.kt +++ b/app/src/test/java/com/theveloper/pixelplay/data/navidrome/model/NavidromeCredentialsTest.kt @@ -19,15 +19,26 @@ class NavidromeCredentialsTest { } @Test - fun `connectionValidationError rejects insecure http urls`() { + fun `connectionValidationError accepts local http urls`() { val credentials = NavidromeCredentials( serverUrl = "http://192.168.1.20:4533", username = "user", password = "pass" ) + assertNull(credentials.connectionValidationError()) + } + + @Test + fun `connectionValidationError rejects remote http urls`() { + val credentials = NavidromeCredentials( + serverUrl = "http://music.example.com", + username = "user", + password = "pass" + ) + assertEquals( - "Use an https:// server URL for Navidrome/Subsonic.", + "Use an https:// server URL for remote Navidrome/Subsonic servers. HTTP is only allowed for local network addresses.", credentials.connectionValidationError() ) }