Merge pull request #4 from theDanielJLewis/djl-branch-1

More internal link fixes
pull/707/head
Daniel J. Lewis 2025-06-13 17:14:51 -04:00 zatwierdzone przez GitHub
commit 2225d9a28d
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
3 zmienionych plików z 358 dodań i 331 usunięć

Wyświetl plik

@ -6,17 +6,17 @@
## Purpose
Podcasting is a tremendous ecosystem brimming with tons of stories and ideas that go freely from any platform to any application. That comes
with a huge drawback: finding and being found can be harsh. Podcast creators struggle to be found while podcast listeners struggle to find content.
Podcasting is a tremendous ecosystem brimming with tons of stories and ideas that go freely from any platform to any application. That comes
with a huge drawback: finding and being found can be harsh. Podcast creators struggle to be found while podcast listeners struggle to find content.
Several platforms are now implementing recommendation engines, but these features are expensive and unattainable for small players. Moreover they
are slowly creating closed silos and removing power from content creators. This specification is about giving control to content creators on the
are slowly creating closed silos and removing power from content creators. This specification is about giving control to content creators on the
content they want to recommend, and at the same time providing a free recommendation system to all players.
<br><br>
## Specification
```xml
<podcast:recommendations
url="[url to json file(string)]"
@ -40,36 +40,38 @@ There may be several occurences of this tag for the same element (one per langua
#### Attributes
- `url` (required): This is the url to the json file.
- `type` (required): Mime type, must be json.
- `language` (optional): The language of the recommended episodes (two-letter language codes, with some possible modifiers, such as "en-us"). If there
is no language attribute given, the linked file is assumed to be the same language that is specified by the RSS `<language>` element.
- `url` (required): This is the url to the json file.
- `type` (required): Mime type, must be json.
- `language` (optional): The language of the recommended episodes (two-letter language codes, with some possible modifiers, such as "en-us"). If there
is no language attribute given, the linked file is assumed to be the same language that is specified by the RSS `<language>` element.
#### Examples
- `<podcast:recommendations url="https://domain.tld/recommendation?guid=1234" type="application/json" />`
- `<podcast:recommendations url="https://domain.tld/recommendation?guid=1234" type="application/json" language="en" />`
- `<podcast:recommendations url="https://domain.tld/recommendation?guid=1234" type="application/json" />`
- `<podcast:recommendations url="https://domain.tld/recommendation?guid=1234" type="application/json" language="en" />`
<br><br>
## JSON File Specification
The recommendations file contains a simple JSON object with 2 required properties and a few optional properties. This top-level object
The recommendations file contains a simple JSON object with 2 required properties and a few optional properties. This top-level object
is simply a container for the main payload, which is an array of `recommendation` objects contained in the `recommendations` property.
#### Required Attributes
- `version` (required - string) The version number of the format being used.
- `recommendations` (required - array) An array of `recommendations` objects defined below.
- `version` (required - string) The version number of the format being used.
- `recommendations` (required - array) An array of `recommendations` objects defined below.
#### Optional Attributes
- `comment` (optional - string) A comment on this file.
- `title` (optional - string) The name of the source podcast or **source** podcast episode. Applies to both Channel and Item.
- `feed` (optional - string) The RSS URL of the **source** podcast. Applies to both podcast Channel and podcast Item.
- `guid` (optional - string) The GUID of the **source** element. Applies to both podcast Channel and podcast Item.
- `url` (optionnal - string) The enclosure URL of the **source** medium.
- `comment` (optional - string) A comment on this file.
- `title` (optional - string) The name of the source podcast or **source** podcast episode. Applies to both Channel and Item.
- `feed` (optional - string) The RSS URL of the **source** podcast. Applies to both podcast Channel and podcast Item.
- `guid` (optional - string) The GUID of the **source** element. Applies to both podcast Channel and podcast Item.
- `url` (optionnal - string) The enclosure URL of the **source** medium.
#### Example
```json
{
"version": "1.0",
@ -82,63 +84,63 @@ is simply a container for the main payload, which is an array of `recommendation
}
```
<br><br>
## The "recommendations" Array
Each recommendation is defined in a `recommendation` object that resides within the `recommendations` array. It is meant to structure data
Each recommendation is defined in a `recommendation` object that resides within the `recommendations` array. It is meant to structure data
that might otherwise be present - buried within shownotes HTML.
#### Required attributes:
- `linkType` (required - string) The link type of this recommended content, it can be:
- 'generic',
- 'feed',
- 'item',
- 'none'
- `medium` (required - string) The [medium](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#medium) type. It can be:
- `podcast`,
- `music`,
- `video`,
- `film`,
- `audiobook`,
- `newsletter`,
- `blog`
- `title` (required - string) The title for this recommended content.
- `linkType` (required - string) The link type of this recommended content, it can be:
- 'generic',
- 'feed',
- 'item',
- 'none'
- `medium` (required - string) The [medium](/docs/tags/medium.md) type. It can be:
- `podcast`,
- `music`,
- `video`,
- `film`,
- `audiobook`,
- `newsletter`,
- `blog`
- `title` (required - string) The title for this recommended content.
#### Optional Attributes:
- `url` (optional - string) The URL for this recommended content. If recommended content type is *"feed"* this is the home page of the podcast/medium.
If recommended content type is *"feed-item"* this is the enclosure URL.
- `image` (optional - string) The image URL for this recommended content. Image must have a 1:1 ratio (square).
- `displayStartTime` (optional - float) The start time (in seconds) that tells when this recommended content should start being displayed. If `displayStartTime`
is omitted, recommendation will be displayed from the beginning. Applies only when called from an *Item* (not from the *Channel*).
- `displayDuration` (optional - float) The duration (in seconds) that tells when this recommended content should stop being displayed. If `displayDuration` is
omitted, recommendation will be displayed until the end. Applies only when called from an *Item* (not from the *Channel*).
- `feed` (optional - string) The RSS URL of this recommended content. Applies to *"feed"*, *"feed-item"* types.
- `guid` (optional - string) The GUID of this recommended content. Applies to *"feed"* and *"feed-item"* types.
- `startTime` (optional - float) The start time (in seconds) of this recommended content. Applies to *"feed-item"* type only.
- `duration` (optional - float) The duration (in seconds) of this recommended content. Applies to *"feed-item"* type only.
- `motive` (optional - string) The reason why this content is recommended. It can be:
- `references` (content that was used when creating this podcast, similar to the Wikipedia References paragraph),
- `additional content` (content that provides extra information),
- `acknowledgment` (thanking people),
- `advertising` (sponsored content),
- `audience exchange` (exchanging audiences between podcasts),
- `content-based recommendation` (content related thanks to semantic indexation),
- `audience-based recommendation` (people who liked this also liked…),
- `made by the same people` (the creators of this podcast also made that…)
- `relevance` (optional - float) The relevance of this recommended content regarding this Channel or Item. Number must be in [0…1]. 0 is for irrelevant content, 1 is
for contents that match perfectly.
- `url` (optional - string) The URL for this recommended content. If recommended content type is _"feed"_ this is the home page of the podcast/medium.
If recommended content type is _"feed-item"_ this is the enclosure URL.
- `image` (optional - string) The image URL for this recommended content. Image must have a 1:1 ratio (square).
- `displayStartTime` (optional - float) The start time (in seconds) that tells when this recommended content should start being displayed. If `displayStartTime`
is omitted, recommendation will be displayed from the beginning. Applies only when called from an _Item_ (not from the _Channel_).
- `displayDuration` (optional - float) The duration (in seconds) that tells when this recommended content should stop being displayed. If `displayDuration` is
omitted, recommendation will be displayed until the end. Applies only when called from an _Item_ (not from the _Channel_).
- `feed` (optional - string) The RSS URL of this recommended content. Applies to _"feed"_, _"feed-item"_ types.
- `guid` (optional - string) The GUID of this recommended content. Applies to _"feed"_ and _"feed-item"_ types.
- `startTime` (optional - float) The start time (in seconds) of this recommended content. Applies to _"feed-item"_ type only.
- `duration` (optional - float) The duration (in seconds) of this recommended content. Applies to _"feed-item"_ type only.
- `motive` (optional - string) The reason why this content is recommended. It can be:
- `references` (content that was used when creating this podcast, similar to the Wikipedia References paragraph),
- `additional content` (content that provides extra information),
- `acknowledgment` (thanking people),
- `advertising` (sponsored content),
- `audience exchange` (exchanging audiences between podcasts),
- `content-based recommendation` (content related thanks to semantic indexation),
- `audience-based recommendation` (people who liked this also liked…),
- `made by the same people` (the creators of this podcast also made that…)
- `relevance` (optional - float) The relevance of this recommended content regarding this Channel or Item. Number must be in [0…1]. 0 is for irrelevant content, 1 is
for contents that match perfectly.
#### Example of Structure
```json
{
"linkType": "generic",
"title": "History of podcasting",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Podcasts_%28iOS%29.svg/440px-Podcasts_%28iOS%29.svg.png",
"url": "https://en.wikipedia.org/wiki/History_of_podcasting"
"linkType": "generic",
"title": "History of podcasting",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Podcasts_%28iOS%29.svg/440px-Podcasts_%28iOS%29.svg.png",
"url": "https://en.wikipedia.org/wiki/History_of_podcasting"
}
```
@ -148,53 +150,52 @@ Here is what a very basic recommendations file may look like:
```json
{
"version": "1.0",
"recommendations":
[
{
"linkType": "none",
"medium": "text",
"title": "Eat five vegetables every day.",
"motive": "advertising"
},
{
"linkType": "generic",
"medium": "html",
"title": "History of podcasting",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Podcasts_%28iOS%29.svg/440px-Podcasts_%28iOS%29.svg.png",
"url": "https://en.wikipedia.org/wiki/History_of_podcasting",
"motive": "additional content"
},
{
"linkType": "feed",
"medium": "podcast",
"title": "Podcasting 2.0",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://podcastindex.org/podcast/920666",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"motive": "audience exchange"
},
{
"linkType": "item",
"medium": "podcast",
"title": "Episode 26: Manning Battlestations",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://mp3s.nashownotes.com/PC20-26-2021-02-26-Final.mp3",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"guid": "PC2026"
},
{
"linkType": "item",
"medium": "podcast",
"title": "GO PODCASTING!!!",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://mp3s.nashownotes.com/PC20-26-2021-02-26-Final.mp3",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"guid": "PC2026",
"startTime": 4737.0,
"duration": 5.0
}
]
"version": "1.0",
"recommendations": [
{
"linkType": "none",
"medium": "text",
"title": "Eat five vegetables every day.",
"motive": "advertising"
},
{
"linkType": "generic",
"medium": "html",
"title": "History of podcasting",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Podcasts_%28iOS%29.svg/440px-Podcasts_%28iOS%29.svg.png",
"url": "https://en.wikipedia.org/wiki/History_of_podcasting",
"motive": "additional content"
},
{
"linkType": "feed",
"medium": "podcast",
"title": "Podcasting 2.0",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://podcastindex.org/podcast/920666",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"motive": "audience exchange"
},
{
"linkType": "item",
"medium": "podcast",
"title": "Episode 26: Manning Battlestations",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://mp3s.nashownotes.com/PC20-26-2021-02-26-Final.mp3",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"guid": "PC2026"
},
{
"linkType": "item",
"medium": "podcast",
"title": "GO PODCASTING!!!",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://mp3s.nashownotes.com/PC20-26-2021-02-26-Final.mp3",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"guid": "PC2026",
"startTime": 4737.0,
"duration": 5.0
}
]
}
```
@ -202,69 +203,68 @@ Here is what a very basic recommendations file may look like:
```json
{
"version": "1.0",
"title": "Podnews podcasting news",
"feed": "https://podnews.net/rss",
"guid": "9b024349-ccf0-5f69-a609-6b82873eab3c",
"recommendations":
[
{
"linkType": "none",
"medium": "text",
"title": "Eat five vegetables every day.",
"motive": "advertising"
},
{
"displayStartTime": 0.0,
"displayDuration": 120.0,
"linkType": "generic",
"medium": "html",
"title": "History of podcasting",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Podcasts_%28iOS%29.svg/440px-Podcasts_%28iOS%29.svg.png",
"url": "https://en.wikipedia.org/wiki/History_of_podcasting",
"motive": "additional content",
"relevance": 0.8
},
{
"displayStartTime": 120.50,
"displayDuration": 60.0,
"linkType": "feed",
"medium": "podcast",
"title": "Podcasting 2.0",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://podcastindex.org/podcast/920666",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"motive": "audience exchange",
"relevance": 0.7
},
{
"displayStartTime": 240.60,
"displayDuration": 180.0,
"linkType": "feed-item",
"medium": "podcast",
"title": "Episode 26: Manning Battlestations",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://mp3s.nashownotes.com/PC20-26-2021-02-26-Final.mp3",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"guid": "PC2026",
"relevance": 0.5
},
{
"displayStartTime": 3600.10,
"displayDuration": 60.0,
"linkType": "item",
"medium": "podcast",
"title": "GO PODCASTING!!!",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://mp3s.nashownotes.com/PC20-26-2021-02-26-Final.mp3",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"guid": "PC2026",
"startTime": 4737.0,
"duration": 5.0,
"motive": "additional content",
"relevance": 0.9
}
]
"version": "1.0",
"title": "Podnews podcasting news",
"feed": "https://podnews.net/rss",
"guid": "9b024349-ccf0-5f69-a609-6b82873eab3c",
"recommendations": [
{
"linkType": "none",
"medium": "text",
"title": "Eat five vegetables every day.",
"motive": "advertising"
},
{
"displayStartTime": 0.0,
"displayDuration": 120.0,
"linkType": "generic",
"medium": "html",
"title": "History of podcasting",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Podcasts_%28iOS%29.svg/440px-Podcasts_%28iOS%29.svg.png",
"url": "https://en.wikipedia.org/wiki/History_of_podcasting",
"motive": "additional content",
"relevance": 0.8
},
{
"displayStartTime": 120.5,
"displayDuration": 60.0,
"linkType": "feed",
"medium": "podcast",
"title": "Podcasting 2.0",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://podcastindex.org/podcast/920666",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"motive": "audience exchange",
"relevance": 0.7
},
{
"displayStartTime": 240.6,
"displayDuration": 180.0,
"linkType": "feed-item",
"medium": "podcast",
"title": "Episode 26: Manning Battlestations",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://mp3s.nashownotes.com/PC20-26-2021-02-26-Final.mp3",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"guid": "PC2026",
"relevance": 0.5
},
{
"displayStartTime": 3600.1,
"displayDuration": 60.0,
"linkType": "item",
"medium": "podcast",
"title": "GO PODCASTING!!!",
"image": "https://noagendaassets.com/enc/1601061118.678_pciavatar.jpg",
"url": "https://mp3s.nashownotes.com/PC20-26-2021-02-26-Final.mp3",
"feed": "http://mp3s.nashownotes.com/pc20rss.xml",
"guid": "PC2026",
"startTime": 4737.0,
"duration": 5.0,
"motive": "additional content",
"relevance": 0.9
}
]
}
```
@ -278,5 +278,6 @@ As a safeguard against that, apps should:
- Fetch all recommendations at the same time disregarding `displayStartTime` so that HTTP requests cannot be used as a way of measuring who listens to what.
Discussion here:
- https://github.com/Podcastindex-org/podcast-namespace/issues/205
- https://podcastindex.social/web/statuses/105833620038854052

Wyświetl plik

@ -6,7 +6,7 @@
# THIS IS NOT THE FINAL SPECIFICATION
You [want to look here for that](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#social-interact)
You [want to look here for that](/docs/tags/social-interact.md)
<hr>
@ -18,7 +18,7 @@ You [want to look here for that](https://github.com/Podcastindex-org/podcast-nam
<small>Version 1.0 by Benjamin Bellamy - 2021.04.13</small>
**This is not the final specification. You [want to look here for that](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#social-interact)**
**This is not the final specification. You [want to look here for that](/docs/tags/social-interact.md)**
<br />
@ -32,6 +32,7 @@ necessary to make them possible - as seamless as possible.
Of course not all podcast apps would implement all platforms. Each one would implement the one(s) they want to provide their users a better interaction with.
There are three elements:
- **"podcast:social"** for the \<channel> element: tells the user **which platform(s)** is/are used for this podcast.
- **"podcast:socialSignUp"** for the \<podcast:social> element: tells the user **where to sign up** on this platform.
- **"podcast:socialInteract"** for the \<item> element: tells the user where to **interact with this specific episode**.
@ -42,43 +43,44 @@ There are three elements:
- **\<podcast:social platform="[platform_id]" protocol="[protocol_name]" accountId="[podcast_account_id]" accountUrl="[podcast_account_url]" priority="[platform_priority]">**[one or more "podcast:socialSignUp" elements]**\</podcast:social>**
Channel (optional | multiple)
Channel (optional | multiple)
This element allows a podcaster to specify one or more platforms where listeners can interact.
There may be several occurences of this tag for the same element (on several platforms, the podcast may have several accounts on the same plaforms…)
This element allows a podcaster to specify one or more platforms where listeners can interact.
There may be several occurences of this tag for the same element (on several platforms, the podcast may have several accounts on the same plaforms…)
- `platform` (required): This is the platform id. It can be one of the following:
- castopod
- mastodon
- peertube
- facebook
- twitter
- instagram
- slack
- discord
- cast.garden
- 3speak
- peakd.com
- …
- castopod
- mastodon
- peertube
- facebook
- twitter
- instagram
- slack
- discord
- cast.garden
- 3speak
- peakd.com
- …
- `protocol` (required): This is the protocol name. It can be one of the following:
- activitypub
- xmpp
- irc
- matrix
- facebook
- twitter
- instagram
- slack
- discord
- hive
- …
- `accountId` (required): The podcast ID on this platform.
- `accountUrl` (required): The podcast URL on this platform.
- `priority` (optional): This platform priority (useful if the podcaster wants to tell which platform is preferred, lower is better)
- activitypub
- xmpp
- irc
- matrix
- facebook
- twitter
- instagram
- slack
- discord
- hive
- …
- `accountId` (required): The podcast ID on this platform.
- `accountUrl` (required): The podcast URL on this platform.
- `priority` (optional): This platform priority (useful if the podcaster wants to tell which platform is preferred, lower is better)
Examples:
- `<podcast:social platform="twitter" protocol="twitter" accountId="@PodcastindexOrg" accountUrl="https://twitter.com/PodcastindexOrg"></podcast:social>`
- `<podcast:social platform="mastodon" protocol="activitypub" accountId="@podcastindex@noagendasocial.com" accountUrl="https://noagendasocial.com/@podcastindex"></podcast:social>`
Examples:
- `<podcast:social platform="twitter" protocol="twitter" accountId="@PodcastindexOrg" accountUrl="https://twitter.com/PodcastindexOrg"></podcast:social>`
- `<podcast:social platform="mastodon" protocol="activitypub" accountId="@podcastindex@noagendasocial.com" accountUrl="https://noagendasocial.com/@podcastindex"></podcast:social>`
### SocialSignUp Element
@ -88,11 +90,12 @@ There are three elements:
This element allows easy onboarding for listeners on social/discussion platforms, especially for decentralized ones (such as Matrix or ActivityPub) where podcasters and listeners can register on different servers.
- `homeUrl` (required): The platform home URL.
- `signUpUrl` (required): The platform sign up URL.
- `priority` (optional): This platform priority (useful if the podcaster wants to tell which platform is preferred, lower is better)
- `homeUrl` (required): The platform home URL.
- `signUpUrl` (required): The platform sign up URL.
- `priority` (optional): This platform priority (useful if the podcaster wants to tell which platform is preferred, lower is better)
Examples:
- `<podcast:socialSignUp homeUrl="https://twitter.com/" signUpUrl="https://twitter.com/login" priority="1" />`
- `<podcast:socialSignUp homeUrl="https://podcastindex.social/public" signUpUrl="https://podcastindex.social/auth/sign_up" priority="2" />`
@ -105,44 +108,46 @@ There are three elements:
This element allows listeners to interact with (comment, share, like, review…) an episode, or a podcast.
- `platform` (required): This is the platform id. It can be one of the following:
- castopod
- mastodon
- peertube
- facebook
- twitter
- instagram
- slack
- discord
- cast.garden
- 3speak
- peakd
- fountain
- none *(to indicate a strong opt-out preference)*
- …
- castopod
- mastodon
- peertube
- facebook
- twitter
- instagram
- slack
- discord
- cast.garden
- 3speak
- peakd
- fountain
- none _(to indicate a strong opt-out preference)_
- …
- `protocol` (required): This is the protocol name. It can be one of the following:
- activitypub
- facebook
- twitter
- instagram
- slack
- discord
- xmpp
- irc
- matrix
- hive
- lightningcomments (see #347 for protocol description)
- …
- `accountId` (required): The podcast ID on this platform.
- `pubDate` (optional): publication date on this platform. This can be useful when there are several interactions for the same platform for the same episode (for instance, two Tweets about the same episode). Format must be [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).
- `priority` (optional): This platform priority (useful if the podcaster wants to tell which platform is preferred, lower is better)
- element's content: URL to the social media post on this platform corresponding to this episode (if at the `<item>` level) or for this podcast (if at the `channel` level), or a short reason for apps to display when comments are disabled (if `platform="none"`)
- activitypub
- facebook
- twitter
- instagram
- slack
- discord
- xmpp
- irc
- matrix
- hive
- lightningcomments (see #347 for protocol description)
- …
- `accountId` (required): The podcast ID on this platform.
- `pubDate` (optional): publication date on this platform. This can be useful when there are several interactions for the same platform for the same episode (for instance, two Tweets about the same episode). Format must be [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).
- `priority` (optional): This platform priority (useful if the podcaster wants to tell which platform is preferred, lower is better)
- element's content: URL to the social media post on this platform corresponding to this episode (if at the `<item>` level) or for this podcast (if at the `channel` level), or a short reason for apps to display when comments are disabled (if `platform="none"`)
Examples:
- `<podcast:socialInteract platform="twitter" protocol="twitter" accountId="@Podverse" priority="2" pubDate="2021-04-14T10:25:42Z">https://twitter.com/Podverse/status/1375624446296395781</podcast:socialInteract>`
- `<podcast:socialInteract priority="1" platform="castopod" protocol="activitypub" accountId="@heloise@lespoesiesdheloise.fr" pubDate="2021-04-08T20:07:13+0000">https://lespoesiesdheloise.fr/@heloise/notes/e4b3d7f3-e84b-40c6-b828-f5537f0c3659</podcast:socialInteract>`
- `<podcast:socialInteract priority="1" platform="fountain" protocol="lightningcomments" accountId="123868c219bdb51a33560d854d500fe7d3123a1ad9e05dd89d0007e11313588123">https://api.fountain.fm/v1/comments?feed=221233&amp;episode=1230123071</podcast:socialInteract>`
Or to opt out:
- `<podcast:socialInteract platform="none">Comments disabled for this episode</podcast:socialInteract>`
## Full RSS feed example
@ -187,5 +192,6 @@ There are three elements:
```
Discussion here:
- https://github.com/Podcastindex-org/podcast-namespace/issues/153
- https://podcastindex.social/web/statuses/106065482252134072

Wyświetl plik

@ -4,7 +4,6 @@
<br>
# Purpose
"Claiming" a podcast in a podcast directory, platform, app or service means that your customer is proving that
@ -20,8 +19,8 @@ material.
<br>
# The Claiming Service
Run a podcast directory, app, platform or service and want to check that someone is the owner of this podcast?
Benefits of using "quick-claim" are that it's one-click within a browser or webview, rather than sending a potential
@ -29,16 +28,18 @@ user an email. Assuming they are logged-in to their podcast host (or can log in)
instantly and come back to your service within seconds.
Additional benefits are:
* Anyone authorized to access the podcast host dashboard can claim the show (subject to the user having the right
- Anyone authorized to access the podcast host dashboard can claim the show (subject to the user having the right
permissions).
* Email confirmations are often subject to delay, fall into spam, or are blocked altogether.
* Email confirmation emails can be faked and lead to security concerns.
* The listing of an email address in an RSS feed leads to privacy issues and spam.
* Email confirmations often lead to around [25%](https://www.quora.com/What-is-a-typical-abandonment-rate-for-email-verifications) abandomnent
- Email confirmations are often subject to delay, fall into spam, or are blocked altogether.
- Email confirmation emails can be faked and lead to security concerns.
- The listing of an email address in an RSS feed leads to privacy issues and spam.
- Email confirmations often lead to around [25%](https://www.quora.com/What-is-a-typical-abandonment-rate-for-email-verifications) abandomnent
<br>
## 1. Check that quick-claiming is enabled in the RSS feed
```xml
<podcast:verify
auth="https://hostingprovider.com/claiming/"
@ -49,8 +50,8 @@ Additional benefits are:
The presence of the `<podcast:verify>` tag means that this feed supports quick claiming. The tag will have two required
attributes that must be present:
* `auth` (required): The https URL of a "quick claim" page on the hosting provider that is protected by customer login.
* `pub` (required): The public key corresponding to the private key the hosting provider users to sign claiming requests.
- `auth` (required): The https URL of a "quick claim" page on the hosting provider that is protected by customer login.
- `pub` (required): The public key corresponding to the private key the hosting provider users to sign claiming requests.
<br>
@ -59,9 +60,8 @@ attributes that must be present:
You will redirect your user to the `auth` url present in the podcaster's RSS feed, with 1 to 3 URL parameters, as
defined below.
### Param: `consumer`
Your directory/service base URL of the return_path.
It's recommanded to support open graph allowing the hosting provider to present a clean name and icon for your
@ -74,25 +74,27 @@ for open graph data while ensuring the return path is tied to the hostname and i
#### Examples:
`consumer=https://podcastindex.org`
```html
PodcastIndex.org (podcastindex.org)
The Podcast Index is here to preserve, protect and extend the open, independent podcasting ecosystem.
This service would like to verify you are the owner of this podcast. Do you want us to confirm ?
```html
PodcastIndex.org (podcastindex.org) The Podcast Index is here to preserve,
protect and extend the open, independent podcasting ecosystem. This service
would like to verify you are the owner of this podcast. Do you want us to
confirm ?
```
`consumer=https://podcastindex.org/quick_claim`
```html
🗼PodcastIndex Quick Claiming (podcastindex.org)
Claiming your podcast into The Podcast Index directory gives you cool stuff !
This service would like to verify you are the owner of this podcast. Do you want us to confirm ?
```html
🗼PodcastIndex Quick Claiming (podcastindex.org) Claiming your podcast into The
Podcast Index directory gives you cool stuff ! This service would like to verify
you are the owner of this podcast. Do you want us to confirm ?
```
<br>
### Param: `return_path`
A path relative to the `consumer` parameter, to redirect with the result of the authentication. If the consumer is enough,
A path relative to the `consumer` parameter, to redirect with the result of the authentication. If the consumer is enough,
you can omit it.
#### Examples:
@ -100,16 +102,19 @@ you can omit it.
These are example calculations of what the return url will be with different inputs:
`consumer=https://podcastindex.org/quick_claim` (no return path) ⤵ <br>
```http
https://podcastindex.org/quick_claim?token=[token]
```
`consumer=https://podcastindex.org/quick_claim&return_path=/claimed.php`<br>
```http
https://podcastindex.org/quick_claim/claimed.php?token=[token]
```
`consumer=https://podcastindex.org/quick_claim&return_path=../claim.php`<br>
```http
https://podcastindex.org/claim.php?token=[token]
```
@ -117,13 +122,14 @@ https://podcastindex.org/claim.php?token=[token]
<br>
### Param: `guid`
If a [`<podcast:guid>`](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#guid) is present in the RSS feed it SHOULD be included. If it's not we omit it or send an empty string.
If a [`<podcast:guid>`](/docs/tags/guid.md) is present in the RSS feed it SHOULD be included. If it's not we omit it or send an empty string.
We keep it simple, we should trust the users of our spec (in this case the hosting providers) : if they don't include
a [`<podcast:guid>`](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#guid), I don't see the point to calculate it and give it to them, as they most likely don't have it on their
a [`<podcast:guid>`](/docs/tags/guid.md), I don't see the point to calculate it and give it to them, as they most likely don't have it on their
hand and probably don't use it. If they would, it would be included in the feed.
If we don't have any [`<podcast:guid>`](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#guid), maybe the hosting provider has only partial support of the podcasting 2.0 spec. Maybe
If we don't have any [`<podcast:guid>`](/docs/tags/guid.md), maybe the hosting provider has only partial support of the podcasting 2.0 spec. Maybe
they only support claiming and include the guid in the auth url. Perhaps it's a Wordpress extension and there is only
one podcast and no need to "select a podcast" when you're logged in. We can't know and guess all usages.
@ -133,14 +139,14 @@ one podcast and no need to "select a podcast" when you're logged in. We can't kn
Here's a simple example, in PHP, of a quick claim button:
```php
```php
<a
class="btn btn-default"
href="<?php echo $feed_auth_url."?consumer=https://myservice.com/quick_claim&guid=".$feed_guid; ?>"
class="btn btn-default"
href="<?php echo $feed_auth_url."?consumer=https://myservice.com/quick_claim&guid=".$feed_guid; ?>"
>
Claim this now
</a>
```
```
<br>
@ -154,14 +160,16 @@ The GET request will contain only one parameter: `token`.
This parameter will be a JSON Web Token ([JWT](https://jwt.io/)), and will include the following data :
```typescript
type QuickClaimResponse = {
guid?: GUID;
accepted: true;
} | {
guid?: GUID;
accepted: false;
failureReason: "back" | string;
};
type QuickClaimResponse =
| {
guid?: GUID
accepted: true
}
| {
guid?: GUID
accepted: false
failureReason: "back" | string
}
```
Using JWT lets us ensure the hosting provider wrote the response. It also allow us to define, or not, an expiration
@ -183,6 +191,7 @@ confirmed the operation, and that it's the same entity giving you this informati
<br><br>
# The Hosting Service
Quick-claim is designed to allow your customers to demonstrate that they own their podcast on third-party services.
The benefits of using quick claiming are that it's one-click for your customers from the service they wish to claim,
@ -209,8 +218,8 @@ If you're a podcast host wanting to add quick claiming for your customers, then
The tag needs two attributes :
* `auth` (required): The URL of a secure page that is protected on your server by customer login.
* `pub` (required): The public key corresponding to the private key you use to sign claiming requests.
- `auth` (required): The URL of a secure page that is protected on your server by customer login.
- `pub` (required): The public key corresponding to the private key you use to sign claiming requests.
<br>
@ -219,6 +228,7 @@ The tag needs two attributes :
Your claiming `auth` url will be called with 1 to 3 parameters, as defined below:
### Param: `consumer`
The directory/service base URL.
This url must be used to present the permission asker to the user. It's recommended for services to support
@ -231,6 +241,7 @@ open graph data while ensuring the return path is tied to the hostname and ident
<br>
### Param: `return_path`
A path relative to the `consumer` parameter, to use as a callback to the `consumer` with the result of the authentication.
If the `consumer` is enough, you can omit this parameter.
@ -239,16 +250,19 @@ These are example calculations of what the return url will be with different inp
#### Examples:
`consumer=https://podcastindex.org/quick_claim` (no return path) ⤵ <br>
```http
https://podcastindex.org/quick_claim?token=[token]
```
`consumer=https://podcastindex.org/quick_claim&return_path=/claimed.php`<br>
```http
https://podcastindex.org/quick_claim/claimed.php?token=[token]
```
`consumer=https://podcastindex.org/quick_claim&return_path=../claim.php`<br>
```http
https://podcastindex.org/claim.php?token=[token]
```
@ -256,19 +270,20 @@ https://podcastindex.org/claim.php?token=[token]
<br>
### Param: `guid`
If a `<podcast:guid>` tag was present in the RSS feed, expect it to also be included as a parameter here.
### PHP Example:
```php
```php
<?php
$HOSTING_PRIVATE_KEY = "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2
OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r
1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G";
if (!$user['loggedin']) {
// this user is not logged in. Take them to log in
// Retain the consumer, return_path, guid, and bring them back here
// this user is not logged in. Take them to log in
// Retain the consumer, return_path, guid, and bring them back here
//
exit;
}
@ -369,7 +384,7 @@ TAGS;
<input type="hidden" name="action" value="back" />
<input type="submit" value="cancel this request">
</form>
```
```
The above will successfully check that your user is authenticated and send back the JWT token to the directory service
if the user agreed.
@ -378,7 +393,7 @@ if the user agreed.
## Full Workflow Example
This section will summarize everything into one big example. Here are the defined terms we will use in this example:
This section will summarize everything into one big example. Here are the defined terms we will use in this example:
- **DIRECTORY**: a podcast directory platform
- **HOST**: a podcast hosting service
@ -417,6 +432,7 @@ When **CREATOR** agrees to the claim request, the **HOST** will generate a JWT t
authenticating the **CREATOR** decision.
#### Unsigned JWT:
```json
{
"guid": "ead4c236-bf58-58c6-a2c6-a6b28d128cb6",
@ -425,6 +441,7 @@ authenticating the **CREATOR** decision.
```
#### Signed JWT:
```base64
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJndWlkIjoiZWFkNGMyMzYtYmY1OC01OGM2LWEyYzYtYTZiMjhkMTI4Y2I2IiwiYWNjZXB0ZWQiOnRydWV9.eOXYFi9uUSUAKWcI8GdJ15RIhjoCvR0l9TUCPsqhsTYqaGFTwbH6zXzYqIqhxmtSotvL8ZLumP64LRFBjHX5Mw
```
@ -434,6 +451,7 @@ Decode/Encode online with : https://jwt.io/
#### Key Details
- Private Key
```
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2
@ -443,6 +461,7 @@ OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r
```
- Public Key
```
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9
@ -492,6 +511,7 @@ https://directory.com/quick_claiming/ead4c236-bf58-58c6-a2c6-a6b28d128cb6/return
<br><br>
## Final Thoughts
Here are my thoughts on this idea and how to implement it, feel free to make any remarks about it.
JWT seems to me to be the middle ground between complexity and simplicity for a decentralized authorization system.