-
Notifications
You must be signed in to change notification settings - Fork 2
Episodes endpoint #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3a2d2ed
3ef2756
e99fded
e0b3ad1
2f74ab1
afdb6d8
75cc271
6ca76c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,8 @@ info: | |
| tags: | ||
| - name: Subscriptions | ||
| description: All actions relating to subscription management | ||
| - name: Episodes | ||
| description: All actions relating to episode management | ||
| paths: | ||
| /subscriptions: | ||
| get: | ||
|
|
@@ -202,6 +204,51 @@ paths: | |
| security: | ||
| - podcast_auth: | ||
| - read:subscriptions | ||
| /episodes: | ||
| get: | ||
| tags: | ||
| - Episodes | ||
| summary: Retrieve all episodes for the authenticated user | ||
| description: Retrieve all episodes that has changed for the authenticated user since the provided timestamp | ||
| operationId: getEpisodes | ||
| parameters: | ||
| - in: query | ||
| name: since | ||
| schema: | ||
| type: string | ||
| format: date-time | ||
| required: false | ||
| example: | ||
| '2022-04-23T18:25:43.511Z' | ||
| - in: query | ||
| name: page | ||
| schema: | ||
| type: number | ||
| required: false | ||
| example: | ||
| 1 | ||
| - in: query | ||
| name: per_page | ||
| schema: | ||
| type: number | ||
| required: false | ||
| example: | ||
| 5 | ||
| responses: | ||
| '200': | ||
| description: Successful operation | ||
| content: | ||
| application/json: | ||
| schema: | ||
| $ref: '#/components/schemas/Episodes' | ||
| application/xml: | ||
| schema: | ||
| $ref: '#/components/schemas/Episodes' | ||
| '401': | ||
| $ref: '#/components/responses/Unauthorized' | ||
| security: | ||
| - podcast_auth: | ||
| - read:subscriptions | ||
| components: | ||
| responses: | ||
| Unauthorized: | ||
|
|
@@ -357,6 +404,7 @@ components: | |
| guid: | ||
| type: string | ||
| format: guid | ||
| description: The unique identifier (guid) for the subscription, as declared in the feed or generated by the server | ||
| is_subscribed: | ||
| type: boolean | ||
| subscription_changed: | ||
|
|
@@ -550,6 +598,167 @@ components: | |
| subscription_changed: 2023-02-23T14:41:00.000Z | ||
| guid_changed: 2023-02-23T14:41:00.000Z | ||
| new_guid: 965fcecf-ce04-482b-b57c-3119b866cc61 | ||
| Episode: | ||
| xml: | ||
| name: episode | ||
| required: | ||
| - podcast_guid | ||
| - sync_id | ||
| - episode_guid | ||
| - title | ||
| - publish_date | ||
| - enclosure_url | ||
| - episode_url | ||
| type: object | ||
| properties: | ||
| podcast_guid: | ||
| type: string | ||
| format: guid | ||
| sync_id: | ||
| type: string | ||
| format: guid | ||
| episode_guid: | ||
| type: string | ||
| title: | ||
| type: string | ||
| publish_date: | ||
| type: string | ||
| format: date-time | ||
| enclosure_url: | ||
| type: string | ||
| format: url | ||
| episode_url: | ||
| type: string | ||
| format: url | ||
| playback_position: | ||
| value: | ||
| type: number | ||
| format: integer | ||
| timestamp: | ||
| type: string | ||
| format: date-time | ||
| played_status: | ||
| value: | ||
| type: boolean | ||
| timestamp: | ||
| type: string | ||
| format: date-time | ||
| new_status: | ||
| value: | ||
| type: boolean | ||
| timestamp: | ||
| type: string | ||
| format: date-time | ||
| download_status: | ||
| value: | ||
| type: boolean | ||
| timestamp: | ||
| type: string | ||
| format: date-time | ||
| favorite_status: | ||
| value: | ||
| type: boolean | ||
| timestamp: | ||
| type: string | ||
| format: date-time | ||
| example: | ||
| podcast_guid: 31740ac6-e39d-49cd-9179-634bcecf4143 | ||
| sync_id: cff3ea32-4215-4f98-bc23-5358d1f35b55 | ||
| episode_guid: https://example.com/podcast/episode-5-the-history-of-RSS | ||
| title: The history of RSS | ||
| publish_date: 2022-04-24T17:53:21.573Z | ||
| enclosure_url: https://example.com/podcast/episode-5-the-history-of-RSS.mp3 | ||
| episode_url: https://example.com/podcast/episode-5-the-history-of-RSS | ||
| playback_position: | ||
| value: 0 | ||
| timestamp: 2024-11-02T13:19 | ||
| played_status: | ||
| value: true | ||
| timestamp: 2024-11-02T13:19 | ||
| new_status: | ||
| value: false | ||
| timestamp: 2024-10-30T17:31 | ||
| download_status: | ||
| value: false | ||
| timestamp: 2024-11-02T13:19 | ||
| favorite_status: | ||
| value: false | ||
| timestamp: 2024-11-02T13:19 | ||
| Episodes: | ||
| required: | ||
| - total | ||
| - page | ||
| - per_page | ||
| - episodes | ||
| xml: | ||
| name: episodes | ||
| type: object | ||
| properties: | ||
| total: | ||
| type: number | ||
| page: | ||
| type: number | ||
| per_page: | ||
| type: number | ||
| next: | ||
| type: string | ||
| format: url | ||
| previous: | ||
| type: string | ||
| format: url | ||
| episodes: | ||
| type: array | ||
| items: | ||
| $ref: '#/components/schemas/Episode' | ||
| example: | ||
| total: 2 | ||
| page: 1 | ||
| per_page: 5 | ||
| episodes: | ||
| - podcast_guid: 31740ac6-e39d-49cd-9179-634bcecf4143 | ||
| sync_id: cff3ea32-4215-4f98-bc23-5358d1f35b55 | ||
| episode_guid: https://example.com/podcast/episode-5-the-history-of-RSS | ||
| title: The history of RSS | ||
| publish_date: 2022-04-24T17:53:21.573Z | ||
| enclosure_url: https://example.com/podcast/episode-5-the-history-of-RSS.mp3 | ||
| episode_url: https://example.com/podcast/episode-5-the-history-of-RSS | ||
| playback_position: | ||
| value: 0 | ||
| timestamp: 2024-11-02T13:19 | ||
| played_status: | ||
| value: true | ||
| timestamp: 2024-11-02T13:19 | ||
| new_status: | ||
| value: false | ||
| timestamp: 2024-10-30T17:31 | ||
| download_status: | ||
| value: false | ||
| timestamp: 2024-11-02T13:19 | ||
| favorite_status: | ||
| value: false | ||
| timestamp: 2024-11-02T13:19 | ||
| - podcast_guid: 9d6786c9-ed48-470d-acbe-e593547f4b5b | ||
| sync_id: 5773f457-e71b-417d-8ea8-f07c38a03a3e | ||
| episode_guid: 01999e25-08cd-4f29-a61e-6ca459b40d27 | ||
| title: Walk with the weatherman | ||
| publish_date: 2022-04-27T19:35:20.000Z | ||
| enclosure_url: https://op3.dev/e/https://podcasts.example2.net/audio/@digitalcitizen/49-walk-with-the-weatherman.mp3 | ||
| episode_url: https://podcasts.example2.net/@digitalcitizen/episodes/49-walk-with-the-weatherman | ||
| playback_position: | ||
| value: 2100 | ||
| timestamp: 2024-11-01T17:38 | ||
| played_status: | ||
| value: false | ||
| timestamp: 2024-04-28T09:20 | ||
| new_status: | ||
| value: false | ||
| timestamp: 2024-11-01T17:02 | ||
| download_status: | ||
| value: true | ||
| timestamp: 2024-11-01T17:02 | ||
| favorite_status: | ||
| value: false | ||
| timestamp: 2024-04-28T09:20 | ||
| Deletion: | ||
| xml: | ||
| name: deletion | ||
|
|
@@ -599,8 +808,8 @@ components: | |
| implicit: | ||
| authorizationUrl: https://test.openpodcastapi.com/oauth/authorize | ||
| scopes: | ||
| write:subscriptions: modify subscription information for your account | ||
| read:subscriptions: read your subscription information | ||
| write:subscriptions: modify subscription information & related episodes for your account | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @keunes I think generally it's best practice with Oauth to keep read/write separate. The principle of least privilege dictates that you should give applications/people access only to what they absolutely need to get the job done. If you made an app that could read subscriptions to, for example, display your favorite podcasts on your website, you should not give that app access to modify those subscriptions. |
||
| read:subscriptions: read your subscription information & related episodes | ||
| api_key: | ||
| type: apiKey | ||
| name: api_key | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| import { Badge } from '@astrojs/starlight/components'; | ||
| --- | ||
|
|
||
| <Badge text="Core" variant="caution" size="small" /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| import { Badge } from '@astrojs/starlight/components'; | ||
| --- | ||
|
|
||
| <Badge text="Optional" variant="success" size="small" /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| --- | ||
| title: Add or update episodes | ||
| description: Create new or update existing episodes for a user | ||
| sidebar: | ||
| order: 5 | ||
| badge: | ||
| text: Core | ||
| variant: caution | ||
| --- | ||
|
|
||
| import CoreAction from "@partials/_core-action.mdx"; | ||
|
|
||
| <CoreAction /> | ||
|
|
||
| ```http title="Endpoint" | ||
| POST /v1/episodes | ||
| ``` | ||
|
|
||
| This endpoint enables clients to register new episodes for or change information of episodes relating to the authenticated user. It returns an array of `success` responses for newly added episodes, and an array of `failure` responses for episodes that couldn't be added. | ||
|
|
||
| This endpoint only accepts an array of objects, as it serves both for the creation and updating of a single and a batch of episodes. | ||
|
|
||
| ## Related fields | ||
|
|
||
| See the [Overview](index) page for an overview of the fields used for this end-point. | ||
|
|
||
| ## Request parameters | ||
|
|
||
| The client MUST provide a list of objects containing a set of parameters, which depend on the presence of a `sync_id`: | ||
| * if the `sync_id` of the episode is known: | ||
| * `podcast_guid` | ||
| * `sync_id` (of the episode) | ||
| * if there is no (known) `sync_id` (yet): | ||
| * all identifier fields except `sync_id` (`podcast_guid`, `episode_guid`, `title`, `publish_date`, `enclosure_url`, `episode_url`), | ||
| * `temporary_id` (optional), | ||
| * and always: any data data fields that were changed | ||
|
|
||
| ## Client side behavior | ||
|
|
||
| :::note[Reminder: pull first, post later] | ||
| As discussed in the [Generic principles], clients SHOULD pull first and post later. In he process, clients are expected to do some [deduplication](identification-deduplication). | ||
| ::: | ||
|
|
||
| Clients SHOULD adopt a **lazy synchronization** approach. This means: | ||
| * not syncing immediately when a new episode is found (e.g. after refreshing a feed) | ||
| * only syncing after an episode has been interacted with by the user or system (e.g. after initiating an automatic download) | ||
|
|
||
| Clients MAY leave the `sync_id` empty when registering new episodes, and leave the generation of the `sync_id` to the server. | ||
|
|
||
| Clients MAY provide a `temporary_id` with the request (e.g. the local database index) that the server will reflect in its response, so that the client can match the episodes from the server response more easily. | ||
|
|
||
| ## Server side behavior | ||
|
|
||
| Servers SHOULD NOT rely on clients for episode discovery. If the server also has a 'client' component (a user interface to interact with subscriptions and episodes), it SHOULD independently ensure episode discovery (refresh feeds or rely on third party episode APIs). This is due to the lazy synchronization applied by clients - if servers do not fetch episodes themselves then the user might notice certain episodes are missing. | ||
|
|
||
| ### `sync_id` | ||
|
|
||
| Servers MUST respond with a `sync_id` at all times. If none exists, they MUST be generated. Episode `sync_id`s are of type guid and could, for example, be the internal episode ID. | ||
|
|
||
| Servers MUST respond with the `temporary_id` for the episode if it was provided by the client. ??What did we say again about asynchronous processing? How long does this temporary ID need to be preserved by the server?? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| --- | ||
| title: Get all episodes of a podcast | ||
| description: Get all episodes for a subscription | ||
| sidebar: | ||
| order: 4 | ||
| badge: | ||
| text: Core | ||
| variant: caution | ||
| --- | ||
|
|
||
| import CoreAction from "@partials/_core-action.mdx"; | ||
|
|
||
| <CoreAction /> | ||
|
|
||
| ```http title="Endpoint" | ||
| GET /v1/subscriptions/{guid}/episodes | ||
| ``` | ||
|
|
||
| TO DO. See [2023-05-30](https://pad.funkwhale.audio/oCfs5kJ6QTu02d_oVHW7DA#) meeting notes. | ||
|
|
||
|
|
||
| `GET/PUT /subscriptions/{guid}/episodes/{sync_id}` --> Really needed? `GET/PUT /episodes` would suffice probably. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sporiff Could I ask you to review? I tried to follow the format of the existing file :-)