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.

151 lines
4.0 KiB

  1. #!/usr/bin/env python
  2. # Licensed under the MIT license
  3. # http://opensource.org/licenses/mit-license.php
  4. # Copyright 2005, Tim Potter <tpot@samba.org>
  5. # Copyright 2006 John-Mark Gurney <gurney_j@resnet.uroegon.edu>
  6. from DIDLLite import TextItem, AudioItem, VideoItem, ImageItem, Resource, StorageFolder
  7. import os
  8. import os.path
  9. import random
  10. import socket
  11. import string
  12. import sys
  13. from twisted.python import log
  14. from twisted.internet import reactor
  15. def generateuuid():
  16. if False:
  17. return 'uuid:asdflkjewoifjslkdfj'
  18. return ''.join([ 'uuid:'] + map(lambda x: random.choice(string.letters), xrange(20)))
  19. listenAddr = sys.argv[1]
  20. if len(sys.argv) > 2:
  21. listenPort = int(sys.argv[2])
  22. if listenPort < 1024 or listenPort > 65535:
  23. raise ValueError, 'port out of range'
  24. else:
  25. listenPort = random.randint(10000, 65000)
  26. log.startLogging(sys.stdout)
  27. # Create SSDP server
  28. from SSDP import SSDPServer, SSDP_PORT, SSDP_ADDR
  29. s = SSDPServer()
  30. port = reactor.listenMulticast(SSDP_PORT, s)
  31. port.joinGroup(SSDP_ADDR)
  32. port.setLoopbackMode(0) # don't get our own sends
  33. uuid = generateuuid()
  34. urlbase = 'http://%s:%d/' % (listenAddr, listenPort)
  35. # Create SOAP server
  36. from twisted.web import server, resource, static
  37. from ContentDirectory import ContentDirectoryServer
  38. from ConnectionManager import ConnectionManagerServer
  39. class WebServer(resource.Resource):
  40. def __init__(self):
  41. resource.Resource.__init__(self)
  42. class RootDevice(static.Data):
  43. def __init__(self):
  44. r = {
  45. 'hostname': socket.gethostname(),
  46. 'uuid': uuid,
  47. 'urlbase': urlbase,
  48. }
  49. d = file('root-device.xml').read() % r
  50. static.Data.__init__(self, d, 'text/xml')
  51. root = WebServer()
  52. cds = ContentDirectoryServer('My Media Server')
  53. root.putChild('ContentDirectory', cds)
  54. cds = cds.control
  55. root.putChild('ConnectionManager', ConnectionManagerServer())
  56. root.putChild('root-device.xml', RootDevice())
  57. # Area of server to serve media files from
  58. from MediaServer import MediaServer
  59. medianode = static.File('media')
  60. medianode.contentTypes.update( {
  61. '.wmv': 'video/x-ms-wmv',
  62. #'.ts': 'video/mp2t',
  63. '.ts': 'video/mpeg', # we may want this instead of mp2t
  64. #'.mp4': 'video/mp4',
  65. '.mp4': 'video/mpeg',
  66. })
  67. root.putChild('media', medianode)
  68. # Set up media files
  69. def addFSPath(parent, title, dpath):
  70. folder = cds.addContainer(parent, title, klass = StorageFolder)
  71. log.msg('title: %s, dpath: %s' % (title, dpath))
  72. for i in os.listdir(dpath):
  73. fpath = os.path.join(dpath, i)
  74. try:
  75. fn, ext = os.path.splitext(i)
  76. ext = ext.lower()
  77. if os.path.isdir(fpath):
  78. addFSPath(folder, fn, fpath)
  79. if not os.path.isfile(fpath):
  80. continue
  81. #if ext == '.ts':
  82. # continue
  83. mt = medianode.contentTypes[ext]
  84. ty = mt.split('/')[0]
  85. if ty == 'video':
  86. klass = VideoItem
  87. elif ty == 'audio':
  88. klass = AudioItem
  89. elif ty == 'text':
  90. klass = TextItem
  91. elif ty == 'image':
  92. klass = ImageItem
  93. else:
  94. raise KeyError, 'no item for mt: %s' % mt
  95. item = cds.addItem(folder, klass, fn)
  96. cds[item].res = Resource('%s%s' % (urlbase, fpath), 'http-get:*:%s:*' % mt)
  97. cds[item].res.size = os.path.getsize(fpath)
  98. except KeyError:
  99. pass
  100. addFSPath('0', 'All Media', 'media')
  101. site = server.Site(root)
  102. reactor.listenTCP(listenPort, site)
  103. # we need to do this after the children are there, since we send notifies
  104. s.register('%s::upnp:rootdevice' % uuid,
  105. 'upnp:rootdevice',
  106. urlbase + 'root-device.xml')
  107. s.register(uuid,
  108. uuid,
  109. urlbase + 'root-device.xml')
  110. s.register('%s::urn:schemas-upnp-org:device:MediaServer:1' % uuid,
  111. 'urn:schemas-upnp-org:device:MediaServer:1',
  112. urlbase + 'root-device.xml')
  113. s.register('%s::urn:schemas-upnp-org:service:ConnectionManager:1' % uuid,
  114. 'urn:schemas-upnp-org:device:ConnectionManager:1',
  115. urlbase + 'root-device.xml')
  116. s.register('%s::urn:schemas-upnp-org:service:ContentDirectory:1' % uuid,
  117. 'urn:schemas-upnp-org:device:ContentDirectory:1',
  118. urlbase + 'root-device.xml')
  119. # Main loop
  120. reactor.run()