# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS.
+# 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
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import sys
+import logging
+import shutil
+import tempfile
+
+from mediagoblin.tools.pluginapi import hook_handle
+from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
+
+_log = logging.getLogger(__name__)
+
class FileTypeNotSupported(Exception):
pass
-class InvalidFileType(Exception):
+
+class TypeNotFound(FileTypeNotSupported):
+ '''Raised if no mediagoblin plugin supporting this file type was found'''
+ pass
+
+
+class MissingComponents(FileTypeNotSupported):
+ '''Raised if plugin found, but it can't process the file for some reason'''
pass
-MEDIA_TYPES = [
- 'mediagoblin.media_types.image',
- 'mediagoblin.media_types.video']
+class MediaManagerBase(object):
+ "Base class for all media managers"
-def get_media_types():
- for media_type in MEDIA_TYPES:
- yield media_type
+ # Please override in actual media managers
+ media_fetch_order = None
+ @staticmethod
+ def sniff_handler(*args, **kwargs):
+ return False
-def get_media_managers():
- for media_type in get_media_types():
- '''
- FIXME
- __import__ returns the lowest-level module. If the plugin is located
- outside the conventional plugin module tree, it will not be loaded
- properly because of the [...]ugin.media_types.
+ def __init__(self, entry):
+ self.entry = entry
- We need this if we want to support a separate site-specific plugin
- folder.
- '''
- try:
- __import__(media_type)
- except ImportError as e:
- raise Exception('ERROR: Could not import {0}: {1}'.format(media_type, e))
-
- yield media_type, sys.modules[media_type].MEDIA_MANAGER
+ def __getitem__(self, i):
+ return getattr(self, i)
+ def __contains__(self, i):
+ return hasattr(self, i)
-def get_media_manager(_media_type = None):
- for media_type, manager in get_media_managers():
- if media_type in _media_type:
- return manager
+def sniff_media_contents(media_file, filename):
+ '''
+ Check media contents using 'expensive' scanning. For example, for video it
+ is checking the contents using gstreamer
+ :param media_file: file-like object with 'name' attribute
+ :param filename: expected filename of the media
+ '''
+ media_type = hook_handle('sniff_handler', media_file, filename)
+ if media_type:
+ _log.info('{0} accepts the file'.format(media_type))
+ return media_type, hook_handle(('media_manager', media_type))
+ else:
+ _log.debug('{0} did not accept the file'.format(media_type))
+ raise FileTypeNotSupported(
+ # TODO: Provide information on which file types are supported
+ _(u'Sorry, I don\'t support that file type :('))
def get_media_type_and_manager(filename):
- for media_type, manager in get_media_managers():
- if filename.find('.') > 0:
- ext = os.path.splitext(filename)[1].lower()
+ '''
+ 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.
+
+ This hook is deprecated, 'type_match_handler' should be used instead
+ '''
+ if os.path.basename(filename).find('.') > 0:
+ # Get the file extension
+ ext = os.path.splitext(filename)[1].lower()
+
+ # Omit the dot from the extension and match it against
+ # the media manager
+ if hook_handle('get_media_type_and_manager', ext[1:]):
+ return hook_handle('get_media_type_and_manager', ext[1:])
+ else:
+ _log.info('File {0} has no file extension, let\'s hope the sniffers get it.'.format(
+ filename))
+
+ raise TypeNotFound(
+ _(u'Sorry, I don\'t support that file type :('))
+
+def type_match_handler(media_file, filename):
+ '''Check media file by name and then by content
+
+ Try to find the media type based on the file name, extension
+ specifically. After that, if media type is one of supported ones, check the
+ contents of the file
+ '''
+ if os.path.basename(filename).find('.') > 0:
+ # Get the file extension
+ ext = os.path.splitext(filename)[1].lower()
+
+ # Omit the dot from the extension and match it against
+ # the media manager
+ hook_result = hook_handle('type_match_handler', ext[1:])
+ if hook_result:
+ _log.info('Info about file found, checking further')
+ MEDIA_TYPE, Manager, sniffer = hook_result
+ if not sniffer:
+ _log.debug('sniffer is None, plugin trusts the extension')
+ return MEDIA_TYPE, Manager
+ _log.info('checking the contents with sniffer')
+ try:
+ sniffer(media_file)
+ _log.info('checked, found')
+ return MEDIA_TYPE, Manager
+ except Exception as e:
+ _log.info('sniffer says it will not accept the file')
+ _log.debug(e)
+ raise
else:
- raise InvalidFileType(
- 'Could not find any file extension in "{0}"'.format(
- filename))
+ _log.info('No plugins handled extension {0}'.format(ext))
+ else:
+ _log.info('File {0} has no known file extension, let\'s hope '
+ 'the sniffers get it.'.format(filename))
+ raise TypeNotFound(_(u'Sorry, I don\'t support that file type :('))
+
+
+def sniff_media(media_file, filename):
+ '''
+ Iterate through the enabled media types and find those suited
+ for a certain file.
+ '''
+ # copy the contents to a .name-enabled temporary file for further checks
+ # TODO: there are cases when copying is not required
+ tmp_media_file = tempfile.NamedTemporaryFile()
+ shutil.copyfileobj(media_file, tmp_media_file)
+ media_file.seek(0)
+ tmp_media_file.seek(0)
+ try:
+ return type_match_handler(tmp_media_file, filename)
+ except TypeNotFound as e:
+ _log.info('No plugins using two-step checking found')
+
+ # keep trying, using old `get_media_type_and_manager`
+ try:
+ return get_media_type_and_manager(filename)
+ except TypeNotFound as e:
+ # again, no luck. Do it expensive way
+ _log.info('No media handler found by file extension')
+ _log.info('Doing it the expensive way...')
+ return sniff_media_contents(tmp_media_file, filename)
- if ext[1:] in manager['accepted_extensions']:
- return media_type, manager