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…