live.voxport.podcast.episode

voxport.live

Schema Diff

+2 -2

From

CID
bafyreifyezrz3mc...
Indexed At
2026-09-01 18:32 UTC
View this version

To

CID
bafyreibtvk5eds3...
Indexed At
2026-09-09 15:42 UTC
View this version

Compatibility Analysis

Breaking Changes Detected

3 breaking changes, 2 non-breaking changes.

Breaking Changes (3)
  • RemovedVertex RemovedVertex { vertex_id: "live.voxport.podcast.series" }
  • RemovedEdge RemovedEdge { src: "live.voxport.podcast.episode:body.series", tgt: "live.voxport.podcast.series", kind: "ref", name: None }
  • ConstraintTightened ConstraintTightened { vertex_id: "live.voxport.podcast.episode:body.series", sort: "ref", old_value: "live.voxport.podcast.series", new_value: "live.voxport.podcast.series#seriesRef" }
Non-Breaking Changes (2)
  • AddedVertex AddedVertex { vertex_id: "live.voxport.podcast.series#seriesRef" }
  • AddedEdge AddedEdge { src: "live.voxport.podcast.episode:body.series", tgt: "live.voxport.podcast.series#seriesRef", kind: "ref", name: None }

Migration Guidance

Removed Elements

  • RemovedVertex { vertex_id: "live.voxport.podcast.series" }

Added Elements

  • AddedVertex { vertex_id: "live.voxport.podcast.series#seriesRef" }

Constraint Changes

  • ConstraintTightened ConstraintTightened { vertex_id: "live.voxport.podcast.episode:body.series", sort: "ref", old_value: "live.voxport.podcast.series", new_value: "live.voxport.podcast.series#seriesRef" }

Additional Notes

  • Breaking: RemovedEdge { src: "live.voxport.podcast.episode:body.series", tgt: "live.voxport.podcast.series", kind: "ref", name: None }
  • Non-breaking: AddedEdge { src: "live.voxport.podcast.episode:body.series", tgt: "live.voxport.podcast.series#seriesRef", kind: "ref", name: None }
1 1
{
2 2
  "id": "live.voxport.podcast.episode",
3 3
  "defs": {
4 4
    "main": {
5 5
      "key": "tid",
6 6
      "type": "record",
7 7
      "record": {
8 8
        "type": "object",
9 9
        "required": [
10 10
          "series",
11 11
          "title",
12 12
          "createdAt"
13 13
        ],
14 14
        "properties": {
15 15
          "tags": {
16 16
            "type": "array",
17 17
            "items": {
18 18
              "type": "string",
19 19
              "maxLength": 64
20 20
            },
21 21
            "maxLength": 20,
22 22
            "description": "Episode tags/keywords"
23 23
          },
24 24
          "audio": {
25 25
            "type": "blob",
26 26
            "accept": [
27 27
              "audio/mpeg",
28 28
              "audio/mp4",
29 29
              "audio/wav",
30 30
              "audio/ogg",
31 31
              "audio/aac"
32 32
            ],
33 33
            "maxSize": 524288000,
34 34
            "description": "Audio file reference"
35 35
          },
36 36
          "image": {
37 37
            "type": "blob",
38 38
            "accept": [
39 39
              "image/png",
40 40
              "image/jpeg"
41 41
            ],
42 42
            "maxSize": 2097152,
43 43
            "description": "Episode-specific cover art"
44 44
          },
45 45
          "title": {
46 46
            "type": "string",
47 47
            "maxLength": 256,
48 48
            "description": "Episode title"
49 49
          },
50 50
          "series": {
51 -
            "ref": "live.voxport.podcast.series",
51 +
            "ref": "live.voxport.podcast.series#seriesRef",
52 52
            "type": "ref",
53 -
            "description": "Reference to parent series"
53 +
            "description": "Reference to parent series (uri + optional cid)"
54 54
          },
55 55
          "persons": {
56 56
            "type": "array",
57 57
            "items": {
58 58
              "ref": "#person",
59 59
              "type": "ref"
60 60
            },
61 61
            "maxLength": 50,
62 62
            "description": "People associated with this episode — hosts, guests, co-hosts (Podcasting 2.0 podcast:person)"
63 63
          },
64 64
          "summary": {
65 65
            "type": "string",
66 66
            "maxLength": 10000,
67 67
            "description": "Episode description/show notes (supports markdown)"
68 68
          },
69 69
          "audioUrl": {
70 70
            "type": "string",
71 71
            "format": "uri",
72 72
            "description": "External audio URL (if not using blob)"
73 73
          },
74 74
          "chapters": {
75 75
            "type": "array",
76 76
            "items": {
77 77
              "ref": "#chapter",
78 78
              "type": "ref"
79 79
            },
80 80
            "maxLength": 100,
81 81
            "description": "Episode chapters/timestamps"
82 82
          },
83 83
          "duration": {
84 84
            "type": "integer",
85 85
            "minimum": 0,
86 86
            "description": "Duration in seconds"
87 87
          },
88 88
          "explicit": {
89 89
            "type": "boolean",
90 90
            "description": "Whether episode is explicit"
91 91
          },
92 92
          "audioType": {
93 93
            "type": "string",
94 94
            "maxLength": 64,
95 95
            "description": "MIME type of the audio file when using audioUrl. Pairs with audioLength for RSS enclosure reconstruction.",
96 96
            "knownValues": [
97 97
              "audio/mpeg",
98 98
              "audio/mp4",
99 99
              "audio/wav",
100 100
              "audio/ogg",
101 101
              "audio/aac"
102 102
            ]
103 103
          },
104 104
          "createdAt": {
105 105
            "type": "string",
106 106
            "format": "datetime"
107 107
          },
108 108
          "updatedAt": {
109 109
            "type": "string",
110 110
            "format": "datetime"
111 111
          },
112 112
          "soundbites": {
113 113
            "type": "array",
114 114
            "items": {
115 115
              "ref": "#soundbite",
116 116
              "type": "ref"
117 117
            },
118 118
            "maxLength": 20,
119 119
            "description": "Short clip markers for apps to surface (Podcasting 2.0 podcast:soundbite)"
120 120
          },
121 121
          "transcript": {
122 122
            "ref": "#transcript",
123 123
            "type": "ref",
124 124
            "description": "Episode transcript (singular — deprecated, prefer transcripts array)"
125 125
          },
126 126
          "audioLength": {
127 127
            "type": "integer",
128 128
            "minimum": 0,
129 129
            "description": "Byte size of the audio file when using audioUrl. Required for a valid RSS enclosure — without this, a firehose consumer cannot reconstruct the feed from records alone."
130 130
          },
131 131
          "episodeType": {
132 132
            "type": "string",
133 133
            "default": "full",
134 134
            "maxLength": 16,
135 135
            "description": "Episode type per RSS spec",
136 136
            "knownValues": [
137 137
              "full",
138 138
              "trailer",
139 139
              "bonus"
140 140
            ]
141 141
          },
142 142
          "publishedAt": {
143 143
            "type": "string",
144 144
            "format": "datetime",
145 145
            "description": "Publication date (null = draft)"
146 146
          },
147 147
          "transcripts": {
148 148
            "type": "array",
149 149
            "items": {
150 150
              "ref": "#transcript",
151 151
              "type": "ref"
152 152
            },
153 153
            "maxLength": 20,
154 154
            "description": "Episode transcripts — multiple entries for different languages/formats (Podcasting 2.0 podcast:transcript). Preferred over the singular transcript field."
155 155
          },
156 156
          "importedGuid": {
157 157
            "type": "string",
158 158
            "maxLength": 2048,
159 159
            "description": "Original GUID from RSS feed (for imported episodes)"
160 160
          },
161 161
          "seasonNumber": {
162 162
            "type": "integer",
163 163
            "minimum": 1,
164 164
            "description": "Season number"
165 165
          },
166 166
          "episodeNumber": {
167 167
            "type": "integer",
168 168
            "minimum": 1,
169 169
            "description": "Episode number in series"
170 170
          },
171 171
          "blueskyPostRef": {
172 172
            "ref": "com.atproto.repo.strongRef",
173 173
            "type": "ref",
174 174
            "description": "Reference to the Atmosphere cross-post (app.bsky.feed.post) for this episode. Set when the episode is auto-cross-posted, enabling custom feed operators to discover the cross-post from the episode record."
175 175
          }
176 176
        }
177 177
      },
178 178
      "description": "A podcast episode belonging to a series"
179 179
    },
180 180
    "person": {
181 181
      "type": "object",
182 182
      "required": [
183 183
        "name"
184 184
      ],
185 185
      "properties": {
186 186
        "did": {
187 187
          "type": "string",
188 188
          "format": "did",
189 189
          "description": "ATProto DID if the person has a verified identity"
190 190
        },
191 191
        "url": {
192 192
          "type": "string",
193 193
          "format": "uri",
194 194
          "description": "External profile URL (maps to podcast:person href attribute)"
195 195
        },
196 196
        "name": {
197 197
          "type": "string",
198 198
          "maxLength": 128,
199 199
          "description": "Full name or alias of the person (spec: max 128 chars)"
200 200
        },
201 201
        "role": {
202 202
          "type": "string",
203 203
          "maxLength": 64,
204 204
          "description": "Role from the Podcast Taxonomy Project (e.g. host, guest, co-host). Defaults to 'host' if absent."
205 205
        },
206 206
        "group": {
207 207
          "type": "string",
208 208
          "maxLength": 64,
209 209
          "description": "Group from the Podcast Taxonomy Project (e.g. cast, writing, visuals). Defaults to 'cast' if absent."
210 210
        },
211 211
        "image": {
212 212
          "type": "string",
213 213
          "format": "uri",
214 214
          "description": "Profile image URL (maps to podcast:person img attribute)"
215 215
        },
216 216
        "handle": {
217 217
          "type": "string",
218 218
          "format": "handle",
219 219
          "description": "ATProto handle (e.g. alice.bsky.social) if the person has a Bluesky account"
220 220
        }
221 221
      }
222 222
    },
223 223
    "chapter": {
224 224
      "type": "object",
225 225
      "required": [
226 226
        "title",
227 227
        "startTime"
228 228
      ],
229 229
      "properties": {
230 230
        "url": {
231 231
          "type": "string",
232 232
          "format": "uri",
233 233
          "description": "Chapter link URL (Podcasting 2.0 chapter url)"
234 234
        },
235 235
        "image": {
236 236
          "type": "string",
237 237
          "format": "uri"
238 238
        },
239 239
        "title": {
240 240
          "type": "string",
241 241
          "maxLength": 256
242 242
        },
243 243
        "hidden": {
244 244
          "type": "boolean",
245 245
          "description": "Whether the chapter is hidden from the table of contents (Podcasting 2.0 toc:false)"
246 246
        },
247 247
        "endTime": {
248 248
          "type": "integer",
249 249
          "description": "End time in seconds (optional)"
250 250
        },
251 251
        "startTime": {
252 252
          "type": "integer",
253 253
          "description": "Start time in seconds"
254 254
        }
255 255
      }
256 256
    },
257 257
    "soundbite": {
258 258
      "type": "object",
259 259
      "required": [
260 260
        "startTime",
261 261
        "duration"
262 262
      ],
263 263
      "properties": {
264 264
        "title": {
265 265
          "type": "string",
266 266
          "maxLength": 128,
267 267
          "description": "Title/label for the soundbite (spec: max 128 chars)"
268 268
        },
269 269
        "duration": {
270 270
          "type": "string",
271 271
          "maxLength": 16,
272 272
          "description": "Duration in fractional seconds (e.g. '60.0', '42.25'). Matches Podcasting 2.0 spec exactly."
273 273
        },
274 274
        "startTime": {
275 275
          "type": "string",
276 276
          "maxLength": 16,
277 277
          "description": "Start time in fractional seconds (e.g. '73.0', '1234.5'). Matches Podcasting 2.0 spec exactly."
278 278
        }
279 279
      }
280 280
    },
281 281
    "transcript": {
282 282
      "type": "object",
283 283
      "required": [
284 284
        "url",
285 285
        "type"
286 286
      ],
287 287
      "properties": {
288 288
        "rel": {
289 289
          "type": "string",
290 290
          "maxLength": 16,
291 291
          "description": "If 'captions', the file is a closed captions file with time codes",
292 292
          "knownValues": [
293 293
            "captions"
294 294
          ]
295 295
        },
296 296
        "url": {
297 297
          "type": "string",
298 298
          "format": "uri",
299 299
          "description": "URL of the transcript file"
300 300
        },
301 301
        "type": {
302 302
          "type": "string",
303 303
          "maxLength": 64,
304 304
          "description": "MIME type of the transcript file",
305 305
          "knownValues": [
306 306
            "text/plain",
307 307
            "text/html",
308 308
            "text/vtt",
309 309
            "application/x-subrip",
310 310
            "application/json"
311 311
          ]
312 312
        },
313 313
        "language": {
314 314
          "type": "string",
315 315
          "format": "language",
316 316
          "description": "Language of the transcript (defaults to feed language if absent)"
317 317
        }
318 318
      }
319 319
    }
320 320
  },
321 321
  "$type": "com.atproto.lexicon.schema",
322 322
  "lexicon": 1,
323 323
  "description": "A podcast episode belonging to a series"
324 324
}

Compare Other Versions

Lexicon Garden

@