]> git.parisson.com Git - timeside.git/commitdiff
change export to decode/encode
authoryomguy <yomguy@parisson.com>
Wed, 7 Oct 2009 13:17:00 +0000 (13:17 +0000)
committeryomguy <yomguy@parisson.com>
Wed, 7 Oct 2009 13:17:00 +0000 (13:17 +0000)
21 files changed:
decode/__init__.py [new file with mode: 0644]
decode/api.py [new file with mode: 0644]
decode/core.py [new file with mode: 0644]
decode/flac.py [new file with mode: 0644]
decode/mp3.py [new file with mode: 0644]
decode/ogg.py [new file with mode: 0644]
decode/wav.py [new file with mode: 0644]
encode/__init__.py [new file with mode: 0644]
encode/api.py [new file with mode: 0644]
encode/core.py [new file with mode: 0644]
encode/flac.py [new file with mode: 0644]
encode/mp3.py [new file with mode: 0644]
encode/ogg.py [new file with mode: 0644]
encode/wav.py [new file with mode: 0644]
export/__init__.py [deleted file]
export/api.py [deleted file]
export/core.py [deleted file]
export/flac.py [deleted file]
export/mp3.py [deleted file]
export/ogg.py [deleted file]
export/wav.py [deleted file]

diff --git a/decode/__init__.py b/decode/__init__.py
new file mode 100644 (file)
index 0000000..06cb43a
--- /dev/null
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+from timeside.export.api import *
+from timeside.export.core import *
+from timeside.export.ogg import *
+from timeside.export.flac import *
+from timeside.export.wav import *
+from timeside.export.mp3 import *
\ No newline at end of file
diff --git a/decode/api.py b/decode/api.py
new file mode 100644 (file)
index 0000000..7ee861c
--- /dev/null
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007-2009 Parisson
+# Copyright (c) 2007 Olivier Guilyardi <olivier@samalyse.com>
+# Copyright (c) 2007-2009 Guillaume Pellerin <pellerin@parisson.com>
+#
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+from timeside.core import Interface, TimeSideError
+
+class IExporter(Interface):
+    """Export 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 
+    # you'll obviously want to include this extra argument.
+
+    def get_format():
+        """Return the export/encoding format as a short string 
+        Example: "MP3", "OGG", "AVI", ...
+        """
+   
+    def get_description():
+        """Return a string describing what this export 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():
+        """Return the filename extension corresponding to this export format"""
+
+    def get_mime_type():
+        """Return the mime type corresponding to this export format"""
+
+    def set_cache_dir(path):
+        """Set the directory where cached files should be stored. Does nothing
+        if the exporter 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):
+        """Perform the exporting process and return the absolute path 
+        to the resulting file.
+
+        item_id is the media item id that uniquely identifies this audio/video
+        resource
+
+        source is the audio/video source file absolute path. For audio that
+        should be a WAV file
+
+        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 export drivers implement some sort of
+        cache instead of re-encoding each time process() is called.
+
+        It should be possible to make subsequent calls to process() with
+        different items, using the same driver instance.
+        """
+
+class ExportProcessError(TimeSideError):
+
+    def __init__(self, message, command, subprocess):
+        self.message = message
+        self.command = str(command)
+        self.subprocess = subprocess
+
+    def __str__(self):
+        if self.subprocess.stderr != None:
+            error = self.subprocess.stderr.read()
+        else:
+            error = ''
+        return "%s ; command: %s; error: %s" % (self.message,
+                                                self.command,
+                                                error)
diff --git a/decode/core.py b/decode/core.py
new file mode 100644 (file)
index 0000000..6f83356
--- /dev/null
@@ -0,0 +1,277 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import re
+import md5
+import string
+import subprocess
+import mutagen
+
+from timeside.export import *
+from timeside.core import *
+import xml.dom.minidom
+import xml.dom.ext
+
+class ExporterCore(Component):
+    """Defines the main parts of the exporting 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
+
+    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('ExporterError: 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('ExporterError: cannot check the md5 key.')
+    
+    def get_file_info(self):
+        """ Return the list of informations of the dest """
+        return self.export.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('ExporterError: 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 export 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 export directory
+        self.ext = self.get_file_extension()
+        export_dir = os.path.join(self.cache_dir,self.ext)
+
+        if not os.path.exists(export_dir):
+            export_dir_split = export_dir.split(os.sep)
+            path = os.sep + export_dir_split[0]
+            for _dir in export_dir_split[1:]:
+                path = os.path.join(path,_dir)
+                if not os.path.exists(path):
+                    os.mkdir(path)
+        else:
+            path = export_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')
+
+        proc = subprocess.Popen(command.encode('utf-8'),
+                    shell = True,
+                    bufsize = buffer_size,
+                    stdin = subprocess.PIPE,
+                    stdout = subprocess.PIPE,
+                    close_fds = True)
+
+        # Core processing
+        while True:
+            __chunk = proc.stdout.read(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
+
+def get_type(value):
+    """ Return a String with the type of value """
+    types = {bool : 'bool', int : 'int', str : 'str'}
+    # 'bool' type must be placed *before* 'int' type, otherwise booleans are
+    # detected as integers
+    for type in types.keys():
+        if isinstance(value, type) :
+            return types[type]
+    raise TypeError(str(value) + ' has an unsupported type')
+
+def get_cast(value, type) :
+    """ Return value, casted into type """
+    if type == 'bool' :
+        if value == 'True' :
+            return True
+        return False
+    elif type == 'int' :
+        return int(value)
+    elif type == 'str' :
+        return str(value)
+    raise TypeError(type + ' is an unsupported type')
+
+def get_file_mime_type(path):
+    """ Return the mime type of a file """
+    try:
+        file_out1, file_out2 = os.popen4('file -i "'+path+'"')
+        for line in file_out2.readlines():
+            line_split = line.split(': ')
+            mime = line_split[len(line_split)-1]
+            return mime[:len(mime)-1]
+    except:
+        raise IOError('ExporterError: path does not exist.')
+
+def get_file_type_desc(path):
+    """ Return the type of a file given by the 'file' command """
+    try:
+        file_out1, file_out2 = os.popen4('file "'+path+'"')
+        for line in file_out2.readlines():
+            description = line.split(': ')
+            description = description[1].split(', ')
+            return description
+    except:
+        raise IOError('ExporterError: path does not exist.')
+
+def iswav(path):
+    """ Tell if path is a WAV """
+    try:
+        mime = get_file_mime_type(path)
+        return mime == 'audio/x-wav'
+    except:
+        raise IOError('ExporterError: path does not exist.')
+
+def iswav16(path):
+    """ Tell if path is a 16 bit WAV """
+    try:
+        file_type_desc = get_file_type_desc(path)
+        return iswav(path) and '16 bit' in file_type_desc
+    except:
+        raise IOError('ExporterError: path does not exist.')
+
+def get_file_name(path):
+    """ Return the file name targeted in the path """
+    return os.path.split(path)[1]
+
+def split_file_name(file):
+    """ Return main file name and its extension """
+    try:
+        return os.path.splitext(file)
+    except:
+        raise IOError('ExporterError: path does not exist.')
+
+def clean_word(word) :
+    """ Return the word without excessive blank spaces, underscores and
+    characters causing problem to exporters"""
+    word = re.sub("^[^\w]+","",word)    #trim the beginning
+    word = re.sub("[^\w]+$","",word)    #trim the end
+    word = re.sub("_+","_",word)        #squeeze continuous _ to one _
+    word = re.sub("^[^\w]+","",word)    #trim the beginning _
+    #word = string.replace(word,' ','_')
+    #word = string.capitalize(word)
+    dict = '&[];"*:,'
+    for letter in dict:
+        word = string.replace(word,letter,'_')
+    return word
+
+def recover_par_key(path):
+    """ Recover a file with par2 key """
+    os.system('par2 r "'+path+'"')
+
+def verify_par_key(path):
+    """ Verify a par2 key """
+    os.system('par2 v "'+path+'.par2"')
+
+
diff --git a/decode/flac.py b/decode/flac.py
new file mode 100644 (file)
index 0000000..60f2f92
--- /dev/null
@@ -0,0 +1,168 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import string
+import subprocess
+
+from timeside.export.core import *
+from timeside.export.api import IExporter
+from mutagen.flac import FLAC
+from tempfile import NamedTemporaryFile
+
+class FlacExporter(ExporterCore):
+    """Defines methods to export to FLAC"""
+
+    implements(IExporter)
+    
+    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'
+
+    def get_mime_type(self):
+        return 'application/flac'
+
+    def get_description(self):
+        return 'FIXME'
+
+    def get_file_info(self):
+        try:
+            file1, file2 = os.popen4('metaflac --list "'+self.dest+'"')
+            info = []
+            for line in file2.readlines():
+                info.append(clean_word(line[:-1]))
+            self.info = info
+            return self.info
+        except:
+            raise IOError('ExporterError: 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 exporter 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('ExporterError: 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('ExporterError: 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)
+
diff --git a/decode/mp3.py b/decode/mp3.py
new file mode 100644 (file)
index 0000000..e75f15f
--- /dev/null
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007 Parisson SARL
+# Copyright (c) 2006-2007 Guillaume Pellerin <pellerin@parisson.com>
+
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import string
+import subprocess
+
+from timeside.export.core import *
+from timeside.export.api import IExporter
+#from mutagen.id3 import *
+
+class Mp3Exporter(ExporterCore):
+    """Defines methods to export to MP3"""
+
+    implements(IExporter)
+    
+    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):
+        return 'MP3'
+    
+    def get_file_extension(self):
+        return 'mp3'
+
+    def get_mime_type(self):
+        return 'audio/mpeg'
+
+    def get_description(self):
+        return "FIXME"
+
+    def set_cache_dir(self,path):
+       self.cache_dir = path
+
+    def get_file_info(self):
+        try:
+            file_out1, file_out2 = os.popen4('mp3info "'+self.dest+'"')
+            info = []
+            for line in file_out2.readlines():
+                info.append(clean_word(line[:-1]))
+            self.info = info
+            return self.info
+        except:
+            raise IOError('ExporterError: 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('ExporterError: 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('ExporterError: cannot tag "'+tag+'"')
+        try:
+            id3.save()
+        except:
+            raise IOError('ExporterError: 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)
+
diff --git a/decode/ogg.py b/decode/ogg.py
new file mode 100644 (file)
index 0000000..270449a
--- /dev/null
@@ -0,0 +1,143 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import string
+import subprocess
+
+from timeside.export.core import *
+from timeside.export.api import IExporter
+from mutagen.oggvorbis import OggVorbis
+
+class OggExporter(ExporterCore):
+    """Defines methods to export to OGG Vorbis"""
+
+    implements(IExporter)
+    
+    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.dub2args_dict = {'creator': 'artist',
+                             'relation': 'album'
+                             }
+        
+    def get_format(self):
+        return 'OGG'
+    
+    def get_file_extension(self):
+        return 'ogg'
+
+    def get_mime_type(self):
+        return 'application/ogg'
+
+    def get_description(self):
+        return 'FIXME'
+
+    def get_file_info(self):
+        try:
+            file_out1, file_out2 = os.popen4('ogginfo "'+self.dest+'"')
+            info = []
+            for line in file_out2.readlines():
+                info.append(clean_word(line[:-1]))
+            self.info = info
+            return self.info
+        except:
+            raise IOError('ExporterError: 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('ExporterError: 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)
+
diff --git a/decode/wav.py b/decode/wav.py
new file mode 100644 (file)
index 0000000..c98a172
--- /dev/null
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import string
+
+from timeside.export.core import *
+from timeside.export.api import IExporter
+
+class WavExporter(ExporterCore):
+    """Defines methods to export to WAV"""
+
+    implements(IExporter)
+    
+    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):
+        return 'WAV'
+    
+    def get_file_extension(self):
+        return 'wav'
+
+    def get_mime_type(self):
+        return 'audio/x-wav'
+
+    def get_description(self):
+        return 'FIXME'
+
+    def get_file_info(self):
+        try:
+            file1, file2 = os.popen4('wavinfo "'+self.dest+'"')
+            info = []
+            for line in file2.readlines():
+                info.append(clean_word(line[:-1]))
+            self.info = info
+            return self.info
+        except:
+            raise IOError('ExporterError: 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('ExporterError: 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('ExporterError: 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('ExporterError: 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'
+
diff --git a/encode/__init__.py b/encode/__init__.py
new file mode 100644 (file)
index 0000000..06cb43a
--- /dev/null
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+from timeside.export.api import *
+from timeside.export.core import *
+from timeside.export.ogg import *
+from timeside.export.flac import *
+from timeside.export.wav import *
+from timeside.export.mp3 import *
\ No newline at end of file
diff --git a/encode/api.py b/encode/api.py
new file mode 100644 (file)
index 0000000..7ee861c
--- /dev/null
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007-2009 Parisson
+# Copyright (c) 2007 Olivier Guilyardi <olivier@samalyse.com>
+# Copyright (c) 2007-2009 Guillaume Pellerin <pellerin@parisson.com>
+#
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+from timeside.core import Interface, TimeSideError
+
+class IExporter(Interface):
+    """Export 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 
+    # you'll obviously want to include this extra argument.
+
+    def get_format():
+        """Return the export/encoding format as a short string 
+        Example: "MP3", "OGG", "AVI", ...
+        """
+   
+    def get_description():
+        """Return a string describing what this export 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():
+        """Return the filename extension corresponding to this export format"""
+
+    def get_mime_type():
+        """Return the mime type corresponding to this export format"""
+
+    def set_cache_dir(path):
+        """Set the directory where cached files should be stored. Does nothing
+        if the exporter 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):
+        """Perform the exporting process and return the absolute path 
+        to the resulting file.
+
+        item_id is the media item id that uniquely identifies this audio/video
+        resource
+
+        source is the audio/video source file absolute path. For audio that
+        should be a WAV file
+
+        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 export drivers implement some sort of
+        cache instead of re-encoding each time process() is called.
+
+        It should be possible to make subsequent calls to process() with
+        different items, using the same driver instance.
+        """
+
+class ExportProcessError(TimeSideError):
+
+    def __init__(self, message, command, subprocess):
+        self.message = message
+        self.command = str(command)
+        self.subprocess = subprocess
+
+    def __str__(self):
+        if self.subprocess.stderr != None:
+            error = self.subprocess.stderr.read()
+        else:
+            error = ''
+        return "%s ; command: %s; error: %s" % (self.message,
+                                                self.command,
+                                                error)
diff --git a/encode/core.py b/encode/core.py
new file mode 100644 (file)
index 0000000..6f83356
--- /dev/null
@@ -0,0 +1,277 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import re
+import md5
+import string
+import subprocess
+import mutagen
+
+from timeside.export import *
+from timeside.core import *
+import xml.dom.minidom
+import xml.dom.ext
+
+class ExporterCore(Component):
+    """Defines the main parts of the exporting 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
+
+    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('ExporterError: 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('ExporterError: cannot check the md5 key.')
+    
+    def get_file_info(self):
+        """ Return the list of informations of the dest """
+        return self.export.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('ExporterError: 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 export 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 export directory
+        self.ext = self.get_file_extension()
+        export_dir = os.path.join(self.cache_dir,self.ext)
+
+        if not os.path.exists(export_dir):
+            export_dir_split = export_dir.split(os.sep)
+            path = os.sep + export_dir_split[0]
+            for _dir in export_dir_split[1:]:
+                path = os.path.join(path,_dir)
+                if not os.path.exists(path):
+                    os.mkdir(path)
+        else:
+            path = export_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')
+
+        proc = subprocess.Popen(command.encode('utf-8'),
+                    shell = True,
+                    bufsize = buffer_size,
+                    stdin = subprocess.PIPE,
+                    stdout = subprocess.PIPE,
+                    close_fds = True)
+
+        # Core processing
+        while True:
+            __chunk = proc.stdout.read(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
+
+def get_type(value):
+    """ Return a String with the type of value """
+    types = {bool : 'bool', int : 'int', str : 'str'}
+    # 'bool' type must be placed *before* 'int' type, otherwise booleans are
+    # detected as integers
+    for type in types.keys():
+        if isinstance(value, type) :
+            return types[type]
+    raise TypeError(str(value) + ' has an unsupported type')
+
+def get_cast(value, type) :
+    """ Return value, casted into type """
+    if type == 'bool' :
+        if value == 'True' :
+            return True
+        return False
+    elif type == 'int' :
+        return int(value)
+    elif type == 'str' :
+        return str(value)
+    raise TypeError(type + ' is an unsupported type')
+
+def get_file_mime_type(path):
+    """ Return the mime type of a file """
+    try:
+        file_out1, file_out2 = os.popen4('file -i "'+path+'"')
+        for line in file_out2.readlines():
+            line_split = line.split(': ')
+            mime = line_split[len(line_split)-1]
+            return mime[:len(mime)-1]
+    except:
+        raise IOError('ExporterError: path does not exist.')
+
+def get_file_type_desc(path):
+    """ Return the type of a file given by the 'file' command """
+    try:
+        file_out1, file_out2 = os.popen4('file "'+path+'"')
+        for line in file_out2.readlines():
+            description = line.split(': ')
+            description = description[1].split(', ')
+            return description
+    except:
+        raise IOError('ExporterError: path does not exist.')
+
+def iswav(path):
+    """ Tell if path is a WAV """
+    try:
+        mime = get_file_mime_type(path)
+        return mime == 'audio/x-wav'
+    except:
+        raise IOError('ExporterError: path does not exist.')
+
+def iswav16(path):
+    """ Tell if path is a 16 bit WAV """
+    try:
+        file_type_desc = get_file_type_desc(path)
+        return iswav(path) and '16 bit' in file_type_desc
+    except:
+        raise IOError('ExporterError: path does not exist.')
+
+def get_file_name(path):
+    """ Return the file name targeted in the path """
+    return os.path.split(path)[1]
+
+def split_file_name(file):
+    """ Return main file name and its extension """
+    try:
+        return os.path.splitext(file)
+    except:
+        raise IOError('ExporterError: path does not exist.')
+
+def clean_word(word) :
+    """ Return the word without excessive blank spaces, underscores and
+    characters causing problem to exporters"""
+    word = re.sub("^[^\w]+","",word)    #trim the beginning
+    word = re.sub("[^\w]+$","",word)    #trim the end
+    word = re.sub("_+","_",word)        #squeeze continuous _ to one _
+    word = re.sub("^[^\w]+","",word)    #trim the beginning _
+    #word = string.replace(word,' ','_')
+    #word = string.capitalize(word)
+    dict = '&[];"*:,'
+    for letter in dict:
+        word = string.replace(word,letter,'_')
+    return word
+
+def recover_par_key(path):
+    """ Recover a file with par2 key """
+    os.system('par2 r "'+path+'"')
+
+def verify_par_key(path):
+    """ Verify a par2 key """
+    os.system('par2 v "'+path+'.par2"')
+
+
diff --git a/encode/flac.py b/encode/flac.py
new file mode 100644 (file)
index 0000000..60f2f92
--- /dev/null
@@ -0,0 +1,168 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import string
+import subprocess
+
+from timeside.export.core import *
+from timeside.export.api import IExporter
+from mutagen.flac import FLAC
+from tempfile import NamedTemporaryFile
+
+class FlacExporter(ExporterCore):
+    """Defines methods to export to FLAC"""
+
+    implements(IExporter)
+    
+    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'
+
+    def get_mime_type(self):
+        return 'application/flac'
+
+    def get_description(self):
+        return 'FIXME'
+
+    def get_file_info(self):
+        try:
+            file1, file2 = os.popen4('metaflac --list "'+self.dest+'"')
+            info = []
+            for line in file2.readlines():
+                info.append(clean_word(line[:-1]))
+            self.info = info
+            return self.info
+        except:
+            raise IOError('ExporterError: 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 exporter 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('ExporterError: 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('ExporterError: 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)
+
diff --git a/encode/mp3.py b/encode/mp3.py
new file mode 100644 (file)
index 0000000..e75f15f
--- /dev/null
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007 Parisson SARL
+# Copyright (c) 2006-2007 Guillaume Pellerin <pellerin@parisson.com>
+
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import string
+import subprocess
+
+from timeside.export.core import *
+from timeside.export.api import IExporter
+#from mutagen.id3 import *
+
+class Mp3Exporter(ExporterCore):
+    """Defines methods to export to MP3"""
+
+    implements(IExporter)
+    
+    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):
+        return 'MP3'
+    
+    def get_file_extension(self):
+        return 'mp3'
+
+    def get_mime_type(self):
+        return 'audio/mpeg'
+
+    def get_description(self):
+        return "FIXME"
+
+    def set_cache_dir(self,path):
+       self.cache_dir = path
+
+    def get_file_info(self):
+        try:
+            file_out1, file_out2 = os.popen4('mp3info "'+self.dest+'"')
+            info = []
+            for line in file_out2.readlines():
+                info.append(clean_word(line[:-1]))
+            self.info = info
+            return self.info
+        except:
+            raise IOError('ExporterError: 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('ExporterError: 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('ExporterError: cannot tag "'+tag+'"')
+        try:
+            id3.save()
+        except:
+            raise IOError('ExporterError: 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)
+
diff --git a/encode/ogg.py b/encode/ogg.py
new file mode 100644 (file)
index 0000000..270449a
--- /dev/null
@@ -0,0 +1,143 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import string
+import subprocess
+
+from timeside.export.core import *
+from timeside.export.api import IExporter
+from mutagen.oggvorbis import OggVorbis
+
+class OggExporter(ExporterCore):
+    """Defines methods to export to OGG Vorbis"""
+
+    implements(IExporter)
+    
+    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.dub2args_dict = {'creator': 'artist',
+                             'relation': 'album'
+                             }
+        
+    def get_format(self):
+        return 'OGG'
+    
+    def get_file_extension(self):
+        return 'ogg'
+
+    def get_mime_type(self):
+        return 'application/ogg'
+
+    def get_description(self):
+        return 'FIXME'
+
+    def get_file_info(self):
+        try:
+            file_out1, file_out2 = os.popen4('ogginfo "'+self.dest+'"')
+            info = []
+            for line in file_out2.readlines():
+                info.append(clean_word(line[:-1]))
+            self.info = info
+            return self.info
+        except:
+            raise IOError('ExporterError: 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('ExporterError: 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)
+
diff --git a/encode/wav.py b/encode/wav.py
new file mode 100644 (file)
index 0000000..c98a172
--- /dev/null
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import string
+
+from timeside.export.core import *
+from timeside.export.api import IExporter
+
+class WavExporter(ExporterCore):
+    """Defines methods to export to WAV"""
+
+    implements(IExporter)
+    
+    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):
+        return 'WAV'
+    
+    def get_file_extension(self):
+        return 'wav'
+
+    def get_mime_type(self):
+        return 'audio/x-wav'
+
+    def get_description(self):
+        return 'FIXME'
+
+    def get_file_info(self):
+        try:
+            file1, file2 = os.popen4('wavinfo "'+self.dest+'"')
+            info = []
+            for line in file2.readlines():
+                info.append(clean_word(line[:-1]))
+            self.info = info
+            return self.info
+        except:
+            raise IOError('ExporterError: 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('ExporterError: 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('ExporterError: 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('ExporterError: 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'
+
diff --git a/export/__init__.py b/export/__init__.py
deleted file mode 100644 (file)
index 06cb43a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from timeside.export.api import *
-from timeside.export.core import *
-from timeside.export.ogg import *
-from timeside.export.flac import *
-from timeside.export.wav import *
-from timeside.export.mp3 import *
\ No newline at end of file
diff --git a/export/api.py b/export/api.py
deleted file mode 100644 (file)
index 7ee861c..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2007-2009 Parisson
-# Copyright (c) 2007 Olivier Guilyardi <olivier@samalyse.com>
-# Copyright (c) 2007-2009 Guillaume Pellerin <pellerin@parisson.com>
-#
-# This file is part of TimeSide.
-
-# TimeSide is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-
-# TimeSide is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
-
-from timeside.core import Interface, TimeSideError
-
-class IExporter(Interface):
-    """Export 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 
-    # you'll obviously want to include this extra argument.
-
-    def get_format():
-        """Return the export/encoding format as a short string 
-        Example: "MP3", "OGG", "AVI", ...
-        """
-   
-    def get_description():
-        """Return a string describing what this export 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():
-        """Return the filename extension corresponding to this export format"""
-
-    def get_mime_type():
-        """Return the mime type corresponding to this export format"""
-
-    def set_cache_dir(path):
-        """Set the directory where cached files should be stored. Does nothing
-        if the exporter 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):
-        """Perform the exporting process and return the absolute path 
-        to the resulting file.
-
-        item_id is the media item id that uniquely identifies this audio/video
-        resource
-
-        source is the audio/video source file absolute path. For audio that
-        should be a WAV file
-
-        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 export drivers implement some sort of
-        cache instead of re-encoding each time process() is called.
-
-        It should be possible to make subsequent calls to process() with
-        different items, using the same driver instance.
-        """
-
-class ExportProcessError(TimeSideError):
-
-    def __init__(self, message, command, subprocess):
-        self.message = message
-        self.command = str(command)
-        self.subprocess = subprocess
-
-    def __str__(self):
-        if self.subprocess.stderr != None:
-            error = self.subprocess.stderr.read()
-        else:
-            error = ''
-        return "%s ; command: %s; error: %s" % (self.message,
-                                                self.command,
-                                                error)
diff --git a/export/core.py b/export/core.py
deleted file mode 100644 (file)
index 6f83356..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# This file is part of TimeSide.
-
-# TimeSide is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-
-# TimeSide is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-import re
-import md5
-import string
-import subprocess
-import mutagen
-
-from timeside.export import *
-from timeside.core import *
-import xml.dom.minidom
-import xml.dom.ext
-
-class ExporterCore(Component):
-    """Defines the main parts of the exporting 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
-
-    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('ExporterError: 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('ExporterError: cannot check the md5 key.')
-    
-    def get_file_info(self):
-        """ Return the list of informations of the dest """
-        return self.export.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('ExporterError: 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 export 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 export directory
-        self.ext = self.get_file_extension()
-        export_dir = os.path.join(self.cache_dir,self.ext)
-
-        if not os.path.exists(export_dir):
-            export_dir_split = export_dir.split(os.sep)
-            path = os.sep + export_dir_split[0]
-            for _dir in export_dir_split[1:]:
-                path = os.path.join(path,_dir)
-                if not os.path.exists(path):
-                    os.mkdir(path)
-        else:
-            path = export_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')
-
-        proc = subprocess.Popen(command.encode('utf-8'),
-                    shell = True,
-                    bufsize = buffer_size,
-                    stdin = subprocess.PIPE,
-                    stdout = subprocess.PIPE,
-                    close_fds = True)
-
-        # Core processing
-        while True:
-            __chunk = proc.stdout.read(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
-
-def get_type(value):
-    """ Return a String with the type of value """
-    types = {bool : 'bool', int : 'int', str : 'str'}
-    # 'bool' type must be placed *before* 'int' type, otherwise booleans are
-    # detected as integers
-    for type in types.keys():
-        if isinstance(value, type) :
-            return types[type]
-    raise TypeError(str(value) + ' has an unsupported type')
-
-def get_cast(value, type) :
-    """ Return value, casted into type """
-    if type == 'bool' :
-        if value == 'True' :
-            return True
-        return False
-    elif type == 'int' :
-        return int(value)
-    elif type == 'str' :
-        return str(value)
-    raise TypeError(type + ' is an unsupported type')
-
-def get_file_mime_type(path):
-    """ Return the mime type of a file """
-    try:
-        file_out1, file_out2 = os.popen4('file -i "'+path+'"')
-        for line in file_out2.readlines():
-            line_split = line.split(': ')
-            mime = line_split[len(line_split)-1]
-            return mime[:len(mime)-1]
-    except:
-        raise IOError('ExporterError: path does not exist.')
-
-def get_file_type_desc(path):
-    """ Return the type of a file given by the 'file' command """
-    try:
-        file_out1, file_out2 = os.popen4('file "'+path+'"')
-        for line in file_out2.readlines():
-            description = line.split(': ')
-            description = description[1].split(', ')
-            return description
-    except:
-        raise IOError('ExporterError: path does not exist.')
-
-def iswav(path):
-    """ Tell if path is a WAV """
-    try:
-        mime = get_file_mime_type(path)
-        return mime == 'audio/x-wav'
-    except:
-        raise IOError('ExporterError: path does not exist.')
-
-def iswav16(path):
-    """ Tell if path is a 16 bit WAV """
-    try:
-        file_type_desc = get_file_type_desc(path)
-        return iswav(path) and '16 bit' in file_type_desc
-    except:
-        raise IOError('ExporterError: path does not exist.')
-
-def get_file_name(path):
-    """ Return the file name targeted in the path """
-    return os.path.split(path)[1]
-
-def split_file_name(file):
-    """ Return main file name and its extension """
-    try:
-        return os.path.splitext(file)
-    except:
-        raise IOError('ExporterError: path does not exist.')
-
-def clean_word(word) :
-    """ Return the word without excessive blank spaces, underscores and
-    characters causing problem to exporters"""
-    word = re.sub("^[^\w]+","",word)    #trim the beginning
-    word = re.sub("[^\w]+$","",word)    #trim the end
-    word = re.sub("_+","_",word)        #squeeze continuous _ to one _
-    word = re.sub("^[^\w]+","",word)    #trim the beginning _
-    #word = string.replace(word,' ','_')
-    #word = string.capitalize(word)
-    dict = '&[];"*:,'
-    for letter in dict:
-        word = string.replace(word,letter,'_')
-    return word
-
-def recover_par_key(path):
-    """ Recover a file with par2 key """
-    os.system('par2 r "'+path+'"')
-
-def verify_par_key(path):
-    """ Verify a par2 key """
-    os.system('par2 v "'+path+'.par2"')
-
-
diff --git a/export/flac.py b/export/flac.py
deleted file mode 100644 (file)
index 60f2f92..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# This file is part of TimeSide.
-
-# TimeSide is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-
-# TimeSide is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-import string
-import subprocess
-
-from timeside.export.core import *
-from timeside.export.api import IExporter
-from mutagen.flac import FLAC
-from tempfile import NamedTemporaryFile
-
-class FlacExporter(ExporterCore):
-    """Defines methods to export to FLAC"""
-
-    implements(IExporter)
-    
-    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'
-
-    def get_mime_type(self):
-        return 'application/flac'
-
-    def get_description(self):
-        return 'FIXME'
-
-    def get_file_info(self):
-        try:
-            file1, file2 = os.popen4('metaflac --list "'+self.dest+'"')
-            info = []
-            for line in file2.readlines():
-                info.append(clean_word(line[:-1]))
-            self.info = info
-            return self.info
-        except:
-            raise IOError('ExporterError: 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 exporter 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('ExporterError: 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('ExporterError: 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)
-
diff --git a/export/mp3.py b/export/mp3.py
deleted file mode 100644 (file)
index e75f15f..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2007 Parisson SARL
-# Copyright (c) 2006-2007 Guillaume Pellerin <pellerin@parisson.com>
-
-# This file is part of TimeSide.
-
-# TimeSide is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-
-# TimeSide is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-import string
-import subprocess
-
-from timeside.export.core import *
-from timeside.export.api import IExporter
-#from mutagen.id3 import *
-
-class Mp3Exporter(ExporterCore):
-    """Defines methods to export to MP3"""
-
-    implements(IExporter)
-    
-    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):
-        return 'MP3'
-    
-    def get_file_extension(self):
-        return 'mp3'
-
-    def get_mime_type(self):
-        return 'audio/mpeg'
-
-    def get_description(self):
-        return "FIXME"
-
-    def set_cache_dir(self,path):
-       self.cache_dir = path
-
-    def get_file_info(self):
-        try:
-            file_out1, file_out2 = os.popen4('mp3info "'+self.dest+'"')
-            info = []
-            for line in file_out2.readlines():
-                info.append(clean_word(line[:-1]))
-            self.info = info
-            return self.info
-        except:
-            raise IOError('ExporterError: 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('ExporterError: 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('ExporterError: cannot tag "'+tag+'"')
-        try:
-            id3.save()
-        except:
-            raise IOError('ExporterError: 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)
-
diff --git a/export/ogg.py b/export/ogg.py
deleted file mode 100644 (file)
index 270449a..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# This file is part of TimeSide.
-
-# TimeSide is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-
-# TimeSide is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-import string
-import subprocess
-
-from timeside.export.core import *
-from timeside.export.api import IExporter
-from mutagen.oggvorbis import OggVorbis
-
-class OggExporter(ExporterCore):
-    """Defines methods to export to OGG Vorbis"""
-
-    implements(IExporter)
-    
-    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.dub2args_dict = {'creator': 'artist',
-                             'relation': 'album'
-                             }
-        
-    def get_format(self):
-        return 'OGG'
-    
-    def get_file_extension(self):
-        return 'ogg'
-
-    def get_mime_type(self):
-        return 'application/ogg'
-
-    def get_description(self):
-        return 'FIXME'
-
-    def get_file_info(self):
-        try:
-            file_out1, file_out2 = os.popen4('ogginfo "'+self.dest+'"')
-            info = []
-            for line in file_out2.readlines():
-                info.append(clean_word(line[:-1]))
-            self.info = info
-            return self.info
-        except:
-            raise IOError('ExporterError: 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('ExporterError: 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)
-
diff --git a/export/wav.py b/export/wav.py
deleted file mode 100644 (file)
index c98a172..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-
-# This file is part of TimeSide.
-
-# TimeSide is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-
-# TimeSide is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-import string
-
-from timeside.export.core import *
-from timeside.export.api import IExporter
-
-class WavExporter(ExporterCore):
-    """Defines methods to export to WAV"""
-
-    implements(IExporter)
-    
-    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):
-        return 'WAV'
-    
-    def get_file_extension(self):
-        return 'wav'
-
-    def get_mime_type(self):
-        return 'audio/x-wav'
-
-    def get_description(self):
-        return 'FIXME'
-
-    def get_file_info(self):
-        try:
-            file1, file2 = os.popen4('wavinfo "'+self.dest+'"')
-            info = []
-            for line in file2.readlines():
-                info.append(clean_word(line[:-1]))
-            self.info = info
-            return self.info
-        except:
-            raise IOError('ExporterError: 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('ExporterError: 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('ExporterError: 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('ExporterError: 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'
-