storage_class = string(default="mediagoblin.storage.filestorage:BasicFileStorage")
base_dir = string(default="%(here)s/user_dev/media/queue")
-
-# Should we keep the original file?
[media_type:mediagoblin.media_types.video]
+# Should we keep the original file?
keep_original = boolean(default=False)
+[media_type:mediagoblin.media_types.audio]
+# vorbisenc qualiy
+quality = float(default=0.3)
+
[beaker.cache]
type = string(default="file")
pass
+def sniff_media(media):
+ '''
+ Iterate through the enabled media types and find those suited
+ for a certain file.
+ '''
+ pass
+
+
def get_media_types():
"""
Generator, yields the available media types
--- /dev/null
+# 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/>.
+
+from mediagoblin.media_types.audio.processing import process_audio, \
+ sniff_handler
+
+MEDIA_MANAGER = {
+ 'human_readable': 'Audio',
+ 'processor': process_audio,
+ 'sniff_handler': sniff_handler,
+ 'display_template': 'mediagoblin/media_displays/audio.html',
+ 'accepted_extensions': ['mp3', 'flac', 'ogg', 'wav', 'm4a']}
--- /dev/null
+# 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/>.
+
+import logging
+import tempfile
+import os
+
+from mediagoblin import mg_globals as mgg
+from mediagoblin.processing import create_pub_filepath
+
+from mediagoblin.media_types.audio.transcoders import AudioTranscoder
+
+_log = logging.getLogger()
+
+def sniff_handler(media):
+ return True
+
+def process_audio(entry):
+ audio_config = mgg.global_config['media_type:mediagoblin.media_types.audio']
+
+ workbench = mgg.workbench_manager.create_workbench()
+
+ queued_filepath = entry.queued_media_file
+ queued_filename = workbench.localized_file(
+ mgg.queue_store, queued_filepath,
+ 'source')
+
+ ogg_filepath = create_pub_filepath(
+ entry,
+ '{original}.webm'.format(
+ original=os.path.splitext(
+ queued_filepath[-1])[0]))
+
+ ogg_tmp = tempfile.NamedTemporaryFile()
+
+ with ogg_tmp:
+ transcoder = AudioTranscoder()
+
+ transcoder.transcode(
+ queued_filename,
+ ogg_tmp.name,
+ quality=audio_config['quality'])
+
+ data = transcoder.discover(ogg_tmp.name)
+
+ _log.debug('Saving medium...')
+ mgg.public_store.get_file(ogg_filepath, 'wb').write(
+ ogg_tmp.read())
+
+ entry.media_files['ogg'] = ogg_filepath
+
+ entry.media_data['audio'] = {
+ u'length': int(data.audiolength)}
+
+ thumbnail_tmp = tempfile.NamedTemporaryFile()
+
+ with thumbnail_tmp:
+ entry.media_files['thumb'] = ['fake', 'thumb', 'path.jpg']
+
+ mgg.queue_store.delete_file(queued_filepath)
+
+ entry.save()
--- /dev/null
+# 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/>.
+
+class AudioTranscoder(object):
+
--- /dev/null
+# 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/>.
+
+import pdb
+import logging
+
+from mediagoblin.processing import BadMediaFail
+
+
+_log = logging.getLogger(__name__)
+
+CPU_COUNT = 2 # Just assuming for now
+
+# IMPORT MULTIPROCESSING
+try:
+ import multiprocessing
+ try:
+ CPU_COUNT = multiprocessing.cpu_count()
+ except NotImplementedError:
+ _log.warning('multiprocessing.cpu_count not implemented!\n'
+ 'Assuming 2 CPU cores')
+except ImportError:
+ _log.warning('Could not import multiprocessing, assuming 2 CPU cores')
+
+# IMPORT GOBJECT
+try:
+ import gobject
+ gobject.threads_init()
+except ImportError:
+ raise Exception('gobject could not be found')
+
+# IMPORT PYGST
+try:
+ import pygst
+
+ # We won't settle for less. For now, this is an arbitrary limit
+ # as we have not tested with > 0.10
+ pygst.require('0.10')
+
+ import gst
+
+ import gst.extend.discoverer
+except ImportError:
+ raise Exception('gst/pygst >= 0.10 could not be imported')
+
+class AudioTranscoder(object):
+ def __init__(self):
+ _log.info('Initializing {0}'.format(self.__class__.__name__))
+
+ # Instantiate MainLoop
+ self._loop = gobject.MainLoop()
+
+ def discover(self, src):
+ _log.info('Discovering {0}'.format(src))
+ self._discovery_path = src
+
+ self._discoverer = gst.extend.discoverer.Discoverer(
+ self._discovery_path)
+ self._discoverer.connect('discovered', self.__on_discovered)
+ self._discoverer.discover()
+
+ self._loop.run() # Run MainLoop
+
+ # Once MainLoop has returned, return discovery data
+ return self._discovery_data
+
+ def __on_discovered(self, data, is_media):
+ if not is_media:
+ self.halt()
+ _log.error('Could not discover {0}'.format(self._src_path))
+ raise BadMediaFail()
+
+ _log.debug('Discovered: {0}'.format(data.__dict__))
+
+ self._discovery_data = data
+
+ # Gracefully shut down MainLoop
+ self.halt()
+
+ def transcode(self, src, dst, **kw):
+ self._discovery_data = kw.get('data', self.discover(src))
+
+ self.__on_progress = kw.get('progress_callback')
+
+ quality = kw.get('quality', 0.3)
+
+ # 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 ! '
+ 'progressreport silent=true ! '
+ 'filesink location="{dst}"'.format(
+ src=src,
+ tolerance=80000000,
+ quality=quality,
+ dst=dst))
+
+ self.bus = self.pipeline.get_bus()
+ self.bus.add_signal_watch()
+ self.bus.connect('message', self.__on_bus_message)
+
+ self.pipeline.set_state(gst.STATE_PLAYING)
+
+ self._loop.run()
+
+ def __on_bus_message(self, bus, message):
+ _log.debug(message)
+
+ if (message.type == gst.MESSAGE_ELEMENT
+ and message.structure.get_name() == 'progress'):
+ data = dict(message.structure)
+
+ if self.__on_progress:
+ self.__on_progress(data)
+
+ _log.info('{0}% done...'.format(
+ data.get('percent')))
+ elif message.type == gst.MESSAGE_EOS:
+ _log.info('Done')
+ self.halt()
+
+ def halt(self):
+ _log.info('Quitting MainLoop gracefully...')
+ gobject.idle_add(self._loop.quit)
+
+if __name__ == '__main__':
+ import sys
+ logging.basicConfig()
+ _log.setLevel(logging.INFO)
+
+ transcoder = AudioTranscoder()
+ data = transcoder.discover(sys.argv[1])
+ res = transcoder.transcode(*sys.argv[1:3])
+
+ pdb.set_trace()
def process_video(entry):
"""
- Code to process a video
-
- Much of this code is derived from the arista-transcoder script in
- the arista PyPI package and changed to match the needs of
- MediaGoblin
-
- This function sets up the arista video encoder in some kind of new thread
- and attaches callbacks to that child process, hopefully, the
- entry-complete callback will be called when the video is done.
+ Process a video entry, transcode the queued media files (originals) and
+ create a thumbnail for the entry.
"""
video_config = mgg.global_config['media_type:mediagoblin.media_types.video']
entry,
'{original}-640p.webm'.format(
original=os.path.splitext(
- queued_filepath[-1])[0] # Select the
+ queued_filepath[-1])[0] # Select the file name without .ext
))
thumbnail_filepath = create_pub_filepath(
pass
except ImportError:
_log.warning('Could not import multiprocessing, defaulting to 2 CPU cores')
- pass
try:
import gtk
-except:
+except ImportError:
raise Exception('Could not find pygtk')
try:
import gobject
gobject.threads_init()
-except:
+except ImportError:
raise Exception('gobject could not be found')
try:
pygst.require('0.10')
import gst
from gst.extend import discoverer
-except:
+except ImportError:
raise Exception('gst/pygst 0.10 could not be found')
--- /dev/null
+{#
+# 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/>.
+#}
+
+{% extends 'mediagoblin/user_pages/media.html' %}
+
+{% block mediagoblin_media %}
+ <div class="audio-media">
+ <audio controls="controls"
+ preload="metadata">
+ <source src="{{ request.app.public_store.file_url(
+ media.media_files.ogg) }}" type="video/webm; encoding="vorbis"" />
+ <div class="no_html5">
+ {%- trans -%}Sorry, this audio will not work because
+ your web browser does not support HTML5
+ audio.{%- endtrans -%}<br/>
+ {%- trans -%}You can get a modern web browser that
+ can play the audio at <a href="http://getfirefox.com">
+ http://getfirefox.com</a>!{%- endtrans -%}
+ </div>
+ </audio>
+ </div>
+ {% if 'original' in media.media_files %}
+ <p>
+ <a href="{{ request.app.public_store.file_url(
+ media.media_files['original']) }}">
+ {%- trans -%}
+ Original
+ {%- endtrans -%}
+ </a>
+ </p>
+ {% endif %}
+{% endblock %}