def _notify(self, statevars): # create the request body propset = Element("e:propertyset") propset.attrib['xmlns:e'] = "urn:schemas-upnp-org:event-1-0" prop = SubElement(propset, "e:property") # add each evented statevar to the property set for statevar in statevars: if statevar.sendEvents: SubElement(prop, statevar.name).text = statevar.text_value else: raise Exception("StateVar '%s' is not evented" % statevar.name) postData = xmlprint(propset) logger.log_debug("NOTIFY property set:\n" + postData) # send the NOTIFY request to each callback for url,urlparts in self.callbacks: # set the NOTIFY headers headers = { 'Host': urlparts.netloc, 'Content-Type': MimeType('text', 'xml'), 'NT': 'upnp:event', 'NTS': 'upnp:propchange', 'SID': self.id, 'SEQ': self.seqid } # creator = protocol.ClientCreator(reactor, HTTPClientProtocol) request = ClientRequest("NOTIFY", urlparts.path, headers, postData) d = creator.connectTCP(urlparts.hostname, urlparts.port) d.addCallback(self._sendNotifyRequest, request) logger.log_debug("sending NOTIFY to %s" % url) self.seqid = self.seqid + 1
def _dispatchSoapRequest(self, request): try: try: envelope = XML(request.soap_data) body = envelope.find("{http://schemas.xmlsoap.org/soap/envelope/}Body") # determine UPnP action action = body.find("{%s}%s" % (request.soap_ns, request.soap_action)) # look up the action in the service upnp_action = self.service._actions[request.soap_action] # build a list of the action arguments in_args = {} for arg in action: in_args[arg.tag] = arg.text # execute the UPnP action logger.log_debug("executing %s#%s" % (self.service.serviceID, request.soap_action)) out_args = upnp_action(request, self.service, in_args) # return the action response env = Element("s:Envelope") env.attrib['xmlns:s'] = "http://schemas.xmlsoap.org/soap/envelope/" env.attrib['s:encodingStyle'] = "http://schemas.xmlsoap.org/soap/encoding/" env.attrib['xmlns:i'] = "http://www.w3.org/1999/XMLSchema-instance" body = SubElement(env, "s:Body") resp = SubElement(body, "u:%sResponse" % request.soap_action) resp.attrib['xmlns:u'] = request.soap_ns for (name,type,value) in out_args: arg = SubElement(resp, name) arg.attrib["i:type"] = type arg.text = value output = xmlprint(env) return HttpResponse(200, headers={'EXT': ''}, stream=output) except UPNPError, e: raise e except Exception, e: logger.log_error("caught unhandled exception: %s" % e) raise UPNPError(500, "Internal server error")
def getDescription(self, host, relativeUrls=False): root = Element("root") root.attrib["xmlns"] = "urn:schemas-upnp-org:device-1-0" version = SubElement(root, "specVersion") SubElement(version, "major").text = "1" SubElement(version, "minor").text = "0" device = SubElement(root, "device") SubElement(device, "deviceType").text = self.deviceType SubElement(device, "friendlyName").text = self.friendlyName SubElement(device, "manufacturer").text = self.manufacturer SubElement(device, "UDN").text = "uuid:%s" % self.UDN SubElement(device, "modelName").text = self.modelName if self.manufacturerURL: SubElement(device, "manufacturerURL").text = self.manufacturerURL if self.modelDescription: SubElement(device, "modelDescription").text = self.modelDescription if self.modelURL: SubElement(device, "modelURL").text = self.modelURL if self.modelNumber: SubElement(device, "modelNumber").text = self.modelNumber if self.serialNumber: SubElement(device, "serialNumber").text = self.serialNumber if relativeUrls: urlbase = "" SubElement(device, "URLBase").text = "http://%s" % host else: urlbase = "http://%s" % host svc_list = SubElement(device, "serviceList") for svc in self._services.values(): service = SubElement(svc_list, "service") SubElement(service, "serviceType").text = svc.serviceType SubElement(service, "serviceId").text = svc.serviceID SubElement(service, "SCPDURL").text = "%s/%s/%s" % ( urlbase, self.UDN.replace(":", "_"), svc.serviceID.replace(":", "_"), ) SubElement(service, "controlURL").text = "%s/%s/%s/control" % ( urlbase, self.UDN.replace(":", "_"), svc.serviceID.replace(":", "_"), ) SubElement(service, "eventSubURL").text = "%s/%s/%s/event" % ( urlbase, self.UDN.replace(":", "_"), svc.serviceID.replace(":", "_"), ) return xmlprint(root)
def getDescription(self): scpd = Element("scpd") scpd.attrib['xmlns'] = 'urn:schemas-upnp-org:service-1-0' version = SubElement(scpd, "specVersion") SubElement(version, "major").text = "1" SubElement(version, "minor").text = "0" action_list = SubElement(scpd, "actionList") for action_name,upnp_action in self._actions.items(): action = SubElement(action_list, "action") SubElement(action, "name").text = action_name arg_list = SubElement(action, "argumentList") all_args = upnp_action.in_args + upnp_action.out_args for upnp_arg in all_args: arg = SubElement(arg_list, "argument") SubElement(arg, "name").text = upnp_arg.name SubElement(arg, "direction").text = upnp_arg.direction SubElement(arg, "relatedStateVariable").text = upnp_arg.related.name if upnp_arg.retval: SubElement(arg, "retval") var_list = SubElement(scpd, "serviceStateTable") for var_name,upnp_stateVar in self._stateVars.items(): stateVar = SubElement(var_list, "stateVariable") if upnp_stateVar.sendEvents: stateVar.attrib['sendEvents'] = "yes" else: stateVar.attrib['sendEvents'] = "no" SubElement(stateVar, "name").text = var_name SubElement(stateVar, "dataType").text = upnp_stateVar.type #if not upnp_stateVar.text_value == None: # SubElement(stateVar, "defaultValue").text = upnp_stateVar.text_value if not upnp_stateVar.allowedValueList == None: allowed_list = SubElement(stateVar, "allowedValueList") for allowed in upnp_stateVar.allowedValueList: SubElement(allowed_list, "allowedValue").text = allowed if not upnp_stateVar.allowedMin == None and not upnp_stateVar.allowedMax == None: allowed_range = SubElement(stateVar, "allowedValueRange") SubElement(allowed_range, "minimum").text = str(upnp_stateVar.allowedMin) SubElement(allowed_range, "maximum").text = str(upnp_stateVar.allowedMax) if not upnp_stateVar.allowedStep == None: SubElement(allowed_range, "step").text = str(upnp_stateVar.allowedStep) return xmlprint(scpd)
def Browse(self, request, objectID, browseFlag, filter, startingIndex, requestedCount, sortCriteria): # determine the host:port of content host = request.headers.getHeader('host').split(':',1)[0] port = CoreHttpConfig.HTTP_PORT # break up the objectID into segments. objectIDs have the following form: # 0/<artist>/<album>/<song> segments = objectID.split('/') if len(segments) < 1 or segments[0] != '0': raise UPNPError(701, "ObjectID %i is invalid" % objectID) # generate the DIDL envelope didl = Element("DIDL-Lite") didl.attrib["xmlns"] = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" didl.attrib["xmlns:upnp"] = "urn:schemas-upnp-org:metadata-1-0/upnp/" didl.attrib["xmlns:dc"] = "http://purl.org/dc/elements/1.1/" # browse the metadata of one specific item if browseFlag == 'BrowseMetadata': if len(segments) == 1: container = SubElement(didl, 'container') container.attrib['id'] = objectID container.attrib['parentID'] = '-1' container.attrib['restricted'] = '1' container.attrib['childCount'] = '2' SubElement(container, 'dc:title').text = 'Media on Higgins' SubElement(container, 'upnp:class').text = 'object.container.storageFolder' elif len(segments) > 1: if segments[1] == 'music': if len(segments) == 2: container = SubElement(didl, 'container') container.attrib['id'] = objectID container.attrib['parentID'] = '0' container.attrib['restricted'] = '1' container.attrib['childCount'] = str(len(Artist.objects.all())) SubElement(container, 'dc:title').text = 'Music' SubElement(container, 'upnp:class').text = 'object.container.storageFolder' elif len(segments) == 3: artist = Artist.objects.get(id=int(segments[2])) container = SubElement(didl, 'container') container.attrib['id'] = objectID container.attrib['parentID'] = '/'.join(segments[:1]) container.attrib['restricted'] = '1' container.attrib['childCount'] = str(len(Album.objects.filter(artist=artist))) SubElement(container, 'dc:title').text = str(artist.name) SubElement(container, 'upnp:class').text = 'object.container.person.musicArtist' elif len(segments) == 4: album = Album.objects.get(id=int(segments[3]), artist=int(segments[2])) container = SubElement(didl, 'container') container.attrib['id'] = objectID container.attrib['parentID'] = '/'.join(segments[:1]) container.attrib['restricted'] = '1' container.attrib['childCount'] = str(len(Song.objects.filter(album=album))) SubElement(container, 'dc:title').text = str(album.name) SubElement(container, 'upnp:class').text = 'object.container.album.musicAlbum' if segments[1] == 'playlists': if len(segments) == 2: container = SubElement(didl, 'container') container.attrib['id'] = objectID container.attrib['parentID'] = '0' container.attrib['restricted'] = '1' container.attrib['childCount'] = str(len(Playlist.objects.all())) SubElement(container, 'dc:title').text = 'Playlists' SubElement(container, 'upnp:class').text = 'object.container.storageFolder' elif len(segments) == 3: playlist = Playlist.objects.get(id=int(segments[2])) container = SubElement(didl, 'container') container.attrib['id'] = objectID container.attrib['parentID'] = '/'.join(segments[:1]) container.attrib['restricted'] = '1' container.attrib['childCount'] = str(len(playlist)) SubElement(container, 'dc:title').text = str(playlist.name) SubElement(container, 'upnp:class').text = 'object.container.playlistContainer' total_matches = 1 number_returned = 1 elif browseFlag == 'BrowseDirectChildren': def getMatches(startingIndex, requestedCount, qset): # don't return more than 100 items total_matches = len(qset) if requestedCount > 100 or requestedCount == 0: requestedCount = 100 if startingIndex >= total_matches: raise UPNPError(402, "startingIndex %i is out of range" % startingIndex) if startingIndex + requestedCount > total_matches: requestedCount = total_matches - startingIndex matches = qset[startingIndex:startingIndex + requestedCount] number_returned = len(matches) retval = (matches, total_matches, number_returned) logger.log_debug("getMatches: %s" % str(retval)) return retval # determine the number of matches if len(segments) == 1: container = SubElement(didl, "container") container.attrib["id"] = '0/music' container.attrib["parentID"] = '0' container.attrib["restricted"] = "1" container.attrib['childCount'] = str(len(Album.objects.all())) SubElement(container, "upnp:class").text = "object.container.storageFolder" SubElement(container, "dc:title").text = 'Music' container = SubElement(didl, "container") container.attrib["id"] = '0/playlists' container.attrib["parentID"] = '0' container.attrib["restricted"] = "1" container.attrib['childCount'] = str(len(Playlist.objects.all())) SubElement(container, "upnp:class").text = "object.container.storageFolder" SubElement(container, "dc:title").text = 'Playlists' total_matches = 2 number_returned = 2 if len(segments) > 1: if segments[1] == 'music': if len(segments) == 2: qset = Artist.objects.all() matches,total_matches,number_returned = getMatches(startingIndex, requestedCount, qset) for artist in matches: container = SubElement(didl, "container") container.attrib["id"] = objectID + '/' + str(artist.id) container.attrib["parentID"] = '/'.join(segments[:-1]) container.attrib["restricted"] = "1" container.attrib['childCount'] = str(len(Album.objects.filter(artist=artist))) SubElement(container, "upnp:class").text = "object.container.person.musicArtist" SubElement(container, "dc:title").text = artist.name elif len(segments) == 3: qset = Album.objects.filter(artist=int(segments[2])) matches,total_matches,number_returned = getMatches(startingIndex, requestedCount, qset) for album in matches: container = SubElement(didl, "container") container.attrib["id"] = objectID + '/' + str(album.id) container.attrib["parentID"] = '/'.join(segments[:-1]) container.attrib["restricted"] = "1" container.attrib['childCount'] = str(len(Song.objects.filter(album=album))) SubElement(container, "upnp:class").text = "object.container.album.musicAlbum" SubElement(container, "dc:title").text = album.name elif len(segments) == 4: qset = Song.objects.filter(album=int(segments[3])) matches,total_matches,number_returned = getMatches(startingIndex, requestedCount, qset) for song in matches: item = SubElement(didl, "item") item.attrib["id"] = objectID + '/' + str(song.id) item.attrib["parentID"] = '/'.join(segments[:-1]) item.attrib["restricted"] = "1" SubElement(item, "upnp:class").text = "object.item.audioItem.musicTrack" SubElement(item, "dc:title").text = song.name SubElement(item, "upnp:artist").text = str(song.artist.name) SubElement(item, "upnp:album").text = str(song.album.name) SubElement(item, "upnp:genre").text = str(song.album.genre.name) SubElement(item, "upnp:originalTrackNumber").text = str(song.track_number) resource = SubElement(item, "res") resource.attrib["protocolInfo"] = "http-get:*:%s:*" % str(song.file.mimetype) resource.attrib["size"] = str(song.file.size) resource.text = "http://%s:%i/content/%i" % (host, port, song.id) if segments[1] == 'playlists': if len(segments) == 2: qset = Playlist.objects.all() matches,total_matches,number_returned = getMatches(startingIndex, requestedCount, qset) for playlist in matches: container = SubElement(didl, "container") container.attrib["id"] = objectID + '/' + str(playlist.id) container.attrib["parentID"] = '/'.join(segments[:-1]) container.attrib["restricted"] = "1" container.attrib['childCount'] = str(len(playlist)) SubElement(container, "upnp:class").text = "object.container.playlistContainer" SubElement(container, "dc:title").text = playlist.name elif len(segments) == 3: playlist = Playlist.objects.get(id=segments[2]) qset = playlist.list_songs() matches,total_matches,number_returned = getMatches(startingIndex, requestedCount, qset) for song in matches: item = SubElement(didl, "item") item.attrib["id"] = objectID + '/' + str(song.id) item.attrib["parentID"] = '/'.join(segments[:-1]) item.attrib["restricted"] = "1" SubElement(item, "upnp:class").text = "object.item.audioItem.musicTrack" SubElement(item, "dc:title").text = song.name SubElement(item, "upnp:artist").text = str(song.artist.name) SubElement(item, "upnp:album").text = str(song.album.name) SubElement(item, "upnp:genre").text = str(song.album.genre.name) startingIndex += 1 SubElement(item, "upnp:originalTrackNumber").text = str(startingIndex) resource = SubElement(item, "res") resource.attrib["protocolInfo"] = "http-get:*:%s:*" % str(song.file.mimetype) resource.attrib["size"] = str(song.file.size) resource.text = "http://%s:%i/content/%i" % (host, port, song.id) else: raise UPNPError(402, "unknown browse flag %s" % browseFlag) result = xmlprint(didl, pretty=False, withXMLDecl=False) update_id = 0 return { 'NumberReturned': number_returned, 'TotalMatches': total_matches, 'Result': result, 'UpdateID': update_id }