|
- # Licensed under the MIT license
- # http://opensource.org/licenses/mit-license.php
- # Copyright 2005, Tim Potter <tpot@samba.org>
- # Copyright 2006 John-Mark Gurney <gurney_j@resnet.uoregon.edu>
-
- #
- # 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, dict):
- """This class implements the CDS actions over SOAP."""
-
- def getnextID(self):
- ret = str(self.nextID)
- self.nextID += 1
- return ret
-
- def addContainer(self, parent, title, klass = Container, **kwargs):
- ret = self.addItem(parent, klass, title, **kwargs)
- self.children[ret] = []
- return ret
-
- def addItem(self, parent, klass, *args, **kwargs):
- assert isinstance(self[parent], Container)
- nid = self.getnextID()
- i = klass(nid, parent, *args, **kwargs)
- self.children[parent].append(i)
- self[i.id] = i
- return i.id
-
- def getchildren(self, item):
- assert isinstance(self[item], Container)
- return self.children[item][:]
-
- def __init__(self, title, *args):
- super(ContentDirectoryControl, self).__init__(*args)
- fakeparent = '-1'
- self.nextID = 0
- self.children = { fakeparent: []}
- self.needupdate = False
- self.updateId = 0
- self[fakeparent] = Container(None, '-1', 'fake')
- root = self.addContainer(fakeparent, title)
- assert root == '0'
- del self[fakeparent]
- del self.children[fakeparent]
-
- def doupdate(self):
- if self.needupdate:
- self.needupdate += 1
- self.needupdate = False
-
- # Required actions
-
- def soap_GetSearchCapabilities(self, *args, **kwargs):
- """Required: Return the searching capabilities supported by the device."""
-
- log.msg('GetSearchCapabilities()')
- return { 'SearchCapabilitiesResponse': { 'SearchCaps': '' }}
-
- def soap_GetSortCapabilities(self, *args, **kwargs):
- """Required: Return the CSV list of meta-data tags that can be used in
- sortCriteria."""
-
- log.msg('GetSortCapabilities()')
- return { 'SortCapabilitiesResponse': { 'SortCaps': '' }}
-
- def soap_GetSystemUpdateID(self, *args, **kwargs):
- """Required: Return the current value of state variable SystemUpdateID."""
-
- log.msg('GetSystemUpdateID()')
- self.needupdate = True
- return { 'SystemUpdateIdResponse': { 'Id': self.updateId }}
-
- BrowseFlags = ('BrowseMetaData', 'BrowseDirectChildren')
-
- def soap_Browse(self, *args):
- """Required: Incrementally browse the native heirachy of the Content
- Directory objects exposed by the Content Directory Service."""
-
- (ObjectID, BrowseFlag, Filter, StartingIndex, RequestedCount,
- SortCriteria) = args
- StartingIndex = int(StartingIndex)
- RequestedCount = int(RequestedCount)
-
- log.msg('Browse(ObjectID=%s, BrowseFlags=%s, Filter=%s, '
- 'StartingIndex=%s RequestedCount=%s SortCriteria=%s)' %
- (`ObjectID`, `BrowseFlag`, `Filter`, `StartingIndex`,
- `RequestedCount`, `SortCriteria`))
-
- didl = DIDLElement()
- result = {}
-
- try:
- if BrowseFlag == 'BrowseDirectChildren':
- ch = self.getchildren(ObjectID)[StartingIndex: StartingIndex + RequestedCount]
- filter(lambda x, s = self, d = didl: d.addItem(s[x.id]) and None, ch)
- else:
- didl.addItem(self[ObjectID])
-
- 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, title):
- resource.Resource.__init__(self)
- self.putChild('scpd.xml', static.File('content-directory-scpd.xml'))
- self.control = ContentDirectoryControl(title)
- self.putChild('control', self.control)
|