jellyfin / jellyfin-android

Android Client for Jellyfin
https://jellyfin.org
GNU General Public License v2.0
1.46k stars 241 forks source link

After losing connection to server, client returns to server address input screen #544

Open davidrutland opened 2 years ago

davidrutland commented 2 years ago

Describe the bug

This affects version 2.3.2 onwards on Android- so I've downgraded to 2.3.1

Be playing audiobooks while driving. Connection drops. Audiobook continues, but Jellyfin returns to server address input screen (as if the server had moved rather than the more obvious explanation) Hit 'Connect' Audiobook continues continuing, but controls (play/pause/currently playing) is gone Eventually, audiobook stops.

Up to and including 2.3.1 this did not happen. Connection dropped and Jellyfin just stayed on the same screen. Still able to pause / resume etc. Shortish interruptions did not affect playback at all.

Logs

No - because I downgraded.

Application version

2.3.2 upwards

Where did you install the app from?

F-Droid

Device information

Sony G8341

Android version

Android 9

Jellyfin server version

10.7.7.0

Which video player implementations does this bug apply to?

Sarlay commented 2 years ago

I have the same issue

comfreak89 commented 2 years ago

I have a similar issue on 2 devices: we loose the connection when playing trailers.

exalented commented 2 years ago

This issue can be resolved by setting the PublishedServerUrl. Seeing as though removing this fixed DLNA problems in the past it's resonable that people would be encountering this issue. Note that jellyfin-android would not successfully connect without a trailing / in the client (this is also the case with the WebOS client). This has been tested and seems to work with 'multi-domain' where you can access it both publically and on your lan, with and without https.

Configure your server something similar to this and reverse proxy if you need.

    environment:
      - JELLYFIN_PublishedServerUrl=http://server:8096/media
SegiH commented 2 years ago

If anyone wants to delve into the source code, I found the place where this is being triggered: java/org/jellyfin/mobile/fragment/ConnectFragment.kt in override fun onViewCreated which is defined as:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Apply window insets
        ViewCompat.requestApplyInsets(serverSetupLayout)

        hostInput.setText(mainViewModel.serverState.value.server?.hostname)
        hostInput.setSelection(hostInput.length())
        hostInput.setOnEditorActionListener { _, action, event ->
            when {
                action == EditorInfo.IME_ACTION_DONE || event.keyCode == KeyEvent.KEYCODE_ENTER -> {
                    connect()
                    true
                }
                else -> false
            }
        }
        connectButton.setOnClickListener {
            connect()
        }
        chooseServerButton.setOnClickListener {
            chooseServer()
        }

        if (arguments?.getBoolean(Constants.FRAGMENT_CONNECT_EXTRA_ERROR) == true)
            showConnectionError()

        // Show keyboard
        serverSetupLayout.doOnNextLayout {
            @Suppress("MagicNumber")
            hostInput.postDelayed(25) {
                hostInput.requestFocus()

                requireContext().getSystemService<InputMethodManager>()?.showSoftInput(hostInput, InputMethodManager.SHOW_IMPLICIT)
            }
        }

        discoverServers()
    }

The 2 lines above:

        if (arguments?.getBoolean(Constants.FRAGMENT_CONNECT_EXTRA_ERROR) == true)
            showConnectionError()

are being triggered which sends the user back to the server address input screen. I tested this out in Android Studio by running the Android client in the emulator, logging into my instance of JF and then enabling airplane mode in the emulator to replicate this behavior.

SegiH commented 2 years ago

ith the WebOS client)

exalented,

Are you supposed to use the local or public URL for the value of this var ? I used the local URL because I'm behind a reverse proxy but still have this issue

exalented commented 2 years ago

I run 10.8.0_alpha3 behind two reverse proxies actually.

JELLYFIN_PublishedServerUrl is a local address within my LAN. When you connect to your instance make sure you have a trailing slash (/). So for example you'd goto the following in a browser, the WebOS wrapper or the android client: http://jellyfinserver.local:8096/media/ or https://jellyfinserver.tld/media/ .

SegiH commented 2 years ago

Thanks.

I have added the trailing slash which was missing. I will update you with the results

nielsvanvelzen commented 2 years ago

A trailing slash should not matter as the server address normalization code from our SDK takes care of that. If it does change behavior of the app it's a bug.

jellyfin-bot commented 2 years ago

This issue has gone 120 days without comment. To avoid abandoned issues, it will be closed in 21 days if there are no new comments.

If you're the original submitter of this issue, please comment confirming if this issue still affects you in the latest release or master branch, or close the issue if it has been fixed. If you're another user also affected by this bug, please comment confirming so. Either action will remove the stale label.

This bot exists to prevent issues from becoming stale and forgotten. Jellyfin is always moving forward, and bugs are often fixed as side effects of other changes. We therefore ask that bug report authors remain vigilant about their issues to ensure they are closed if fixed, or re-confirmed - perhaps with fresh logs or reproduction examples - regularly. If you have any questions you can reach us on Matrix or Social Media.

SegiH commented 2 years ago

I am using Jellyfin for Android 2.4.4 from Play Store on Android 12 and still have this issue with the app when it thinks I've lost my network connection.

SegiH commented 1 year ago

I am still getting kicked back to the "Connect to Server" screen.

I downloaded the Kotlin source for the Android app today and ran it in debugging mode with a breakpoint at https://github.com/jellyfin/jellyfin-android/blob/ad4103d511b61967308c6653d637c2065911897d/app/src/main/java/org/jellyfin/mobile/setup/ConnectFragment.kt#L30

This is the same method that I referenced in my previous post but the app was written in Java then and has since been re-written in Kotlin.

I started playing a song while debugging the app and toggled airplane mode to simulate momentarily losing network connection and sure enough, the breakpoint gets hit.

Now in same cases, my connection will go from 5G -> Wifi or 5G-> 4G but the result is the same.

nielsvanvelzen commented 1 year ago

This is the same method that I referenced in my previous post but the app was written in Java then and has since been re-written in Kotlin.

The app never used Java so that's not possible

Maxr1998 commented 1 year ago

Technically we have some code parts that are/were Java (mostly for Chromecast) but not in this case of course.

Maxr1998 commented 1 year ago

On the topic itself: network/connection errors are meant to give you an option to switch to another server or fix the connection URL. Otherwise, you might be stuck on a dead, non-actionable screen. So I don't think this issue is an actual problem as long as it doesn't happen randomly although you're actually connected to your server.

SegiH commented 1 year ago

That's exactly what's happening to me.

I'm randomly getting kicked back to the server selection screen

Maxr1998 commented 1 year ago

Without logs it's hard to tell what's causing it. Would you mind taking a logcat of your device while it happens and sending it to us?

solidsnake1298 commented 1 year ago

I've attached a log for this issue. I start music playback in my drive way while still on WiFi, start driving away, cuts over to cellular, drops back to server connect screen. But music is still playing. The Jellyfin app on my phone (Pixel 6a) is always using the DDNS address for my server and my router does a NAT loopback.

These are probably the pertinent events.

[2023-06-26 13:37:38.011 +00:00] [INF] RemoteClientBitrateLimit: 10000000, RemoteIp: "192.168.1.1", IsInLocalNetwork: True

[2023-06-26 13:38:57.545 +00:00] [INF] Sending ForceKeepAlive message to 1 inactive WebSockets.
...........
[2023-06-26 13:39:06.142 +00:00] [INF] User policy for "Chris". EnableAudioPlaybackTranscoding: True

[2023-06-26 13:39:06.142 +00:00] [INF] RemoteClientBitrateLimit: 10000000, RemoteIp: "172.58.187.7", IsInLocalNetwork: False

[2023-06-26 13:39:09.549 +00:00] [INF] Lost 1 WebSockets.

[2023-06-26 13:39:25.695 +00:00] [WRN] "172.58.187.7/32": External request received, however, only an internal interface bind found.

[2023-06-26 13:39:38.704 +00:00] [WRN] "172.58.187.7/32": External request received, however, only an internal interface bind found.

[2023-06-26 13:39:39.081 +00:00] [WRN] "172.58.187.7/32": External request received, however, only an internal interface bind found.

[2023-06-26 13:39:39.170 +00:00] [WRN] "172.58.187.7/32": External request received, however, only an internal interface bind found.

[2023-06-26 13:39:39.432 +00:00] [INF] WS "172.58.187.7" request

[2023-06-26 13:39:57.909 +00:00] [WRN] WS "172.58.187.7" error receiving data: "The remote party closed the WebSocket connection without completing the close handshake."

[2023-06-26 13:39:57.920 +00:00] [INF] WS "172.58.187.7" closed

[2023-06-26 13:40:09.462 +00:00] [WRN] "172.58.187.7/32": External request received, however, only an internal interface bind found.

[2023-06-26 13:40:09.724 +00:00] [WRN] "172.58.187.7/32": External request received, however, only an internal interface bind found.

[2023-06-26 13:40:09.796 +00:00] [WRN] "172.58.187.7/32": External request received, however, only an internal interface bind found.

[2023-06-26 13:40:09.984 +00:00] [INF] WS "172.58.187.7" request

jellyfin_error_logs_20230626.txt

Maxr1998 commented 1 year ago

It looks like it's primarily caused by the websocket disconnect. We should probably ignore that event instead of going to the connect screen.

SegiH commented 1 year ago

Good idea. I can test this out by building the app without that logic

SegiH commented 1 year ago

Here's an apk with the fix. I renamed the package in this build so you can install it along side the original apk and changed

val encounteredConnectionError = arguments?.getBoolean(Constants.FRAGMENT_CONNECT_EXTRA_ERROR) == true

to

val encounteredConnectionError = false

in ConnectFragment.kt

Edit: Forgot to mention, the only way that I can currently test this fix is by toggling WiFi while a song is playing and seeing if it sends me back to the connect screen and it didn't do that for me.

solidsnake1298 commented 1 year ago

I will give it a go this morning and report back.

solidsnake1298 commented 1 year ago

Here's an apk with the fix. I renamed the package in this build so you can install it along side the original apk and changed

val encounteredConnectionError = arguments?.getBoolean(Constants.FRAGMENT_CONNECT_EXTRA_ERROR) == true

to

val encounteredConnectionError = false

in ConnectFragment.kt

Edit: Forgot to mention, the only way that I can currently test this fix is by toggling WiFi while a song is playing and seeing if it sends me back to the connect screen and it didn't do that for me.

I also couldn't replicate by toggling WiFi, either. Only by moving out of range.

Regarding the Jellyfin Debug apk you gave me. Still no change. It drops me to the server screen. One small change is that the music stops immediately where before it continued to play, probably, whatever was buffered.

When I get home from work later tonight I will check the logs again and post if I see anything different.

SegiH commented 1 year ago

Ok thanks. I'll take another look and see if there's anything else I can to do to prevent the app from taking you to the connect screen.

At this point, it would be better to never send you back to that screen ever.

solidsnake1298 commented 1 year ago

The logs are a bit different this morning with the Debug verision.

[2023-06-27 13:16:11.129 +00:00] [INF] User policy for "Chris". EnableAudioPlaybackTranscoding: True
[2023-06-27 13:16:11.130 +00:00] [INF] RemoteClientBitrateLimit: 10000000, RemoteIp: "192.168.1.1", IsInLocalNetwork: True
[2023-06-27 13:25:54.234 +00:00] [INF] Playback stopped reported by app "Jellyfin Android" "0.0.0-dev.1" playing "Magia". Stopped at "311984" ms
[2023-06-27 13:32:34.839 +00:00] [WRN] WS "192.168.1.1" error receiving data: "The remote party closed the WebSocket connection without completing the close handshake."
[2023-06-27 13:32:34.846 +00:00] [INF] WS "192.168.1.1" closed

jellyfin_error_logs_20230627.log

SegiH commented 1 year ago

I think I have found the root cause of this issue.

In org/jellyfin/mobile/webapp/JellyfinWebViewClient.kt, onReceivedError() is being triggered with the error ERROR_HOST_LOOKUP and ERROR_CONNECT.

I temporarily commented out these 2 error codes (Lines 99 and 100) and started playing a song and then enabled airplane mode.

Instead of taking me back to the connection page, it showed a playback error which is a slightly better way to handle it IMO.

This is not a permanent fix but at least helps to point us to the right direction of the cause of this.

There should be something that says that if these 2 errors are received, wait a few seconds then check connectivity status again

Edit: Adding apk for you guys to test this temporary fix

solidsnake1298 commented 1 year ago

Whoops. Didn't see this until I already got to work. I will give it a try tomorrow morning and report back.

Ideally the android app would quietly handle the transition from internal to external access. But would that require a server side code change to assist in a graceful transition?

Also, would there be an issue where the playback settings between internal and external are different, like in my setup? Direct play internally, 128Kbps transcode externally. How would the app handle that transition if a track wasn't complete buffered before transitioning to cellular? Could it seamlessly continue the track with the different bitrates/codecs?

SegiH commented 1 year ago

I'm not sure about the server side question but when I played a song in a place where my internet connection is slow. The song starting playing and was still buffering when I enabled airplane mode.

That sounds similar to the circumstance that you are asking about as far as buffering.

That's when I got a playback error message and the song stopped playing but it didn't take me to the server login screen.

Seems a lot easier way to deal with this issue where you need to start playing the media again rather than logging into Jellyfin again.

solidsnake1298 commented 1 year ago

I was not dropped to the server address screen this morning, nor was I shown an error message. Though I think the whole song had buffered by the time I was out of WiFi range. The next song started without issue.

Regardless, this is a huge improvement over the current production version.

SegiH commented 1 year ago

Glad to hear that it doesn't crash back to the login screen. I just played a song and let it load completely before turning on airplane mode and it did not crash. I was able to seek within the song and it seeked just fine

solidsnake1298 commented 1 year ago

More good news. This morning the song I started as I left for work did not fully buffer before I was out of range of my WiFi. The song continued to play and when it came time to pull down the rest of the song the app did so without issue and there was no discernible transition.

I will have to check whether or not that song was already 128Kbps MP3. Pretty sure it is 320Kbps MP3. I will also inspect the server logs.

Speaking of logs, both yesterday and this morning the debug app you had me install generated an error heap or whatever it was called. Do you want me to post those here? I didn't look very long, but I didn't see a way to export it.

Tomorrow I will make sure to deliberately pick a long FLAC encoded song and see what happens.

SegiH commented 1 year ago

Yea please send it to me

solidsnake1298 commented 1 year ago

Here are the dumps.

https://drive.google.com/file/d/1jKKwqtINc-uDan2GSpNZ5tw0EMR-aVPO/view?usp=drive_link https://drive.google.com/file/d/1jFwNywdW-zwS8xbV9DqFl72em_F5JT3A/view?usp=drive_link

SegiH commented 1 year ago

I just did a test. I checked out the original code and did not change anything. I caused this error to happen while a song was playing and since its a debugging app, it triggers an error log that you are seeing.

My code change doesn't seem to be the cause of this

SegiH commented 1 year ago

I submitted pull requested 1131 to add this fix into the main JF app

LinAGKar commented 1 month ago

Any update on this? The pull request has been ignored for a year