duncan3dc / sonos

A PHP library for interacting with Sonos speakers
Apache License 2.0
107 stars 33 forks source link

Accessing Sonos Favorites #50

Open Nibbler73 opened 8 years ago

Nibbler73 commented 8 years ago

Hello,

I'm experimenting by building a simple and child-proof controller for the sonos box. Based on a RPi with the Touchscreen and as I'm developing this in PHP, your sonos library seems to be the awesome tool to talk to Sonos.

I'd like to list the Sonos Favorites in a cover-flow style and start playing the selected Spotify/Radio/Whatever if tapped on.

I can fetch the Playlists from the Network which works great including the Album-Art.

Is there a way to fetch the Sonos Favorites, as only these are able to include Radio Stations (that is, internet radio, Spotify Radio, Amazon Prime Radio, ...).

Thanks

duncan3dc commented 8 years ago

Hi, the library doesn't support Sonos Favourites at the moment. I'll take a look at adding this when I get some time

duncan3dc commented 8 years ago

I've done a bit of work on this (on the favourites branch), but as these lists can contain tracks, streams, and collections of tracks. It's not simple to handle them, eg:

$controller = $sonos->getControllerByRoom("The Hall");
$queue = $controller->getQueue();

$favourites = new Favourites($controller);

foreach ($favourites->getTracks() as $track) {
    if (isset($track->title)) {
        echo $track->title . "\n";
    } else {
        echo $track->getName() . "\n";
    }

    if ($track instanceof Stream) {
        echo "\tIgnoring Stream\n";
        continue;
    }

    if ($track instanceof \Countable) {
        if (count($track) < 1) {
            echo "\tIgnoring empty countable\n";
            continue;
        }
    }

    $queue->addTrack($track);
}

I'll continue to think about it, but it might require changing some existing classes and therefore need to be part of 2.0

Nibbler73 commented 8 years ago

This looks promising, thanks.

From the Controller-App on mobile devices, I would assume the Collection or Countable returned is most probably a Playlist from the Favourites.

Should fetching the favorites work like this?

$favourites = $sonos->getFavourites();

I'll give it a try and see if I can start working with the new Favourites() object.

Thanks for the quick reaction.

duncan3dc commented 8 years ago

Yeh a countable would be a playlist, it could be a Spotify playlist, or a Sonos playlist, or anything else though. I've not added a $sonos->getFavourites() method yet, but that would probably make it in once I've figured everything else out

zoic21 commented 6 years ago

Hi, I test this on 2.0 version. I can get list of favourites but I can't find how to add it in queue...

kvanisterdael commented 5 years ago

I have done this from my code with the following methods and a SonosFavorite class. Maybe this can help...

class SonosFavorite implements UriInterface 
{
    protected $title;    
    protected $uri;    
    protected $metadata;

    public function __construct($title, $uri, $metadata) {
        $this->title = $title;
        $this->uri = $uri;
        $this->metadata = $metadata;
    }

    public function getTitle() {
        return $this->title;
    }

    public function getUri(): string {
        return $this->uri;
    }

    public function getMetaData(): string {
        return $this->metadata;
    }

    public function isRadio() {
        return (strpos($this->uri, 'x-sonosapi-stream:') !== false
            || strpos($this->uri, 'x-sonosapi-radio:') !== false
            || strpos($this->uri, 'pndrradio:') !== false
            || strpos($this->uri, 'x-sonosapi-hls:') !== false
            || strpos($this->uri, 'x-sonosprog-http:') !== false
            || strpos($this->uri, 'x-rincon-mp3radio:') !== false);
    }

    public function isLineIn() {
        return (strpos($this->uri, 'x-rincon-stream:') !== false 
            || strpos($this->uri, 'x-sonos-htastream:') !== false);
    }
}
private function getFavorites() 
    {
        // Get favorites
        if ($this->setController())
        {
            $data = $this->controller->soap("ContentDirectory", "Browse", [
                "ObjectID"          =>  "FV:2",
                "BrowseFlag"        =>  "BrowseDirectChildren",
                "Filter"            =>  "*",
                "StartingIndex"     =>  0,
                "RequestedCount"    =>  100,
                "SortCriteria"      =>  "",
            ]);
            $parser = new XmlParser($data["Result"]);
            $favorites = [];
            foreach ($parser->getTags("item") as $item) {
                $id = $item->getAttribute("id");
                $title = $item->getTag("title")->nodeValue;
                $uri = $item->getTag("res")->nodeValue;
                $metadata = $item->getTag("resMD")->nodeValue;
                $fav = new SonosFavorite($title, $uri, $metadata);
                $favorites[] = $fav;
            }
            return $favorites;
        }
        return array();
    }

private function playFavorite($fav) 
    {
        if ($this->setController())
        {
            if ($fav->isRadio() || $fav->isLineIn()) {
                // Playing radio station favorite works!
                $stream = new Stream($fav->getUri());
                $this->controller->useStream($stream)->play();
            }
            else {
                $queue = $this->controller->getQueue();
                $queue->clear();
                $this->controller->soap("AVTransport", "AddURIToQueue", [
                    "InstanceID"                        =>  0,
                    "EnqueuedURI"                       =>  $fav->getUri(),
                    "EnqueuedURIMetaData"               =>  $fav->getMetaData(),
                    "DesiredFirstTrackNumberEnqueued"   =>  0,
                    "EnqueueAsNext"                     =>  0,
                ]);                
                $this->controller->useQueue();
                $this->controller->setShuffle(true); // Always play lists in Shuffle mode
                $this->controller->play();
            }
        }
    }