Package ewa :: Module wsgiapp
[hide private]
[frames] | no frames]

Source Code for Module ewa.wsgiapp

  1  """ 
  2   
  3  A WSGI application that generates mp3s dynamically 
  4  according to a ruleset. 
  5   
  6  """ 
  7   
  8  import mimetypes 
  9  import os 
 10  import time 
 11   
 12  import eyeD3 
 13   
 14  import ewa.audio 
 15  from ewa.logutil import debug, info, error, exception 
 16   
 17  _codes = {200:'200 OK', 
 18            404:'404 Not Found', 
 19            500:'500 Internal Server Error'} 
 20   
 21  GENERIC_SENDFILE_HEADER = 'X-Sendfile' 
 22   
 23  LIGHTTPD_SENDFILE_HEADER = 'X-LIGHTTPD-send-file' 
 24   
 25  MP3_MIMETYPE = 'audio/mpeg' 
 26   
 27  mimetypes.init() 
 28   
 29   
30 -def guess_mime(filename):
31 mtype, extension = mimetypes.guess_type(filename) 32 if mtype is None: 33 return 'application/octet-stream' 34 return mtype
35 36
37 -class EwaApp(object):
38
39 - def __init__(self, 40 rule, 41 basedir, 42 targetdir=None, 43 stream=False, 44 refresh_rate=0, 45 use_xsendfile=True, 46 sendfile_header=GENERIC_SENDFILE_HEADER, 47 content_disposition='', 48 **spliceKwargs):
49 self.rule = rule 50 self.stream = stream 51 if stream and targetdir: 52 raise ValueError("in streaming mode but targetdir supplied") 53 self.spliceKwargs = spliceKwargs 54 self.refresh_rate = refresh_rate 55 self.use_xsendfile = use_xsendfile 56 self.sendfile_header = sendfile_header 57 self.content_disposition = content_disposition 58 if self.stream: 59 self.provider = ewa.audio.StreamAudioProvider(basedir, 60 tolerate_vbr=False) 61 else: 62 self.provider = ewa.audio.FSAudioProvider(basedir, 63 False, 64 targetdir)
65 66 basedir = property(lambda x: x.provider.basedir) 67 68 targetdir = property(lambda x: getattr(x.provider, 'basedir', None)) 69
70 - def send(self, start_response, status, headers=None, iterable=None):
71 codeline = _codes[status] 72 if headers is None: 73 headers = [('Content-Type', 'text/plain')] 74 start_response(codeline, headers) 75 if iterable is None: 76 return [codeline[4:]] 77 else: 78 return iterable
79
80 - def _create_combined(self, mp3file):
81 # strip leading '/' 82 if mp3file.startswith('/'): 83 mp3file = mp3file[1:] 84 mainpath = self.provider.get_main_path(mp3file) 85 # if this blows up, propagate 86 maintime = os.path.getmtime(mainpath) 87 if os.path.isdir(mainpath): 88 ## if self.index_directories: 89 ## # implement this eventually. 90 ## # probably the file-existence/directory 91 ## # check should be moved to __call__ 92 ## pass 93 raise OSError 94 if self.stream: 95 try: 96 return self.provider.create_combined( 97 mp3file, 98 self.rule, 99 **self.spliceKwargs), MP3_MIMETYPE 100 except (ewa.audio.AudioProviderException, 101 eyeD3.InvalidAudioFormatException): 102 info("%s cannot be processed. Serving statically", mainpath) 103 return open(mainpath), guess_mime(mainpath) 104 else: 105 path = self.provider.get_combined_path(mp3file) 106 try: 107 mtime = os.path.getmtime(path) 108 except OSError: 109 debug("OSError in getting mod time (ok)") 110 pass 111 else: 112 # if the main file modified? 113 regen = maintime > mtime 114 if not regen: 115 if self.refresh_rate == 0: 116 debug("no refresh, returning target path") 117 return path, MP3_MIMETYPE 118 else: 119 t = time.time() 120 if t-mtime < self.refresh_rate: 121 debug("not necessary to refresh, " 122 "returning target path") 123 return path, MP3_MIMETYPE 124 125 # if we get here we regenerate 126 debug("need to regenerate combined file") 127 try: 128 path2 = self.provider.create_combined( 129 mp3file, 130 self.rule, 131 **self.spliceKwargs) 132 except (ewa.audio.AudioProviderException, 133 eyeD3.InvalidAudioFormatException): 134 info("%s cannot be processed. Serving statically", mainpath) 135 return mainpath, guess_mime(mainpath) 136 # should be the same 137 debug("path returned from provider: %s", path2) 138 debug("our calculated path: %s", path) 139 return path2, MP3_MIMETYPE
140
141 - def __call__(self, environ, start_response):
142 mp3file = environ['SCRIPT_NAME']+environ['PATH_INFO'] 143 info("mp3file: %s", mp3file) 144 if not mp3file: 145 return self.send(start_response, 404) 146 try: 147 result, mtype = self._create_combined(mp3file) 148 except (OSError, IOError): 149 exception("Error in looking for file %s", mp3file) 150 return self.send(start_response, 404) 151 except: 152 error("error creating combined file") 153 exception("internal server error") 154 return self.send(start_response, 500) 155 else: 156 return self.sendfile(result, start_response, mtype)
157
158 - def sendfile(self, result, start_response, mtype):
159 headers = [('Content-Type', mtype)] 160 if mtype == MP3_MIMETYPE and self.content_disposition: 161 headers.append(('Content-Disposition', self.content_disposition)) 162 if self.use_xsendfile: 163 length = os.path.getsize(result) 164 headers.extend([(self.sendfile_header, result), 165 ('Content-Length', "%d" % length)]) 166 debug('headers are: %s', headers) 167 return self.send(start_response, 168 200, 169 headers, 170 "OK") 171 else: 172 if not self.stream: 173 result = open(result, 'rb') 174 return self.send(start_response, 175 200, 176 headers, 177 result)
178