diff options
-rw-r--r-- | mediagoblin/media_types/__init__.py | 56 | ||||
-rw-r--r-- | mediagoblin/media_types/video/__init__.py | 2 | ||||
-rw-r--r-- | mediagoblin/static/css/audio.css | 53 | ||||
-rw-r--r-- | mediagoblin/static/js/audio.js | 140 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/media_displays/audio.html | 7 | ||||
-rw-r--r-- | mediagoblin/tests/test_submission.py | 22 |
6 files changed, 255 insertions, 25 deletions
diff --git a/mediagoblin/media_types/__init__.py b/mediagoblin/media_types/__init__.py index db8c6f73..93d2319f 100644 --- a/mediagoblin/media_types/__init__.py +++ b/mediagoblin/media_types/__init__.py @@ -36,16 +36,24 @@ def sniff_media(media): Iterate through the enabled media types and find those suited for a certain file. ''' - media_file = tempfile.NamedTemporaryFile() - media_file.write(media.file.read()) - media.file.seek(0) - for media_type, manager in get_media_managers(): - _log.info('Sniffing {0}'.format(media_type)) - if manager['sniff_handler'](media_file, media=media): - _log.info('{0} accepts the file'.format(media_type)) - return media_type, manager - else: - _log.debug('{0} did not accept the file'.format(media_type)) + + try: + return get_media_type_and_manager(media.filename) + except FileTypeNotSupported: + _log.info('No media handler found by file extension. Doing it the expensive way...') + # Create a temporary file for sniffers suchs as GStreamer-based + # Audio video + media_file = tempfile.NamedTemporaryFile() + media_file.write(media.file.read()) + media.file.seek(0) + + for media_type, manager in get_media_managers(): + _log.info('Sniffing {0}'.format(media_type)) + if manager['sniff_handler'](media_file, media=media): + _log.info('{0} accepts the file'.format(media_type)) + return media_type, manager + else: + _log.debug('{0} did not accept the file'.format(media_type)) raise FileTypeNotSupported( # TODO: Provide information on which file types are supported @@ -66,7 +74,7 @@ def get_media_managers(): ''' for media_type in get_media_types(): __import__(media_type) - + yield media_type, sys.modules[media_type].MEDIA_MANAGER @@ -91,22 +99,22 @@ def get_media_manager(_media_type): def get_media_type_and_manager(filename): ''' - Get the media type and manager based on a filename + Try to find the media type based on the file name, extension + specifically. This is used as a speedup, the sniffing functionality + then falls back on more in-depth bitsniffing of the source file. ''' if filename.find('.') > 0: # Get the file extension ext = os.path.splitext(filename)[1].lower() - else: - raise InvalidFileType( - _(u'Could not extract any file extension from "{filename}"').format( - filename=filename)) - for media_type, manager in get_media_managers(): - # Omit the dot from the extension and match it against - # the media manager - if ext[1:] in manager['accepted_extensions']: - return media_type, manager + for media_type, manager in get_media_managers(): + # Omit the dot from the extension and match it against + # the media manager + if ext[1:] in manager['accepted_extensions']: + return media_type, manager else: - raise FileTypeNotSupported( - # TODO: Provide information on which file types are supported - _(u'Sorry, I don\'t support that file type :(')) + _log.info('File {0} has no file extension, let\'s hope the sniffers get it.'.format( + filename)) + + raise FileTypeNotSupported( + _(u'Sorry, I don\'t support that file type :(')) diff --git a/mediagoblin/media_types/video/__init__.py b/mediagoblin/media_types/video/__init__.py index 5351abe6..a53927d5 100644 --- a/mediagoblin/media_types/video/__init__.py +++ b/mediagoblin/media_types/video/__init__.py @@ -26,4 +26,4 @@ MEDIA_MANAGER = { "display_template": "mediagoblin/media_displays/video.html", "default_thumb": "images/media_thumbs/video.jpg", "accepted_extensions": [ - "mp4", "mov", "webm", "avi", "3gp", "3gpp", "mkv", "ogv", "ogg"]} + "mp4", "mov", "webm", "avi", "3gp", "3gpp", "mkv", "ogv"]} diff --git a/mediagoblin/static/css/audio.css b/mediagoblin/static/css/audio.css new file mode 100644 index 00000000..5f7a888a --- /dev/null +++ b/mediagoblin/static/css/audio.css @@ -0,0 +1,53 @@ +.audio-spectrogram { + position: relative; +} +.playhead { + position: absolute; + top: 0; + left: 0; + background: rgba(134, 212, 177, 0.3); + border-right: thin solid #ffaa00; + height: 100%; + -webkit-transition: width .1s ease-out; + -moz-transition: width .1s ease-out; +} +.audio-control-play-pause { + position: absolute; + bottom: 0; + left: 5px; + cursor: pointer; + /* background: rgba(0, 0, 0, 0.7); */ + font-size: 40px; + width: 50px; + text-shadow: 0 0 10px black; +} + .audio-control-play-pause.playing { + color: #b71500; + } + .audio-control-play-pause.paused { + color: rgb(134, 212, 177); + } +.buffered { + position: absolute; + bottom: 0; + left: 0; + height: 2px; + width: 0; + -webkit-transition: width 1s ease-out; + -moz-transition: width 1s ease-out; + background: rgba(134, 177, 212, 1); + cursor: pointer; +} +.seekbar { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.audio-currentTime { + position: absolute; + bottom: 0; + right: 0; + background: rgba(0, 0, 0, 0.7); +} diff --git a/mediagoblin/static/js/audio.js b/mediagoblin/static/js/audio.js new file mode 100644 index 00000000..8c91bd0b --- /dev/null +++ b/mediagoblin/static/js/audio.js @@ -0,0 +1,140 @@ +/** + * GNU MediaGoblin -- federated, autonomous media hosting + * Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * 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/>. + */ + +var audioPlayer = new Object(); + +(function (audioPlayer) { + audioPlayer.init = function (audioElement) { + audioPlayer.audioElement = audioElement; + console.log(audioElement); + attachEvents(); + $(audioElement).hide(); + }; + + function attachEvents () { + audioPlayer.audioElement.addEventListener('durationchange', audioPlayer.durationChange, true); + audioPlayer.audioElement.addEventListener('timeupdate', audioPlayer.timeUpdate, true); + audioPlayer.audioElement.addEventListener('progress', audioPlayer.onProgress, true); + $(document).ready( function () { + $('.audio-spectrogram').delegate('.seekbar', 'click', audioPlayer.onSeek); + $('.audio-spectrogram').delegate('.audio-control-play-pause', 'click', audioPlayer.playPause); + }); + } + + audioPlayer.onProgress = function(a, b, c) { + console.log(a, b, c); + buffered = audioPlayer.audioElement.buffered; + + ranges = new Array(); + + for (i = 0; i < buffered.length; i++) { + ranges[i] = new Array(); + ranges[i][0] = buffered.start(i); + ranges[i][1] = buffered.end(i); + } + console.log('ranges', ranges); + $('.audio-spectrogram .buffered').width( + (ranges[0][1] / audioPlayer.audioElement.duration) * audioPlayer.imageElement.width()); + }; + + audioPlayer.onSeek = function (e) { + console.log('onSeek', e); + im = audioPlayer.imageElement; + pos = e.offsetX / im.width(); + audioPlayer.audioElement.currentTime = pos * audioPlayer.audioElement.duration; + audioPlayer.audioElement.play(); + audioPlayer.setState(audioPlayer.PLAYING); + }; + + audioPlayer.playPause = function (e) { + console.log('playPause', e); + if (audioPlayer.audioElement.paused) { + audioPlayer.audioElement.play(); + audioPlayer.setState(audioPlayer.PLAYING); + } else { + audioPlayer.audioElement.pause(); + audioPlayer.setState(audioPlayer.PAUSED); + } + }; + + audioPlayer.NULL = null; + audioPlayer.PLAYING = 2; + audioPlayer.PAUSED = 4; + + audioPlayer.state = audioPlayer.NULL; + + audioPlayer.setState = function (state) { + if (state == audioPlayer.state) { + return; + } + + switch (state) { + case audioPlayer.PLAYING: + $('.audio-spectrogram .audio-control-play-pause') + .removeClass('paused').addClass('playing') + .text('■'); + break; + case audioPlayer.PAUSED: + $('.audio-spectrogram .audio-control-play-pause') + .removeClass('playing').addClass('paused') + .text('▶'); + break; + } + }; + + audioPlayer.durationChange = function () { + duration = audioPlayer.audioElement.duration; + }; + + audioPlayer.timeUpdate = function () { + currentTime = audioPlayer.audioElement.currentTime; + playhead = audioPlayer.imageElement.parent().find('.playhead'); + playhead.css('width', (currentTime / audioPlayer.audioElement.duration) * audioPlayer.imageElement.width()); + time = formatTime(currentTime); + duration = formatTime(audioPlayer.audioElement.duration); + audioPlayer.imageElement.parent().find('.audio-currentTime').text(time + '/' + duration); + }; + + function formatTime(seconds) { + var h = Math.floor(seconds / (60 * 60)); + var m = Math.floor((seconds - h * 60 * 60) / 60); + var s = Math.round(seconds - h * 60 * 60 - m * 60); + return '' + (h ? (h < 10 ? '0' + h : h) + ':' : '') + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s); + } + + audioPlayer.attachToImage = function (imageElement) { + /** + * Attach the player to an image element + */ + console.log(imageElement); + im = $(imageElement); + audioPlayer.imageElement = im; + $('<div class="playhead"></div>').appendTo(im.parent()); + $('<div class="buffered"></div>').appendTo(im.parent()); + $('<div class="seekbar"></div>').appendTo(im.parent()); + $('<div class="audio-control-play-pause paused">▶</div>').appendTo(im.parent()); + $('<div class="audio-currentTime">00:00</div>').appendTo(im.parent()); + }; +})(audioPlayer); + +$(document).ready(function () { + audioElements = $('.audio-media .audio-player'); + audioPlayer.init(audioElements[0]); + audioPlayer.attachToImage($('.audio-spectrogram img')[0]); +}); + diff --git a/mediagoblin/templates/mediagoblin/media_displays/audio.html b/mediagoblin/templates/mediagoblin/media_displays/audio.html index f130e323..36bd9d1d 100644 --- a/mediagoblin/templates/mediagoblin/media_displays/audio.html +++ b/mediagoblin/templates/mediagoblin/media_displays/audio.html @@ -18,6 +18,13 @@ {% extends 'mediagoblin/user_pages/media.html' %} +{% block mediagoblin_head %} + {{ super() }} + <link rel="stylesheet" type="text/css" href="{{ request.staticdirect('/css/audio.css') }}" /> + <script type="text/javascript" src="{{ request.staticdirect( + '/js/audio.js') }}"></script> +{% endblock %} + {% block mediagoblin_media %} <div class="audio-media"> {% if 'spectrogram' in media.media_files %} diff --git a/mediagoblin/tests/test_submission.py b/mediagoblin/tests/test_submission.py index 53ba25d8..702741b4 100644 --- a/mediagoblin/tests/test_submission.py +++ b/mediagoblin/tests/test_submission.py @@ -219,6 +219,28 @@ class TestSubmission: request.db.MediaEntry.find( {'_id': media._id}).count()) + def test_sniffing(self): + ''' + Test sniffing mechanism to assert that regular uploads work as intended + ''' + template.clear_test_template_context() + response = self.test_app.post( + '/submit/', { + 'title': 'UNIQUE_TITLE_PLS_DONT_CREATE_OTHER_MEDIA_WITH_THIS_TITLE' + }, upload_files=[( + 'file', GOOD_JPG)]) + + response.follow() + + context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/user_pages/user.html'] + + request = context['request'] + + media = request.db.MediaEntry.find_one({ + u'title': u'UNIQUE_TITLE_PLS_DONT_CREATE_OTHER_MEDIA_WITH_THIS_TITLE'}) + + assert media.media_type == 'mediagoblin.media_types.image' + def test_malicious_uploads(self): # Test non-suppoerted file with non-supported extension # ----------------------------------------------------- |