# 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 . import os 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 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 class MediaManagerBase(object): "Base class for all media managers" # Please override in actual media managers media_fetch_order = None @staticmethod def sniff_handler(*args, **kwargs): return False def __init__(self, entry): self.entry = entry def __getitem__(self, i): return getattr(self, i) def __contains__(self, i): return hasattr(self, i) 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): ''' 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: _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)