use name_builder with store_public, not create_pub_filepath
[mediagoblin.git] / mediagoblin / media_types / audio / transcoders.py
index e59214b0b9f8e78a9d67678d80b12f56d8a37487..84e6af7e560e3d634e45d0c56000b6067dbabfe9 100644 (file)
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import pdb
 import logging
+try:
+    from PIL import Image
+except ImportError:
+    import Image
 
 from mediagoblin.processing import BadMediaFail
+from mediagoblin.media_types.audio import audioprocessing
 
 
 _log = logging.getLogger(__name__)
 
-CPU_COUNT = 2 # Just assuming for now
+CPU_COUNT = 2  # Just assuming for now
 
 # IMPORT MULTIPROCESSING
 try:
@@ -56,14 +60,84 @@ try:
 except ImportError:
     raise Exception('gst/pygst >= 0.10 could not be imported')
 
+import numpy
+
+
+class AudioThumbnailer(object):
+    def __init__(self):
+        _log.info('Initializing {0}'.format(self.__class__.__name__))
+
+    def spectrogram(self, src, dst, **kw):
+        width = kw['width']
+        height = int(kw.get('height', float(width) * 0.3))
+        fft_size = kw.get('fft_size', 2048)
+        callback = kw.get('progress_callback')
+
+        processor = audioprocessing.AudioProcessor(
+            src,
+            fft_size,
+            numpy.hanning)
+
+        samples_per_pixel = processor.audio_file.nframes / float(width)
+
+        spectrogram = audioprocessing.SpectrogramImage(width, height, fft_size)
+
+        for x in range(width):
+            if callback and x % (width / 10) == 0:
+                callback((x * 100) / width)
+
+            seek_point = int(x * samples_per_pixel)
+
+            (spectral_centroid, db_spectrum) = processor.spectral_centroid(
+                seek_point)
+
+            spectrogram.draw_spectrum(x, db_spectrum)
+
+        if callback:
+            callback(100)
+
+        spectrogram.save(dst)
+
+    def thumbnail_spectrogram(self, src, dst, thumb_size):
+        '''
+        Takes a spectrogram and creates a thumbnail from it
+        '''
+        if not (type(thumb_size) == tuple and len(thumb_size) == 2):
+            raise Exception('thumb_size argument should be a tuple(width, height)')
+
+        im = Image.open(src)
+
+        im_w, im_h = [float(i) for i in im.size]
+        th_w, th_h = [float(i) for i in thumb_size]
+
+        wadsworth_position = im_w * 0.3
+
+        start_x = max((
+                wadsworth_position - ((im_h * (th_w / th_h)) / 2.0),
+                0.0))
+
+        stop_x = start_x + (im_h * (th_w / th_h))
+
+        th = im.crop((
+                int(start_x), 0,
+                int(stop_x), int(im_h)))
+
+        if th.size[0] > th_w or th.size[1] > th_h:
+            th.thumbnail(thumb_size, Image.ANTIALIAS)
+
+        th.save(dst)
+
+
 class AudioTranscoder(object):
     def __init__(self):
         _log.info('Initializing {0}'.format(self.__class__.__name__))
 
         # Instantiate MainLoop
         self._loop = gobject.MainLoop()
+        self._failed = None
 
     def discover(self, src):
+        self._src_path = src
         _log.info('Discovering {0}'.format(src))
         self._discovery_path = src
 
@@ -74,14 +148,17 @@ class AudioTranscoder(object):
 
         self._loop.run()  # Run MainLoop
 
+        if self._failed:
+            raise self._failed
+
         # Once MainLoop has returned, return discovery data
-        return self._discovery_data
+        return getattr(self, '_discovery_data', False)
 
     def __on_discovered(self, data, is_media):
         if not is_media:
-            self.halt()
+            self._failed = BadMediaFail()
             _log.error('Could not discover {0}'.format(self._src_path))
-            raise BadMediaFail()
+            self.halt()
 
         _log.debug('Discovered: {0}'.format(data.__dict__))
 
@@ -91,23 +168,28 @@ class AudioTranscoder(object):
         self.halt()
 
     def transcode(self, src, dst, **kw):
+        _log.info('Transcoding {0} into {1}'.format(src, dst))
         self._discovery_data = kw.get('data', self.discover(src))
 
         self.__on_progress = kw.get('progress_callback')
 
         quality = kw.get('quality', 0.3)
 
+        mux_string = kw.get(
+            'mux_string',
+            'vorbisenc quality={0} ! webmmux'.format(quality))
+
         # Set up pipeline
         self.pipeline = gst.parse_launch(
-            'filesrc location="{src}" ! ' 
+            'filesrc location="{src}" ! '
             'decodebin2 ! queue ! audiorate tolerance={tolerance} ! '
             'audioconvert ! audio/x-raw-float,channels=2 ! '
-            'vorbisenc quality={quality} ! webmmux ! '
+            '{mux_string} ! '
             'progressreport silent=true ! '
             'filesink location="{dst}"'.format(
                 src=src,
                 tolerance=80000000,
-                quality=quality,
+                mux_string=mux_string,
                 dst=dst))
 
         self.bus = self.pipeline.get_bus()
@@ -126,7 +208,7 @@ class AudioTranscoder(object):
             data = dict(message.structure)
 
             if self.__on_progress:
-                self.__on_progress(data)
+                self.__on_progress(data.get('percent'))
 
             _log.info('{0}% done...'.format(
                     data.get('percent')))
@@ -135,6 +217,9 @@ class AudioTranscoder(object):
             self.halt()
 
     def halt(self):
+        if getattr(self, 'pipeline', False):
+            self.pipeline.set_state(gst.STATE_NULL)
+            del self.pipeline
         _log.info('Quitting MainLoop gracefully...')
         gobject.idle_add(self._loop.quit)
 
@@ -143,8 +228,10 @@ if __name__ == '__main__':
     logging.basicConfig()
     _log.setLevel(logging.INFO)
 
-    transcoder = AudioTranscoder()
-    data = transcoder.discover(sys.argv[1])
-    res = transcoder.transcode(*sys.argv[1:3])
+    #transcoder = AudioTranscoder()
+    #data = transcoder.discover(sys.argv[1])
+    #res = transcoder.transcode(*sys.argv[1:3])
+
+    thumbnailer = AudioThumbnailer()
 
-    pdb.set_trace()
+    thumbnailer.spectrogram(*sys.argv[1:], width=640)