{"id":"2ae4f575abefd339143df678f1fff696debad2247efae0c5a4146f3054b2cbc6","pubkey":"586a1173944bb5b2011093895531eef2905add1f37be80657ada80831d609ded","created_at":1780342582,"kind":30817,"tags":[["d","music-playlists-extended"],["title","Music Playlists (Extended)"],["a","30817:1bc70a0148b3f316da33fe3c89f23e3e71ac4ff998027ec712b905cd24f6a411:music-playlists","","fork"],["p","1bc70a0148b3f316da33fe3c89f23e3e71ac4ff998027ec712b905cd24f6a411"],["client","nostrhub.io"]],"content":"# Music Playlists (Extended)\n\nThis spec defines an addressable event kind for publishing music playlists on Nostr. Tracks can be referenced either by Nostr-native coordinates (`a` tags) or by [Podcast Index](https://podcastindex.org/) GUIDs (`i` tags) — both are valid and may be used together in the same playlist.\n\n## Event Kind\n\n- `34139`: Playlist (addressable per [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md))\n\n## Playlist Event\n\nA playlist is an addressable event containing an ordered list of music tracks.\n\n### Format\n\nThe `.content` field is reserved for a **track listing** — an ordered list of tracks that clients can display as a fallback. It SHOULD use Markdown and include one track per line in playlist order:\n\n```\n# Playlist Title\n\nArtist One - Track Title\nArtist Two - Track Title\n\nN tracks\n```\n\nFor playlists using only `a` tags (Nostr-native), `.content` MAY be omitted since tracks resolve directly via relays.\n\nFor playlist **descriptions**, use the `alt` and/or `description` tags — not `.content`.\n\n### Tags\n\n**Required:**\n- `d` - Unique identifier for this playlist\n- `title` - Playlist title\n- `alt` - Short human-readable description (NIP-31) — used for accessibility and link previews\n\n**Optional:**\n- `description` - Longer human-readable description of the playlist\n- `image` - URL to playlist artwork\n- `a` - Track references in Nostr-native format `36787:<pubkey>:<d-tag>` (multiple, ordered)\n- `i` - Track and feed references using [Podcasting 2.0](https://github.com/Podcastindex-org/podcast-namespace) GUIDs (multiple, ordered — see [Podcast Index Track References](#podcast-index-track-references))\n- `t` - Category tags for discovery\n- `type` - Playlist type (see [Playlist Types](#playlist-types))\n- `role` - Playlist role (see [Playlist Roles](#playlist-roles))\n\n### Example (Nostr-native `a` tags only)\n\n```json\n{\n  \"kind\": 34139,\n  \"content\": \"Survival Guide - January Shock\\nJune & The Jets - You Sure Did\\nBear's Snare - Ocean Breeze\\n\\n3 tracks\",\n  \"tags\": [\n    [\"d\", \"summer-vibes-2024\"],\n    [\"title\", \"Summer Vibes 2024\"],\n    [\"alt\", \"Playlist: Summer Vibes 2024\"],\n    [\"description\", \"A hand-picked selection of chill electronic tracks from Summer 2024, curated for late-night listening.\"],\n    [\"t\", \"playlist\"],\n    [\"t\", \"music\"],\n    [\"type\", \"ep\"],\n    [\"role\", \"release\"],\n    [\"image\", \"https://cdn.blossom.example/img/playlist.jpg\"],\n    [\"a\", \"36787:abc123...:january-shock\"],\n    [\"a\", \"36787:def456...:you-sure-did\"],\n    [\"a\", \"36787:abc123...:ocean-breeze\"]\n  ]\n}\n```\n\n### Example (both `a` and `i` tags)\n\n```json\n{\n  \"kind\": 34139,\n  \"content\": \"# Summer Vibes 2024\\n\\nSurvival Guide - January Shock\\nJune & The Jets - You Sure Did\\nBear's Snare - Ocean Breeze\\n\\n3 tracks\",\n  \"tags\": [\n    [\"d\", \"summer-vibes-2024\"],\n    [\"title\", \"Summer Vibes 2024\"],\n    [\"alt\", \"Music playlist: Summer Vibes 2024\"],\n    [\"description\", \"A hand-picked selection of chill electronic tracks from Summer 2024, curated for late-night listening.\"],\n    [\"t\", \"playlist\"],\n    [\"t\", \"music\"],\n    [\"type\", \"ep\"],\n    [\"role\", \"release\"],\n    [\"image\", \"https://cdn.blossom.example/img/playlist.jpg\"],\n    [\"a\", \"36787:abc123...:january-shock\"],\n    [\"a\", \"36787:def456...:you-sure-did\"],\n    [\"a\", \"36787:abc123...:ocean-breeze\"],\n    [\"i\", \"podcast:item:guid:a1b2c3d4-e5f6-7890-abcd-ef1234567890\"],\n    [\"i\", \"podcast:item:guid:b2c3d4e5-f6a7-8901-bcde-f12345678901\"],\n    [\"i\", \"podcast:item:guid:c3d4e5f6-a7b8-9012-cdef-012345678902\"],\n    [\"i\", \"podcast:guid:d7b4abee-1234-5678-9abc-def012345678\"],\n    [\"i\", \"podcast:guid:e8c5bcff-2345-6789-abcd-ef0123456789\"]\n  ]\n}\n```\n\n## Nostr-native Track References\n\nPlaylists may reference music tracks using `a` tags in the format:\n\n```\n[\"a\", \"36787:<pubkey>:<d-tag>\"]\n```\n\nWhere:\n- `36787` is the Music Track event kind (see [Music Tracks](NIP-Music-Tracks.md))\n- `<pubkey>` is the track author's public key (hex)\n- `<d-tag>` is the track's unique identifier\n\n`a` tags resolve by querying Nostr relays for the referenced track events.\n\n## Podcast Index Track References\n\nPlaylists may additionally reference music tracks using `i` tags with prefixes from the [Podcasting 2.0 namespace](https://github.com/Podcastindex-org/podcast-namespace):\n\n```\n[\"i\", \"podcast:item:guid:<itemGuid>\"]\n[\"i\", \"podcast:guid:<feedGuid>\"]\n```\n\nWhere:\n- `podcast:item:guid` references a specific track by its RSS [`<guid>`](https://www.rssboard.org/rss-specification#hrelementsOfLtitemgt) element value. One tag per track, in playlist order.\n- `podcast:guid` references the feed containing one or more tracks, by the feed's [`<podcast:guid>`](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#guid) element value. Emitters SHOULD NOT emit duplicate `podcast:guid` tags for the same feed.\n\nItem GUID tags SHOULD appear in playlist order. Feed GUID tags MAY appear in any order.\n\nThe mapping between items and feeds is implicit. Clients resolve all feed GUIDs, parse each feed, and search across them to match each item GUID.\n\n### Resolution\n\nClients resolve `i` tags by:\n\n1. Collecting `podcast:guid` values from `i` tags\n2. Looking up feed URLs via a podcast catalogue (e.g., the Podcast Index API [`podcasts/byguid`](https://podcastindex-org.github.io/docs-api/#get-/podcasts/byguid) endpoint, or any compatible directory)\n3. Fetching and parsing each feed's RSS XML\n4. Matching each `podcast:item:guid` across all parsed feeds\n5. Falling back to the `.content` track listing for unresolved items\n\n## Playlist Types\n\nThe `type` tag describes the release format:\n\n- `album` - A full-length studio album\n- `ep` - An extended play (typically 4-6 tracks)\n- `single` - A single track release\n- `compilation` - A collection of tracks from various sources/artists\n- `live` - A live recording/performance\n- `remix` - A remix album\n- `soundtrack` - Music from a film, game, or other media\n- `mixtape` - A user-curated or DJ mix\n- `demo` - A demo recording\n\nClients MAY use additional values. Unknown values SHOULD be displayed as-is.\n\n## Playlist Roles\n\nThe `role` tag describes the playlist's role in the ecosystem:\n\n- `release` - An artist's official release (album, EP, single, etc.) (default)\n- `curated` - A public themed playlist assembled by a single curator\n- `personal` - A user's private collection, useful for organizing tracks\n- `collaborative` - A community playlist editable by multiple contributors\n\nWhen `role` is omitted, `release` is assumed.\n\n## Implementation Notes\n\n- Playlists are updatable (addressable events — re-publishing with the same `d` tag replaces the previous version)\n- `a` and `i` tag order are both canonical; clients SHOULD preserve track order when displaying\n- Clients SHOULD handle missing/deleted tracks gracefully — show a placeholder from `.content` or skip\n- When a feed cannot be resolved (dead URL, removed from index), clients MAY show the corresponding `.content` line instead\n- When a referenced track is not found via `a` tags, clients MAY fall back to `i` tag resolution or skip\n- Use `naddr` identifiers to link to playlists\n- Playlists support NIP-25 reactions and NIP-22 comments","sig":"8e7d3e36d6c3fea3eb1909cb8ef7ec8814b87b8db2ba951ef6983cc395aaa5d85bfae300c6ead246c085de3cda977d4e5cba1d68c8ab2324d17b3ab08b78d543"}