Adds a playback queue feature allowing users to line up items to play sequentially. The queue persists across sessions and is managed at both the JavaScript/Vue layer and the native Android layer for seamless background playback.
Queue management UI: New QueueModal component with drag-to-reorder, play from position, and individual item removal. Accessible via a queue button in the audio player, side drawer, and item more menus
Add to queue: Queue buttons added to item/episode more menus, podcast latest episode rows, and the episode detail page
Persistent storage: Queue is saved to Capacitor Preferences (SharedPreferences on Android) and restored on app launch
Native Android auto-advance: QueueManager reads the queue directly from SharedPreferences so the Android PlayerNotificationService can advance to the next item on playback completion, even when the app is backgrounded
Progress preservation: Current playback position is saved in the queue item so playback resumes from where it left off
Native→JS sync: When the native layer advances the queue, it emits onQueueChanged so the Vue store reloads from storage and stays in sync
Changed files
store/index.js — queue state, getters, mutations and actions
Add an audiobook to the queue from the item more menu; verify it appears in the queue modal
Add a podcast episode to the queue from the episode page and latest episodes list
Drag items to reorder in the queue modal; verify order is persisted after closing and reopening
Delete an item from the queue; verify it is removed
Play an item from the queue modal while something is already playing; verify the current item is added back to the front of the queue
Let playback finish; verify the next queue item starts automatically
Force-close the app and reopen; verify the queue is restored from storage
Test background auto-advance (screen off, app backgrounded) on Android
🔄 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/1704
**Author:** [@andykelk](https://github.com/andykelk)
**Created:** 10/20/2025
**Status:** 🔄 Open
**Base:** `master` ← **Head:** `feature/playback-queue`
---
### 📝 Commits (10+)
- [`66903d1`](https://github.com/advplyr/audiobookshelf-app/commit/66903d118fdba6cc325f11f025329a7b07cb956e) Add playback queue state management and strings
- [`3815f8d`](https://github.com/advplyr/audiobookshelf-app/commit/3815f8d651ee67f9454168611dbd75aa6e22279a) feat: use device storage to persist queue
- [`89d6f79`](https://github.com/advplyr/audiobookshelf-app/commit/89d6f796bb16712102239ae58b1169797bbabe48) Add QueueModal component for queue management
- [`4347335`](https://github.com/advplyr/audiobookshelf-app/commit/434733523bb73f343d145d0e21279dc7137d8f49) fix: update close modal pattern
- [`901c3e5`](https://github.com/advplyr/audiobookshelf-app/commit/901c3e58072f07dc715c3ca2fc21df54297ad9a2) feat: add queue button for items and episodes
- [`37d8d47`](https://github.com/advplyr/audiobookshelf-app/commit/37d8d4748f51a860d3533e0d0e4d364cf945a774) feat: add queue button to latest podcast episodes
- [`ee82ad7`](https://github.com/advplyr/audiobookshelf-app/commit/ee82ad7c898c079c88d4f8f73c6dd7a1d347ef36) feat: allow queue access from the side drawer
- [`1ff116d`](https://github.com/advplyr/audiobookshelf-app/commit/1ff116d08bfaf8479663ad4ccf44821e24cf51ea) feat: preserve playback progress when adding items to queue
- [`c6d547b`](https://github.com/advplyr/audiobookshelf-app/commit/c6d547bc35d131f96e00314059852f0f8af20df3) fix: use correct images for queue items
- [`74ba76f`](https://github.com/advplyr/audiobookshelf-app/commit/74ba76ffe81d906f1db413b1a99eea2187eed018) fix: queue modal button functionality and drag interactions
### 📊 Changes
**19 files changed** (+845 additions, -21 deletions)
<details>
<summary>View changed files</summary>
➕ `android/app/src/main/java/com/audiobookshelf/app/data/PlaybackQueueItem.kt` (+62 -0)
➕ `android/app/src/main/java/com/audiobookshelf/app/managers/QueueManager.kt` (+74 -0)
📝 `android/app/src/main/java/com/audiobookshelf/app/media/MediaProgressSyncer.kt` (+57 -8)
📝 `android/app/src/main/java/com/audiobookshelf/app/player/AbMediaDescriptionAdapter.kt` (+5 -0)
📝 `android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt` (+99 -3)
📝 `android/app/src/main/java/com/audiobookshelf/app/plugins/AbsAudioPlayer.kt` (+4 -0)
📝 `components/app/AudioPlayer.vue` (+13 -3)
📝 `components/app/SideDrawer.vue` (+10 -0)
📝 `components/modals/ItemMoreMenuModal.vue` (+22 -2)
➕ `components/modals/QueueModal.vue` (+187 -0)
📝 `components/tables/podcast/EpisodeRow.vue` (+22 -1)
📝 `components/tables/podcast/LatestEpisodeRow.vue` (+20 -1)
📝 `layouts/default.vue` (+4 -0)
➕ `mixins/queueMixin.js` (+58 -0)
📝 `pages/item/_id/_episode/index.vue` (+19 -1)
📝 `plugins/localStore.js` (+19 -0)
📝 `store/globals.js` (+5 -1)
📝 `store/index.js` (+160 -1)
📝 `strings/en-us.json` (+5 -0)
</details>
### 📄 Description
## Summary
Adds a playback queue feature allowing users to line up items to play sequentially. The queue persists across sessions and is managed at both the JavaScript/Vue layer and the native Android layer for seamless background playback.
- **Queue management UI**: New `QueueModal` component with drag-to-reorder, play from position, and individual item removal. Accessible via a queue button in the audio player, side drawer, and item more menus
- **Add to queue**: Queue buttons added to item/episode more menus, podcast latest episode rows, and the episode detail page
- **Persistent storage**: Queue is saved to Capacitor `Preferences` (SharedPreferences on Android) and restored on app launch
- **Native Android auto-advance**: `QueueManager` reads the queue directly from SharedPreferences so the Android `PlayerNotificationService` can advance to the next item on playback completion, even when the app is backgrounded
- **Progress preservation**: Current playback position is saved in the queue item so playback resumes from where it left off
- **Native→JS sync**: When the native layer advances the queue, it emits `onQueueChanged` so the Vue store reloads from storage and stays in sync
## Changed files
- `store/index.js` — queue state, getters, mutations and actions
- `store/globals.js` — `showQueueModal` flag
- `plugins/localStore.js` — `getPlaybackQueue` / `setPlaybackQueue` methods
- `mixins/queueMixin.js` — shared `addItemToQueue` helper used across components
- `components/modals/QueueModal.vue` — queue management modal
- `components/app/AudioPlayer.vue` — queue button + `onQueueChanged` listener
- `components/app/SideDrawer.vue` — queue menu item
- `components/modals/ItemMoreMenuModal.vue` — "Add to Queue" action
- `components/tables/podcast/LatestEpisodeRow.vue` — queue button
- `pages/item/_id/_episode/index.vue` — "Add to Queue" action
- `layouts/default.vue` — mount `QueueModal`, load saved queue on init
- `android/.../data/PlaybackQueueItem.kt` — data model matching JS queue item structure
- `android/.../managers/QueueManager.kt` — reads/writes queue from SharedPreferences
- `android/.../media/MediaProgressSyncer.kt` — mark finished even when timer is stopped
- `android/.../player/PlayerNotificationService.kt` — native queue auto-advance on playback end
- `android/.../player/AbMediaDescriptionAdapter.kt` — `invalidateCache()` for cover art refresh
- `android/.../plugins/AbsAudioPlayer.kt` — `onQueueChanged` event bridge
- `strings/en-us.json` — `ButtonAddToQueue`, `ButtonClearQueue`, `LabelQueue`, `MessageQueueEmpty`
## Test plan
- [ ] Add an audiobook to the queue from the item more menu; verify it appears in the queue modal
- [ ] Add a podcast episode to the queue from the episode page and latest episodes list
- [ ] Drag items to reorder in the queue modal; verify order is persisted after closing and reopening
- [ ] Delete an item from the queue; verify it is removed
- [ ] Play an item from the queue modal while something is already playing; verify the current item is added back to the front of the queue
- [ ] Let playback finish; verify the next queue item starts automatically
- [ ] Force-close the app and reopen; verify the queue is restored from storage
- [ ] Test background auto-advance (screen off, app backgrounded) on Android
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/advplyr/audiobookshelf-app/pull/1704
Author: @andykelk
Created: 10/20/2025
Status: 🔄 Open
Base:
master← Head:feature/playback-queue📝 Commits (10+)
66903d1Add playback queue state management and strings3815f8dfeat: use device storage to persist queue89d6f79Add QueueModal component for queue management4347335fix: update close modal pattern901c3e5feat: add queue button for items and episodes37d8d47feat: add queue button to latest podcast episodesee82ad7feat: allow queue access from the side drawer1ff116dfeat: preserve playback progress when adding items to queuec6d547bfix: use correct images for queue items74ba76ffix: queue modal button functionality and drag interactions📊 Changes
19 files changed (+845 additions, -21 deletions)
View changed files
➕
android/app/src/main/java/com/audiobookshelf/app/data/PlaybackQueueItem.kt(+62 -0)➕
android/app/src/main/java/com/audiobookshelf/app/managers/QueueManager.kt(+74 -0)📝
android/app/src/main/java/com/audiobookshelf/app/media/MediaProgressSyncer.kt(+57 -8)📝
android/app/src/main/java/com/audiobookshelf/app/player/AbMediaDescriptionAdapter.kt(+5 -0)📝
android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt(+99 -3)📝
android/app/src/main/java/com/audiobookshelf/app/plugins/AbsAudioPlayer.kt(+4 -0)📝
components/app/AudioPlayer.vue(+13 -3)📝
components/app/SideDrawer.vue(+10 -0)📝
components/modals/ItemMoreMenuModal.vue(+22 -2)➕
components/modals/QueueModal.vue(+187 -0)📝
components/tables/podcast/EpisodeRow.vue(+22 -1)📝
components/tables/podcast/LatestEpisodeRow.vue(+20 -1)📝
layouts/default.vue(+4 -0)➕
mixins/queueMixin.js(+58 -0)📝
pages/item/_id/_episode/index.vue(+19 -1)📝
plugins/localStore.js(+19 -0)📝
store/globals.js(+5 -1)📝
store/index.js(+160 -1)📝
strings/en-us.json(+5 -0)📄 Description
Summary
Adds a playback queue feature allowing users to line up items to play sequentially. The queue persists across sessions and is managed at both the JavaScript/Vue layer and the native Android layer for seamless background playback.
QueueModalcomponent with drag-to-reorder, play from position, and individual item removal. Accessible via a queue button in the audio player, side drawer, and item more menusPreferences(SharedPreferences on Android) and restored on app launchQueueManagerreads the queue directly from SharedPreferences so the AndroidPlayerNotificationServicecan advance to the next item on playback completion, even when the app is backgroundedonQueueChangedso the Vue store reloads from storage and stays in syncChanged files
store/index.js— queue state, getters, mutations and actionsstore/globals.js—showQueueModalflagplugins/localStore.js—getPlaybackQueue/setPlaybackQueuemethodsmixins/queueMixin.js— sharedaddItemToQueuehelper used across componentscomponents/modals/QueueModal.vue— queue management modalcomponents/app/AudioPlayer.vue— queue button +onQueueChangedlistenercomponents/app/SideDrawer.vue— queue menu itemcomponents/modals/ItemMoreMenuModal.vue— "Add to Queue" actioncomponents/tables/podcast/LatestEpisodeRow.vue— queue buttonpages/item/_id/_episode/index.vue— "Add to Queue" actionlayouts/default.vue— mountQueueModal, load saved queue on initandroid/.../data/PlaybackQueueItem.kt— data model matching JS queue item structureandroid/.../managers/QueueManager.kt— reads/writes queue from SharedPreferencesandroid/.../media/MediaProgressSyncer.kt— mark finished even when timer is stoppedandroid/.../player/PlayerNotificationService.kt— native queue auto-advance on playback endandroid/.../player/AbMediaDescriptionAdapter.kt—invalidateCache()for cover art refreshandroid/.../plugins/AbsAudioPlayer.kt—onQueueChangedevent bridgestrings/en-us.json—ButtonAddToQueue,ButtonClearQueue,LabelQueue,MessageQueueEmptyTest plan
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.