ewa.audio

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
     23  path_join=os.path.join
     24  path_exists=os.path.exists
     25  getmtime=os.path.getmtime
     26
     27 -def is_original(s):
     28      return getattr(s, 'is_original', False)
     29
     30 -class AudioProviderException(RuntimeError):
     31      pass
     32
     33 -class FileNotFound(AudioProviderException):
     34      pass
     35
     36 -class BaseAudioProvider(object):
     37 -    def __init__(self, basedir):
     38          self.basedir=os.path.normpath(os.path.abspath(basedir))
     39
     40 -    def get_main_path(self, audioname):
     41          return path_join(self.basedir,
     42                           'main',
     43                           audioname)
     44
     45 -    def get_extra_master_path(self,
     46                                audioname):
     47          p=path_join(self.basedir,
     48                      'extra',
     49                      'master',
     50                      audioname)
     51          if path_exists(p):
     52              return p
     53          raise FileNotFound(p)
     54
     55
     56 -    def get_extra_transcoded_path(self,
     57                                    audioname,
     58                                    mode,
     59                                    bitrate,
     60                                    samplerate,
     61                                    create=False):
     62          path= path_join(self.basedir,
     63                          'extra',
     64                          'transcoded',
     65                          str(bitrate),
     66                          str(samplerate),
     67                          mode,
     68                          audioname)
     69          if not path.endswith('.mp3') or path.endswith('.MP3'):
     70              path+='.mp3'
     71          if create:
     72              orig_path=self.get_extra_master_path(audioname)
     73              if path_exists(path):
     74                  trans_mtime=getmtime(path)
     75                  orig_mtime=getmtime(orig_path)
     76                  if orig_mtime <= trans_mtime:
     77                      # no transcoding necessary, return
     78                      return path
     79              # either transcoded file is out of date
     80              # or it doesn't exist, transcode
     81              transcode(orig_path, path, bitrate, samplerate, mode)
     82          return path
     83
     84 -    def get_playlist(self, audioname, rule, create=True):
     85          symbols=list(rule(audioname))
     86          if not symbols:
     87              return symbols
     88          audiopath=self.get_main_path(audioname)
     89          isvbr, bitrate, samplerate, mode=get_vbr_bitrate_samplerate_mode(audiopath)
     90          if isvbr:
     91              warn("file is vbr: %s", audiopath)
     92              raise AudioProviderException, "vbr not supported: %s" % audiopath
     93
     94          def resolve(x):
     95              if is_original(x):
     96                  path=self.get_main_path(x)
     97                  if create and not path_exists(path):
     98                      raise FileNotFound(path)
     99                  return path
    100              else:
    101                  try:
    102                      return self.get_extra_transcoded_path(x, mode, bitrate, samplerate, create)
    103                  except FileNotFound:
    104                      warn("extra audio not found: %s", x)
    105                      return None
    106
    107          return [y for y in (resolve(x) for x in symbols) if y]
    108
    109
    110 -class FSAudioProvider(BaseAudioProvider):
    111
    112 -    def __init__(self, basedir, targetdir=None):
    113          super(FSAudioProvider, self).__init__(basedir)
    114          if targetdir is None:
    115              targetdir=path_join(basedir, 'combined')
    116          self.targetdir=os.path.abspath(targetdir)
    117
    118 -    def get_combined_path(self, audioname):
    119          return path_join(self.targetdir, audioname)
    120
    121 -    def create_combined(self, audioname, rule, **spliceKwargs):
    122          if audioname.startswith('/'):
    123              audioname=audioname[1:]
    124          mainpath=self.get_main_path(audioname)
    125          playlist=self.get_playlist(audioname, rule)
    126          target=self.get_combined_path(audioname)
    127          parent=os.path.dirname(target)
    128          if not path_exists(parent):
    129              os.makedirs(parent)
    130          renamed='%s%d~%d~' % (target,
    131                                os.getpid(),
    132                                thread.get_ident())
    133          fp=open(renamed, 'wb')
    134          for chunk in splice(playlist, mainpath, **spliceKwargs):
    135              fp.write(chunk)
    136          fp.close()
    137          os.rename(renamed, target)
    138          return target
    139
    140 -class StreamAudioProvider(BaseAudioProvider):
    141
    142 -    def create_combined(self, audioname, rule, **spliceKwargs):
    143          if audioname.startswith('/'):
    144              audioname=audioname[1:]
    145          mainpath=self.get_main_path(audioname)
    146          playlist=self.get_playlist(audioname, rule)
    147          return splice(playlist, mainpath, **spliceKwargs)
    148
    149
    150
    151  __all__=['AudioProviderException',
    152           'FileNotFound',
    153           'FSAudioProvider',
    154           'StreamAudioProvider']
    155