PHP Sonos (Klasse zum Ansteuern einzelner Player)

Ich hab das oben korrigiert: Leider liefert GetMediaInfo nur NOT_IMPLEMENTED. :frowning:

Wenn Du in Deinen PHP Skripten die Player durchgehst, wirst Du bei den Slaves einer Zone als Transport URI die Rincon des Coordinators erhalten. Diese Info nutze ich um mir so ein Array mit den aktuellen Gruppen und den Verknüpfungen zu erstellen - kannst Du sicher auch so machen.

Hi Benjamin, nach einigen Änderungen einfach mal den ganzen Beitrag überarbeitet:

Anpassungen in GetPlaylist($value)
interpret wird zu artist
titel wird zu title

Bei GetTransportSettings() folgende Zeile gelöscht:

echo "
===" . $this->address. "====
" . $returnContent . "
===
";

Dann bei GetImportedPlaylist habe ich die Substring wieder eingebaut, da ich im Titel die „.m3u“ nicht haben möchte.
Wenn das bei Dir anders ist, dann sollten wir einen Schalter einbauen.
Die Funktionalitäten wie URI funktionieren natürlich es geht nur um die Anzeige.

Hast Du andere Importierte Playlisten? bei mir sind das name.m3u

Gruß
Ta Lun

PHPSonos.inc_110329.zip (6.68 KB)

OK, habe ich beides bei mir auch korrigiert. Der Interpret heißt jetzt auch Artist, wie in den anderen Funktionen.

Die AudioIn Methoden zum Abfragen der Daten hast Du ja vielleicht schon gesehen?! Hab gerade rausgefunden, man kann unkompliziert den Player zum Abspielen des Line-In Signals bringen mit:


$sonos->SetAVTransportURI("x-rincon-stream:RINCON_000E5832FB5C01400");

… wobei das „-stream“ hier auf den Input leitet. :smiley:

hehe ok das ist echt einfach :wink:

Und da hat sich bei mir der Fehlerteufel eingeschlichen:
In Zeile 1056 habe ich beim Kopieren das $titel nicht angepasst auf $title

// br substring use cuts my playlist names at the 4th char
	$liste[$i]['title'] = substr((string)$title[0],0,-4);

Dann bei GetImportedPlaylist habe ich die Substring wieder eingebaut, da ich im Titel die „.m3u“ nicht haben möchte.
Wenn das bei Dir anders ist, dann sollten wir einen Schalter einbauen.
Die Funktionalitäten wie URI funktionieren natürlich es geht nur um die Anzeige.

Hast Du andere Importierte Playlisten? bei mir sind das name.m3u

Meine PL sind aus Itunes importiert und haben kein .m3u im Namen. Deine Original Routinen haben bei mir den Namen der PL auf 4 Zeichen gekürzt und nicht nur die Endung abgeschnitten - versuche mal folgendes (ca. Zeile 1059):

			$liste[$i]['title'] = (string)$title[0];
								$liste[$i]['title']=preg_replace("/^(.+)\.m3u$/i","\\1",$liste[$i]['title']);

            $liste[$i]['typ'] = "Import";
            $liste[$i]['file'] = (string)$xml->container[$i]->res;


Klappt das?

:smiley: Perfekt So muß es sein.
Das heißt, der schmeißt bei den Itunes Playlisten die Endung weg oder haben die sowas nicht?

Ich bin gerade ein wenig am Überlegen wie ich das Browsen am besten realisieren kann und bin mir nicht klar wie der Filter funktioniert, wenn man z.B. bei Objekt ID A:ARTIST übergibt funktioniert der Filter irgendwie nicht…

Hast Du da ne Idee??

Mit meinem Regex wird nur .m3u weggeworfen. Die Itunes Pl stammen aus einer Datenbank und haben keine Endung im Titel.

Bzgl. Browse:
Eine Übersicht gibts´s hier z.B. Twonky und NAS - Ich werde wahnsinnig! - HTPC - Beisammen.de
… ich bekomme aber auch noch nicht das gewünschte Ergebniss. :mad:
Da wird man vermutlich mal die Spezifikationen lesen müssen oder per Wireshark mitlesen…
Grüße, Benjamin

hehe ja Klar das Dein RegEx nur m3u wegschmeißt mir ging es um Sonos :smiley:

Danke schaue mir den Link mal genauer an.

Ich habe der Klasse die notwendigen Änderungen spendiert um mittels IPS eine Meldung ausgeben zu können und zuverlässig danach eine Queue, PL oder einen Radiosender weiterzuspielen.

Des Weiteren können Radiosender nun benannt werden und einige Funktionen ermöglichen zusätzlich weitere Funktionen oder geben weitere Informationen, welche per SOAP verfügbar sind.

Historie:


- andre added setter functions
- 110108  - br added comments based on UPNP information from devicespy;
also added the function XMLsendPacket to get non filtered answers
- 110120 - br added Set and GetLEDState
- 110202 - br added GetZoneAttributes
- 110202 - br added GetZoneInfo
- 110203 - br added gestposinfo TrackURI (contains x-rincon of the zone master if we are slave)
- 110206 - br added AddMember(Rincon...) and RemoveMember(Rincon...)
- 110207 - br added RamptoVolume
- 110208 - br added calculation of Content-Length to some functions
- 110318 - br fiddled with Playmode (maybe fixed a bug)
- 110318 - br added Get and Set CrossfadeMode
- 110318 - br added SaveQueue
- 110328 - ta lun added GetPlaylist($value)
- 110328 - ta lun added GetImportedPlaylists()
- 110328 - ta lun added GetSonosPlaylists()
- 110328 - ta lun added GetCurrentPlaylist()
- 110328 - br corrected titel to title and other things...
- 110328 - br added optional parameter id to SaveQueue
- 110406 - br edited Seek to accept UPNP Unit parameter as option (sec. arg is Target then)
- 110406 - br edited GetPositionInfo to also reflect UPNP return value names
- 110406 - br fixed non valid soap request in seek()
- 110406 - br added return of CurrentURI and CurrentUriMetaData to GetMediaInfo (Current File or QUEUE)
				This info is needed to restart a queue, pl or radiostation
- 110407 - br consolidated SetRadio, SetQueue and SetAVTransportURI
				two last now also Support MetaData as 2nd parameter; 
SetRadio supports the name of a radiostation as second parameter

Beispiel zur Meldungsausgabe:


$sonos = new PHPSonos("192.168.0.115"); //Sonos ZP IPAdresse

$vol=$sonos->GetVolume();
// br_TTS_Speak($IDMediaPlayer,"Test Meldung",1*1000);
// Ramp down
$sonos->SetVolume($vol/100*40);
IPS_Sleep(100);
$sonos->SetVolume($vol/100*10);

// Saving off information
$oldpi = $sonos->GetPositionInfo();
$oldmi = $sonos->GetMediaInfo();

$oldti = $sonos->GetTransportInfo();

// Setting AVT to IPS-Meldung
$sonos->SetVolume($vol/100*70);
$sonos->SetAVTransportURI("x-file-cifs://touch/IP-Symcon$/media/Meldung.wav");
$sonos->Play();

$sonos->SetVolume($vol);

while ($sonos->GetTransportInfo()==1){	// Wait here for Meldung
	IPS_Sleep(30);
}

$sonos->SetVolume($vol/100*40);

// Setting old queue
$sonos->SetAVTransportURI($oldmi['CurrentURI'],$oldmi['CurrentURIMetaData']);
try {
	// Seek TRack_Nr
	$sonos->Seek("TRACK_NR",$oldpi['Track']);
	// Seek REl_time
	$sonos->Seek("REL_TIME",$oldpi['RelTime']);
} catch (Exception $e) {
	// Seek not posssible -maybe a radiostation?
}
// Only play if old pi is PLAYING
if ($oldti==1) $sonos->Play();
$sonos->RampToVolume("AUTOPLAY_RAMP_TYPE",$vol) . "
";

?>

Ergänzung Zone Groups:
Die Zone Slaves geben die Meldung auch synchron aus und spielen synchron weiter. Das Zusammenfügen aller Zonen zur Group ist im o.g. Code nicht drin, sollte aber prinzipiell machbar sein! Vor dem Zusammenfügen müssten dann die entsprechenden pi/mi/ti Informationen je ZoneGroup weggespeichert werden um danach ohne Unterbrechnung fortsetzen zu können.

(Beta Code für das WF und das Management von SONOS-Zonengruppen ist via Forumsuche zum Stichwort br_ips zu finden)

Grüße, Benjamin

PHPSonos.inc.zip (8.36 KB)

Hi,
bin gerade noch beim schauen, was Du geändert hast aber folgendes ist mir schon aufgefallen:

	if ($arg2=="NONE"){
		$Unit="REL_TIME"; $position=$arg1;
	} else {$Unit=$arg1; $position=$arg2;}

Halte das vorgehen für ein wenig unglücklich. Sollte $arg1 nicht immer position sein und arg2 die weiteren Parameter?

Warum hast Du bei GetPositionInfo() den Bedarf z.B. duration umzubenennen bzw. jetzt zweimal auszugeben?

Ansonsten schaue ich mir das am Wochenende etwas näher an mal sehen was ich da so entdecke :wink:

Der Sinn der Turnübung bei Seek war, das bisheriger Code weiter problemlos funktioniert und gleichzeitig zu ermöglichen als ersten Parameter optional den Parameter Unit zu ermöglichen (welcher ja so in der WSDL-Datei beschrieben ist). Ich versuche die Klasse ein wenig in die Richtung zu bringen, dass sie mehr Richtung der per WDSL/ SOAP propagierten Informationen arbeitet, ohne alte Antworten komplett kaputt zu machen.

GetPositionInfo, sollte jetzt neben duration auch die Duration als TrackDuration ausgeben - ich habe das eigentlich nur eingefügt, da dies der angezeigte Name in meinen offiziellen Intel Utils. zu UPNP ist (wobei ich eine alte Version laufen habe) und ich die Funktion und die Antworten an die eigentlichen UPNP/SOAP Antworten bzw. die Doku. angleichen wollte. Oder hab ich da jetzt was übersehen?

Viele Grüße, Benjamin

Ok ich finde das aber ein wenig merkwürdig.

Seek(„0:01:00“) bzw. Seek(„REL_TIME“,„0:01:00“)
Für mich gehören obtionale Parameter immer nach „hinten“.

Bezüglich der Anpassung der Ausgabe halt eich die Ausgabe von zwei gleichen Informationen aber mit anderen Namen für überflüssig.
Sprich entweder man macht es so, das man die Struktur einmal komplett ändert oder diese gleich läßt. Ob wir uns hier an einen solchen Standard halten müssen weiß ich nicht aber ich bin da eigentlich extrem entspannt.

Es ist aber auf jedenfall gut, das Du da weiter forscht. ich bin z.Zt. immer mal wieder dabei die Informationen für den nächsten Track zu ermitteln, da scheint es aber so zu sein, das sich der Sonos Controller beim Sonos an einem Event anmeldet, der dann direkt die Meldung an einen Prot mit einer url schmeißt.
Da bin ich aber noch am suchen…

Ja, sowas ist nicht schön, aber ich möchte erreichen das die Funktionen der Klasse auch die eigentlichen SOAP Parameter akzeptieren, ohne dabei die Klasse so zu verändern, dass bestehende, auf PHPSonos basierende Skripte nicht mehr laufen.

Es ist aber auf jedenfall gut, das Du da weiter forscht. ich bin z.Zt. immer mal wieder dabei die Informationen für den nächsten Track zu ermitteln, da scheint es aber so zu sein, das sich der Sonos Controller beim Sonos an einem Event anmeldet, der dann direkt die Meldung an einen Prot mit einer url schmeißt.
Da bin ich aber noch am suchen…

Die von Dir gesuchten Infos kommen als Subscribe bei Mediarenderer (AvTransport) als NextTrackURI und als NextTrackMetaData. Die Subscription muss man regelmässig erneuern und ich meine der Port wird dyn. zugewiesen. Diese Info wird man vermutlich nicht per PHP sinnvoll auffangen können, sondern besser mittels C# und einem dauerend laufenden Programm auffangen und dann dem PHP Code zur Verfügung stellen.

Grüße, Benjamin

Hi,
ich bin nun schon ein kleines Stückchen weiter aber komme an einem Punkt nicht klar. Evtl. siehst Du ja wo ich auf dem Schlauch stehe.

Wenn ich mit Fiddler einen Request absetze wie diesen:

SUBSCRIBE http://192.168.0.206:1400/MediaRenderer/AVTransport/Event HTTP/1.1
User-Agent: Fiddler
CALLBACK: <http://192.168.0.2:80/local/sonos/ajax.php>
NT: upnp:event
TIMEOUT: Second-3600

Dann bekomme ich an meine Entwicklungsumgebung eine Antwort mit den Antworten, die wir brauchen.

Nun habe ich mir gedacht, das wäre doch was tolles für die Klasse, sich an einen Event zu hängen aber ich bekomme das nicht so hin, wie ich mir das vorstelle. So sieht das z.Zt aus:

function Subscribe(){
$content='SUBSCRIBE /MediaRenderer/AVTransport/Event HTTP/1.1
HOST: '.$this->address.':1400
CALLBACK: <http://192.168.0.2:80/local/sonos/ajax.php>
NT: upnp:event
TIMEOUT: Second-3600';
$this->sendPacket($content);
}

Wenn ich nun mit $_SESSION[$aktuell][‚sonos‘]->Subscribe(); einen Aufruf mache scheint er keine vernüftige antwort zu bekommen. Diese Seite wird geladen und geladen aber der Request scheint nie anzukommen…
Irgendwelche Ideen?

Der Timeout ist kleiner, wenn das DeviceSpy dieses Abo macht. Ist das die Zeit, nachder ich die Subcription erneuern muss?

Baue mal nen zusätzlichen Linefeed (siehe Leerzeile unten) und die Übermittlung der Content-Length: 0 ein, sonst wartet der Server vermutlich auf Eingaben.

Bei mir klappts so:


/* urn:upnp-org:serviceId:AVTransport */
	function SubscribeMRAVTransport(){
$content='SUBSCRIBE /MediaRenderer/AVTransport/Event HTTP/1.1
HOST: '.$this->address.':1400
CALLBACK: <http://192.168.0.2:80/local/sonos/ajax.php>
NT: upnp:event
TIMEOUT: Second-300
Content-Length: 0

';
$this->sendPacket($content);
}

Die Antwort ist dann:


HTTP/1.1 200 OK
SID: uuid:RINCON_000E5832FB5C01400_sub0000085434
TIMEOUT: Second-300
SERVER: Linux UPnP/1.0 Sonos/14.4-33290 (ZP120)
Connection: close

Das Auffangen der Infos muss dann aber wohl außerhalb der Klasse erfolgen, oder?

Grüße, Benjamin

Super das war es. Super jetzt gehts ans Umsetzen :wink:

Jup das NOTIFY ist dann leider seperat zu lösen.

Ich habe das bisher zum Testen so gemacht und werde mir dann mal Überlegen, wie ich das Ändere:

$datei = fopen("subscribe.txt","a+");
	fwrite($datei,"
Anfgang");
if($_SERVER["REQUEST_METHOD"]=="NOTIFY"){
	$test = file_get_contents('php://input');
	    $returnContent = substr($test, stripos($test, '<'));
        $returnContent = substr($returnContent, 0, strrpos($returnContent, '>') + 4);
       $returnContent = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $returnContent);
	
	fwrite($datei,$returnContent);
}	
	fwrite($datei,"ENDE
");
	fclose($datei);

Cool, das es klappt!
Für IPS müsste das Auffangen wohl über Registervariablen klappen oder eine User Seite. Dann könnte man die Klasse aber nicht mehr so allgemein halten. :frowning:

Schauen wir mal, was sich so entwickelt. :smiley:

Vg, Benjamin

kleiner Zwischenbericht.
Ich habe nun das mit dem Subscribe und dem notify hinbekommen.

Ich setze über die Klasse das Subscribe ab und bekomme über meine aufruf Datei das Notify mit. Dieses schreibe ich dann (vorverarbeitet) in eine Datei, die ich dann bei aktualisierungen abhole.

Einen anderen Weg als über die Datei habe ich nicht gefunden bin aber für jede Schandtat bereit :wink:

Als nächstes will ich das Thema Browse verarbeiten und aktualisiere dann die Infos, für die Klasse…

Wie bereits angekündigt hier meine Änderungen.

Ich fange an mit dem Subscribe und meine Verarbeitung für das Notify.
Grundlegende Information:
1.) Ich übergebe meine Daten an eine php mit 2 möglichen parametern.
1.1) Erster Parameter = „do“ dient dazu in einem switch zu ermitteln was getan werden soll.
1.2) Zweiter parameter = „wert“ dient dazu evtl. Werte zu übergeben.

In der Sonos Klasse folgende neue (schon teilweise bekannte) Funktion:

		function Subscribe($callback){
$content='SUBSCRIBE /MediaRenderer/AVTransport/Event HTTP/1.1
HOST: '.$this->address.':1400
CALLBACK: <'.$callback.'>
NT: upnp:event
TIMEOUT: Second-300
Content-Length: 0

';
$this->sendPacket($content);
} 

Aufruf:

case 'Subscribe':
				$_SESSION[$aktuell]['sonos']->Subscribe("http://".$host.":80/musik/ajax.php?do=notify&wert=".$_SESSION['DeviceNumber']);
	break;

Erläuterung:
Beim Subscribe bekomment man nicht direkt eine Information als Antwort sondern das Sonos System ruft die übergebene URL auf.

Diese verarbeitet das dann bei mir wie folgt:

case 'notify':
	if($_SERVER["REQUEST_METHOD"]=="NOTIFY"){
				$notify = file_get_contents('php://input');
	    	$notify = substr($notify, stripos($notify, '<'));
        $notify = substr($notify, 0, strrpos($notify, '>') + 4);
       	$notify = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $notify);
				$notify = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $notify);
 				$suchen = preg_match('/(.+)<item id="-1" parentID="-1" restricted="true">(.*?)<\/item><\/DIDL-Lite>(.+)"\/><r:EnqueuedTransportURI/is', $notify,$gefundenes_wort);
				$ergebnis = '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/">'.$gefundenes_wort[2].'</DIDL-Lite>';
 				$datei = fopen($wert."_subscribe.txt","w+");
				fwrite($datei,$ergebnis);
				fclose($datei);
	}else{
		$notify = file_get_contents('php://input');
		$datei = fopen($wert."_blub.txt","w+");
				fwrite($datei,$notify);
				fclose($datei);
		
	}
	break;

Ich erstelle also eine Datei pro Sonos System (sind in meiner Session hinterlegt) und schreibe bei diesen Aufruf die Datei x_subscribe.txt.
z.Zt. ist noch das else für Debugzwecke enthalten.
Ich durchlaufe str_replace zweimal, weil es beim ersten mal nicht alle Codes sauber ersetzt.

Das eigentliche Ergebnis befindet sich dann unter folgendem Aufruf:

case 'NextSong':
		if(file_exists($_SESSION['DeviceNumber']."_subscribe.txt")){
			$datei = file_get_contents($_SESSION['DeviceNumber']."_subscribe.txt");
			$xml = new SimpleXMLElement($datei);
			$title = $xml->xpath("dc:title");
			$cover = $xml->xpath("upnp:albumArtURI");
			$album = $xml->xpath("upnp:album");
			$artist = $xml->xpath("dc:creator");
			$file = $xml->res;
			$nextsong['title'] = (string)$title[0];
			$nextsong['albumArtURI'] = "http://".$_SESSION[$aktuell]['IP'].":1400".(string)$cover[0];
			$nextsong['artist'] = (string)$artist[0];
			$nextsong['album'] = (string)$album[0];
			$nextsong['file'] = (string)$file;
    }else{
    	$nextsong['title'] = "NONEXTSONG";
    	$nextsong['albumArtURI'] = "NONEXTSONG";
    	$nextsong['artist'] = "NONEXTSONG";
    	$nextsong['album'] = "NONEXTSONG";
    	$nextsong['file'] = "NONEXTSONG";
    }
		echo json_encode($nextsong);
	break;

Und somit hat man dann die Möglichkeit sich den nächsten Song darstellen zu lassen.

Und nun zum Browse:
Anpassung in der SOnos Klasse:

    //Browse Informationen
     public function Browse($value,$meta="BrowseDirectChildren")
    {

       switch($meta){
       case 'BrowseDirectChildren':
       case 'c':
       case 'child':
      	$meta="BrowseDirectChildren";
       break; 	
       case 'BrowseMetadata':
       case 'm':
       case 'meta':
	       	$meta = "BrowseMetadata";
       break;
       default:
       return false;       	
      } 
        $header='POST /MediaServer/ContentDirectory/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400';
$xml='<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1"><ObjectID>'.htmlspecialchars($value).'</ObjectID><BrowseFlag>'.$meta.'</BrowseFlag><Filter></Filter><StartingIndex>0</StartingIndex><RequestedCount>1000</RequestedCount><SortCriteria></SortCriteria></u:Browse>
</s:Body>
</s:Envelope>';
$content=$header . '
Content-Length: '. strlen($xml) .'

'. $xml;

    $returnContent = $this->sendPacket($content);
    $xmlParser = xml_parser_create();
        $returnContent = substr($returnContent, stripos($returnContent, '<'));
        $returnContent = substr($returnContent, 0, strrpos($returnContent, '>') + 4);
        $returnContent = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $returnContent);

        $xml = new SimpleXMLElement($returnContent);
        $liste = array();
        for($i=0,$size=count($xml);$i<$size;$i++)
        {
            //Wenn Container vorhanden, dann ist es ein Browse Element
            //Wenn Item vorhanden, dann ist es ein Song.
            if(isset($xml->container[$i])){
            		$aktrow = $xml->container[$i];
		            $attr = $xml->container[$i]->attributes();
		            $liste[$i]['typ'] = "container";
             }else if(isset($xml->item[$i])){
            	//Item vorhanden also nur noch Musik
            		$aktrow = $xml->item[$i];
		            $attr = $xml->item[$i]->attributes();
		            $liste[$i]['typ'] = "item";
            }else{
            	//Fehler aufgetreten
            	return;
            }	
            		$id = $attr['id'];
		            $parentid = $attr['parentID'];
		            $albumart = $aktrow->xpath("upnp:albumArtURI");
		            $titel = $aktrow->xpath("dc:title");
		            $interpret = $aktrow->xpath("dc:creator");
		            $album = $aktrow->xpath("upnp:album");
	            	if(isset($aktrow->res)){
	  	          	$res = (string)$aktrow->res;
	  	          	$liste[$i]['res'] = urlencode($res);
		          	}else{
	    	      		$liste[$i]['res'] = "leer";
	      	    	}
	        	    if(isset($albumart[0])){
	                $liste[$i]['albumArtURI']="http://" . $this->address . ":1400".(string)$albumart[0];
	          	  }else{
	                $liste[$i]['albumArtURI'] ="leer";
	            	}
	            	$liste[$i]['title']=(string)$titel[0];
	            	if(isset($interpret[0])){
             		   $liste[$i]['artist']=(string)$interpret[0];
            		}else{
		               $liste[$i]['artist']="leer";
    		        }
		            if(isset($id) && !empty($id)){
		                $liste[$i]['id']=urlencode((string)$id);
		            }else{
		                $liste[$i]['id']="leer";
		            }
		            if(isset($parentid) && !empty($parentid)){
		                $liste[$i]['parentid']=urlencode((string)$parentid);
		            }else{
		                $liste[$i]['parentid']="leer";
		            }
           			if(isset($album[0])){
                	$liste[$i]['album']=(string)$album[0];
            		}else{
                	$liste[$i]['album']="leer";
            		}
            
        }
return $liste;
    }

Hier bin ich nicht so glücklich mit, da ich dieses urlencode notwendig ist, damit dies bei mir sauber läuft. Der Grund sind Zeichen wie z.B. „#“ oder „&“
Ich habe den Meta Aufruf genommen, da man beim Browsen die echte parentid nur durch die Meta Angaben bekommt aber den Inhalt über die Child elemente. Ich unterscheide nach container und item. Items sind die eigentlichen Songs.

Aufruf könnte dann so aussehen:

	case 'BrowseMeta':
			$browseliste = $_SESSION[$aktuell]['sonos']->Browse($wert,"m");
			echo json_encode($browseliste);
	break;

Nun noch ein paar Bilder um mal einen Eindruck zu bekommen.
Ich kenne das IPS System nicht und weiß nicht wieviel sich davon dabei umsetzen läßt.


Also wenn da noch ein paar Anregungen (gerne auch Verbesserungsvorschläge) kommen wäre das cool.

Die Sonos Klasse lade ich beim nächsten mal mit hoch…

Hallo Ta Lun,
ich habe auch schon mit dem Subscribe gespielt - Da hast Du gute :smiley: Arbeit geleistet! Ich habe in der aktuellen Woche keine Zeit, dies für die PHPSONOS Klasse und IPS umzubauen - ich würde auch gerne prüfen ob man das per IPS direkter als über eine PHP Seite auffangen kann und Deinen Code genauer ansehen (die Anpassungen zum Browse hab ich noch garnicht angesehen).

Grüße, Benjamin