nips/XX.md

5.9 KiB

NIP-XX

Internet Radio

draft optional

This NIP defines a standard for publishing and discovering internet radio stations on Nostr, enabling decentralized radio station directories, streaming metadata, social features, and interoperability between radio applications.

Rationale

Traditional internet radio directories are centralized services controlled by single entities, making them vulnerable to censorship, takedowns, and service discontinuation. Radio stations and listeners lack data portability between different platforms and applications.

This specification leverages Nostr's decentralized infrastructure to create an open, censorship-resistant radio ecosystem where:

  • Radio stations can publish their metadata and streaming information directly
  • Users maintain portable favorites lists across applications
  • Developers can build interoperable radio clients without vendor lock-in
  • Communities can engage around stations through comments and live chat
  • Discovery happens through the network rather than centralized algorithms
  • Organic, decentralized maintenance of station entries helps keep quality high and mitigate broken links
  • Direct listener-to-station monetization becomes possible through Nostr's native micropayment capabilities

Overview

This specification defines radio station events and describes how existing Nostr protocols can be used to create a complete radio ecosystem:

  • Radio Station Events (kind:31237) - Station metadata and streaming information (defined in this NIP)
  • Live Chat Messages - Real-time chat during streaming using existing live chat protocols
  • Station Comments - Persistent discussion threads using existing comment protocols
  • Favorites Management - Personal and curated station collections using NIP-78
  • Application Discovery - Handler registration using NIP-89

Radio Station Events

Radio stations are published as addressable events of kind:31237. These events contain streaming URLs, metadata, and technical specifications needed for playback.

Event Structure

{
  "kind": 31237,
  "content": "<JSON metadata>",
  "tags": [
    ["d", "<station-identifier>"],
    ["name", "<station-name>"],
    ["t", "<genre>"],
    ["l", "<ISO-639-1-code>"],
    ["countryCode", "<ISO-3166-2-code>"],
    ["location", "<human-readable-location>"],
    ["thumbnail", "<image-url>"],
    ["website", "<station-website>"]
  ]
}

Content Format

The content field MUST contain a JSON string with the following structure:

{
  "description": "Station description with **markdown** support",
  "streams": [
    {
      "url": "https://stream.example.com/radio.mp3",
      "format": "audio/mpeg",
      "quality": {
        "bitrate": 128000,
        "codec": "mp3",
        "sampleRate": 44100
      },
      "primary": true
    }
  ],
  "streamingServerUrl": "https://server.example.com"
}

Required Fields

  • description: Human-readable description (markdown supported)
  • streams: Array of stream objects (minimum one required)

Optional Fields

  • streamingServerUrl: Base URL of the streaming server (needed for metadata endpoints when using typical streaming servers like Icecast that provide /status-json.xsl, /admin/stats, or similar metadata APIs)

Stream Object

Each stream object MUST include:

  • url: Direct URL to the audio stream
  • format: MIME type (e.g., "audio/mpeg", "audio/aac")
  • quality: Object with bitrate, codec, and sampleRate
  • primary: Boolean indicating the default stream (optional)

Required Tags

  • d: Unique identifier for the station (used for addressability)
  • name: Human-readable station name
  • t: Genre/category tags (multiple allowed)
  • l: ISO 639-1 language codes (multiple allowed)
  • countryCode: ISO 3166-2 country code
  • location: Human-readable location string
  • thumbnail: Station logo/image URL
  • website: Station's official website

Example

{
  "kind": 31237,
  "pubkey": "a1b2c3d4...",
  "created_at": 1690000000,
  "content": "{\"description\":\"Eclectic mix of jazz, world music, and electronic sounds from France.\",\"streams\":[{\"url\":\"https://icecast.radiofrance.fr/fiprock-hifi.aac\",\"format\":\"audio/aac\",\"quality\":{\"bitrate\":128000,\"codec\":\"aac\",\"sampleRate\":44100},\"primary\":true}]}",
  "tags": [
    ["d", "a7f9d2e1b8c3"],
    ["name", "FIP Radio"],
    ["t", "jazz"],
    ["t", "world"],
    ["t", "electronic"],
    ["l", "fr"],
    ["countryCode", "FR"],
    ["location", "Paris, France"],
    ["thumbnail", "https://example.com/fip-logo.png"],
    ["website", "https://www.radiofrance.fr/fip"]
  ]
}

Social Features

Radio stations can leverage existing Nostr social protocols:

Live Chat Messages

Real-time chat during radio streaming uses existing live chat protocols such as NIP-53. Messages should reference the radio station via a tag to associate chat with the specific station.

Station Comments

Persistent discussion threads use existing comment protocols such as NIP-22. Comments should reference the radio station event to create threaded discussions about stations.

Implementation Notes

Station Identification

  • The d tag value SHOULD be a random identifier to ensure stability over time
  • If no d tag is present, it defaults to an empty string
  • Multiple stations can be published by the same pubkey using different d tags

Streaming Compatibility

  • Support multiple stream formats for broader client compatibility
  • Include quality metadata to enable adaptive streaming
  • Mark one stream as primary for default selection

Content Indexing

  • Use t tags for genre/category filtering
  • Include l tags for internationalization
  • Add location for geographical discovery

Reference Implementation

A reference implementation is available at WaveFunc, demonstrating station publishing, favorites management, and social features.