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

Source Code for Module ewa.audio

  1  """ 
  2   
  3  This module is responsible for managing audio files: 
  4   
  5    * managing the main (content) audio files, located in 
  6      <audioroot>/main/path/to/audiofile 
  7    * managing extra audio files, located in 
  8      <audioroot/extra, in two subdirectories, 
  9      <audioroot/extra/masters and <audioroot/extra/transcoded. 
 10    * resolving audiofile symbolic names to files within 
 11      the managed hierarchy. 
 12    * creating combined (spliced) files according to rules passed 
 13      in. 
 14   
 15  """ 
 16  import os 
 17  import thread 
 18   
 19  from ewa.mp3 import get_vbr_bitrate_samplerate_mode, splice 
 20  from ewa.transcode import transcode 
 21  from ewa.logutil import warn 
 22  from ewa.rules import DefaultRule 
 23   
 24  path_join = os.path.join 
 25  path_exists = os.path.exists 
 26  getmtime = os.path.getmtime 
 27   
 28   
29 -def is_original(s):
30 return getattr(s, 'is_original', False)
31 32
33 -class AudioProviderException(RuntimeError):
34 pass
35 36
37 -class FileNotFound(AudioProviderException):
38 pass
39 40
41 -class BaseAudioProvider(object):
42 - def __init__(self, basedir, tolerate_vbr=True):
43 self.basedir = os.path.normpath(os.path.abspath(basedir)) 44 self.tolerate_vbr = tolerate_vbr
45
46 - def get_main_path(self, audioname):
47 return path_join(self.basedir, 48 'main', 49 audioname)
50
51 - def get_extra_master_path(self, 52 audioname):
53 p = path_join(self.basedir, 54 'extra', 55 'master', 56 audioname) 57 if path_exists(p): 58 return p 59 raise FileNotFound(p)
60
61 - def get_extra_transcoded_path(self, 62 audioname, 63 mode, 64 bitrate, 65 samplerate, 66 create=False):
67 path = path_join(self.basedir, 68 'extra', 69 'transcoded', 70 str(bitrate), 71 str(samplerate), 72 mode, 73 audioname) 74 if not path.endswith('.mp3') or path.endswith('.MP3'): 75 path += '.mp3' 76 if create: 77 orig_path = self.get_extra_master_path(audioname) 78 if path_exists(path): 79 trans_mtime = getmtime(path) 80 orig_mtime = getmtime(orig_path) 81 if orig_mtime <= trans_mtime: 82 # no transcoding necessary, return 83 return path 84 # either transcoded file is out of date 85 # or it doesn't exist, transcode 86 transcode(orig_path, path, bitrate, samplerate, mode) 87 return path
88
89 - def get_playlist(self, audioname, rule, create=True):
90 symbols = list(rule(audioname)) 91 if not symbols: 92 return symbols 93 audiopath = self.get_main_path(audioname) 94 (isvbr, bitrate, 95 samplerate, mode) = get_vbr_bitrate_samplerate_mode(audiopath) 96 if isvbr: 97 warn("file is vbr: %s", audiopath) 98 if not self.tolerate_vbr: 99 raise AudioProviderException("vbr not supported: %s" % audiopath) 100 else: 101 # just return the default rule for vbr 102 symbols = list(DefaultRule()(audioname)) 103 104 def resolve(x): 105 if is_original(x): 106 path = self.get_main_path(x) 107 if create and not path_exists(path): 108 raise FileNotFound(path) 109 return path 110 else: 111 try: 112 return self.get_extra_transcoded_path(x, 113 mode, 114 bitrate, 115 samplerate, 116 create) 117 except FileNotFound: 118 warn("extra audio not found: %s", x) 119 return None
120 121 return [y for y in (resolve(x) for x in symbols) if y]
122 123
124 -class FSAudioProvider(BaseAudioProvider):
125
126 - def __init__(self, basedir, tolerate_vbr=True, targetdir=None):
127 super(FSAudioProvider, self).__init__(basedir, tolerate_vbr) 128 if targetdir is None: 129 targetdir = path_join(basedir, 'combined') 130 self.targetdir = os.path.abspath(targetdir)
131
132 - def get_combined_path(self, audioname):
133 return path_join(self.targetdir, audioname)
134
135 - def create_combined(self, audioname, rule, **spliceKwargs):
136 if audioname.startswith('/'): 137 audioname = audioname[1:] 138 mainpath = self.get_main_path(audioname) 139 playlist = self.get_playlist(audioname, rule) 140 target = self.get_combined_path(audioname) 141 parent = os.path.dirname(target) 142 if not path_exists(parent): 143 os.makedirs(parent) 144 renamed = '%s%d~%d~' % (target, 145 os.getpid(), 146 thread.get_ident()) 147 fp = open(renamed, 'wb') 148 for chunk in splice(playlist, mainpath, **spliceKwargs): 149 fp.write(chunk) 150 fp.close() 151 os.rename(renamed, target) 152 return target
153 154
155 -class StreamAudioProvider(BaseAudioProvider):
156
157 - def create_combined(self, audioname, rule, **spliceKwargs):
158 if audioname.startswith('/'): 159 audioname = audioname[1:] 160 mainpath = self.get_main_path(audioname) 161 playlist = self.get_playlist(audioname, rule) 162 return splice(playlist, mainpath, **spliceKwargs)
163 164 165 __all__ = ['AudioProviderException', 166 'FileNotFound', 167 'FSAudioProvider', 168 'StreamAudioProvider'] 169