# -*- coding: utf-8 -*- # Copyright 2011-2012 Antoine Bertin # # This file is part of subliminal. # # subliminal is free software; you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # subliminal 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 Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with subliminal. If not, see . from .core import (SERVICES, LANGUAGE_INDEX, SERVICE_INDEX, SERVICE_CONFIDENCE, MATCHING_CONFIDENCE, create_list_tasks, consume_task, create_download_tasks, group_by_video, key_subtitles) from .language import language_set, language_list, LANGUAGES import logging from six import string_types, text_type, iteritems, itervalues __all__ = ['list_subtitles', 'download_subtitles'] logger = logging.getLogger("subliminal") def list_subtitles(paths, languages=None, services=None, force=True, multi=False, cache_dir=None, max_depth=3, scan_filter=None, os_auth=None, os_hash=True): """List subtitles in given paths according to the criteria :param paths: path(s) to video file or folder :type paths: string or list :param languages: languages to search for, in preferred order :type languages: list of :class:`~subliminal.language.Language` or string :param list services: services to use for the search, in preferred order :param bool force: force searching for subtitles even if some are detected :param bool multi: search multiple languages for the same video :param string cache_dir: path to the cache directory to use :param int max_depth: maximum depth for scanning entries :param function scan_filter: filter function that takes a path as argument and returns a boolean indicating whether it has to be filtered out (``True``) or not (``False``) :return: found subtitles :rtype: dict of :class:`~subliminal.videos.Video` => [:class:`~subliminal.subtitles.ResultSubtitle`] """ services = services or SERVICES languages = language_set(languages) if languages is not None else language_set(LANGUAGES) if isinstance(paths, string_types): paths = [paths] if any([not isinstance(p, text_type) for p in paths]): logger.warning(u'Not all entries are unicode') results = [] service_instances = {} tasks = create_list_tasks(paths, languages, services, force, multi, cache_dir, max_depth, scan_filter) for task in tasks: try: if 'opensubtitles' == task.service: setattr(task.config, 'enforce_hash', os_hash) result = consume_task(task, service_instances, os_auth=os_auth) results.append((task.video, result)) except: logger.error(u'Error consuming task %r' % task, exc_info=True) for service_instance in itervalues(service_instances): service_instance.terminate() return group_by_video(results) def download_subtitles(paths, languages=None, services=None, force=True, multi=False, cache_dir=None, max_depth=3, scan_filter=None, order=None, os_auth=None, os_hash=True): """Download subtitles in given paths according to the criteria :param paths: path(s) to video file or folder :type paths: string or list :param languages: languages to search for, in preferred order :type languages: list of :class:`~subliminal.language.Language` or string :param list services: services to use for the search, in preferred order :param bool force: force searching for subtitles even if some are detected :param bool multi: search multiple languages for the same video :param string cache_dir: path to the cache directory to use :param int max_depth: maximum depth for scanning entries :param function scan_filter: filter function that takes a path as argument and returns a boolean indicating whether it has to be filtered out (``True``) or not (``False``) :param order: preferred order for subtitles sorting :type list: list of :data:`~subliminal.core.LANGUAGE_INDEX`, :data:`~subliminal.core.SERVICE_INDEX`, :data:`~subliminal.core.SERVICE_CONFIDENCE`, :data:`~subliminal.core.MATCHING_CONFIDENCE` :param os_auth: username and password :type os_auth: list :return: downloaded subtitles :rtype: dict of :class:`~subliminal.videos.Video` => [:class:`~subliminal.subtitles.ResultSubtitle`] .. note:: If you use ``multi=True``, :data:`~subliminal.core.LANGUAGE_INDEX` has to be the first item of the ``order`` list or you might get unexpected results. """ services = services or SERVICES languages = language_list(languages) if languages is not None else language_list(LANGUAGES) if isinstance(paths, string_types): paths = [paths] order = order or [LANGUAGE_INDEX, SERVICE_INDEX, SERVICE_CONFIDENCE, MATCHING_CONFIDENCE] subtitles_by_video = list_subtitles(paths, languages, services, force, multi, cache_dir, max_depth, scan_filter, os_auth=os_auth, os_hash=os_hash) for video, subtitles in iteritems(subtitles_by_video): try: subtitles.sort(key=lambda s: key_subtitles(s, video, languages, services, order), reverse=True) except StopIteration: break results = [] service_instances = {} tasks = create_download_tasks(subtitles_by_video, languages, multi) for task in tasks: try: result = consume_task(task, service_instances) results.append((task.video, result)) except: logger.error(u'Error consuming task %r' % task, exc_info=True) for service_instance in itervalues(service_instances): service_instance.terminate() return group_by_video(results)