Skip to content

Commit

Permalink
Improve missing library error catching
Browse files Browse the repository at this point in the history
  • Loading branch information
toasterofbread committed Jul 29, 2024
1 parent d9bdd66 commit 12036eb
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.toasterofbread.spmp.platform.playerservice

import ProgramArguments
import com.toasterofbread.spmp.platform.AppContext
import kotlinx.coroutines.Job

Expand All @@ -10,5 +9,5 @@ actual object LocalServer {
actual fun startLocalServer(
context: AppContext,
port: Int,
): Job = throw IllegalAccessError()
): Result<Job> = throw IllegalAccessError()
}
Original file line number Diff line number Diff line change
Expand Up @@ -256,21 +256,21 @@ open class ExternalPlayerService(plays_audio: Boolean): SpMsPlayerService(plays_
return
}

try {
local_server_process =
LocalServer.startLocalServer(
player.context,
player.settings.platform.SERVER_PORT.get()
)

if (!automatic && local_server_process == null) {
local_server_error = RuntimeException(getString("loading_splash_local_server_command_not_set"))
LocalServer.startLocalServer(
player.context,
player.settings.platform.SERVER_PORT.get()
).fold(
onSuccess = {
local_server_process = it
if (!automatic && local_server_process == null) {
local_server_error = RuntimeException(getString("loading_splash_local_server_command_not_set"))
}
},
onFailure = { e ->
local_server_process = null
local_server_error = e
}
}
catch (e: Throwable) {
local_server_process = null
local_server_error = e
}
)
}

val server_unavailability_reason: String? = remember { LocalServer.getLocalServerUnavailabilityReason() }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.toasterofbread.spmp.platform.playerservice

import ProgramArguments
import com.toasterofbread.spmp.platform.AppContext
import kotlinx.coroutines.Job

Expand All @@ -10,5 +9,5 @@ expect object LocalServer {
fun startLocalServer(
context: AppContext,
port: Int
): Job
): Result<Job>
}
2 changes: 1 addition & 1 deletion shared/src/commonMain/resources/assets/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@
<string name="video_format_endpoint_youtubei">Youtubei</string>
<string name="video_format_endpoint_newpipe">NewPipe</string>

<string name="error_message_generic">A error occurred</string>
<string name="error_message_generic">An error occurred</string>
<string name="error_yt_feed_parse_failed">YouTube feed parsing failed</string>
<string name="error_player_service_not_connected">Player service isn't connecting&#xA;A bug may have occurred</string>
<string name="error_while_connecting_to_server_at_$x">An error occurred while connecting to server at $x</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,82 @@ import dev.toastbits.spms.server.SpMs
private const val POLL_INTERVAL: Long = 100
private const val CLIENT_REPLY_ATTEMPTS: Int = 10

private fun Exception.getMissingLibrariesInMessage(): List<String>? =
cause?.message?.let { getLibrariesInLibraryLoadErrorMessage(it) }
?: message?.let { getLibrariesInLibraryLoadErrorMessage(it) }

private fun getLibrariesInLibraryLoadErrorMessage(message: String): List<String>? {
val end_index: Int = message.indexOf("in java.library.path")
if (end_index != -1) {
val libraries: List<String> = listOf(message.substring(3, end_index))
check(libraries.isNotEmpty()) { "Message: '$message" }
return libraries
}

val split: List<String> = message.split(' ')
val libraries: List<String> = split.filter { it.endsWith(".so") || it.endsWith(".dll") }
if (libraries.isNotEmpty()) {
return libraries
}

return null
}

actual object LocalServer {
private fun createServer(): SpMs = SpMs(headless = false, enable_gui = false)
private data class MissingLibrariesException(val libraries: List<String>): RuntimeException(
getString("warning_server_unavailable") + libraries.joinToString(getString("server_missing_files_splitter"))
) {
init {
require(libraries.isNotEmpty())
}
}

actual fun getLocalServerUnavailabilityReason(): String? {
val server: SpMs =
try {
createServer()
private fun createServer(): Result<SpMs> =
try {
Result.success(SpMs(headless = false, enable_gui = false))
}
catch (e: Throwable) {
if (e !is NoClassDefFoundError && e !is UnsatisfiedLinkError) {
throw e
}
catch (e: NoClassDefFoundError) {
val split_message: List<String> = e.cause?.message?.split(" ") ?: emptyList()
val missing_files: List<String> = split_message.filter { it.endsWith(".so") || it.endsWith(".dll") }

return getString("warning_server_unavailable") + missing_files.joinToString(getString("server_missing_files_splitter"))
}
catch (e: UnsatisfiedLinkError) {
val message: String = e.message ?: "NO MESSAGE"
val end_index: Int = message.indexOf("in java.library.path")
val missing_files: List<String> = listOf(message.substring(3, end_index))
return getString("warning_server_unavailable") + missing_files.joinToString(getString("server_missing_files_splitter"))
}
e.printStackTrace()

server.release()
return null
}
val missing_libraries: List<String> =
e.cause?.message?.let { getLibrariesInLibraryLoadErrorMessage(it) }
?: e.message?.let { getLibrariesInLibraryLoadErrorMessage(it) }
?: emptyList()

check(missing_libraries.isNotEmpty()) { "Cause: '${e.cause?.message}'\nMessage: ${e.message}" }

Result.failure(MissingLibrariesException(missing_libraries))
}

actual fun getLocalServerUnavailabilityReason(): String? =
createServer().fold(
onSuccess = {
it.release()
return@fold null
},
onFailure = {
if (it is MissingLibrariesException) {
return@fold it.message
}
else {
return@fold it.message ?: (getString("error_message_generic") + " (${it::class})")
}
}
)

actual fun startLocalServer(
context: AppContext,
port: Int
): Job {
val server: SpMs = SpMs(headless = false, enable_gui = false)
): Result<Job> = runCatching {
val server: SpMs = createServer().getOrThrow()

server.bind(port)

return context.coroutine_scope.launch(Dispatchers.IO) {
return@runCatching context.coroutine_scope.launch(Dispatchers.IO) {
try {
while (true) {
server.poll(CLIENT_REPLY_ATTEMPTS)
Expand Down

0 comments on commit 12036eb

Please sign in to comment.