Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crashing with my lms server #2

Open
ohuc opened this issue Aug 23, 2024 · 36 comments
Open

Crashing with my lms server #2

ohuc opened this issue Aug 23, 2024 · 36 comments

Comments

@ohuc
Copy link

ohuc commented Aug 23, 2024

Crashing constanly

@maniac103
Copy link
Owner

What version of the app - built from git or F-Droid? Did you try restarting the server? What server version?

@maniac103
Copy link
Owner

And most importantly: can you provide output of adb logcat?

@ohuc
Copy link
Author

ohuc commented Aug 24, 2024

Running the F-Droid Build
Not sure about the server version this is how it's running
https://music-assistant.io/player-support/slimproto/
Also, I'm trying to connect to a music assistant client which connects perfectly when I use the squeezer app.

Here are the logs
2024-08-24 11:09:39.133 27815 27815 de.maniac103.squeezeclient E AndroidRuntime : FATAL EXCEPTION: main Process: de.maniac103.squeezeclient, PID: 27815 T3.b: Field 'mediadirs' is required for type with serial name 'de.maniac103.squeezeclient.cometd.response.ServerStatusResponse', but it was missing at X3.P.f(Unknown Source:114) at O2.V.<init>(Unknown Source:26) at O2.T.a(Unknown Source:78) at Z3.n.i(Unknown Source:10) at Y3.c.a(Unknown Source:56) at M2.a0.b(Unknown Source:104) at M2.C.p(Unknown Source:32) at M2.C.k(Unknown Source:12) at I3.I.a(Unknown Source:76) at I3.x0.a(Unknown Source:799) at J3.G.j(Unknown Source:4) at J3.F.t(Unknown Source:138) at J3.F.a(Unknown Source:0) at Q2.c.p(Unknown Source:107) at Q2.c.j(Unknown Source:48) at I3.K.a(Unknown Source:60) at I3.o0.n(Unknown Source:201) at I3.n0.p(Unknown Source:12) at m3.a.n(Unknown Source:8) at F3.L.run(Unknown Source:114) at android.os.Handler.handleCallback(Handler.java:959) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loopOnce(Looper.java:232) at android.os.Looper.loop(Looper.java:317) at android.app.ActivityThread.main(ActivityThread.java:8700) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886) Suppressed: L3.g: [y0{Cancelling}@d35c1d2, Dispatchers.Main]

@ohuc
Copy link
Author

ohuc commented Aug 24, 2024

Also I'm trying to just get the controls of the Music assistant server in my mobile phone which the squeezer app could do but it has a lot of delay issues

@droyholmes
Copy link

droyholmes commented Aug 24, 2024

I've been looking for an android client for Music Assistant also. I don't have a LMS up currently, does this act as a client/endpoint for any slim protocol/squeezelite server, or is it perhaps a wrapper around a LMS server's web interface? I'm currently sharing files to MA from a NAS.

@ohuc
Copy link
Author

ohuc commented Aug 24, 2024

I've been looking for an android client for Music Assistant also. I don't have a LMS up currently, does this act as a client/endpoint for any slim protocol/squeezelite server, or is it perhaps a wrapper around a LMS server's web interface? I'm currently sharing files to MA from a NAS.

Well i just enabled the "Slimproto (Squeezebox players)"
as one of the player providers and used the music assistant companion app for windows to emulate it as a speaker, however now while trying to connect to the server with squeeze client it crashes

So I think the music assistant server acts as a LMS server itself

@maniac103
Copy link
Owner

maniac103 commented Aug 24, 2024

This app interacts as client to LMS' cometd server interface.
From the log it looks like MA's implementation just doesn't provide media directories, which are used only for reconstructing the folder structure during music download, so I guess I can make those optional.
I want to make one thing clear though: if MA differs more significantly from LMS than in such minor details, I don't have any intentions to support it, for two reasons:

  • I don't have any way (or rather: intentions) to test it because I don't use MA myself
  • The logic behind the data decoding is complicated enough as is already, I don't want to make it even more complicated for supporting a server that never was intended to be supported.

I hope for your understanding there ;-)

I'll provide a test APK for making mediadirs optional, we'll see where this goes.

@ohuc
Copy link
Author

ohuc commented Aug 24, 2024

I don't think it differs too much because it seems to be running all good in the squeezer app except for the fact that there is a bit of a delay

Alright waiting for the APK

@droyholmes
Copy link

I appreciate you taking the time to even make a test APK. I totally understand the complications as well as your limited time. Good luck on this project either way!

@maniac103
Copy link
Owner

Test APK (rename to .apk)

(File is just named .zip due to Github's file name restrictions)

This makes mediadirs optional, which in turn renders the download folder structure option 'match server' useless. If all goes well, I'll improve things there to make that option become unavailable in that case.

Please let me know if this works for you, or - in case it doesn't - provide a new log.

@ohuc
Copy link
Author

ohuc commented Aug 25, 2024

Still crashing

Here's the logs:
2024-08-25 17:29:12.563 11297 11297 de.maniac103.squeezeclient E AndroidRuntime : FATAL EXCEPTION: main Process: de.maniac103.squeezeclient, PID: 11297 x3.b: Field 'mediadirs' is required for type with serial name 'de.maniac103.squeezeclient.cometd.response.ServerStatusResponse', but it was missing at B3.Q.f(Unknown Source:114) at u2.V.<init>(Unknown Source:26) at u2.T.a(Unknown Source:78) at D3.o.i(Unknown Source:10) at C3.c.a(Unknown Source:56) at s2.Z.b(Unknown Source:104) at s2.B.s(Unknown Source:32) at s2.B.n(Unknown Source:12) at n3.I.a(Unknown Source:76) at F2.u.a(Unknown Source:489) at o3.G.l(Unknown Source:4) at o3.F.u(Unknown Source:138) at o3.F.a(Unknown Source:0) at w2.c.s(Unknown Source:107) at w2.c.l(Unknown Source:48) at n3.K.a(Unknown Source:60) at n3.l0.m(Unknown Source:201) at n3.k0.s(Unknown Source:12) at R2.a.q(Unknown Source:8) at k3.K.run(Unknown Source:114) at android.os.Handler.handleCallback(Handler.java:959) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loopOnce(Looper.java:232) at android.os.Looper.loop(Looper.java:317) at android.app.ActivityThread.main(ActivityThread.java:8700) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886) Suppressed: q3.g: [x0{Cancelling}@fe36fd8, Dispatchers.Main]

@maniac103
Copy link
Owner

Stupid me ... note to self: don't do stuff in a hurry, it leads to mistakes :-(

Next attempt

@ohuc
Copy link
Author

ohuc commented Aug 25, 2024

Thanks for the test apk :)
However it still seems to be crashing

I think it may be a lot different to the way lms works I guess because it seems to be crashing at every little minor detail

2024-08-25 18:13:33.501 30406 30406 de.maniac103.squeezeclient E AndroidRuntime : FATAL EXCEPTION: main Process: de.maniac103.squeezeclient, PID: 30406 D3.j: String literal for key 'offset' should be quoted. Use 'isLenient = true' in 'Json {}' builder to accept non-compliant JSON. JSON input: .....ype":"track","style":"itemplay","nextWindow":"nowPlaying"}]} at D3.o.d(Unknown Source:36) at D3.o.e(Unknown Source:34) at D3.a.M(Unknown Source:52) at D3.a.w(Unknown Source:4) at B3.e0.a(Unknown Source:5) at D3.o.i(Unknown Source:10) at D3.a.f(Unknown Source:38) at u2.M.a(Unknown Source:392) at D3.o.i(Unknown Source:10) at C3.c.a(Unknown Source:56) at s2.Z.i(Unknown Source:91) at s2.L.s(Unknown Source:12) at R2.a.q(Unknown Source:8) at q3.s.A(Unknown Source:6) at k3.a.q(Unknown Source:22) at R2.a.q(Unknown Source:31) at k3.C.B(Unknown Source:50) at k3.h.r(Unknown Source:80) at k3.h.v(Unknown Source:2) at m3.j.a(Unknown Source:6) at m3.h.H(Unknown Source:114) at m3.h.g(Unknown Source:56) at m3.h.r(Unknown Source:89) at s2.Y.s(Unknown Source:93) at R2.a.q(Unknown Source:8) at k3.K.run(Unknown Source:114) at android.os.Handler.handleCallback(Handler.java:959) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loopOnce(Looper.java:232) at android.os.Looper.loop(Looper.java:317) at android.app.ActivityThread.main(ActivityThread.java:8700) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886) Suppressed: q3.g: [s0{Cancelling}@e8b172e, Dispatchers.Main.immediate]

@maniac103
Copy link
Owner

One notable difference between Squeeze Client and Squeezer that was important to me is that I made the JSON consumer as rigid as possible to get a better understanding of what input data I can rely on and what data is optional. Squeezer is more lenient in that regard, at the expense of much more complicated code (because it needs special case handling for missing values everywhere).
However, in this particular case, it looks like the JSON is not actually valid?
Here's another APK, this time as debug version, which should dump the received data into logcat. The crash will be the same, but can you provide the data right before the crash from the log, please?

@ohuc
Copy link
Author

ohuc commented Aug 25, 2024

sorry provided the same log earlier
Is this what you were looking for?

2024-08-25 19:10:55.936 26472 26503 de.maniac103.squeezeclient.debug D CometdClient : Received event message (2255 bytes): Message(channelId=ChannelId(channel=/a73e39a062e711ef934a181deab7bf10/slim/request/3), clientId=null, id=14, successful=true, data={"player_name":"nk-HP-EliteBook-850-G5","player_connected":1,"player_needs_upgrade":false,"player_is_upgrading":false,"power":1,"signalstrength":0,"waitingToPlay":0,"mode":"stop","remote":1,"current_title":"Music Assistant","time":0,"duration":359,"sync_master":"","sync_slaves":"","mixer volume":4,"player_ip":"192.168.29.201","playlist_cur_index":0,"playlist_tracks":2,"can_seek":0,"digital_volume_control":1,"playlist_timestamp":1724590059,"playlist repeat":1,"playlist shuffle":0,"playlist mode":"off","rate":1,"seq_no":0,"sleep":0,"will_sleep_in":0,"uuid":null,"alarm_state":"none","alarm_snooze_seconds":540,"alarm_timeout_seconds":3600,"count":2,"offset":"-","base":{"actions":{"more":{"itemsParams":"params","window":{"isContextMenu":1},"cmd":["contextmenu"],"player":0,"params":{"context":"playlist","menu":"track"}}}},"preset_loop":[0,0,0,0,0,0,0,0,0,0],"preset_data":[{},{},{},{},{},{},{},{},{},{}],"item_loop":[{"track":"Sunset in Calabasas","album":"","trackType":"radio","icon":"https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/63/0b/60/630b60e3-6234-9df1-80e2-833eee57be18/859770370543_cover.jpg/1400x1400bb.jpg","artist":"adrian","text":"Sunset in Calabasas","params":{"track_id":"d9860b39bc0147b69d8803a21886239e","item_id":"d9860b39bc0147b69d8803a21886239e","uri":"http://192.168.29.201:8097/single/18:1d:ea:b7:bf:10/d9860b39bc0147b69d8803a21886239e.flac?ts=1724590054"},"type":"track","style":"itemplay","nextWindow":"nowPlaying"},{"track":"Sunset in Calabasas","album":"","trackType":"radio","icon":"https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/63/0b/60/630b60e3-6234-9df1-80e2-833eee57be18/859770370543_cover.jpg/1400x1400bb.jpg","artist":"adrian","text":"Sunset in Calabasas","params":{"track_id":"d9860b39bc0147b69d8803a21886239e","item_id":"d9860b39bc0147b69d8803a21886239e","uri":"http://192.168.29.201:8097/single/18:1d:ea:b7:bf:10/d9860b39bc0147b69d8803a21886239e.flac?ts=1724590059"},"type":"track","style":"itemplay","nextWindow":"nowPlaying"}]})

@ohuc
Copy link
Author

ohuc commented Aug 25, 2024

These are the logs right before the crash
Screenshot_20240825-191306

@maniac103
Copy link
Owner

Hmm, what's strange is that that JSON seems fine. Both the JSON and the stack trace before suggest it's a PlayerStatusResponse it's choking at, thus to reproduce what you're sseing, I tried this, what should match what the actual event decoding is doing:

        val jsonstring = """
            {"player_name":"nk-HP-EliteBook-850-G5","player_connected":1,"player_needs_upgrade":false,"player_is_upgrading":false,"power":1,"signalstrength":0,"waitingToPlay":0,"mode":"stop","remote":1,"current_title":"Music Assistant","time":0,"duration":359,"sync_master":"","sync_slaves":"","mixer volume":4,"player_ip":"192.168.29.201","playlist_cur_index":0,"playlist_tracks":2,"can_seek":0,"digital_volume_control":1,"playlist_timestamp":1724590059,"playlist repeat":1,"playlist shuffle":0,"playlist mode":"off","rate":1,"seq_no":0,"sleep":0,"will_sleep_in":0,"uuid":null,"alarm_state":"none","alarm_snooze_seconds":540,"alarm_timeout_seconds":3600,"count":2,"offset":"-","base":{"actions":{"more":{"itemsParams":"params","window":{"isContextMenu":1},"cmd":["contextmenu"],"player":0,"params":{"context":"playlist","menu":"track"}}}},"preset_loop":[0,0,0,0,0,0,0,0,0,0],"preset_data":[{},{},{},{},{},{},{},{},{},{}],"item_loop":[{"track":"Sunset in Calabasas","album":"","trackType":"radio","icon":"https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/63/0b/60/630b60e3-6234-9df1-80e2-833eee57be18/859770370543_cover.jpg/1400x1400bb.jpg","artist":"adrian","text":"Sunset in Calabasas","params":{"track_id":"d9860b39bc0147b69d8803a21886239e","item_id":"d9860b39bc0147b69d8803a21886239e","uri":"http://192.168.29.201:8097/single/18:1d:ea:b7:bf:10/d9860b39bc0147b69d8803a21886239e.flac?ts=1724590054"},"type":"track","style":"itemplay","nextWindow":"nowPlaying"},{"track":"Sunset in Calabasas","album":"","trackType":"radio","icon":"https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/63/0b/60/630b60e3-6234-9df1-80e2-833eee57be18/859770370543_cover.jpg/1400x1400bb.jpg","artist":"adrian","text":"Sunset in Calabasas","params":{"track_id":"d9860b39bc0147b69d8803a21886239e","item_id":"d9860b39bc0147b69d8803a21886239e","uri":"http://192.168.29.201:8097/single/18:1d:ea:b7:bf:10/d9860b39bc0147b69d8803a21886239e.flac?ts=1724590059"},"type":"track","style":"itemplay","nextWindow":"nowPlaying"}]}
        """.trimIndent()
        val jsonelem = json.parseToJsonElement(jsonstring)
        val resp = json.decodeFromJsonElement<PlayerStatusResponse>(jsonelem)

Can you please provide a slightly fuller log of the debug app, including the fatal stack trace (basically including a full readable version of the log entries in your screenshot)?

@ohuc
Copy link
Author

ohuc commented Aug 26, 2024

Debug log I got right before it
2024-08-26 16:24:25.081 31402 31443 de.maniac103.squeezeclient.debug D CometdClient : Received event message (1159 bytes): Message(channelId=ChannelId(channel=/8ef9c9ec639911ef96b9181deab7bf10/slim/playerstatus/181deab7bf10), clientId=null, id=9, successful=true, data={"player_name":"nk-HP-EliteBook-850-G5","player_connected":1,"player_needs_upgrade":false,"player_is_upgrading":false,"power":1,"signalstrength":0,"waitingToPlay":0,"mode":"stop","remote":1,"current_title":"Music Assistant","time":0,"duration":0,"sync_master":"","sync_slaves":"","mixer volume":66,"player_ip":"192.168.29.201","playlist_cur_index":0,"playlist_tracks":0,"can_seek":0,"digital_volume_control":1,"playlist_timestamp":1724667732,"playlist repeat":0,"playlist shuffle":0,"playlist mode":"off","rate":1,"seq_no":0,"sleep":0,"will_sleep_in":0,"uuid":null,"alarm_state":"none","alarm_snooze_seconds":540,"alarm_timeout_seconds":3600,"count":0,"offset":"-","base":{"actions":{"more":{"itemsParams":"params","window":{"isContextMenu":1},"cmd":["contextmenu"],"player":0,"params":{"context":"playlist","menu":"track"}}}},"preset_loop":[0,0,0,0,0,0,0,0,0,0],"preset_data":[{},{},{},{},{},{},{},{},{},{}],"item_loop":[]})

And fatal stack trace logs:

2024-08-26 16:23:25.644 31209 31209 de.maniac103.squeezeclient.debug E AndroidRuntime : FATAL EXCEPTION: main Process: de.maniac103.squeezeclient.debug, PID: 31209 kotlinx.serialization.json.internal.JsonDecodingException: String literal for key 'offset' should be quoted. Use 'isLenient = true' in 'Json {}' builder to accept non-compliant JSON. JSON input: .....preset_data":[{},{},{},{},{},{},{},{},{},{}],"item_loop":[]} at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24) at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32) at kotlinx.serialization.json.internal.AbstractJsonTreeDecoder.decodeTaggedString(TreeJsonDecoder.kt:144) at kotlinx.serialization.json.internal.AbstractJsonTreeDecoder.decodeTaggedString(TreeJsonDecoder.kt:36) at kotlinx.serialization.internal.TaggedDecoder.decodeString(Tagged.kt:231) at kotlinx.serialization.internal.StringSerializer.deserialize(Primitives.kt:160) at kotlinx.serialization.internal.StringSerializer.deserialize(Primitives.kt:156) at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:77) at kotlinx.serialization.json.internal.AbstractJsonTreeDecoder.decodeSerializableValue(TreeJsonDecoder.kt:52) at kotlinx.serialization.internal.TaggedDecoder.decodeSerializableValue(Tagged.kt:207) at kotlinx.serialization.internal.TaggedDecoder$decodeNullableSerializableElement$1.invoke(Tagged.kt:288) at kotlinx.serialization.internal.TaggedDecoder.tagBlock(Tagged.kt:294) at kotlinx.serialization.internal.TaggedDecoder.decodeNullableSerializableElement(Tagged.kt:286) at de.maniac103.squeezeclient.cometd.response.PlayerStatusResponse$$serializer.deserialize(PlayerStatusResponse.kt:40) at de.maniac103.squeezeclient.cometd.response.PlayerStatusResponse$$serializer.deserialize(PlayerStatusResponse.kt:40) at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:77) at kotlinx.serialization.json.internal.AbstractJsonTreeDecoder.decodeSerializableValue(TreeJsonDecoder.kt:52) at kotlinx.serialization.json.internal.TreeJsonDecoderKt.readJson(TreeJsonDecoder.kt:25) at kotlinx.serialization.json.Json.decodeFromJsonElement(Json.kt:127) at de.maniac103.squeezeclient.cometd.ConnectionHelper.fetchPlaylist(ConnectionHelper.kt:565) at de.maniac103.squeezeclient.cometd.ConnectionHelper$fetchPlaylist$1.invokeSuspend(Unknown Source:15) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:28) at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:99) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:231) at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:187) at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:159) at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:466) at kotlinx.coroutines.CancellableContinuationImpl.completeResume(CancellableContinuationImpl.kt:582) at kotlinx.coroutines.channels.BufferedChannelKt.tryResume0(BufferedChannel.kt:2927) at kotlinx.coroutines.channels.BufferedChannelKt.access$tryResume0(BufferedChannel.kt:1) at kotlinx.coroutines.channels.BufferedChannel.tryResumeReceiver(BufferedChannel.kt:669) at kotlinx.coroutines.channels.BufferedChannel.updateCellSend(BufferedChannel.kt:481) at kotlinx.coroutines.channels.BufferedChannel.access$updateCellSend(BufferedChannel.kt:36) at kotlinx.coroutines.channels.BufferedChannel.send$suspendImpl(BufferedChannel.kt:3120) at kotlinx.coroutines.channels.BufferedChannel.send(Unknown Source:0) at kotlinx.coroutines.channels.ChannelCoroutine.send(Unknown Source:2) at de.maniac103.squeezeclient.cometd.ConnectionHelper$publishOneShotRequest$subscriptionChannel$1.invokeSuspend(ConnectionHelper.kt:322)

@maniac103
Copy link
Owner

maniac103 commented Aug 26, 2024

OK, thanks. Strangely enough your new JSON also doesn't reproduce it, but looking at MA sources hopefully got me on the right track.

->
Next test APK

(Built on another machine, so signing keys might be different ... just uninstall the previous debug APK in that case)

Diff so far:

diff --git a/app/src/main/java/de/maniac103/squeezeclient/cometd/response/PlayerStatusResponse.kt b/app/src/main/java/de/maniac103/squeezeclient/cometd/response/PlayerStatusResponse.kt
index 4a59509..cb84bae 100644
--- a/app/src/main/java/de/maniac103/squeezeclient/cometd/response/PlayerStatusResponse.kt
+++ b/app/src/main/java/de/maniac103/squeezeclient/cometd/response/PlayerStatusResponse.kt
@@ -34,14 +34,17 @@ import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.JsonPrimitive
 import kotlinx.serialization.json.decodeFromJsonElement
+import kotlinx.serialization.json.int
·
 // offset and item_loop are not returned when fetching non-existing pages
 @Serializable
 data class PlayerStatusResponse(
     @SerialName("mode")
     val state: PlayerStatus.PlayState,
-    val offset: String? = null,
+    // might be a string or a number (LMS always sends strings, MA sends "-" or numbers)
+    val offset: JsonPrimitive? = null,
     val count: Int,
     @SerialName("item_loop")
     private val items: List<JsonObject>? = null,
@@ -122,10 +125,10 @@ data class PlayerStatusResponse(
     }
·
     fun asModelPlaylist(json: Json, fetchOffset: Int): Playlist {
-        val actualOffset = when (offset) {
-            null -> fetchOffset
-            "-" -> playlistCurrentIndex
-            else -> offset.toInt()
+        val actualOffset = when {
+            offset == null -> fetchOffset
+            offset.content == "-" -> playlistCurrentIndex
+            else -> offset.int
         }
         val (playlist, reachableCount) = if (items != null) {
             val playlist = items.mapNotNull {it.asModelPlaylistItem(json, base) }
diff --git a/app/src/main/java/de/maniac103/squeezeclient/cometd/response/ServerStatusResponse.kt b/app/src/main/java/de/maniac103/squeezeclient/cometd/response/ServerStatusResponse.kt
index e51c3ca..abc8431 100644
--- a/app/src/main/java/de/maniac103/squeezeclient/cometd/response/ServerStatusResponse.kt
+++ b/app/src/main/java/de/maniac103/squeezeclient/cometd/response/ServerStatusResponse.kt
@@ -25,7 +25,9 @@ import kotlinx.serialization.Serializable
 data class ServerStatusResponse(
     val version: String,
     @SerialName("players_loop")
+    // players_loop is omitted if there are no players
     val players: List<Player> = emptyList(),
     @SerialName("mediadirs")
-    val mediaDirectories: List<String>
+    // LMS sends those (with valid contents), MA doesn't
+    val mediaDirectories: List<String> = emptyList()
 )

@ohuc
Copy link
Author

ohuc commented Aug 26, 2024

Finally! It stopped crashing
But it has similar issues which squeezer app had
-> there is a lot delay
-> the app still crashes out of nowhere sometimes and stops working until reset the data

I don't think there is any point of trying to make it compatible with music assistant
Thanks for providing the test apks :)

@ohuc ohuc closed this as completed Aug 26, 2024
@ohuc ohuc reopened this Aug 26, 2024
@ohuc
Copy link
Author

ohuc commented Aug 26, 2024

Also I get this message out of nowhere a lot of times
Screenshot_20240826-213549

@maniac103
Copy link
Owner

Well, a log would help ;-)

@ohuc
Copy link
Author

ohuc commented Aug 26, 2024

Here you go:
2024-08-26 21:35:49.089 20961 20961 de.maniac103.squeezeclient.debug E AndroidRuntime : FATAL EXCEPTION: main Process: de.maniac103.squeezeclient.debug, PID: 20961 java.lang.IllegalStateException at de.maniac103.squeezeclient.cometd.ConnectionHelper.publishOneShotRequest(ConnectionHelper.kt:313) at de.maniac103.squeezeclient.cometd.ConnectionHelper.setVolume(ConnectionHelper.kt:261) at de.maniac103.squeezeclient.ui.volume.VolumeFragment$handleKeyDown$1.invokeSuspend(VolumeFragment.kt:120) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:363) at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26) at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:21) at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:88) at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:123) at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:52) at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1) at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:43) at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1) at de.maniac103.squeezeclient.ui.volume.VolumeFragment.handleKeyDown(VolumeFragment.kt:118) at de.maniac103.squeezeclient.ui.MainActivity.onKeyDown(MainActivity.kt:238) at android.view.KeyEvent.dispatch(KeyEvent.java:3029) at android.app.Activity.dispatchKeyEvent(Activity.java:4502) at androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.kt:103) at androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:85) at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.kt:117) at androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:604) at androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59) at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:3397) at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:364) at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:7953) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:7793) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7186) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7243) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7209) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7375) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7217) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:7432) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7190) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7243) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7209) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7217) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7190) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7243) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7209) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7408) at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:7641) at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:4910) at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:4287) at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:4278) at android.view.inputmethod.InputMethodManager.-$$Nest$mfinishedInputEvent(Unknown Source:0) at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:4887)

@ohuc
Copy link
Author

ohuc commented Aug 26, 2024

Also, after playing around a bit, I can definitely say that the app is working way better than the Squeezer app.

However, I'm not sure wheather the delay in updating the squeeze client app normal behaviour
Like when I press the play pause buttons, they do work but they don't update in the app

Like in this video when I pressed the play pause button it did work and paused the music but it never updated in the app until I forced tap and restarted the app

screen-20240826-214514.mp4

@maniac103
Copy link
Owner

maniac103 commented Aug 26, 2024

This sounds like player status updates aren't sent as expected. Unlike Squeezer I'm not trying to 'predict' server responses to commands for updating the UI, but wait for the server sending an update event as kind of acknowledgement.
As mentioned, a log from the time before pressing the button until - say - 10 seconds after pressing would be helpful.

@ohuc
Copy link
Author

ohuc commented Aug 26, 2024

2024-08-26 22:22:05.800 28500 28500 de.maniac103.squeezeclient.debug W MediaSessionCompat : Couldn't find a unique registered media button receiver in the given context.

2024-08-26 22:22:44.300 28885 28885 de.maniac103.squeezeclient.debug I view_enqueue_input_event : [Motion - Cancel,de.maniac103.squeezeclient.debug/de.maniac103.squeezeclient.ui.MainActivity]

The xonnection lost error I keep getting out of nowhere
2024-08-26 22:23:37.673 28885 28885 de.maniac103.squeezeclient.debug W MainActivity : Connection failed de.maniac103.squeezeclient.cometd.CometdClient$CometdException: Not connected at de.maniac103.squeezeclient.cometd.CometdClient.publish(CometdClient.kt:118) at de.maniac103.squeezeclient.cometd.ConnectionHelper.publishRequest(ConnectionHelper.kt:349) at de.maniac103.squeezeclient.cometd.ConnectionHelper.publishRequest$default(ConnectionHelper.kt:339) at de.maniac103.squeezeclient.cometd.ConnectionHelper$connect$1.invokeSuspend(ConnectionHelper.kt:154) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:28) at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:99) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104) at android.os.Handler.handleCallback(Handler.java:959) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loopOnce(Looper.java:232) at android.os.Looper.loop(Looper.java:317) at android.app.ActivityThread.main(ActivityThread.java:8700) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)

@maniac103
Copy link
Owner

maniac103 commented Aug 27, 2024

By log I was referring to the full log generated by the app, not only stack traces and warnings ;-) In particular, I'm interested in the messages exchanged between app and server.
To give a bit of background/explanation:

  • I assume the 'Not connected' exceptions happen because MA doesn't send regular server status messages. When requesting those, the app requests them to be received in a certain interval, to which LMS reacts by sending those in said interval. The lack of responses is interpreted as 'connection has failed'.
  • I also assume - as mentioned before - that MA doesn't send player status updates after receiving commands. When sending e.g. a 'play' command, the app expects a player status update event which reports play state changing from 'pause' to 'play'. It does not try to second guess the outcome of commands sent.

I want to see the communication to confirm the assumptions. If they hold true, I guess that's a topic for the MA folks to solve; I don't want to adopt for that in the app because it means restructuring a lot of stuff. If they don't hold true, we'll need to see what else is off there.
Looking at the MA code it looks like they implemented sending events in response to subscriptions, but I don't understand that code well enough to judge whether the implementation is sufficient.

@maniac103
Copy link
Owner

2024-08-26 21:35:49.089 20961 20961 de.maniac103.squeezeclient.debug E AndroidRuntime : FATAL EXCEPTION: main Process: de.maniac103.squeezeclient.debug, PID: 20961 java.lang.IllegalStateException
at de.maniac103.squeezeclient.cometd.ConnectionHelper.publishOneShotRequest(ConnectionHelper.kt:313)
at de.maniac103.squeezeclient.cometd.ConnectionHelper.setVolume(ConnectionHelper.kt:261)
at de.maniac103.squeezeclient.ui.volume.VolumeFragment$handleKeyDown$1.invokeSuspend(VolumeFragment.kt:120)

I fixed this one via 3229162

@ohuc
Copy link
Author

ohuc commented Aug 28, 2024

Hey sorry for the late response,
Here's all the logs
logs.log

I also noticed that if there is nothing playing or in the queue the app keeps crashing

@maniac103
Copy link
Owner

The crashing is because they omit parts of the player status response if a given player is not powered, unlike LMS (example). I'd consider this an MA bug, but I can work around that (and probably should review LMS code as well to see what properties actually might be optional).

The larger issue is the update events, where, if I'm reading the code here correctly, determine the type of events to be sent (and whether to send events at all) by interpreting the channel names the client subscribed for instead of looking at the subscription itself. In particular, they seem to expect the player ID as part of the channel name in its original MAC address form (11:22:33:44:55:66), but I cut out the colons (112233445566) because colons are not allowed in channel names as per the cometd spec (mind : not being part of mark). I consider that a bug in MA as well, but one I think it's worth filing a bug report for: they should treat channel names in subscriptions as opaque and look at the actual subscription contents.

@droyholmes
Copy link

droyholmes commented Sep 1, 2024

At this point I agree that a rollup of this thread should be posted to the MA Git issues. Perhaps some modifications to the Lyrion "light" server should be made. I wonder if it has been tested with many squeezebox devices. I agree out of all the protocols offered by MA, slimproto is the most feature complete. And it could spark interest in the squeezebox used market.

I haven't been involved in the testing of the client so I'm not the person to bring their attention to it. If further assistance in testing is needed message me. I've got several "ThinkView Smart"s running lineageOS that i need a simple solution like this for.

@borgqueenx
Copy link

At this point I agree that a rollup of this thread should be posted to the MA Git issues. Perhaps some modifications to the Lyrion "light" server should be made. I wonder if it has been tested with many squeezebox devices. I agree out of all the protocols offered by MA, slimproto is the most feature complete. And it could spark interest in the squeezebox used market.

I haven't been involved in the testing of the client so I'm not the person to bring their attention to it. If further assistance in testing is needed message me. I've got several "ThinkView Smart"s running lineageOS that i need a simple solution like this for.

haha, i also got 3 thinksmart view devices to run this, but the app is crashing as soon as i try to connect to home assistant. (slimproto integration)

There is a well working SB Player app in the play store thats just 4 euro or so, however it disconnects and wont reconnect after some time, even with all settings to prevent the app from closing. i want to use this for notification sounds, so disconnects are sad.

@ohuc
Copy link
Author

ohuc commented Sep 15, 2024

Which SB app are you referring to?

@borgqueenx
Copy link

borgqueenx commented Sep 15, 2024

Which SB app are you referring to?

Thats literary the name of it. SB Player. It also connects to home assistant slimproto. but it disconnects quite often at seemingly random times, and does not try to reconnect.

@droyholmes
Copy link

At this point I agree that a rollup of this thread should be posted to the MA Git issues. Perhaps some modifications to the Lyrion "light" server should be made. I wonder if it has been tested with many squeezebox devices. I agree out of all the protocols offered by MA, slimproto is the most feature complete. And it could spark interest in the squeezebox used market.
I haven't been involved in the testing of the client so I'm not the person to bring their attention to it. If further assistance in testing is needed message me. I've got several "ThinkView Smart"s running lineageOS that i need a simple solution like this for.

haha, i also got 3 thinksmart view devices to run this, but the app is crashing as soon as i try to connect to home assistant. (slimproto integration)

There is a well working SB Player app in the play store thats just 4 euro or so, however it disconnects and wont reconnect after some time, even with all settings to prevent the app from closing. i want to use this for notification sounds, so disconnects are sad.

Did you try using Music Assistant? Check their documentation for player, you cannot have the slimproto integration cannot run side by side with MA. Strangely MA doesn't use LMS as a music source, so I'm not sure what is going on with that.

@borgqueenx
Copy link

it seems this issue is a lot less common with the slimproto in music assistant, compared to slimproto without using music assistant. weird.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants