# Licensed under the MIT license # http://opensource.org/licenses/mit-license.php # (c) 2005, Tim Potter # # This module implements the Content Directory Service (CDS) service # type as documented in the ContentDirectory:1 Service Template # Version 1.01 # # # TODO: Figure out a nicer pattern for debugging soap server calls as # twisted swallows the tracebacks. At the moment I'm going: # # try: # .... # except: # traceback.print_exc(file = log.logfile) # from twisted.python import log from twisted.web import resource, static from elementtree.ElementTree import Element, SubElement, tostring from upnp import UPnPPublisher from DIDLLite import DIDLElement, Container, Movie, Resource, MusicTrack import traceback from urllib import quote class ContentDirectoryControl(UPnPPublisher): """This class implements the CDS actions over SOAP.""" # Required actions def soap_GetSearchCapabilities(self, *args, **kwargs): """Return the searching capabilities supported by the device.""" log.msg('GetSearchCapabilities()') def soap_GetSortCapabilities(self, *args, **kwargs): """Return the CSV list of meta-data tags that can be used in sortCriteria.""" log.msg('GetSortCapabilities()') def soap_GetSystemUpdateID(self, *args, **kwargs): """Return the current value of state variable SystemUpdateID.""" log.msg('GetSystemUpdateID()') BrowseFlags = ('BrowseMetaData', 'BrowseDirectChildren') def soap_Browse(self, *args): """Incrementally browse the native heirachy of the Content Directory objects exposed by the Content Directory Service.""" (ObjectID, BrowseFlag, Filter, StartingIndex, RequestedCount, SortCriteria) = args log.msg('Browse(ObjectID=%s, BrowseFlags=%s, Filter=%s, ' 'StartingIndex=%s RequestedCount=%s SortCriteria=%s)' % (`ObjectID`, `BrowseFlag`, `Filter`, `StartingIndex`, `RequestedCount`, `SortCriteria`)) didl = DIDLElement() try: if ObjectID == '0\\Music\\': c = Container('0\\Music\\Spotty\\', '0\\Music\\', 'Spotty') c.childCount = 6 c.searchable = 0 didl.addItem(c) c = Container('0\\Music\\Foot\\', '0\\Music\\', 'Foot') c.childCount = 1 c.searchable = 0 didl.addItem(c) if ObjectID == '0\\Music\\Spotty\\': m = MusicTrack('0\\Music\\media\\foo.mp3', '0\\Music\\', 'foo.mp3') m.res = Resource('http://192.168.1.105:8080/media/foo.mp3', 'http-get:*:audio/mpeg:*') m.res.size = 1234 m.res.bitrate = 256 m.genre = 'Others' m.artist = 'Others' m.album = 'Others' didl.addItem(m) result = {'BrowseResponse': {'Result': didl.toString() , 'NumberReturned': didl.numItems(), 'TotalMatches': didl.numItems(), 'UpdateID': 0}} except: traceback.print_exc(file=log.logfile) log.msg('Returning: %s' % result) return result # Optional actions def soap_Search(self, *args, **kwargs): """Search for objects that match some search criteria.""" (ContainerID, SearchCriteria, Filter, StartingIndex, RequestedCount, SortCriteria) = args log.msg('Search(ContainerID=%s, SearchCriteria=%s, Filter=%s, ' \ 'StartingIndex=%s, RequestedCount=%s, SortCriteria=%s)' % (`ContainerID`, `SearchCriteria`, `Filter`, `StartingIndex`, `RequestedCount`, `SortCriteria`)) def soap_CreateObject(self, *args, **kwargs): """Create a new object.""" (ContainerID, Elements) = args log.msg('CreateObject(ContainerID=%s, Elements=%s)' % (`ContainerID`, `Elements`)) def soap_DestroyObject(self, *args, **kwargs): """Destroy the specified object.""" (ObjectID) = args log.msg('DestroyObject(ObjectID=%s)' % `ObjectID`) def soap_UpdateObject(self, *args, **kwargs): """Modify, delete or insert object metadata.""" (ObjectID, CurrentTagValue, NewTagValue) = args log.msg('UpdateObject(ObjectID=%s, CurrentTagValue=%s, ' \ 'NewTagValue=%s)' % (`ObjectID`, `CurrentTagValue`, `NewTagValue`)) def soap_ImportResource(self, *args, **kwargs): """Transfer a file from a remote source to a local destination in the Content Directory Service.""" (SourceURI, DestinationURI) = args log.msg('ImportResource(SourceURI=%s, DestinationURI=%s)' % (`SourceURI`, `DestinationURI`)) def soap_ExportResource(self, *args, **kwargs): """Transfer a file from a local source to a remote destination.""" (SourceURI, DestinationURI) = args log.msg('ExportResource(SourceURI=%s, DestinationURI=%s)' % (`SourceURI`, `DestinationURI`)) def soap_StopTransferResource(self, *args, **kwargs): """Stop a file transfer initiated by ImportResource or ExportResource.""" (TransferID) = args log.msg('StopTransferResource(TransferID=%s)' % TransferID) def soap_GetTransferProgress(self, *args, **kwargs): """Query the progress of a file transfer initiated by an ImportResource or ExportResource action.""" (TransferID, TransferStatus, TransferLength, TransferTotal) = args log.msg('GetTransferProgress(TransferID=%s, TransferStatus=%s, ' \ 'TransferLength=%s, TransferTotal=%s)' % (`TransferId`, `TransferStatus`, `TransferLength`, `TransferTotal`)) def soap_DeleteResource(self, *args, **kwargs): """Delete a specified resource.""" (ResourceURI) = args log.msg('DeleteResource(ResourceURI=%s)' % `ResourceURI`) def soap_CreateReference(self, *args, **kwargs): """Create a reference to an existing object.""" (ContainerID, ObjectID) = args log.msg('CreateReference(ContainerID=%s, ObjectID=%s)' % (`ContainerID`, `ObjectID`)) class ContentDirectoryServer(resource.Resource): def __init__(self): resource.Resource.__init__(self) self.putChild('scpd.xml', static.File('content-directory-scpd.xml')) self.putChild('control', ContentDirectoryControl())