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.

352 lines
9.3 KiB

  1. # Licensed under the MIT license
  2. # http://opensource.org/licenses/mit-license.php
  3. # Copyright 2005, Tim Potter <tpot@samba.org>
  4. from elementtree.ElementTree import Element, SubElement, tostring, _ElementInterface
  5. class Resource:
  6. """An object representing a resource."""
  7. def __init__(self, data, protocolInfo):
  8. self.data = data
  9. self.protocolInfo = protocolInfo
  10. self.bitrate = None
  11. self.size = None
  12. def toElement(self):
  13. root = Element('res')
  14. root.attrib['protocolInfo'] = self.protocolInfo
  15. root.text = self.data
  16. if self.bitrate is not None:
  17. root.attrib['bitrate'] = str(self.bitrate)
  18. if self.size is not None:
  19. root.attrib['size'] = str(self.size)
  20. return root
  21. class Object:
  22. """The root class of the entire content directory class heirachy."""
  23. klass = 'object'
  24. creator = None
  25. res = None
  26. writeStatus = None
  27. def __init__(self, cd, id, parentID, title, restricted = False,
  28. creator = None):
  29. self.cd = cd
  30. self.id = id
  31. self.parentID = parentID
  32. self.title = title
  33. self.creator = creator
  34. if restricted:
  35. self.restricted = '1'
  36. else:
  37. self.restricted = '0'
  38. def toElement(self):
  39. root = Element(self.elementName)
  40. root.attrib['id'] = self.id
  41. root.attrib['parentID'] = self.parentID
  42. SubElement(root, 'dc:title').text = self.title
  43. SubElement(root, 'upnp:class').text = self.klass
  44. root.attrib['restricted'] = self.restricted
  45. if self.creator is not None:
  46. SubElement(root, 'dc:creator').text = self.creator
  47. if self.res is not None:
  48. root.append(self.res.toElement())
  49. if self.writeStatus is not None:
  50. SubElement(root, 'upnp:writeStatus').text = self.writeStatus
  51. return root
  52. def toString(self):
  53. return tostring(self.toElement())
  54. class Item(Object):
  55. """A class used to represent atomic (non-container) content
  56. objects."""
  57. klass = Object.klass + '.item'
  58. elementName = 'item'
  59. refID = None
  60. def toElement(self):
  61. root = Object.toElement(self)
  62. if self.refID is not None:
  63. SubElement(root, 'refID').text = self.refID
  64. return root
  65. class ImageItem(Item):
  66. klass = Item.klass + '.imageItem'
  67. class Photo(ImageItem):
  68. klass = ImageItem.klass + '.photo'
  69. class AudioItem(Item):
  70. """A piece of content that when rendered generates some audio."""
  71. klass = Item.klass + '.audioItem'
  72. genre = None
  73. description = None
  74. longDescription = None
  75. publisher = None
  76. language = None
  77. relation = None
  78. rights = None
  79. def toElement(self):
  80. root = Item.toElement(self)
  81. if self.genre is not None:
  82. SubElement(root, 'upnp:genre').text = self.genre
  83. if self.description is not None:
  84. SubElement(root, 'dc:description').text = self.description
  85. if self.longDescription is not None:
  86. SubElement(root, 'upnp:longDescription').text = \
  87. self.longDescription
  88. if self.publisher is not None:
  89. SubElement(root, 'dc:publisher').text = self.publisher
  90. if self.language is not None:
  91. SubElement(root, 'dc:language').text = self.language
  92. if self.relation is not None:
  93. SubElement(root, 'dc:relation').text = self.relation
  94. if self.rights is not None:
  95. SubElement(root, 'dc:rights').text = self.rights
  96. return root
  97. class MusicTrack(AudioItem):
  98. """A discrete piece of audio that should be interpreted as music."""
  99. klass = AudioItem.klass + '.musicTrack'
  100. artist = None
  101. album = None
  102. originalTrackNumber = None
  103. playlist = None
  104. storageMedium = None
  105. contributor = None
  106. date = None
  107. def toElement(self):
  108. root = AudioItem.toElement(self)
  109. if self.artist is not None:
  110. SubElement(root, 'upnp:artist').text = self.artist
  111. if self.album is not None:
  112. SubElement(root, 'upnp:album').text = self.album
  113. if self.originalTrackNumber is not None:
  114. SubElement(root, 'upnp:originalTrackNumber').text = \
  115. self.originalTrackNumber
  116. if self.playlist is not None:
  117. SubElement(root, 'upnp:playlist').text = self.playlist
  118. if self.storageMedium is not None:
  119. SubElement(root, 'upnp:storageMedium').text = self.storageMedium
  120. if self.contributor is not None:
  121. SubElement(root, 'dc:contributor').text = self.contributor
  122. if self.date is not None:
  123. SubElement(root, 'dc:date').text = self.date
  124. return root
  125. class AudioBroadcast(AudioItem):
  126. klass = AudioItem.klass + '.audioBroadcast'
  127. class AudioBook(AudioItem):
  128. klass = AudioItem.klass + '.audioBook'
  129. class VideoItem(Item):
  130. klass = Item.klass + '.videoItem'
  131. class Movie(VideoItem):
  132. klass = VideoItem.klass + '.movie'
  133. class VideoBroadcast(VideoItem):
  134. klass = VideoItem.klass + '.videoBroadcast'
  135. class MusicVideoClip(VideoItem):
  136. klass = VideoItem.klass + '.musicVideoClip'
  137. class PlaylistItem(Item):
  138. klass = Item.klass + '.playlistItem'
  139. class TextItem(Item):
  140. klass = Item.klass + '.textItem'
  141. class Container(Object, list):
  142. """An object that can contain other objects."""
  143. klass = Object.klass + '.container'
  144. elementName = 'container'
  145. childCount = property(lambda x: len(x))
  146. createClass = None
  147. searchClass = None
  148. searchable = None
  149. def __init__(self, cd, id, parentID, title, restricted = 0, creator = None):
  150. Object.__init__(self, cd, id, parentID, title, restricted, creator)
  151. list.__init__(self)
  152. def toElement(self):
  153. root = Object.toElement(self)
  154. root.attrib['childCount'] = str(self.childCount)
  155. if self.createClass is not None:
  156. SubElement(root, 'upnp:createclass').text = self.createClass
  157. if self.searchClass is not None:
  158. if not isinstance(self.searchClass, (list, tuple)):
  159. self.searchClass = ['searchClass']
  160. for i in searchClass:
  161. SubElement(root, 'upnp:searchclass').text = i
  162. if self.searchable is not None:
  163. root.attrib['searchable'] = str(self.searchable)
  164. return root
  165. class Person(Container):
  166. klass = Container.klass + '.person'
  167. class MusicArtist(Person):
  168. klass = Person.klass + '.musicArtist'
  169. class PlaylistContainer(Container):
  170. klass = Container.klass + '.playlistContainer'
  171. class Album(Container):
  172. klass = Container.klass + '.album'
  173. class MusicAlbum(Album):
  174. klass = Album.klass + '.musicAlbum'
  175. class PhotoAlbum(Album):
  176. klass = Album.klass + '.photoAlbum'
  177. class Genre(Container):
  178. klass = Container.klass + '.genre'
  179. class MusicGenre(Genre):
  180. klass = Genre.klass + '.musicGenre'
  181. class MovieGenre(Genre):
  182. klass = Genre.klass + '.movieGenre'
  183. class StorageSystem(Container):
  184. klass = Container.klass + '.storageSystem'
  185. total = -1
  186. used = -1
  187. free = -1
  188. maxpartition = -1
  189. medium = 'UNKNOWN'
  190. def toElement(self):
  191. root = Container.toElement(self)
  192. SubElement(root, 'upnp:storageTotal').text = str(self.total)
  193. SubElement(root, 'upnp:storageUsed').text = str(self.used)
  194. SubElement(root, 'upnp:storageFree').text = str(self.free)
  195. SubElement(root, 'upnp:storageMaxPartition').text = str(self.maxpartition)
  196. SubElement(root, 'upnp:storageMedium').text = self.medium
  197. return root
  198. class StorageVolume(Container):
  199. klass = Container.klass + '.storageVolume'
  200. total = -1
  201. used = -1
  202. free = -1
  203. medium = 'UNKNOWN'
  204. def toElement(self):
  205. root = Container.toElement(self)
  206. SubElement(root, 'upnp:storageTotal').text = str(self.total)
  207. SubElement(root, 'upnp:storageUsed').text = str(self.used)
  208. SubElement(root, 'upnp:storageFree').text = str(self.free)
  209. SubElement(root, 'upnp:storageMedium').text = self.medium
  210. return root
  211. class StorageFolder(Container):
  212. klass = Container.klass + '.storageFolder'
  213. used = -1
  214. def toElement(self):
  215. root = Container.toElement(self)
  216. if self.used is not None:
  217. SubElement(root, 'upnp:storageUsed').text = str(self.used)
  218. return root
  219. class DIDLElement(_ElementInterface):
  220. def __init__(self):
  221. _ElementInterface.__init__(self, 'DIDL-Lite', {})
  222. self.attrib['xmlns'] = 'urn:schemas-upnp-org:metadata-1-0/DIDL-Lite'
  223. self.attrib['xmlns:dc'] = 'http://purl.org/dc/elements/1.1/'
  224. self.attrib['xmlns:upnp'] = 'urn:schemas-upnp-org:metadata-1-0/upnp'
  225. def addContainer(self, id, parentID, title, restricted = False):
  226. e = Container(id, parentID, title, restricted, creator = '')
  227. self.append(e.toElement())
  228. def addItem(self, item):
  229. self.append(item.toElement())
  230. def numItems(self):
  231. return len(self)
  232. def toString(self):
  233. return tostring(self)
  234. if __name__ == '__main__':
  235. root = DIDLElement()
  236. root.addContainer('0\Movie\\', '0\\', 'Movie')
  237. root.addContainer('0\Music\\', '0\\', 'Music')
  238. root.addContainer('0\Photo\\', '0\\', 'Photo')
  239. root.addContainer('0\OnlineMedia\\', '0\\', 'OnlineMedia')
  240. print tostring(root)