Example #1
0
    def get_subtitles(self, entry):
        if (
            entry.get('subtitles', eval_lazy=False)
            or not ('location' in entry)
            or ('$RECYCLE.BIN' in entry['location'])
            or not os.path.exists(entry['location'])
        ):
            return
        from subliminal import scan_video
        from subliminal.core import search_external_subtitles, refine

        try:
            video = scan_video(entry['location'])
            # grab external and internal subtitles
            subtitles = video.subtitle_languages
            refiner = ('metadata',)
            refine(video, episode_refiners=refiner, movie_refiners=refiner)
            subtitles |= set(search_external_subtitles(entry['location']).values())
            if subtitles:
                # convert to human-readable strings
                subtitles = [str(l) for l in subtitles]
                entry['subtitles'] = subtitles
                log.debug('Found subtitles %s for %s', '/'.join(subtitles), entry['title'])
        except Exception as e:
            log.error('Error checking local subtitles for %s: %s', entry['title'], e)
Example #2
0
    def get_subtitles(self, entry):
        if (entry.get('subtitles', eval_lazy=False)
                or not ('location' in entry)
                or ('$RECYCLE.BIN' in entry['location'])
                or not os.path.exists(entry['location'])):
            return
        from subliminal import scan_video
        from subliminal.core import search_external_subtitles, refine

        try:
            video = scan_video(entry['location'])
            # grab external and internal subtitles
            subtitles = video.subtitle_languages
            refiner = ('metadata', )
            refine(video, episode_refiners=refiner, movie_refiners=refiner)
            subtitles |= set(
                search_external_subtitles(entry['location']).values())
            if subtitles:
                # convert to human-readable strings
                subtitles = [str(l) for l in subtitles]
                entry['subtitles'] = subtitles
                log.debug('Found subtitles %s for %s', '/'.join(subtitles),
                          entry['title'])
        except Exception as e:
            log.error('Error checking local subtitles for %s: %s',
                      entry['title'], e)
Example #3
0
def test_refine_video_metadata(mkv):
    scanned_video = scan_video(mkv['test5'])
    refine(scanned_video,
           episode_refiners=('metadata', ),
           movie_refiners=('metadata', ))
    assert type(scanned_video) is Movie
    assert scanned_video.name == mkv['test5']
    assert scanned_video.source is None
    assert scanned_video.release_group is None
    assert scanned_video.resolution is None
    assert scanned_video.video_codec == 'H.264'
    assert scanned_video.audio_codec == 'AAC'
    assert scanned_video.imdb_id is None
    assert scanned_video.hashes == {
        'opensubtitles': '49e2530ea3bd0d18',
        # 'shooter': '36f3e2c50566ca01f939bf15d8031432;b6132ab62b8f7d4aaabe9d6344b90d90;'
        #           'bea6074cef7f1de85794f3941530ba8b;18db05758d5d0d96f246249e4e4b5d79',
        'thesubdb': '64a8b87f12daa4f31895616e6c3fd39e'
    }
    assert scanned_video.size == 31762747
    assert scanned_video.subtitle_languages == {
        Language('spa'),
        Language('deu'),
        Language('jpn'),
        Language('und'),
        Language('ita'),
        Language('fra'),
        Language('hun')
    }
    assert scanned_video.title == 'test5'
    assert scanned_video.year is None
Example #4
0
def test_refine_video_metadata(mkv):
    scanned_video = scan_video(mkv['test5'])
    refine(scanned_video, episode_refiners=('metadata',), movie_refiners=('metadata',))
    assert type(scanned_video) is Movie
    assert scanned_video.name == mkv['test5']
    assert scanned_video.format is None
    assert scanned_video.release_group is None
    assert scanned_video.resolution is None
    assert scanned_video.video_codec == 'h264'
    assert scanned_video.audio_codec == 'AAC'
    assert scanned_video.imdb_id is None
    assert scanned_video.hashes == {
        'napiprojekt': 'de2e9caa58dd53a6ab9d241e6b252e35',
        'opensubtitles': '49e2530ea3bd0d18',
        'shooter': '36f3e2c50566ca01f939bf15d8031432;b6132ab62b8f7d4aaabe9d6344b90d90;'
                   'bea6074cef7f1de85794f3941530ba8b;18db05758d5d0d96f246249e4e4b5d79',
        'thesubdb': '64a8b87f12daa4f31895616e6c3fd39e'}
    assert scanned_video.size == 31762747
    assert scanned_video.subtitle_languages == {Language('spa'), Language('deu'), Language('jpn'), Language('und'),
                                                Language('ita'), Language('fra'), Language('hun')}
    assert scanned_video.title == 'test5'
    assert scanned_video.year is None
Example #5
0
    def on_task_output(self, task, config):
        """
        Configuration::
            subliminal:
                languages: List of languages (as IETF codes) in order of preference. At least one is required.
                alternatives: List of second-choice languages; subs will be downloaded but entries rejected.
                exact_match: Use file hash only to search for subs, otherwise Subliminal will try to guess by filename.
                providers: List of providers from where to download subtitles.
                single: Download subtitles in single mode (no language code added to subtitle filename).
                directory: Path to directory where to save the subtitles, default is next to the video.
                hearing_impaired: Prefer subtitles for the hearing impaired when available
                authentication: >
                  Dictionary of configuration options for different providers.
                  Keys correspond to provider names, and values are dictionaries, usually specifying `username` and
                  `password`.
        """
        if not task.accepted:
            logger.debug('nothing accepted, aborting')
            return
        import subliminal
        from babelfish import Language
        from dogpile.cache.exception import RegionAlreadyConfigured
        from subliminal import save_subtitles, scan_video
        from subliminal.cli import MutexLock
        from subliminal.core import (
            ARCHIVE_EXTENSIONS,
            refine,
            scan_archive,
            search_external_subtitles,
        )
        from subliminal.score import episode_scores, movie_scores
        from subliminal.video import VIDEO_EXTENSIONS

        try:
            subliminal.region.configure(
                'dogpile.cache.dbm',
                arguments={
                    'filename': os.path.join(tempfile.gettempdir(), 'cachefile.dbm'),
                    'lock_factory': MutexLock,
                },
            )
        except RegionAlreadyConfigured:
            pass

        # Let subliminal be more verbose if our logger is set to DEBUG
        if logger.level(task.manager.options.loglevel).no <= logger.level('DEBUG').no:
            logging.getLogger("subliminal").setLevel(logging.INFO)
        else:
            logging.getLogger("subliminal").setLevel(logging.CRITICAL)

        logging.getLogger("dogpile").setLevel(logging.CRITICAL)
        logging.getLogger("enzyme").setLevel(logging.WARNING)
        try:
            languages = set([Language.fromietf(s) for s in config.get('languages', [])])
            alternative_languages = set(
                [Language.fromietf(s) for s in config.get('alternatives', [])]
            )
        except ValueError as e:
            raise plugin.PluginError(e)
        # keep all downloaded subtitles and save to disk when done (no need to write every time)
        downloaded_subtitles = collections.defaultdict(list)
        providers_list = config.get('providers', None)
        provider_configs = config.get('authentication', None)
        # test if only one language was provided, if so we will download in single mode
        # (aka no language code added to subtitle filename)
        # unless we are forced not to by configuration
        # if we pass 'yes' for single in configuration but choose more than one language
        # we ignore the configuration and add the language code to the
        # potentially downloaded files
        single_mode = config.get('single', '') and len(languages | alternative_languages) <= 1
        hearing_impaired = config.get('hearing_impaired', False)

        with subliminal.core.ProviderPool(
            providers=providers_list, provider_configs=provider_configs
        ) as provider_pool:
            for entry in task.accepted:
                if 'location' not in entry:
                    logger.warning('Cannot act on entries that do not represent a local file.')
                    continue
                if not os.path.exists(entry['location']):
                    entry.fail('file not found: %s' % entry['location'])
                    continue
                if '$RECYCLE.BIN' in entry['location']:  # ignore deleted files in Windows shares
                    continue

                try:
                    entry_languages = set(entry.get('subtitle_languages', [])) or languages

                    if entry['location'].endswith(VIDEO_EXTENSIONS):
                        video = scan_video(entry['location'])
                    elif entry['location'].endswith(ARCHIVE_EXTENSIONS):
                        video = scan_archive(entry['location'])
                    else:
                        entry.reject(
                            'File extension is not a supported video or archive extension'
                        )
                        continue
                    # use metadata refiner to get mkv metadata
                    refiner = ('metadata',)
                    refine(video, episode_refiners=refiner, movie_refiners=refiner)
                    existing_subtitles = set(search_external_subtitles(entry['location']).values())
                    video.subtitle_languages |= existing_subtitles
                    if isinstance(video, subliminal.Episode):
                        title = video.series
                        hash_scores = episode_scores['hash']
                    else:
                        title = video.title
                        hash_scores = movie_scores['hash']
                    logger.info('Name computed for {} was {}', entry['location'], title)
                    msc = hash_scores if config['exact_match'] else 0
                    if entry_languages.issubset(video.subtitle_languages):
                        logger.debug(
                            'All preferred languages already exist for "{}"', entry['title']
                        )
                        entry['subtitles_missing'] = set()
                        continue  # subs for preferred lang(s) already exists
                    else:
                        # Gather the subtitles for the alternative languages too, to avoid needing to search the sites
                        # again. They'll just be ignored if the main languages are found.
                        all_subtitles = provider_pool.list_subtitles(
                            video, entry_languages | alternative_languages
                        )
                        try:
                            subtitles = provider_pool.download_best_subtitles(
                                all_subtitles,
                                video,
                                entry_languages,
                                min_score=msc,
                                hearing_impaired=hearing_impaired,
                            )
                        except TypeError as e:
                            logger.error(
                                'Downloading subtitles failed due to a bug in subliminal. Please seehttps://github.com/Diaoul/subliminal/issues/921. Error: {}',
                                e,
                            )
                            subtitles = []
                        if subtitles:
                            downloaded_subtitles[video].extend(subtitles)
                            logger.info('Subtitles found for {}', entry['location'])
                        else:
                            # only try to download for alternatives that aren't already downloaded
                            subtitles = provider_pool.download_best_subtitles(
                                all_subtitles,
                                video,
                                alternative_languages,
                                min_score=msc,
                                hearing_impaired=hearing_impaired,
                            )

                            if subtitles:
                                downloaded_subtitles[video].extend(subtitles)
                                entry.reject('subtitles found for a second-choice language.')
                            else:
                                entry.reject('cannot find any subtitles for now.')

                        downloaded_languages = set(
                            [Language.fromietf(str(l.language)) for l in subtitles]
                        )
                        if entry_languages:
                            entry['subtitles_missing'] = entry_languages - downloaded_languages
                            if len(entry['subtitles_missing']) > 0:
                                entry.reject('Subtitles for all primary languages not found')
                except ValueError as e:
                    logger.error('subliminal error: {}', e)
                    entry.fail()

        if downloaded_subtitles:
            if task.options.test:
                logger.verbose('Test mode. Found subtitles:')
            # save subtitles to disk
            for video, subtitle in downloaded_subtitles.items():
                if subtitle:
                    _directory = config.get('directory')
                    if _directory:
                        _directory = os.path.expanduser(_directory)
                    if task.options.test:
                        logger.verbose(
                            '     FOUND LANGUAGES {} for {}',
                            [str(l.language) for l in subtitle],
                            video.name,
                        )
                        continue
                    save_subtitles(video, subtitle, single=single_mode, directory=_directory)
Example #6
0
    def on_task_output(self, task, config):
        """
        Configuration::
            subliminal:
                languages: List of languages (as IETF codes) in order of preference. At least one is required.
                alternatives: List of second-choice languages; subs will be downloaded but entries rejected.
                exact_match: Use file hash only to search for subs, otherwise Subliminal will try to guess by filename.
                providers: List of providers from where to download subtitles.
                single: Download subtitles in single mode (no language code added to subtitle filename).
                directory: Path to directory where to save the subtitles, default is next to the video.
                hearing_impaired: Prefer subtitles for the hearing impaired when available
                authentication: >
                  Dictionary of configuration options for different providers.
                  Keys correspond to provider names, and values are dictionaries, usually specifying `username` and
                  `password`.
        """
        if not task.accepted:
            log.debug('nothing accepted, aborting')
            return
        from babelfish import Language
        from dogpile.cache.exception import RegionAlreadyConfigured
        import subliminal
        from subliminal import scan_video, save_subtitles
        from subliminal.cli import MutexLock
        from subliminal.core import ARCHIVE_EXTENSIONS, scan_archive, refine, search_external_subtitles
        from subliminal.score import episode_scores, movie_scores
        from subliminal.video import VIDEO_EXTENSIONS
        try:
            subliminal.region.configure('dogpile.cache.dbm',
                                        arguments={
                                            'filename': os.path.join(tempfile.gettempdir(), 'cachefile.dbm'),
                                            'lock_factory': MutexLock,
                                        })
        except RegionAlreadyConfigured:
            pass

        # Let subliminal be more verbose if our logger is set to DEBUG
        if log.isEnabledFor(logging.DEBUG):
            logging.getLogger("subliminal").setLevel(logging.INFO)
        else:
            logging.getLogger("subliminal").setLevel(logging.CRITICAL)

        logging.getLogger("dogpile").setLevel(logging.CRITICAL)
        logging.getLogger("enzyme").setLevel(logging.WARNING)
        try:
            languages = set([Language.fromietf(s) for s in config.get('languages', [])])
            alternative_languages = set([Language.fromietf(s) for s in config.get('alternatives', [])])
        except ValueError as e:
            raise plugin.PluginError(e)
        # keep all downloaded subtitles and save to disk when done (no need to write every time)
        downloaded_subtitles = collections.defaultdict(list)
        providers_list = config.get('providers', None)
        provider_configs = config.get('authentication', None)
        # test if only one language was provided, if so we will download in single mode
        # (aka no language code added to subtitle filename)
        # unless we are forced not to by configuration
        # if we pass 'yes' for single in configuration but choose more than one language
        # we ignore the configuration and add the language code to the
        # potentially downloaded files
        single_mode = config.get('single', '') and len(languages | alternative_languages) <= 1
        hearing_impaired = config.get('hearing_impaired', False)

        with subliminal.core.ProviderPool(providers=providers_list, provider_configs=provider_configs) as provider_pool:
            for entry in task.accepted:
                if 'location' not in entry:
                    log.warning('Cannot act on entries that do not represent a local file.')
                    continue
                if not os.path.exists(entry['location']):
                    entry.fail('file not found: %s' % entry['location'])
                    continue
                if '$RECYCLE.BIN' in entry['location']:  # ignore deleted files in Windows shares
                    continue

                try:
                    entry_languages = set(entry.get('subtitle_languages', [])) or languages

                    if entry['location'].endswith(VIDEO_EXTENSIONS):
                        video = scan_video(entry['location'])
                    elif entry['location'].endswith(ARCHIVE_EXTENSIONS):
                        video = scan_archive(entry['location'])
                    else:
                        entry.reject('File extension is not a supported video or archive extension')
                        continue
                    # use metadata refiner to get mkv metadata
                    refiner = ('metadata',)
                    refine(video, episode_refiners=refiner, movie_refiners=refiner)
                    existing_subtitles = set(search_external_subtitles(entry['location']).values())
                    video.subtitle_languages |= existing_subtitles
                    if isinstance(video, subliminal.Episode):
                        title = video.series
                        hash_scores = episode_scores['hash']
                    else:
                        title = video.title
                        hash_scores = movie_scores['hash']
                    log.info('Name computed for %s was %s', entry['location'], title)
                    msc = hash_scores if config['exact_match'] else 0
                    if entry_languages.issubset(video.subtitle_languages):
                        log.debug('All preferred languages already exist for "%s"', entry['title'])
                        entry['subtitles_missing'] = set()
                        continue  # subs for preferred lang(s) already exists
                    else:
                        # Gather the subtitles for the alternative languages too, to avoid needing to search the sites
                        # again. They'll just be ignored if the main languages are found.
                        all_subtitles = provider_pool.list_subtitles(video, entry_languages | alternative_languages)

                        subtitles = provider_pool.download_best_subtitles(all_subtitles, video, entry_languages,
                                                                          min_score=msc,
                                                                          hearing_impaired=hearing_impaired)
                        if subtitles:
                            downloaded_subtitles[video].extend(subtitles)
                            log.info('Subtitles found for %s', entry['location'])
                        else:
                            # only try to download for alternatives that aren't alread downloaded
                            subtitles = provider_pool.download_best_subtitles(all_subtitles, video,
                                                                              alternative_languages, min_score=msc,
                                                                              hearing_impaired=hearing_impaired)

                            if subtitles:
                                downloaded_subtitles[video].extend(subtitles)
                                entry.reject('subtitles found for a second-choice language.')
                            else:
                                entry.reject('cannot find any subtitles for now.')

                        downloaded_languages = set([Language.fromietf(str(l.language))
                                                    for l in subtitles])
                        if entry_languages:
                            entry['subtitles_missing'] = entry_languages - downloaded_languages
                            if len(entry['subtitles_missing']) > 0:
                                entry.reject('Subtitles for all primary languages not found')
                except ValueError as e:
                    log.error('subliminal error: %s', e)
                    entry.fail()

        if downloaded_subtitles:
            if task.options.test:
                log.verbose('Test mode. Found subtitles:')
            # save subtitles to disk
            for video, subtitle in downloaded_subtitles.items():
                if subtitle:
                    _directory = config.get('directory')
                    if _directory:
                        _directory = os.path.expanduser(_directory)
                    if task.options.test:
                        log.verbose('     FOUND LANGUAGES %s for %s', [str(l.language) for l in subtitle], video.name)
                        continue
                    save_subtitles(video, subtitle, single=single_mode, directory=_directory)