]> git.parisson.com Git - timeside.git/commitdiff
modify encode and decode apis, adapt each plugins
authoryomguy <yomguy@parisson.com>
Thu, 8 Oct 2009 14:08:23 +0000 (14:08 +0000)
committeryomguy <yomguy@parisson.com>
Thu, 8 Oct 2009 14:08:23 +0000 (14:08 +0000)
12 files changed:
decode/api.py
decode/core.py
decode/flac.py
decode/mp3.py
decode/ogg.py
decode/wav.py
encode/api.py
encode/core.py
encode/flac.py
encode/mp3.py
encode/ogg.py
encode/wav.py

index 61f5f87b508c5eae817ec6993ddac311271ac16d..7963848a5affb54241b4a992107258f1d83d18f7 100644 (file)
@@ -29,46 +29,28 @@ class IDecoder(Interface):
     # from the caller's point of view. However, when implementing the class 
     # you'll obviously want to include this extra argument.
 
-    def get_format():
+    def format():
         """Return the decode/encoding format as a short string 
         Example: "MP3", "OGG", "AVI", ...
         """
    
-    def get_description():
+    def description():
         """Return a string describing what this decode format provides, is good 
         for, etc... The description is meant to help the end user decide what 
         format is good for him/her
         """
 
-    def get_file_extension():
+    def file_extension():
         """Return the filename extension corresponding to this decode format"""
 
-    def get_mime_type():
+    def mime_type():
         """Return the mime type corresponding to this decode format"""
 
-    def set_cache_dir(path):
-        """Set the directory where cached files should be stored. Does nothing
-        if the decodeer doesn't support caching. 
-       
-        The driver shouldn't assume that this method will always get called. A
-        temporary directory should be used if that's not the case.
-        """
-
-    def process(item_id, source, metadata, options=None):
+    def process(source, options=None):
         """Perform the decoding process and stream the result through a generator
 
-        item_id is the media item id that uniquely identifies this audio/video
-        resource
-
         source is the audio/video source file absolute path.
 
-        metadata is a tuple containing tuples for each descriptor return by
-        the dc.Ressource of the item, in the model order :
-        ((name1, value1),(name2, value2),(name1, value3), ...)
-
-        The returned file path is not meant to be permanent in any way, it 
-        should be considered temporary/volatile by the caller.
-
         It is highly recommended that decode drivers implement some sort of
         cache instead of re-encoding each time process() is called.
 
@@ -76,6 +58,7 @@ class IDecoder(Interface):
         different items, using the same driver instance.
         """
 
+
 class DecodeProcessError(TimeSideError):
 
     def __init__(self, message, command, subprocess):
index 8e6874b3d9cd3ce9bb78fe0d9fac975390a02b8f..da895f43a8bb5c4b987710649cbe35ceb9cee600 100644 (file)
@@ -32,151 +32,50 @@ from timeside.core import *
 import xml.dom.minidom
 import xml.dom.ext
 
-class DecoderCore(Component):
-    """Defines the main parts of the decodeing tools :
-    paths, metadata parsing, data streaming thru system command"""
 
-    def __init__(self):
-        self.source = ''
-        self.collection = ''
-        self.verbose = ''
-        self.dest = ''
-        self.metadata = []
-        self.cache_dir = 'cache'
-        self.buffer_size = 0xFFFF
+class SubProcessPipe:
 
-    def set_cache_dir(self,path):
-        self.cache_dir = path
-
-    def normalize(self):
-        """ Normalize the source and return its path """
-        args = ''
-        if self.verbose == '0':
-            args = '-q'
-        try:
-            os.system('normalize-audio '+args+' "'+self.source+'"')
-            return self.source
-        except:
-            raise IOError('DecoderError: cannot normalize, path does not exist.')
-
-    def check_md5_key(self):
-        """ Check if the md5 key is OK and return a boolean """
-        try:
-            md5_log = os.popen4('md5sum -c "'+self.dest+ \
-                                '" "'+self.dest+'.md5"')
-            return 'OK' in md5_log.split(':')
-        except IOError:
-            raise IOError('DecoderError: cannot check the md5 key.')
-    
-    def get_file_info(self):
-        """ Return the list of informations of the dest """
-        return self.decode.get_file_info()
-
-    def get_wav_length_sec(self) :
-        """ Return the length of the audio source file in seconds """
-        try:
-            file1, file2 = os.popen4('wavinfo "'+self.source+ \
-                                     '" | grep wavDataSize')
-            for line in file2.readlines():
-                line_split = line.split(':')
-                value = int(int(line_split[1])/(4*44100))
-                return value
-        except:
-            raise IOError('DecoderError: cannot get the wav length.')
-
-    def compare_md5_key(self, source, dest):
-        """ Compare source and dest files wih md5 method """
-        f_source = open(source).read()
-        f_dest = open(dest).read()
-        return md5.new(f_source).digest() == md5.new(f_dest).digest()
-
-    def write_metadata_xml(self,path):
-        doc = xml.dom.minidom.Document()
-        root = doc.createElement('timeside')
-        doc.appendChild(root)
-        for tag in self.metadata.keys() :
-            value = self.metadata[tag]
-            node = doc.createElement(tag)
-            node.setAttribute('value', str(value))
-            #node.setAttribute('type', get_type(value))
-            root.appendChild(node)
-        xml_file = open(path, "w")
-        xml.dom.ext.PrettyPrint(doc, xml_file)
-        xml_file.close()
-
-    def pre_process(self, item_id, source, metadata, ext,
-                    cache_dir, options=None):
-        """ Pre processing : prepare the decode path and return it"""
-        self.item_id = str(item_id)
-        self.source = source
-        file_name = get_file_name(self.source)
-        file_name_wo_ext, file_ext = split_file_name(file_name)
-        self.cache_dir = cache_dir
-        self.metadata = metadata
-        #self.collection = self.metadata['Collection']
-        #self.artist = self.metadata['Artist']
-        #self.title = self.metadata['Title']
-
-        # Normalize if demanded
-        if not options is None:
-            self.options = options
-            if 'normalize' in self.options and \
-                self.options['normalize'] == True:
-                self.normalize()
-
-        # Define the decode directory
-        self.ext = self.get_file_extension()
-        decode_dir = os.path.join(self.cache_dir,self.ext)
-
-        if not os.path.exists(decode_dir):
-            decode_dir_split = decode_dir.split(os.sep)
-            path = os.sep + decode_dir_split[0]
-            for _dir in decode_dir_split[1:]:
-                path = os.path.join(path,_dir)
-                if not os.path.exists(path):
-                    os.mkdir(path)
-        else:
-            path = decode_dir
-
-        # Set the target file
-        target_file = self.item_id+'.'+self.ext
-        dest = os.path.join(path,target_file)
-        return dest
-
-    def core_process(self, command, buffer_size, dest):
-        """Encode and stream audio data through a generator"""
+    def __init__(self, command, stdin=None):
+        """Read media and stream data through a generator.
+        Taken from Telemeta (see http://telemeta.org)"""
 
-        __chunk = 0
-        file_out = open(dest,'w')
+        self.buffer_size = 0xFFFF
+
+        if not stdin:
+            stdin =  subprocess.PIPE
 
-        proc = subprocess.Popen(command.encode('utf-8'),
+        self.proc = subprocess.Popen(command.encode('utf-8'),
                     shell = True,
-                    bufsize = buffer_size,
-                    stdin = subprocess.PIPE,
+                    bufsize = self.buffer_size,
+                    stdin = stdin,
                     stdout = subprocess.PIPE,
                     close_fds = True)
 
-        # Core processing
+        self.input = self.proc.stdin
+        self.output = self.proc.stdout
+
+
+class DecoderCore(Component):
+    """Defines the main parts of the decoding tools :
+    paths, metadata parsing, data streaming thru system command"""
+
+    def __init__(self):
+        self.command = 'sox "%s" -s -q -b 16 -r 44100 -t wav -c2 -'
+
+    def process(self, source, options=None):
+        """Encode and stream audio data through a generator"""
+
+        proc = SubProcessPipe(self.command) % source
+
         while True:
-            __chunk = proc.stdout.read(buffer_size)
+            __chunk = proc.output.read(self.proc.buffer_size)
             status = proc.poll()
             if status != None and status != 0:
                 raise ExportProcessError('Command failure:', command, proc)
             if len(__chunk) == 0:
                 break
             yield __chunk
-            file_out.write(__chunk)
-
-        file_out.close()
-
-    def post_process(self, item_id, source, metadata, ext, 
-                     cache_dir, options=None):
-        """ Post processing : write tags, print infos, etc..."""
-        #self.write_tags()
-        if not options is None:
-            if 'verbose' in self.options and self.options['verbose'] != '0':
-                print self.dest
-                print self.get_file_info()
+
 
 
 # External functions
index 37b956e9ae71d065e4d50152f5cacf7dfe06d2d6..466b6e807c7f0d2eb5e966a73439fb123f434d6e 100644 (file)
@@ -32,29 +32,25 @@ class FlacDecoder(DecoderCore):
     """Defines methods to decode to FLAC"""
 
     implements(IDecoder)
-    
+
     def __init__(self):
-        self.item_id = ''
-        self.source = ''
-        self.metadata = {}
-        self.options = {}
-        self.description = ''
-        self.dest = ''
-        self.quality_default = '-5'
-        self.info = []
-        self.buffer_size = 0xFFFF
-
-    def get_format(self):
+        self.description = self.description()
+        self.format = self.format()
+        self.mime_type = self.mime_type()
+
+    def format(self):
         return 'FLAC'
-    
-    def get_file_extension(self):
+
+    def file_extension(self):
         return 'flac'
 
-    def get_mime_type(self):
+    def mime_type(self):
         return 'application/flac'
 
-    def get_description(self):
-        return 'FIXME'
+    def description(self):
+        return """
+        Free Lossless Audio Codec (FLAC) is a file format for lossless audio data compression. During compression, FLAC does not lose quality from the audio stream, as lossy compression formats such as MP3, AAC, and Vorbis do. Josh Coalson is the primary author of FLAC.
+        """
 
     def get_file_info(self):
         try:
@@ -68,101 +64,3 @@ class FlacDecoder(DecoderCore):
             raise IOError('DecoderError: metaflac is not installed or ' + \
                            'file does not exist.')
 
-    def set_cache_dir(self,path):
-        """Set the directory where cached files should be stored. Does nothing
-        if the decodeer doesn't support caching. 
-       
-        The driver shouldn't assume that this method will always get called. A
-        temporary directory should be used if that's not the case.
-        """
-        self.cache_dir = path
-
-    def decode(self):
-        try:
-            file_name, ext = get_file_name(self.source)
-            dest = self.cache_dir+os.sep+file_name+'.wav'
-            os.system('flac -d -o "'+dest+'" "'+self.source+'"')
-            self.source = dest
-            return dest
-        except:
-            raise IOError('DecoderError: decoder is not compatible.')
-
-    def write_tags(self, file):
-        media = FLAC(file)
-        for tag in self.metadata:
-            name = tag[0]
-            value = clean_word(tag[1])
-            if name == 'COMMENT':
-                media['DESCRIPTION'] = unicode(value)
-            else:
-                media[name] = unicode(value)
-        try:
-            media.save()
-        except:
-            raise IOError('DecoderError: cannot write tags.')
-
-    def get_args(self,options=None):
-        """Get process options and return arguments for the encoder"""
-        args = []
-        if not options is None:
-            self.options = options
-            if not ('verbose' in self.options and self.options['verbose'] != '0'):
-                args.append('-s')
-            if 'flac_quality' in self.options:
-                args.append('-f ' + self.options['flac_quality'])
-            else:
-                args.append('-f ' + self.quality_default)
-        else:
-            args.append('-s -f ' + self.quality_default)
-
-        #for tag in self.metadata.keys():
-            #value = clean_word(self.metadata[tag])
-            #args.append('-c %s="%s"' % (tag, value))
-            #if tag in self.dub2args_dict.keys():
-                #arg = self.dub2args_dict[tag]
-                #args.append('-c %s="%s"' % (arg, value))
-
-        return args
-
-    def process(self, item_id, source, metadata, options=None):
-        self.item_id = item_id
-        self.source = source
-        self.metadata = metadata
-        self.args = self.get_args(options)
-        self.ext = self.get_file_extension()
-        self.args = ' '.join(self.args)
-        self.command = 'sox "%s" -s -q -b 16 -r 44100 -t wav -c2 - | flac -c %s - ' % (self.source, self.args)
-
-        # Pre-proccessing
-        self.dest = self.pre_process(self.item_id,
-                                         self.source,
-                                         self.metadata,
-                                         self.ext,
-                                         self.cache_dir,
-                                         self.options)
-
-        # Processing (streaming + cache writing)
-        stream = self.core_process(self.command, self.buffer_size, self.dest)
-
-        for chunk in stream:
-            pass
-
-        self.write_tags(self.dest)
-        file = open(self.dest,'r')
-        
-        while True:
-            chunk = file.read(self.buffer_size)
-            if len(chunk) == 0:
-                break
-            yield chunk
-
-        file.close()
-
-        # Post-proccessing
-        #self.post_process(self.item_id,
-                         #self.source,
-                         #self.metadata,
-                         #self.ext,
-                         #self.cache_dir,
-                         #self.options)
-
index b89a924231ece107419386d0c5e1122685bf64c4..81e4851608f18ca380fabba463d2c77fb1b2fde8 100644 (file)
@@ -26,53 +26,31 @@ import subprocess
 
 from timeside.decode.core import *
 from timeside.decode.api import IDecoder
-#from mutagen.id3 import *
+
 
 class Mp3Decoder(DecoderCore):
     """Defines methods to decode to MP3"""
 
     implements(IDecoder)
-    
+
     def __init__(self):
-        self.item_id = ''
-        self.metadata = {}
-        self.description = ''
-        self.info = []
-        self.source = ''
-        self.dest = ''
-        self.options = {}
-        self.bitrate_default = '192'
-        self.buffer_size = 0xFFFF
-        self.dub2id3_dict = {'title': 'TIT2', #title2
-                             'creator': 'TCOM', #composer
-                             'creator': 'TPE1', #lead
-                             'identifier': 'UFID', #Unique ID...
-                             'identifier': 'TALB', #album
-                             'type': 'TCON', #genre
-                             'publisher': 'TPUB', #comment
-                             #'date': 'TYER', #year
-                             }
-        self.dub2args_dict = {'title': 'tt', #title2
-                             'creator': 'ta', #composerS
-                             'relation': 'tl', #album
-                             #'type': 'tg', #genre
-                             'publisher': 'tc', #comment
-                             'date': 'ty', #year
-                             }
-    def get_format(self):
+        self.description = self.description()
+        self.format = self.format()
+        self.mime_type = self.mime_type()
+
+    def format(self):
         return 'MP3'
-    
-    def get_file_extension(self):
+
+    def file_extension(self):
         return 'mp3'
 
-    def get_mime_type(self):
+    def mime_type(self):
         return 'audio/mpeg'
 
-    def get_description(self):
-        return "FIXME"
-
-    def set_cache_dir(self,path):
-       self.cache_dir = path
+    def description(self):
+        return """
+        MPEG-1 Audio Layer 3, more commonly referred to as MP3, is a patented digital audio encoding format using a form of lossy data compression. It is a common audio format for consumer audio storage, as well as a de facto standard of digital audio compression for the transfer and playback of music on digital audio players. MP3 is an audio-specific format that was designed by the Moving Picture Experts Group as part of its MPEG-1 standard.
+        """
 
     def get_file_info(self):
         try:
@@ -85,85 +63,3 @@ class Mp3Decoder(DecoderCore):
         except:
             raise IOError('DecoderError: file does not exist.')
 
-    def decode(self):
-        try:
-            os.system('sox "'+self.source+'" -s -q -r 44100 -t wav "' \
-                        +self.cache_dir+os.sep+self.item_id+'"')
-            return self.cache_dir+os.sep+self.item_id+'.wav'
-        except:
-            raise IOError('DecoderError: decoder is not compatible.')
-
-    def write_tags(self):
-        """Write all ID3v2.4 tags by mapping dub2id3_dict dictionnary with the
-            respect of mutagen classes and methods"""
-        from mutagen import id3  
-        id3 = id3.ID3(self.dest)
-        for tag in self.metadata.keys():
-            if tag in self.dub2id3_dict.keys():
-                frame_text = self.dub2id3_dict[tag]
-                value = self.metadata[tag]
-                frame = mutagen.id3.Frames[frame_text](3,value)
-                try:
-                    id3.add(frame)
-                except:
-                    raise IOError('DecoderError: cannot tag "'+tag+'"')
-        try:
-            id3.save()
-        except:
-            raise IOError('DecoderError: cannot write tags')
-
-    def get_args(self, options=None):
-        """Get process options and return arguments for the encoder"""
-        args = []
-        if not options is None: 
-            self.options = options
-            if not ( 'verbose' in self.options and self.options['verbose'] != '0' ):
-                args.append('-S')
-            if 'mp3_bitrate' in self.options:
-                args.append('-b ' + self.options['mp3_bitrate'])
-            else:
-                args.append('-b '+self.bitrate_default)
-            #Copyrights, etc..
-            args.append('-c -o')
-        else:
-            args.append('-S -c -o')
-
-        for tag in self.metadata:
-            name = tag[0]
-            value = clean_word(tag[1])
-            if name in self.dub2args_dict.keys():
-                arg = self.dub2args_dict[name]
-                args.append('--' + arg + ' "' + value + '"')
-        return args
-
-    def process(self, item_id, source, metadata, options=None):
-        self.item_id = item_id
-        self.source = source
-        self.metadata = metadata
-        self.args = self.get_args(options)
-        self.ext = self.get_file_extension()
-        self.args = ' '.join(self.args)
-        self.command = 'sox "%s" -q -b 16 -r 44100 -t wav - | lame %s -' % (self.source, self.args)
-        #self.command = 'lame %s "%s" -' % (self.args, self.source)
-
-        # Pre-proccessing
-        self.dest = self.pre_process(self.item_id,
-                                         self.source,
-                                         self.metadata,
-                                         self.ext,
-                                         self.cache_dir,
-                                         self.options)
-
-        # Processing (streaming + cache writing)
-        stream = self.core_process(self.command, self.buffer_size, self.dest)
-        for chunk in stream:
-            yield chunk
-    
-        # Post-proccessing
-        #self.post_process(self.item_id,
-                         #self.source,
-                         #self.metadata,
-                         #self.ext,
-                         #self.cache_dir,
-                         #self.options)
-
index 278e0896dd1d54fc12bc2488b55a4fb7f7d18fbc..b4fa46a6b5c57b6918f354971c92b43ad7a7f605 100644 (file)
@@ -31,32 +31,30 @@ class OggDecoder(DecoderCore):
     """Defines methods to decode to OGG Vorbis"""
 
     implements(IDecoder)
-    
+
     def __init__(self):
-        self.item_id = ''
-        self.metadata = {}
-        self.description = ''
-        self.info = []
-        self.source = ''
-        self.dest = ''
-        self.options = {}
+        self.description = self.description()
+        self.format = self.format()
+        self.mime_type = self.mime_type()
+
         self.bitrate_default = '192'
-        self.buffer_size = 0xFFFF
         self.dub2args_dict = {'creator': 'artist',
                              'relation': 'album'
                              }
-        
-    def get_format(self):
-        return 'OGG'
-    
-    def get_file_extension(self):
+
+    def format(self):
+        return 'OggVorbis'
+
+    def file_extension(self):
         return 'ogg'
 
-    def get_mime_type(self):
+    def mime_type(self):
         return 'application/ogg'
 
-    def get_description(self):
-        return 'FIXME'
+    def description(self):
+        return """
+        Vorbis is a free software / open source project headed by the Xiph.Org Foundation (formerly Xiphophorus company). The project produces an audio format specification and software implementation (codec) for lossy audio compression. Vorbis is most commonly used in conjunction with the Ogg container format and it is therefore often referred to as Ogg Vorbis. (source Wikipedia)
+        """
 
     def get_file_info(self):
         try:
@@ -69,75 +67,3 @@ class OggDecoder(DecoderCore):
         except:
             raise IOError('DecoderError: file does not exist.')
 
-    def set_cache_dir(self,path):
-       self.cache_dir = path
-
-    def decode(self):
-        try:
-            os.system('oggdec -o "'+self.cache_dir+os.sep+self.item_id+
-                      '.wav" "'+self.source+'"')
-            return self.cache_dir+os.sep+self.item_id+'.wav'
-        except:
-            raise IOError('DecoderError: decoder is not compatible.')
-
-    def write_tags(self):
-        media = OggVorbis(self.dest)
-        for tag in self.metadata.keys():
-            media[tag] = str(self.metadata[tag])
-        media.save()
-
-    def get_args(self,options=None):
-        """Get process options and return arguments for the encoder"""
-        args = []
-        if not options is None:
-            self.options = options
-            if not ('verbose' in self.options and self.options['verbose'] != '0'):
-                args.append('-Q ')
-            if 'ogg_bitrate' in self.options:
-                args.append('-b '+self.options['ogg_bitrate'])
-            elif 'ogg_quality' in self.options:
-                args.append('-q '+self.options['ogg_quality'])
-            else:
-                args.append('-b '+self.bitrate_default)
-        else:
-            args.append('-Q -b '+self.bitrate_default)
-
-        for tag in self.metadata:
-            name = tag[0]
-            value = clean_word(tag[1])
-            args.append('-c %s="%s"' % (name, value))
-            if name in self.dub2args_dict.keys():
-                arg = self.dub2args_dict[name]
-                args.append('-c %s="%s"' % (arg, value))
-        return args
-
-    def process(self, item_id, source, metadata, options=None):
-        self.item_id = item_id
-        self.source = source
-        self.metadata = metadata
-        self.args = self.get_args(options)
-        self.ext = self.get_file_extension()
-        self.args = ' '.join(self.args)
-        self.command = 'sox "%s" -s -q -b 16 -r 44100 -t wav -c2 - | oggenc %s -' % (self.source, self.args)
-
-        # Pre-proccessing
-        self.dest = self.pre_process(self.item_id,
-                                        self.source,
-                                        self.metadata,
-                                        self.ext,
-                                        self.cache_dir,
-                                        self.options)
-
-        # Processing (streaming + cache writing)
-        stream = self.core_process(self.command, self.buffer_size, self.dest)
-        for chunk in stream:
-            yield chunk
-    
-        # Post-proccessing
-        #self.post_process(self.item_id,
-                        #self.source,
-                        #self.metadata,
-                        #self.ext,
-                        #self.cache_dir,
-                        #self.options)
-
index 87a64ad9f6c7acc3b483e24c1bd007c6d8ea7329..1a270140e50af475bab1c4bbe60cacb25f21e9bc 100644 (file)
@@ -29,28 +29,25 @@ class WavDecoder(DecoderCore):
     """Defines methods to decode to WAV"""
 
     implements(IDecoder)
-    
+
     def __init__(self):
-        self.item_id = ''
-        self.metadata = {}
-        self.description = ''
-        self.info = []
-        self.source = ''
-        self.dest = ''
-        self.options = {}
-        self.buffer_size = 0xFFFF
-
-    def get_format(self):
+        self.description = self.description()
+        self.format = self.format()
+        self.mime_type = self.mime_type()
+
+    def format(self):
         return 'WAV'
-    
-    def get_file_extension(self):
+
+    def file_extension(self):
         return 'wav'
 
-    def get_mime_type(self):
+    def mime_type(self):
         return 'audio/x-wav'
 
-    def get_description(self):
-        return 'FIXME'
+    def description(self):
+        return """
+        WAV (or WAVE), short for Waveform audio format, also known as Audio for Windows, is a Microsoft and IBM audio file format standard for storing an audio bitstream on PCs. It is an application of the RIFF bitstream format method for storing data in “chunks”, and thus is also close to the 8SVX and the AIFF format used on Amiga and Macintosh computers, respectively. It is the main format used on Windows systems for raw and typically uncompressed audio. The usual bitstream encoding is the Pulse Code Modulation (PCM) format.
+        """
 
     def get_file_info(self):
         try:
@@ -63,96 +60,3 @@ class WavDecoder(DecoderCore):
         except:
             raise IOError('DecoderError: wavinfo id not installed or file does not exist.')
 
-    def set_cache_dir(self,path):
-        self.cache_dir = path
-
-    def decode(self):
-        try:
-            file_name, ext = get_file_name(self.source)
-            dest = self.cache_dir+os.sep+file_name+'.wav'
-            os.system('sox "'+self.source+'" -s -r 44100 -t wav -c2 "'+ \
-                      dest+'.wav"')
-            self.source = dest
-            return dest
-        except:
-            raise IOError('DecoderError: decoder is not compatible.')
-
-    def write_tags(self):
-        # Create metadata XML file !
-        self.write_metadata_xml(self.dest+'.xml')
-    
-    def create_md5_key(self):
-        """ Create the md5 keys of the dest """
-        try:
-            os.system('md5sum -b "'+self.dest+'" >"'+self.dest+'.md5"')
-        except:
-            raise IOError('DecoderError: cannot create the md5 key.')
-    
-    def create_par_key(self):
-        """ Create the par2 keys of the dest """
-        args = 'c -n1 '
-        if 'verbose' in self.options and self.options['verbose'] != '0':
-            args = args
-        else:
-            args = args + '-q -q '
-
-        try:
-            os.system('par2 '+args+' "'+self.dest+'"')
-        except:
-            raise IOError('DecoderError: cannot create the par2 key.')
-
-    def process(self, item_id, source, metadata, options=None):
-        self.item_id = item_id
-        self.source = source
-        self.metadata = metadata
-        self.options = {}
-
-        if not options is None:
-            self.options = options
-
-        # Pre-proccessing
-        self.ext = self.get_file_extension()
-        self.dest = self.pre_process(self.item_id,
-                                        self.source,
-                                        self.metadata,
-                                        self.ext,
-                                        self.cache_dir,
-                                        self.options)
-
-        # Initializing
-        file_in = open(self.source,'rb')
-        file_out = open(self.dest,'w')
-
-        # Core Processing
-        while True:
-            chunk = file_in.read(self.buffer_size)
-            if len(chunk) == 0:
-                break
-            yield chunk
-            file_out.write(chunk)
-
-        file_in.close()
-        file_out.close()
-
-        # Create the md5 key
-        #if 'md5' in self.metadata and self.metadata['md5']:
-        self.create_md5_key()
-
-        # Create the par2 key
-        #if 'par2' in self.metadata and self.metadata['par2']:
-        #self.create_par_key()
-
-        # Pre-proccessing
-        self.post_process(self.item_id,
-                        self.source,
-                        self.metadata,
-                        self.ext,
-                        self.cache_dir,
-                        self.options)
-
-
-
-            #if self.compare_md5_key():
-            #os.system('cp -a "'+self.source+'" "'+ self.dest+'"')
-            #print 'COPIED'
-
index b97edca848bc824445025ade75c0a83719e25d9c..41870370baa84e07a0d00a282e67724a9ec2b020 100644 (file)
@@ -24,49 +24,46 @@ from timeside.core import Interface, TimeSideError
 class IEncoder(Interface):
     """Encoder driver interface"""
 
-    # Remark: the method prototypes do not include any self or cls argument 
-    # because an interface is meant to show what methods a class must expose 
-    # from the caller's point of view. However, when implementing the class 
+    # Remark: the method prototypes do not include any self or cls argument
+    # because an interface is meant to show what methods a class must expose
+    # from the caller's point of view. However, when implementing the class
     # you'll obviously want to include this extra argument.
 
-    def get_format():
-        """Return the encode/encoding format as a short string 
+    def format():
+        """Return the encode/encoding format as a short string
         Example: "MP3", "OGG", "AVI", ...
         """
-   
-    def get_description():
-        """Return a string describing what this encode format provides, is good 
-        for, etc... The description is meant to help the end user decide what 
+
+    def description():
+        """Return a string describing what this encode format provides, is good
+        for, etc... The description is meant to help the end user decide what
         format is good for him/her
         """
 
-    def get_file_extension():
+    def file_extension():
         """Return the filename extension corresponding to this encode format"""
 
-    def get_mime_type():
+    def mime_type():
         """Return the mime type corresponding to this encode format"""
 
     def set_cache_dir(path):
         """Set the directory where cached files should be stored. Does nothing
-        if the encodeer doesn't support caching. 
-       
+        if the encodeer doesn't support caching.
+
         The driver shouldn't assume that this method will always get called. A
         temporary directory should be used if that's not the case.
         """
 
-    def process(item_id, source, metadata, options=None):
+    def process(source, metadata, options=None):
         """Perform the encoding process and stream the result as a generator.
 
-        item_id is the media item id that uniquely identifies this audio/video
-        resource
-
-        source is a raw audio stream coming from the decoder.
+        source is a raw audio stream coming from a decoder.
 
         metadata is a tuple containing tuples for each descriptor return by
         the dc.Ressource of the item, in the model order :
         ((name1, value1),(name2, value2),(name1, value3), ...)
 
-        The returned file path is not meant to be permanent in any way, it 
+        The returned file path is not meant to be permanent in any way, it
         should be considered temporary/volatile by the caller.
 
         It is highly recommended that encode drivers implement some sort of
@@ -76,7 +73,7 @@ class IEncoder(Interface):
         different items, using the same driver instance.
         """
 
-class ExportProcessError(TimeSideError):
+class EncodeProcessError(TimeSideError):
 
     def __init__(self, message, command, subprocess):
         self.message = message
index 00f428ac96450ffbcde6b1571ef990a6bcfca332..7988279b7f0826fa32d53aef6548a4d608645a27 100644 (file)
@@ -32,151 +32,44 @@ from timeside.core import *
 import xml.dom.minidom
 import xml.dom.ext
 
-class EncoderCore(Component):
-    """Defines the main parts of the encodeing tools :
-    paths, metadata parsing, data streaming thru system command"""
 
-    def __init__(self):
-        self.source = ''
-        self.collection = ''
-        self.verbose = ''
-        self.dest = ''
-        self.metadata = []
-        self.cache_dir = 'cache'
-        self.buffer_size = 0xFFFF
+class SubProcessPipe:
+    """Read media and stream data through a generator.
+    Taken from Telemeta (see http://telemeta.org)"""
 
-    def set_cache_dir(self,path):
-        self.cache_dir = path
-
-    def normalize(self):
-        """ Normalize the source and return its path """
-        args = ''
-        if self.verbose == '0':
-            args = '-q'
-        try:
-            os.system('normalize-audio '+args+' "'+self.source+'"')
-            return self.source
-        except:
-            raise IOError('EncoderError: cannot normalize, path does not exist.')
-
-    def check_md5_key(self):
-        """ Check if the md5 key is OK and return a boolean """
-        try:
-            md5_log = os.popen4('md5sum -c "'+self.dest+ \
-                                '" "'+self.dest+'.md5"')
-            return 'OK' in md5_log.split(':')
-        except IOError:
-            raise IOError('EncoderError: cannot check the md5 key.')
-    
-    def get_file_info(self):
-        """ Return the list of informations of the dest """
-        return self.encode.get_file_info()
-
-    def get_wav_length_sec(self) :
-        """ Return the length of the audio source file in seconds """
-        try:
-            file1, file2 = os.popen4('wavinfo "'+self.source+ \
-                                     '" | grep wavDataSize')
-            for line in file2.readlines():
-                line_split = line.split(':')
-                value = int(int(line_split[1])/(4*44100))
-                return value
-        except:
-            raise IOError('EncoderError: cannot get the wav length.')
-
-    def compare_md5_key(self, source, dest):
-        """ Compare source and dest files wih md5 method """
-        f_source = open(source).read()
-        f_dest = open(dest).read()
-        return md5.new(f_source).digest() == md5.new(f_dest).digest()
-
-    def write_metadata_xml(self,path):
-        doc = xml.dom.minidom.Document()
-        root = doc.createElement('timeside')
-        doc.appendChild(root)
-        for tag in self.metadata.keys() :
-            value = self.metadata[tag]
-            node = doc.createElement(tag)
-            node.setAttribute('value', str(value))
-            #node.setAttribute('type', get_type(value))
-            root.appendChild(node)
-        xml_file = open(path, "w")
-        xml.dom.ext.PrettyPrint(doc, xml_file)
-        xml_file.close()
-
-    def pre_process(self, item_id, source, metadata, ext,
-                    cache_dir, options=None):
-        """ Pre processing : prepare the encode path and return it"""
-        self.item_id = str(item_id)
-        self.source = source
-        file_name = get_file_name(self.source)
-        file_name_wo_ext, file_ext = split_file_name(file_name)
-        self.cache_dir = cache_dir
-        self.metadata = metadata
-        #self.collection = self.metadata['Collection']
-        #self.artist = self.metadata['Artist']
-        #self.title = self.metadata['Title']
-
-        # Normalize if demanded
-        if not options is None:
-            self.options = options
-            if 'normalize' in self.options and \
-                self.options['normalize'] == True:
-                self.normalize()
-
-        # Define the encode directory
-        self.ext = self.get_file_extension()
-        encode_dir = os.path.join(self.cache_dir,self.ext)
-
-        if not os.path.exists(encode_dir):
-            encode_dir_split = encode_dir.split(os.sep)
-            path = os.sep + encode_dir_split[0]
-            for _dir in encode_dir_split[1:]:
-                path = os.path.join(path,_dir)
-                if not os.path.exists(path):
-                    os.mkdir(path)
-        else:
-            path = encode_dir
-
-        # Set the target file
-        target_file = self.item_id+'.'+self.ext
-        dest = os.path.join(path,target_file)
-        return dest
-
-    def core_process(self, command, buffer_size, dest):
-        """Encode and stream audio data through a generator"""
-
-        __chunk = 0
-        file_out = open(dest,'w')
+    def __init__(self, command, stdin=None):
+        self.buffer_size = 0xFFFF
+        if not stdin:
+            stdin =  subprocess.PIPE
 
-        proc = subprocess.Popen(command.encode('utf-8'),
+        self.proc = subprocess.Popen(command.encode('utf-8'),
                     shell = True,
-                    bufsize = buffer_size,
-                    stdin = subprocess.PIPE,
+                    bufsize = self.buffer_size,
+                    stdin = stdin,
                     stdout = subprocess.PIPE,
                     close_fds = True)
 
-        # Core processing
+        self.input = self.proc.stdin
+        self.output = self.proc.stdout
+
+
+class EncoderCore(Component):
+    """Defines the main parts of the encoding tools :
+    paths, metadata parsing, data streaming thru system command"""
+
+    def core_process(self, command, stdin):
+        """Encode and stream audio data through a generator"""
+
+        proc = SubProcessPipe(command, stdin)
+
         while True:
-            __chunk = proc.stdout.read(buffer_size)
+            __chunk = proc.output.read(self.proc.buffer_size)
             status = proc.poll()
             if status != None and status != 0:
-                raise ExportProcessError('Command failure:', command, proc)
+                raise EncodeProcessError('Command failure:', command, proc)
             if len(__chunk) == 0:
                 break
             yield __chunk
-            file_out.write(__chunk)
-
-        file_out.close()
-
-    def post_process(self, item_id, source, metadata, ext, 
-                     cache_dir, options=None):
-        """ Post processing : write tags, print infos, etc..."""
-        #self.write_tags()
-        if not options is None:
-            if 'verbose' in self.options and self.options['verbose'] != '0':
-                print self.dest
-                print self.get_file_info()
 
 
 # External functions
index e6553907ea6c4efe10b6e8864e4c1edd1bb82d2c..8e41a8bf981b8bd2416f1ef1a9d5e88647f9b1f8 100644 (file)
@@ -32,21 +32,13 @@ class FlacEncoder(EncoderCore):
     """Defines methods to encode to FLAC"""
 
     implements(IEncoder)
-    
+
     def __init__(self):
-        self.item_id = ''
-        self.source = ''
-        self.metadata = {}
-        self.options = {}
-        self.description = ''
-        self.dest = ''
         self.quality_default = '-5'
-        self.info = []
-        self.buffer_size = 0xFFFF
 
     def get_format(self):
         return 'FLAC'
-    
+
     def get_file_extension(self):
         return 'flac'
 
@@ -54,7 +46,9 @@ class FlacEncoder(EncoderCore):
         return 'application/flac'
 
     def get_description(self):
-        return 'FIXME'
+        return """
+        Free Lossless Audio Codec (FLAC) is a file format for lossless audio data compression. During compression, FLAC does not lose quality from the audio stream, as lossy compression formats such as MP3, AAC, and Vorbis do. Josh Coalson is the primary author of FLAC.
+        """
 
     def get_file_info(self):
         try:
@@ -68,25 +62,6 @@ class FlacEncoder(EncoderCore):
             raise IOError('EncoderError: metaflac is not installed or ' + \
                            'file does not exist.')
 
-    def set_cache_dir(self,path):
-        """Set the directory where cached files should be stored. Does nothing
-        if the encodeer doesn't support caching. 
-       
-        The driver shouldn't assume that this method will always get called. A
-        temporary directory should be used if that's not the case.
-        """
-        self.cache_dir = path
-
-    def decode(self):
-        try:
-            file_name, ext = get_file_name(self.source)
-            dest = self.cache_dir+os.sep+file_name+'.wav'
-            os.system('flac -d -o "'+dest+'" "'+self.source+'"')
-            self.source = dest
-            return dest
-        except:
-            raise IOError('EncoderError: decoder is not compatible.')
-
     def write_tags(self, file):
         media = FLAC(file)
         for tag in self.metadata:
@@ -124,45 +99,27 @@ class FlacEncoder(EncoderCore):
 
         return args
 
-    def process(self, item_id, source, metadata, options=None):
-        self.item_id = item_id
-        self.source = source
-        self.metadata = metadata
-        self.args = self.get_args(options)
-        self.ext = self.get_file_extension()
-        self.args = ' '.join(self.args)
-        self.command = 'sox "%s" -s -q -b 16 -r 44100 -t wav -c2 - | flac -c %s - ' % (self.source, self.args)
-
-        # Pre-proccessing
-        self.dest = self.pre_process(self.item_id,
-                                         self.source,
-                                         self.metadata,
-                                         self.ext,
-                                         self.cache_dir,
-                                         self.options)
-
-        # Processing (streaming + cache writing)
-        stream = self.core_process(self.command, self.buffer_size, self.dest)
-
-        for chunk in stream:
-            pass
-
-        self.write_tags(self.dest)
-        file = open(self.dest,'r')
-        
+    def process(self, source, metadata, options=None):
+        buffer_size = 0xFFFF
+        args = self.get_args(options)
+        args = ' '.join(args)
+        ext = self.get_file_extension()
+        command = 'flac -c %s -' % args
+
+        stream = self.core_process(command, source)
+        temp_file = NamedTemporaryFile(delete=False)
+        for __chunk in stream:
+            temp_file.write(__chunk)
+            temp_file.flush()
+
+        self.write_tags(temp_file)
+
         while True:
-            chunk = file.read(self.buffer_size)
-            if len(chunk) == 0:
+            __chunk = temp_file.read(buffer_size)
+            if len(__chunk) == 0:
                 break
-            yield chunk
+            yield __chunk
 
-        file.close()
+        temp_file.close()
 
-        # Post-proccessing
-        #self.post_process(self.item_id,
-                         #self.source,
-                         #self.metadata,
-                         #self.ext,
-                         #self.cache_dir,
-                         #self.options)
 
index 0b4fa3813c532bb4698aaddb5cc024c4ceee1aea..72136a16fb7b5b3a8d54dd32004ce40ff6303bcf 100644 (file)
@@ -32,17 +32,12 @@ class Mp3Encoder(EencoderCore):
     """Defines methods to encode to MP3"""
 
     implements(IEncoder)
-    
+
     def __init__(self):
-        self.item_id = ''
-        self.metadata = {}
-        self.description = ''
-        self.info = []
-        self.source = ''
-        self.dest = ''
-        self.options = {}
+        self.description = self.description()
+        self.format = self.format()
+        self.mime_type = self.mime_type()
         self.bitrate_default = '192'
-        self.buffer_size = 0xFFFF
         self.dub2id3_dict = {'title': 'TIT2', #title2
                              'creator': 'TCOM', #composer
                              'creator': 'TPE1', #lead
@@ -61,7 +56,7 @@ class Mp3Encoder(EencoderCore):
                              }
     def get_format(self):
         return 'MP3'
-    
+
     def get_file_extension(self):
         return 'mp3'
 
@@ -69,10 +64,9 @@ class Mp3Encoder(EencoderCore):
         return 'audio/mpeg'
 
     def get_description(self):
-        return "FIXME"
-
-    def set_cache_dir(self,path):
-       self.cache_dir = path
+        return """
+        MPEG-1 Audio Layer 3, more commonly referred to as MP3, is a patented digital audio encoding format using a form of lossy data compression. It is a common audio format for consumer audio storage, as well as a de facto standard of digital audio compression for the transfer and playback of music on digital audio players. MP3 is an audio-specific format that was designed by the Moving Picture Experts Group as part of its MPEG-1 standard.
+        """
 
     def get_file_info(self):
         try:
@@ -85,18 +79,10 @@ class Mp3Encoder(EencoderCore):
         except:
             raise IOError('EncoderError: file does not exist.')
 
-    def decode(self):
-        try:
-            os.system('sox "'+self.source+'" -s -q -r 44100 -t wav "' \
-                        +self.cache_dir+os.sep+self.item_id+'"')
-            return self.cache_dir+os.sep+self.item_id+'.wav'
-        except:
-            raise IOError('EncoderError: decoder is not compatible.')
-
     def write_tags(self):
         """Write all ID3v2.4 tags by mapping dub2id3_dict dictionnary with the
             respect of mutagen classes and methods"""
-        from mutagen import id3  
+        from mutagen import id3
         id3 = id3.ID3(self.dest)
         for tag in self.metadata.keys():
             if tag in self.dub2id3_dict.keys():
@@ -112,11 +98,10 @@ class Mp3Encoder(EencoderCore):
         except:
             raise IOError('EncoderError: cannot write tags')
 
-    def get_args(self, options=None):
+    def get_args(self):
         """Get process options and return arguments for the encoder"""
         args = []
-        if not options is None: 
-            self.options = options
+        if not self.options is None:
             if not ( 'verbose' in self.options and self.options['verbose'] != '0' ):
                 args.append('-S')
             if 'mp3_bitrate' in self.options:
@@ -136,34 +121,14 @@ class Mp3Encoder(EencoderCore):
                 args.append('--' + arg + ' "' + value + '"')
         return args
 
-    def process(self, item_id, source, metadata, options=None):
-        self.item_id = item_id
-        self.source = source
+    def process(self, source, metadata, options=None):
         self.metadata = metadata
-        self.args = self.get_args(options)
-        self.ext = self.get_file_extension()
-        self.args = ' '.join(self.args)
-        self.command = 'sox "%s" -q -b 16 -r 44100 -t wav - | lame %s -' % (self.source, self.args)
-        #self.command = 'lame %s "%s" -' % (self.args, self.source)
-
-        # Pre-proccessing
-        self.dest = self.pre_process(self.item_id,
-                                         self.source,
-                                         self.metadata,
-                                         self.ext,
-                                         self.cache_dir,
-                                         self.options)
-
-        # Processing (streaming + cache writing)
-        stream = self.core_process(self.command, self.buffer_size, self.dest)
-        for chunk in stream:
-            yield chunk
-    
-        # Post-proccessing
-        #self.post_process(self.item_id,
-                         #self.source,
-                         #self.metadata,
-                         #self.ext,
-                         #self.cache_dir,
-                         #self.options)
+        self.options = options
+        args = get_args(options)
+        args = ' '.join(args)
+        command = 'lame %s - -' % args
+
+        stream = self.core_process(command, source)
+        for __chunk in stream:
+            yield __chunk
 
index 7437ac75e62fdc1a69e529b3446198f10cef1810..e4a2d8c6cf54a1088259e928948ddd81b5659e97 100644 (file)
@@ -25,42 +25,38 @@ import subprocess
 
 from timeside.encode.core import *
 from timeside.encode.api import IEncoder
-from mutagen.oggvorbis import OggVorbis
 
-class OggEncoder(EncoderCore):
+class OggVorbisEncoder(EncoderCore):
     """Defines methods to encode to OGG Vorbis"""
 
     implements(IEncoder)
-    
+
     def __init__(self):
-        self.item_id = ''
-        self.metadata = {}
-        self.description = ''
-        self.info = []
-        self.source = ''
-        self.dest = ''
-        self.options = {}
+        self.description = self.description()
+        self.format = self.format()
+        self.mime_type = self.mime_type()
         self.bitrate_default = '192'
-        self.buffer_size = 0xFFFF
         self.dub2args_dict = {'creator': 'artist',
                              'relation': 'album'
                              }
-        
-    def get_format(self):
-        return 'OGG'
-    
-    def get_file_extension(self):
+
+    def format(self):
+        return 'OggVorbis'
+
+    def file_extension(self):
         return 'ogg'
 
-    def get_mime_type(self):
+    def mime_type(self):
         return 'application/ogg'
 
-    def get_description(self):
-        return 'FIXME'
+    def description(self):
+        return """
+        Vorbis is a free software / open source project headed by the Xiph.Org Foundation (formerly Xiphophorus company). The project produces an audio format specification and software implementation (codec) for lossy audio compression. Vorbis is most commonly used in conjunction with the Ogg container format and it is therefore often referred to as Ogg Vorbis. (source Wikipedia)
+        """
 
-    def get_file_info(self):
+    def get_file_info(self, file):
         try:
-            file_out1, file_out2 = os.popen4('ogginfo "'+self.dest+'"')
+            file_out1, file_out2 = os.popen4('ogginfo "' + file + '"')
             info = []
             for line in file_out2.readlines():
                 info.append(clean_word(line[:-1]))
@@ -69,28 +65,17 @@ class OggEncoder(EncoderCore):
         except:
             raise IOError('EncoderError: file does not exist.')
 
-    def set_cache_dir(self,path):
-       self.cache_dir = path
-
-    def decode(self):
-        try:
-            os.system('oggdec -o "'+self.cache_dir+os.sep+self.item_id+
-                      '.wav" "'+self.source+'"')
-            return self.cache_dir+os.sep+self.item_id+'.wav'
-        except:
-            raise IOError('EncoderError: decoder is not compatible.')
-
-    def write_tags(self):
-        media = OggVorbis(self.dest)
+    def write_tags(self, file):
+        from mutagen.oggvorbis import OggVorbis
+        media = OggVorbis(file)
         for tag in self.metadata.keys():
             media[tag] = str(self.metadata[tag])
         media.save()
 
-    def get_args(self,options=None):
+    def get_args(self):
         """Get process options and return arguments for the encoder"""
         args = []
-        if not options is None:
-            self.options = options
+        if not self.options is None:
             if not ('verbose' in self.options and self.options['verbose'] != '0'):
                 args.append('-Q ')
             if 'ogg_bitrate' in self.options:
@@ -111,33 +96,15 @@ class OggEncoder(EncoderCore):
                 args.append('-c %s="%s"' % (arg, value))
         return args
 
-    def process(self, item_id, source, metadata, options=None):
-        self.item_id = item_id
-        self.source = source
+    def process(self, source, metadata, options=None):
         self.metadata = metadata
-        self.args = self.get_args(options)
-        self.ext = self.get_file_extension()
-        self.args = ' '.join(self.args)
-        self.command = 'sox "%s" -s -q -b 16 -r 44100 -t wav -c2 - | oggenc %s -' % (self.source, self.args)
-
-        # Pre-proccessing
-        self.dest = self.pre_process(self.item_id,
-                                        self.source,
-                                        self.metadata,
-                                        self.ext,
-                                        self.cache_dir,
-                                        self.options)
-
-        # Processing (streaming + cache writing)
-        stream = self.core_process(self.command, self.buffer_size, self.dest)
-        for chunk in stream:
-            yield chunk
-    
-        # Post-proccessing
-        #self.post_process(self.item_id,
-                        #self.source,
-                        #self.metadata,
-                        #self.ext,
-                        #self.cache_dir,
-                        #self.options)
+        self.options = options
+        args = self.get_args(options)
+        args = ' '.join(args)
+        command = 'oggenc %s -' % args
+
+        stream = self.core_process(command, source)
+        for __chunk in stream:
+            yield __chunk
+
 
index b1e822cacf0b37b3bc5e18765d63c4b3b961ae2a..482a532794429f3739a164efb81a3a97a1e15c08 100644 (file)
@@ -29,20 +29,13 @@ class WavEncoder(EncoderCore):
     """Defines methods to encode to WAV"""
 
     implements(IEncoder)
-    
+
     def __init__(self):
-        self.item_id = ''
-        self.metadata = {}
-        self.description = ''
-        self.info = []
-        self.source = ''
-        self.dest = ''
-        self.options = {}
-        self.buffer_size = 0xFFFF
+        pass
 
     def get_format(self):
         return 'WAV'
-    
+
     def get_file_extension(self):
         return 'wav'
 
@@ -50,7 +43,9 @@ class WavEncoder(EncoderCore):
         return 'audio/x-wav'
 
     def get_description(self):
-        return 'FIXME'
+        return """
+        WAV (or WAVE), short for Waveform audio format, also known as Audio for Windows, is a Microsoft and IBM audio file format standard for storing an audio bitstream on PCs. It is an application of the RIFF bitstream format method for storing data in “chunks”, and thus is also close to the 8SVX and the AIFF format used on Amiga and Macintosh computers, respectively. It is the main format used on Windows systems for raw and typically uncompressed audio. The usual bitstream encoding is the Pulse Code Modulation (PCM) format.
+        """
 
     def get_file_info(self):
         try:
@@ -63,96 +58,11 @@ class WavEncoder(EncoderCore):
         except:
             raise IOError('EncoderError: wavinfo id not installed or file does not exist.')
 
-    def set_cache_dir(self,path):
-        self.cache_dir = path
-
-    def decode(self):
-        try:
-            file_name, ext = get_file_name(self.source)
-            dest = self.cache_dir+os.sep+file_name+'.wav'
-            os.system('sox "'+self.source+'" -s -r 44100 -t wav -c2 "'+ \
-                      dest+'.wav"')
-            self.source = dest
-            return dest
-        except:
-            raise IOError('EncoderError: decoder is not compatible.')
-
-    def write_tags(self):
-        # Create metadata XML file !
-        self.write_metadata_xml(self.dest+'.xml')
-    
-    def create_md5_key(self):
-        """ Create the md5 keys of the dest """
-        try:
-            os.system('md5sum -b "'+self.dest+'" >"'+self.dest+'.md5"')
-        except:
-            raise IOError('EncoderError: cannot create the md5 key.')
-    
-    def create_par_key(self):
-        """ Create the par2 keys of the dest """
-        args = 'c -n1 '
-        if 'verbose' in self.options and self.options['verbose'] != '0':
-            args = args
-        else:
-            args = args + '-q -q '
-
-        try:
-            os.system('par2 '+args+' "'+self.dest+'"')
-        except:
-            raise IOError('EncoderError: cannot create the par2 key.')
-
-    def process(self, item_id, source, metadata, options=None):
-        self.item_id = item_id
-        self.source = source
+    def process(self, source, metadata, options=None):
         self.metadata = metadata
-        self.options = {}
-
-        if not options is None:
-            self.options = options
-
-        # Pre-proccessing
-        self.ext = self.get_file_extension()
-        self.dest = self.pre_process(self.item_id,
-                                        self.source,
-                                        self.metadata,
-                                        self.ext,
-                                        self.cache_dir,
-                                        self.options)
-
-        # Initializing
-        file_in = open(self.source,'rb')
-        file_out = open(self.dest,'w')
-
-        # Core Processing
-        while True:
-            chunk = file_in.read(self.buffer_size)
-            if len(chunk) == 0:
-                break
-            yield chunk
-            file_out.write(chunk)
-
-        file_in.close()
-        file_out.close()
-
-        # Create the md5 key
-        #if 'md5' in self.metadata and self.metadata['md5']:
-        self.create_md5_key()
-
-        # Create the par2 key
-        #if 'par2' in self.metadata and self.metadata['par2']:
-        #self.create_par_key()
-
-        # Pre-proccessing
-        self.post_process(self.item_id,
-                        self.source,
-                        self.metadata,
-                        self.ext,
-                        self.cache_dir,
-                        self.options)
-
-
-
-            #if self.compare_md5_key():
-            #os.system('cp -a "'+self.source+'" "'+ self.dest+'"')
-            #print 'COPIED'
+        self.options = options
+        command = 'sox - -s -q -b 16 -r 44100 -t wav -c2 -'
 
+        stream = self.core_process(command, source)
+        for __chunk in stream:
+            yield __chunk