Browse Source

improve error handling...

restructure code so that we can support a url in a file that points
to the pls file...

[git-p4: depot-paths = "//depot/": change = 1304]
main
John-Mark Gurney 15 years ago
parent
commit
e24bfdc587
1 changed files with 63 additions and 44 deletions
  1. +63
    -44
      shoutcast.py

+ 63
- 44
shoutcast.py View File

@@ -12,7 +12,7 @@ __version__ = '$Change$'
# to zero till we get one? # to zero till we get one?


import ConfigParser import ConfigParser
import StringIO
import cStringIO as StringIO
import os.path import os.path
import random import random


@@ -24,7 +24,7 @@ from DIDLLite import Container, MusicGenre, Item, AudioItem, Resource
from FSStorage import registerklassfun from FSStorage import registerklassfun


from twisted.protocols import shoutcast from twisted.protocols import shoutcast
from twisted.python import log, threadable
from twisted.python import log, threadable, failure
from twisted.internet import defer, protocol, reactor from twisted.internet import defer, protocol, reactor
from twisted.web import error, http, resource, server from twisted.web import error, http, resource, server
from twisted.web.client import getPage, _parse from twisted.web.client import getPage, _parse
@@ -180,16 +180,16 @@ class ShoutProxy(resource.Resource):
self.urls = None self.urls = None
self.fetchingurls = False self.fetchingurls = False


def dump_exc(self):
def dump_exc(self, failure, request):
exc = StringIO.StringIO() exc = StringIO.StringIO()
traceback.print_exc(file=exc)
failure.printBriefTraceback(file=exc)
failure.printTraceback()
exc.seek(0) exc.seek(0)
self.request.setHeader('content-type', 'text/html')
self.request.write(error.ErrorPage(http.INTERNAL_SERVER_ERROR,
request.setHeader('content-type', 'text/html')
request.write(error.ErrorPage(http.INTERNAL_SERVER_ERROR,
http.RESPONSES[http.INTERNAL_SERVER_ERROR], http.RESPONSES[http.INTERNAL_SERVER_ERROR],
'<pre>%s</pre>' % exc.read()).render(self.request))
self.request.finish()
self.request = None
'<pre>%s</pre>' % exc.read()).render(request))
request.finish()


def startNextConnection(self, request): def startNextConnection(self, request):
url = self.urls[self.urlpos] url = self.urls[self.urlpos]
@@ -205,37 +205,32 @@ class ShoutProxy(resource.Resource):


def gotPLS(self, page): def gotPLS(self, page):
self.fetchingurls = False self.fetchingurls = False
try:
pls = ConfigParser.SafeConfigParser()
pls.readfp(StringIO.StringIO(page))
assert pls.getint(PLSsection, 'Version') == 2
assert pls.has_option(PLSsection, 'numberofentries')
cnt = pls.getint(PLSsection, 'numberofentries')
self.urls = []
for i in range(cnt):
i += 1 # stupid one based arrays
self.urls.append(pls.get(PLSsection,
'File%d' % i))
#log.msg('pls urls:', self.urls)
self.urlpos = random.randrange(len(self.urls))
except:
self.dump_exc()
self.urls = None
self.triggerdefered(lambda x: x.errback(1))
return

self.triggerdefered(lambda x: x.callback(1))
pls = ConfigParser.SafeConfigParser()
pls.readfp(StringIO.StringIO(page))
# KCSM 91.1 doesn't provide a version
#assert pls.getint(PLSsection, 'Version') == 2
assert pls.has_option(PLSsection, 'numberofentries')
cnt = pls.getint(PLSsection, 'numberofentries')
self.urls = []
for i in range(cnt):
i += 1 # stupid one based arrays
self.urls.append(pls.get(PLSsection,
'File%d' % i))
#log.msg('pls urls:', self.urls)
self.urlpos = random.randrange(len(self.urls))

self.triggerdefered(lambda x: x.callback(True))


def errPLS(self, failure): def errPLS(self, failure):
self.fetchingurls = False self.fetchingurls = False
self.triggerdefered(lambda x: x.errback(1))
# XXX - retry?
self.triggerdefered(lambda x: x.errback(failure))


def processRequest(self, ign, request): def processRequest(self, ign, request):
self.startNextConnection(request) self.startNextConnection(request)


def errRequest(self, failure, request): def errRequest(self, failure, request):
request.write(failure.render(self.request))
request.finish()
self.dump_exc(failure, request)


def render(self, request): def render(self, request):
request.setHeader('content-type', self.mt) request.setHeader('content-type', self.mt)
@@ -258,9 +253,10 @@ class ShoutProxy(resource.Resource):
self.fetchingurls = True self.fetchingurls = True
# Not really sure if ascii is the correct one, # Not really sure if ascii is the correct one,
# shouldn't getPage do proper escaping for me? # shouldn't getPage do proper escaping for me?
getPage(self.shoutpls.encode('ascii')) \
.addCallbacks(self.gotPLS, self.errPLS)
self.afterurls = [ defer.Deferred() ] self.afterurls = [ defer.Deferred() ]
d = getPage(self.shoutpls.encode('ascii'))
d.addCallback(self.gotPLS)
d.addErrback(self.errPLS)
else: else:
self.afterurls.append(defer.Deferred()) self.afterurls.append(defer.Deferred())
# Always add the callback if we don't have urls # Always add the callback if we don't have urls
@@ -269,6 +265,7 @@ class ShoutProxy(resource.Resource):
errbackArgs=(request, )) errbackArgs=(request, ))
else: else:
self.startNextConnection(request) self.startNextConnection(request)

# and make sure the connection doesn't get closed # and make sure the connection doesn't get closed
return server.NOT_DONE_YET return server.NOT_DONE_YET


@@ -276,20 +273,37 @@ class ShoutProxy(resource.Resource):
'triggerdefered', ] 'triggerdefered', ]
threadable.synchronize(ShoutProxy) threadable.synchronize(ShoutProxy)


class ShoutStation(AudioItem):
class ShoutURL(AudioItem):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.station = kwargs['station']
del kwargs['station']
url = kwargs.pop('url')
mimetype = kwargs.pop('mimetype', 'audio/mpeg')
bitrate = kwargs.pop('bitrate', None)


kwargs['content'] = ShoutProxy(self.station['PLS_URL'],
self.station['MimeType'].encode('ascii'))
kwargs['content'] = ShoutProxy(url, mimetype)
AudioItem.__init__(self, *args, **kwargs) AudioItem.__init__(self, *args, **kwargs)
self.url = '%s/%s' % (self.cd.urlbase, self.id) self.url = '%s/%s' % (self.cd.urlbase, self.id)
self.res = Resource(self.url, 'http-get:*:%s:*' % \
self.station['MimeType'].encode('ascii'))
self.res = Resource(self.url, 'http-get:*:%s:*' % mimetype)
#self.res = Resource(self.url + '/pcm', 'http-get:*:%s:*' % \ #self.res = Resource(self.url + '/pcm', 'http-get:*:%s:*' % \
# 'audio/x-wav') # 'audio/x-wav')
self.bitrate = self.station['Bitrate'] * 128 # 1024k / 8bit
if bitrate is not None:
self.bitrate = bitrate

class ShoutFile(ShoutURL):
def __init__(self, *args, **kwargs):
file = kwargs.pop('file')
kwargs['url'] = open(file).read().strip()

ShoutURL.__init__(self, *args, **kwargs)

class ShoutStation(ShoutURL):
def __init__(self, *args, **kwargs):
self.station = kwargs.pop('station')

kwargs['url'] = self.station['PLS_URL']
kwargs['mimetype'] = self.station['MimeType'].encode('ascii')
kwargs['bitrate'] = self.station['Bitrate'] * 128 # 1024k / 8bit

ShoutURL.__init__(*args, **kwargs)


class ShoutGenre(MusicGenre): class ShoutGenre(MusicGenre):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -397,10 +411,15 @@ class ShoutCast(Container):
if doupdate: if doupdate:
Container.doUpdate(self) Container.doUpdate(self)


def detectshoutcastfile(path, fobj):
path = os.path.basename(path)
def detectshoutcastfile(origpath, fobj):
path = os.path.basename(origpath)
if path == 'SHOUTcast Radio': if path == 'SHOUTcast Radio':
return ShoutCast, { } return ShoutCast, { }

ext = os.path.splitext(path)[1]
if ext == '.scst':
return ShoutFile, { 'file': origpath }

return None, None return None, None


registerklassfun(detectshoutcastfile) registerklassfun(detectshoutcastfile)

Loading…
Cancel
Save