Easier initial setup for server URLs (#300)

Use the server scoring API when adding a new server. This allows just
entering a hostname or IP address without the scheme (`http`/`https`) or
port in most cases.

Note: non-standard ports would still need to be provided

Closes #5 
Closes #296
This commit is contained in:
damontecres 2025-11-22 12:48:39 -05:00 committed by GitHub
parent df8ec83841
commit df78b42bac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 72 additions and 36 deletions

View file

@ -12,6 +12,7 @@ import com.github.damontecres.wholphin.data.model.JellyfinServer
import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.setValueOnMain
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.LoadingState
import dagger.hilt.android.lifecycle.HiltViewModel
@ -23,6 +24,8 @@ import org.jellyfin.sdk.Jellyfin
import org.jellyfin.sdk.api.client.HttpClientOptions
import org.jellyfin.sdk.api.client.extensions.quickConnectApi
import org.jellyfin.sdk.api.client.extensions.systemApi
import org.jellyfin.sdk.discovery.RecommendedServerInfoScore
import org.jellyfin.sdk.discovery.RecommendedServerIssue
import org.jellyfin.sdk.model.serializer.toUUID
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
import timber.log.Timber
@ -130,15 +133,20 @@ class SwitchServerViewModel
status
}
fun addServer(serverUrl: String) {
fun addServer(inputUrl: String) {
addServerState.value = LoadingState.Loading
viewModelScope.launchIO {
try {
val serverInfo by jellyfin
.createApi(serverUrl)
.systemApi
.getPublicSystemInfo()
val scores =
jellyfin.discovery.getRecommendedServers(inputUrl).sortedBy { it.score }
val bestServer =
scores.firstOrNull { it.score != RecommendedServerInfoScore.BAD }
val serverInfo = bestServer?.systemInfo?.getOrNull()
if (bestServer != null && serverInfo != null) {
val serverUrl = bestServer.address
val id = serverInfo.id?.toUUIDOrNull()
if (id != null && serverInfo.startupWizardCompleted == true) {
val server =
JellyfinServer(
@ -170,8 +178,36 @@ class SwitchServerViewModel
LoadingState.Error("Server returned invalid response")
}
}
} else {
Timber.w("Error connecting with %s: %s", inputUrl, scores)
// No good server candidate
val errors =
scores.joinToString("\n") {
val issues =
it.issues.firstOrNull()?.let {
when (it) {
is RecommendedServerIssue.InvalidProductName,
is RecommendedServerIssue.MissingSystemInfo,
-> "Invalid server info"
is RecommendedServerIssue.SecureConnectionFailed,
is RecommendedServerIssue.ServerUnreachable,
is RecommendedServerIssue.SlowResponse,
-> "Unable to connect"
RecommendedServerIssue.MissingVersion,
is RecommendedServerIssue.OutdatedServerVersion,
is RecommendedServerIssue.UnsupportedServerVersion,
-> "Unsupported server version"
}
}
"${it.address} - $issues"
}
val message = "Error, tried addresses:\n$errors"
addServerState.setValueOnMain(LoadingState.Error(message))
}
} catch (ex: Exception) {
Timber.w(ex, "Error creating API for $serverUrl")
Timber.w(ex, "Error creating API for $inputUrl")
withContext(Dispatchers.Main) {
addServerState.value =
LoadingState.Error(exception = ex)

View file

@ -36,7 +36,7 @@
<string name="download_and_update"><![CDATA[Download & Update]]></string>
<string name="downloading">Downloading…</string>
<string name="enabled">Enabled</string>
<string name="enter_server_url">Enter Server IP or URL including port</string>
<string name="enter_server_url">Enter Server IP or URL</string>
<string name="episodes">Episodes</string>
<string name="error_loading_collection">Error loading collection %1$s</string>
<string name="external_track">External</string>