[PR #1835] Fixed a progress saving error when connection to internet is lost #1716

Open
opened 2026-04-25 00:00:48 +02:00 by adam · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/advplyr/audiobookshelf-app/pull/1835
Author: @ckbaker10
Created: 3/30/2026
Status: 🔄 Open

Base: masterHead: fix-progress-save-error


📝 Commits (2)

  • c3f952f Fix local saving during network loss and playback switch
  • 7d70fa6 Wrong thread

📊 Changes

2 files changed (+114 additions, -1 deletions)

View changed files

📝 android/app/src/main/java/com/audiobookshelf/app/media/MediaProgressSyncer.kt (+54 -0)
📝 android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt (+60 -1)

📄 Description

Brief summary

When streaming an audiobook from the server and the network connection drops, playback progress is lost and the app does not automatically switch to a downloaded local copy. This PR fixes both issues by saving server progress locally as a fallback and by automatically switching to a local copy when the network is lost.

Which issue is fixed?

Pull Request Type

  • Affects: Android only
  • Changes: Backend (native Kotlin player/sync logic)

In-depth Description

There are two problems when streaming from the server and the network drops mid-playback:

Problem 1: Progress is lost

In MediaProgressSyncer.sync(), the server-item code path only syncs progress to the server. When the network is down, it logs "Not sending progress to server" and discards the progress entirely -- nothing is saved locally. This means the user loses their listening position and has to manually find their place again.

Fix: When a server item cannot sync (network down or server sync failure), the progress is now saved to the local database as a LocalMediaProgress entry with a server-offline- prefixed ID. This ensures the listening position is always persisted. These offline entries are cleaned up naturally by the existing DB cleanup cycle after the progress is synced back to the server.

Problem 2: No automatic fallback to local copy

When the network drops during server streaming, the player stalls and eventually errors out. The only existing fallback is direct play to transcode (HLS), which also requires a server connection. There is no fallback to a downloaded local copy of the same audiobook.

Fix: A new switchToLocalCopyIfAvailable() method checks if a local copy of the currently streaming audiobook exists (via getLocalLibraryItemByLId). If found, it seamlessly switches playback to the local copy at the same position. This is triggered in two places:

  1. Proactively in the networkCallback.onLost() -- when the system detects network loss, the app immediately switches to the local copy before the player buffers run out.
  2. Reactively in handlePlayerPlaybackError() -- if a playback error occurs on a server item, the app tries switching to a local copy before falling back to transcode or failing.

For podcasts, the correct local episode is matched via serverEpisodeId. The playback position (currentTime) is preserved across the switch since both server and local sessions use the same absolute time coordinate system.

Files changed

  • MediaProgressSyncer.kt -- Added saveServerProgressLocally() method; called when network is down or server sync fails in the non-local branch
  • PlayerNotificationService.kt -- Added switchToLocalCopyIfAvailable() method; added onLost to networkCallback; modified handlePlayerPlaybackError() to try local copy first

How have you tested this?

  1. Stream an audiobook from the server that also has a downloaded local copy
  2. Disable network (airplane mode / WiFi off) during playback
  3. Verify: playback switches seamlessly to the local copy at the same position
  4. Verify: progress is saved locally and visible in the app
  5. Re-enable network
  6. Verify: progress syncs back to the server

Also tested:

  • Streaming without a local copy available: player behaves as before (stalls/errors, no crash)
  • Network loss while not playing: no unnecessary switch attempt
  • Podcast episode streaming with local copy: correct episode matched and switched

Screenshots

N/A -- backend changes only, no UI modifications.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/advplyr/audiobookshelf-app/pull/1835 **Author:** [@ckbaker10](https://github.com/ckbaker10) **Created:** 3/30/2026 **Status:** 🔄 Open **Base:** `master` ← **Head:** `fix-progress-save-error` --- ### 📝 Commits (2) - [`c3f952f`](https://github.com/advplyr/audiobookshelf-app/commit/c3f952f290d0dc5a5b97daa765b0dbd61f8641d2) Fix local saving during network loss and playback switch - [`7d70fa6`](https://github.com/advplyr/audiobookshelf-app/commit/7d70fa6f89ca52a88ea850ed08d0539dd65e1055) Wrong thread ### 📊 Changes **2 files changed** (+114 additions, -1 deletions) <details> <summary>View changed files</summary> 📝 `android/app/src/main/java/com/audiobookshelf/app/media/MediaProgressSyncer.kt` (+54 -0) 📝 `android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt` (+60 -1) </details> ### 📄 Description ## Brief summary When streaming an audiobook from the server and the network connection drops, playback progress is lost and the app does not automatically switch to a downloaded local copy. This PR fixes both issues by saving server progress locally as a fallback and by automatically switching to a local copy when the network is lost. ## Which issue is fixed? <!-- TODO: Link issue number if applicable, e.g. Fixes #1234 --> ## Pull Request Type - Affects: Android only - Changes: Backend (native Kotlin player/sync logic) ## In-depth Description There are two problems when streaming from the server and the network drops mid-playback: ### Problem 1: Progress is lost In `MediaProgressSyncer.sync()`, the server-item code path only syncs progress to the server. When the network is down, it logs "Not sending progress to server" and discards the progress entirely -- nothing is saved locally. This means the user loses their listening position and has to manually find their place again. **Fix:** When a server item cannot sync (network down or server sync failure), the progress is now saved to the local database as a `LocalMediaProgress` entry with a `server-offline-` prefixed ID. This ensures the listening position is always persisted. These offline entries are cleaned up naturally by the existing DB cleanup cycle after the progress is synced back to the server. ### Problem 2: No automatic fallback to local copy When the network drops during server streaming, the player stalls and eventually errors out. The only existing fallback is direct play to transcode (HLS), which also requires a server connection. There is no fallback to a downloaded local copy of the same audiobook. **Fix:** A new `switchToLocalCopyIfAvailable()` method checks if a local copy of the currently streaming audiobook exists (via `getLocalLibraryItemByLId`). If found, it seamlessly switches playback to the local copy at the same position. This is triggered in two places: 1. **Proactively** in the `networkCallback.onLost()` -- when the system detects network loss, the app immediately switches to the local copy before the player buffers run out. 2. **Reactively** in `handlePlayerPlaybackError()` -- if a playback error occurs on a server item, the app tries switching to a local copy before falling back to transcode or failing. For podcasts, the correct local episode is matched via `serverEpisodeId`. The playback position (`currentTime`) is preserved across the switch since both server and local sessions use the same absolute time coordinate system. ### Files changed - `MediaProgressSyncer.kt` -- Added `saveServerProgressLocally()` method; called when network is down or server sync fails in the non-local branch - `PlayerNotificationService.kt` -- Added `switchToLocalCopyIfAvailable()` method; added `onLost` to `networkCallback`; modified `handlePlayerPlaybackError()` to try local copy first ## How have you tested this? 1. Stream an audiobook from the server that also has a downloaded local copy 2. Disable network (airplane mode / WiFi off) during playback 3. Verify: playback switches seamlessly to the local copy at the same position 4. Verify: progress is saved locally and visible in the app 5. Re-enable network 6. Verify: progress syncs back to the server Also tested: - Streaming without a local copy available: player behaves as before (stalls/errors, no crash) - Network loss while not playing: no unnecessary switch attempt - Podcast episode streaming with local copy: correct episode matched and switched ## Screenshots N/A -- backend changes only, no UI modifications. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
adam added the pull-request label 2026-04-25 00:00:48 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/audiobookshelf-app#1716