From b42fcf26fab6c20a8bc6eb530da53f96ebd53bc8 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sat, 7 Sep 2024 19:46:30 -0700 Subject: [PATCH 01/24] Initial proposed new `/libraries/{id}/items` routes --- docs/controllers/LibraryController.yaml | 399 ++++++++++++++++++++++++ 1 file changed, 399 insertions(+) diff --git a/docs/controllers/LibraryController.yaml b/docs/controllers/LibraryController.yaml index b985c05ecd..8c3f2bf845 100644 --- a/docs/controllers/LibraryController.yaml +++ b/docs/controllers/LibraryController.yaml @@ -57,6 +57,137 @@ components: description: The fields to include in the response. The only current option is `rssfeed`. type: string example: 'rssfeed' + libraryBook: + type: object + description: A book object used for displaying items in a library. + properties: + bookId: + $ref: '../objects/LibraryItem.yaml#/components/schemas/libraryItemId' + coverPath: + $ref: '../objects/mediaTypes/Book.yaml#/components/schemas/bookCoverPath' + title: + type: string + description: The title of the book + example: 'The Way of Kings' + subtitle: + type: string + description: The subtitle of the book + example: 'A Stormlight Archive Novel' + authors: + type: array + description: The authors of the book + items: + type: string + example: ['Brandon Sanderson'] + duration: + type: integer + nullable: true + description: The duration of the book in seconds. Will be null if the book is an ebook. + example: 123456 + size: + type: integer + description: The size of the book with all files in bytes. + example: 987654 + hasEbook: + type: boolean + description: Whether the book has an ebook + example: true + hasAudio: + type: boolean + description: Whether the book has audio + example: true + hasRss: + type: boolean + description: Whether the book has an RSS feed open + example: false + explicit: + type: boolean + description: Whether the book is explicit + example: false + abridged: + type: boolean + description: Whether the book is abridged + example: false + extraInfo: + type: string + description: The extra info displayed when sorting or filtering. For example, the publish year. + example: '2010' + count: + type: integer + description: The number of books in the series when using the "Collapse Series" option. + example: 4 + progress: + type: number + description: The progress of the book as a percentage. Will be `1.0` if finished. + example: 0.23265 + libraryPodcast: + type: object + description: A podcast object used for displaying items in a library. + properties: + podcastId: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastId' + coverPath: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastCoverPath' + title: + type: string + description: The title of the podcast + example: 'The Daily' + author: + type: string + description: The author of the podcast + example: 'The New York Times' + explicit: + type: boolean + description: Whether the podcast is explicit + example: false + extraInfo: + type: string + description: The extra info displayed when sorting or filtering. For example, the publish year. + example: '2010' + count: + type: integer + description: The number of episodes in the podcast. + example: 50 + libraryPodcastEpisode: + type: object + description: A podcast episode object used for displaying episodes in a library. + properties: + episodeId: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/episodeId' + podcastId: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastId' + coverPath: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/episodeCoverPath' + title: + type: string + description: The title of the podcast episode + example: 'The Daily - October 1, 2021' + description: + type: string + description: The description of the podcast episode + example: 'The Daily is a podcast from The New York Times.' + seasonNumber: + type: integer + nullable: true + description: The season number of the podcast episode. + example: 1 + episodeNumber: + type: integer + nullable: true + description: The episode number of the podcast episode. + example: 1 + publishDate: + type: integer + description: The publish date of the podcast episode in ms since POSIX epoch. + example: 1633522963509 + duration: + type: integer + description: The duration of the podcast episode in seconds. + example: 1234 + progress: + type: number + description: The progress of the podcast episode as a percentage. Will be `1.0` if finished. + example: 0.23265 parameters: limit: in: query @@ -243,6 +374,274 @@ paths: example: 'Issues deleted.' '404': $ref: '#/components/responses/library404' + /api/libraries/{id}/books: + parameters: + - name: id + in: path + description: The ID of the library. + required: true + schema: + $ref: '../objects/Library.yaml#/components/schemas/libraryId' + get: + operationId: getLibraryBooks + summary: Get books in a library + description: Get books in a library by ID on server. Only available for libraries with mediaType `book`. + tags: + - Libraries + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/page' + - in: query + name: sort + description: The field to sort by from the request. + example: 'numBooks' + schema: + type: string + default: 'name' + - $ref: '#/components/parameters/desc' + - in: query + name: filter + description: The filter for the library. + example: 'media.metadata.title' + schema: + type: string + - in: query + name: collapseSeries + description: Whether to collapse series into a single cover + schema: + type: integer + default: 0 + responses: + '200': + description: getLibraryBooks OK + content: + application/json: + schema: + type: object + properties: + books: + type: array + description: The books returned from the library with only required fields for displaying in a library. + items: + $ref: '#/components/schemas/libraryBook' + total: + $ref: '../schemas.yaml#/components/schemas/total' + limit: + $ref: '../schemas.yaml#/components/schemas/limit' + page: + $ref: '../schemas.yaml#/components/schemas/page' + sortBy: + $ref: '#/components/schemas/sortBy' + sortDesc: + $ref: '#/components/schemas/sortDesc' + filterBy: + $ref: '#/components/schemas/filterBy' + collapseSeries: + $ref: '#/components/schemas/collapseSeries' + '403': + description: Library is not a book library. + content: + text/html: + schema: + type: string + example: Library is not a book library. + '404': + description: Library not found. + content: + text/html: + schema: + type: string + example: Library not found. + /api/libraries/{id}/podcasts: + parameters: + - name: id + in: path + description: The ID of the library. + required: true + schema: + $ref: '../objects/Library.yaml#/components/schemas/libraryId' + get: + operationId: getLibraryPodcasts + summary: Get podcasts in a library + description: Get podcasts in a library by ID on server. Only available for libraries with mediaType `podcast`. + tags: + - Libraries + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/page' + - in: query + name: sort + description: The field to sort by from the request. + example: 'numEpisodes' + schema: + type: string + default: 'numEpisodes' + - $ref: '#/components/parameters/desc' + - in: query + name: filter + description: The filter for the library. + example: 'media.metadata.title' + schema: + type: string + responses: + '200': + description: getLibraryPodcasts OK + content: + application/json: + schema: + type: object + properties: + podcasts: + type: array + description: The podcasts returned from the library with only required fields for displaying in a library. + items: + $ref: '#/components/schemas/libraryPodcast' + total: + $ref: '../schemas.yaml#/components/schemas/total' + limit: + $ref: '../schemas.yaml#/components/schemas/limit' + page: + $ref: '../schemas.yaml#/components/schemas/page' + sortBy: + $ref: '#/components/schemas/sortBy' + sortDesc: + $ref: '#/components/schemas/sortDesc' + filterBy: + $ref: '#/components/schemas/filterBy' + '403': + description: Library is not a podcast library. + content: + text/html: + schema: + type: string + example: Library is not a podcast library. + '404': + description: Library not found. + content: + text/html: + schema: + type: string + example: Library not found. + /api/libraries/{id}/podcast-episodes: + parameters: + - name: id + in: path + description: The ID of the library. + required: true + schema: + $ref: '../objects/Library.yaml#/components/schemas/libraryId' + get: + operationId: getLibraryPodcastEpisodes + summary: Get podcast episodes in a library + description: Get podcast episodes in a library by ID on server. Only available for libraries with mediaType `podcast`. + tags: + - Libraries + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/page' + - in: query + name: sort + description: The field to sort by from the request. + example: 'publishDate' + schema: + type: string + default: 'publishDate' + - $ref: '#/components/parameters/desc' + - in: query + name: filter + description: The filter for the library. + example: 'media.metadata.title' + schema: + type: string + responses: + '200': + description: getLibraryPodcastEpisodes OK + content: + application/json: + schema: + type: object + properties: + episodes: + type: array + description: The podcast episodes returned from the library with only required fields for displaying in a library. + items: + $ref: '#/components/schemas/libraryPodcastEpisode' + total: + $ref: '../schemas.yaml#/components/schemas/total' + limit: + $ref: '../schemas.yaml#/components/schemas/limit' + page: + $ref: '../schemas.yaml#/components/schemas/page' + sortBy: + $ref: '#/components/schemas/sortBy' + sortDesc: + $ref: '#/components/schemas/sortDesc' + filterBy: + $ref: '#/components/schemas/filterBy' + '403': + description: Library is not a podcast library. + content: + text/html: + schema: + type: string + example: Library is not a podcast library. + '404': + description: Library not found. + content: + text/html: + schema: + type: string + example: Library not found. + /api/libraries/{id}/recent-episodes: + parameters: + - name: id + in: path + description: The ID of the library. + required: true + schema: + $ref: '../objects/Library.yaml#/components/schemas/libraryId' + get: + operationId: getLibraryRecentEpisodes + summary: Get recent episodes in a library + description: Get recent episodes in a library by ID on server. Only available for libraries with mediaType `podcast`. + tags: + - Libraries + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/page' + responses: + '200': + description: getLibraryRecentEpisodes OK + content: + application/json: + schema: + type: object + properties: + episodes: + type: array + description: The recent podcast episodes returned from the library with only required fields for displaying in a library. + items: + $ref: '#/components/schemas/libraryPodcastEpisode' + total: + $ref: '../schemas.yaml#/components/schemas/total' + limit: + $ref: '../schemas.yaml#/components/schemas/limit' + page: + $ref: '../schemas.yaml#/components/schemas/page' + '403': + description: Library is not a podcast library. + content: + text/html: + schema: + type: string + example: Library is not a podcast library. + '404': + description: Library not found. + content: + text/html: + schema: + type: string + example: Library not found. /api/libraries/{id}/items: parameters: - name: id From 9cc3caaaeda6de193eeca00039d5c81f9d048105 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Tue, 10 Sep 2024 20:06:21 -0700 Subject: [PATCH 02/24] Initial: new root spec for all endpoints --- docs/newRoot.yaml | 754 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 754 insertions(+) create mode 100644 docs/newRoot.yaml diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml new file mode 100644 index 0000000000..a37af0820b --- /dev/null +++ b/docs/newRoot.yaml @@ -0,0 +1,754 @@ +openapi: 3.0.0 +info: + title: Audiobookshelf API + version: 0.2.0 + description: Audiobookshelf API with autogenerated OpenAPI doc + license: + name: GPL-3.0 + url: 'https://www.gnu.org/licenses/gpl-3.0.html' +servers: + - url: http://abs.mydomain.com:13378 + description: Development server +security: + - BearerAuth: [] +components: + securitySchemes: + BearerAuth: + description: Bearer authentication + type: http + scheme: bearer + schemas: + itemId: + type: string + description: A unique ID for the item. This ID is unique across all tables. + format: uuid + duration: + type: number + description: The length of the item in seconds. Will be 0 if no audio is associated with the item. + bitrate: + type: number + description: The bitrate of the audio file. + progress: + type: number + description: The user's progress through the media. Will be 1.0 if completed. + nullable: true + size: + type: integer + description: The size of the item in bytes. + title: + type: string + description: The title of the item. + titleNullable: + type: string + description: The title of the item. + nullable: true + subtitle: + type: string + description: The subtitle of the item. + nullable: true + description: + type: string + description: The description of the item. + nullable: true + isExplicit: + type: boolean + description: Whether the item contains explicit content. + isAbridged: + type: boolean + description: Whether the item is abridged. + seriesSequence: + type: object + description: The series name and sequence number of the item. + properties: + seriesId: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + sequence: + type: string + description: The sequence number of the item in the series. If the item does not have a specific sequence, this will be null. + nullable: true + seriesSequenceArray: + type: array + description: An array of series names and sequence numbers associated with the item. + nullable: true + items: + $ref: '#/components/schemas/seriesSequence' + imagePath: + type: string + description: The absolute path of the image on the server. Null if no image is associated with the item. + nullable: true + filePath: + type: string + description: The absolute path of the file on the server. + bookPerson: + type: object + description: A person associated with a book. + properties: + personId: + $ref: '#/components/schemas/itemId' + name: + type: string + description: The name of the person. + authorObjectArray: + type: array + description: An array of author objects associated with a book. + nullable: true + items: + $ref: '#/components/schemas/bookPerson' + authorNameArray: + type: array + description: An array of author names associated with a book. + nullable: true + items: + type: string + narratorObjectArray: + type: array + description: An array of narrator objects associated with a book. + nullable: true + items: + $ref: '#/components/schemas/bookPerson' + narratorNameArray: + type: array + description: An array of narrator names associated with a book. + nullable: true + items: + type: string + publisher: + type: string + description: The publisher of the book. + nullable: true + publishYear: + type: integer + description: The year the book was published. + nullable: true + isbn: + type: string + description: The ISBN of the book. + nullable: true + asin: + type: string + description: The ASIN of the book. + nullable: true + genre: + type: string + description: The genre of the item. + nullable: true + genreArray: + type: array + description: An array of genres associated with the item. + nullable: true + items: + $ref: '#/components/schemas/genre' + tag: + type: string + description: The tag of the item. + nullable: true + tagArray: + type: array + description: An array of tags associated with the item. + nullable: true + items: + $ref: '#/components/schemas/tag' + language: + type: string + description: The language of the item. + nullable: true + bookChapter: + type: object + description: A chapter in a book. + properties: + title: + type: string + description: The title of the chapter. + start: + type: number + description: The start time of the chapter in seconds. + end: + type: number + description: The end time of the chapter in seconds. + chapterArray: + type: array + description: An array of chapters in a book. Will be null if the item has no chapters. + nullable: true + items: + $ref: '#/components/schemas/bookChapter' + fileType: + type: string + description: The type of file. + enum: ['audio', 'image', 'ebook', 'metadata'] + file: + type: object + description: Any media or image file associated with a book or podcast. + properties: + fileId: + $ref: '#/components/schemas/itemId' + path: + $ref: '#/components/schemas/filePath' + fileName: + type: string + description: The name of the file. + size: + $ref: '#/components/schemas/size' + duration: + $ref: '#/components/schemas/duration' + fileType: + $ref: '#/components/schemas/fileType' + audioFileCodec: + type: string + description: The codec of an audio file. + audioTrack: + type: object + description: An audio track associated with a book. + properties: + fileId: + $ref: '#/components/schemas/itemId' + path: + $ref: '#/components/schemas/filePath' + fileName: + type: string + description: The name of the file. + size: + $ref: '#/components/schemas/size' + duration: + $ref: '#/components/schemas/duration' + codec: + $ref: '#/components/schemas/audioFileCodec' + bitrate: + $ref: '#/components/schemas/bitrate' + ebookFile: + type: object + description: An ebook file associated with a book. + properties: + fileId: + $ref: '#/components/schemas/itemId' + path: + $ref: '#/components/schemas/filePath' + fileName: + type: string + description: The name of the file. + isPrimary: + type: boolean + description: Whether the file is the primary ebook file. + size: + $ref: '#/components/schemas/size' + bookObject: + type: object + description: Information about the book and its audio files. + properties: + coverPath: + $ref: '#/components/schemas/imagePath' + title: + $ref: '#/components/schemas/title' + subtitle: + $ref: '#/components/schemas/subtitle' + authors: + $ref: '#/components/schemas/authorObjectArray' + narrators: + $ref: '#/components/schemas/narratorObjectArray' + description: + $ref: '#/components/schemas/description' + genres: + $ref: '#/components/schemas/genreArray' + tags: + $ref: '#/components/schemas/tagArray' + series: + $ref: '#/components/schemas/seriesSequenceArray' + publishYear: + $ref: '#/components/schemas/publishYear' + publisher: + $ref: '#/components/schemas/publisher' + isbn: + $ref: '#/components/schemas/isbn' + asin: + $ref: '#/components/schemas/asin' + language: + $ref: '#/components/schemas/language' + explicit: + $ref: '#/components/schemas/isExplicit' + abridged: + $ref: '#/components/schemas/isAbridged' + chapters: + $ref: '#/components/schemas/chapterArray' + files: + type: array + description: All files associated with the book. + items: + $ref: '#/components/schemas/file' + audioTracks: + type: array + nullable: true + description: The audio tracks of the book. Will be null if the book is an ebook. + items: + $ref: '#/components/schemas/audioTrack' + ebookFiles: + type: array + nullable: true + description: The ebook files of the book. Will be null if no ebooks are associated with the book. + items: + $ref: '#/components/schemas/ebookFile' + progress: + $ref: '#/components/schemas/progress' + duration: + $ref: '#/components/schemas/duration' + size: + $ref: '#/components/schemas/size' + responses: + badRequest: + description: Bad request. + content: + text/html: + schema: + type: string + example: Bad request. + forbidden: + description: Forbidden. + content: + text/html: + schema: + type: string + example: Forbidden. + notFound: + description: Item not found. + content: + text/html: + schema: + type: string + example: Item not found. +tags: + - name: Book + description: Book endpoints +paths: + /api/book/{id}: + parameters: + - name: id + in: path + required: true + description: The ID of the book. + schema: + type: string + format: uuid + get: + operationId: getBookById + summary: Get book by ID + description: Get a book by its ID. This endpoint returns all of the information needed for the book details page and editing. + tags: + - Book + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookObject' + '404': + $ref: '#/components/responses/notFound' + post: + operationId: updateBookById + summary: Update book by ID + description: Update a book by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Book + requestBody: + content: + application/json: + schema: + type: object + properties: + title: + $ref: '#/components/schemas/titleNullable' + subtitle: + $ref: '#/components/schemas/subtitle' + authors: + $ref: '#/components/schemas/authorNameArray' + narrators: + $ref: '#/components/schemas/narratorNameArray' + description: + $ref: '#/components/schemas/description' + genres: + $ref: '#/components/schemas/genreArray' + tags: + $ref: '#/components/schemas/tagArray' + series: + $ref: '#/components/schemas/seriesSequenceArray' + publishYear: + $ref: '#/components/schemas/publishYear' + publisher: + $ref: '#/components/schemas/publisher' + isbn: + $ref: '#/components/schemas/isbn' + asin: + $ref: '#/components/schemas/asin' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteBookById + summary: Remove book by ID + description: Remove the book and associated entries from the database. This does not delete any files from the filesystem. If files should be deleted, use the `/api/book/{id}/hardDelete` endpoint instead. + tags: + - Book + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/book/{id}/hardDelete: + parameters: + - name: id + in: path + required: true + description: The ID of the book. + schema: + type: string + format: uuid + delete: + operationId: hardDeleteBookById + summary: Hard delete book by ID + description: Hard delete the book and associated entries from the database. This deletes the book's files from the filesystem. This action cannot be undone. + tags: + - Book + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/book/{id}/download: + parameters: + - name: id + in: path + required: true + description: The ID of the book. + schema: + type: string + format: uuid + get: + operationId: downloadBookById + summary: Download book by ID + description: Download the book by its ID. This endpoint will return the book's files as a zip archive. + tags: + - Book + responses: + '200': + description: OK + content: + application/zip: + schema: + type: string + format: binary + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/book/{id}/cover: + parameters: + - name: id + in: path + required: true + description: The ID of the book. + schema: + type: string + format: uuid + get: + operationId: getBookCoverById + summary: Get book cover by ID + description: Get the book cover by its ID. This endpoint will return the book's cover image. If no query parameters are provided, the image will be returned in the original format with the original dimensions. + tags: + - Book + parameters: + - name: width + in: query + required: false + description: The width of the image in pixels. + schema: + type: integer + minimum: 1 + - name: height + in: query + required: false + description: The height of the image in pixels. If this parameter is not provided, the image will be scaled proportionally to the width. + schema: + type: integer + minimum: 1 + - name: format + in: query + required: false + description: The format of the image. If not provided, the image will be returned in the original format. + schema: + type: string + enum: ['jpeg', 'png', 'webp'] + default: 'jpeg' + responses: + '200': + description: OK + content: + image/jpeg: + schema: + type: string + format: binary + image/png: + schema: + type: string + format: binary + image/webp: + schema: + type: string + format: binary + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + post: + operationId: uploadBookCoverById + summary: Upload book cover by ID + description: Upload the book cover image to the book by the book ID. This endpoint will replace the book's cover image with the provided image. The image should be in JPEG, PNG, or WebP format. Alternatively, the image can be provided as a URL to download the image from. + tags: + - Book + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + cover: + type: string + format: binary + application/json: + schema: + type: object + properties: + url: + type: string + description: The URL to download the image from. + format: uri + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + properties: + coverPath: + $ref: '#/components/schemas/imagePath' + success: + type: boolean + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updateBookCoverById + summary: Update book cover by ID + description: Update the book cover to be an existing image in the database. This endpoint will replace the book's cover image with the provided image. The image should be in JPEG, PNG, or WebP format. + tags: + - Book + requestBody: + content: + application/json: + schema: + type: object + properties: + coverId: + $ref: '#/components/schemas/itemId' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + properties: + coverPath: + $ref: '#/components/schemas/imagePath' + success: + type: boolean + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteBookCoverById + summary: Remove book cover by ID + description: Remove the book cover image from the book. The cover image file is not deleted but is no longer associated with the book. + tags: + - Book + responses: + '200': + description: OK + content: + application/json: + schema: + properties: + coverPath: + $ref: '#/components/schemas/imagePath' + success: + type: boolean + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/book/{id}/match: + parameters: + - name: id + in: path + required: true + description: The ID of the book. + schema: + type: string + format: uuid + post: + operationId: matchBookById + summary: Match book by ID + description: Match the book selected by ID against an online database. This performs a quick match against the online database and returns the best match. Quick match will apply the cover from the first match and fill empty metadata fields. Metadata fields are not overwritten unless the "Prefer Matched Metadata" setting is enabled or the "force" query is set. + tags: + - Book + parameters: + - in: query + name: force + required: false + description: Whether to force the match and overwrite all metadata fields. + schema: + type: boolean + default: false + - in: query + name: provider + required: false + description: The provider to use for the match. If not provided, the default library provider will be used. + schema: + type: string + enum: ['google', 'openlibrary', 'goodreads'] + - in: query + name: title + required: false + description: The title of the book to match. + schema: + type: string + - in: query + name: author + required: false + description: The author of the book to match. + schema: + type: string + - in: query + name: isbn + required: false + description: The ISBN of the book to match. + schema: + type: string + - in: query + name: asin + required: false + description: The ASIN of the book to match. Note that this needs to match the Audible page, not the Amazon page. + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/bookObject' + - type: object + properties: + updated: + type: boolean + description: Whether the book was updated with the match. + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/book/{id}/tracks: + parameters: + - name: id + in: path + required: true + description: The ID of the book. + schema: + type: string + format: uuid + patch: + operationId: updateBookTracksById + summary: Update book tracks + description: Update the book's audio tracks based on the provided file IDs. This endpoint will replace the book's audio tracks with the provided tracks. The tracks should be in the correct order. + tags: + - Book + requestBody: + content: + application/json: + schema: + type: object + properties: + trackIds: + type: array + description: The IDs of the audio files to use as tracks. + items: + $ref: '#/components/schemas/itemId' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/book/{id}/scan: + parameters: + - name: id + in: path + required: true + description: The ID of the book. + schema: + type: string + format: uuid + post: + operationId: scanBookById + summary: Scan book by ID + description: Scan the book by its ID. This endpoint will scan the book's files and update the book's metadata based on the files found according to the metadata priority settings. + tags: + - Book + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' From c7db327aa3fe624942da07023c7b64a92358abc1 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Wed, 11 Sep 2024 22:21:13 -0700 Subject: [PATCH 03/24] Add: initial podcast endpoints --- docs/newRoot.yaml | 666 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 666 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index a37af0820b..a7ac694cc5 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -35,6 +35,9 @@ components: size: type: integer description: The size of the item in bytes. + trackCount: + type: integer + description: The number of tracks in the podcast. title: type: string description: The title of the item. @@ -96,6 +99,9 @@ components: nullable: true items: $ref: '#/components/schemas/bookPerson' + authorName: + type: string + description: The name of an author associated with a book. authorNameArray: type: array description: An array of author names associated with a book. @@ -118,6 +124,10 @@ components: type: string description: The publisher of the book. nullable: true + publishDate: + type: string + description: The date the item was published. + nullable: true publishYear: type: integer description: The year the book was published. @@ -154,6 +164,47 @@ components: type: string description: The language of the item. nullable: true + hasFeedOpen: + type: boolean + description: Whether the item has an open feed. + rssFeed: + type: string + description: The RSS feed of the podcast. + nullable: true + itunesId: + type: string + description: The iTunes ID of the podcast. + nullable: true + podcastType: + type: string + description: The type of podcast. + enum: ['episodic', 'serial'] + episodeNumber: + type: integer + description: The episode number of the podcast. + seasonNumber: + type: integer + description: The season number of the podcast. + autoDownloadEnabled: + type: boolean + description: Whether auto-download is enabled for the podcast. + autoDownloadSchedule: + type: string + description: The cron expression of when to check for new episodes to auto-download from the RSS feed for the podcast. + example: '0 0 * * *' + nullable: true + lastEpisodeCheck: + type: string + description: The date and time the podcast was last checked for new episodes. + nullable: true + maxEpisodesToKeep: + type: integer + description: The maximum number of episodes to keep for the podcast. If zero, keep all episodes. + default: 0 + maxNewEpisodestoDownload: + type: integer + description: The maximum number of new episodes to download for the podcast. If zero, download all new episodes. + default: 3 bookChapter: type: object description: A chapter in a book. @@ -287,12 +338,119 @@ components: description: The ebook files of the book. Will be null if no ebooks are associated with the book. items: $ref: '#/components/schemas/ebookFile' + hasFeedOpen: + $ref: '#/components/schemas/hasFeedOpen' progress: $ref: '#/components/schemas/progress' duration: $ref: '#/components/schemas/duration' size: $ref: '#/components/schemas/size' + podcastObject: + type: object + description: Information about the podcast. + properties: + coverPath: + $ref: '#/components/schemas/imagePath' + title: + $ref: '#/components/schemas/title' + author: + type: string + description: The author or publisher of the podcast. + description: + $ref: '#/components/schemas/description' + duration: + $ref: '#/components/schemas/duration' + trackCount: + $ref: '#/components/schemas/trackCount' + size: + $ref: '#/components/schemas/size' + genres: + $ref: '#/components/schemas/genreArray' + tags: + $ref: '#/components/schemas/tagArray' + releaseDate: + $ref: '#/components/schemas/publishDate' + itunesId: + type: string + description: The iTunes ID of the podcast. + language: + $ref: '#/components/schemas/language' + explicit: + $ref: '#/components/schemas/isExplicit' + hasFeedOpen: + $ref: '#/components/schemas/hasFeedOpen' + rssFeed: + $ref: '#/components/schemas/rssFeed' + type: + $ref: '#/components/schemas/podcastType' + autoDownloadEnabled: + $ref: '#/components/schemas/autoDownloadEnabled' + autoDownloadSchedule: + $ref: '#/components/schemas/autoDownloadSchedule' + lastEpisodeCheck: + $ref: '#/components/schemas/lastEpisodeCheck' + maxEpisodesToKeep: + $ref: '#/components/schemas/maxEpisodesToKeep' + maxNewEpisodestoDownload: + $ref: '#/components/schemas/maxNewEpisodestoDownload' + podcastMatchObject: + type: object + description: Match information for a podcast from an online provider. + properties: + id: + type: integer + description: The ID of the podcast. + artistId: + type: integer + description: The ID of the artist. + title: + $ref: '#/components/schemas/title' + artistName: + $ref: '#/components/schemas/authorName' + description: + $ref: '#/components/schemas/description' + releaseDate: + $ref: '#/components/schemas/publishDate' + genres: + $ref: '#/components/schemas/genreArray' + cover: + $ref: '#/components/schemas/imagePath' + trackCount: + $ref: '#/components/schemas/trackCount' + feedUrl: + $ref: '#/components/schemas/rssFeed' + pageUrl: + type: string + description: The URL of the podcast page. + explicit: + $ref: '#/components/schemas/isExplicit' + podcastEpisodeDisplayObject: + type: object + description: An episode of a podcast, only includes the information needed to display the episode. + properties: + title: + $ref: '#/components/schemas/title' + description: + $ref: '#/components/schemas/description' + releaseDate: + $ref: '#/components/schemas/publishDate' + duration: + $ref: '#/components/schemas/duration' + size: + $ref: '#/components/schemas/size' + episodeNumber: + $ref: '#/components/schemas/episodeNumber' + seasonNumber: + $ref: '#/components/schemas/seasonNumber' + explicit: + $ref: '#/components/schemas/isExplicit' + coverPath: + $ref: '#/components/schemas/imagePath' + hasFeedOpen: + $ref: '#/components/schemas/hasFeedOpen' + progress: + $ref: '#/components/schemas/progress' responses: badRequest: description: Bad request. @@ -752,3 +910,511 @@ paths: $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' + /api/podcast/{id}: + parameters: + - name: id + in: path + required: true + description: The ID of the podcast. + schema: + type: string + format: uuid + get: + operationId: getPodcastById + summary: Get podcast by ID + description: Get a podcast by its ID. This endpoint returns all of the information needed for the podcast details page and editing, but does not include file information or podcast episode information. + tags: + - Podcast + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastObject' + '404': + $ref: '#/components/responses/notFound' + post: + operationId: updatePodcastById + summary: Update podcast by ID + description: Update a podcast by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Podcast + requestBody: + content: + application/json: + schema: + type: object + properties: + title: + $ref: '#/components/schemas/titleNullable' + author: + type: string + description: The author or publisher of the podcast. + description: + $ref: '#/components/schemas/description' + genres: + $ref: '#/components/schemas/genreArray' + tags: + $ref: '#/components/schemas/tagArray' + releaseDate: + $ref: '#/components/schemas/publishDate' + itunesId: + $ref: '#/components/schemas/itunesId' + language: + $ref: '#/components/schemas/language' + explicit: + $ref: '#/components/schemas/isExplicit' + rssFeed: + $ref: '#/components/schemas/rssFeed' + type: + $ref: '#/components/schemas/podcastType' + autoDownloadEnabled: + $ref: '#/components/schemas/autoDownloadEnabled' + autoDownloadSchedule: + $ref: '#/components/schemas/autoDownloadSchedule' + maxEpisodesToKeep: + $ref: '#/components/schemas/maxEpisodesToKeep' + maxNewEpisodestoDownload: + $ref: '#/components/schemas/maxNewEpisodestoDownload' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deletePodcastById + summary: Remove podcast by ID + description: Remove the podcast and associated entries from the database. This does not delete any files from the filesystem. + tags: + - Podcast + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast/{id}/hardDelete: + parameters: + - name: id + in: path + required: true + description: The ID of the podcast. + schema: + type: string + format: uuid + delete: + operationId: hardDeletePodcastById + summary: Hard delete podcast by ID + description: Hard delete the podcast and associated entries from the database. This deletes the podcast's files from the filesystem. This action cannot be undone. + tags: + - Podcast + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast/{id}/download: + parameters: + - name: id + in: path + required: true + description: The ID of the podcast. + schema: + type: string + format: uuid + get: + operationId: downloadPodcastById + summary: Download podcast by ID + description: Download the podcast by its ID. This endpoint will return the podcast's files as a zip archive. + tags: + - Podcast + responses: + '200': + description: OK + content: + application/zip: + schema: + type: string + format: binary + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast/{id}/cover: + parameters: + - name: id + in: path + required: true + description: The ID of the podcast. + schema: + type: string + format: uuid + get: + operationId: getPodcastCoverById + summary: Get podcast cover by ID + description: Get the podcast cover by its ID. This endpoint will return the podcast's cover image. If no query parameters are provided, the image will be returned in the original format with the original dimensions. + tags: + - Podcast + parameters: + - name: width + in: query + required: false + description: The width of the image in pixels. + schema: + type: integer + minimum: 1 + - name: height + in: query + required: false + description: The height of the image in pixels. If this parameter is not provided, the image will be scaled proportionally to the width. + schema: + type: integer + minimum: 1 + - name: format + in: query + required: false + description: The format of the image. If not provided, the image will be returned in the original format. + schema: + type: string + enum: ['jpeg', 'png', 'webp'] + default: 'jpeg' + responses: + '200': + description: OK + content: + image/jpeg: + schema: + type: string + format: binary + image/png: + schema: + type: string + format: binary + image/webp: + schema: + type: string + format: binary + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + post: + operationId: uploadPodcastCoverById + summary: Upload podcast cover by ID + description: Upload the podcast cover image to the podcast by the podcast ID. This endpoint will replace the podcast's cover image with the provided image. The image should be in JPEG, PNG, or WebP format. Alternatively, the image can be provided as a URL to download the image from. + tags: + - Podcast + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + cover: + type: string + format: binary + application/json: + schema: + type: object + properties: + url: + type: string + description: The URL to download the image from. + format: uri + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + properties: + coverPath: + $ref: '#/components/schemas/imagePath' + success: + type: boolean + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updatePodcastCoverById + summary: Update podcast cover by ID + description: Update the podcast cover to be an existing image in the database. This endpoint will replace the podcast's cover image with the provided image. The image should be in JPEG, PNG, or WebP format. + tags: + - Podcast + requestBody: + content: + application/json: + schema: + type: object + properties: + coverId: + $ref: '#/components/schemas/itemId' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + properties: + coverPath: + $ref: '#/components/schemas/imagePath' + success: + type: boolean + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deletePodcastCoverById + summary: Remove podcast cover by ID + description: Remove the podcast cover image from the podcast. The cover image file is not deleted but is no longer associated with the podcast. + tags: + - Podcast + responses: + '200': + description: OK + content: + application/json: + schema: + properties: + coverPath: + $ref: '#/components/schemas/imagePath' + success: + type: boolean + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast/{id}/match: + parameters: + - name: id + in: path + required: true + description: The ID of the podcast. + schema: + type: string + format: uuid + post: + operationId: matchPodcastById + summary: Match podcast by ID + description: Match the podcast selected by ID against an online database. This returns an array of possible matches. The user can select the best match from the list and select which metadata fields should be kept. + tags: + - Podcast + parameters: + - in: query + name: provider + required: false + description: The provider to use for the match. If not provided, the default library provider will be used. + schema: + type: string + enum: ['itunes', 'google', 'podcastindex'] + - in: query + name: rssFeed + required: false + description: The RSS feed of the podcast to match. + schema: + type: string + - in: query + name: title + required: false + description: The title of the podcast to match. + schema: + type: string + - in: query + name: itunesId + required: false + description: The iTunes ID of the podcast to match. + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/podcastMatchObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast/{id}/tracks: + parameters: + - name: id + in: path + required: true + description: The ID of the podcast. + schema: + type: string + format: uuid + get: + operationId: getPodcastTracksById + summary: Get podcast tracks by ID + description: Get the podcast's audio tracks by its ID. This endpoint will return the podcast's audio tracks. + tags: + - Podcast + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/audioTrack' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updatePodcastTracksById + summary: Update podcast tracks by ID + description: Update the podcast's audio tracks based on the provided file IDs. This endpoint will replace the podcast's audio tracks with the provided tracks. The tracks should be in the correct order. + tags: + - Podcast + requestBody: + content: + application/json: + schema: + type: object + properties: + trackIds: + type: array + description: The IDs of the audio files to use as tracks. + items: + $ref: '#/components/schemas/itemId' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast/{id}/scan: + parameters: + - name: id + in: path + required: true + description: The ID of the podcast. + schema: + type: string + format: uuid + post: + operationId: scanPodcastById + summary: Scan podcast by ID + description: Scan the podcast by its ID. This endpoint will scan the podcast's files and update the podcast's metadata based on the files found according to the metadata priority settings. + tags: + - Podcast + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast/{id}/episodes: + parameters: + - name: id + in: path + required: true + description: The ID of the podcast. + schema: + type: string + format: uuid + get: + operationId: getPodcastEpisodesById + summary: Get podcast episodes by ID + description: Get the podcast's episodes by its ID. This endpoint will return the podcast's episodes. + tags: + - Podcast + parameters: + - in: query + name: limit + required: true + description: The maximum number of episodes to return. This defines the page size + schema: + type: integer + minimum: 1 + default: 20 + - in: query + name: page + required: true + description: The page of episodes to return. This is used in conjunction with the limit parameter. + schema: + type: integer + minimum: 1 + default: 1 + - in: query + name: sort + required: false + description: The field to sort the episodes by. + schema: + type: string + enum: ['releaseDate', 'title', 'duration', 'size', 'episodeNumber', 'seasonNumber'] + default: 'releaseDate' + - in: query + name: filter + required: false + description: The field to filter the episodes by. + schema: + type: string + enum: ['incomplete', 'complete', 'in-progress', 'all'] + default: 'incomplete' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of episodes. + returnCount: + type: integer + description: The number of episodes returned. + episodes: + type: array + items: + $ref: '#/components/schemas/podcastEpisodeDisplayObject' + '404': + $ref: '#/components/responses/notFound' From d89c97742ce88357fb044852f85af57d431b0265 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Fri, 13 Sep 2024 10:31:54 -0700 Subject: [PATCH 04/24] Add: initial library media endpoints --- docs/newRoot.yaml | 296 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 295 insertions(+), 1 deletion(-) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index a7ac694cc5..a87d570f03 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -30,7 +30,7 @@ components: description: The bitrate of the audio file. progress: type: number - description: The user's progress through the media. Will be 1.0 if completed. + description: The user's progress through the media. Will be 1.0 if completed, and 0.0 or null if not started. nullable: true size: type: integer @@ -181,9 +181,11 @@ components: enum: ['episodic', 'serial'] episodeNumber: type: integer + nullable: true description: The episode number of the podcast. seasonNumber: type: integer + nullable: true description: The season number of the podcast. autoDownloadEnabled: type: boolean @@ -283,6 +285,96 @@ components: description: Whether the file is the primary ebook file. size: $ref: '#/components/schemas/size' + libraryBook: + type: object + description: A book object used for displaying items in a library. + properties: + bookId: + $ref: '#/components/schemas/itemId' + coverPath: + $ref: '#/components/schemas/imagePath' + title: + $ref: '#/components/schemas/title' + subtitle: + $ref: '#/components/schemas/subtitle' + authors: + $ref: '#/components/schemas/authorNameArray' + duration: + $ref: '#/components/schemas/duration' + size: + $ref: '#/components/schemas/size' + hasEbook: + type: boolean + description: Whether the book has an ebook + example: true + hasAudio: + type: boolean + description: Whether the book has audio + example: true + hasRss: + $ref: '#/components/schemas/hasFeedOpen' + explicit: + $ref: '#/components/schemas/isExplicit' + abridged: + $ref: '#/components/schemas/isAbridged' + extraInfo: + type: string + description: The extra info displayed when sorting or filtering. For example, the publish year. + example: '2010' + count: + type: integer + description: The number of books in the series when using the "Collapse Series" option. + example: 4 + progress: + $ref: '#/components/schemas/progress' + libraryPodcast: + type: object + description: A podcast object used for displaying items in a library. + properties: + podcastId: + $ref: '#/components/schemas/itemId' + coverPath: + $ref: '#/components/schemas/imagePath' + title: + $ref: '#/components/schemas/title' + author: + $ref: '#/components/schemas/authorName' + explicit: + $ref: '#/components/schemas/isExplicit' + extraInfo: + type: string + description: The extra info displayed when sorting or filtering. For example, the publish year. + example: '2010' + count: + type: integer + description: The number of episodes in the podcast. + example: 50 + libraryPodcastEpisode: + type: object + description: A podcast episode object used for displaying episodes in a library. + properties: + episodeId: + $ref: '#/components/schemas/itemId' + podcastId: + $ref: '#/components/schemas/itemId' + coverPath: + $ref: '#/components/schemas/imagePath' + title: + $ref: '#/components/schemas/title' + description: + $ref: '#/components/schemas/description' + seasonNumber: + $ref: '#/components/schemas/seasonNumber' + episodeNumber: + $ref: '#/components/schemas/episodeNumber' + publishDate: + type: integer + description: The publish date of the podcast episode in ms since POSIX epoch. + example: 1633522963509 + duration: + $ref: '#/components/schemas/duration' + progress: + $ref: '#/components/schemas/progress' bookObject: type: object description: Information about the book and its audio files. @@ -1418,3 +1510,205 @@ paths: $ref: '#/components/schemas/podcastEpisodeDisplayObject' '404': $ref: '#/components/responses/notFound' + /api/library/{id}/books: + parameters: + - name: id + in: path + required: true + description: The ID of the library. + schema: + type: string + format: uuid + get: + operationId: getLibraryBooksById + summary: Get books in library + description: Get the books in the library by its ID. This endpoint will return the books in the library. + tags: + - Library + parameters: + - in: query + name: limit + required: true + description: The maximum number of books to return. This defines the page size + schema: + type: integer + minimum: 1 + default: 20 + - in: query + name: page + required: true + description: The page of books to return. This is used in conjunction with the limit parameter. + schema: + type: integer + minimum: 1 + default: 1 + - in: query + name: sort + required: false + description: The field to sort the books by. + schema: + type: string + enum: ['title', 'publishYear', 'author', 'narrator', 'series', 'seriesSequence', 'size', 'duration', 'progress'] + default: 'title' + - in: query + name: filter + required: false + description: A key-value pair of how to filter books. The key is the field to filter by and the value is the value to filter by. If the value is an array, the filter will be an OR filter. + schema: + type: string + enum: ['genre', 'tag', 'author', 'narrator', 'series', 'seriesSequence', 'publishYear', 'publisher', 'isbn', 'asin', 'progress', 'all'] + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of books. + returnCount: + type: integer + description: The number of books returned. + books: + type: array + items: + $ref: '#/components/schemas/libraryBook' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/podcasts: + parameters: + - name: id + in: path + required: true + description: The ID of the library. + schema: + type: string + format: uuid + get: + operationId: getLibraryPodcastsById + summary: Get podcasts in library + description: Get the podcasts in the library by its ID. This endpoint will return the podcasts in the library. + tags: + - Library + parameters: + - in: query + name: limit + required: true + description: The maximum number of podcasts to return. This defines the page size + schema: + type: integer + minimum: 1 + default: 20 + - in: query + name: page + required: true + description: The page of podcasts to return. This is used in conjunction with the limit parameter. + schema: + type: integer + minimum: 1 + default: 1 + - in: query + name: sort + required: false + description: The field to sort the podcasts by. + schema: + type: string + enum: ['title', 'author', 'releaseDate', 'type', 'rssFeed', 'autoDownloadEnabled', 'autoDownloadSchedule', 'lastEpisodeCheck', 'maxEpisodesToKeep', 'maxNewEpisodestoDownload'] + default: 'title' + - in: query + name: filter + required: false + description: A key-value pair of how to filter podcasts. The key is the field to filter by and the value is the value to filter by. If the value is an array, the filter will be an OR filter. + schema: + type: string + enum: ['genre', 'tag', 'author', 'rssFeed', 'type', 'autoDownloadEnabled', 'autoDownloadSchedule', 'lastEpisodeCheck', 'maxEpisodesToKeep', 'maxNewEpisodestoDownload', 'all'] + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of podcasts. + returnCount: + type: integer + description: The number of podcasts returned. + podcasts: + type: array + items: + $ref: '#/components/schemas/libraryPodcast' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/podcast-episodes: + parameters: + - name: id + in: path + required: true + description: The ID of the library. + schema: + type: string + format: uuid + get: + operationId: getLibraryPodcastEpisodesById + summary: Get podcast episodes in library + description: Get the podcast episodes in the library by its ID. This endpoint will return the podcast episodes in the library. + tags: + - Library + parameters: + - in: query + name: limit + required: true + description: The maximum number of podcast episodes to return. This defines the page size + schema: + type: integer + minimum: 1 + default: 20 + - in: query + name: page + required: true + description: The page of podcast episodes to return. This is used in conjunction with the limit parameter. + schema: + type: integer + minimum: 1 + default: 1 + - in: query + name: sort + required: false + description: The field to sort the podcast episodes by. + schema: + type: string + enum: ['releaseDate', 'title', 'duration', 'size', 'episodeNumber', 'seasonNumber'] + default: 'releaseDate' + - in: query + name: filter + required: false + description: A key-value pair of how to filter podcast episodes. The key is the field to filter by and the value is the value to filter by. If the value is an array, the filter will be an OR filter. + schema: + type: string + enum: ['incomplete', 'complete', 'in-progress', 'all'] + default: 'incomplete' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of podcast episodes. + returnCount: + type: integer + description: The number of podcast episodes returned. + episodes: + type: array + items: + $ref: '#/components/schemas/libraryPodcastEpisode' + '404': + $ref: '#/components/responses/notFound' From 288386302d80d9fbd8f8362802b643931b090579 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sat, 14 Sep 2024 11:09:06 -0700 Subject: [PATCH 05/24] Add: podcast-episodes, claen up parameters --- docs/newRoot.yaml | 647 +++++++++++++++++++++++++++++++--------------- 1 file changed, 437 insertions(+), 210 deletions(-) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index a87d570f03..940d1acd20 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -17,6 +17,202 @@ components: description: Bearer authentication type: http scheme: bearer + parameters: + pathLibraryId: + name: id + in: path + required: true + description: The ID of the library. + schema: + type: string + format: uuid + pathBookId: + name: id + in: path + required: true + description: The ID of the book. + schema: + type: string + format: uuid + pathPodcastId: + name: id + in: path + required: true + description: The ID of the podcast. + schema: + type: string + format: uuid + pathPodcastEpisodeId: + name: id + in: path + required: true + description: The ID of the podcast episode. + schema: + type: string + format: uuid + queryLimit: + name: limit + in: query + required: false + description: The number of items per page. + schema: + type: integer + minimum: 1 + default: 20 + queryPage: + name: page + in: query + required: false + description: The page number of items to return. The first page is 1, and the page size is determined by the limit parameter. + schema: + type: integer + minimum: 1 + default: 1 + queryDesc: + name: desc + in: query + required: false + description: Whether to sort the items in descending order. + schema: + type: boolean + default: false + queryFilterGenre: + name: genre + in: query + required: false + description: The genre ID to filter by. + schema: + type: string + format: uuid + queryFilterTag: + name: tag + in: query + required: false + description: The tag ID to filter by. + schema: + type: string + format: uuid + queryFilterAuthor: + name: author + in: query + required: false + description: The author ID to filter by. + schema: + type: string + format: uuid + queryFilterNarrator: + name: narrator + in: query + required: false + description: The narrator ID to filter by. + schema: + type: string + format: uuid + queryFilterPublisher: + name: publisher + in: query + required: false + description: The publisher name to filter by. + schema: + type: string + queryFilterSeries: + name: series + in: query + required: false + description: The series ID to filter by. + schema: + type: string + format: uuid + queryFilterLanguage: + name: language + in: query + required: false + description: The language to filter by. + schema: + type: string + queryFilterProgress: + name: progress + in: query + required: false + description: The progress to filter by. + schema: + type: string + enum: ['not-started', 'in-progress', 'completed', 'not-completed'] + queryFilterMissing: + name: missing + in: query + required: false + description: Which fields to filter by missing values. + schema: + type: string + enum: ['asin', 'isbn', 'author', 'title', 'subtitle', 'publish-year', 'series', 'description', 'genre', 'tag', 'narrator', 'publisher', 'language', 'cover'] + queryFilterTrackCount: + name: trackCount + in: query + required: false + description: Filter if the book has a single audio file, multiple audio files, or no audio files (ebook). + schema: + type: string + enum: ['single', 'multiple', 'none'] + queryFilterEbook: + name: ebook + in: query + required: false + description: Filter by presence or abscence of ebook files. + schema: + type: string + enum: ['has-ebook', 'no-ebook', 'has-primary', 'no-primary', 'has-supplementary', 'no-supplementary'] + queryFilterAbridged: + name: abridged + in: query + required: false + description: Filter by abridged or unabridged books. + schema: + type: boolean + queryFilterExplicit: + name: explicit + in: query + required: false + description: Filter by explicit or non-explicit content. + schema: + type: boolean + queryFilterIssues: + name: issues + in: query + required: false + description: Filter by books with issues. + schema: + type: boolean + queryFilterFeedOpen: + name: feedOpen + in: query + required: false + description: Filter by books with open feeds. + schema: + type: boolean + queryFilterShareOpen: + name: shareOpen + in: query + required: false + description: Filter by books with open shares. + schema: + type: boolean + querySortBooks: + name: sort + in: query + required: false + description: The field to sort the books by. + schema: + type: string + enum: ['title', 'publishYear', 'author-fl', 'author-lf', 'size', 'duration', 'progress', 'file-birthtime', 'file-mtime', 'random'] + querySortPodcasts: + name: sort + in: query + required: false + description: The field to sort the podcasts by. + schema: + type: string + enum: ['title', 'author', 'created', 'size', 'episodeCount', 'random'] schemas: itemId: type: string @@ -187,6 +383,10 @@ components: type: integer nullable: true description: The season number of the podcast. + episodeType: + type: string + description: The type of episode. + enum: ['full', 'trailer', 'bonus', 'teaser', 'sponsored', 'patron'] autoDownloadEnabled: type: boolean description: Whether auto-download is enabled for the podcast. @@ -285,7 +485,7 @@ components: description: Whether the file is the primary ebook file. size: $ref: '#/components/schemas/size' - libraryBook: + displayBookObject: type: object description: A book object used for displaying items in a library. properties: @@ -327,7 +527,7 @@ components: example: 4 progress: $ref: '#/components/schemas/progress' - libraryPodcast: + displayPodcastObject: type: object description: A podcast object used for displaying items in a library. properties: @@ -349,7 +549,7 @@ components: type: integer description: The number of episodes in the podcast. example: 50 - libraryPodcastEpisode: + displayPodcastEpisodeObject: type: object description: A podcast episode object used for displaying episodes in a library. properties: @@ -543,6 +743,40 @@ components: $ref: '#/components/schemas/hasFeedOpen' progress: $ref: '#/components/schemas/progress' + podcastEpisodeObject: + type: object + description: An episode of a podcast. + properties: + episodeId: + $ref: '#/components/schemas/itemId' + podcastId: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + subtitle: + $ref: '#/components/schemas/subtitle' + description: + $ref: '#/components/schemas/description' + releaseDate: + $ref: '#/components/schemas/publishDate' + duration: + $ref: '#/components/schemas/duration' + size: + $ref: '#/components/schemas/size' + episodeType: + $ref: '#/components/schemas/episodeType' + episodeNumber: + $ref: '#/components/schemas/episodeNumber' + seasonNumber: + $ref: '#/components/schemas/seasonNumber' + explicit: + $ref: '#/components/schemas/isExplicit' + coverPath: + $ref: '#/components/schemas/imagePath' + rssFeed: + $ref: '#/components/schemas/rssFeed' + hasFeedOpen: + $ref: '#/components/schemas/hasFeedOpen' responses: badRequest: description: Bad request. @@ -571,13 +805,7 @@ tags: paths: /api/book/{id}: parameters: - - name: id - in: path - required: true - description: The ID of the book. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathBookId' get: operationId: getBookById summary: Get book by ID @@ -662,13 +890,7 @@ paths: $ref: '#/components/responses/notFound' /api/book/{id}/hardDelete: parameters: - - name: id - in: path - required: true - description: The ID of the book. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathBookId' delete: operationId: hardDeleteBookById summary: Hard delete book by ID @@ -688,13 +910,7 @@ paths: $ref: '#/components/responses/notFound' /api/book/{id}/download: parameters: - - name: id - in: path - required: true - description: The ID of the book. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathBookId' get: operationId: downloadBookById summary: Download book by ID @@ -715,13 +931,7 @@ paths: $ref: '#/components/responses/notFound' /api/book/{id}/cover: parameters: - - name: id - in: path - required: true - description: The ID of the book. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathBookId' get: operationId: getBookCoverById summary: Get book cover by ID @@ -867,13 +1077,7 @@ paths: $ref: '#/components/responses/notFound' /api/book/{id}/match: parameters: - - name: id - in: path - required: true - description: The ID of the book. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathBookId' post: operationId: matchBookById summary: Match book by ID @@ -938,13 +1142,7 @@ paths: $ref: '#/components/responses/notFound' /api/book/{id}/tracks: parameters: - - name: id - in: path - required: true - description: The ID of the book. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathBookId' patch: operationId: updateBookTracksById summary: Update book tracks @@ -978,13 +1176,7 @@ paths: $ref: '#/components/responses/notFound' /api/book/{id}/scan: parameters: - - name: id - in: path - required: true - description: The ID of the book. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathBookId' post: operationId: scanBookById summary: Scan book by ID @@ -1004,13 +1196,7 @@ paths: $ref: '#/components/responses/notFound' /api/podcast/{id}: parameters: - - name: id - in: path - required: true - description: The ID of the podcast. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathPodcastId' get: operationId: getPodcastById summary: Get podcast by ID @@ -1102,13 +1288,7 @@ paths: $ref: '#/components/responses/notFound' /api/podcast/{id}/hardDelete: parameters: - - name: id - in: path - required: true - description: The ID of the podcast. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathPodcastId' delete: operationId: hardDeletePodcastById summary: Hard delete podcast by ID @@ -1128,13 +1308,7 @@ paths: $ref: '#/components/responses/notFound' /api/podcast/{id}/download: parameters: - - name: id - in: path - required: true - description: The ID of the podcast. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathPodcastId' get: operationId: downloadPodcastById summary: Download podcast by ID @@ -1155,13 +1329,7 @@ paths: $ref: '#/components/responses/notFound' /api/podcast/{id}/cover: parameters: - - name: id - in: path - required: true - description: The ID of the podcast. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathPodcastId' get: operationId: getPodcastCoverById summary: Get podcast cover by ID @@ -1307,13 +1475,7 @@ paths: $ref: '#/components/responses/notFound' /api/podcast/{id}/match: parameters: - - name: id - in: path - required: true - description: The ID of the podcast. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathPodcastId' post: operationId: matchPodcastById summary: Match podcast by ID @@ -1361,13 +1523,7 @@ paths: $ref: '#/components/responses/notFound' /api/podcast/{id}/tracks: parameters: - - name: id - in: path - required: true - description: The ID of the podcast. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathPodcastId' get: operationId: getPodcastTracksById summary: Get podcast tracks by ID @@ -1418,13 +1574,7 @@ paths: $ref: '#/components/responses/notFound' /api/podcast/{id}/scan: parameters: - - name: id - in: path - required: true - description: The ID of the podcast. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathPodcastId' post: operationId: scanPodcastById summary: Scan podcast by ID @@ -1444,13 +1594,7 @@ paths: $ref: '#/components/responses/notFound' /api/podcast/{id}/episodes: parameters: - - name: id - in: path - required: true - description: The ID of the podcast. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathPodcastId' get: operationId: getPodcastEpisodesById summary: Get podcast episodes by ID @@ -1510,53 +1654,184 @@ paths: $ref: '#/components/schemas/podcastEpisodeDisplayObject' '404': $ref: '#/components/responses/notFound' - /api/library/{id}/books: + /api/podcast-episode/{id}: parameters: - - name: id - in: path + - $ref: '#/components/parameters/pathPodcastEpisodeId' + get: + operationId: getPodcastEpisodeById + summary: Get podcast episode by ID + description: Get a podcast episode by its ID. This endpoint returns all of the information needed for the podcast episode details page and editing. + tags: + - Podcast Episode + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeObject' + '404': + $ref: '#/components/responses/notFound' + post: + operationId: updatePodcastEpisodeById + summary: Update podcast episode by ID + description: Update a podcast episode by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Podcast Episode + requestBody: + content: + application/json: + schema: + type: object + properties: + title: + $ref: '#/components/schemas/titleNullable' + description: + $ref: '#/components/schemas/description' + releaseDate: + $ref: '#/components/schemas/publishDate' + episodeNumber: + $ref: '#/components/schemas/episodeNumber' + seasonNumber: + $ref: '#/components/schemas/seasonNumber' + explicit: + $ref: '#/components/schemas/isExplicit' + #coverPath: + #$ref: '#/components/schemas/imagePath' + #hasFeedOpen: + #$ref: '#/components/schemas/hasFeedOpen' required: true - description: The ID of the library. - schema: - type: string - format: uuid + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deletePodcastEpisodeById + summary: Remove podcast episode by ID + description: Remove the podcast episode and associated entries from the database. This does not delete any files from the filesystem. + tags: + - Podcast Episode + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast-episode/{id}/hardDelete: + parameters: + - $ref: '#/components/parameters/pathPodcastEpisodeId' + delete: + operationId: hardDeletePodcastEpisodeById + summary: Hard delete podcast episode by ID + description: Hard delete the podcast episode and associated entries from the database. This deletes the podcast episode's files from the filesystem. This action cannot be undone. + tags: + - Podcast Episode + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast-episode/{id}/download: + parameters: + - $ref: '#/components/parameters/pathPodcastEpisodeId' get: - operationId: getLibraryBooksById - summary: Get books in library - description: Get the books in the library by its ID. This endpoint will return the books in the library. + operationId: downloadPodcastEpisodeById + summary: Download podcast episode by ID + description: Download the podcast episode by its ID. This endpoint will return the podcast episode file as a raw file. tags: - - Library + - Podcast Episode + responses: + '200': + description: OK + content: + application/octet-stream: + schema: + type: string + format: binary + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast-episode/{id}/match: + parameters: + - $ref: '#/components/parameters/pathPodcastEpisodeId' + post: + operationId: matchPodcastEpisodeById + summary: Match podcast episode by ID + description: Match the podcast episode selected by ID against an online database. This returns an array of possible matches. The user can select the best match from the list and select which metadata fields should be kept. + tags: + - Podcast Episode parameters: - in: query - name: limit - required: true - description: The maximum number of books to return. This defines the page size - schema: - type: integer - minimum: 1 - default: 20 - - in: query - name: page - required: true - description: The page of books to return. This is used in conjunction with the limit parameter. - schema: - type: integer - minimum: 1 - default: 1 - - in: query - name: sort - required: false - description: The field to sort the books by. - schema: - type: string - enum: ['title', 'publishYear', 'author', 'narrator', 'series', 'seriesSequence', 'size', 'duration', 'progress'] - default: 'title' - - in: query - name: filter + name: title required: false - description: A key-value pair of how to filter books. The key is the field to filter by and the value is the value to filter by. If the value is an array, the filter will be an OR filter. + description: The title of the podcast episode to match. schema: type: string - enum: ['genre', 'tag', 'author', 'narrator', 'series', 'seriesSequence', 'publishYear', 'publisher', 'isbn', 'asin', 'progress', 'all'] + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + # TODO, verify this schema + $ref: '#/components/schemas/podcastEpisodeObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/books: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryBooksById + summary: Get books in library + description: Get the books in the library by its ID. This endpoint will return the books in the library. + tags: + - Library + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/querySortBooks' + - $ref: '#/components/parameters/queryDesc' + - $ref: '#/components/parameters/queryFilterGenre' + - $ref: '#/components/parameters/queryFilterTag' + - $ref: '#/components/parameters/queryFilterAuthor' + - $ref: '#/components/parameters/queryFilterNarrator' + - $ref: '#/components/parameters/queryFilterSeries' + - $ref: '#/components/parameters/queryFilterPublisher' + - $ref: '#/components/parameters/queryFilterLanguage' + - $ref: '#/components/parameters/queryFilterProgress' + - $ref: '#/components/parameters/queryFilterMissing' + - $ref: '#/components/parameters/queryFilterTrackCount' + - $ref: '#/components/parameters/queryFilterEbook' + - $ref: '#/components/parameters/queryFilterAbridged' + - $ref: '#/components/parameters/queryFilterExplicit' + - $ref: '#/components/parameters/queryFilterIssues' + - $ref: '#/components/parameters/queryFilterFeedOpen' + - $ref: '#/components/parameters/queryFilterShareOpen' responses: '200': description: OK @@ -1574,18 +1849,12 @@ paths: books: type: array items: - $ref: '#/components/schemas/libraryBook' + $ref: '#/components/schemas/displayBookObject' '404': $ref: '#/components/responses/notFound' /api/library/{id}/podcasts: parameters: - - name: id - in: path - required: true - description: The ID of the library. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathLibraryId' get: operationId: getLibraryPodcastsById summary: Get podcasts in library @@ -1593,37 +1862,14 @@ paths: tags: - Library parameters: - - in: query - name: limit - required: true - description: The maximum number of podcasts to return. This defines the page size - schema: - type: integer - minimum: 1 - default: 20 - - in: query - name: page - required: true - description: The page of podcasts to return. This is used in conjunction with the limit parameter. - schema: - type: integer - minimum: 1 - default: 1 - - in: query - name: sort - required: false - description: The field to sort the podcasts by. - schema: - type: string - enum: ['title', 'author', 'releaseDate', 'type', 'rssFeed', 'autoDownloadEnabled', 'autoDownloadSchedule', 'lastEpisodeCheck', 'maxEpisodesToKeep', 'maxNewEpisodestoDownload'] - default: 'title' - - in: query - name: filter - required: false - description: A key-value pair of how to filter podcasts. The key is the field to filter by and the value is the value to filter by. If the value is an array, the filter will be an OR filter. - schema: - type: string - enum: ['genre', 'tag', 'author', 'rssFeed', 'type', 'autoDownloadEnabled', 'autoDownloadSchedule', 'lastEpisodeCheck', 'maxEpisodesToKeep', 'maxNewEpisodestoDownload', 'all'] + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/querySortPodcasts' + - $ref: '#/components/parameters/queryDesc' + - $ref: '#/components/parameters/queryFilterGenre' + - $ref: '#/components/parameters/queryFilterTag' + - $ref: '#/components/parameters/queryFilterLanguage' + - $ref: '#/components/parameters/queryFilterIssues' responses: '200': description: OK @@ -1641,18 +1887,12 @@ paths: podcasts: type: array items: - $ref: '#/components/schemas/libraryPodcast' + $ref: '#/components/schemas/displayPodcastObject' '404': $ref: '#/components/responses/notFound' /api/library/{id}/podcast-episodes: parameters: - - name: id - in: path - required: true - description: The ID of the library. - schema: - type: string - format: uuid + - $ref: '#/components/parameters/pathLibraryId' get: operationId: getLibraryPodcastEpisodesById summary: Get podcast episodes in library @@ -1660,22 +1900,8 @@ paths: tags: - Library parameters: - - in: query - name: limit - required: true - description: The maximum number of podcast episodes to return. This defines the page size - schema: - type: integer - minimum: 1 - default: 20 - - in: query - name: page - required: true - description: The page of podcast episodes to return. This is used in conjunction with the limit parameter. - schema: - type: integer - minimum: 1 - default: 1 + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' - in: query name: sort required: false @@ -1684,6 +1910,7 @@ paths: type: string enum: ['releaseDate', 'title', 'duration', 'size', 'episodeNumber', 'seasonNumber'] default: 'releaseDate' + - $ref: '#/components/parameters/queryDesc' - in: query name: filter required: false @@ -1709,6 +1936,6 @@ paths: episodes: type: array items: - $ref: '#/components/schemas/libraryPodcastEpisode' + $ref: '#/components/schemas/displayPodcastEpisodeObject' '404': $ref: '#/components/responses/notFound' From 0795b030fe4eed5a2e7c8dfb3d408c68eb127485 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sat, 14 Sep 2024 17:06:52 -0700 Subject: [PATCH 06/24] Add: author endpoints --- docs/newRoot.yaml | 341 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 940d1acd20..52604a7953 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -50,6 +50,14 @@ components: schema: type: string format: uuid + pathAuthorId: + name: id + in: path + required: true + description: The ID of the author. + schema: + type: string + format: uuid queryLimit: name: limit in: query @@ -205,6 +213,7 @@ components: schema: type: string enum: ['title', 'publishYear', 'author-fl', 'author-lf', 'size', 'duration', 'progress', 'file-birthtime', 'file-mtime', 'random'] + default: 'title' querySortPodcasts: name: sort in: query @@ -213,6 +222,37 @@ components: schema: type: string enum: ['title', 'author', 'created', 'size', 'episodeCount', 'random'] + default: 'title' + querySortAuthors: + name: sort + in: query + required: false + description: The field to sort the authors by. + schema: + type: string + enum: ['author-fl', 'author-lf', 'bookCount', 'seriesCount', 'updatedAt', 'createdAt', 'random'] + default: 'author-fl' + queryAuthorName: + name: name + in: query + required: false + description: The name of the author. + schema: + type: string + queryAsin: + name: asin + in: query + required: false + description: The ASIN of the book or author. + schema: + type: string + queryRegion: + name: region + in: query + required: false + description: The region to search for the book or author. + schema: + type: string schemas: itemId: type: string @@ -367,6 +407,10 @@ components: type: string description: The RSS feed of the podcast. nullable: true + imageUrl: + type: string + description: The URL of the image to download. + format: uri itunesId: type: string description: The iTunes ID of the podcast. @@ -527,6 +571,23 @@ components: example: 4 progress: $ref: '#/components/schemas/progress' + displaySeriesObject: + type: object + description: A series object used for displaying items in a library. + properties: + seriesId: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + count: + type: integer + description: The number of books in the series. + example: 10 + books: + type: array + description: The books in the series. + items: + $ref: '#/components/schemas/displayBookObject' displayPodcastObject: type: object description: A podcast object used for displaying items in a library. @@ -575,6 +636,42 @@ components: $ref: '#/components/schemas/duration' progress: $ref: '#/components/schemas/progress' + authorObject: + type: object + description: Information about the author. + properties: + authorId: + $ref: '#/components/schemas/itemId' + name: + $ref: '#/components/schemas/authorName' + asin: + $ref: '#/components/schemas/asin' + description: + $ref: '#/components/schemas/description' + image: + $ref: '#/components/schemas/imagePath' + books: + type: array + description: The books written by the author. + items: + $ref: '#/components/schemas/displayBookObject' + series: + type: array + description: The series written by the author. + items: + $ref: '#/components/schemas/displaySeriesObject' + authorDisplayObject: + type: object + description: An author object used for displaying in a library. + properties: + authorId: + $ref: '#/components/schemas/itemId' + name: + $ref: '#/components/schemas/authorName' + count: + type: integer + description: The number of books by the author. + example: 10 bookObject: type: object description: Information about the book and its audio files. @@ -1852,6 +1949,40 @@ paths: $ref: '#/components/schemas/displayBookObject' '404': $ref: '#/components/responses/notFound' + /api/library/{id}/authors: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryAuthorsById + summary: Get authors in library + description: Get the authors in the library by its ID. This endpoint will return the authors in the library. + tags: + - Library + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/querySortAuthors' + - $ref: '#/components/parameters/queryDesc' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of authors. + returnCount: + type: integer + description: The number of authors returned. + authors: + type: array + items: + $ref: '#/components/schemas/authorDisplayObject' + '404': + $ref: '#/components/responses/notFound' /api/library/{id}/podcasts: parameters: - $ref: '#/components/parameters/pathLibraryId' @@ -1939,3 +2070,213 @@ paths: $ref: '#/components/schemas/displayPodcastEpisodeObject' '404': $ref: '#/components/responses/notFound' + /api/author/{id}: + parameters: + - $ref: '#/components/parameters/pathAuthorId' + get: + operationId: getAuthorById + summary: Get author by ID + description: Get an author by its ID. This endpoint returns all of the information needed for the author details page and editing. + tags: + - Author + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/authorObject' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updateAuthorById + summary: Update author by ID + description: Update an author by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Author + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/authorName' + descrption: + $ref: '#/components/schemas/description' + asin: + $ref: '#/components/schemas/asin' + imageUrl: + $ref: '#/components/schemas/imageUrl' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/authorObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteAuthorById + summary: Remove author by ID + description: Remove the author and associated entries from the database. This does not delete any files from the filesystem. + tags: + - Author + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/authorObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/author/{id}/image: + parameters: + - $ref: '#/components/parameters/pathAuthorId' + get: + operationId: getAuthorImageById + summary: Get author image by ID + description: Get the author image by its ID. This endpoint will return the author's image. If no query parameters are provided, the image will be returned in the original format with the original dimensions. + security: [] # No security for getting image + tags: + - Author + parameters: + - name: width + in: query + required: false + description: The width of the image in pixels. + schema: + type: integer + minimum: 1 + - name: height + in: query + required: false + description: The height of the image in pixels. If this parameter is not provided, the image will be scaled proportionally to the width. + schema: + type: integer + minimum: 1 + - name: format + in: query + required: false + description: The format of the image. If not provided, the image will be returned in the original format. + schema: + type: string + enum: ['jpeg', 'png', 'webp'] + default: 'jpeg' + responses: + '200': + description: OK + content: + image/jpeg: + schema: + type: string + format: binary + image/png: + schema: + type: string + format: binary + image/webp: + schema: + type: string + format: binary + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + post: + operationId: uploadAuthorImageById + summary: Upload author image by ID + description: Upload the author image to the author by the author ID. This endpoint will replace the author's image with the provided image. The image should be in JPEG, PNG, or WebP format. Alternatively, the image can be provided as a URL to download the image from. + tags: + - Author + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + image: + type: string + format: binary + application/json: + schema: + type: object + properties: + url: + type: string + description: The URL to download the image from. + format: uri + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + properties: + imageData: + $ref: '#/components/schemas/file' + success: + type: boolean + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteAuthorImageById + summary: Remove author image by ID + description: Remove the author image from the author. The image file is not deleted but is no longer associated with the author. + tags: + - Author + responses: + '200': + description: OK + content: + application/json: + schema: + properties: + imageData: + $ref: '#/components/schemas/file' + success: + type: boolean + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/author/{id}/match: + parameters: + - $ref: '#/components/parameters/pathAuthorId' + post: + operationId: matchAuthorById + summary: Match author by ID + description: Match the author selected by ID against an online database. + tags: + - Author + parameters: + - $ref: '#/components/parameters/queryAuthorName' + - $ref: '#/components/parameters/queryAsin' + - $ref: '#/components/parameters/queryRegion' + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/authorObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' From cb2a8b3abf53bb518fb2e094bc2e5f0cad51b2a0 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sat, 14 Sep 2024 18:01:23 -0700 Subject: [PATCH 07/24] Add: create and delete library --- docs/newRoot.yaml | 216 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 52604a7953..5f025444b8 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -258,6 +258,13 @@ components: type: string description: A unique ID for the item. This ID is unique across all tables. format: uuid + mediaType: + type: string + description: The type of media that the library contains. Will be `book` or `podcast`. + enum: ['book', 'podcast'] + libraryProvider: + type: string + description: Preferred metadata provider for the library. duration: type: number description: The length of the item in seconds. Will be 0 if no audio is associated with the item. @@ -317,6 +324,14 @@ components: type: string description: The absolute path of the image on the server. Null if no image is associated with the item. nullable: true + folderPath: + type: string + description: The absolute path of the folder on the server. + folderArray: + type: array + description: An array of folders associated with the item. + items: + $ref: '#/components/schemas/folderPath' filePath: type: string description: The absolute path of the file on the server. @@ -491,6 +506,78 @@ components: $ref: '#/components/schemas/duration' fileType: $ref: '#/components/schemas/fileType' + libraryIcon: + type: string + description: The icon of the library. + enum: ['database', 'audiobookshelf', 'books-1', 'books-2', 'book-1', 'microphone-1', 'microphone-3', 'radio', 'podcast', 'rss', 'headphones', 'music', 'file-picture', 'rocket', 'power', 'star', 'heart'] + libraryDisplayOrder: + type: integer + description: Display position of the library in the list of libraries. If the display order is higher than the number of libraries, the library will be placed at the end of the list. + minimum: 1 + libraryObject: + type: object + description: A library object which includes either books or podcasts. + properties: + id: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + folders: + $ref: '#/components/schemas/folderArray' + displayOrder: + $ref: '#/components/schemas/libraryDisplayOrder' + icon: + $ref: '#/components/schemas/libraryIcon' + mediaType: + $ref: '#/components/schemas/mediaType' + provider: + $ref: '#/components/schemas/libraryProvider' + settings: + $ref: '#/components/schemas/librarySettings' + librarySettings: + type: object + description: The settings for the library. + properties: + coverAspectRatio: + type: number + description: The aspect ratio of the cover image. + default: 1 + disableWatcher: + type: boolean + description: Whether to disable the folder watcher. + skipMatchingMediaWithAsin: + type: boolean + description: Whether to skip matching media with an ASIN. + skipMatchingMediaWithIsbn: + type: boolean + description: Whether to skip matching media with an ISBN. + autoScanCronExpression: + type: string + description: The cron expression for when to automatically scan the library folders. + nullable: true + example: '0 1 * * *' + audiobooksOnly: + type: boolean + description: Whether to only scan audiobooks. Ebook files cannot be primary, but can still be supplementary. + epubsAllowScriptedContent: + type: boolean + description: Whether to allow scripted content in EPUB files. + hideSingleBookSeries: + type: boolean + description: Whether to hide series with only one book. + onlyShowLaterBooksInContinueSeries: + type: boolean + description: Whether to only show later books in a series when using the "Continue Series" option. + metadataPrecedence: + type: array + description: The precedence of metadata sources. + items: + type: string + enum: ['folderStructure', 'audioMetatags', 'nfoFile', 'txtFiles', 'opfFile', 'absMetadata'] + podcastSearchRegion: + type: string + description: The region to use when searching for podcasts. + example: 'us' audioFileCodec: type: string description: The codec of an audio file. @@ -2280,3 +2367,132 @@ paths: $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' + /api/library: + get: + operationId: getLibraries + summary: Get libraries + description: Get all libraries. This endpoint will return all libraries. + tags: + - Library + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/libraryObject' + '403': + $ref: '#/components/responses/forbidden' + post: + operationId: createLibrary + summary: Create library + description: Create a new library. The request body should contain the library's name. + tags: + - Library + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/title' + folders: + $ref: '#/components/schemas/folderArray' + icon: + $ref: '#/components/schemas/libraryIcon' + mediaType: + $ref: '#/components/schemas/mediaType' + provider: + $ref: '#/components/schemas/libraryProvider' + settings: + $ref: '#/components/schemas/librarySettings' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/libraryObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + /api/library/{id}: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryById + summary: Get library by ID + description: Get a library by its ID. This endpoint returns all of the information needed for the library details page and editing. + tags: + - Library + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/libraryObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updateLibraryById + summary: Update library by ID + description: Update a library by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Library + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/titleNullable' + folders: + $ref: '#/components/schemas/folderArray' + displayOrder: + $ref: '#/components/schemas/libraryDisplayOrder' + icon: + $ref: '#/components/schemas/libraryIcon' + provider: + $ref: '#/components/schemas/libraryProvider' + settings: + $ref: '#/components/schemas/librarySettings' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/libraryObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteLibraryById + summary: Remove library by ID + description: Remove the library and associated entries from the database. This does not delete any files from the filesystem. + tags: + - Library + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/libraryObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' From 1f10f472f7b635989ef2bcf95b9cbb21ba464b75 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Mon, 16 Sep 2024 21:54:52 -0700 Subject: [PATCH 08/24] Additional library endpoints --- docs/newRoot.yaml | 607 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 605 insertions(+), 2 deletions(-) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 5f025444b8..51b6d3898d 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -58,6 +58,14 @@ components: schema: type: string format: uuid + pathNarratorId: + name: id + in: path + required: true + description: The ID of the narrator. + schema: + type: string + format: uuid queryLimit: name: limit in: query @@ -76,6 +84,14 @@ components: type: integer minimum: 1 default: 1 + querySubObjectLimit: + name: subObjectLimit + in: query + required: false + description: The number of sub-objects to return. If null, return all sub-objects. + schema: + type: integer + minimum: 1 queryDesc: name: desc in: query @@ -223,6 +239,33 @@ components: type: string enum: ['title', 'author', 'created', 'size', 'episodeCount', 'random'] default: 'title' + querySortSeries: + name: sort + in: query + required: false + description: The field to sort the series by. + schema: + type: string + enum: ['title', 'bookCount', 'duration', 'lastbook-added', 'lastbook-updated', 'createdAt', 'random'] + default: 'title' + querySortCollections: + name: sort + in: query + required: false + description: The field to sort the collections by. + schema: + type: string + enum: ['title', 'bookCount', 'duration', 'lastbook-added', 'lastbook-updated', 'createdAt', 'random'] + default: 'title' + querySortPlaylists: + name: sort + in: query + required: false + description: The field to sort the playlists by. + schema: + type: string + enum: ['title', 'bookCount', 'duration', 'lastbook-added', 'lastbook-updated', 'createdAt', 'random'] + default: 'title' querySortAuthors: name: sort in: query @@ -353,6 +396,9 @@ components: authorName: type: string description: The name of an author associated with a book. + narratorName: + type: string + description: The name of a narrator associated with a book. authorNameArray: type: array description: An array of author names associated with a book. @@ -658,7 +704,7 @@ components: example: 4 progress: $ref: '#/components/schemas/progress' - displaySeriesObject: + seriesDisplayObject: type: object description: A series object used for displaying items in a library. properties: @@ -675,6 +721,84 @@ components: description: The books in the series. items: $ref: '#/components/schemas/displayBookObject' + progress: + $ref: '#/components/schemas/progress' + bookCollectionDisplayObject: + type: object + description: A collection object used for displaying books in a library. + properties: + collectionId: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + count: + type: integer + description: The number of items in the collection. + example: 10 + duration: + $ref: '#/components/schemas/duration' + books: + type: array + description: The books in the collection. + items: + $ref: '#/components/schemas/displayBookObject' + bookPlaylistDisplayObject: + type: object + description: A playlist object used for displaying books in a library. + properties: + playlistId: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + count: + type: integer + description: The number of items in the playlist. + example: 10 + duration: + $ref: '#/components/schemas/duration' + books: + type: array + description: The books in the playlist. + items: + $ref: '#/components/schemas/displayBookObject' + podcastEpisodePlaylistDisplayObject: + type: object + description: A playlist object used for displaying episodes in a library. + properties: + playlistId: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + count: + type: integer + description: The number of items in the playlist. + example: 10 + duration: + $ref: '#/components/schemas/duration' + episodes: + type: array + description: The episodes in the playlist. + items: + $ref: '#/components/schemas/podcastEpisodeDisplayObject' + podcastEpisodeCollectionDisplayObject: + type: object + description: A collection object used for displaying episodes in a library. + properties: + collectionId: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + count: + type: integer + description: The number of items in the collection. + example: 10 + duration: + $ref: '#/components/schemas/duration' + episodes: + type: array + description: The episodes in the collection. + items: + $ref: '#/components/schemas/podcastEpisodeDisplayObject' displayPodcastObject: type: object description: A podcast object used for displaying items in a library. @@ -746,7 +870,7 @@ components: type: array description: The series written by the author. items: - $ref: '#/components/schemas/displaySeriesObject' + $ref: '#/components/schemas/seriesDisplayObject' authorDisplayObject: type: object description: An author object used for displaying in a library. @@ -759,6 +883,33 @@ components: type: integer description: The number of books by the author. example: 10 + narratorDisplayObject: + type: object + description: A narrator object used for displaying in a library. + properties: + narratorId: + $ref: '#/components/schemas/itemId' + name: + type: string + description: The name of the narrator. + count: + type: integer + description: The number of books narrated by the narrator. + example: 10 + narratorObject: + type: object + description: Information about the narrator. + properties: + narratorId: + $ref: '#/components/schemas/itemId' + name: + type: string + description: The name of the narrator. + books: + type: array + description: The books narrated by the narrator. + items: + $ref: '#/components/schemas/displayBookObject' bookObject: type: object description: Information about the book and its audio files. @@ -983,6 +1134,20 @@ components: schema: type: string example: Item not found. + bookLibraryOnly: + description: This endpoint is for book libraries only. + content: + text/html: + schema: + type: string + example: This endpoint is for book libraries only. + podcastLibraryOnly: + description: This endpoint is for podcast libraries only. + content: + text/html: + schema: + type: string + example: This endpoint is for podcast libraries only. tags: - name: Book description: Book endpoints @@ -2421,6 +2586,34 @@ paths: $ref: '#/components/responses/badRequest' '403': $ref: '#/components/responses/forbidden' + /api/library/order: + patch: + operationId: updateLibraryOrder + summary: Update library order + description: Update the order of the libraries. The request body must contain all library IDs in the order they should be displayed. + tags: + - Library + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/itemId' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/libraryObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' /api/library/{id}: parameters: - $ref: '#/components/parameters/pathLibraryId' @@ -2496,3 +2689,413 @@ paths: $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' + /api/library/{id}/issues: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + delete: + operationId: deleteLibraryIssuesById + summary: Remove items with issues + description: Remove all items with issues located in the library. This does not delete any files from the filesystem. A file with issues is defined as a file missing from the filesystem. + tags: + - Library + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + removed: + type: integer + description: The number of items removed. + remaining: + type: integer + description: The number of items remaining after removing items with issues. + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/series: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibrarySeriesById + summary: Get series in library + description: Get the series in the library by its ID. This endpoint will return the series in the library. + tags: + - Library + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/querySortSeries' + - $ref: '#/components/parameters/querySubObjectLimit' + - $ref: '#/components/parameters/queryDesc' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of series. + returnCount: + type: integer + description: The number of series returned. + series: + type: array + items: + $ref: '#/components/schemas/seriesDisplayObject' + '403': + $ref: '#/components/responses/bookLibraryOnly' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/book-collections: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryBookCollectionsById + summary: Get book collections in library + description: Get the collections in the library by its ID. This endpoint will return the collections in the library. This endpoint will return an error if the library is not `books` media type. + tags: + - Library + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/querySortCollections' + - $ref: '#/components/parameters/querySubObjectLimit' + - $ref: '#/components/parameters/queryDesc' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of collections. + returnCount: + type: integer + description: The number of collections returned. + collections: + type: array + items: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '403': + $ref: '#/components/responses/bookLibraryOnly' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/book-playlists: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryBookPlaylistsById + summary: Get book playlists in library + description: Get the playlists in the library by its ID. This endpoint will return the playlists in the library for this user. This endpoint will return an error if the library is not `books` media type. + tags: + - Library + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/querySortPlaylists' + - $ref: '#/components/parameters/querySubObjectLimit' + - $ref: '#/components/parameters/queryDesc' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of playlists. + returnCount: + type: integer + description: The number of playlists returned. + playlists: + type: array + items: + $ref: '#/components/schemas/bookPlaylistDisplayObject' + '403': + $ref: '#/components/responses/bookLibraryOnly' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/podcast-episode-playlists: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryPodcastEpisodePlaylistsById + summary: Get podcast episode playlists in library + description: Get the playlists in the library by its ID. This endpoint will return the playlists in the library for this user. This endpoint will return an error if the library is not `podcasts` media type. + tags: + - Library + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/querySortPlaylists' + - $ref: '#/components/parameters/querySubObjectLimit' + - $ref: '#/components/parameters/queryDesc' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of playlists. + returnCount: + type: integer + description: The number of playlists returned. + playlists: + type: array + items: + $ref: '#/components/schemas/podcastEpisodePlaylistDisplayObject' + '403': + $ref: '#/components/responses/podcastLibraryOnly' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/podcast-episode-collections: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryPodcastEpisodeCollectionsById + summary: Get podcast episode collections in library + description: Get the collections in the library by its ID. This endpoint will return the collections in the library. This endpoint will return an error if the library is not `podcasts` media type. + tags: + - Library + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/querySortCollections' + - $ref: '#/components/parameters/querySubObjectLimit' + - $ref: '#/components/parameters/queryDesc' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of collections. + returnCount: + type: integer + description: The number of collections returned. + collections: + type: array + items: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '403': + $ref: '#/components/responses/podcastLibraryOnly' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/narrators: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryNarratorsById + summary: Get narrators in library + description: Get the narrators in the library by its ID. This endpoint will return the narrators in the library. + tags: + - Library + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/queryDesc' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of narrators. + returnCount: + type: integer + description: The number of narrators returned. + narrators: + type: array + items: + $ref: '#/components/schemas/narratorDisplayObject' + '404': + $ref: '#/components/responses/notFound' + /api/narrator/{id}: + parameters: + - $ref: '#/components/parameters/pathNarratorId' + get: + operationId: getNarratorById + summary: Get narrator by ID + description: Get a narrator by its ID. This endpoint returns all of the information needed for the narrator details page and editing. + tags: + - Narrator + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/narratorObject' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updateNarratorById + summary: Update narrator by ID + description: Update a narrator by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Narrator + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/narratorName' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/narratorObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteNarratorById + summary: Remove narrator by ID + description: Remove the narrator and associated entries from the database. This does not delete any files from the filesystem. + tags: + - Narrator + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/narratorObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/scan: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + post: + operationId: scanLibraryById + summary: Scan library by ID + description: Scan the library by its ID. This will scan the library's folders for new media and update the database with the new media. + tags: + - Library + requestBody: + content: + application/json: + schema: + type: object + properties: + folders: + type: array + description: An array of folder IDs in the library to scan for media. + items: + type: string + format: uuid + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + added: + type: integer + description: The number of items added to the library. + updated: + type: integer + description: The number of items updated in the library. + removed: + type: integer + description: The number of items removed from the library. + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/match: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + post: + operationId: matchLibraryById + summary: Match library by ID + description: Match all items in the library which do not have an ASIN or ISBN against an online database. If a provider is not specified, the default library provider will be used. Matched items will have missing details filled in by default and data will not be overwritten, unless the "Prefer matched metadata" setting is enabled. + tags: + - Library + requestBody: + content: + application/json: + schema: + type: object + properties: + provider: + $ref: '#/components/schemas/libraryProvider' + required: false + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + updated: + type: integer + description: The number of items updated in the library. This will be equal to or less than `found`. + found: + type: integer + description: The number of unmatched items found in the online database. + notFound: + type: integer + description: The number of unmatched items not found in the online database. + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/opml: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryOpmlById + summary: Get library OPML by ID + description: Generate an OPML file for the library. This endpoint is only available for podcast libraries. + tags: + - Library + responses: + '200': + description: OK + content: + application/xml: + schema: + type: string + format: binary + '403': + $ref: '#/components/responses/podcastLibraryOnly' + '404': + $ref: '#/components/responses/notFound' From 1556a0426e15bb021dc9f68f61f095fb5499955e Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sat, 21 Sep 2024 15:56:48 -0700 Subject: [PATCH 09/24] Add: ereader endpoints --- docs/newRoot.yaml | 233 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 51b6d3898d..b6274a9eba 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -1112,6 +1112,81 @@ components: $ref: '#/components/schemas/rssFeed' hasFeedOpen: $ref: '#/components/schemas/hasFeedOpen' + ereaderName: + type: string + description: The name of the e-reader device. + ereaderDeviceObject: + type: object + description: An e-reader device configured to receive EPUB through e-mail. + properties: + name: + $ref: '#/components/schemas/ereaderName' + email: + type: string + description: The email address associated with the e-reader device. + availabilityOption: + type: string + description: The availability option for the device. + enum: ['adminOrUp', 'userOrUp', 'guestOrUp', 'specificUsers'] + users: + type: array + description: List of specific user ids allowed to access the device. + items: + type: string + format: uuid + required: + - name + - email + - availabilityOption + emailSettings: + type: object + description: The email settings for sending emails. + properties: + id: + type: string + description: The unique identifier for the email settings. Currently this is always `email-settings` + enum: ['email-settings'] + host: + type: string + description: The SMTP host address. + nullable: true + port: + type: integer + minimum: 1 + description: The port number for the SMTP server. + example: 465 + secure: + type: boolean + description: Indicates if the connection should use SSL/TLS. + rejectUnauthorized: + type: boolean + description: Indicates if unauthorized SSL/TLS certificates should be rejected. + user: + type: string + description: The username for SMTP authentication. + nullable: true + pass: + type: string + description: The password for SMTP authentication. + nullable: true + testAddress: + type: string + description: The test email address used for sending test emails. + nullable: true + fromAddress: + type: string + description: The default "from" email address for outgoing emails. A formatted name can be included using the syntax of `Formatted Name `. + nullable: true + ereaderDevices: + type: array + description: List of configured e-reader devices. + items: + $ref: '#/components/schemas/ereaderDeviceObject' + required: + - id + - port + - secure + - ereaderDevices responses: badRequest: description: Bad request. @@ -3099,3 +3174,161 @@ paths: $ref: '#/components/responses/podcastLibraryOnly' '404': $ref: '#/components/responses/notFound' + /api/email/settings: + get: + operationId: getEmailSettings + summary: Get email settings + description: Get the email settings for sending e-books to e-readers. + tags: + - Email + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/emailSettings' + '403': + $ref: '#/components/responses/forbidden' + patch: + operationId: updateEmailSettings + summary: Update email settings + description: Update the email settings for sending e-books to e-readers. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Email + requestBody: + content: + application/json: + schema: + type: object + properties: + emailSettings: + $ref: '#/components/schemas/emailSettings' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/emailSettings' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + /api/email/test: + post: + operationId: testEmailSettings + summary: Test email settings + description: Test the email settings for sending e-books to e-readers. This will send a test email to the specified e-reader. + tags: + - Email + requestBody: + content: + application/json: + schema: + type: object + properties: + ereader-name: + $ref: '#/components/schemas/ereaderName' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + /api/email/create-device: + post: + operationId: addEreaderDevice + summary: Add e-reader device + description: Add an e-reader device to the list of devices that can receive e-books via email. + tags: + - Email + requestBody: + content: + application/json: + schema: + type: object + properties: + ereader: + $ref: '#/components/schemas/ereaderDeviceObject' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ereaderDeviceObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + /api/email/delete-device: + delete: + operationId: deleteEreaderDevice + summary: Remove e-reader device + description: Remove an e-reader device from the list of devices that can receive e-books via email. + tags: + - Email + requestBody: + content: + application/json: + schema: + type: object + properties: + ereader-name: + $ref: '#/components/schemas/ereaderName' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ereaderDeviceObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + /api/email/send-ebook: + post: + operationId: sendEbookToEreader + summary: Send e-book to e-reader + description: Send an e-book to an e-reader device. The e-reader device must be in the list of devices that can receive e-books via email for this user. + tags: + - Email + requestBody: + content: + application/json: + schema: + type: object + properties: + ereader-name: + $ref: '#/components/schemas/ereaderName' + book-id: + $ref: '#/components/schemas/itemId' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' From da6afc7664ca44ccc05391e4c692a80bbfb254c5 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Fri, 27 Sep 2024 09:15:29 -0700 Subject: [PATCH 10/24] Add: RSS Feed endpoints --- docs/newRoot.yaml | 199 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index b6274a9eba..b4fd5ef847 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -66,6 +66,21 @@ components: schema: type: string format: uuid + pathFeedId: + name: id + in: path + required: true + description: The ID of the feed. + schema: + type: string + format: uuid + pathFeedSlug: + name: slug + in: path + required: true + description: The slug of the feed. + schema: + type: string queryLimit: name: limit in: query @@ -461,6 +476,25 @@ components: type: string description: The language of the item. nullable: true + feedUrl: + type: string + description: The RSS feed hosted by ABS. + feedItemType: + type: string + description: The type of media in the feed. + enum: ['book', 'collection', 'series', 'podcast'] + feedOwnerName: + type: string + description: The owner of the feed. + feedOwnerEmail: + type: string + description: The email of the feed owner. + feedPreventIndexing: + type: boolean + description: Whether the feed should be indexed by search engines. + feedSlug: + type: string + description: The slug of the feed. By default this is a custom UUID, but can be any valid URL slug. hasFeedOpen: type: boolean description: Whether the item has an open feed. @@ -1187,6 +1221,30 @@ components: - port - secure - ereaderDevices + rssFeedObject: + type: object + description: An RSS feed object hosted by ABS. + properties: + id: + $ref: '#/components/schemas/itemId' + feedUrl: + $ref: '#/components/schemas/feedUrl' + imageUrl: + $ref: '#/components/schemas/imageUrl' + title: + $ref: '#/components/schemas/title' + description: + $ref: '#/components/schemas/description' + feedPreventIndexing: + $ref: '#/components/schemas/feedPreventIndexing' + feedItemType: + $ref: '#/components/schemas/feedItemType' + feedOwnerName: + $ref: '#/components/schemas/feedOwnerName' + feedOwnerEmail: + $ref: '#/components/schemas/feedOwnerEmail' + feedUserId: + $ref: '#/components/schemas/itemId' responses: badRequest: description: Bad request. @@ -2976,6 +3034,38 @@ paths: $ref: '#/components/responses/podcastLibraryOnly' '404': $ref: '#/components/responses/notFound' + /api/library/{id}/feeds: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryFeedsById + summary: Get feeds in library + description: Get the feeds in the library by its ID. This endpoint will return the feeds in the library. + tags: + - Library + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of feeds. + returnCount: + type: integer + description: The number of feeds returned. + feeds: + type: array + items: + $ref: '#/components/schemas/rssFeedObject' + '404': + $ref: '#/components/responses/notFound' /api/library/{id}/narrators: parameters: - $ref: '#/components/parameters/pathLibraryId' @@ -3332,3 +3422,112 @@ paths: $ref: '#/components/responses/badRequest' '403': $ref: '#/components/responses/forbidden' + /api/feeds: + get: + operationId: getFeeds + summary: Get feeds + description: Get all feeds. This endpoint will return all feeds for the server. + tags: + - RSS Feed + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/rssFeedObject' + '403': + $ref: '#/components/responses/forbidden' + post: + operationId: createFeed + summary: Create feed + description: Create a new feed. The request body should contain the feed's slug and the ID of the object the feed corresponds to. + tags: + - RSS Feed + requestBody: + content: + application/json: + schema: + type: object + properties: + entityId: + $ref: '#/components/schemas/itemId' + slug: + $ref: '#/components/schemas/feedSlug' + preventIndexing: + $ref: '#/components/schemas/feedPreventIndexing' + ownerName: + $ref: '#/components/schemas/feedOwnerName' + ownerEmail: + $ref: '#/components/schemas/feedOwnerEmail' + required: + - entityId + - slug + - preventIndexing + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/rssFeedObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + /api/feeds/{id}: + parameters: + - $ref: '#/components/parameters/pathFeedId' + get: + operationId: getFeedById + summary: Get feed object by ID + description: Get a feed by its ID. This endpoint returns all of the information needed for the feed details page and editing. + tags: + - RSS Feed + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/rssFeedObject' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteFeedById + summary: Remove feed by ID + description: Remove the feed and associated entries from the database. This does not delete any files from the filesystem. + tags: + - RSS Feed + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/rssFeedObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /feed/{slug}: + parameters: + - $ref: '#/components/parameters/pathFeedSlug' + get: + operationId: getFeedBySlug + summary: Get RSS feed + description: Get a feed by its slug. This endpoint returns the RSS feed as a string. + tags: + - RSS Feed + responses: + '200': + description: OK + content: + application/xml: + schema: + type: string + format: binary + '404': + $ref: '#/components/responses/notFound' From e85fd0835d285c47364c200031ba42592cfa3cc6 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Fri, 27 Sep 2024 11:46:58 -0700 Subject: [PATCH 11/24] Add: series endpoints --- docs/newRoot.yaml | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index b4fd5ef847..5bd8e35e4b 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -34,6 +34,14 @@ components: schema: type: string format: uuid + pathSeriesId: + name: id + in: path + required: true + description: The ID of the series. + schema: + type: string + format: uuid pathPodcastId: name: id in: path @@ -3531,3 +3539,70 @@ paths: format: binary '404': $ref: '#/components/responses/notFound' + /api/series/{id}: + parameters: + - $ref: '#/components/parameters/pathSeriesId' + get: + operationId: getSeriesById + summary: Get series by ID + description: Get a series by its ID. This endpoint returns all of the information needed for the series page. + tags: + - Series + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/seriesDisplayObject' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updateSeriesById + summary: Update series by ID + description: Update a series by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Series + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/title' + descrption: + $ref: '#/components/schemas/description' + imageUrl: + $ref: '#/components/schemas/imageUrl' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/seriesDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteSeriesById + summary: Remove series by ID + description: Remove the series and associated entries from the database. This does not delete any files from the filesystem. + tags: + - Series + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/seriesDisplayObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' From f54098074f77987f24d3e147b90a9e979d96b89f Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Tue, 1 Oct 2024 21:55:53 -0700 Subject: [PATCH 12/24] Initial user endpoints --- docs/newRoot.yaml | 308 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 5bd8e35e4b..a9e5678106 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -26,6 +26,14 @@ components: schema: type: string format: uuid + pathUserId: + name: id + in: path + required: true + description: The ID of the user. + schema: + type: string + format: uuid pathBookId: name: id in: path @@ -298,6 +306,15 @@ components: type: string enum: ['author-fl', 'author-lf', 'bookCount', 'seriesCount', 'updatedAt', 'createdAt', 'random'] default: 'author-fl' + querySortUsers: + name: sort + in: query + required: false + description: The field to sort the users by. + schema: + type: string + enum: ['username', 'email', 'createdAt', 'updatedAt', 'hasOpenId', 'accountType', 'isEnabled'] + default: 'username' queryAuthorName: name: name in: query @@ -331,6 +348,40 @@ components: libraryProvider: type: string description: Preferred metadata provider for the library. + createdAt: + type: number + description: The date and time the item was created in ms since POSIX epoch. + updatedAt: + type: number + description: The date and time the item was last updated in ms since POSIX epoch. + userPermissions: + type: object + description: The permissions of the user. + properties: + download: + type: boolean + description: Whether the user can download files. + update: + type: boolean + description: Whether the user can update metadata. + delete: + type: boolean + description: Whether the user can delete the item. + upload: + type: boolean + description: Whether the user can upload files. + accessAllLibraries: + type: boolean + description: Whether the user can access all libraries. + accessAllTags: + type: boolean + description: Whether the user can access all tags. + accessExplicitContent: + type: boolean + description: Whether the user can access explicit content. + selectedTagsAccessible: + type: boolean + description: Whether the user can access selected tags. If false, invert so selected tags are not accessible. duration: type: number description: The length of the item in seconds. Will be 0 if no audio is associated with the item. @@ -1253,6 +1304,87 @@ components: $ref: '#/components/schemas/feedOwnerEmail' feedUserId: $ref: '#/components/schemas/itemId' + username: + type: string + description: The username of the user. + userPassword: + type: string + description: The password of the user. Only used when creating the user or updating the password. + userEmail: + type: string + nullable: true + description: The email address of the user. + userType: + type: string + description: The type of user. + enum: ['root', 'admin', 'user', 'guest'] + apiToken: + type: string + description: The API token of the user. + seriesHideFromContinueListening: + type: array + description: The series that are hidden from the "Continue Listening" section. + items: + $ref: '#/components/schemas/itemId' + userIsActive: + type: boolean + description: Whether the user is active. + userIsLocked: + type: boolean + description: Whether the user is locked. + userLastSeen: + type: number + description: The last time the user was seen in ms since POSIX epoch. + userLibrariesAccessible: + type: array + description: The libraries the user has access to. + nullable: true + items: + $ref: '#/components/schemas/itemId' + userItemTagsSelected: + type: array + description: The tags the user has selected to filter items. + nullable: true + items: + $ref: '#/components/schemas/tag' + userLinkedToOpenId: + type: boolean + description: Whether the user is linked to an OpenID. + openIdSub: + type: string + description: The sub claim of the OpenID token. + userObject: + type: object + description: A user object. + properties: + id: + $ref: '#/components/schemas/itemId' + username: + $ref: '#/components/schemas/username' + email: + $ref: '#/components/schemas/userEmail' + type: + $ref: '#/components/schemas/userType' + token: + $ref: '#/components/schemas/apiToken' + seriesHideFromContinueListening: + $ref: '#/components/schemas/seriesHideFromContinueListening' + isActive: + $ref: '#/components/schemas/userIsActive' + isLocked: + $ref: '#/components/schemas/userIsLocked' + lastSeen: + $ref: '#/components/schemas/userLastSeen' + createdAt: + $ref: '#/components/schemas/createdAt' + permissions: + $ref: '#/components/schemas/userPermissions' + hasOpenIdLink: + $ref: '#/components/schemas/userLinkedToOpenId' + librariesAccessible: + $ref: '#/components/schemas/userLibrariesAccessible' + itemTagsSelected: + $ref: '#/components/schemas/userItemTagsSelected' responses: badRequest: description: Bad request. @@ -3606,3 +3738,179 @@ paths: $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' + /api/users: + get: + operationId: getUsers + summary: Get users + description: Get all users. This endpoint will return all users, with optional paging and sorting. + tags: + - User + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/querySortUsers' + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/userObject' + '403': + $ref: '#/components/responses/forbidden' + post: + operationId: createUser + summary: Create user + description: Create a new user. Only one root user can exist per server. + tags: + - User + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/username' + email: + $ref: '#/components/schemas/userEmail' + password: + $ref: '#/components/schemas/userPassword' + type: + $ref: '#/components/schemas/userType' + isActive: + $ref: '#/components/schemas/userIsActive' + isLocked: + $ref: '#/components/schemas/userIsLocked' + permissions: + $ref: '#/components/schemas/userPermissions' + librariesAccesible: + $ref: '#/components/schemas/userLibrariesAccessible' + itemTagsSelected: + $ref: '#/components/schemas/userItemTagsSelected' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/userObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + /api/user/{id}: + parameters: + - $ref: '#/components/parameters/pathUserId' + get: + operationId: getUserById + summary: Get user by ID + description: Get a user by its ID. This endpoint returns all of the information needed for the user details page and editing. + tags: + - User + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/userObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updateUserById + summary: Update user by ID + description: Update a user by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - User + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/username' + email: + $ref: '#/components/schemas/userEmail' + password: + $ref: '#/components/schemas/userPassword' + type: + $ref: '#/components/schemas/userType' + isActive: + $ref: '#/components/schemas/userIsActive' + isLocked: + $ref: '#/components/schemas/userIsLocked' + permissions: + $ref: '#/components/schemas/userPermissions' + librariesAccesible: + $ref: '#/components/schemas/userLibrariesAccessible' + itemTagsSelected: + $ref: '#/components/schemas/userItemTagsSelected' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/userObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteUserById + summary: Remove user by ID + description: Remove the user and associated entries from the database. This does not delete any files from the filesystem. + tags: + - User + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/userObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/user/{id}/openid-unlink: + parameters: + - $ref: '#/components/parameters/pathUserId' + patch: + operationId: unlinkOpenId + summary: Unlink OpenID + description: Unlink an OpenID account from a user account. + tags: + - User + requestBody: + content: + application/json: + schema: + type: object + properties: + openIdSub: + $ref: '#/components/schemas/openIdSub' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/userObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' From 121c15716ac1487ab08496f33410ad35a09280f5 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Mon, 7 Oct 2024 20:38:55 -0700 Subject: [PATCH 13/24] Add: podcast download queue endpoints --- docs/newRoot.yaml | 174 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index a9e5678106..6501bbce09 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -557,6 +557,10 @@ components: hasFeedOpen: type: boolean description: Whether the item has an open feed. + episodeUrl: + type: string + description: The URL of the podcast episode. + format: uri rssFeed: type: string description: The RSS feed of the podcast. @@ -1171,6 +1175,24 @@ components: $ref: '#/components/schemas/hasFeedOpen' progress: $ref: '#/components/schemas/progress' + podcastEpisodeQueueObject: + type: object + description: An episode of a podcast, only includes the information needed to include the episode in the queue. + properties: + podcastId: + $ref: '#/components/schemas/itemId' + coverPath: + $ref: '#/components/schemas/imagePath' + episodeUrl: + $ref: '#/components/schemas/episodeUrl' + libraryId: + $ref: '#/components/schemas/itemId' + episodeTitle: + $ref: '#/components/schemas/title' + episodeNumber: + $ref: '#/components/schemas/episodeNumber' + releaseDate: + $ref: '#/components/schemas/publishDate' podcastEpisodeObject: type: object description: An episode of a podcast. @@ -1949,6 +1971,124 @@ paths: $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' + /api/podcast/{id}/checknew: + parameters: + - $ref: '#/components/parameters/pathPodcastId' + post: + operationId: checkNewEpisodesById + summary: Check for new episodes + description: Check for new episodes for the podcast by its ID. This endpoint will check the podcast's RSS feed for new episodes and add them to the database. + tags: + - Podcast + requestBody: + content: + application/json: + schema: + type: object + properties: + limit: + type: integer + description: The maximum number of new episodes to check for. Use 0 for unlimited. + minimum: 0 + default: 3 + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + newEpisodes: + type: array + description: The new episodes added to the download queue. + items: + $ref: '#/components/schemas/podcastEpisodeQueueObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/podcast/{id}/download-queue: + parameters: + - $ref: '#/components/parameters/pathPodcastId' + get: + operationId: getPodcastDownloadQueueById + summary: Get podcast download queue by ID + description: Get the podcast download queue by its ID. This endpoint will return the podcast's download queue, which includes the episodes that are queued for download. + tags: + - Podcast + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + downloadQueue: + type: array + description: The episodes queued for download. + items: + $ref: '#/components/schemas/podcastEpisodeQueueObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + post: + operationId: addPodcastDownloadQueueById + summary: Add podcast episode to download queue + description: Add a podcast episode to the download queue by the podcast ID. This endpoint will add the episode to the end of the download queue. + tags: + - Podcast + requestBody: + content: + application/json: + schema: + type: object + properties: + episodes: + type: array + description: The podcasts to add to the download queue. + items: + $ref: '#/components/schemas/podcastEpisodeQueueObject' + required: true + responses: + '200': + description: OK + content: + text/plain: + schema: + type: string + example: Episodes added to download queue. + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: clearPodcastDownloadQueueById + summary: Clear podcast download queue by ID + description: Clear the podcast download queue by its ID. This endpoint will remove all episodes from the podcast's download queue. + tags: + - Podcast + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + downloadQueue: + type: array + description: The episodes removed from the download queue. + items: + $ref: '#/components/schemas/podcastEpisodeQueueObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' /api/podcast/{id}/cover: parameters: - $ref: '#/components/parameters/pathPodcastId' @@ -2095,6 +2235,40 @@ paths: $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' + /api/podcast/{id}/match-episodes: + parameters: + - $ref: '#/components/parameters/pathPodcastId' + post: + operationId: quickMatchPodcastEpisodesById + summary: Quick match podcast episodes + description: Quick match all episodes in the podcast against an RSS feed. Quick match will fill empty metadata fields. Metadata fields are not overwritten unless the "Prefer Matched Metadata" setting is enabled or the "force" query is set. + tags: + - Podcast + parameters: + - in: query + name: force + required: false + description: Whether to force the match and overwrite all metadata fields. + schema: + type: boolean + default: false + responses: + '200': + description: OK + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/podcastObject' + - type: object + properties: + updated: + type: boolean + description: Whether the podcast was updated with the match. + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' /api/podcast/{id}/match: parameters: - $ref: '#/components/parameters/pathPodcastId' From c2ae15cc8b71e78988856c989aa642a04279f3c4 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Mon, 7 Oct 2024 21:08:02 -0700 Subject: [PATCH 14/24] Update: library opml endpoints --- docs/newRoot.yaml | 127 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 106 insertions(+), 21 deletions(-) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 6501bbce09..1375981862 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -2718,6 +2718,112 @@ paths: type: array items: $ref: '#/components/schemas/displayPodcastObject' + '400': + $ref: '#/components/responses/podcastLibraryOnly' + '404': + $ref: '#/components/responses/notFound' + post: + operationId: addPodcastToLibraryById + summary: Add podcast to library by ID + description: Add a podcast to the library by its ID. This endpoint will add the podcast to the library. + tags: + - Library + requestBody: + content: + application/json: + schema: + type: object + properties: + podcast: + $ref: '#/components/schemas/podcastObject' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/displayPodcastObject' + '400': + $ref: '#/components/responses/podcastLibraryOnly' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/library/{id}/opml: + parameters: + - $ref: '#/components/parameters/pathLibraryId' + get: + operationId: getLibraryOpmlById + summary: Get library OPML + description: Get the library's OPML file by library ID. This endpoint will return the library's OPML file. + tags: + - Library + responses: + '200': + description: OK + content: + application/xml: + schema: + type: string + format: binary + '400': + $ref: '#/components/responses/podcastLibraryOnly' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + post: + operationId: createPodcastByOpml + summary: Create podcast by OPML + description: Add podcasts to library by using an OPML file. This endpoint accepts a file upload or a link to an OPML file. + tags: + - Library + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + opml: + type: string + format: binary + application/json: + schema: + type: object + properties: + url: + type: string + description: The URL to download the OPML file from. + format: uri + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of podcasts imported. + success: + type: array + description: The podcasts successfully imported. + items: + type: string + description: The podcast name which was successfully imported. + errors: + type: array + description: The podcasts that failed to import. + items: + type: string + description: The podcast name which failed to import. + '400': + $ref: '#/components/responses/podcastLibraryOnly' + '403': + $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' /api/library/{id}/podcast-episodes: @@ -3557,27 +3663,6 @@ paths: $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' - /api/library/{id}/opml: - parameters: - - $ref: '#/components/parameters/pathLibraryId' - get: - operationId: getLibraryOpmlById - summary: Get library OPML by ID - description: Generate an OPML file for the library. This endpoint is only available for podcast libraries. - tags: - - Library - responses: - '200': - description: OK - content: - application/xml: - schema: - type: string - format: binary - '403': - $ref: '#/components/responses/podcastLibraryOnly' - '404': - $ref: '#/components/responses/notFound' /api/email/settings: get: operationId: getEmailSettings From 12e95c16902f668ddc1b2565c7408a8d73415a11 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Mon, 7 Oct 2024 21:35:12 -0700 Subject: [PATCH 15/24] Add: initial listening session object --- docs/newRoot.yaml | 125 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 1375981862..341c84d9ca 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -1407,6 +1407,85 @@ components: $ref: '#/components/schemas/userLibrariesAccessible' itemTagsSelected: $ref: '#/components/schemas/userItemTagsSelected' + playMethod: + type: integer + description: The method used to play the media. + enum: [0, 1, 2, 3] + deviceInfo: + type: object + description: Information about the media player. + properties: + id: + $ref: '#/components/schemas/itemId' + userId: + $ref: '#/components/schemas/itemId' + deviceId: + type: string + description: The ID of the device. + ipAddress: + type: string + description: The IP address of the device. + clientVersion: + type: string + description: The version of the client. + manufacturer: + type: string + description: The manufacturer of the device. + model: + type: string + description: The model of the device. + sdkVersion: + type: string + description: The SDK version of the device. + clientName: + type: string + description: The name of the client. + deviceName: + type: string + description: The name of the device. + listeningSessionObject: + type: object + description: A listening session object. + properties: + id: + $ref: '#/components/schemas/itemId' + userId: + $ref: '#/components/schemas/itemId' + libraryId: + $ref: '#/components/schemas/itemId' + libraryItemId: + $ref: '#/components/schemas/itemId' + bookId: + $ref: '#/components/schemas/itemId' + episodeId: + $ref: '#/components/schemas/itemId' + mediaType: + $ref: '#/components/schemas/mediaType' + displayTitle: + $ref: '#/components/schemas/title' + displayAuthor: + $ref: '#/components/schemas/authorName' + mediaDuration: + $ref: '#/components/schemas/duration' + sessionStartTime: + type: number + description: The start time of the session in ms since POSIX epoch. + sessionEndTime: + type: number + description: The end time of the session in ms since POSIX epoch. + mediaStartTime: + type: number + description: The start time within the media. + endTime: + type: number + description: The end time within the media. + timeListened: + type: number + description: The time listened in seconds. + playMethod: + $ref: '#/components/schemas/playMethod' + deviceInfo: + $ref: '#/components/schemas/deviceInfo' responses: badRequest: description: Bad request. @@ -4173,3 +4252,49 @@ paths: $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' + /api/my: + get: + operationId: getMyUser + summary: Get my user + description: Get the user information for the currently logged in user. + tags: + - Myself + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/userObject' + '403': + $ref: '#/components/responses/forbidden' + /api/my/listening-sessions: + get: + operationId: getMyListeningSessions + summary: Get my listening sessions + description: Get the listening sessions for the currently logged in user. + tags: + - Myself + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of listening sessions. + returnCount: + type: integer + description: The number of listening sessions returned. + listeningSessions: + type: array + items: + $ref: '#/components/schemas/listeningSessionObject' + '403': + $ref: '#/components/responses/forbidden' From 77192782a9ae0a9b969837f77c0d22545838bdb7 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Thu, 10 Oct 2024 20:24:17 -0700 Subject: [PATCH 16/24] Add: my progress endpoints --- docs/newRoot.yaml | 225 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 341c84d9ca..1e3c62d1c7 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -66,6 +66,14 @@ components: schema: type: string format: uuid + pathItemId: + name: id + in: path + required: true + description: The ID of the item. This ID can be a book, series, or podcast episode. + schema: + type: string + format: uuid pathAuthorId: name: id in: path @@ -193,6 +201,22 @@ components: schema: type: string enum: ['not-started', 'in-progress', 'completed', 'not-completed'] + queryHideContinueListening: + name: hideFromContinueListening + in: query + required: false + description: Do not include items which are removed from the "Continue Listening" list. True to hide, false to show. + schema: + type: boolean + default: false + queryProgressItemType: + name: progressItemType + in: query + required: false + description: The type of item to filter by progress. If not provided, return all items in progress. + schema: + type: string + enum: ['book', 'series', 'podcast-episode'] queryFilterMissing: name: missing in: query @@ -315,6 +339,15 @@ components: type: string enum: ['username', 'email', 'createdAt', 'updatedAt', 'hasOpenId', 'accountType', 'isEnabled'] default: 'username' + querySortProgress: + name: sort + in: query + required: false + description: The field to sort the progress by. + schema: + type: string + enum: ['progress', 'createdAt', 'updatedAt'] + default: 'updatedAt' queryAuthorName: name: name in: query @@ -1486,6 +1519,56 @@ components: $ref: '#/components/schemas/playMethod' deviceInfo: $ref: '#/components/schemas/deviceInfo' + audioProgress: + type: number + description: The progress of the item in seconds. + audioProgressPercent: + type: number + description: The progress of the item as a percentage. + ebookProgress: + type: string + description: The location in the ebook that the user has read to. + ebookProgressPercent: + type: number + description: The progress of the item as a percentage. + isFinished: + type: boolean + description: Whether the item is finished. + finishedAt: + type: number + description: The time the item was finished in ms since POSIX epoch. + hideFromContinueListening: + type: boolean + description: Whether the item is hidden from the "Continue Listening" section. + lastPlayed: + type: number + description: The last time the item was played in ms since POSIX epoch. + progressObject: + type: object + description: The user progress of an item. + properties: + userId: + $ref: '#/components/schemas/itemId' + itemId: + $ref: '#/components/schemas/itemId' + mediaType: + $ref: '#/components/schemas/mediaType' + audioProgress: + $ref: '#/components/schemas/audioProgress' + audioPercent: + $ref: '#/components/schemas/audioProgressPercent' + ebookProgress: + $ref: '#/components/schemas/ebookProgress' + ebookPercent: + $ref: '#/components/schemas/ebookProgressPercent' + isFinished: + $ref: '#/components/schemas/isFinished' + hideFromContinueListening: + $ref: '#/components/schemas/hideFromContinueListening' + finishedAt: + $ref: '#/components/schemas/finishedAt' + lastPlayed: + $ref: '#/components/schemas/lastPlayed' responses: badRequest: description: Bad request. @@ -4298,3 +4381,145 @@ paths: $ref: '#/components/schemas/listeningSessionObject' '403': $ref: '#/components/responses/forbidden' + /api/my/progress/{id}: + parameters: + - $ref: '#/components/parameters/pathItemId' + get: + operationId: getMyProgress + summary: Get my progress + description: Get the progress for an item for the currently logged in user. The item ID can be a book, series, or podcast episode. + tags: + - Myself + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/progressObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updateMyProgress + summary: Update my progress + description: Update the progress for an item for the currently logged in user. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Myself + requestBody: + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/audioProgress' + - $ref: '#/components/schemas/audioProgressPercent' + - $ref: '#/components/schemas/ebookProgress' + - $ref: '#/components/schemas/ebookProgressPercent' + - $ref: '#/components/schemas/isFinished' + - $ref: '#/components/schemas/hideFromContinueListening' + - $ref: '#/components/schemas/finishedAt' + - $ref: '#/components/schemas/lastPlayed' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/progressObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteMyProgress + summary: Remove my progress + description: Remove the progress for an item for the currently logged in user. + tags: + - Myself + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/progressObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/my/progress/batch-update: + patch: + operationId: updateMyProgressBatch + summary: Update my progress in batch + description: Update the progress for multiple items for the currently logged in user. The request body should contain an array of objects, each containing the item ID and the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Myself + requestBody: + content: + application/json: + schema: + type: array + items: + anyOf: + - $ref: '#/components/schemas/itemId' + - $ref: '#/components/schemas/audioProgress' + - $ref: '#/components/schemas/audioProgressPercent' + - $ref: '#/components/schemas/ebookProgress' + - $ref: '#/components/schemas/ebookProgressPercent' + - $ref: '#/components/schemas/isFinished' + - $ref: '#/components/schemas/hideFromContinueListening' + - $ref: '#/components/schemas/finishedAt' + - $ref: '#/components/schemas/lastPlayed' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + updated: + type: integer + description: The number of items updated. + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + /api/my/progress: + get: + operationId: getMyItemsInProgress + summary: Get all my items in progress + description: Get the items in progress for the currently logged in user. This endpoint only returns the progress object and does not return the full media object for displaying. This endpoint will return all items in progress, with optional paging and sorting. This endpoint can be used to get rows for the home page. + tags: + - Myself + parameters: + - $ref: '#/components/parameters/queryLimit' + - $ref: '#/components/parameters/queryPage' + - $ref: '#/components/parameters/queryHideContinueListening' + - $ref: '#/components/parameters/queryProgressItemType' + - $ref: '#/components/parameters/querySortProgress' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of items in progress. + returnCount: + type: integer + description: The number of items in progress returned. + itemsInProgress: + type: array + items: + $ref: '#/components/schemas/progressObject' + '403': + $ref: '#/components/responses/forbidden' From a6290d1e879e0c473b5d7f68734a564379816dbd Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Fri, 11 Oct 2024 10:31:28 -0700 Subject: [PATCH 17/24] Add: collection and podcast endpoints --- docs/newRoot.yaml | 799 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 796 insertions(+), 3 deletions(-) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 1e3c62d1c7..29e2166796 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -74,6 +74,30 @@ components: schema: type: string format: uuid + pathItemSubId: + name: itemId + in: path + required: true + description: The ID of the sub-item. This ID can be a book, series, or podcast episode. + schema: + type: string + format: uuid + pathCollectionId: + name: id + in: path + required: true + description: The ID of the collection. + schema: + type: string + format: uuid + pathPlaylistId: + name: id + in: path + required: true + description: The ID of the playlist. + schema: + type: string + format: uuid pathAuthorId: name: id in: path @@ -4097,7 +4121,7 @@ paths: - $ref: '#/components/parameters/pathSeriesId' get: operationId: getSeriesById - summary: Get series by ID + summary: Get series description: Get a series by its ID. This endpoint returns all of the information needed for the series page. tags: - Series @@ -4112,7 +4136,7 @@ paths: $ref: '#/components/responses/notFound' patch: operationId: updateSeriesById - summary: Update series by ID + summary: Update series description: Update a series by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. tags: - Series @@ -4144,7 +4168,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deleteSeriesById - summary: Remove series by ID + summary: Remove series description: Remove the series and associated entries from the database. This does not delete any files from the filesystem. tags: - Series @@ -4523,3 +4547,772 @@ paths: $ref: '#/components/schemas/progressObject' '403': $ref: '#/components/responses/forbidden' + /api/collection/{id}/books: + parameters: + - $ref: '#/components/parameters/pathCollectionId' + get: + operationId: getCollectionBooksById + summary: Get collection of books + description: Get a collection by its ID. This endpoint returns all of the information needed for the collection details page and editing. + tags: + - Collection + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/bookLibraryOnly' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updateBookCollectionById + summary: Update book collection + description: Update a collection by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Collection + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/title' + description: + $ref: '#/components/schemas/description' + imageUrl: + $ref: '#/components/schemas/imageUrl' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteBookCollectionById + summary: Remove book collection + description: Remove the collection and associated entries from the database. This does not delete any files from the filesystem. + tags: + - Collection + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/collection/{id}/update-book: + parameters: + - $ref: '#/components/parameters/pathCollectionId' + post: + operationId: addOrUpdateBookToCollection + summary: Add or update book in collection + description: Add or update a book in a collection. The request body should contain the book ID and the position in the collection. If the position is not specified, the book is added to the end of the collection. + tags: + - Collection + requestBody: + content: + application/json: + schema: + type: object + properties: + bookId: + $ref: '#/components/schemas/itemId' + position: + type: integer + description: The position in the collection to add the book. + minimum: 0 + nullable: true + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/collection/{id}/remove-book/{itemId}: + parameters: + - $ref: '#/components/parameters/pathCollectionId' + - $ref: '#/components/parameters/pathItemSubId' + delete: + operationId: removeBookFromCollection + summary: Remove book from collection + description: Remove a book from a collection. + tags: + - Collection + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/collection/{id}/bulk/update-book: + parameters: + - $ref: '#/components/parameters/pathCollectionId' + post: + operationId: bulkUpdateCollectionBooks + summary: Bulk update collection books + description: Bulk update the books in a collection. The request body should contain an array of objects, each containing the book ID and the position in the collection. If the position is not specified, books are added to the end of the collection in the order they are specified in the request. + tags: + - Collection + requestBody: + content: + application/json: + schema: + type: array + items: + type: object + properties: + bookId: + $ref: '#/components/schemas/itemId' + position: + type: integer + description: The position in the collection to add the book. + minimum: 0 + nullable: true + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + /api/collection/{id}/bulk/remove-book: + parameters: + - $ref: '#/components/parameters/pathCollectionId' + post: + operationId: bulkRemoveBooksFromCollection + summary: Bulk remove books from collection + description: Bulk remove books from a collection. The request body should contain an array of book IDs to remove. + tags: + - Collection + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/itemId' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/collection/{id}/podcast-episodes: + parameters: + - $ref: '#/components/parameters/pathCollectionId' + get: + operationId: getCollectionPodcastEpisodesById + summary: Get collection of podcast episodes + description: Get a collection by its ID. This endpoint returns all of the information needed for the collection details page and editing. + tags: + - Collection + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/podcastLibraryOnly' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updatePodcastEpisodeCollectionById + summary: Update podcast episode collection + description: Update a collection by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Collection + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/title' + description: + $ref: '#/components/schemas/description' + imageUrl: + $ref: '#/components/schemas/imageUrl' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deletePodcastEpisodeCollectionById + summary: Remove podcast episode collection + description: Remove the collection and associated entries from the database. This does not delete any files from the filesystem. + tags: + - Collection + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/collection/{id}/update-podcast-episode: + parameters: + - $ref: '#/components/parameters/pathCollectionId' + post: + operationId: addOrUpdatePodcastEpisodeToCollection + summary: Add or update podcast episode in collection + description: Add or update a podcast episode in a collection. The request body should contain the podcast episode ID and the position in the collection. If the position is not specified, the podcast episode is added to the end of the collection. + tags: + - Collection + requestBody: + content: + application/json: + schema: + type: object + properties: + podcastEpisodeId: + $ref: '#/components/schemas/itemId' + position: + type: integer + description: The position in the collection to add the podcast episode. + minimum: 0 + nullable: true + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/collection/{id}/remove-podcast-episode/{itemId}: + parameters: + - $ref: '#/components/parameters/pathCollectionId' + - $ref: '#/components/parameters/pathItemSubId' + delete: + operationId: removePodcastEpisodeFromCollection + summary: Remove podcast episode from collection + description: Remove a podcast episode from a collection. + tags: + - Collection + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/collection/{id}/bulk/update-podcast-episode: + parameters: + - $ref: '#/components/parameters/pathCollectionId' + post: + operationId: bulkUpdateCollectionPodcastEpisodes + summary: Bulk update collection podcast episodes + description: Bulk update the podcast episodes in a collection. The request body should contain an array of objects, each containing the podcast episode ID and the position in the collection. If the position is not specified, podcast episodes are added to the end of the collection in the order they are specified in the request. + tags: + - Collection + requestBody: + content: + application/json: + schema: + type: array + items: + type: object + properties: + podcastEpisodeId: + $ref: '#/components/schemas/itemId' + position: + type: integer + description: The position in the collection to add the podcast episode. + minimum: 0 + nullable: true + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + /api/collection/{id}/bulk/remove-podcast-episode: + parameters: + - $ref: '#/components/parameters/pathCollectionId' + post: + operationId: bulkRemovePodcastEpisodesFromCollection + summary: Bulk remove podcast episodes from collection + description: Bulk remove podcast episodes from a collection. The request body should contain an array of podcast episode IDs to remove. + tags: + - Collection + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/itemId' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + # We want to do the same thing as collections for playlists + /api/playlist/{id}/books: + parameters: + - $ref: '#/components/parameters/pathPlaylistId' + get: + operationId: getPlaylistBooksById + summary: Get playlist of books + description: Get a playlist by its ID. This endpoint returns all of the information needed for the playlist details page and editing. + tags: + - Playlist + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/bookLibraryOnly' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updateBookPlaylistById + summary: Update book playlist + description: Update a playlist by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Playlist + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/title' + description: + $ref: '#/components/schemas/description' + imageUrl: + $ref: '#/components/schemas/imageUrl' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteBookPlaylistById + summary: Remove book playlist + description: Remove the playlist and associated entries from the database. This does not delete any files from the filesystem. + tags: + - Playlist + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/playlist/{id}/update-book: + parameters: + - $ref: '#/components/parameters/pathPlaylistId' + post: + operationId: addOrUpdateBookToPlaylist + summary: Add or update book in playlist + description: Add or update a book in a playlist. The request body should contain the book ID and the position in the playlist. If the position is not specified, the book is added to the end of the playlist. + tags: + - Playlist + requestBody: + content: + application/json: + schema: + type: object + properties: + bookId: + $ref: '#/components/schemas/itemId' + position: + type: integer + description: The position in the playlist to add the book. + minimum: 0 + nullable: true + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/playlist/{id}/remove-book/{itemId}: + parameters: + - $ref: '#/components/parameters/pathPlaylistId' + - $ref: '#/components/parameters/pathItemSubId' + delete: + operationId: removeBookFromPlaylist + summary: Remove book from playlist + description: Remove a book from a playlist. + tags: + - Playlist + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/playlist/{id}/bulk/update-book: + parameters: + - $ref: '#/components/parameters/pathPlaylistId' + post: + operationId: bulkUpdatePlaylistBooks + summary: Bulk update playlist books + description: Bulk update the books in a playlist. The request body should contain an array of objects, each containing the book ID and the position in the playlist. If the position is not specified, books are added to the end of the playlist in the order they are specified in the request. + tags: + - Playlist + requestBody: + content: + application/json: + schema: + type: array + items: + type: object + properties: + bookId: + $ref: '#/components/schemas/itemId' + position: + type: integer + description: The position in the playlist to add the book. + minimum: 0 + nullable: true + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + /api/playlist/{id}/bulk/remove-book: + parameters: + - $ref: '#/components/parameters/pathPlaylistId' + post: + operationId: bulkRemoveBooksFromPlaylist + summary: Bulk remove books from playlist + description: Bulk remove books from a playlist. The request body should contain an array of book IDs to remove. + tags: + - Playlist + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/itemId' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/playlist/{id}/podcast-episodes: + parameters: + - $ref: '#/components/parameters/pathPlaylistId' + get: + operationId: getPlaylistPodcastEpisodesById + summary: Get playlist of podcast episodes + description: Get a playlist by its ID. This endpoint returns all of the information needed for the playlist details page and editing. + tags: + - Playlist + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/podcastLibraryOnly' + '404': + $ref: '#/components/responses/notFound' + patch: + operationId: updatePodcastEpisodePlaylistById + summary: Update podcast episode playlist + description: Update a playlist by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. + tags: + - Playlist + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + $ref: '#/components/schemas/title' + description: + $ref: '#/components/schemas/description' + imageUrl: + $ref: '#/components/schemas/imageUrl' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deletePodcastEpisodePlaylistById + summary: Remove podcast episode playlist + description: Remove the playlist and associated entries from the database. This does not delete any files from the filesystem. + tags: + - Playlist + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/playlist/{id}/update-podcast-episode: + parameters: + - $ref: '#/components/parameters/pathPlaylistId' + post: + operationId: addOrUpdatePodcastEpisodeToPlaylist + summary: Add or update podcast episode in playlist + description: Add or update a podcast episode in a playlist. The request body should contain the podcast episode ID and the position in the playlist. If the position is not specified, the podcast episode is added to the end of the playlist. + tags: + - Playlist + requestBody: + content: + application/json: + schema: + type: object + properties: + podcastEpisodeId: + $ref: '#/components/schemas/itemId' + position: + type: integer + description: The position in the playlist to add the podcast episode. + minimum: 0 + nullable: true + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/playlist/{id}/remove-podcast-episode/{itemId}: + parameters: + - $ref: '#/components/parameters/pathPlaylistId' + - $ref: '#/components/parameters/pathItemSubId' + delete: + operationId: removePodcastEpisodeFromPlaylist + summary: Remove podcast episode from playlist + description: Remove a podcast episode from a playlist. + tags: + - Playlist + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/playlist/{id}/bulk/update-podcast-episode: + parameters: + - $ref: '#/components/parameters/pathPlaylistId' + post: + operationId: bulkUpdatePlaylistPodcastEpisodes + summary: Bulk update playlist podcast episodes + description: Bulk update the podcast episodes in a playlist. The request body should contain an array of objects, each containing the podcast episode ID and the position in the playlist. If the position is not specified, podcast episodes are added to the end of the playlist in the order they are specified in the request. + tags: + - Playlist + requestBody: + content: + application/json: + schema: + type: array + items: + type: object + properties: + podcastEpisodeId: + $ref: '#/components/schemas/itemId' + position: + type: integer + description: The position in the playlist to add the podcast episode. + minimum: 0 + nullable: true + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + /api/playlist/{id}/bulk/remove-podcast-episode: + parameters: + - $ref: '#/components/parameters/pathPlaylistId' + post: + operationId: bulkRemovePodcastEpisodesFromPlaylist + summary: Bulk remove podcast episodes from playlist + description: Bulk remove podcast episodes from a playlist. The request body should contain an array of podcast episode IDs to remove. + tags: + - Playlist + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/itemId' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/podcastEpisodeCollectionDisplayObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' From a6ea614690d4a100c5ae473d25e1295bb1b265e2 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Fri, 11 Oct 2024 10:53:26 -0700 Subject: [PATCH 18/24] Backup endpoints --- docs/newRoot.yaml | 300 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 299 insertions(+), 1 deletion(-) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 29e2166796..d8a909e6da 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -129,6 +129,14 @@ components: description: The slug of the feed. schema: type: string + pathBackupId: + name: id + in: path + required: true + description: The ID of the backup. + schema: + type: string + format: uuid queryLimit: name: limit in: query @@ -1593,6 +1601,54 @@ components: $ref: '#/components/schemas/finishedAt' lastPlayed: $ref: '#/components/schemas/lastPlayed' + backupObject: + type: object + description: A backup object. + properties: + id: + $ref: '#/components/schemas/itemId' + databaseType: + type: string + description: The type of database. + example: sqlite + backupTime: + type: number + description: The time the backup was created in ms since POSIX epoch. + backupSize: + $ref: '#/components/schemas/size' + serverVersion: + type: string + description: The version of the server when the backup was created. + example: 2.14.0 + path: + type: string + description: The path to the backup file. + fullPath: + type: string + description: The full path to the backup file. + filename: + type: string + description: The name of the backup file. + backupSettings: + type: object + description: The backup settings for the server. + properties: + automaticBackupsEnabled: + type: boolean + description: Whether automatic backups are enabled. + backupSchedule: + type: string + description: The cron schedule for automatic backups. + maxBackups: + type: integer + description: The maximum number of backups to keep. Use 0 for unlimited + minimum: 0 + default: 5 + maxBackupSize: + type: integer + description: The maximum size of a backup in GB. Use 0 for unlimited + minimum: 0 + default: 1 responses: badRequest: description: Bad request. @@ -4547,6 +4603,38 @@ paths: $ref: '#/components/schemas/progressObject' '403': $ref: '#/components/responses/forbidden' + /api/my/password: + patch: + operationId: updateMyPassword + summary: Update my password + description: Update the password for the currently logged in user. The request body should contain the current password and the new password. + tags: + - Myself + requestBody: + content: + application/json: + schema: + type: object + properties: + currentPassword: + $ref: '#/components/schemas/userPassword' + newPassword: + $ref: '#/components/schemas/userPassword' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' /api/collection/{id}/books: parameters: - $ref: '#/components/parameters/pathCollectionId' @@ -4931,7 +5019,6 @@ paths: $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' - # We want to do the same thing as collections for playlists /api/playlist/{id}/books: parameters: - $ref: '#/components/parameters/pathPlaylistId' @@ -5316,3 +5403,214 @@ paths: $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' + /api/backup: + get: + operationId: getAllBackups + summary: Get all backups + description: Get all backups. This endpoint returns all of the information needed for the backups page. + tags: + - Backup + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + backups: + type: array + items: + $ref: '#/components/schemas/backupObject' + '403': + $ref: '#/components/responses/forbidden' + patch: + operationId: updateBackupSettings + summary: Update backup settings + description: Update the backup settings. The request body should contain the new settings. This endpoint will return a 400 error if the backup path is set by an environment variable. + tags: + - Backup + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/backupSettings' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/backupSettings' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + /api/backup/create: + post: + operationId: createBackup + summary: Create backup + description: Create a backup. This endpoint creates a backup of the database and files and returns the backup information. + tags: + - Backup + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/backupObject' + '403': + $ref: '#/components/responses/forbidden' + /api/backup/{id}: + parameters: + - $ref: '#/components/parameters/pathBackupId' + get: + operationId: getBackupById + summary: Get backup by ID + description: Get a backup by its ID. This endpoint returns all of the information needed for the backup details page. + tags: + - Backup + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/backupObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteBackupById + summary: Remove backup + description: Remove a backup by its ID. This endpoint removes the backup from the database and deletes the backup files. + tags: + - Backup + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/backupObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/backup/{id}/apply: + parameters: + - $ref: '#/components/parameters/pathBackupId' + post: + operationId: applyBackupById + summary: Apply backup + description: Apply a backup by its ID. This endpoint restores the database and files from the backup. + tags: + - Backup + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/backupObject' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/backup/{id}/download: + parameters: + - $ref: '#/components/parameters/pathBackupId' + get: + operationId: downloadBackupById + summary: Download backup + description: Download a backup by its ID. This endpoint returns the backup files as a zip archive. + tags: + - Backup + responses: + '200': + description: OK + content: + application/zip: + schema: + type: string + format: binary + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/backup/upload: + post: + operationId: uploadBackup + summary: Upload backup + description: Upload a backup. This endpoint uploads a backup zip archive. + tags: + - Backup + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + backup: + type: string + format: binary + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/backupObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + /api/backup/path: + get: + operationId: getBackupPath + summary: Get backup path + description: Get the path to the backup directory. + tags: + - Backup + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + path: + type: string + '403': + $ref: '#/components/responses/forbidden' + patch: + operationId: updateBackupPath + summary: Update backup path + description: Update the path to the backup directory. The request body should contain the new path. This endpoint will return a 400 error if the backup path is set by an environment variable. + tags: + - Backup + requestBody: + content: + application/json: + schema: + type: object + properties: + path: + $ref: '#/components/schemas/folderPath' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/folderPath' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' From b41ea7880818e892e88e884f7d848a2241ce26d9 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Fri, 11 Oct 2024 17:26:25 -0700 Subject: [PATCH 19/24] Add: bookmarks and Tools endpoints --- docs/newRoot.yaml | 236 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index d8a909e6da..f7c10300e4 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -1601,6 +1601,19 @@ components: $ref: '#/components/schemas/finishedAt' lastPlayed: $ref: '#/components/schemas/lastPlayed' + bookmarkObject: + type: object + description: A bookmark object. + properties: + itemId: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + position: + type: number + description: The position of the bookmark in seconds. + createdAt: + $ref: '#/components/schemas/createdAt' backupObject: type: object description: A backup object. @@ -4603,6 +4616,91 @@ paths: $ref: '#/components/schemas/progressObject' '403': $ref: '#/components/responses/forbidden' + /api/my/bookmark: + get: + operationId: getMyBookmarks + summary: Get my bookmarks + description: Get the bookmarks for the currently logged in user. This endpoint will return all bookmarks. + tags: + - Myself + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: The total number of bookmarks. + bookmarks: + type: array + items: + $ref: '#/components/schemas/bookmarkObject' + '403': + $ref: '#/components/responses/forbidden' + post: + operationId: createMyBookmark + summary: Create my bookmark + description: Create a new bookmark for the currently logged in user. The request body should contain the item ID, title, and the position in the item. Each bookmark for an item must have a unique title. If the title is included, the existing bookmark will be updated. + tags: + - Myself + requestBody: + content: + application/json: + schema: + type: object + properties: + itemId: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + position: + type: integer + description: The position in the item to bookmark. + minimum: 0 + nullable: true + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookmarkObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + delete: + operationId: deleteMyBookmark + summary: Remove my bookmark + description: Remove a bookmark for the currently logged in user. The item ID and title of the bookmark must be specified. + tags: + - Myself + requestBody: + content: + application/json: + schema: + type: object + properties: + itemId: + $ref: '#/components/schemas/itemId' + title: + $ref: '#/components/schemas/title' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/bookmarkObject' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' /api/my/password: patch: operationId: updateMyPassword @@ -5614,3 +5712,141 @@ paths: $ref: '#/components/responses/badRequest' '403': $ref: '#/components/responses/forbidden' + /api/tool/encode/{id}: + parameters: + - $ref: '#/components/parameters/pathBookId' + post: + operationId: encodeToolById + summary: Encode book + description: Encode audiobook to M4B. This endpoint begins the encoding process for the book with the specified ID. + tags: + - Tool + requestBody: + content: + application/json: + schema: + type: object + properties: + bitrate: + type: string + description: The bitrate to encode the audiobook to. + example: '64k' + codec: + type: string + description: The codec to encode the audiobook with. Use `copy` to not re-encode the audio if the source codec is compatible with M4B. + example: 'aac' + channels: + type: integer + description: The number of channels to encode the audiobook with. + example: 1 + required: true + responses: + '200': + description: OK + content: + text/plain: + schema: + type: string + example: 'Encoding started.' + '400': + $ref: '#/components/responses/bookLibraryOnly' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: cancelEncodeToolById + summary: Cancel encode + description: Cancel the encoding process for the book with the specified ID. + tags: + - Tool + responses: + '200': + description: OK + content: + text/plain: + schema: + type: string + example: 'Encoding canceled.' + '400': + $ref: '#/components/responses/bookLibraryOnly' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/tool/embed-metadata/{id}: + parameters: + - $ref: '#/components/parameters/pathBookId' + post: + operationId: embedMetadataToolById + summary: Embed metadata + description: Embed metadata into an audiobook. This endpoint embeds metadata into the book with the specified ID. + tags: + - Tool + requestBody: + content: + application/json: + schema: + type: object + properties: + forceEmbedChapters: + type: string + description: Whether to force embedding chapters even if the book already has chapters. Use `1` to force embed chapters. + enum: ['0', '1'] + backup: + type: string + description: Whether to create a backup of the book before embedding metadata. Use `1` to create a backup. + enum: ['0', '1'] + responses: + '200': + description: OK + content: + text/plain: + schema: + type: string + example: 'Metadata embedded.' + '400': + $ref: '#/components/responses/bookLibraryOnly' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/tool/embed-metadata/batch: + post: + operationId: embedMetadataToolBatch + summary: Batch embed metadata + description: Embed metadata into multiple audiobooks. This endpoint embeds metadata into the books with the specified IDs. + tags: + - Tool + requestBody: + content: + application/json: + schema: + type: object + properties: + bookIds: + type: array + items: + $ref: '#/components/schemas/itemId' + description: The IDs of the books to embed metadata into. + forceEmbedChapters: + type: string + description: Whether to force embedding chapters even if the book already has chapters. Use `1` to force embed chapters. + enum: ['0', '1'] + backup: + type: string + description: Whether to create a backup of the book before embedding metadata. Use `1` to create a backup. + enum: ['0', '1'] + required: true + responses: + '200': + description: OK + content: + text/plain: + schema: + type: string + example: 'Metadata embed began.' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' From c08c2a62cbf5f438b12b984ba54ecf46c91afb79 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Fri, 11 Oct 2024 19:21:40 -0700 Subject: [PATCH 20/24] Add: search endpoints --- docs/newRoot.yaml | 181 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index f7c10300e4..8108fb813b 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -401,6 +401,57 @@ components: description: The region to search for the book or author. schema: type: string + querySearchProvider: + name: provider + in: query + required: false + description: The provider to use for searching. + schema: + type: string + querySearchTitle: + name: title + in: query + required: false + description: The title to use for searching. + schema: + type: string + querySearchAuthor: + name: author + in: query + required: false + description: The author to use for searching. + schema: + type: string + querySearchId: + name: id + in: query + required: false + description: The library item ID to use for searching. + schema: + type: string + querySearchIsPodcast: + name: isPodcast + in: query + required: false + description: Whether the search is for a podcast. + schema: + type: integer + description: 0 for false, 1 for true + enum: [0, 1] + querySearchTerm: + name: term + in: query + required: false + description: The search term to use for searching. + schema: + type: string + querySearchAsin: + name: asin + in: query + required: false + description: The ASIN to use for searching. + schema: + type: string schemas: itemId: type: string @@ -5850,3 +5901,133 @@ paths: $ref: '#/components/responses/badRequest' '403': $ref: '#/components/responses/forbidden' + /api/search/covers: + get: + operationId: findCovers + summary: Find covers + description: Find covers by title. This endpoint searches for covers by title and returns the results. + tags: + - Search + parameters: + - $ref: '#/components/parameters/querySearchTitle' + - $ref: '#/components/parameters/querySearchAuthor' + - $ref: '#/components/parameters/querySearchProvider' + - $ref: '#/components/parameters/querySearchIsPodcast' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + covers: + type: array + items: + $ref: '#/components/schemas/imageUrl' + '400': + $ref: '#/components/responses/badRequest' + /api/search/book: + get: + operationId: findBooks + summary: Find books + description: Find books by title. This endpoint searches for books by title and returns the results. + tags: + - Search + parameters: + - $ref: '#/components/parameters/querySearchId' + - $ref: '#/components/parameters/querySearchTitle' + - $ref: '#/components/parameters/querySearchAuthor' + - $ref: '#/components/parameters/querySearchProvider' + - $ref: '#/components/parameters/querySearchIsPodcast' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + books: + type: array + items: + anyOf: + - $ref: '#/components/schemas/imageUrl' + - $ref: '#/components/schemas/title' + - $ref: '#/components/schemas/authorName' + - $ref: '#/components/schemas/description' + - $ref: '#/components/schemas/publishYear' + - $ref: '#/components/schemas/genreArray' + '400': + $ref: '#/components/responses/badRequest' + /api/search/podcast: + get: + operationId: findPodcasts + summary: Find podcasts + description: Find podcasts by title. This endpoint searches for podcasts by title and returns the results. + tags: + - Search + parameters: + - $ref: '#/components/parameters/querySearchTerm' + - $ref: '#/components/parameters/querySearchProvider' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + podcasts: + type: array + items: + $ref: '#/components/schemas/podcastMatchObject' + '400': + $ref: '#/components/responses/badRequest' + /api/search/chapter: + get: + operationId: findChapters + summary: Find chapters + description: Find chapters by title. This endpoint searches for chapters by ASIN (Audible SIN) and returns the results. + tags: + - Search + parameters: + - $ref: '#/components/parameters/querySearchAsin' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + lengthSeconds: + type: integer + description: The length of the book in seconds. + example: 1234 + lengthMilliseconds: + type: integer + description: The length of the book in milliseconds. + example: 1234567 + chapters: + type: array + items: + type: object + description: The chapter information. + properties: + title: + $ref: '#/components/schemas/title' + startTime: + type: integer + description: The start time of the chapter in milliseconds. + example: 123456 + endTime: + type: integer + description: The end time of the chapter in milliseconds. + example: 234567 + length: + type: integer + description: The length of the chapter in milliseconds. + example: 11111 + '400': + $ref: '#/components/responses/badRequest' From af1f4e7971a8cf3d309835160b172a35e75f228c Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Fri, 11 Oct 2024 19:26:16 -0700 Subject: [PATCH 21/24] Add: cache clearing --- docs/newRoot.yaml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 8108fb813b..7d2f30764e 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -6031,3 +6031,39 @@ paths: example: 11111 '400': $ref: '#/components/responses/badRequest' + /api/cache/purge-all: + post: + operationId: purgeAllCache + summary: Purge all cache + description: Purge all cache. This endpoint purges all cache entries under `metadata/cache`. + tags: + - Cache + responses: + '200': + description: OK + content: + text/plain: + schema: + type: string + example: 'Server cache purged.' + '403': + $ref: '#/components/responses/forbidden' + /api/cache/purge-items: + post: + operationId: purgeCacheItems + summary: Purge cache items + description: Purge cache items. This endpoint purges cache entries by key under `metadata/cache/items`. + tags: + - Cache + responses: + '200': + description: OK + content: + text/plain: + schema: + type: string + example: 'Item cache purged.' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' From 75861ca6db1159580f359a66ef3822a0fa888995 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Fri, 11 Oct 2024 20:17:08 -0700 Subject: [PATCH 22/24] Initial misc routes --- docs/newRoot.yaml | 241 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 7d2f30764e..04ea6204b5 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -6067,3 +6067,244 @@ paths: $ref: '#/components/responses/badRequest' '403': $ref: '#/components/responses/forbidden' + /api/upload/book: + post: + operationId: uploadBook + summary: Upload book + description: Upload a book. This endpoint uploads a book zip archive. + tags: + - Upload + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + book: + type: string + format: binary + required: true + responses: + '200': + description: OK + content: + text/html: + schema: + type: string + example: 'Book uploaded.' + '400': + $ref: '#/components/responses/bookLibraryOnly' + '403': + $ref: '#/components/responses/forbidden' + /api/upload/podcast: + post: + operationId: uploadPodcast + summary: Upload podcast + description: Upload a podcast. This endpoint uploads a podcast zip archive. + tags: + - Upload + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + podcast: + type: string + format: binary + required: true + responses: + '200': + description: OK + content: + text/html: + schema: + type: string + example: 'Podcast uploaded.' + '400': + $ref: '#/components/responses/podcastLibraryOnly' + '403': + $ref: '#/components/responses/forbidden' + /api/tags: + get: + operationId: getAllTags + summary: Get all tags + description: Get all tags. This endpoint returns all of the tags in the database. + tags: + - Misc + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + tags: + type: array + items: + $ref: '#/components/schemas/tag' + '403': + $ref: '#/components/responses/forbidden' + /api/tag/{tag}: + parameters: + - $ref: '#/components/parameters/pathTag' + post: + operationId: renameTag + summary: Rename tag + description: Rename the specificed tag. + tags: + - Misc + requestBody: + content: + application/json: + schema: + type: object + properties: + newName: + $ref: '#/components/schemas/tag' + required: true + responses: + '200': + description: OK + content: + text/html: + schema: + type: string + example: 'Tag renamed.' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteTag + summary: Remove tag + description: Remove the specified tag. + tags: + - Misc + responses: + '200': + description: OK + content: + text/html: + schema: + type: string + example: 'Tag deleted.' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/genre: + get: + operationId: getAllGenres + summary: Get all genres + description: Get all genres. This endpoint returns all of the genres in the database. + tags: + - Misc + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + genres: + type: array + items: + $ref: '#/components/schemas/genre' + '403': + $ref: '#/components/responses/forbidden' + /api/genre/{gerne}: + parameters: + - $ref: '#/components/parameters/pathGenre' + post: + operationId: renameGenre + summary: Rename genre + description: Rename the specificed genre. + tags: + - Misc + requestBody: + content: + application/json: + schema: + type: object + properties: + newName: + $ref: '#/components/schemas/genre' + required: true + responses: + '200': + description: OK + content: + text/html: + schema: + type: string + example: 'Genre renamed.' + '400': + $ref: '#/components/responses/badRequest' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + delete: + operationId: deleteGenre + summary: Remove genre + description: Remove the specified genre. + tags: + - Misc + responses: + '200': + description: OK + content: + text/html: + schema: + type: string + example: 'Genre deleted.' + '403': + $ref: '#/components/responses/forbidden' + '404': + $ref: '#/components/responses/notFound' + /api/validate-cron: + post: + operationId: validateCronExpression + summary: Validate cron expression + description: Validate a cron expression. This endpoint returns whether the cron expression is valid. + tags: + - Misc + requestBody: + content: + application/json: + schema: + type: object + properties: + cron: + type: string + description: The cron expression to validate. + example: '0 0 * * *' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + valid: + type: boolean + description: Whether the cron expression is valid. + '400': + $ref: '#/components/responses/badRequest' + #this.router.post('/upload', MiscController.handleUpload.bind(this)) + #this.router.get('/tasks', MiscController.getTasks.bind(this)) + #this.router.patch('/settings', MiscController.updateServerSettings.bind(this)) + #this.router.patch('/sorting-prefixes', MiscController.updateSortingPrefixes.bind(this)) + #this.router.post('/authorize', MiscController.authorize.bind(this)) + #this.router.get('/auth-settings', MiscController.getAuthSettings.bind(this)) + #this.router.patch('/auth-settings', MiscController.updateAuthSettings.bind(this)) + #this.router.post('/watcher/update', MiscController.updateWatchedPath.bind(this)) + #this.router.get('/stats/year/:year', MiscController.getAdminStatsForYear.bind(this)) + #this.router.get('/logger-data', MiscController.getLoggerData.bind(this)) From edf04414c45ce8ab7f04ee58bd61ff7a24cacd46 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sat, 2 Nov 2024 10:06:44 -0700 Subject: [PATCH 23/24] Fix: tag and genre path, remove "by ID" text --- docs/newRoot.yaml | 131 +++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 60 deletions(-) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 04ea6204b5..9083bb02de 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -82,6 +82,20 @@ components: schema: type: string format: uuid + pathTag: + name: tag + in: path + required: true + description: The tag name. + schema: + type: string + pathGenre: + name: genre + in: path + required: true + description: The genre name. + schema: + type: string pathCollectionId: name: id in: path @@ -1758,7 +1772,7 @@ paths: - $ref: '#/components/parameters/pathBookId' get: operationId: getBookById - summary: Get book by ID + summary: Get book description: Get a book by its ID. This endpoint returns all of the information needed for the book details page and editing. tags: - Book @@ -1773,7 +1787,7 @@ paths: $ref: '#/components/responses/notFound' post: operationId: updateBookById - summary: Update book by ID + summary: Update book description: Update a book by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. tags: - Book @@ -1823,7 +1837,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deleteBookById - summary: Remove book by ID + summary: Remove book description: Remove the book and associated entries from the database. This does not delete any files from the filesystem. If files should be deleted, use the `/api/book/{id}/hardDelete` endpoint instead. tags: - Book @@ -1843,7 +1857,7 @@ paths: - $ref: '#/components/parameters/pathBookId' delete: operationId: hardDeleteBookById - summary: Hard delete book by ID + summary: Hard delete book description: Hard delete the book and associated entries from the database. This deletes the book's files from the filesystem. This action cannot be undone. tags: - Book @@ -1863,7 +1877,7 @@ paths: - $ref: '#/components/parameters/pathBookId' get: operationId: downloadBookById - summary: Download book by ID + summary: Download book description: Download the book by its ID. This endpoint will return the book's files as a zip archive. tags: - Book @@ -1884,8 +1898,9 @@ paths: - $ref: '#/components/parameters/pathBookId' get: operationId: getBookCoverById - summary: Get book cover by ID + summary: Get book cover description: Get the book cover by its ID. This endpoint will return the book's cover image. If no query parameters are provided, the image will be returned in the original format with the original dimensions. + security: [] # No security for getting image tags: - Book parameters: @@ -1927,13 +1942,11 @@ paths: schema: type: string format: binary - '403': - $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' post: operationId: uploadBookCoverById - summary: Upload book cover by ID + summary: Upload book cover description: Upload the book cover image to the book by the book ID. This endpoint will replace the book's cover image with the provided image. The image should be in JPEG, PNG, or WebP format. Alternatively, the image can be provided as a URL to download the image from. tags: - Book @@ -1974,7 +1987,7 @@ paths: $ref: '#/components/responses/notFound' patch: operationId: updateBookCoverById - summary: Update book cover by ID + summary: Update book cover description: Update the book cover to be an existing image in the database. This endpoint will replace the book's cover image with the provided image. The image should be in JPEG, PNG, or WebP format. tags: - Book @@ -2006,7 +2019,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deleteBookCoverById - summary: Remove book cover by ID + summary: Remove book cover description: Remove the book cover image from the book. The cover image file is not deleted but is no longer associated with the book. tags: - Book @@ -2030,7 +2043,7 @@ paths: - $ref: '#/components/parameters/pathBookId' post: operationId: matchBookById - summary: Match book by ID + summary: Match book description: Match the book selected by ID against an online database. This performs a quick match against the online database and returns the best match. Quick match will apply the cover from the first match and fill empty metadata fields. Metadata fields are not overwritten unless the "Prefer Matched Metadata" setting is enabled or the "force" query is set. tags: - Book @@ -2129,7 +2142,7 @@ paths: - $ref: '#/components/parameters/pathBookId' post: operationId: scanBookById - summary: Scan book by ID + summary: Scan book description: Scan the book by its ID. This endpoint will scan the book's files and update the book's metadata based on the files found according to the metadata priority settings. tags: - Book @@ -2149,7 +2162,7 @@ paths: - $ref: '#/components/parameters/pathPodcastId' get: operationId: getPodcastById - summary: Get podcast by ID + summary: Get podcast description: Get a podcast by its ID. This endpoint returns all of the information needed for the podcast details page and editing, but does not include file information or podcast episode information. tags: - Podcast @@ -2164,7 +2177,7 @@ paths: $ref: '#/components/responses/notFound' post: operationId: updatePodcastById - summary: Update podcast by ID + summary: Update podcast description: Update a podcast by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. tags: - Podcast @@ -2221,7 +2234,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deletePodcastById - summary: Remove podcast by ID + summary: Remove podcast description: Remove the podcast and associated entries from the database. This does not delete any files from the filesystem. tags: - Podcast @@ -2241,7 +2254,7 @@ paths: - $ref: '#/components/parameters/pathPodcastId' delete: operationId: hardDeletePodcastById - summary: Hard delete podcast by ID + summary: Hard delete podcast description: Hard delete the podcast and associated entries from the database. This deletes the podcast's files from the filesystem. This action cannot be undone. tags: - Podcast @@ -2261,7 +2274,7 @@ paths: - $ref: '#/components/parameters/pathPodcastId' get: operationId: downloadPodcastById - summary: Download podcast by ID + summary: Download podcast description: Download the podcast by its ID. This endpoint will return the podcast's files as a zip archive. tags: - Podcast @@ -2319,7 +2332,7 @@ paths: - $ref: '#/components/parameters/pathPodcastId' get: operationId: getPodcastDownloadQueueById - summary: Get podcast download queue by ID + summary: Get podcast download queue description: Get the podcast download queue by its ID. This endpoint will return the podcast's download queue, which includes the episodes that are queued for download. tags: - Podcast @@ -2374,7 +2387,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: clearPodcastDownloadQueueById - summary: Clear podcast download queue by ID + summary: Clear podcast download queue description: Clear the podcast download queue by its ID. This endpoint will remove all episodes from the podcast's download queue. tags: - Podcast @@ -2400,7 +2413,7 @@ paths: - $ref: '#/components/parameters/pathPodcastId' get: operationId: getPodcastCoverById - summary: Get podcast cover by ID + summary: Get podcast cover description: Get the podcast cover by its ID. This endpoint will return the podcast's cover image. If no query parameters are provided, the image will be returned in the original format with the original dimensions. tags: - Podcast @@ -2443,13 +2456,11 @@ paths: schema: type: string format: binary - '403': - $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' post: operationId: uploadPodcastCoverById - summary: Upload podcast cover by ID + summary: Upload podcast cover description: Upload the podcast cover image to the podcast by the podcast ID. This endpoint will replace the podcast's cover image with the provided image. The image should be in JPEG, PNG, or WebP format. Alternatively, the image can be provided as a URL to download the image from. tags: - Podcast @@ -2490,7 +2501,7 @@ paths: $ref: '#/components/responses/notFound' patch: operationId: updatePodcastCoverById - summary: Update podcast cover by ID + summary: Update podcast cover description: Update the podcast cover to be an existing image in the database. This endpoint will replace the podcast's cover image with the provided image. The image should be in JPEG, PNG, or WebP format. tags: - Podcast @@ -2522,7 +2533,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deletePodcastCoverById - summary: Remove podcast cover by ID + summary: Remove podcast cover description: Remove the podcast cover image from the podcast. The cover image file is not deleted but is no longer associated with the podcast. tags: - Podcast @@ -2580,7 +2591,7 @@ paths: - $ref: '#/components/parameters/pathPodcastId' post: operationId: matchPodcastById - summary: Match podcast by ID + summary: Match podcast description: Match the podcast selected by ID against an online database. This returns an array of possible matches. The user can select the best match from the list and select which metadata fields should be kept. tags: - Podcast @@ -2628,7 +2639,7 @@ paths: - $ref: '#/components/parameters/pathPodcastId' get: operationId: getPodcastTracksById - summary: Get podcast tracks by ID + summary: Get podcast tracks description: Get the podcast's audio tracks by its ID. This endpoint will return the podcast's audio tracks. tags: - Podcast @@ -2645,7 +2656,7 @@ paths: $ref: '#/components/responses/notFound' patch: operationId: updatePodcastTracksById - summary: Update podcast tracks by ID + summary: Update podcast tracks description: Update the podcast's audio tracks based on the provided file IDs. This endpoint will replace the podcast's audio tracks with the provided tracks. The tracks should be in the correct order. tags: - Podcast @@ -2679,7 +2690,7 @@ paths: - $ref: '#/components/parameters/pathPodcastId' post: operationId: scanPodcastById - summary: Scan podcast by ID + summary: Scan podcast description: Scan the podcast by its ID. This endpoint will scan the podcast's files and update the podcast's metadata based on the files found according to the metadata priority settings. tags: - Podcast @@ -2699,7 +2710,7 @@ paths: - $ref: '#/components/parameters/pathPodcastId' get: operationId: getPodcastEpisodesById - summary: Get podcast episodes by ID + summary: Get podcast episodes description: Get the podcast's episodes by its ID. This endpoint will return the podcast's episodes. tags: - Podcast @@ -2761,7 +2772,7 @@ paths: - $ref: '#/components/parameters/pathPodcastEpisodeId' get: operationId: getPodcastEpisodeById - summary: Get podcast episode by ID + summary: Get podcast episode description: Get a podcast episode by its ID. This endpoint returns all of the information needed for the podcast episode details page and editing. tags: - Podcast Episode @@ -2776,7 +2787,7 @@ paths: $ref: '#/components/responses/notFound' post: operationId: updatePodcastEpisodeById - summary: Update podcast episode by ID + summary: Update podcast episode description: Update a podcast episode by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. tags: - Podcast Episode @@ -2818,7 +2829,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deletePodcastEpisodeById - summary: Remove podcast episode by ID + summary: Remove podcast episode description: Remove the podcast episode and associated entries from the database. This does not delete any files from the filesystem. tags: - Podcast Episode @@ -2838,7 +2849,7 @@ paths: - $ref: '#/components/parameters/pathPodcastEpisodeId' delete: operationId: hardDeletePodcastEpisodeById - summary: Hard delete podcast episode by ID + summary: Hard delete podcast episode description: Hard delete the podcast episode and associated entries from the database. This deletes the podcast episode's files from the filesystem. This action cannot be undone. tags: - Podcast Episode @@ -2858,7 +2869,7 @@ paths: - $ref: '#/components/parameters/pathPodcastEpisodeId' get: operationId: downloadPodcastEpisodeById - summary: Download podcast episode by ID + summary: Download podcast episode description: Download the podcast episode by its ID. This endpoint will return the podcast episode file as a raw file. tags: - Podcast Episode @@ -2879,7 +2890,7 @@ paths: - $ref: '#/components/parameters/pathPodcastEpisodeId' post: operationId: matchPodcastEpisodeById - summary: Match podcast episode by ID + summary: Match podcast episode description: Match the podcast episode selected by ID against an online database. This returns an array of possible matches. The user can select the best match from the list and select which metadata fields should be kept. tags: - Podcast Episode @@ -3030,7 +3041,7 @@ paths: $ref: '#/components/responses/notFound' post: operationId: addPodcastToLibraryById - summary: Add podcast to library by ID + summary: Add podcast to library description: Add a podcast to the library by its ID. This endpoint will add the podcast to the library. tags: - Library @@ -3186,7 +3197,7 @@ paths: - $ref: '#/components/parameters/pathAuthorId' get: operationId: getAuthorById - summary: Get author by ID + summary: Get author description: Get an author by its ID. This endpoint returns all of the information needed for the author details page and editing. tags: - Author @@ -3201,7 +3212,7 @@ paths: $ref: '#/components/responses/notFound' patch: operationId: updateAuthorById - summary: Update author by ID + summary: Update author description: Update an author by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. tags: - Author @@ -3235,7 +3246,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deleteAuthorById - summary: Remove author by ID + summary: Remove author description: Remove the author and associated entries from the database. This does not delete any files from the filesystem. tags: - Author @@ -3255,7 +3266,7 @@ paths: - $ref: '#/components/parameters/pathAuthorId' get: operationId: getAuthorImageById - summary: Get author image by ID + summary: Get author image description: Get the author image by its ID. This endpoint will return the author's image. If no query parameters are provided, the image will be returned in the original format with the original dimensions. security: [] # No security for getting image tags: @@ -3305,7 +3316,7 @@ paths: $ref: '#/components/responses/notFound' post: operationId: uploadAuthorImageById - summary: Upload author image by ID + summary: Upload author image description: Upload the author image to the author by the author ID. This endpoint will replace the author's image with the provided image. The image should be in JPEG, PNG, or WebP format. Alternatively, the image can be provided as a URL to download the image from. tags: - Author @@ -3346,7 +3357,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deleteAuthorImageById - summary: Remove author image by ID + summary: Remove author image description: Remove the author image from the author. The image file is not deleted but is no longer associated with the author. tags: - Author @@ -3370,7 +3381,7 @@ paths: - $ref: '#/components/parameters/pathAuthorId' post: operationId: matchAuthorById - summary: Match author by ID + summary: Match author description: Match the author selected by ID against an online database. tags: - Author @@ -3478,7 +3489,7 @@ paths: - $ref: '#/components/parameters/pathLibraryId' get: operationId: getLibraryById - summary: Get library by ID + summary: Get library description: Get a library by its ID. This endpoint returns all of the information needed for the library details page and editing. tags: - Library @@ -3495,7 +3506,7 @@ paths: $ref: '#/components/responses/notFound' patch: operationId: updateLibraryById - summary: Update library by ID + summary: Update library description: Update a library by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. tags: - Library @@ -3533,7 +3544,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deleteLibraryById - summary: Remove library by ID + summary: Remove library description: Remove the library and associated entries from the database. This does not delete any files from the filesystem. tags: - Library @@ -3830,7 +3841,7 @@ paths: - $ref: '#/components/parameters/pathNarratorId' get: operationId: getNarratorById - summary: Get narrator by ID + summary: Get narrator description: Get a narrator by its ID. This endpoint returns all of the information needed for the narrator details page and editing. tags: - Narrator @@ -3845,7 +3856,7 @@ paths: $ref: '#/components/responses/notFound' patch: operationId: updateNarratorById - summary: Update narrator by ID + summary: Update narrator description: Update a narrator by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. tags: - Narrator @@ -3873,7 +3884,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deleteNarratorById - summary: Remove narrator by ID + summary: Remove narrator description: Remove the narrator and associated entries from the database. This does not delete any files from the filesystem. tags: - Narrator @@ -3893,7 +3904,7 @@ paths: - $ref: '#/components/parameters/pathLibraryId' post: operationId: scanLibraryById - summary: Scan library by ID + summary: Scan library description: Scan the library by its ID. This will scan the library's folders for new media and update the database with the new media. tags: - Library @@ -3935,7 +3946,7 @@ paths: - $ref: '#/components/parameters/pathLibraryId' post: operationId: matchLibraryById - summary: Match library by ID + summary: Match library description: Match all items in the library which do not have an ASIN or ISBN against an online database. If a provider is not specified, the default library provider will be used. Matched items will have missing details filled in by default and data will not be overwritten, unless the "Prefer matched metadata" setting is enabled. tags: - Library @@ -4187,7 +4198,7 @@ paths: - $ref: '#/components/parameters/pathFeedId' get: operationId: getFeedById - summary: Get feed object by ID + summary: Get feed object description: Get a feed by its ID. This endpoint returns all of the information needed for the feed details page and editing. tags: - RSS Feed @@ -4202,7 +4213,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deleteFeedById - summary: Remove feed by ID + summary: Remove feed description: Remove the feed and associated entries from the database. This does not delete any files from the filesystem. tags: - RSS Feed @@ -4372,7 +4383,7 @@ paths: - $ref: '#/components/parameters/pathUserId' get: operationId: getUserById - summary: Get user by ID + summary: Get user description: Get a user by its ID. This endpoint returns all of the information needed for the user details page and editing. tags: - User @@ -4389,7 +4400,7 @@ paths: $ref: '#/components/responses/notFound' patch: operationId: updateUserById - summary: Update user by ID + summary: Update user description: Update a user by its ID. The request body should contain only the fields that need to be updated. If a field should be cleared, the field should exist and have a value of `null`. At least one field must be present. tags: - User @@ -4433,7 +4444,7 @@ paths: $ref: '#/components/responses/notFound' delete: operationId: deleteUserById - summary: Remove user by ID + summary: Remove user description: Remove the user and associated entries from the database. This does not delete any files from the filesystem. tags: - User @@ -5617,7 +5628,7 @@ paths: - $ref: '#/components/parameters/pathBackupId' get: operationId: getBackupById - summary: Get backup by ID + summary: Get backup description: Get a backup by its ID. This endpoint returns all of the information needed for the backup details page. tags: - Backup @@ -6217,7 +6228,7 @@ paths: $ref: '#/components/schemas/genre' '403': $ref: '#/components/responses/forbidden' - /api/genre/{gerne}: + /api/genre/{genre}: parameters: - $ref: '#/components/parameters/pathGenre' post: From 3d2f796f688027e97f030bbfc0164d7a5fc957e9 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sat, 9 Nov 2024 12:26:47 -0700 Subject: [PATCH 24/24] Remove: security header for author image get request --- docs/newRoot.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/newRoot.yaml b/docs/newRoot.yaml index 9083bb02de..ed2f0d1744 100644 --- a/docs/newRoot.yaml +++ b/docs/newRoot.yaml @@ -3310,8 +3310,6 @@ paths: schema: type: string format: binary - '403': - $ref: '#/components/responses/forbidden' '404': $ref: '#/components/responses/notFound' post: