[SCM] morituri/master: * morituri/image/image.py: Add ImageEncodeTask to encode a disk image to a different profile and directory. * morituri/common/encode.py: Add lossy encoding profiles for mp3 and vorbis. Rename muxer to tagger since that's what we use it for. Do progress probe after level to make sure we get samples for offsets. * morituri/rip/image.py: Add rip image encode command.

js at users.alioth.debian.org js at users.alioth.debian.org
Sun Oct 19 20:09:14 UTC 2014


The following commit has been merged in the master branch:
commit fe68f676c95c193000dede551220c20928336d32
Author: Thomas Vander Stichele <thomas (at) apestaart (dot) org>
Date:   Sat Oct 17 13:53:58 2009 +0000

    	* morituri/image/image.py:
    	  Add ImageEncodeTask to encode a disk image to a different profile
    	  and directory.
    	* morituri/common/encode.py:
    	  Add lossy encoding profiles for mp3 and vorbis.
    	  Rename muxer to tagger since that's what we use it for.
    	  Do progress probe after level to make sure we get samples for
    	  offsets.
    	* morituri/rip/image.py:
    	  Add rip image encode command.

diff --git a/ChangeLog b/ChangeLog
index fb74e38..c00d809 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2009-10-17  Thomas Vander Stichele  <thomas at apestaart dot org>
 
+	* morituri/image/image.py:
+	  Add ImageEncodeTask to encode a disk image to a different profile
+	  and directory.
+	* morituri/common/encode.py:
+	  Add lossy encoding profiles for mp3 and vorbis.
+	  Rename muxer to tagger since that's what we use it for.
+	  Do progress probe after level to make sure we get samples for
+	  offsets.
+	* morituri/rip/image.py:
+	  Add rip image encode command.
+
+2009-10-17  Thomas Vander Stichele  <thomas at apestaart dot org>
+
 	* morituri/test/José González.toc (added):
 	  Add test file for a test.
 
diff --git a/morituri/common/encode.py b/morituri/common/encode.py
index f5ddc17..f380325 100644
--- a/morituri/common/encode.py
+++ b/morituri/common/encode.py
@@ -33,6 +33,7 @@ class Profile(object):
     name = None
     extension = None
     pipeline = None
+    losless = None
 
     def test(self):
         """
@@ -44,7 +45,8 @@ class Profile(object):
 class FlacProfile(Profile):
     name = 'flac'
     extension = 'flac'
-    pipeline = 'flacenc name=muxer quality=8'
+    pipeline = 'flacenc name=tagger quality=8'
+    lossless = True
 
     # FIXME: we should do something better than just printing ERRORS
     def test(self):
@@ -65,26 +67,49 @@ class FlacProfile(Profile):
 class AlacProfile(Profile):
     name = 'alac'
     extension = 'alac'
-    pipeline = 'ffenc_alac name=muxer'
+    pipeline = 'ffenc_alac name=tagger'
+    lossless = True
 
 class WavProfile(Profile):
     name = 'wav'
     extension = 'wav'
-    pipeline = 'wavenc name=muxer'
+    pipeline = 'wavenc name=tagger'
+    lossless = True
 
 class WavpackProfile(Profile):
     name = 'wavpack'
     extension = 'wv'
-    pipeline = 'wavpackenc bitrate=0 name=muxer'
+    pipeline = 'wavpackenc bitrate=0 name=tagger'
+    lossless = True
+
+class MP3Profile(Profile):
+    name = 'mp3'
+    extension = 'mp3'
+    pipeline = 'lame name=tagger quality=0 ! id3v2mux'
+    lossless = False
+
+class VorbisProfile(Profile):
+    name = 'vorbis'
+    extension = 'oga'
+    pipeline = 'audioconvert ! vorbisenc name=tagger ! oggmux'
+    lossless = False
 
 
 PROFILES = {
-    'wav': WavProfile,
-    'flac': FlacProfile,
-    'alac': AlacProfile,
+    'wav':     WavProfile,
+    'flac':    FlacProfile,
+    'alac':    AlacProfile,
     'wavpack': WavpackProfile,
 }
 
+LOSSY_PROFILES = {
+    'mp3':     MP3Profile,
+    'vorbis':  VorbisProfile,
+}
+
+ALL_PROFILES = PROFILES.copy()
+ALL_PROFILES.update(LOSSY_PROFILES)
+
 class EncodeTask(task.Task):
     """
     I am a task that encodes a .wav file.
@@ -125,11 +150,11 @@ class EncodeTask(task.Task):
             filesink location="%s" name=sink''' % (self._inpath,
                 self._profile.pipeline, self._outpath))
 
-        muxer = self._pipeline.get_by_name('muxer')
+        tagger = self._pipeline.get_by_name('tagger')
 
         # set tags
         if self._taglist:
-            muxer.merge_tags(self._taglist, gst.TAG_MERGE_APPEND)
+            tagger.merge_tags(self._taglist, gst.TAG_MERGE_APPEND)
 
         self.debug('pausing pipeline')
         self._pipeline.set_state(gst.STATE_PAUSED)
@@ -138,7 +163,7 @@ class EncodeTask(task.Task):
 
         # get length
         self.debug('query duration')
-        length, qformat = muxer.query_duration(gst.FORMAT_DEFAULT)
+        length, qformat = tagger.query_duration(gst.FORMAT_DEFAULT)
         # wavparse 0.10.14 returns in bytes
         if qformat == gst.FORMAT_BYTES:
             self.debug('query returned in BYTES format')
@@ -146,11 +171,6 @@ class EncodeTask(task.Task):
         self.debug('total length: %r', length)
         self._length = length
 
-        # add a probe so we can track progress
-        sinkpad = muxer.get_pad('sink')
-        srcpad = sinkpad.get_peer()
-        srcpad.add_buffer_probe(self._probe_handler)
-
         # add eos handling
         bus = self._pipeline.get_bus()
         bus.add_signal_watch()
@@ -159,6 +179,10 @@ class EncodeTask(task.Task):
         # set up level callbacks
         bus.connect('message::element', self._message_element_cb)
         self._level = self._pipeline.get_by_name('level')
+        # add a probe so we can track progress
+        # we connect to level because this gives us offset in samples
+        srcpad = self._level.get_static_pad('src')
+        srcpad.add_buffer_probe(self._probe_handler)
 
         self.debug('scheduling setting to play')
         # since set_state returns non-False, adding it as timeout_add
@@ -175,6 +199,8 @@ class EncodeTask(task.Task):
         self.debug('scheduled setting to play')
 
     def _probe_handler(self, pad, buffer):
+        # update progress based on buffer offset (expected to be in samples)
+        # versus length in samples
         # marshal to main thread
         self.runner.schedule(0, self.setProgress,
             float(buffer.offset) / self._length)
diff --git a/morituri/image/image.py b/morituri/image/image.py
index 199d585..0c29006 100644
--- a/morituri/image/image.py
+++ b/morituri/image/image.py
@@ -24,9 +24,11 @@
 Wrap on-disk CD images based on the .cue file.
 """
 
+import os
+
 import gst
 
-from morituri.common import task, checksum, log, common
+from morituri.common import task, checksum, log, common, encode
 from morituri.image import cue, table
 
 class Image(object, log.Loggable):
@@ -215,3 +217,45 @@ class ImageVerifyTask(task.MultiSeparateTask):
             self.lengths[trackIndex] = end - index.relative
 
         task.MultiSeparateTask.stop(self)
+
+class ImageEncodeTask(task.MultiSeparateTask):
+    """
+    I encode a disk image to a different format.
+    """
+    
+    description = "Encoding tracks"
+
+    def __init__(self, image, profile, outdir):
+        task.MultiSeparateTask.__init__(self)
+
+        self._image = image
+        self._profile = profile
+        cue = image.cue
+        self._tasks = []
+        self.lengths = {}
+
+        def add(index):
+            path = image.getRealPath(index.path)
+            assert type(path) is unicode, "%r is not unicode" % path
+            self.debug('schedule encode of %r', path)
+            root, ext = os.path.splitext(os.path.basename(path))
+            outpath = os.path.join(outdir, root + '.' + profile.extension)
+            self.debug('schedule encode to %r', outpath)
+            taskk = encode.EncodeTask(path, os.path.join(outdir,
+                root + '.' + profile.extension), profile)
+            self.addTask(taskk)
+
+        try:
+            htoa = cue.table.tracks[0].indexes[0]
+            self.debug('encoding htoa track')
+            add(htoa)
+        except IndexError:
+            self.debug('no htoa track')
+            pass
+
+        for trackIndex, track in enumerate(cue.table.tracks):
+            self.debug('encoding track %d', trackIndex + 1)
+            index = track.indexes[1]
+            add(index)
+
+
diff --git a/morituri/rip/image.py b/morituri/rip/image.py
index 39ac17e..d8c0f43 100644
--- a/morituri/rip/image.py
+++ b/morituri/rip/image.py
@@ -20,12 +20,76 @@
 # You should have received a copy of the GNU General Public License
 # along with morituri.  If not, see <http://www.gnu.org/licenses/>.
 
+import os
+
 from morituri.common import logcommand, task, checksum, accurip, program
+from morituri.common import encode
 from morituri.image import image, cue
 from morituri.result import result
 from morituri.program import cdrdao, cdparanoia
 
 
+class Encode(logcommand.LogCommand):
+    summary = "encode image"
+
+    def addOptions(self):
+        # FIXME: get from config
+        self.parser.add_option('-O', '--output-directory',
+            action="store", dest="output_directory",
+            help="output directory (defaults to current directory)")
+
+        default = 'vorbis'
+        self.parser.add_option('', '--profile',
+            action="store", dest="profile",
+            help="profile for encoding (default '%s', choices '%s')" % (
+                default, "', '".join(encode.ALL_PROFILES.keys())),
+            default=default)
+
+
+    def do(self, args):
+        prog = program.Program()
+        prog.outdir = (self.options.output_directory or os.getcwd())
+        prog.outdir = prog.outdir.decode('utf-8')
+        profile = encode.ALL_PROFILES[self.options.profile]()
+
+        runner = task.SyncRunner()
+
+        for arg in args:
+            arg = unicode(arg)
+            indir = os.path.dirname(arg)
+            cueImage = image.Image(arg)
+            cueImage.setup(runner)
+            # FIXME: find a decent way to get an album-specific outdir
+            root, ext = os.path.splitext(os.path.basename(indir))
+            outdir = os.path.join(prog.outdir, root)
+            try:
+                os.makedirs(outdir)
+            except:
+                # FIXME: handle other exceptions than OSError Errno 17
+                pass
+            # FIXME: handle this nicer
+            assert outdir != indir
+
+            taskk = image.ImageEncodeTask(cueImage, profile, outdir)
+            runner.run(taskk)
+
+            # FIXME: translate .m3u file if it exists
+            root, ext = os.path.splitext(arg)
+            m3upath = root + '.m3u'
+            if os.path.exists(m3upath):
+                self.debug('translating .m3u file')
+                inm3u = open(m3upath)
+                outm3u = open(os.path.join(outdir, os.path.basename(m3upath)),
+                    'w')
+                for line in inm3u.readlines():
+                    root, ext = os.path.splitext(line)
+                    if ext:
+                        # newline is swallowed by splitext here
+                        outm3u.write('%s.%s\n' % (root, profile.extension))
+                    else:
+                        outm3u.write('%s' % root)
+                outm3u.close()
+
 class Verify(logcommand.LogCommand):
     summary = "verify image"
 
@@ -35,6 +99,7 @@ class Verify(logcommand.LogCommand):
         cache = accurip.AccuCache()
 
         for arg in args:
+            arg = unicode(arg)
             cueImage = image.Image(arg)
             cueImage.setup(runner)
 
@@ -56,4 +121,4 @@ class Verify(logcommand.LogCommand):
 class Image(logcommand.LogCommand):
     summary = "handle images"
 
-    subCommandClasses = [Verify, ]
+    subCommandClasses = [Encode, Verify, ]

-- 
morituri packaging



More information about the pkg-multimedia-commits mailing list