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.

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