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 tolerate_broken=False) 62 else: 63 self.provider = ewa.audio.FSAudioProvider(basedir, 64 False, 65 False, 66 targetdir)
67 68 basedir = property(lambda x: x.provider.basedir) 69 70 targetdir = property(lambda x: getattr(x.provider, 'basedir', None)) 71
72 - def send(self, start_response, status, headers=None, iterable=None):
73 codeline = _codes[status] 74 if headers is None: 75 headers = [('Content-Type', 'text/plain')] 76 start_response(codeline, headers) 77 if iterable is None: 78 return [codeline[4:]] 79 else: 80 return iterable
81
82 - def _create_combined(self, mp3file):
83 # strip leading '/' 84 if mp3file.startswith('/'): 85 mp3file = mp3file[1:] 86 mainpath = self.provider.get_main_path(mp3file) 87 # if this blows up, propagate 88 maintime = os.path.getmtime(mainpath) 89 if os.path.isdir(mainpath): 90 ## if self.index_directories: 91 ## # implement this eventually. 92 ## # probably the file-existence/directory 93 ## # check should be moved to __call__ 94 ## pass 95 raise OSError 96 if self.stream: 97 try: 98 return self.provider.create_combined( 99 mp3file, 100 self.rule, 101 **self.spliceKwargs), MP3_MIMETYPE 102 except (ewa.audio.AudioProviderException, 103 eyeD3.InvalidAudioFormatException): 104 info("%s cannot be processed. Serving statically", mainpath) 105 return open(mainpath), guess_mime(mainpath) 106 else: 107 path = self.provider.get_combined_path(mp3file) 108 try: 109 mtime = os.path.getmtime(path) 110 except OSError: 111 debug("OSError in getting mod time (ok)") 112 pass 113 else: 114 # if the main file modified? 115 regen = maintime > mtime 116 if not regen: 117 if self.refresh_rate == 0: 118 debug("no refresh, returning target path") 119 return path, MP3_MIMETYPE 120 else: 121 t = time.time() 122 if t-mtime < self.refresh_rate: 123 debug("not necessary to refresh, " 124 "returning target path") 125 return path, MP3_MIMETYPE 126 127 # if we get here we regenerate 128 debug("need to regenerate combined file") 129 try: 130 path2 = self.provider.create_combined( 131 mp3file, 132 self.rule, 133 **self.spliceKwargs) 134 except (ewa.audio.AudioProviderException, 135 eyeD3.InvalidAudioFormatException): 136 info("%s cannot be processed. Serving statically", mainpath) 137 return mainpath, guess_mime(mainpath) 138 # should be the same 139 debug("path returned from provider: %s", path2) 140 debug("our calculated path: %s", path) 141 return path2, MP3_MIMETYPE
142
143 - def __call__(self, environ, start_response):
144 mp3file = environ['SCRIPT_NAME']+environ['PATH_INFO'] 145 info("mp3file: %s", mp3file) 146 if not mp3file: 147 return self.send(start_response, 404) 148 try: 149 result, mtype = self._create_combined(mp3file) 150 except (OSError, IOError): 151 exception("Error in looking for file %s", mp3file) 152 return self.send(start_response, 404) 153 except: 154 error("error creating combined file") 155 exception("internal server error") 156 return self.send(start_response, 500) 157 else: 158 return self.sendfile(result, start_response, mtype)
159
160 - def sendfile(self, result, start_response, mtype):
161 headers = [('Content-Type', mtype)] 162 if mtype == MP3_MIMETYPE and self.content_disposition: 163 headers.append(('Content-Disposition', self.content_disposition)) 164 if self.use_xsendfile: 165 length = os.path.getsize(result) 166 headers.extend([(self.sendfile_header, result), 167 ('Content-Length', "%d" % length)]) 168 debug('headers are: %s', headers) 169 return self.send(start_response, 170 200, 171 headers, 172 "OK") 173 else: 174 if not self.stream: 175 result = open(result, 'rb') 176 return self.send(start_response, 177 200, 178 headers, 179 result)
180