A Python UPnP Media Server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
6.6 KiB

  1. # Licensed under the MIT license
  2. # http://opensource.org/licenses/mit-license.php
  3. # (c) 2005, Tim Potter <tpot@samba.org>
  4. #
  5. # This module implements the Content Directory Service (CDS) service
  6. # type as documented in the ContentDirectory:1 Service Template
  7. # Version 1.01
  8. #
  9. #
  10. # TODO: Figure out a nicer pattern for debugging soap server calls as
  11. # twisted swallows the tracebacks. At the moment I'm going:
  12. #
  13. # try:
  14. # ....
  15. # except:
  16. # traceback.print_exc(file = log.logfile)
  17. #
  18. from twisted.python import log
  19. from twisted.web import resource, static
  20. from elementtree.ElementTree import Element, SubElement, tostring
  21. from upnp import UPnPPublisher
  22. from DIDLLite import DIDLElement, Container, Movie, Resource, MusicTrack
  23. import traceback
  24. from urllib import quote
  25. class ContentDirectoryControl(UPnPPublisher):
  26. """This class implements the CDS actions over SOAP."""
  27. # Required actions
  28. def soap_GetSearchCapabilities(self, *args, **kwargs):
  29. """Return the searching capabilities supported by the device."""
  30. log.msg('GetSearchCapabilities()')
  31. def soap_GetSortCapabilities(self, *args, **kwargs):
  32. """Return the CSV list of meta-data tags that can be used in
  33. sortCriteria."""
  34. log.msg('GetSortCapabilities()')
  35. def soap_GetSystemUpdateID(self, *args, **kwargs):
  36. """Return the current value of state variable SystemUpdateID."""
  37. log.msg('GetSystemUpdateID()')
  38. BrowseFlags = ('BrowseMetaData', 'BrowseDirectChildren')
  39. def soap_Browse(self, *args):
  40. """Incrementally browse the native heirachy of the Content
  41. Directory objects exposed by the Content Directory Service."""
  42. (ObjectID, BrowseFlag, Filter, StartingIndex, RequestedCount,
  43. SortCriteria) = args
  44. log.msg('Browse(ObjectID=%s, BrowseFlags=%s, Filter=%s, '
  45. 'StartingIndex=%s RequestedCount=%s SortCriteria=%s)' %
  46. (`ObjectID`, `BrowseFlag`, `Filter`, `StartingIndex`,
  47. `RequestedCount`, `SortCriteria`))
  48. didl = DIDLElement()
  49. try:
  50. if ObjectID == '0\\Music\\':
  51. c = Container('0\\Music\\Spotty\\', '0\\Music\\', 'Spotty')
  52. c.childCount = 6
  53. c.searchable = 0
  54. didl.addItem(c)
  55. c = Container('0\\Music\\Foot\\', '0\\Music\\', 'Foot')
  56. c.childCount = 1
  57. c.searchable = 0
  58. didl.addItem(c)
  59. if ObjectID == '0\\Music\\Spotty\\':
  60. m = MusicTrack('0\\Music\\media\\foo.mp3', '0\\Music\\', 'foo.mp3')
  61. m.res = Resource('http://192.168.1.105:8080/media/foo.mp3', 'http-get:*:audio/mpeg:*')
  62. m.res.size = 1234
  63. m.res.bitrate = 256
  64. m.genre = 'Others'
  65. m.artist = 'Others'
  66. m.album = 'Others'
  67. didl.addItem(m)
  68. result = {'BrowseResponse': {'Result': didl.toString() ,
  69. 'NumberReturned': didl.numItems(),
  70. 'TotalMatches': didl.numItems(),
  71. 'UpdateID': 0}}
  72. except:
  73. traceback.print_exc(file=log.logfile)
  74. log.msg('Returning: %s' % result)
  75. return result
  76. # Optional actions
  77. def soap_Search(self, *args, **kwargs):
  78. """Search for objects that match some search criteria."""
  79. (ContainerID, SearchCriteria, Filter, StartingIndex,
  80. RequestedCount, SortCriteria) = args
  81. log.msg('Search(ContainerID=%s, SearchCriteria=%s, Filter=%s, ' \
  82. 'StartingIndex=%s, RequestedCount=%s, SortCriteria=%s)' %
  83. (`ContainerID`, `SearchCriteria`, `Filter`,
  84. `StartingIndex`, `RequestedCount`, `SortCriteria`))
  85. def soap_CreateObject(self, *args, **kwargs):
  86. """Create a new object."""
  87. (ContainerID, Elements) = args
  88. log.msg('CreateObject(ContainerID=%s, Elements=%s)' %
  89. (`ContainerID`, `Elements`))
  90. def soap_DestroyObject(self, *args, **kwargs):
  91. """Destroy the specified object."""
  92. (ObjectID) = args
  93. log.msg('DestroyObject(ObjectID=%s)' % `ObjectID`)
  94. def soap_UpdateObject(self, *args, **kwargs):
  95. """Modify, delete or insert object metadata."""
  96. (ObjectID, CurrentTagValue, NewTagValue) = args
  97. log.msg('UpdateObject(ObjectID=%s, CurrentTagValue=%s, ' \
  98. 'NewTagValue=%s)' % (`ObjectID`, `CurrentTagValue`,
  99. `NewTagValue`))
  100. def soap_ImportResource(self, *args, **kwargs):
  101. """Transfer a file from a remote source to a local
  102. destination in the Content Directory Service."""
  103. (SourceURI, DestinationURI) = args
  104. log.msg('ImportResource(SourceURI=%s, DestinationURI=%s)' %
  105. (`SourceURI`, `DestinationURI`))
  106. def soap_ExportResource(self, *args, **kwargs):
  107. """Transfer a file from a local source to a remote
  108. destination."""
  109. (SourceURI, DestinationURI) = args
  110. log.msg('ExportResource(SourceURI=%s, DestinationURI=%s)' %
  111. (`SourceURI`, `DestinationURI`))
  112. def soap_StopTransferResource(self, *args, **kwargs):
  113. """Stop a file transfer initiated by ImportResource or
  114. ExportResource."""
  115. (TransferID) = args
  116. log.msg('StopTransferResource(TransferID=%s)' % TransferID)
  117. def soap_GetTransferProgress(self, *args, **kwargs):
  118. """Query the progress of a file transfer initiated by
  119. an ImportResource or ExportResource action."""
  120. (TransferID, TransferStatus, TransferLength, TransferTotal) = args
  121. log.msg('GetTransferProgress(TransferID=%s, TransferStatus=%s, ' \
  122. 'TransferLength=%s, TransferTotal=%s)' %
  123. (`TransferId`, `TransferStatus`, `TransferLength`,
  124. `TransferTotal`))
  125. def soap_DeleteResource(self, *args, **kwargs):
  126. """Delete a specified resource."""
  127. (ResourceURI) = args
  128. log.msg('DeleteResource(ResourceURI=%s)' % `ResourceURI`)
  129. def soap_CreateReference(self, *args, **kwargs):
  130. """Create a reference to an existing object."""
  131. (ContainerID, ObjectID) = args
  132. log.msg('CreateReference(ContainerID=%s, ObjectID=%s)' %
  133. (`ContainerID`, `ObjectID`))
  134. class ContentDirectoryServer(resource.Resource):
  135. def __init__(self):
  136. resource.Resource.__init__(self)
  137. self.putChild('scpd.xml', static.File('content-directory-scpd.xml'))
  138. self.putChild('control', ContentDirectoryControl())