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.

125 lines
3.4 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. #
  7. # $Id$
  8. #
  9. from DIDLLite import TextItem, AudioItem, VideoItem, ImageItem, Resource, StorageFolder
  10. from FSStorage import FSDirectory
  11. import os
  12. import os.path
  13. import random
  14. import socket
  15. import string
  16. import sys
  17. from twisted.python import log
  18. from twisted.internet import reactor
  19. def generateuuid():
  20. if False:
  21. return 'uuid:asdflkjewoifjslkdfj'
  22. return ''.join([ 'uuid:'] + map(lambda x: random.choice(string.letters), xrange(20)))
  23. listenAddr = sys.argv[1]
  24. if len(sys.argv) > 2:
  25. listenPort = int(sys.argv[2])
  26. if listenPort < 1024 or listenPort > 65535:
  27. raise ValueError, 'port out of range'
  28. else:
  29. listenPort = random.randint(10000, 65000)
  30. log.startLogging(sys.stdout)
  31. # Create SSDP server
  32. from SSDP import SSDPServer, SSDP_PORT, SSDP_ADDR
  33. s = SSDPServer()
  34. port = reactor.listenMulticast(SSDP_PORT, s)
  35. port.joinGroup(SSDP_ADDR)
  36. port.setLoopbackMode(0) # don't get our own sends
  37. uuid = generateuuid()
  38. urlbase = 'http://%s:%d/' % (listenAddr, listenPort)
  39. # Create SOAP server
  40. from twisted.web import server, resource, static
  41. from ContentDirectory import ContentDirectoryServer
  42. from ConnectionManager import ConnectionManagerServer
  43. class WebServer(resource.Resource):
  44. def __init__(self):
  45. resource.Resource.__init__(self)
  46. class RootDevice(static.Data):
  47. def __init__(self):
  48. r = {
  49. 'hostname': socket.gethostname(),
  50. 'uuid': uuid,
  51. 'urlbase': urlbase,
  52. }
  53. d = file('root-device.xml').read() % r
  54. static.Data.__init__(self, d, 'text/xml')
  55. root = WebServer()
  56. content = resource.Resource()
  57. cds = ContentDirectoryServer('My Media Server', klass = FSDirectory, path = 'media', urlbase = os.path.join(urlbase, 'content'), webbase = content) # This sets up the root to be the media dir so we don't have to enumerate the directory
  58. root.putChild('ContentDirectory', cds)
  59. cds = cds.control
  60. root.putChild('ConnectionManager', ConnectionManagerServer())
  61. root.putChild('root-device.xml', RootDevice())
  62. root.putChild('content', content)
  63. # Area of server to serve media files from
  64. from MediaServer import MediaServer
  65. medianode = static.File('media')
  66. medianode.contentTypes.update( {
  67. '.wmv': 'video/x-ms-wmv',
  68. #'.ts': 'video/mp2t',
  69. '.ts': 'video/mpeg', # we may want this instead of mp2t
  70. #'.mp4': 'video/mp4',
  71. '.mp4': 'video/mpeg',
  72. '.dat': 'video/mpeg', # VCD tracks
  73. '.ogm': 'application/ogg',
  74. '.vob': 'video/mpeg',
  75. #'.m4a': 'audio/mp4', # D-Link can't seem to play AAC files.
  76. })
  77. root.putChild('media', medianode)
  78. site = server.Site(root)
  79. reactor.listenTCP(listenPort, site)
  80. # we need to do this after the children are there, since we send notifies
  81. s.register('%s::upnp:rootdevice' % uuid,
  82. 'upnp:rootdevice',
  83. urlbase + 'root-device.xml')
  84. s.register(uuid,
  85. uuid,
  86. urlbase + 'root-device.xml')
  87. s.register('%s::urn:schemas-upnp-org:device:MediaServer:1' % uuid,
  88. 'urn:schemas-upnp-org:device:MediaServer:1',
  89. urlbase + 'root-device.xml')
  90. s.register('%s::urn:schemas-upnp-org:service:ConnectionManager:1' % uuid,
  91. 'urn:schemas-upnp-org:device:ConnectionManager:1',
  92. urlbase + 'root-device.xml')
  93. s.register('%s::urn:schemas-upnp-org:service:ContentDirectory:1' % uuid,
  94. 'urn:schemas-upnp-org:device:ContentDirectory:1',
  95. urlbase + 'root-device.xml')
  96. # Main loop
  97. reactor.run()