diff options
Diffstat (limited to 'mediagoblin')
-rw-r--r-- | mediagoblin/config_spec.ini | 9 | ||||
-rw-r--r-- | mediagoblin/media_types/ascii/processing.py | 9 | ||||
l--------- | mediagoblin/media_types/audio/audioprocessing.py | 1 | ||||
-rw-r--r-- | mediagoblin/media_types/audio/processing.py | 65 | ||||
-rw-r--r-- | mediagoblin/media_types/audio/transcoders.py | 90 | ||||
-rw-r--r-- | mediagoblin/media_types/image/processing.py | 2 | ||||
-rw-r--r-- | mediagoblin/media_types/video/processing.py | 15 | ||||
-rw-r--r-- | mediagoblin/media_types/video/transcoders.py | 53 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/media_displays/audio.html | 9 | ||||
-rw-r--r-- | mediagoblin/tests/test_submission.py | 3 |
10 files changed, 228 insertions, 28 deletions
diff --git a/mediagoblin/config_spec.ini b/mediagoblin/config_spec.ini index 452d9745..b429677c 100644 --- a/mediagoblin/config_spec.ini +++ b/mediagoblin/config_spec.ini @@ -65,6 +65,14 @@ base_url = string(default="/mgoblin_media/") storage_class = string(default="mediagoblin.storage.filestorage:BasicFileStorage") base_dir = string(default="%(here)s/user_dev/media/queue") +[media:medium] +max_width = integer(default=640) +max_height = integer(default=640) + +[media:thumb] +max_width = integer(default=180) +max_height = integer(default=180) + [media_type:mediagoblin.media_types.video] # Should we keep the original file? keep_original = boolean(default=False) @@ -72,6 +80,7 @@ keep_original = boolean(default=False) [media_type:mediagoblin.media_types.audio] # vorbisenc qualiy quality = float(default=0.3) +create_spectrogram = boolean(default=False) [beaker.cache] diff --git a/mediagoblin/media_types/ascii/processing.py b/mediagoblin/media_types/ascii/processing.py index f698b97a..75184c1f 100644 --- a/mediagoblin/media_types/ascii/processing.py +++ b/mediagoblin/media_types/ascii/processing.py @@ -24,7 +24,16 @@ from mediagoblin.media_types.ascii import asciitoimage _log = logging.getLogger(__name__) +SUPPORTED_EXTENSIONS = ['txt', 'asc', 'nfo'] + def sniff_handler(media_file, **kw): + if not kw.get('media') == None: + name, ext = os.path.splitext(kw['media'].filename) + clean_ext = ext[1:].lower() + + if clean_ext in SUPPORTED_EXTENSIONS: + return True + return False def process_ascii(entry): diff --git a/mediagoblin/media_types/audio/audioprocessing.py b/mediagoblin/media_types/audio/audioprocessing.py new file mode 120000 index 00000000..c5e3c52c --- /dev/null +++ b/mediagoblin/media_types/audio/audioprocessing.py @@ -0,0 +1 @@ +../../../extlib/freesound/audioprocessing.py
\ No newline at end of file diff --git a/mediagoblin/media_types/audio/processing.py b/mediagoblin/media_types/audio/processing.py index 7aa7ace8..6769f605 100644 --- a/mediagoblin/media_types/audio/processing.py +++ b/mediagoblin/media_types/audio/processing.py @@ -21,9 +21,10 @@ import os from mediagoblin import mg_globals as mgg from mediagoblin.processing import create_pub_filepath -from mediagoblin.media_types.audio.transcoders import AudioTranscoder +from mediagoblin.media_types.audio.transcoders import AudioTranscoder, \ + AudioThumbnailer -_log = logging.getLogger() +_log = logging.getLogger(__name__) def sniff_handler(media_file, **kw): transcoder = AudioTranscoder() @@ -33,7 +34,9 @@ def sniff_handler(media_file, **kw): if data.is_audio == True and data.is_video == False: return True except: - return False + pass + + return False def process_audio(entry): audio_config = mgg.global_config['media_type:mediagoblin.media_types.audio'] @@ -51,10 +54,9 @@ def process_audio(entry): original=os.path.splitext( queued_filepath[-1])[0])) - ogg_tmp = tempfile.NamedTemporaryFile() + transcoder = AudioTranscoder() - with ogg_tmp: - transcoder = AudioTranscoder() + with tempfile.NamedTemporaryFile() as ogg_tmp: transcoder.transcode( queued_filename, @@ -72,11 +74,54 @@ def process_audio(entry): entry.media_data['audio'] = { u'length': int(data.audiolength)} - thumbnail_tmp = tempfile.NamedTemporaryFile() - - with thumbnail_tmp: + if audio_config['create_spectrogram']: + spectrogram_filepath = create_pub_filepath( + entry, + '{original}-spectrogram.jpg'.format( + original=os.path.splitext( + queued_filepath[-1])[0])) + + with tempfile.NamedTemporaryFile(suffix='.wav') as wav_tmp: + _log.info('Creating WAV source for spectrogram') + transcoder.transcode( + queued_filename, + wav_tmp.name, + mux_string='wavenc') + + thumbnailer = AudioThumbnailer() + + with tempfile.NamedTemporaryFile(suffix='.jpg') as spectrogram_tmp: + thumbnailer.spectrogram( + wav_tmp.name, + spectrogram_tmp.name, + width=mgg.global_config['media:medium']['max_width']) + + _log.debug('Saving spectrogram...') + mgg.public_store.get_file(spectrogram_filepath, 'wb').write( + spectrogram_tmp.read()) + + entry.media_files['spectrogram'] = spectrogram_filepath + + with tempfile.NamedTemporaryFile(suffix='.jpg') as thumb_tmp: + thumbnailer.thumbnail_spectrogram( + spectrogram_tmp.name, + thumb_tmp.name, + (mgg.global_config['media:thumb']['max_width'], + mgg.global_config['media:thumb']['max_height'])) + + thumb_filepath = create_pub_filepath( + entry, + '{original}-thumbnail.jpg'.format( + original=os.path.splitext( + queued_filepath[-1])[0])) + + mgg.public_store.get_file(thumb_filepath, 'wb').write( + thumb_tmp.read()) + + entry.media_files['thumb'] = thumb_filepath + else: entry.media_files['thumb'] = ['fake', 'thumb', 'path.jpg'] - + mgg.queue_store.delete_file(queued_filepath) entry.save() diff --git a/mediagoblin/media_types/audio/transcoders.py b/mediagoblin/media_types/audio/transcoders.py index c5634964..7649309c 100644 --- a/mediagoblin/media_types/audio/transcoders.py +++ b/mediagoblin/media_types/audio/transcoders.py @@ -16,8 +16,10 @@ import pdb import logging +from PIL import Image from mediagoblin.processing import BadMediaFail +from mediagoblin.media_types.audio import audioprocessing _log = logging.getLogger(__name__) @@ -56,6 +58,73 @@ 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('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 - (th_w / 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__)) @@ -103,17 +172,21 @@ class AudioTranscoder(object): 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}" ! ' '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() @@ -141,6 +214,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) @@ -149,8 +225,12 @@ 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() + + thumbnailer.spectrogram(*sys.argv[1:], width=640) pdb.set_trace() diff --git a/mediagoblin/media_types/image/processing.py b/mediagoblin/media_types/image/processing.py index 364a5afa..28cde2aa 100644 --- a/mediagoblin/media_types/image/processing.py +++ b/mediagoblin/media_types/image/processing.py @@ -42,7 +42,7 @@ def sniff_handler(media_file, **kw): _log.info('Found file extension in supported filetypes') return True else: - _log.debug('Media present, extension not found in {1}'.format( + _log.debug('Media present, extension not found in {0}'.format( SUPPORTED_FILETYPES)) else: _log.warning('Need additional information (keyword argument \'media\')' diff --git a/mediagoblin/media_types/video/processing.py b/mediagoblin/media_types/video/processing.py index 1890ef0c..d2562e3b 100644 --- a/mediagoblin/media_types/video/processing.py +++ b/mediagoblin/media_types/video/processing.py @@ -29,6 +29,18 @@ _log = logging.getLogger(__name__) _log.setLevel(logging.DEBUG) def sniff_handler(media_file, **kw): + transcoder = transcoders.VideoTranscoder() + try: + data = transcoder.discover(media_file.name) + + _log.debug('Discovered: {0}'.format(data.__dict__)) + + if data.is_video == True: + return True + except: + _log.error('Exception caught when trying to discover {0}'.format( + kw.get('media'))) + return False def process_video(entry): @@ -61,7 +73,8 @@ def process_video(entry): with tmp_dst: # Transcode queued file to a VP8/vorbis file that fits in a 640x640 square - transcoder = transcoders.VideoTranscoder(queued_filename, tmp_dst.name) + transcoder = transcoders.VideoTranscoder() + transcoder.transcode(queued_filename, tmp_dst.name) # Push transcoded video to public storage _log.debug('Saving medium...') diff --git a/mediagoblin/media_types/video/transcoders.py b/mediagoblin/media_types/video/transcoders.py index 903bd810..6c2e885e 100644 --- a/mediagoblin/media_types/video/transcoders.py +++ b/mediagoblin/media_types/video/transcoders.py @@ -25,8 +25,6 @@ import pdb import urllib _log = logging.getLogger(__name__) -logging.basicConfig() -_log.setLevel(logging.DEBUG) CPU_COUNT = 2 try: @@ -340,10 +338,15 @@ class VideoTranscoder: that it was refined afterwards and therefore is done more correctly. ''' - def __init__(self, src, dst, **kwargs): + def __init__(self): _log.info('Initializing VideoTranscoder...') self.loop = gobject.MainLoop() + + def transcode(self, src, dst, **kwargs): + ''' + Transcode a video file into a 'medium'-sized version. + ''' self.source_path = src self.destination_path = dst @@ -357,6 +360,30 @@ class VideoTranscoder: self._setup() self._run() + def discover(self, src): + ''' + Discover properties about a media file + ''' + _log.info('Discovering {0}'.format(src)) + + self.source_path = src + self._setup_discover(discovered_callback=self.__on_discovered) + + self.discoverer.discover() + + self.loop.run() + + return self._discovered_data + + def __on_discovered(self, data, is_media): + if not is_media: + self.__stop() + raise Exception('Could not discover {0}'.format(self.source_path)) + + self._discovered_data = data + + self.__stop_mainloop() + def _setup(self): self._setup_discover() self._setup_pipeline() @@ -369,12 +396,14 @@ class VideoTranscoder: _log.debug('Initializing MainLoop()') self.loop.run() - def _setup_discover(self): + def _setup_discover(self, **kw): _log.debug('Setting up discoverer') self.discoverer = discoverer.Discoverer(self.source_path) # Connect self.__discovered to the 'discovered' event - self.discoverer.connect('discovered', self.__discovered) + self.discoverer.connect( + 'discovered', + kw.get('discovered_callback', self.__discovered)) def __discovered(self, data, is_media): ''' @@ -614,14 +643,15 @@ class VideoTranscoder: if __name__ == '__main__': os.nice(19) + logging.basicConfig() from optparse import OptionParser parser = OptionParser( - usage='%prog [-v] -a [ video | thumbnail ] SRC DEST') + usage='%prog [-v] -a [ video | thumbnail | discover ] SRC [ DEST ]') parser.add_option('-a', '--action', dest='action', - help='One of "video" or "thumbnail"') + help='One of "video", "discover" or "thumbnail"') parser.add_option('-v', dest='verbose', @@ -645,13 +675,18 @@ if __name__ == '__main__': _log.debug(args) - if not len(args) == 2: + if not len(args) == 2 and not options.action == 'discover': parser.print_help() sys.exit() + transcoder = VideoTranscoder() + if options.action == 'thumbnail': VideoThumbnailer(*args) elif options.action == 'video': def cb(data): print('I\'m a callback!') - transcoder = VideoTranscoder(*args, progress_callback=cb) + transcoder.transcode(*args, progress_callback=cb) + elif options.action == 'discover': + print transcoder.discover(*args).__dict__ + diff --git a/mediagoblin/templates/mediagoblin/media_displays/audio.html b/mediagoblin/templates/mediagoblin/media_displays/audio.html index 802a85c1..f130e323 100644 --- a/mediagoblin/templates/mediagoblin/media_displays/audio.html +++ b/mediagoblin/templates/mediagoblin/media_displays/audio.html @@ -20,7 +20,14 @@ {% block mediagoblin_media %} <div class="audio-media"> - <audio controls="controls" + {% if 'spectrogram' in media.media_files %} + <div class="audio-spectrogram"> + <img src="{{ request.app.public_store.file_url( + media.media_files.spectrogram) }}" + alt="Spectrogram" /> + </div> + {% endif %} + <audio class="audio-player" controls="controls" preload="metadata"> <source src="{{ request.app.public_store.file_url( media.media_files.ogg) }}" type="video/webm; encoding="vorbis"" /> diff --git a/mediagoblin/tests/test_submission.py b/mediagoblin/tests/test_submission.py index 217926a4..53ba25d8 100644 --- a/mediagoblin/tests/test_submission.py +++ b/mediagoblin/tests/test_submission.py @@ -231,7 +231,8 @@ class TestSubmission: context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] form = context['submit_form'] - assert re.match(r'^Could not extract any file extension from ".*?"$', str(form.file.errors[0])) + assert 'Sorry, I don\'t support that file type :(' == \ + str(form.file.errors[0]) assert len(form.file.errors) == 1 # NOTE: The following 2 tests will ultimately fail, but they |