|
|
@@ -41,7 +41,6 @@ for i in modules: |
|
|
|
for i in modules: |
|
|
|
debug.insertnamespace(i, modmap[i]) |
|
|
|
|
|
|
|
from DIDLLite import TextItem, AudioItem, VideoItem, ImageItem, Resource, StorageFolder |
|
|
|
from FSStorage import FSDirectory |
|
|
|
import os |
|
|
|
import os.path |
|
|
@@ -51,38 +50,51 @@ import string |
|
|
|
import sys |
|
|
|
from twisted.python import log |
|
|
|
from twisted.internet import reactor |
|
|
|
from twisted.application import internet, service |
|
|
|
from twisted.python import usage |
|
|
|
|
|
|
|
def generateuuid(): |
|
|
|
if False: |
|
|
|
return 'uuid:asdflkjewoifjslkdfj' |
|
|
|
return ''.join([ 'uuid:'] + map(lambda x: random.choice(string.letters), xrange(20))) |
|
|
|
|
|
|
|
listenAddr = sys.argv[1] |
|
|
|
if len(sys.argv) > 2: |
|
|
|
listenPort = int(sys.argv[2]) |
|
|
|
if listenPort < 1024 or listenPort > 65535: |
|
|
|
raise ValueError, 'port out of range' |
|
|
|
else: |
|
|
|
listenPort = random.randint(10000, 65000) |
|
|
|
class Options(usage.Options): |
|
|
|
optParameters = [ |
|
|
|
[ 'title', 't', 'My Media Server', 'Title of the server.', ], |
|
|
|
[ 'path', 'p', 'media', 'Root path of the media to be served.', ], |
|
|
|
] |
|
|
|
|
|
|
|
log.startLogging(sys.stdout) |
|
|
|
def parseArgs(self, addr, port=None): |
|
|
|
self['addr'] = addr |
|
|
|
if port is None: |
|
|
|
port = random.randint(10000, 65000) |
|
|
|
else: |
|
|
|
port = int(port) |
|
|
|
if listenPort < 1024 or listenPort > 65535: |
|
|
|
raise ValueError( |
|
|
|
'port must be between 1024 and 65535') |
|
|
|
self['port'] = port |
|
|
|
|
|
|
|
# Create SSDP server |
|
|
|
listenAddr = config['addr'] |
|
|
|
listenPort = config['port'] |
|
|
|
|
|
|
|
application = service.Application("PyMeds") |
|
|
|
|
|
|
|
# Create SSDP server |
|
|
|
from SSDP import SSDPServer, SSDP_PORT, SSDP_ADDR |
|
|
|
|
|
|
|
s = SSDPServer() |
|
|
|
debug.insertnamespace('s', s) |
|
|
|
|
|
|
|
port = reactor.listenMulticast(SSDP_PORT, s, listenMultiple=True) |
|
|
|
port = internet.MulticastServer(SSDP_PORT, s, listenMultiple=True) |
|
|
|
port.setServiceParent(application) |
|
|
|
port.joinGroup(SSDP_ADDR) |
|
|
|
port.setLoopbackMode(0) # don't get our own sends |
|
|
|
|
|
|
|
uuid = generateuuid() |
|
|
|
urlbase = 'http://%s:%d/' % (listenAddr, listenPort) |
|
|
|
|
|
|
|
# Create SOAP server |
|
|
|
|
|
|
|
# Create SOAP server and content server |
|
|
|
from twisted.web import server, resource, static |
|
|
|
from ContentDirectory import ContentDirectoryServer |
|
|
|
from ConnectionManager import ConnectionManagerServer |
|
|
@@ -104,16 +116,11 @@ class RootDevice(static.Data): |
|
|
|
root = WebServer() |
|
|
|
debug.insertnamespace('root', root) |
|
|
|
content = resource.Resource() |
|
|
|
mediapath = 'media' |
|
|
|
if not os.path.isdir(mediapath): |
|
|
|
print >>sys.stderr, \ |
|
|
|
'Sorry, %s is not a directory, no content to serve.' % `mediapath` |
|
|
|
sys.exit(1) |
|
|
|
|
|
|
|
# This sets up the root to be the media dir so we don't have to |
|
|
|
# enumerate the directory |
|
|
|
cds = ContentDirectoryServer('My Media Server', klass=FSDirectory, |
|
|
|
path=mediapath, 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 |
|
|
|
cds = ContentDirectoryServer(config['title'], klass=FSDirectory, |
|
|
|
path=config['path'], urlbase=os.path.join(urlbase, 'content'), |
|
|
|
webbase=content) |
|
|
|
debug.insertnamespace('cds', cds) |
|
|
|
root.putChild('ContentDirectory', cds) |
|
|
|
cds = cds.control |
|
|
@@ -123,6 +130,7 @@ root.putChild('content', content) |
|
|
|
|
|
|
|
|
|
|
|
# Purely to ensure some sane mime-types. On MacOSX I need these. |
|
|
|
# XXX - There isn't any easier way to get to the mime-type dict that I know of. |
|
|
|
medianode = static.File('pymediaserv') |
|
|
|
medianode.contentTypes.update( { |
|
|
|
# From: http://support.microsoft.com/kb/288102 |
|
|
@@ -135,6 +143,10 @@ medianode.contentTypes.update( { |
|
|
|
'.wm': 'video/x-ms-wm', |
|
|
|
'.wmx': 'video/x-ms-wmx', |
|
|
|
|
|
|
|
# From: http://www.matroska.org/technical/specs/notes.html |
|
|
|
'.mkv': 'video/x-matroska', |
|
|
|
'.mka': 'audio/x-matroska', |
|
|
|
|
|
|
|
#'.ts': 'video/mp2t', |
|
|
|
'.ts': 'video/mpeg', # we may want this instead of mp2t |
|
|
|
'.m2t': 'video/mpeg', |
|
|
@@ -149,29 +161,23 @@ medianode.contentTypes.update( { |
|
|
|
del medianode |
|
|
|
|
|
|
|
site = server.Site(root) |
|
|
|
reactor.listenTCP(listenPort, site) |
|
|
|
internet.TCPServer(listenPort, site).setServiceParent(application) |
|
|
|
|
|
|
|
# we need to do this after the children are there, since we send notifies |
|
|
|
import urlparse |
|
|
|
rdxml = urlparse.join(urlbase, 'root-device.xml') |
|
|
|
s.register('%s::upnp:rootdevice' % uuid, |
|
|
|
'upnp:rootdevice', |
|
|
|
urlbase + 'root-device.xml') |
|
|
|
'upnp:rootdevice', rdxml) |
|
|
|
|
|
|
|
s.register(uuid, |
|
|
|
uuid, |
|
|
|
urlbase + 'root-device.xml') |
|
|
|
rdxml) |
|
|
|
|
|
|
|
s.register('%s::urn:schemas-upnp-org:device:MediaServer:1' % uuid, |
|
|
|
'urn:schemas-upnp-org:device:MediaServer:1', |
|
|
|
urlbase + 'root-device.xml') |
|
|
|
'urn:schemas-upnp-org:device:MediaServer:1', rdxml) |
|
|
|
|
|
|
|
s.register('%s::urn:schemas-upnp-org:service:ConnectionManager:1' % uuid, |
|
|
|
'urn:schemas-upnp-org:device:ConnectionManager:1', |
|
|
|
urlbase + 'root-device.xml') |
|
|
|
'urn:schemas-upnp-org:device:ConnectionManager:1', rdxml) |
|
|
|
|
|
|
|
s.register('%s::urn:schemas-upnp-org:service:ContentDirectory:1' % uuid, |
|
|
|
'urn:schemas-upnp-org:device:ContentDirectory:1', |
|
|
|
urlbase + 'root-device.xml') |
|
|
|
|
|
|
|
# Main loop |
|
|
|
|
|
|
|
reactor.run() |
|
|
|
'urn:schemas-upnp-org:device:ContentDirectory:1', rdxml) |