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.

146 lines
3.9 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. # Modules to import, maybe config file or something?
  10. def tryloadmodule(mod):
  11. try:
  12. return __import__(mod)
  13. except ImportError:
  14. import traceback
  15. traceback.print_exc()
  16. pass
  17. # ZipStorage w/ tar support should be last as it will gobble up empty files.
  18. modules = [ 'dvd', 'shoutcast', 'ZipStorage' ]
  19. modmap = {}
  20. for i in modules:
  21. modmap[i] = tryloadmodule(i)
  22. import debug # my debugging module
  23. debug.doDebugging(True) # open up debugging port
  24. for i in modules:
  25. debug.insertnamespace(i, modmap[i])
  26. from DIDLLite import TextItem, AudioItem, VideoItem, ImageItem, Resource, StorageFolder
  27. from FSStorage import FSDirectory
  28. import os
  29. import os.path
  30. import random
  31. import socket
  32. import string
  33. import sys
  34. from twisted.python import log
  35. from twisted.internet import reactor
  36. def generateuuid():
  37. if False:
  38. return 'uuid:asdflkjewoifjslkdfj'
  39. return ''.join([ 'uuid:'] + map(lambda x: random.choice(string.letters), xrange(20)))
  40. listenAddr = sys.argv[1]
  41. if len(sys.argv) > 2:
  42. listenPort = int(sys.argv[2])
  43. if listenPort < 1024 or listenPort > 65535:
  44. raise ValueError, 'port out of range'
  45. else:
  46. listenPort = random.randint(10000, 65000)
  47. log.startLogging(sys.stdout)
  48. # Create SSDP server
  49. from SSDP import SSDPServer, SSDP_PORT, SSDP_ADDR
  50. s = SSDPServer()
  51. debug.insertnamespace('s', s)
  52. port = reactor.listenMulticast(SSDP_PORT, s)
  53. port.joinGroup(SSDP_ADDR)
  54. port.setLoopbackMode(0) # don't get our own sends
  55. uuid = generateuuid()
  56. urlbase = 'http://%s:%d/' % (listenAddr, listenPort)
  57. # Create SOAP server
  58. from twisted.web import server, resource, static
  59. from ContentDirectory import ContentDirectoryServer
  60. from ConnectionManager import ConnectionManagerServer
  61. class WebServer(resource.Resource):
  62. def __init__(self):
  63. resource.Resource.__init__(self)
  64. class RootDevice(static.Data):
  65. def __init__(self):
  66. r = {
  67. 'hostname': socket.gethostname(),
  68. 'uuid': uuid,
  69. 'urlbase': urlbase,
  70. }
  71. d = file('root-device.xml').read() % r
  72. static.Data.__init__(self, d, 'text/xml')
  73. root = WebServer()
  74. debug.insertnamespace('root', root)
  75. content = resource.Resource()
  76. 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
  77. debug.insertnamespace('cds', cds)
  78. root.putChild('ContentDirectory', cds)
  79. cds = cds.control
  80. root.putChild('ConnectionManager', ConnectionManagerServer())
  81. root.putChild('root-device.xml', RootDevice())
  82. root.putChild('content', content)
  83. # Purely to ensure some sane mime-types. On MacOSX I need these.
  84. medianode = static.File('pymediaserv')
  85. medianode.contentTypes.update( {
  86. '.wmv': 'video/x-ms-wmv',
  87. #'.ts': 'video/mp2t',
  88. '.ts': 'video/mpeg', # we may want this instead of mp2t
  89. '.m2t': 'video/mpeg',
  90. '.mp4': 'video/mp4',
  91. #'.mp4': 'video/mpeg',
  92. '.dat': 'video/mpeg', # VCD tracks
  93. '.ogm': 'application/ogg',
  94. '.vob': 'video/mpeg',
  95. #'.m4a': 'audio/mp4', # D-Link can't seem to play AAC files.
  96. })
  97. del medianode
  98. site = server.Site(root)
  99. reactor.listenTCP(listenPort, site)
  100. # we need to do this after the children are there, since we send notifies
  101. s.register('%s::upnp:rootdevice' % uuid,
  102. 'upnp:rootdevice',
  103. urlbase + 'root-device.xml')
  104. s.register(uuid,
  105. uuid,
  106. urlbase + 'root-device.xml')
  107. s.register('%s::urn:schemas-upnp-org:device:MediaServer:1' % uuid,
  108. 'urn:schemas-upnp-org:device:MediaServer:1',
  109. urlbase + 'root-device.xml')
  110. s.register('%s::urn:schemas-upnp-org:service:ConnectionManager:1' % uuid,
  111. 'urn:schemas-upnp-org:device:ConnectionManager:1',
  112. urlbase + 'root-device.xml')
  113. s.register('%s::urn:schemas-upnp-org:service:ContentDirectory:1' % uuid,
  114. 'urn:schemas-upnp-org:device:ContentDirectory:1',
  115. urlbase + 'root-device.xml')
  116. # Main loop
  117. reactor.run()