joeyboli/radioapisdk

Modern PHP SDK for RadioAPI - retrieve radio stream metadata, search music tracks, and analyze image colors

Installs: 14

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/joeyboli/radioapisdk

1.0.10 2026-02-04 13:25 UTC

This package is auto-updated.

Last update: 2026-02-04 13:27:21 UTC


README

A clean, modern PHP client for RadioAPI that makes working with radio streams and music data feel effortless. Get real-time stream metadata, search across all the major streaming platforms, and even pull color palettes from album artwork—all with an API that just works.

PHP Version License

What You Can Do

  • Stream Metadata - Pull current track info from any radio stream
  • Music Search - Find tracks across Spotify, Deezer, Apple Music, and more
  • Color Extraction - Grab dominant colors from album art for theming
  • Fluent API - Chain methods together naturally
  • Type Safe - Full PHP 8.1+ type declarations throughout
  • Zero Config - Just install and go
  • Built on ElliePHP - Powered by the rock-solid ElliePHP HttpClient

What You'll Need

  • PHP 8.1 or newer
  • ElliePHP HttpClient

Getting Started

Pop this into your terminal:

composer require joeyboli/radioapisdk

Quick Example

Here's the basic idea:

<?php

use RadioAPI\RadioAPI;

// Set up your client
$api = RadioAPI::make('https://api.radioapi.io', 'your-api-key')
    ->language('en')
    ->service(RadioAPI::SPOTIFY)
    ->withHistory();

// Find out what's playing on a stream
$stream = $api->getStreamTitle('https://stream.example.com/radio');

if ($stream->isSuccess()) {
    $track = $stream->getCurrentTrack();
    echo "{$track['artist']} - {$track['song']}\n";
    
    // Check what played before
    foreach ($stream->getHistory() as $historical) {
        echo "Previously: {$historical['artist']} - {$historical['song']}\n";
    }
}

// Search for a specific track
$search = $api->searchMusic('The Beatles - Hey Jude');

if ($search->hasResults()) {
    $firstTrack = $search->getFirstTrack();
    echo "Found: {$firstTrack['artist']} - {$firstTrack['title']}\n";
}

// Pull colors from album artwork
$colors = $api->getImageColors('https://example.com/album-art.jpg');

if ($colors->isSuccess()) {
    echo "Dominant color: " . $colors->getDominantColorHex() . "\n";
    echo "Text color: " . $colors->getTextColorHex() . "\n";
}

Setting Things Up

The Basics

You've got a couple ways to create your client:

use RadioAPI\RadioAPI;

// Simple version
$api = new RadioAPI('https://api.radioapi.io', 'your-api-key');

// Or chain some config options
$api = RadioAPI::make('https://api.radioapi.io', 'your-api-key')
    ->language('en')           // Response language
    ->service(RadioAPI::SPOTIFY)  // Default service
    ->withHistory()            // Include track history
    ->timeout(60);             // Request timeout in seconds

Configuration Options

Here's what you can tweak:

Method What It Does Default
language(string $code) Set language for responses (ISO 639-1) 'en'
service(string $service) Choose your default platform null
withHistory(bool $enabled) Turn track history on or off true
withoutHistory() Quick way to disable history -
timeout(int $seconds) How long to wait for responses 30

Available Services

Music streaming platforms you can use:

RadioAPI::SPOTIFY         // Spotify
RadioAPI::DEEZER          // Deezer
RadioAPI::APPLE_MUSIC     // Apple Music / iTunes
RadioAPI::YOUTUBE_MUSIC   // YouTube Music
RadioAPI::FLO_MUSIC       // FLO Music
RadioAPI::LINE_MUSIC      // LINE Music
RadioAPI::KKBOX_MUSIC     // KKBOX
RadioAPI::AUTO            // Let the API figure it out

Radio platforms:

RadioAPI::AZURACAST       // AzuraCast
RadioAPI::LIVE365         // Live365
RadioAPI::RADIOKING       // RadioKing

What You Can Do With It

Get Stream Info

Pull the current track from any radio stream:

public function getStreamTitle(
    string $streamUrl,
    ?string $service = null,
    ?bool $withHistory = null
): StreamTitleResponse

What you pass in:

  • $streamUrl - The radio stream URL
  • $service - Optional: override the default service
  • $withHistory - Optional: override history setting

Examples:

// Basic usage
$response = $api->getStreamTitle('https://stream.example.com/radio');

// Force a specific service
$response = $api->getStreamTitle(
    'https://stream.example.com/radio',
    RadioAPI::SPOTIFY
);

// Skip history for this one request
$response = $api->getStreamTitle(
    'https://stream.example.com/radio',
    null,
    false
);

Search for Music

Find tracks across streaming services:

public function searchMusic(
    string $query,
    ?string $service = null,
    ?string $language = null
): MusicSearchResponse

What you pass in:

  • $query - What you're looking for
  • $service - Optional: which platform to search
  • $language - Optional: override the language

Examples:

// Just search
$response = $api->searchMusic('The Beatles - Hey Jude');

// Search on Spotify specifically
$response = $api->searchMusic(
    'The Beatles - Hey Jude',
    RadioAPI::SPOTIFY
);

// Search in French
$response = $api->searchMusic(
    'The Beatles - Hey Jude',
    RadioAPI::SPOTIFY,
    'fr'
);

Extract Colors from Images

Pull color palettes from any image URL:

public function getImageColors(string $imageUrl): ColorResponse

What you pass in:

  • $imageUrl - The image URL to analyze

Example:

$response = $api->getImageColors('https://example.com/album-art.jpg');

if ($response->isSuccess()) {
    $hex = $response->getDominantColorHex();
    $rgb = $response->getDominantColorRgb();
    $css = $response->getDominantColorCss();
}

Working with Responses

StreamTitleResponse

Everything you need from stream metadata:

// Check if it worked
$response->isSuccess(): bool
$response->getError(): ?string

// Current track info
$response->getCurrentTrack(): ?array
$response->getArtist(): ?string
$response->getTitle(): ?string
$response->getAlbum(): ?string

// History stuff
$response->getHistory(): array
$response->hasHistory(): bool
$response->getHistoryCount(): int
$response->getLastTrack(): ?array

// Stream details
$response->getStreamInfo(): array

// Raw data if you need it
$response->getRawData(): array

Real example:

$response = $api->getStreamTitle('https://stream.example.com/radio');

if ($response->isSuccess()) {
    // Get the details
    $artist = $response->getArtist();
    $title = $response->getTitle();
    $album = $response->getAlbum();
    
    echo "$artist - $title";
    if ($album) {
        echo " (from $album)";
    }
    
    // Check what played before
    if ($response->hasHistory()) {
        echo "\nPreviously played:\n";
        foreach ($response->getHistory() as $track) {
            echo "- {$track['artist']} - {$track['song']}\n";
        }
    }
} else {
    echo "Error: " . $response->getError();
}

MusicSearchResponse

All your search result tools:

// Status checks
$response->isSuccess(): bool
$response->getError(): ?string
$response->hasResults(): bool

// Get tracks
$response->getTracks(): array
$response->getFirstTrack(): ?array
$response->getResultCount(): int

// Filter and transform results
$response->getTracksByService(string $service): array
$response->filter(callable $callback): array
$response->map(callable $callback): array

// Info about the search
$response->getQuery(): ?string
$response->getService(): ?string

// Raw data
$response->getRawData(): array

Real example:

$response = $api->searchMusic('The Beatles - Hey Jude');

if ($response->hasResults()) {
    // Loop through everything
    foreach ($response->getTracks() as $track) {
        echo "{$track['artist']} - {$track['title']}\n";
    }
    
    // Just grab the first one
    $first = $response->getFirstTrack();
    
    // Filter for explicit tracks
    $explicit = $response->filter(fn($t) => $t['explicit'] ?? false);
    
    // Simplify the data
    $simplified = $response->map(fn($t) => [
        'artist' => $t['artist'],
        'title' => $t['title']
    ]);
    
    // Get results from just Spotify
    $spotifyTracks = $response->getTracksByService('spotify');
}

ColorResponse

Color extraction made easy:

// Success check
$response->isSuccess(): bool
$response->getError(): ?string

// Hex format
$response->getDominantColorHex(): ?string
$response->getTextColorHex(): ?string

// RGB format
$response->getDominantColorRgb(): ?array
$response->getTextColorRgb(): ?array

// CSS format
$response->getDominantColorCss(): ?string
$response->getTextColorCss(): ?string

// Flutter format (if you need it)
$response->getDominantColorFlutterHex(): ?string
$response->getTextColorFlutterHex(): ?string

// Full color palette
$response->getPalette(): array
$response->getPaletteColor(int $index): ?array
$response->getPaletteCount(): int

// Raw data
$response->getRawData(): array

Real example:

$response = $api->getImageColors('https://example.com/album-art.jpg');

if ($response->isSuccess()) {
    // Get colors in whatever format you need
    $hex = $response->getDominantColorHex();        // "#FF5733"
    $rgb = $response->getDominantColorRgb();        // ['r' => 255, 'g' => 87, 'b' => 51]
    $css = $response->getDominantColorCss();        // "rgb(255, 87, 51)"
    $flutter = $response->getDominantColorFlutterHex(); // "0xFFFF5733"
    
    // Use it in your HTML
    echo "<div style='background-color: $css;'>";
    echo "  <span style='color: " . $response->getTextColorCss() . ";'>";
    echo "    Content with good contrast";
    echo "  </span>";
    echo "</div>";
    
    // Or grab the whole palette
    $palette = $response->getPalette();
    foreach ($palette as $color) {
        echo "Color: {$color['hex']}\n";
    }
}

When Things Go Wrong

RadioAPIException

All errors come through as a RadioAPIException with helpful info:

use RadioAPI\Exceptions\RadioAPIException;

try {
    $response = $api->getStreamTitle('https://invalid-url.example.com');
} catch (RadioAPIException $e) {
    // Get the details
    echo $e->getMessage();              // Simple error message
    echo $e->getStatusCode();           // HTTP status code
    echo $e->getDetailedMessage();      // Full context
    
    // Figure out what went wrong
    if ($e->isClientError()) {
        // 4xx - something wrong with your request
    }
    if ($e->isServerError()) {
        // 5xx - API server issue
    }
    if ($e->isNetworkError()) {
        // Connection problems
    }
    
    // Check specific issues
    if ($e->isUnauthorized()) {
        // 401 - bad API key
    }
    if ($e->isRateLimited()) {
        // 429 - slow down
    }
    if ($e->isNotFound()) {
        // 404 - doesn't exist
    }
    
    // Dig deeper if needed
    $errorData = $e->getErrorData();    // API response
    $context = $e->getContext();        // Request context
    
    // Check specific error fields
    if ($e->hasErrorField('detail')) {
        $detail = $e->getErrorField('detail');
    }
}

Exception Methods

Method What It Does Returns
getMessage() Basic error message string
getStatusCode() HTTP status code int
getDetailedMessage() Full details with context string
getErrorData() API error response array
getContext() Request context array
isClientError() Check for 4xx errors bool
isServerError() Check for 5xx errors bool
isNetworkError() Check for connection issues bool
isUnauthorized() Check for 401 bool
isForbidden() Check for 403 bool
isNotFound() Check for 404 bool
isRateLimited() Check for 429 bool
hasErrorField(string) Check if error field exists bool
getErrorField(string, mixed) Get error field value mixed

Real-World Examples

Radio Station Monitor

<?php

use RadioAPI\RadioAPI;
use RadioAPI\Exceptions\RadioAPIException;

$api = RadioAPI::make('https://api.radioapi.io', 'your-api-key')
    ->language('en')
    ->service(RadioAPI::SPOTIFY)
    ->withHistory();

$streamUrl = 'https://stream.example.com/radio';

try {
    $response = $api->getStreamTitle($streamUrl);
    
    if ($response->isSuccess()) {
        $track = $response->getCurrentTrack();
        
        echo "Now Playing:\n";
        echo "Artist: {$track['artist']}\n";
        echo "Song: {$track['song']}\n";
        
        if (isset($track['album'])) {
            echo "Album: {$track['album']}\n";
        }
        
        if ($response->hasHistory()) {
            echo "\nRecently Played:\n";
            foreach ($response->getHistory() as $idx => $historical) {
                echo ($idx + 1) . ". {$historical['artist']} - {$historical['song']}\n";
            }
        }
    }
} catch (RadioAPIException $e) {
    echo "Error: " . $e->getMessage() . "\n";
    
    if ($e->isRateLimited()) {
        echo "Rate limit exceeded. Please try again later.\n";
    }
}

Search Across Multiple Platforms

<?php

use RadioAPI\RadioAPI;

$api = RadioAPI::make('https://api.radioapi.io', 'your-api-key');

$query = 'Billie Eilish - bad guy';
$services = [
    RadioAPI::SPOTIFY,
    RadioAPI::DEEZER,
    RadioAPI::APPLE_MUSIC,
];

foreach ($services as $service) {
    $response = $api->searchMusic($query, $service);
    
    if ($response->hasResults()) {
        $track = $response->getFirstTrack();
        echo "Found on " . ucfirst($service) . ":\n";
        echo "  {$track['artist']} - {$track['title']}\n";
        if (isset($track['url'])) {
            echo "  URL: {$track['url']}\n";
        }
        echo "\n";
    }
}

Dynamic Album Art Theming

<?php

use RadioAPI\RadioAPI;

$api = RadioAPI::make('https://api.radioapi.io', 'your-api-key');

$albumArtUrl = 'https://example.com/album-art.jpg';
$response = $api->getImageColors($albumArtUrl);

if ($response->isSuccess()) {
    $bgColor = $response->getDominantColorCss();
    $textColor = $response->getTextColorCss();
    
    echo "<!DOCTYPE html>\n";
    echo "<html>\n";
    echo "<head>\n";
    echo "  <style>\n";
    echo "    body {\n";
    echo "      background-color: $bgColor;\n";
    echo "      color: $textColor;\n";
    echo "    }\n";
    echo "  </style>\n";
    echo "</head>\n";
    echo "<body>\n";
    echo "  <h1>Album Theme</h1>\n";
    echo "  <p>This page is themed with colors from the album art.</p>\n";
    
    // Show the whole palette
    echo "  <h2>Color Palette:</h2>\n";
    echo "  <div style='display: flex; gap: 10px;'>\n";
    foreach ($response->getPalette() as $color) {
        $hex = $color['hex'] ?? '#000';
        echo "    <div style='width: 50px; height: 50px; background-color: $hex;'></div>\n";
    }
    echo "  </div>\n";
    echo "</body>\n";
    echo "</html>\n";
}

Complete Workflow

<?php

use RadioAPI\RadioAPI;
use RadioAPI\Exceptions\RadioAPIException;

// Set up your client
$api = RadioAPI::make('https://api.radioapi.io', 'your-api-key')
    ->language('en')
    ->service(RadioAPI::SPOTIFY)
    ->timeout(60);

try {
    // Step 1: Get what's currently playing
    $streamUrl = 'https://stream.example.com/radio';
    $stream = $api->getStreamTitle($streamUrl);
    
    if ($stream->isSuccess()) {
        $currentTrack = $stream->getCurrentTrack();
        $artist = $currentTrack['artist'];
        $song = $currentTrack['song'];
        
        echo "Now Playing: $artist - $song\n\n";
        
        // Step 2: Search for more details about the track
        $search = $api->searchMusic("$artist - $song");
        
        if ($search->hasResults()) {
            $trackDetails = $search->getFirstTrack();
            
            // Step 3: Pull colors from the album art
            if (isset($trackDetails['artwork'])) {
                $colors = $api->getImageColors($trackDetails['artwork']);
                
                if ($colors->isSuccess()) {
                    echo "Album Colors:\n";
                    echo "- Dominant: " . $colors->getDominantColorHex() . "\n";
                    echo "- Text: " . $colors->getTextColorHex() . "\n";
                }
            }
            
            // Show everything
            echo "\nTrack Details:\n";
            echo "Artist: " . ($trackDetails['artist'] ?? 'N/A') . "\n";
            echo "Title: " . ($trackDetails['title'] ?? 'N/A') . "\n";
            echo "Album: " . ($trackDetails['album'] ?? 'N/A') . "\n";
            echo "Year: " . ($trackDetails['year'] ?? 'N/A') . "\n";
            
            if (isset($trackDetails['url'])) {
                echo "Listen: {$trackDetails['url']}\n";
            }
        }
    }
} catch (RadioAPIException $e) {
    echo "Error: " . $e->getMessage() . "\n";
    
    // Handle specific problems
    if ($e->isUnauthorized()) {
        echo "Please check your API key.\n";
    } elseif ($e->isRateLimited()) {
        echo "Rate limit exceeded. Please wait before retrying.\n";
    } elseif ($e->isServerError()) {
        echo "API server error. Please try again later.\n";
    }
}

Tips and Best Practices

Reuse Your Client

Don't create a new client for every request—that's wasteful:

// Do this ✓
$api = RadioAPI::make('https://api.radioapi.io', 'key');
$stream1 = $api->getStreamTitle($url1);
$stream2 = $api->getStreamTitle($url2);

// Not this ✗
$stream1 = RadioAPI::make('https://api.radioapi.io', 'key')->getStreamTitle($url1);
$stream2 = RadioAPI::make('https://api.radioapi.io', 'key')->getStreamTitle($url2);

Always Handle Errors

Things can go wrong, so be ready:

try {
    $response = $api->getStreamTitle($url);
} catch (RadioAPIException $e) {
    // Handle it gracefully
    error_log($e->getDetailedMessage());
}

Check Success Before Using Data

Don't assume everything worked:

$response = $api->getStreamTitle($url);

if ($response->isSuccess()) {
    $track = $response->getCurrentTrack();
    // Now you can safely use the data
} else {
    echo "Error: " . $response->getError();
}

Use the Configuration Chain

It's cleaner and reads better:

$api = RadioAPI::make('https://api.radioapi.io', 'key')
    ->language('fr')
    ->service(RadioAPI::DEEZER)
    ->withHistory()
    ->timeout(45);

Use Helper Methods

They're there for a reason:

// Do this ✓
$artist = $response->getArtist();
$tracks = $response->getTracks();

// Not this ✗
$artist = $response->getRawData()['artist'] ?? null;
$tracks = $response->getRawData()['tracks'] ?? [];

Testing

Run the tests:

composer test

Static analysis:

composer phpstan

Check code style:

composer cs-check

Fix code style:

composer cs-fix

License

MIT licensed

Contributing

Found a bug? Want to add a feature? Pull requests are welcome!

Built With

Changelog

1.0.9 (2026-02-04)

  • Complete rewrite using ElliePHP HttpClient
  • Fluent configuration API
  • Better type safety with PHP 8.1+
  • Improved error handling
  • Comprehensive docs
  • More helpful response methods