From 4b4feea96524d12198452743d37015348c9e5ddc Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sun, 7 Jul 2024 19:54:49 +0000 Subject: [PATCH] Initial: podcast schemas and endpoints --- docs/controllers/PodcastController.yaml | 331 +++++++++++++++++++++ docs/objects/entities/PodcastEpisode.yaml | 75 +++++ docs/objects/mediaTypes/Podcast.yaml | 74 +++++ docs/objects/metadata/PodcastMetadata.yaml | 59 ++++ docs/root.yaml | 20 ++ 5 files changed, 559 insertions(+) create mode 100644 docs/controllers/PodcastController.yaml create mode 100644 docs/objects/entities/PodcastEpisode.yaml create mode 100644 docs/objects/mediaTypes/Podcast.yaml create mode 100644 docs/objects/metadata/PodcastMetadata.yaml diff --git a/docs/controllers/PodcastController.yaml b/docs/controllers/PodcastController.yaml new file mode 100644 index 000000000..c3b6fbb1e --- /dev/null +++ b/docs/controllers/PodcastController.yaml @@ -0,0 +1,331 @@ +paths: + /api/podcasts: + post: + summary: Create a new podcast + operationId: createPodcast + requestBody: + required: true + content: + application/json: + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/Podcast' + responses: + 200: + description: Successfully created a podcast + content: + application/json: + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/Podcast' + 400: + description: Bad request + 403: + description: Forbidden + 404: + description: Not found + + /api/podcasts/feed: + post: + summary: Get podcast feed + operationId: getPodcastFeed + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + rssFeed: + type: string + description: The RSS feed URL of the podcast + responses: + 200: + description: Successfully retrieved podcast feed + content: + application/json: + schema: + type: object + properties: + podcast: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/Podcast' + 400: + description: Bad request + 403: + description: Forbidden + 404: + description: Not found + + /api/podcasts/opml: + post: + summary: Get feeds from OPML text + operationId: getFeedsFromOPMLText + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + opmlText: + type: string + description: The OPML text containing podcast feeds + responses: + 200: + description: Successfully retrieved feeds from OPML text + content: + application/json: + schema: + type: array + items: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/Podcast' + 400: + description: Bad request + 403: + description: Forbidden + + /api/podcasts/{id}/checknew: + parameters: + - name: id + in: path + description: Podcast ID + required: true + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastId' + + get: + summary: Check and download new episodes + operationId: checkNewEpisodes + parameters: + - name: limit + in: query + description: Maximum number of episodes to download + required: false + schema: + type: integer + responses: + 200: + description: Successfully checked and downloaded new episodes + content: + application/json: + schema: + type: object + properties: + episodes: + type: array + items: + $ref: '../objects/entities/PodcastEpisode.yaml#/components/schemas/PodcastEpisode' + 403: + description: Forbidden + 404: + description: Not found + 500: + description: Server error + + /api/podcasts/{id}/clear-queue: + parameters: + - name: id + in: path + description: Podcast ID + required: true + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastId' + + get: + summary: Clear episode download queue + operationId: clearEpisodeDownloadQueue + responses: + 200: + description: Successfully cleared download queue + 403: + description: Forbidden + + /api/podcasts/{id}/downloads: + parameters: + - name: id + in: path + description: Podcast ID + required: true + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastId' + + get: + summary: Get episode downloads + operationId: getEpisodeDownloads + responses: + 200: + description: Successfully retrieved episode downloads + content: + application/json: + schema: + type: object + properties: + downloads: + type: array + items: + $ref: '../objects/entities/PodcastEpisode.yaml#/components/schemas/PodcastEpisode' + 404: + description: Not found + + /api/podcasts/{id}/search-episode: + parameters: + - name: id + in: path + description: Podcast ID + required: true + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastId' + + get: + summary: Find episode by title + operationId: findEpisode + parameters: + - name: title + in: query + description: Title of the episode to search for + required: true + schema: + type: string + responses: + 200: + description: Successfully found episodes + content: + application/json: + schema: + type: object + properties: + episodes: + type: array + items: + $ref: '../objects/entities/PodcastEpisode.yaml#/components/schemas/PodcastEpisode' + 404: + description: Not found + 500: + description: Server error + + /api/podcasts/{id}/download-episodes: + parameters: + - name: id + in: path + description: Podcast ID + required: true + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastId' + + post: + summary: Download podcast episodes + operationId: downloadEpisodes + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + type: string + responses: + 200: + description: Successfully started episode download + 400: + description: Bad request + 403: + description: Forbidden + + /api/podcasts/{id}/match-episodes: + parameters: + - name: id + in: path + description: Podcast ID + required: true + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastId' + + post: + summary: Quick match podcast episodes + operationId: quickMatchEpisodes + parameters: + - name: override + in: query + description: Override existing details if set to 1 + required: false + schema: + type: string + responses: + 200: + description: Successfully matched episodes + content: + application/json: + schema: + type: object + properties: + numEpisodesUpdated: + type: integer + 403: + description: Forbidden + + /api/podcasts/{id}/episode/{episodeId}: + parameters: + - name: id + in: path + description: Podcast ID + required: true + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastId' + - name: episodeId + in: path + description: Episode ID + required: true + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/podcastId' + + patch: + summary: Update a podcast episode + operationId: updateEpisode + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + 200: + description: Successfully updated episode + content: + application/json: + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/Podcast' + 404: + description: Not found + + get: + summary: Get a specific podcast episode + operationId: getEpisode + responses: + 200: + description: Successfully retrieved episode + content: + application/json: + schema: + $ref: '../objects/entities/PodcastEpisode.yaml#/components/schemas/PodcastEpisode' + 404: + description: Not found + + delete: + summary: Remove a podcast episode + operationId: removeEpisode + parameters: + - name: hard + in: query + description: Hard delete the episode if set to 1 + required: false + schema: + type: string + responses: + 200: + description: Successfully removed episode + content: + application/json: + schema: + $ref: '../objects/mediaTypes/Podcast.yaml#/components/schemas/Podcast' + 404: + description: Not found + 500: + description: Server error diff --git a/docs/objects/entities/PodcastEpisode.yaml b/docs/objects/entities/PodcastEpisode.yaml new file mode 100644 index 000000000..d565af657 --- /dev/null +++ b/docs/objects/entities/PodcastEpisode.yaml @@ -0,0 +1,75 @@ +components: + schemas: + PodcastEpisode: + type: object + description: A single episode of a podcast. + properties: + libraryItemId: + $ref: '../LibraryItem.yaml#/components/schemas/libraryItemId' + podcastId: + $ref: '../mediaTypes/Podcast.yaml#/components/schemas/podcastId' + id: + $ref: '../mediaTypes/Podcast.yaml#/components/schemas/podcastId' + oldEpisodeId: + $ref: '../mediaTypes/Podcast.yaml#/components/schemas/oldPodcastId' + index: + type: integer + description: The index of the episode within the podcast. + nullable: true + season: + type: string + description: The season number of the episode. + nullable: true + episode: + type: string + description: The episode number within the season. + nullable: true + episodeType: + type: string + description: The type of episode (e.g., full, trailer). + nullable: true + title: + type: string + description: The title of the episode. + nullable: true + subtitle: + type: string + description: The subtitle of the episode. + nullable: true + description: + type: string + description: The description of the episode. + nullable: true + enclosure: + type: object + description: The enclosure object containing additional episode data. + nullable: true + additionalProperties: true + guid: + type: string + description: The globally unique identifier for the episode. + nullable: true + pubDate: + type: string + format: date-time + description: The publication date of the episode. + nullable: true + chapters: + type: array + description: The chapters within the episode. + items: + type: object + audioFile: + $ref: '../files/AudioFile.yaml#/components/schemas/audioFile' + publishedAt: + $ref: '../../schemas.yaml#/components/schemas/createdAt' + addedAt: + $ref: '../../schemas.yaml#/components/schemas/addedAt' + updatedAt: + $ref: '../../schemas.yaml#/components/schemas/updatedAt' + audioTrack: + $ref: '../files/AudioTrack.yaml#/components/schemas/AudioTrack' + duration: + $ref: '../../schemas.yaml#/components/schemas/durationSec' + size: + $ref: '../../schemas.yaml#/components/schemas/size' diff --git a/docs/objects/mediaTypes/Podcast.yaml b/docs/objects/mediaTypes/Podcast.yaml new file mode 100644 index 000000000..8cc351bf1 --- /dev/null +++ b/docs/objects/mediaTypes/Podcast.yaml @@ -0,0 +1,74 @@ +components: + schemas: + podcastId: + type: string + description: The ID of podcasts and podcast episodes after 2.3.0. + format: uuid + example: e4bb1afb-4a4f-4dd6-8be0-e615d233185b + oldPodcastId: + description: The ID of podcasts on server version 2.2.23 and before. + type: string + nullable: true + format: 'pod_[a-z0-9]{18}' + example: pod_o78uaoeuh78h6aoeif + + Podcast: + type: object + description: A podcast containing multiple episodes. + properties: + id: + $ref: '#/components/schemas/podcastId' + libraryItemId: + $ref: '../LibraryItem.yaml#/components/schemas/libraryItemId' + metadata: + $ref: '../metadata/PodcastMetadata.yaml#/components/schemas/PodcastMetadata' + coverPath: + type: string + description: The file path to the podcast's cover image. + nullable: true + tags: + type: array + description: The tags associated with the podcast. + items: + type: string + episodes: + type: array + description: The episodes of the podcast. + items: + $ref: '../entities/PodcastEpisode.yaml#/components/schemas/PodcastEpisode' + autoDownloadEpisodes: + type: boolean + description: Whether episodes are automatically downloaded. + autoDownloadSchedule: + type: string + description: The schedule for automatic episode downloads, in cron format. + nullable: true + lastEpisodeCheck: + type: integer + description: The timestamp of the last episode check. + maxEpisodesToKeep: + type: integer + description: The maximum number of episodes to keep. + maxNewEpisodesToDownload: + type: integer + description: The maximum number of new episodes to download when automatically downloading epsiodes. + lastCoverSearch: + type: integer + description: The timestamp of the last cover search. + nullable: true + lastCoverSearchQuery: + type: string + description: The query used for the last cover search. + nullable: true + size: + type: integer + description: The total size of all episodes in bytes. + duration: + type: integer + description: The total duration of all episodes in seconds. + numTracks: + type: integer + description: The number of tracks (episodes) in the podcast. + latestEpisodePublished: + type: integer + description: The timestamp of the most recently published episode. diff --git a/docs/objects/metadata/PodcastMetadata.yaml b/docs/objects/metadata/PodcastMetadata.yaml new file mode 100644 index 000000000..a565d697a --- /dev/null +++ b/docs/objects/metadata/PodcastMetadata.yaml @@ -0,0 +1,59 @@ +components: + schemas: + PodcastMetadata: + type: object + description: Metadata for a podcast. + properties: + title: + type: string + description: The title of the podcast. + nullable: true + author: + type: string + description: The author of the podcast. + nullable: true + description: + type: string + description: The description of the podcast. + nullable: true + releaseDate: + type: string + format: date-time + description: The release date of the podcast. + nullable: true + genres: + type: array + description: The genres of the podcast. + items: + type: string + feedUrl: + type: string + description: The URL of the podcast feed. + nullable: true + imageUrl: + type: string + description: The URL of the podcast's image. + nullable: true + itunesPageUrl: + type: string + description: The URL of the podcast's iTunes page. + nullable: true + itunesId: + type: string + description: The iTunes ID of the podcast. + nullable: true + itunesArtistId: + type: string + description: The iTunes artist ID of the podcast. + nullable: true + explicit: + type: boolean + description: Whether the podcast contains explicit content. + language: + type: string + description: The language of the podcast. + nullable: true + type: + type: string + description: The type of podcast (e.g., episodic, serial). + nullable: true diff --git a/docs/root.yaml b/docs/root.yaml index ee9d7b9c6..f819505cc 100644 --- a/docs/root.yaml +++ b/docs/root.yaml @@ -45,6 +45,26 @@ paths: $ref: './controllers/NotificationController.yaml#/paths/~1api~1notifications~1{id}' /api/notifications/{id}/test: $ref: './controllers/NotificationController.yaml#/paths/~1api~1notifications~1{id}~1test' + /api/podcasts: + $ref: './controllers/PodcastController.yaml#/paths/~1api~1podcasts' + /api/podcasts/feed: + $ref: './controllers/PodcastController.yaml#/paths/~1api~1podcasts~1feed' + /api/podcasts/opml: + $ref: './controllers/PodcastController.yaml#/paths/~1api~1podcasts~1opml' + /api/podcasts/{id}/checknew: + $ref: './controllers/PodcastController.yaml#/paths/~1api~1podcasts~1{id}~1checknew' + /api/podcasts/{id}/clear-queue: + $ref: './controllers/PodcastController.yaml#/paths/~1api~1podcasts~1{id}~1clear-queue' + /api/podcasts/{id}/downloads: + $ref: './controllers/PodcastController.yaml#/paths/~1api~1podcasts~1{id}~1downloads' + /api/podcasts/{id}/search-episode: + $ref: './controllers/PodcastController.yaml#/paths/~1api~1podcasts~1{id}~1search-episode' + /api/podcasts/{id}/download-episodes: + $ref: './controllers/PodcastController.yaml#/paths/~1api~1podcasts~1{id}~1download-episodes' + /api/podcasts/{id}/match-episodes: + $ref: './controllers/PodcastController.yaml#/paths/~1api~1podcasts~1{id}~1match-episodes' + /api/podcasts/{id}/episode/{episodeId}: + $ref: './controllers/PodcastController.yaml#/paths/~1api~1podcasts~1{id}~1episode~1{episodeId}' /api/series/{id}: $ref: './controllers/SeriesController.yaml#/paths/~1api~1series~1{id}' tags: