Skip to content

feat: Add native podcast episode bookmarking#5173

Open
johneliott wants to merge 1 commit intoadvplyr:masterfrom
johneliott:feature/podcast-bookmarks
Open

feat: Add native podcast episode bookmarking#5173
johneliott wants to merge 1 commit intoadvplyr:masterfrom
johneliott:feature/podcast-bookmarks

Conversation

@johneliott
Copy link
Copy Markdown

@johneliott johneliott commented Apr 8, 2026

Resolves #884

Changes

1. Backend Database Model (server/models/User.js)

  • Modified the createBookmark, updateBookmark, removeBookmark, and findBookmark methods to accept an optional episodeId parameter.
  • Instead of migrating every user's old JSON blob or making a massive breaking schema change, the system now simply attaches episodeId to the JSON payload for new podcast bookmarks while seamlessly falling back to standard libraryItemId behavior for audiobooks (or legacy data).

2. Backend API Endpoints (server/controllers/MeController.js)

  • Updated POST /api/me/item/:id/bookmark and PATCH /api/me/item/:id/bookmark to parse episodeId directly from the req.body.
  • Updated DELETE /api/me/item/:id/bookmark/:time to accept an episodeId via a query parameter (req.query.episode).

3. Frontend Vuex Store (client/store/user.js)

  • Updated the global state getter getUserBookmarksForItem to take an optional episodeId. If provided, it filters out all bookmarks that belong to different episodes, ensuring you only see the bookmarks for the episode you are currently listening to.

4. Frontend UI Components (client/components/...)

  • PlayerUi.vue: Removed the !isPodcast block that artificially hid the bookmark icon when playing a podcast.
  • MediaPlayerContainer.vue: Passed the active $store.state.streamEpisodeId down into the computed bookmarks() list and into the <modals-bookmarks-modal> prop.
  • BookmarksModal.vue: Added the episodeId prop. When creating a new bookmark, it injects the episodeId into the POST body. When deleting, it intelligently appends ?episode=${this.episodeId} to the DELETE URL.

Automated Test Plan: Podcast Episode Bookmarks

Prerequisites

  • A running instance of Audiobookshelf (e.g., http://localhost:13380).
  • A valid user authentication token (<AUTH_TOKEN>).
  • A Library Item ID representing a podcast (bc57c7f0-08dc-4b7e-b570-2268dd7d6cc2).
  • An Episode ID belonging to that podcast (09690803-ae60-4b25-8f7d-f2bcccd6bb83).

Step 1: Create a Bookmark

Command:

curl -s -X POST \
  -H "Authorization: Bearer <AUTH_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"time": 420, "title": "Ziva Cooper talking about THC receptors", "episodeId": "09690803-ae60-4b25-8f7d-f2bcccd6bb83"}' \
  http://localhost:13380/api/me/item/bc57c7f0-08dc-4b7e-b570-2268dd7d6cc2/bookmark

Response:

{
  "libraryItemId": "bc57c7f0-08dc-4b7e-b570-2268dd7d6cc2",
  "time": 420,
  "title": "Ziva Cooper talking about THC receptors",
  "createdAt": 1775677075715,
  "episodeId": "09690803-ae60-4b25-8f7d-f2bcccd6bb83"
}

Step 2: Verify the Bookmark (Read)

Command:

curl -s -H "Authorization: Bearer <AUTH_TOKEN>" http://localhost:13380/api/me | grep "Ziva Cooper talking about THC receptors"

Response:

{
  "libraryItemId": "bc57c7f0-08dc-4b7e-b570-2268dd7d6cc2",
  "time": 420,
  "title": "Ziva Cooper talking about THC receptors",
  "createdAt": 1775677075715,
  "episodeId": "09690803-ae60-4b25-8f7d-f2bcccd6bb83"
}

Step 3: Update the Bookmark

Command:

curl -s -X PATCH \
  -H "Authorization: Bearer <AUTH_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"time": 420, "title": "UPDATED: Ziva Cooper on THC vs CBD receptors", "episodeId": "09690803-ae60-4b25-8f7d-f2bcccd6bb83"}' \
  http://localhost:13380/api/me/item/bc57c7f0-08dc-4b7e-b570-2268dd7d6cc2/bookmark

Response:

{
  "libraryItemId": "bc57c7f0-08dc-4b7e-b570-2268dd7d6cc2",
  "time": 420,
  "title": "UPDATED: Ziva Cooper on THC vs CBD receptors",
  "createdAt": 1775677075715,
  "episodeId": "09690803-ae60-4b25-8f7d-f2bcccd6bb83"
}

Step 4: Delete the Bookmark

Command:

curl -s -X DELETE \
  -H "Authorization: Bearer <AUTH_TOKEN>" \
  "http://localhost:13380/api/me/item/bc57c7f0-08dc-4b7e-b570-2268dd7d6cc2/bookmark/420?episode=09690803-ae60-4b25-8f7d-f2bcccd6bb83"

Response:

OK

Step 5: Validate Error & Warning Logging

This step ensures that the server correctly validates input and logs issues as expected.

5.1 Trigger "Invalid Time" Error

  • Command: curl -s -X POST -H "Authorization: Bearer <AUTH_TOKEN>" -H "Content-Type: application/json" -d '{"title": "Missing Time"}' http://localhost:13380/api/me/item/bc57c7f0-08dc-4b7e-b570-2268dd7d6cc2/bookmark
  • Response: ERROR: [MeController] createBookmark invalid time undefined

5.2 Trigger "Invalid Title" Error

  • Command: curl -s -X POST -H "Authorization: Bearer <AUTH_TOKEN>" -H "Content-Type: application/json" -d '{"time": 123}' http://localhost:13380/api/me/item/bc57c7f0-08dc-4b7e-b570-2268dd7d6cc2/bookmark
  • Response: ERROR: [MeController] createBookmark invalid title undefined

5.3 Trigger "Already Exists" Warning

  • Command: curl -s -X POST ... (Duplicate attempt)
  • Response: WARN: [User] Create Bookmark already exists for this time

5.4 Trigger "Update Not Found" Error

  • Command: curl -s -X PATCH ... (Time 9999)
  • Response: ERROR: [User] updateBookmark not found

5.5 Trigger "Remove Not Found" Error

  • Command: curl -s -X DELETE ... (Time 9999)
  • Response: ERROR: [MeController] removeBookmark not found

Manual UI Verification Plan

1. Preparation
Ensure the Audiobookshelf server is running and you are logged in to the web interface.

2. Verify UI Visibility

  • Navigate to a Podcast: Open any podcast series in your library.
  • Start Playback: Click play on any episode.
  • Check the Player: Look at the player controls (usually at the bottom or in the full-screen player).
  • Result: The Bookmark icon (looks like a bookmark ribbon) should now be visible. Previously, this was hidden for all podcasts.

3. Test Bookmark Creation

  • Seek to a timestamp: Move the playhead to a specific spot (e.g., 01:23).
  • Click the Bookmark icon: A modal should appear.
  • Save the Bookmark: Enter a title (or use the default) and click save.
  • Result: The bookmark should be saved. You can verify this by clicking the Bookmark icon again; your saved bookmark should appear in the list.

4. Test Episode Isolation (The Core Fix)
This verifies that bookmarks are now tied to specific episodes rather than the whole podcast series:

  • Switch Episodes: Stop playing Episode A and start playing Episode B from the same podcast.
  • Open Bookmarks: Click the Bookmark icon in the player.
  • Result: The list should be empty (or only show bookmarks created for Episode B). You should not see the bookmark you just created for Episode A.
  • Switch Back: Go back to Episode A.
  • Result: Your original bookmark at 01:23 should reappear.

5. Test Deletion

  • Delete the bookmark: Open the bookmarks list for Episode A and click the "Delete" (trash can) icon.
  • Result: The bookmark should be removed successfully.
Screenshot 2026-04-08 at 12 29 32 PM Screenshot 2026-04-08 at 12 30 13 PM Screenshot 2026-04-08 at 12 30 44 PM Screenshot 2026-04-08 at 12 31 59 PM Screenshot 2026-04-08 at 12 32 17 PM Screenshot 2026-04-08 at 12 32 46 PM Screenshot 2026-04-08 at 12 32 58 PM Screenshot 2026-04-08 at 12 33 18 PM Screenshot 2026-04-08 at 12 33 29 PM Screenshot 2026-04-08 at 12 35 07 PM Screenshot 2026-04-08 at 12 35 26 PM Screenshot 2026-04-08 at 12 35 45 PM

@nichwall
Copy link
Copy Markdown
Contributor

I have not read through the PR code yet, but we are not reviewing AI generated PRs at this time due to an increasing number of low-quality AI PRs. Someone else may take a look at it but I am not planning to review it at this time.

@johneliott
Copy link
Copy Markdown
Author

@nichwall don't fret, this was entirely my effort.

@nichwall
Copy link
Copy Markdown
Contributor

nichwall commented Apr 13, 2026

@nichwall don't fret, this was entirely my effort.

The PR description looks like a verbatim copy paste from an AI agent or chat, so that doesn't inspire confidence when first looking at a PR. I'll try and take a look at the code later, but it'll probably be a few days before I can.

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

Successfully merging this pull request may close these issues.

[Enhancement]: Bookmarks in Podcasts

2 participants