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