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

Feature Request: audiobookshelf server support #67

Open
gordallott opened this issue Feb 11, 2023 · 4 comments
Open

Feature Request: audiobookshelf server support #67

gordallott opened this issue Feb 11, 2023 · 4 comments
Labels
enhancement New feature or request needs info This issue did not provide enough information to reproduce

Comments

@gordallott
Copy link

Hey, I actually had ambitions of building this out myself but after an afternoon of trying and failing to get the garmin connect simulator functioning correctly on my linux machine... i give up. Hopefully just leaving this issue inspires someone else

Audiobookshelf github: https://github.com/advplyr/audiobookshelf is an audiobook/podcast focused server you can use to manage your audiobooks/podcasts. It's pretty good and getting better as it goes, having support in SubMusic for it as a backend would be fantastic. It's got the basic features (with some quirks)

They don't have a programmatically described API but it seems fairly stable and they have good documentation at least, functionally you need to login
and then you can get playlists

from there you can typically drill down into the podcast/audiobook chapters/episodes and get the files from them for downloading


so the quirks...

1, figuring out if a file has already been played is a bit weird, you can either call the progress api for every single library item which is not great for large playlists, or call the authorize API, which is what the webUI does. It contains a User object which contains the mediaProgress of every single piece of audio the signed in user has listened to, which can be manually cross referenced with library items

2, syncing media progress back to the server is a little weird, they want to you do create a local listening session, but I've not been able to figure out how. I think the mobile webapp does that, then you can sync the local listening session. The alternative is manually updating media progress which seems probably simpler

@memen45
Copy link
Owner

memen45 commented Feb 11, 2023

Hey,

Thanks for sharing your idea! It sounds very feasible.

For Linux: I could get the simulator working, although the network during sync mode is not working, so I still have to move to windows for testing. Don't try Eclipse studio though, Visual Studio Code with the plugin is much easier to get working!

With regards to the actual implementation:

  • first issue I see is there is no pagination in the API spec. Since the watch is quite limited in JSON response size, and podcasts typically have many many episodes, we need pagination in order to be able to receive anything at all. We could open an issue at their repo for this.
  • what is the playlist endpoint structure? Is it correct that each playlist consists of a list of books/episodes, and each episode or book contains a list of available audiofiles? That would mean it is quite easy to translate it to the current SubMusic playlist format.
  • progress update seems to be possible on LibraryItemId level, it requires some extra info to be recorded by the SubMusic Scrobble class, but should be quite simple.

Currently, SubMusic does not have an auto-removal feature (e.g. remove the episode once listened). Of course such a feature would be a nice addition, however, AudioBookShelf might remove played episodes/books from the playlist already? If so, we could just sync the progress (the Scrobble), then request the playlist endpoint to retrieve the new playlist.

Also something to take into account: podcasts and audiobooks typically have quite large files, so maybe it is nice to add some memory check before trying to download all files from the playlist.

Hope this helps!

@gordallott
Copy link
Author

first issue I see is there is no pagination in the API spec

yeah, that seems like a big issue. the mechanism for getting progress on individual episodes being either a giant json blob of progress of all episodes or having to hit an api call for every episode progress info is needed for both is a little problematic here too. Looking at my local setup with a podcast that has 500~ episodes produces around 2MB of json. I opened advplyr/audiobookshelf/issues/1504 - maybe we'll have some positive movement around that - it's hard to see a device handling larger libraries/playlists well without pagination

what is the playlist endpoint structure? Is it correct that each playlist consists of a list of books/episodes

yeah basically, with this example json for an audiobook, you can see that you eventually drill down into a set of tracks that have a contentUrl property where the file is accessible,

example json
{
  "id": "pl_qbwet64998s5ra6dcu",
  "libraryId": "lib_c1u6t4p45c35rf0nzd",
  "userId": "root",
  "name": "Favorites",
  "description": null,
  "coverPath": null,
  "items": [
    {
      "libraryItemId": "li_8gch9ve09orgn4fdz8",
      "episodeId": null,
      "libraryItem": {
        "id": "li_8gch9ve09orgn4fdz8",
        "ino": "649641337522215266",
        "libraryId": "lib_c1u6t4p45c35rf0nzd",
        "folderId": "fol_bev1zuxhb0j0s1wehr",
        "path": "/audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule",
        "relPath": "Terry Goodkind/Sword of Truth/Wizards First Rule",
        "isFile": false,
        "mtimeMs": 1650621074299,
        "ctimeMs": 1650621074299,
        "birthtimeMs": 0,
        "addedAt": 1650621073750,
        "updatedAt": 1650621110769,
        "lastScan": 1651830827825,
        "scanVersion": "2.0.21",
        "isMissing": false,
        "isInvalid": false,
        "mediaType": "book",
        "media": {
          "libraryItemId": "li_8gch9ve09orgn4fdz8",
          "metadata": {
            "title": "Wizards First Rule",
            "titleIgnorePrefix": "Wizards First Rule",
            "subtitle": null,
            "authors": [
              {
                "id": "aut_z3leimgybl7uf3y4ab",
                "name": "Terry Goodkind"
              }
            ],
            "narrators": [
              "Sam Tsoutsouvas"
            ],
            "series": [
              {
                "id": "ser_cabkj4jeu8be3rap4g",
                "name": "Sword of Truth",
                "sequence": "1"
              }
            ],
            "genres": [
              "Fantasy"
            ],
            "publishedYear": "2008",
            "publishedDate": null,
            "publisher": "Brilliance Audio",
            "description": "The masterpiece that started Terry Goodkind's New York Times bestselling epic Sword of Truth In the aftermath of the brutal murder of his father, a mysterious woman, Kahlan Amnell, appears in Richard Cypher's forest sanctuary seeking help...and more. His world, his very beliefs, are shattered when ancient debts come due with thundering violence. In a dark age it takes courage to live, and more than mere courage to challenge those who hold dominion, Richard and Kahlan must take up that challenge or become the next victims. Beyond awaits a bewitching land where even the best of their hearts could betray them. Yet, Richard fears nothing so much as what secrets his sword might reveal about his own soul. Falling in love would destroy them - for reasons Richard can't imagine and Kahlan dare not say. In their darkest hour, hunted relentlessly, tormented by treachery and loss, Kahlan calls upon Richard to reach beyond his sword - to invoke within himself something more noble. Neither knows that the rules of battle have just changed...or that their time has run out. Wizard's First Rule is the beginning. One book. One Rule. Witness the birth of a legend.",
            "isbn": null,
            "asin": "B002V0QK4C",
            "language": null,
            "explicit": false,
            "authorName": "Terry Goodkind",
            "authorNameLF": "Goodkind, Terry",
            "narratorName": "Sam Tsoutsouvas",
            "seriesName": "Sword of Truth"
          },
          "coverPath": "/audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule/cover.jpg",
          "tags": [
            "Favorite"
          ],
          "audioFiles": [
            {
              "index": 1,
              "ino": "649644248522215260",
              "metadata": {
                "filename": "Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
                "ext": ".mp3",
                "path": "/audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule/Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
                "relPath": "Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
                "size": 48037888,
                "mtimeMs": 1632223180278,
                "ctimeMs": 1645978261001,
                "birthtimeMs": 0
              },
              "addedAt": 1650621074131,
              "updatedAt": 1651830828023,
              "trackNumFromMeta": 1,
              "discNumFromMeta": null,
              "trackNumFromFilename": 1,
              "discNumFromFilename": null,
              "manuallyVerified": false,
              "invalid": false,
              "exclude": false,
              "error": null,
              "format": "MP2/3 (MPEG audio layer 2/3)",
              "duration": 6004.6675,
              "bitRate": 64000,
              "language": null,
              "codec": "mp3",
              "timeBase": "1/14112000",
              "channels": 2,
              "channelLayout": "stereo",
              "chapters": [],
              "embeddedCoverArt": null,
              "metaTags": {
                "tagAlbum": "SOT Bk01",
                "tagArtist": "Terry Goodkind",
                "tagGenre": "Audiobook Fantasy",
                "tagTitle": "Wizards First Rule 01",
                "tagTrack": "01/20",
                "tagAlbumArtist": "Terry Goodkind",
                "tagComposer": "Terry Goodkind"
              },
              "mimeType": "audio/mpeg"
            },
            {
              "index": 2,
              "ino": "649644248522215261",
              "metadata": {
                "filename": "Terry Goodkind - SOT Bk01 - Wizards First Rule 02.mp3",
                "ext": ".mp3",
                "path": "/audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule/Terry Goodkind - SOT Bk01 - Wizards First Rule 02.mp3",
                "relPath": "Terry Goodkind - SOT Bk01 - Wizards First Rule 02.mp3",
                "size": 47972352,
                "mtimeMs": 1632223180281,
                "ctimeMs": 1645978261001,
                "birthtimeMs": 0
              },
              "addedAt": 1650621074130,
              "updatedAt": 1651830828023,
              "trackNumFromMeta": 2,
              "discNumFromMeta": null,
              "trackNumFromFilename": 1,
              "discNumFromFilename": null,
              "manuallyVerified": false,
              "invalid": false,
              "exclude": false,
              "error": null,
              "format": "MP2/3 (MPEG audio layer 2/3)",
              "duration": 5996.2785,
              "bitRate": 64000,
              "language": null,
              "codec": "mp3",
              "timeBase": "1/14112000",
              "channels": 2,
              "channelLayout": "stereo",
              "chapters": [],
              "embeddedCoverArt": null,
              "metaTags": {
                "tagAlbum": "SOT Bk01",
                "tagArtist": "Terry Goodkind",
                "tagGenre": "Audiobook Fantasy",
                "tagTitle": "Wizards First Rule 02",
                "tagTrack": "02/20",
                "tagAlbumArtist": "Terry Goodkind",
                "tagComposer": "Terry Goodkind"
              },
              "mimeType": "audio/mpeg"
            }
          ],
          "chapters": [
            {
              "id": 0,
              "start": 0,
              "end": 6004.6675,
              "title": "Terry Goodkind - SOT Bk01 - Wizards First Rule 01"
            },
            {
              "id": 1,
              "start": 6004.6675,
              "end": 12000.946,
              "title": "Terry Goodkind - SOT Bk01 - Wizards First Rule 02"
            }
          ],
          "duration": 33854.905,
          "size": 268824228,
          "tracks": [
            {
              "index": 1,
              "startOffset": 0,
              "duration": 6004.6675,
              "title": "Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
              "contentUrl": "/s/item/li_8gch9ve09orgn4fdz8/Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
              "mimeType": "audio/mpeg",
              "metadata": {
                "filename": "Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
                "ext": ".mp3",
                "path": "/audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule/Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
                "relPath": "Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
                "size": 48037888,
                "mtimeMs": 1632223180278,
                "ctimeMs": 1645978261001,
                "birthtimeMs": 0
              }
            },
            {
              "index": 2,
              "startOffset": 6004.6675,
              "duration": 5996.2785,
              "title": "Terry Goodkind - SOT Bk01 - Wizards First Rule 02.mp3",
              "contentUrl": "/s/item/li_8gch9ve09orgn4fdz8/Terry Goodkind - SOT Bk01 - Wizards First Rule 02.mp3",
              "mimeType": "audio/mpeg",
              "metadata": {
                "filename": "Terry Goodkind - SOT Bk01 - Wizards First Rule 02.mp3",
                "ext": ".mp3",
                "path": "/audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule/Terry Goodkind - SOT Bk01 - Wizards First Rule 02.mp3",
                "relPath": "Terry Goodkind - SOT Bk01 - Wizards First Rule 03.mp3",
                "size": 47972352,
                "mtimeMs": 1632223180281,
                "ctimeMs": 1645978261001,
                "birthtimeMs": 0
              }
            }
          ],
          "missingParts": [],
          "ebookFile": null
        },
        "libraryFiles": [
          {
            "ino": "649644248522215260",
            "metadata": {
              "filename": "Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
              "ext": ".mp3",
              "path": "/audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule/Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
              "relPath": "Terry Goodkind - SOT Bk01 - Wizards First Rule 01.mp3",
              "size": 48037888,
              "mtimeMs": 1632223180278,
              "ctimeMs": 1645978261001,
              "birthtimeMs": 0
            },
            "addedAt": 1650621052494,
            "updatedAt": 1650621052494,
            "fileType": "audio"
          },
          {
            "ino": "649644248522215261",
            "metadata": {
              "filename": "Terry Goodkind - SOT Bk01 - Wizards First Rule 02.mp3",
              "ext": ".mp3",
              "path": "/audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule/Terry Goodkind - SOT Bk01 - Wizards First Rule 02.mp3",
              "relPath": "Terry Goodkind - SOT Bk01 - Wizards First Rule 02.mp3",
              "size": 47972352,
              "mtimeMs": 1632223180281,
              "ctimeMs": 1645978261001,
              "birthtimeMs": 0
            },
            "addedAt": 1650621052494,
            "updatedAt": 1650621052494,
            "fileType": "audio"
          },
          {
            "ino": "649644248522215267",
            "metadata": {
              "filename": "cover.jpg",
              "ext": ".jpg",
              "path": "/audiobooks/Terry Goodkind/Sword of Truth/Wizards First Rule/cover.jpg",
              "relPath": "cover.jpg",
              "size": 325531,
              "mtimeMs": 1638754803540,
              "ctimeMs": 1645978261003,
              "birthtimeMs": 0
            },
            "addedAt": 1650621052495,
            "updatedAt": 1650621052495,
            "fileType": "image"
          }
        ],
        "size": 268990279
      }
    }
  ],
  "lastUpdate": 1669623431313,
  "createdAt": 1669623431313
}

I'm not 100% on how they handle transcoding, I know that's something that audiobookshelf does but it's not obvious from the API

@memen45
Copy link
Owner

memen45 commented Feb 14, 2023

Transcoding is not a strict requirement, we could also filter available audio files based on content type.

Based on the response in your referenced issue, the libraryItems can be retrieved by page. That may or may not be sufficient depending on what endpoint is used to obtain the playlists. Below the complete table of features that require implementation:

provider method AudioBookshelf api description Content-Type
ping /status returns an object with server status application/json
recordPlay ? submit a play application/json text/html
getAllPlaylists /playlists too large response size? or /libraries/{id}/playlists returns array of all playlists available for the user application/json
getPlaylist /libraries/{id} returns an array of one playlist object with {id} application/json
getPlaylistSongs /libraries/{id}/items returns an array of songs on the playlist with {id} application/json
getRefId ? returns a refId for a song by {id} (this downloads the song) audio/*
getArtwork {coverPath} ? returns a BitmapResource for a song id image/*
getAllPodcasts /libraries where mediaType: podcast returns array of all podcasts available for the user application/json
getPodcast /libraries/{id} returns a podcast object with {id} application/json
getEpisodes /libraries/{id}/items or /libraries/{id}/recent-episodes returns array of episodes for podcast with {id} application/json

Not sure what series, playlists and collections mean in the AudioBookshelf api?

@memen45 memen45 added enhancement New feature or request needs info This issue did not provide enough information to reproduce labels May 28, 2023
@anzenketh
Copy link

Series, Playlists, and Collections are just ways that multiple books can be collected together.

  • Series are actually set in the metadata of the book itself. It is referencing a book series. Say for example the Harry Potter Series.
  • A collection is a playlist of books. But only used for books. But does not have to be the same series.
  • A playlist is primary used for podcast episodes.

See the following on Collection or playlists https://www.audiobookshelf.org/guides/collections/

One thing to keep in mind is Audio Book shelf works based on Libraries. There is frequently two libraries. One for Audio Books and one for Podcasts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request needs info This issue did not provide enough information to reproduce
Projects
None yet
Development

No branches or pull requests

3 participants