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?

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

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

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.web import error, http, resource, server
from twisted.web.client import getPage, _parse
@@ -180,16 +180,16 @@ class ShoutProxy(resource.Resource):
self.urls = None
self.fetchingurls = False

def dump_exc(self):
def dump_exc(self, failure, request):
exc = StringIO.StringIO()
traceback.print_exc(file=exc)
failure.printBriefTraceback(file=exc)
failure.printTraceback()
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],
'<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):
url = self.urls[self.urlpos]
@@ -205,37 +205,32 @@ class ShoutProxy(resource.Resource):

def gotPLS(self, page):
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):
self.fetchingurls = False
self.triggerdefered(lambda x: x.errback(1))
# XXX - retry?
self.triggerdefered(lambda x: x.errback(failure))

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

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

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

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

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

class ShoutStation(AudioItem):
class ShoutURL(AudioItem):
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)
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:*' % \
# '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):
def __init__(self, *args, **kwargs):
@@ -397,10 +411,15 @@ class ShoutCast(Container):
if doupdate:
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':
return ShoutCast, { }

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

return None, None

registerklassfun(detectshoutcastfile)

Loading…
Cancel
Save