Skip to content

Commit 6299e8e

Browse files
authored
Merge pull request #101 from nb823/podcastindex_feed
PodcastIndex Feed: second extension
2 parents 3df6dfe + 6853ca9 commit 6299e8e

File tree

9 files changed

+1233
-45
lines changed

9 files changed

+1233
-45
lines changed

docs/book/extensions/podcast-index.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,20 @@ single namespace.
1111

1212
| Method | Description |
1313
|-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
14-
| `isLocked()` | Returns whether the feed is open for importing to new platforms. |
14+
| `isLocked()` `isPodcastIndexLocked()` | Returns whether the feed is open for importing to new platforms. |
1515
| `getLockOwner()` `getPodcastIndexLockOwner()` | Returns the email address for owner verification. |
1616
| `getFunding()` `getPodcastIndexFunding()` | Returns funding information. The output is an object with "url" and "value" properties. |
1717
| `getPodcastIndexLicense()` | Returns license information. The output is an object with "identifier" and "url" properties. |
18-
| `getPodcastIndexLocation()` | Returns funding information. The output is an object with "description", "geo" and "osm" properties. |
18+
| `getPodcastIndexLocation()` | Returns location information. The output is an object with "description", "geo" and "osm" properties. |
1919
| `getPodcastIndexImages()` | Returns information on responsive images. The output is an object with a "srcset" property. |
2020
| `getPodcastIndexUpdateFrequency()` | Returns information on the intended release schedule. The output is an object with "description", "complete", "dtstart" and "rrule" properties. |
2121
| `getPodcastIndexPeople()` | Returns information on the involved people. The output is an array of objects, each with the properties "name", "role", "group", "img" and "href". |
22+
| `getPodcastIndexTrailer()` | Returns information on the podcast trailer. The output is an object with the properties "title", "pubdate", "url", "length", "type" and "season". |
23+
| `getPodcastIndexGuid()` | Returns the podcast guid. The output is an object with the property "value". |
24+
| `getPodcastIndexMedium()` | Returns the podcast medium. The output is an object with the property "value". |
25+
| `getPodcastIndexBlocks()` | Returns whether or which platforms are allowed to publicly display this feed. The output is an object with the properties "value" and "id". |
26+
| `getPodcastIndexTxts()` | Returns information on topics that do not have their own tags. The output is an object with the properties "value" and "purpose". |
27+
| `getPodcastIndexPodping()` | Returns whether the feed sends out Podping notifications when changes are made to it. The output is an object with the property "usesPodping". |
2228

2329
### SET methods
2430

@@ -31,7 +37,15 @@ single namespace.
3137
| `setPodcastIndexImages()` | Expects an array with the required key "srcset". |
3238
| `setPodcastIndexUpdateFrequency()` | Expects an array with the required key "description" and the optional keys "complete" (bool), "dtstart" (ISO8601 string) and "rrule". |
3339
| `addPodcastIndexPerson()` | Expects an array with the required key "name" and the optional keys "role", "group", "img" and "href". |
34-
| `setPodcastIndexPeople()` | Expects an array of objects with each the required key "name" and the optional keys "role", "group", "img" and "href". |
40+
| `setPodcastIndexPeople()` | Expects an array of arrays with each the required key "name" and the optional keys "role", "group", "img" and "href". |
41+
| `setPodcastIndexTrailer()` | Expects an array with the required keys "title", "pubdate" and "url" and the optional keys "length", "type" and "season". |
42+
| `setPodcastIndexGuid()` | Expects an array with the required key "value". |
43+
| `setPodcastIndexMedium()` | Expects an array with the required key "value". |
44+
| `addPodcastIndexBlock()` | Expects an array with the required key "value" and the optional key "id". |
45+
| `setPodcastIndexBlocks()` | Expects an array of arrays with each the required key "value" and the optional key "id". |
46+
| `addPodcastIndexTxt()` | Expects an array with the required key "value" and the optional key "purpose". |
47+
| `setPodcastIndexTxts()` | Expects an array of arrays with each the required key "value" and the optional key "purpose". |
48+
| `setPodcastIndexPodping()` | Expects an array with the required key "usesPodping". |
3549

3650
## Episode API
3751

psalm-baseline.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3098,29 +3098,44 @@
30983098
<file src="src/Writer/Extension/PodcastIndex/Feed.php">
30993099
<DocblockTypeContradiction>
31003100
<code><![CDATA[! is_string($value['url'])]]></code>
3101+
<code><![CDATA[! is_string($value['value'])]]></code>
3102+
<code><![CDATA[is_bool($value['usesPodping'])]]></code>
31013103
<code><![CDATA[is_string($value['description'])]]></code>
31023104
<code><![CDATA[is_string($value['description'])]]></code>
31033105
<code><![CDATA[is_string($value['identifier'])]]></code>
31043106
<code><![CDATA[is_string($value['name'])]]></code>
3107+
<code><![CDATA[is_string($value['pubdate'])]]></code>
31053108
<code><![CDATA[is_string($value['srcset'])]]></code>
3109+
<code><![CDATA[is_string($value['title'])]]></code>
3110+
<code><![CDATA[is_string($value['value'])]]></code>
31063111
<code><![CDATA[isset($value['complete']) && ! is_bool($value['complete'])]]></code>
31073112
<code><![CDATA[isset($value['geo']) && ! is_string($value['geo'])]]></code>
31083113
<code><![CDATA[isset($value['group']) && ! is_string($value['group'])]]></code>
3114+
<code><![CDATA[isset($value['id']) && ! is_string($value['id'])]]></code>
3115+
<code><![CDATA[isset($value['length']) && ! is_int($value['length'])]]></code>
31093116
<code><![CDATA[isset($value['osm']) && ! is_string($value['osm'])]]></code>
31103117
<code><![CDATA[isset($value['role']) && ! is_string($value['role'])]]></code>
31113118
<code><![CDATA[isset($value['rrule']) && ! is_string($value['rrule'])]]></code>
3119+
<code><![CDATA[isset($value['season']) && ! is_int($value['season'])]]></code>
3120+
<code><![CDATA[isset($value['type']) && ! is_string($value['type'])]]></code>
31123121
</DocblockTypeContradiction>
31133122
<PossiblyUnusedMethod>
31143123
<code>__call</code>
31153124
<code>__construct</code>
31163125
<code>getEncoding</code>
31173126
<code>setEncoding</code>
3127+
<code>setPodcastIndexBlocks</code>
31183128
<code>setPodcastIndexFunding</code>
3129+
<code>setPodcastIndexGuid</code>
31193130
<code>setPodcastIndexImages</code>
31203131
<code>setPodcastIndexLicense</code>
31213132
<code>setPodcastIndexLocation</code>
31223133
<code>setPodcastIndexLocked</code>
3134+
<code>setPodcastIndexMedium</code>
31233135
<code>setPodcastIndexPeople</code>
3136+
<code>setPodcastIndexPodping</code>
3137+
<code>setPodcastIndexTrailer</code>
3138+
<code>setPodcastIndexTxts</code>
31243139
<code>setPodcastIndexUpdateFrequency</code>
31253140
</PossiblyUnusedMethod>
31263141
<PossiblyUnusedParam>

src/Reader/Extension/PodcastIndex/Feed.php

Lines changed: 213 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,35 @@
2424
* rrule?: string
2525
* }
2626
* @psalm-type PersonObject = object{
27-
* name: string,
28-
* role?: string,
29-
* group?: string,
30-
* img?: string,
31-
* href?: string
27+
* name: string,
28+
* role?: string,
29+
* group?: string,
30+
* img?: string,
31+
* href?: string
3232
* }
33+
* @psalm-type TrailerObject = object{
34+
* title: string,
35+
* pubdate: string,
36+
* url: string,
37+
* length?: int,
38+
* type?: string,
39+
* season?: int
40+
* }
3341
*/
3442
class Feed extends Extension\AbstractFeed
3543
{
3644
/**
3745
* Is the podcast locked (not available for indexing)?
3846
*/
3947
public function isLocked(): bool
48+
{
49+
return $this->isPodcastIndexLocked();
50+
}
51+
52+
/**
53+
* Is the podcast locked (not available for indexing)?
54+
*/
55+
public function isPodcastIndexLocked(): bool
4056
{
4157
if (isset($this->data['locked'])) {
4258
return $this->data['locked'];
@@ -95,9 +111,8 @@ public function getFunding(): ?stdClass
95111
public function getPodcastIndexFunding(): ?stdClass
96112
{
97113
if (array_key_exists('funding', $this->data)) {
98-
/** @var stdClass $object */
99-
$object = $this->data['funding'];
100-
return $object;
114+
/** @psalm-var stdClass */
115+
return $this->data['funding'];
101116
}
102117

103118
$funding = null;
@@ -125,9 +140,8 @@ public function getPodcastIndexFunding(): ?stdClass
125140
public function getPodcastIndexLicense(): object|null
126141
{
127142
if (array_key_exists('license', $this->data)) {
128-
/** @var null|object{identifier: string, url: string} $object */
129-
$object = $this->data['license'];
130-
return $object;
143+
/** @psalm-var null|object{identifier: string, url: string} */
144+
return $this->data['license'];
131145
}
132146

133147
$license = null;
@@ -155,9 +169,8 @@ public function getPodcastIndexLicense(): object|null
155169
public function getPodcastIndexLocation(): object|null
156170
{
157171
if (array_key_exists('location', $this->data)) {
158-
/** @var null|object{description: string, geo?: string, osm?: string} $object */
159-
$object = $this->data['location'];
160-
return $object;
172+
/** @psalm-var null|object{description: string, geo?: string, osm?: string} */
173+
return $this->data['location'];
161174
}
162175

163176
$location = null;
@@ -186,9 +199,8 @@ public function getPodcastIndexLocation(): object|null
186199
public function getPodcastIndexImages(): object|null
187200
{
188201
if (array_key_exists('images', $this->data)) {
189-
/** @var null|object{srcset: string} $object */
190-
$object = $this->data['images'];
191-
return $object;
202+
/** @psalm-var null|object{srcset: string} */
203+
return $this->data['images'];
192204
}
193205

194206
$images = null;
@@ -215,9 +227,8 @@ public function getPodcastIndexImages(): object|null
215227
public function getPodcastIndexUpdateFrequency(): object|null
216228
{
217229
if (array_key_exists('updateFrequency', $this->data)) {
218-
/** @var null|UpdateFrequencyObject $object */
219-
$object = $this->data['updateFrequency'];
220-
return $object;
230+
/** @psalm-var null|UpdateFrequencyObject */
231+
return $this->data['updateFrequency'];
221232
}
222233

223234
$updateFrequency = null;
@@ -247,16 +258,15 @@ public function getPodcastIndexUpdateFrequency(): object|null
247258
public function getPodcastIndexPeople(): array
248259
{
249260
if (array_key_exists('people', $this->data)) {
250-
/** @var list<PersonObject> $people */
251-
$people = $this->data['people'];
252-
return $people;
261+
/** @psalm-var list<PersonObject> */
262+
return $this->data['people'];
253263
}
254264

255265
$nodeList = $this->xpath->query($this->getXpathPrefix() . '/podcast:person');
256266

257267
$personCollection = [];
258268

259-
if ($nodeList->length) {
269+
if ($nodeList->length > 0) {
260270
foreach ($nodeList as $entry) {
261271
assert($entry instanceof DOMElement);
262272
$person = new stdClass();
@@ -275,6 +285,185 @@ public function getPodcastIndexPeople(): array
275285
return $this->data['people'];
276286
}
277287

288+
/**
289+
* Get the podcast trailer
290+
*
291+
* @return null|TrailerObject
292+
*/
293+
public function getPodcastIndexTrailer(): object|null
294+
{
295+
if (array_key_exists('trailer', $this->data)) {
296+
/** @psalm-var null|TrailerObject */
297+
return $this->data['trailer'];
298+
}
299+
300+
$object = null;
301+
302+
$nodeList = $this->xpath->query($this->getXpathPrefix() . '/podcast:trailer');
303+
304+
if ($nodeList->length > 0) {
305+
$item = $nodeList->item(0);
306+
assert($item instanceof DOMElement);
307+
$object = new stdClass();
308+
$object->title = $item->nodeValue;
309+
$object->pubdate = $item->getAttribute('pubdate');
310+
$object->url = $item->getAttribute('url');
311+
$object->length = $item->getAttribute('length');
312+
$object->type = $item->getAttribute('type');
313+
$object->season = $item->getAttribute('season');
314+
}
315+
316+
$this->data['trailer'] = $object;
317+
318+
return $this->data['trailer'];
319+
}
320+
321+
/**
322+
* Get the podcast guid
323+
*
324+
* @return null|object{value: string}
325+
*/
326+
public function getPodcastIndexGuid(): object|null
327+
{
328+
if (array_key_exists('guid', $this->data)) {
329+
/** @psalm-var null|object{value: string} */
330+
return $this->data['guid'];
331+
}
332+
333+
$object = null;
334+
335+
$nodeList = $this->xpath->query($this->getXpathPrefix() . '/podcast:guid');
336+
337+
if ($nodeList->length > 0) {
338+
$item = $nodeList->item(0);
339+
assert($item instanceof DOMElement);
340+
$object = new stdClass();
341+
$object->value = $item->nodeValue;
342+
}
343+
344+
$this->data['guid'] = $object;
345+
346+
return $this->data['guid'];
347+
}
348+
349+
/**
350+
* Get the podcast medium
351+
*
352+
* @return null|object{value: string}
353+
*/
354+
public function getPodcastIndexMedium(): object|null
355+
{
356+
if (array_key_exists('medium', $this->data)) {
357+
/** @psalm-var null|object{value: string} */
358+
return $this->data['medium'];
359+
}
360+
361+
$object = null;
362+
363+
$nodeList = $this->xpath->query($this->getXpathPrefix() . '/podcast:medium');
364+
365+
if ($nodeList->length > 0) {
366+
$item = $nodeList->item(0);
367+
assert($item instanceof DOMElement);
368+
$object = new stdClass();
369+
$object->value = $item->nodeValue;
370+
}
371+
372+
$this->data['medium'] = $object;
373+
374+
return $this->data['medium'];
375+
}
376+
377+
/**
378+
* Get the podcast blocks
379+
*
380+
* @return list<object{value: string, id?: string}>
381+
*/
382+
public function getPodcastIndexBlocks(): array
383+
{
384+
if (array_key_exists('blocks', $this->data)) {
385+
/** @psalm-var list<object{value: string, id?: string}> */
386+
return $this->data['blocks'];
387+
}
388+
389+
$blocks = [];
390+
391+
$nodeList = $this->xpath->query($this->getXpathPrefix() . '/podcast:block');
392+
393+
foreach ($nodeList as $entry) {
394+
assert($entry instanceof DOMElement);
395+
$object = new stdClass();
396+
$object->value = $entry->nodeValue;
397+
$object->id = $entry->getAttribute('id');
398+
399+
$blocks[] = $object;
400+
}
401+
402+
$this->data['blocks'] = $blocks;
403+
404+
return $this->data['blocks'];
405+
}
406+
407+
/**
408+
* Get the podcast txts
409+
*
410+
* @return list<object{value: string, purpose?: string}>
411+
*/
412+
public function getPodcastIndexTxts(): array
413+
{
414+
if (array_key_exists('txts', $this->data)) {
415+
/** @psalm-var list<object{value: string, purpose?: string}> */
416+
return $this->data['txts'];
417+
}
418+
419+
$txts = [];
420+
421+
$nodeList = $this->xpath->query($this->getXpathPrefix() . '/podcast:txt');
422+
423+
foreach ($nodeList as $entry) {
424+
assert($entry instanceof DOMElement);
425+
$object = new stdClass();
426+
$object->value = $entry->nodeValue;
427+
$object->purpose = $entry->getAttribute('purpose');
428+
429+
$txts[] = $object;
430+
}
431+
432+
$this->data['txts'] = $txts;
433+
434+
return $this->data['txts'];
435+
}
436+
437+
/**
438+
* Get the podcast podping
439+
*
440+
* @return null|object{usesPodping: bool}
441+
*/
442+
public function getPodcastIndexPodping(): object|null
443+
{
444+
if (array_key_exists('podping', $this->data)) {
445+
/** @psalm-var null|object{usesPodping: bool} */
446+
return $this->data['podping'];
447+
}
448+
449+
$object = null;
450+
451+
$nodeList = $this->xpath->query($this->getXpathPrefix() . '/podcast:podping');
452+
453+
if ($nodeList->length > 0) {
454+
$item = $nodeList->item(0);
455+
assert($item instanceof DOMElement);
456+
457+
$object = new stdClass();
458+
459+
$object->usesPodping = $item->getAttribute('usesPodping') === 'true';
460+
}
461+
462+
$this->data['podping'] = $object;
463+
464+
return $this->data['podping'];
465+
}
466+
278467
/**
279468
* Register PodcastIndex namespace
280469
*/

0 commit comments

Comments
 (0)