|
| 1 | +--- |
| 2 | +title: Get all episodes |
| 3 | +description: Get all episodes for a user |
| 4 | +sidebar: |
| 5 | + order: 3 |
| 6 | + badge: |
| 7 | + text: Core |
| 8 | + variant: caution |
| 9 | +--- |
| 10 | + |
| 11 | +import CoreAction from "@partials/_core-action.mdx"; |
| 12 | + |
| 13 | +<CoreAction /> |
| 14 | + |
| 15 | +```http title="Endpoint" |
| 16 | +GET /v1/episodes |
| 17 | +``` |
| 18 | + |
| 19 | +This endpoint enables clients to return all episode information relating to the authenticated user. It returns pagination information and an array of `episodes`. |
| 20 | + |
| 21 | +While supported, this endpoint is expected to be used rarely in practice. More often, episodes are retrieved per queue (TBD) or per subscription (TBD). |
| 22 | + |
| 23 | +## Response fields |
| 24 | + |
| 25 | +### Metadata |
| 26 | + |
| 27 | +| Field | Type | Required? | Description | |
| 28 | +| ---------- | ------ | --------- | ------------------------------------------------ | |
| 29 | +| `total` | Number | Yes | The total number of objects returned by the call | |
| 30 | +| `page` | Number | Yes | The number of the page returned in the call | |
| 31 | +| `per_page` | Number | Yes | The number of results returned per page | |
| 32 | +| `next` | String | No | The URL for the next page of results | |
| 33 | +| `previous` | String | No | The URL for the previous page of results | |
| 34 | + |
| 35 | +### Episode fields |
| 36 | + |
| 37 | +| Group | Field | Type | Required? | Description | |
| 38 | +| ---------- | ----------------------------------- | --------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 39 | +| Identifier | `podcast_guid` | String \<UUID\> | Yes | The globally unique ID of the parent podcast | |
| 40 | +| Identifier | `sync_id` | String \<UUID\> | Yes | The synchronisation ID of the episode, globally unique at the server and its clients | |
| 41 | +| Identifier | `episode_guid` | String \<UUID\> | Yes | The globally unique ID of the episode, as present in the RSS feed ([`guid` tag](https://www.rssboard.org/rss-specification#ltguidgtSubelementOfLtitemgt)) | |
| 42 | +| Identifier | `title` | String | Yes | The title of the episode, as present in the RSS feed (`title` tag) | |
| 43 | +| Identifier | `publish_date` | Datetime | Yes | The date of publishing of the episode, as present in the RSS feed ([`pubDate` tag](https://www.rssboard.org/rss-specification#ltpubdategtSubelementOfLtitemgt)). Presented in [ISO 8601 format] | |
| 44 | +| Identifier | `enclosure_url` | String | Yes | The media file of the episode, as present in the RSS feed ([`enclosure` tag](https://www.rssboard.org/rss-specification#ltenclosuregtSubelementOfLtitemgt)) | |
| 45 | +| Identifier | `episode_url` | String | Yes | The (webpage) URL of the episode, as present in the RSS feed (`link`tag) | |
| 46 | +| Data | `playback_position` | Integer | ??YES/NO??| The most recent playback position in seconds | |
| 47 | +| Data | `played_status` | Boolean | No | Whether the episode has been (marked as such) | |
| 48 | +| Data | `new_status` <BadgeOptional /> | Boolean | No | Whether the user (manually) interacted with the episode.<br />_Example:_ In AntennaPod this is used to indicate whether an episode is in the Inbox | |
| 49 | +| Data | `download_status` <BadgeOptional /> | Boolean | No | Whether the episode is downloaded on the client. For further details, see below. | |
| 50 | +| Data | `favorite_status` <BadgeOptional /> | Boolean | No | Whether the episode has been favorited by the user | |
| 51 | + |
| 52 | +The server can ignore the other identifier fields, if it found an episode based on the `podcast_guid` and the `sync_id`. |
| 53 | + |
| 54 | +:::note[Why all idenifiers are required] |
| 55 | + |
| 56 | +Assume client A has refreshed a feed locally, and client B hasn't done so yet. In order for client B to do episode matching with what it receives from the server, it would need to have more data than just the `sync_id`. In this scenario, how does the server know whether client B has already refreshed the feed and is aware of the episode or not (i.e. whether it can rely on `sync_id` alone or needs to send more information)? The server cannot know, thus a 'conversation' between server and client would be needed: |
| 57 | +* `S` "here's a new episode with podcast_guid x and sync_id y" |
| 58 | +* `C` "I don't recognise this one - tell me more!" |
| 59 | +* `S` "Ok, here's all information you can use for episode matching: …" |
| 60 | + |
| 61 | +To avoid such conversation, and as the sending of all matching data involves only a few bytes, all identifiers are sent always. |
| 62 | + |
| 63 | +::: |
| 64 | + |
| 65 | +Remember that each of the data fields MUST have both a 'value' and a 'timestamp'. |
| 66 | + |
| 67 | +:::note[Discussion details] |
| 68 | +See meeting notes from [2024-06-19](https://pad.funkwhale.audio/s/I3F_C2NbQ#GET-episode-information) |
| 69 | +::: |
| 70 | + |
| 71 | +## Parameters |
| 72 | + |
| 73 | +The client MAY add the following parameters to their call: |
| 74 | + |
| 75 | +| Field | Type | In | Required? | Description | |
| 76 | +| ---------- | -------- | ----- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 77 | +| `since` | DateTime | Query | No | The date from which the server should return objects. The server only returns entries whose `timestamp` data for any of the fields are greater than this parameter. Expected in [ISO 8601 format] | |
| 78 | +| `page` | Number | Query | No | The page of results to be returned by the server. Defaults to `1` if not present | |
| 79 | +| `per_page` | Number | Query | No | The number of results to return in each call. Defaults to `50` if not present | |
| 80 | + |
| 81 | +:::note |
| 82 | +If no `since` parameter is provided, the server MUST return all current subscription information. |
| 83 | +::: |
| 84 | + |
| 85 | +## Server-side behavior |
| 86 | + |
| 87 | +No particular behavior expected. |
| 88 | + |
| 89 | +## Client behavior |
| 90 | + |
| 91 | +The client SHOULD update its local episode data to match the information returned in the response. |
| 92 | + |
| 93 | +## Example request |
| 94 | + |
| 95 | +<Tabs syncKey="accepts"> |
| 96 | + <TabItem label="JSON"> |
| 97 | + |
| 98 | + ```console |
| 99 | + $ curl -X 'GET' \ |
| 100 | + '/v1/episodes?since=2024-04-23T18%3A25%3A34.511Z&page=1&per_page=5' \ |
| 101 | + -H 'accept: application/json' |
| 102 | + ``` |
| 103 | + |
| 104 | + </TabItem> |
| 105 | + <TabItem label="XML"> |
| 106 | + |
| 107 | + ```console |
| 108 | + $ curl -X 'GET' \ |
| 109 | + '/v1/episodes?since=2024-04-23T18%3A25%3A34.511Z&page=1&per_page=5' \ |
| 110 | + -H 'accept: application/xml' |
| 111 | + ``` |
| 112 | + |
| 113 | + </TabItem> |
| 114 | +</Tabs> |
| 115 | + |
| 116 | +## Example 200 response |
| 117 | + |
| 118 | +<Tabs syncKey="accepts"> |
| 119 | + <TabItem label="JSON"> |
| 120 | + |
| 121 | + ```json |
| 122 | + { |
| 123 | + "total": 2, |
| 124 | + "page": 1, |
| 125 | + "per_page": 5, |
| 126 | + "episodes": [ |
| 127 | + { |
| 128 | + "podcast_guid": "31740ac6-e39d-49cd-9179-634bcecf4143", |
| 129 | + "sync_id": "cff3ea32-4215-4f98-bc23-5358d1f35b55", |
| 130 | + "episode_guid": "https://example.com/podcast/episode-5-the-history-of-RSS", |
| 131 | + "title": "The history of RSS", |
| 132 | + "publish_date": "2022-04-24T17:53:21.573Z", |
| 133 | + "enclosure_url": "https://example.com/podcast/episode-5-the-history-of-RSS.mp3", |
| 134 | + "episode_url": "https://example.com/podcast/episode-5-the-history-of-RSS", |
| 135 | + "playback_position": { |
| 136 | + "value": 0, |
| 137 | + "timestamp": "2024-11-02T13:19" |
| 138 | + }, |
| 139 | + "played_status": { |
| 140 | + "value": true, |
| 141 | + "timestamp": "2024-11-02T13:19" |
| 142 | + }, |
| 143 | + "new_status": { |
| 144 | + "value": false, |
| 145 | + "timestamp": "2024-10-30T17:31" |
| 146 | + }, |
| 147 | + "download_status": { |
| 148 | + "value": false, |
| 149 | + "timestamp": "2024-11-02T13:19" |
| 150 | + }, |
| 151 | + "favorite_status": { |
| 152 | + "value": false, |
| 153 | + "timestamp": "2024-11-02T13:19" |
| 154 | + } |
| 155 | + }, |
| 156 | + { |
| 157 | + "podcast_guid": "9d6786c9-ed48-470d-acbe-e593547f4b5b", |
| 158 | + "sync_id": "5773f457-e71b-417d-8ea8-f07c38a03a3e", |
| 159 | + "episode_guid": "01999e25-08cd-4f29-a61e-6ca459b40d27", |
| 160 | + "title": "Walk with the weatherman", |
| 161 | + "publish_date": "2022-04-27T19:35:20.000Z", |
| 162 | + "enclosure_url": "https://op3.dev/e/https://podcasts.example2.net/audio/@digitalcitizen/49-walk-with-the-weatherman.mp3", |
| 163 | + "episode_url": "https://podcasts.example2.net/@digitalcitizen/episodes/49-walk-with-the-weatherman", |
| 164 | + "playback_position": { |
| 165 | + "value": 2100, |
| 166 | + "timestamp": "2024-11-01T17:38" |
| 167 | + }, |
| 168 | + "played_status": { |
| 169 | + "value": false, |
| 170 | + "timestamp": "2024-04-28T09:20" |
| 171 | + }, |
| 172 | + "new_status": { |
| 173 | + "value": false, |
| 174 | + "timestamp": "2024-11-01T17:02" |
| 175 | + }, |
| 176 | + "download_status": { |
| 177 | + "value": true, |
| 178 | + "timestamp": "2024-11-01T17:02" |
| 179 | + }, |
| 180 | + "favorite_status": { |
| 181 | + "value": false, |
| 182 | + "timestamp": "2024-04-28T09:20" |
| 183 | + } |
| 184 | + } |
| 185 | + ] |
| 186 | + } |
| 187 | + ``` |
| 188 | + |
| 189 | + </TabItem> |
| 190 | + <TabItem label="XML"> |
| 191 | + |
| 192 | + ```xml |
| 193 | + <?xml version="1.0" encoding="UTF-8"?> |
| 194 | + <episodes> |
| 195 | + <total>2</total> |
| 196 | + <page>1</page> |
| 197 | + <per_page>5</per_page> |
| 198 | + <episode> |
| 199 | + <podcast_guid>31740ac6-e39d-49cd-9179-634bcecf4143</podcast_guid> |
| 200 | + <sync_id>cff3ea32-4215-4f98-bc23-5358d1f35b55</sync_id> |
| 201 | + <episode_guid>https://example.com/podcast/episode-5-the-history-of-RSS</episode_guid> |
| 202 | + <title>The history of RSS</title> |
| 203 | + <publish_date>2022-04-24T17:53:21.573Z</publish_date> |
| 204 | + <enclosure_url>https://example.com/podcast/episode-5-the-history-of-RSS.mp3</enclosure_url> |
| 205 | + <episode_url>https://example.com/podcast/episode-5-the-history-of-RSS</episode_url> |
| 206 | + <playback_position> |
| 207 | + <value>0</value> |
| 208 | + <timestamp>2024-11-02T13:19</timestamp> |
| 209 | + </playback_position> |
| 210 | + <played_status> |
| 211 | + <value>true</value> |
| 212 | + <timestamp>2024-11-02T13:19</timestamp> |
| 213 | + </played_status> |
| 214 | + <new_status> |
| 215 | + <value>false</value> |
| 216 | + <timestamp>2024-10-30T17:31</timestamp> |
| 217 | + </new_status> |
| 218 | + <download_status> |
| 219 | + <value>false</value> |
| 220 | + <timestamp>2024-11-02T13:19</timestamp> |
| 221 | + </download_status> |
| 222 | + <favorite_status> |
| 223 | + <value>false</value> |
| 224 | + <timestamp>2024-11-02T13:19</timestamp> |
| 225 | + </favorite_status> |
| 226 | + </episode> |
| 227 | + <episode> |
| 228 | + <podcast_guid>9d6786c9-ed48-470d-acbe-e593547f4b5b</podcast_guid> |
| 229 | + <sync_id>5773f457-e71b-417d-8ea8-f07c38a03a3e</sync_id> |
| 230 | + <episode_guid>01999e25-08cd-4f29-a61e-6ca459b40d27</episode_guid> |
| 231 | + <title>Walk with the weatherman</title> |
| 232 | + <publish_date>2022-04-27T19:35:20.000Z</publish_date> |
| 233 | + <enclosure_url>https://op3.dev/e/https://podcasts.example2.net/audio/@digitalcitizen/49-walk-with-the-weatherman.mp3</enclosure_url> |
| 234 | + <episode_url>https://podcasts.example2.net/@digitalcitizen/episodes/49-walk-with-the-weatherman</episode_url> |
| 235 | + <playback_position> |
| 236 | + <value>2100</value> |
| 237 | + <timestamp>2024-11-01T17:38</timestamp> |
| 238 | + </playback_position> |
| 239 | + <played_status> |
| 240 | + <value>false</value> |
| 241 | + <timestamp>2024-04-28T09:20</timestamp> |
| 242 | + </played_status> |
| 243 | + <new_status> |
| 244 | + <value>false</value> |
| 245 | + <timestamp>2024-11-01T17:02</timestamp> |
| 246 | + </new_status> |
| 247 | + <download_status> |
| 248 | + <value>true</value> |
| 249 | + <timestamp>2024-11-01T17:02</timestamp> |
| 250 | + </download_status> |
| 251 | + <favorite_status> |
| 252 | + <value>false</value> |
| 253 | + <timestamp>2024-04-28T09:20</timestamp> |
| 254 | + </favorite_status> |
| 255 | + </episode> |
| 256 | + </episodes> |
| 257 | + ``` |
| 258 | + |
| 259 | + </TabItem> |
| 260 | +</Tabs> |
| 261 | + |
| 262 | +[ISO 8601 format]: https://www.iso.org/iso-8601-date-and-time-format.html |
0 commit comments