Ejemplo n.º 1
0
def get_video(video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None):
    if not subtitles_path:
        subtitles_path = get_subtitles_path(video_path)

    try:
        # Encode paths to UTF-8 to ensure subliminal support.
        video_path = video_path.encode('utf-8')
        subtitles_path = subtitles_path.encode('utf-8')
    except UnicodeEncodeError:
        # Fallback to system encoding. This should never happen.
        video_path = video_path.encode(sickbeard.SYS_ENCODING)
        subtitles_path = subtitles_path.encode(sickbeard.SYS_ENCODING)

    try:
        video = subliminal.scan_video(video_path)

        # external subtitles
        if subtitles:
            video.subtitle_languages |= \
                set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(not sickbeard.EMBEDDED_SUBTITLES_ALL and video_path.endswith('.mkv'))

        subliminal.refine(video, embedded_subtitles=embedded_subtitles)
    except Exception as error:
        logger.log(u'Exception: {0}'.format(error), logger.DEBUG)
        return None

    return video
Ejemplo n.º 2
0
def get_video(video_path,
              subtitles_path=None,
              subtitles=True,
              embedded_subtitles=None,
              episode=None):
    if not subtitles_path:
        subtitles_path = get_subtitles_path(video_path)

    try:
        video = subliminal.scan_video(video_path)

        # external subtitles
        if subtitles:
            video.subtitle_languages |= \
                set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(
                not sickrage.app.config.embedded_subtitles_all
                and video_path.endswith('.mkv'))

        # Let sickrage add more information to video file, based on the metadata.
        if episode:
            refine_video(video, episode)

        subliminal.refine(video, embedded_subtitles=embedded_subtitles)
    except Exception as error:
        sickrage.app.log.debug('Exception: {}'.format(error))
        return None

    # remove format metadata
    video.format = ""

    return video
Ejemplo n.º 3
0
def get_video(video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None, episode=None):
    if not subtitles_path:
        subtitles_path = get_subtitles_path(video_path)

    try:
        video = subliminal.scan_video(video_path)
    except Exception as error:
        sickrage.app.log.debug('Exception: {}'.format(error))
    else:
        if video.size > 10485760:
            video.hashes['itasa'] = hash_itasa(video_path)

        # external subtitles
        if subtitles:
            video.subtitle_languages |= \
                set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(
                not sickrage.app.config.embedded_subtitles_all and video_path.endswith('.mkv'))

        subliminal.refine(video, episode_refiners=episode_refiners, embedded_subtitles=embedded_subtitles,
                          release_name=episode.name, tv_episode=episode)

        video.alternative_series = list(get_scene_exceptions(episode.show.indexerid))

        # remove format metadata
        video.format = ""

        return video
Ejemplo n.º 4
0
    def get_video(self, video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None, episode_object=None):
        if not subtitles_path:
            subtitles_path = self.get_subtitles_path(video_path)

        try:
            video = subliminal.scan_video(video_path)
        except Exception as error:
            sickrage.app.log.debug('Exception: {}'.format(error))
        else:
            if video.size > 10485760:
                video.hashes['itasa'] = hash_itasa(video_path)

            # external subtitles
            if subtitles:
                video.subtitle_languages |= set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values())

            if embedded_subtitles is None:
                embedded_subtitles = bool(
                    not sickrage.app.config.embedded_subtitles_all and video_path.endswith('.mkv'))

            subliminal.refine(video, episode_refiners=self.episode_refiners, embedded_subtitles=embedded_subtitles, release_name=episode_object.name,
                              tv_episode=episode_object)

            from sickrage.core.scene_exceptions import get_scene_exceptions
            video.alternative_series = list(get_scene_exceptions(episode_object.show.indexer_id))

            # remove format metadata
            video.format = ""

            return video
Ejemplo n.º 5
0
def get_video(video_path,
              subtitles_path=None,
              subtitles=True,
              embedded_subtitles=None,
              episode=None):
    if not subtitles_path:
        subtitles_path = get_subtitles_path(video_path)

    try:
        video = subliminal.scan_video(video_path)

        # external subtitles
        if subtitles:
            video.subtitle_languages |= set(
                subliminal.core.search_external_subtitles(
                    video_path, directory=subtitles_path).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(not settings.EMBEDDED_SUBTITLES_ALL
                                      and video_path.endswith(".mkv"))

        # Let sickchill add more information to video file, based on the metadata.
        if episode:
            refine_video(video, episode)

        subliminal.refine(video, embedded_subtitles=embedded_subtitles)
    except Exception as error:
        logger.info(traceback.format_exc())
        logger.debug("Exception: {0}".format(error))
        return None

    return video
Ejemplo n.º 6
0
def get_video(video_path,
              subtitles_path=None,
              subtitles=True,
              embedded_subtitles=None):
    if not subtitles_path:
        subtitles_path = get_subtitles_path(video_path)

    try:
        # Encode paths to UTF-8 to ensure subliminal support.
        video_path = video_path.encode('utf-8')
        subtitles_path = subtitles_path.encode('utf-8')
    except UnicodeEncodeError:
        # Fallback to system encoding. This should never happen.
        video_path = video_path.encode(sickbeard.SYS_ENCODING)
        subtitles_path = subtitles_path.encode(sickbeard.SYS_ENCODING)

    try:
        video = subliminal.scan_video(video_path)

        # external subtitles
        if subtitles:
            video.subtitle_languages |= \
                set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(not sickbeard.EMBEDDED_SUBTITLES_ALL
                                      and video_path.endswith('.mkv'))

        subliminal.refine(video, embedded_subtitles=embedded_subtitles)
    except Exception as error:
        logger.log(u'Exception: {0}'.format(error), logger.DEBUG)
        return None

    return video
Ejemplo n.º 7
0
def get_video(video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None, episode=None):
    if not subtitles_path:
        subtitles_path = get_subtitles_path(video_path)

    try:
        # Encode paths to UTF-8 to ensure subliminal support.
        video_path = video_path.encode('utf-8')
        subtitles_path = subtitles_path.encode('utf-8')
    except UnicodeEncodeError:
        # Fallback to system encoding. This should never happen.
        video_path = video_path.encode(sickrage.srCore.SYS_ENCODING)
        subtitles_path = subtitles_path.encode(sickrage.srCore.SYS_ENCODING)

    try:
        video = subliminal.scan_video(video_path)

        # external subtitles
        if subtitles:
            video.subtitle_languages |= \
                set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(
                not sickrage.srCore.srConfig.EMBEDDED_SUBTITLES_ALL and video_path.endswith('.mkv'))

        # Let sickrage add more information to video file, based on the metadata.
        if episode:
            refine_video(video, episode)

        subliminal.refine(video, embedded_subtitles=embedded_subtitles)
    except Exception as error:
        sickrage.srCore.srLogger.debug('Exception: {}'.format(error))
        return None

    return video
    def choose_callback(self, menuitem, files):
        # scan the video
        video = scan_video(files[0].get_location().get_path())
        refine(video,
               episode_refiners=self.config.refiners,
               movie_refiners=self.config.refiners,
               embedded_subtitles=False)

        # load the interface
        builder = Gtk.Builder()
        builder.set_translation_domain('subliminal')
        builder.add_from_file(
            os.path.join(os.path.dirname(__file__), 'subliminal', 'ui',
                         'choose.glade'))

        # set the video filename
        video_filename = builder.get_object('video_filename_label')
        video_filename.set_text(files[0].get_name())

        # start the spinner
        spinner = builder.get_object('spinner')
        spinner.start()

        def _list_subtitles():
            # list subtitles
            with AsyncProviderPool(
                    providers=self.config.providers,
                    provider_configs=self.config.provider_configs) as pool:
                subtitles = pool.list_subtitles(video, self.config.languages)

            # fill the subtitle liststore
            subtitle_liststore = builder.get_object('subtitle_liststore')
            for s in subtitles:
                scaled_score = compute_score(s, video)
                scores = get_scores(video)
                if s.hearing_impaired == self.config.hearing_impaired:
                    scaled_score -= scores['hearing_impaired']
                scaled_score *= 100 / scores['hash']
                subtitle_liststore.append([
                    s.id,
                    nice_language(s.language), scaled_score,
                    s.provider_name.capitalize(), s.hearing_impaired,
                    s.page_link, False
                ])
            subtitle_liststore.set_sort_column_id(2, Gtk.SortType.DESCENDING)

            # stop the spinner
            spinner.stop()

            # connect signals
            builder.connect_signals(
                ChooseHandler(self.config, video, subtitles, spinner))

        threading.Thread(target=_list_subtitles).start()

        # display window
        window = builder.get_object('subtitle_window')
        window.show_all()
        Gtk.main()
Ejemplo n.º 9
0
def get_video(tv_episode, video_path, subtitles_dir=None, subtitles=True, embedded_subtitles=None, release_name=None):
    """Return the subliminal video for the given path.

    The video_path is used as a key to cache the video to avoid
    scanning and parsing the video metadata all the time

    :param tv_episode:
    :type tv_episode: medusa.tv.Episode
    :param video_path: the video path
    :type video_path: str
    :param subtitles_dir: the subtitles directory
    :type subtitles_dir: str or None
    :param subtitles: True if existing external subtitles should be taken into account
    :type subtitles: bool or None
    :param embedded_subtitles: True if embedded subtitles should be taken into account
    :type embedded_subtitles: bool or None
    :param release_name: the release name
    :type release_name: str or None
    :return: video
    :rtype: subliminal.video.Video
    """
    key = video_key.format(video_path=video_path)
    payload = {'subtitles_dir': subtitles_dir, 'subtitles': subtitles, 'embedded_subtitles': embedded_subtitles,
               'release_name': release_name}
    cached_payload = memory_cache.get(key, expiration_time=VIDEO_EXPIRATION_TIME)
    if cached_payload != NO_VALUE and {k: v for k, v in iteritems(cached_payload) if k != 'video'} == payload:
        logger.debug(u'Found cached video information under key %s', key)
        return cached_payload['video']

    video_is_mkv = video_path.endswith('.mkv')
    subtitles_dir = subtitles_dir or get_subtitles_dir(video_path)

    logger.debug(u'Scanning video %s...', video_path)

    try:
        video = scan_video(video_path)
    except ValueError as error:
        logger.warning(u'Unable to scan video: %s. Error: %r', video_path, error)
    else:

        if subtitles:
            video.subtitle_languages |= set(search_external_subtitles(video_path, directory=subtitles_dir).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(not app.IGNORE_EMBEDDED_SUBS and video_is_mkv)

        refine(video, episode_refiners=episode_refiners, embedded_subtitles=embedded_subtitles,
               release_name=release_name, tv_episode=tv_episode)

        video.alternative_series = [alias.title for alias in tv_episode.series.aliases]

        payload['video'] = video
        memory_cache.set(key, payload)
        logger.debug(u'Video information cached under key %s', key)

        return video
Ejemplo n.º 10
0
def get_embedded_subtitles(video_path):
    """Return all embedded subtitles for the given video path.

    :param video_path: video filename to be checked
    :type video_path: str
    :return:
    :rtype: set of Language
    """
    subliminal_video = scan_video(video_path)
    refine(subliminal_video, episode_refiners=('metadata', ))
    return subliminal_video.subtitle_languages
Ejemplo n.º 11
0
def get_embedded_subtitles(video_path):
    """Return all embedded subtitles for the given video path.

    :param video_path: video filename to be checked
    :type video_path: str
    :return:
    :rtype: set of Language
    """
    subliminal_video = scan_video(video_path)
    refine(subliminal_video, episode_refiners=('metadata',))
    return subliminal_video.subtitle_languages
Ejemplo n.º 12
0
def get_video(tv_episode, video_path, subtitles_dir=None, subtitles=True, embedded_subtitles=None, release_name=None):
    """Return the subliminal video for the given path.

    The video_path is used as a key to cache the video to avoid
    scanning and parsing the video metadata all the time

    :param tv_episode:
    :type tv_episode: sickbeard.tv.TVEpisode
    :param video_path: the video path
    :type video_path: str
    :param subtitles_dir: the subtitles directory
    :type subtitles_dir: str or None
    :param subtitles: True if existing external subtitles should be taken into account
    :type subtitles: bool or None
    :param embedded_subtitles: True if embedded subtitles should be taken into account
    :type embedded_subtitles: bool or None
    :param release_name: the release name
    :type release_name: str or None
    :return: video
    :rtype: subliminal.video.Video
    """
    key = video_key.format(video_path=video_path)
    payload = {'subtitles_dir': subtitles_dir, 'subtitles': subtitles, 'embedded_subtitles': embedded_subtitles,
               'release_name': release_name}
    cached_payload = region.get(key, expiration_time=VIDEO_EXPIRATION_TIME)
    if cached_payload != NO_VALUE and {k: v for k, v in iteritems(cached_payload) if k != 'video'} == payload:
        logger.debug(u'Found cached video information under key %s', key)
        return cached_payload['video']

    try:
        video_path = _encode(video_path)
        subtitles_dir = _encode(subtitles_dir or get_subtitles_dir(video_path))

        logger.debug(u'Scanning video %s...', video_path)
        video = scan_video(video_path)

        # external subtitles
        if subtitles:
            video.subtitle_languages |= set(search_external_subtitles(video_path, directory=subtitles_dir).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(not sickbeard.EMBEDDED_SUBTITLES_ALL and video_path.endswith('.mkv'))

        refine(video, episode_refiners=episode_refiners, embedded_subtitles=embedded_subtitles,
               release_name=release_name, tv_episode=tv_episode)

        payload['video'] = video
        region.set(key, payload)
        logger.debug(u'Video information cached under key %s', key)

        return video
    except Exception as error:
        logger.info(u'Exception: %s', error)
    def download_callback(self, menuitem, files):
        # scan videos
        videos = []
        for f in files:
            # ignore non-writable locations
            if not f.can_write():
                continue

            # directories
            if f.is_directory():
                try:
                    scanned_videos = scan_videos(f.get_location().get_path())
                except:
                    continue
                for video in scanned_videos:
                    if check_video(video, languages=self.config.languages, age=self.config.age,
                                   undefined=self.config.single):
                        video.subtitle_languages |= set(search_external_subtitles(video.name).values())
                        refine(video, episode_refiners=self.config.refiners, movie_refiners=self.config.refiners,
                               embedded_subtitles=self.config.embedded_subtitles)
                        videos.append(video)
                continue

            # other inputs
            try:
                video = scan_video(f.get_location().get_path())
            except:
                continue
            if check_video(video, languages=self.config.languages, undefined=self.config.single):
                video.subtitle_languages |= set(search_external_subtitles(video.name).values())
                refine(video, episode_refiners=self.config.refiners, movie_refiners=self.config.refiners,
                       embedded_subtitles=self.config.embedded_subtitles)
                videos.append(video)

        # download best subtitles
        downloaded_subtitles = defaultdict(list)
        with AsyncProviderPool(providers=self.config.providers, provider_configs=self.config.provider_configs) as pool:
            for v in videos:
                scores = get_scores(v)
                subtitles = pool.download_best_subtitles(
                    pool.list_subtitles(v, self.config.languages - v.subtitle_languages),
                    v, self.config.languages, min_score=scores['hash'] * self.config.min_score / 100,
                    hearing_impaired=self.config.hearing_impaired, only_one=self.config.single
                )
                downloaded_subtitles[v] = subtitles

        # save subtitles
        for v, subtitles in downloaded_subtitles.items():
            save_subtitles(v, subtitles, single=self.config.single)
    def choose_callback(self, menuitem, files):
        # scan the video
        video = scan_video(files[0].get_location().get_path())
        refine(video, episode_refiners=self.config.refiners, movie_refiners=self.config.refiners,
               embedded_subtitles=False)

        # load the interface
        builder = Gtk.Builder()
        builder.set_translation_domain('subliminal')
        builder.add_from_file(os.path.join(os.path.dirname(__file__), 'subliminal', 'ui', 'choose.glade'))

        # set the video filename
        video_filename = builder.get_object('video_filename_label')
        video_filename.set_text(files[0].get_name())

        # start the spinner
        spinner = builder.get_object('spinner')
        spinner.start()

        def _list_subtitles():
            # list subtitles
            with AsyncProviderPool(providers=self.config.providers,
                                   provider_configs=self.config.provider_configs) as pool:
                subtitles = pool.list_subtitles(video, self.config.languages)

            # fill the subtitle liststore
            subtitle_liststore = builder.get_object('subtitle_liststore')
            for s in subtitles:
                scaled_score = compute_score(s, video)
                scores = get_scores(video)
                if s.hearing_impaired == self.config.hearing_impaired:
                    scaled_score -= scores['hearing_impaired']
                scaled_score *= 100 / scores['hash']
                subtitle_liststore.append([s.id, nice_language(s.language), scaled_score, s.provider_name.capitalize(),
                                           s.hearing_impaired, s.page_link, False])
            subtitle_liststore.set_sort_column_id(2, Gtk.SortType.DESCENDING)

            # stop the spinner
            spinner.stop()

            # connect signals
            builder.connect_signals(ChooseHandler(self.config, video, subtitles, spinner))

        threading.Thread(target=_list_subtitles).start()

        # display window
        window = builder.get_object('subtitle_window')
        window.show_all()
        Gtk.main()
Ejemplo n.º 15
0
 def _refine_file(video_file) -> List[SeriesEpisodeInfo]:
     log.debug("Refining file %s", video_file)
     try:
         video = Video.fromname(video_file)
     except ValueError:
         log.error("Cannot guess video file type from: %s", video_file)
         return []
     refiner = sorted(refiner_manager.names())
     refine(video, episode_refiners=refiner, movie_refiners=refiner)
     log.debug("refine result: %r", video)
     if isinstance(video, Episode):
         log.debug("series: %s", video.series)
         log.debug("season: %s", video.season)
         if not video.season:
             log.error("No season defined !")
             return []
         if isinstance(video.season, list):
             video.season = video.season[0]
             log.error("Multi season found, only using first one: %s",
                       video.season)
         log.debug("episode: %s", video.episode)
         log.debug("title: %s", video.title)
         log.debug("series_tvdb_id: %s", video.series_tvdb_id)
         r = []
         # Support for double episode
         if not isinstance(video.episode, list):
             video.episode = [video.episode]
         for video_episode in video.episode:
             r.append(
                 SeriesEpisodeInfo(
                     series_episode_uid=SeriesEpisodeUid(
                         tv_db_id=video.series_tvdb_id,
                         season_number=video.season,
                         episode_number=video_episode,
                     ),
                     series_title=video.series,
                     episode_title=video.title,
                     quality=None,
                     video_languages=None,
                     subtitles_languages=None,
                     media_filename=video_file,
                     dirty=True,
                 ))
         return r
     elif isinstance(video, Movie):
         log.debug("movie: %s", video.title)
     return []
Ejemplo n.º 16
0
    def run(self, scan_path, scan_age, languages, *args, **kwargs):
        if not os.path.isdir(scan_path):
            raise IOError('Path \'%s\' doesn\'t exist!' % scan_path)
        if not scan_age >= 1:
            raise ValueError('\'scan_age\' must by at least 1!')
        if not len(languages) >= 1:
            raise ValueError('\'languages\' list can\'t be empty!')

        __tree_dict = lambda: defaultdict(__tree_dict)
        result = __tree_dict()

        age = timedelta(weeks=scan_age)
        languages = set([Language(l) for l in languages])

        scan_start = datetime.now()

        videos = []
        ignored_videos = []

        if not region.is_configured:
            region.configure('dogpile.cache.dbm', expiration_time=timedelta(days=30), arguments={'filename': 'subliminal.dbm', 'lock_factory': MutexLock})

        # scan videos
        scanned_videos = scan_videos(scan_path, age=age)

        for video in scanned_videos:
            video.subtitle_languages |= set(search_external_subtitles(video.name).values())
            if check_video(video, languages=languages, age=age, undefined=False):
                refine(video)
                if languages - video.subtitle_languages:
                    videos.append(video)
                else:
                    ignored_videos.append(video)
            else:
                ignored_videos.append(video)

        if videos:
            result['videos']['collected'] = [os.path.split(v.name)[1] for v in videos]
        if ignored_videos:
            result['videos']['ignored'] = [os.path.split(v.name)[1] for v in ignored_videos]

        scan_end = datetime.now()
        result['meta']['start'] = scan_start.isoformat()
        result['meta']['end'] = scan_end.isoformat()
        result['meta']['duration'] = str(scan_end - scan_start)
        return result
Ejemplo n.º 17
0
def _scan_wanted_item_for_video(wanted_item, is_manual=False):
    video_path = wanted_item.videopath
    log.info('Scanning video')

    try:
        # Scan the video
        video = subliminal.scan_video(video_path)

        # Use our manual refiner only for manual search if enabled
        # Always keep this first because you may completely override the video with this!
        if is_manual and autosubliminal.MANUALREFINEVIDEO:
            refiners = ('manual',)  # don't remove the , -> needs to be a tuple
            subliminal.refine(video, episode_refiners=refiners, movie_refiners=refiners, wanted_item=wanted_item)

        # Use build-in refiners
        if autosubliminal.REFINEVIDEO:
            subliminal.refine(video)

        # Use our namemapping refiner (always enabled to enable our name mappings)
        # This should always be at the end since we want to enrich the result after the build-in refiners
        refiners = ('namemapping',)  # don't remove the , -> needs to be a tuple
        subliminal.refine(video, episode_refiners=refiners, movie_refiners=refiners)
    except Exception:
        log.exception('Error while scanning video, skipping %s', video_path)
        return

    # Add video to wanted item
    wanted_item.video = video

    return video
Ejemplo n.º 18
0
def get_video(video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None, episode=None):
    if not subtitles_path:
        subtitles_path = get_subtitles_path(video_path)

    try:
        # Encode paths to UTF-8 to ensure subliminal support.
        video_path = video_path.encode('utf-8')
        subtitles_path = subtitles_path.encode('utf-8')
    except UnicodeEncodeError:
        # Fallback to system encoding. This should never happen.
        video_path = video_path.encode(sickrage.app.sys_encoding)
        subtitles_path = subtitles_path.encode(sickrage.app.sys_encoding)

    try:
        video = subliminal.scan_video(video_path)

        # external subtitles
        if subtitles:
            video.subtitle_languages |= \
                set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(
                not sickrage.app.config.embedded_subtitles_all and video_path.endswith('.mkv'))

        # Let sickrage add more information to video file, based on the metadata.
        if episode:
            refine_video(video, episode)

        subliminal.refine(video, embedded_subtitles=embedded_subtitles)
    except Exception as error:
        sickrage.app.log.debug('Exception: {}'.format(error))
        return None

    # remove format metadata
    video.format = ""

    return video
Ejemplo n.º 19
0
    def run(self,
            scan_path,
            scan_age,
            languages,
            encoding,
            min_score,
            providers,
            provider_configs,
            max_workers,
            plex_url=None,
            plex_token=None,
            *args,
            **kwargs):
        if not os.path.isdir(scan_path):
            raise IOError('Path \'%s\' doesn\'t exist!' % scan_path)
        if not scan_age >= 1:
            raise ValueError('\'scan_age\' must by at least 1!')
        if not len(languages) >= 1:
            raise ValueError('\'languages\' list can\'t be empty!')
        if not providers:
            raise ValueError('\'providers\' argument can\'t be empty!')
        if not max_workers >= 1:
            raise ValueError('\'max_workers\' must be at least 1!')

        if not provider_configs:
            provider_configs = {}

        __tree_dict = lambda: defaultdict(__tree_dict)
        result = __tree_dict()

        encoding = codecs.lookup(encoding).name
        age = timedelta(weeks=scan_age)
        languages = set([Language(l) for l in languages])

        plex = None
        if plex_url and plex_token:
            plex = PlexServer(plex_url, plex_token)

        scan_start = datetime.now()

        videos = []
        ignored_videos = []

        if not region.is_configured:
            region.configure('dogpile.cache.dbm',
                             expiration_time=timedelta(days=30),
                             arguments={
                                 'filename': 'subliminal.dbm',
                                 'lock_factory': MutexLock
                             })

        # scan videos
        scanned_videos = scan_videos(scan_path, age=age)

        for video in scanned_videos:
            video.subtitle_languages |= set(
                search_external_subtitles(video.name).values())
            if check_video(video,
                           languages=languages,
                           age=age,
                           undefined=False):
                refine(video)
                if languages - video.subtitle_languages:
                    videos.append(video)
                else:
                    ignored_videos.append(video)
            else:
                ignored_videos.append(video)

        if videos:
            result['videos']['collected'] = [
                os.path.split(v.name)[1] for v in videos
            ]
        if ignored_videos:
            result['videos']['ignored'] = len(ignored_videos)

        if videos:
            # download best subtitles
            downloaded_subtitles = defaultdict(list)
            with AsyncProviderPool(max_workers=max_workers,
                                   providers=providers,
                                   provider_configs=provider_configs) as p:
                for video in videos:
                    scores = get_scores(video)
                    subtitles_to_download = p.list_subtitles(
                        video, languages - video.subtitle_languages)
                    downloaded_subtitles[video] = p.download_best_subtitles(
                        subtitles_to_download,
                        video,
                        languages,
                        min_score=scores['hash'] * min_score / 100)

                if p.discarded_providers:
                    result['providers']['discarded'] = list(
                        p.discarded_providers)

            # filter subtitles
            with TinyDB('subtitle_db.json') as db:
                table = db.table('downloaded')
                query = Query()
                for video, subtitles in downloaded_subtitles.items():
                    discarded_subtitles = list()
                    discarded_subtitles_info = list()

                    for s in subtitles:
                        subtitle_hash = hashlib.sha256(s.content).hexdigest()
                        subtitle_file = get_subtitle_path(
                            os.path.split(video.name)[1], s.language)
                        dbo = {'hash': subtitle_hash, 'file': subtitle_file}
                        if table.search((query.hash == subtitle_hash)
                                        & (query.file == subtitle_file)):
                            discarded_subtitles.append(s)
                            discarded_subtitles_info.append(dbo)
                        else:
                            table.insert(dbo)

                    downloaded_subtitles[video] = [
                        x for x in subtitles if x not in discarded_subtitles
                    ]
                    if discarded_subtitles_info:
                        result['subtitles'][
                            'discarded'] = result['subtitles'].get(
                                'discarded', []) + discarded_subtitles_info

            downloaded_subtitles = {
                k: v
                for k, v in downloaded_subtitles.items() if v
            }

            # save subtitles
            saved_subtitles = {}
            for video, subtitles in downloaded_subtitles.items():
                saved_subtitles[video] = save_subtitles(video,
                                                        subtitles,
                                                        directory=None,
                                                        encoding=encoding)

                for key, group in groupby(saved_subtitles[video],
                                          lambda x: x.provider_name):
                    subtitle_filenames = [
                        get_subtitle_path(
                            os.path.split(video.name)[1], s.language)
                        for s in list(group)
                    ]
                    result['subtitles'][key] = result['subtitles'].get(
                        key, []) + subtitle_filenames
            result['subtitles']['total'] = sum(
                len(v) for v in saved_subtitles.values())

            # refresh plex
            for video, subtitles in saved_subtitles.items():
                if plex and subtitles:
                    item_found = False
                    for section in plex.library.sections():
                        try:
                            if isinstance(section,
                                          MovieSection) and isinstance(
                                              video, Movie):
                                results = section.search(title=video.title,
                                                         year=video.year,
                                                         libtype='movie',
                                                         sort='addedAt:desc',
                                                         maxresults=1)

                                if not results:
                                    raise NotFound

                                plex_item = results[0]
                            elif isinstance(section,
                                            ShowSection) and isinstance(
                                                video, Episode):
                                results = section.search(title=video.series,
                                                         year=video.year,
                                                         libtype='show',
                                                         sort='addedAt:desc',
                                                         maxresults=1)

                                if not results:
                                    raise NotFound

                                plex_item = results[0].episode(
                                    season=video.season, episode=video.episode)
                            else:
                                continue
                        except NotFound:
                            continue
                        except BadRequest:
                            continue

                        if plex_item:
                            plex_item.refresh()
                            result['plex']['refreshed'] = result['plex'].get(
                                'refreshed', []) + [
                                    '%s%s' %
                                    (repr(plex_item.section()), repr(video))
                                ]
                            item_found = True

                    if not item_found:
                        result['plex']['failed'] = result['plex'].get(
                            'failed', []) + [repr(video)]

            # convert subtitles
            for video, subtitles in saved_subtitles.items():
                target_format = aeidon.formats.SUBRIP
                for s in subtitles:
                    subtitle_path = get_subtitle_path(video.name, s.language)
                    source_format = aeidon.util.detect_format(
                        subtitle_path, encoding)
                    source_file = aeidon.files.new(
                        source_format, subtitle_path,
                        aeidon.encodings.detect_bom(subtitle_path) or encoding)

                    if source_format != target_format:
                        format_info = {
                            'file':
                            get_subtitle_path(
                                os.path.split(video.name)[1], s.language),
                            'from':
                            source_format.label,
                            'to':
                            target_format.label
                        }
                        result['subtitles'][
                            'converted'] = result['subtitles'].get(
                                'converted', []) + [format_info]

                    aeidon_subtitles = source_file.read()
                    for f in [
                            aeidon.formats.SUBRIP, aeidon.formats.MICRODVD,
                            aeidon.formats.MPL2
                    ]:
                        markup = aeidon.markups.new(f)
                        for s in aeidon_subtitles:
                            s.main_text = markup.decode(s.main_text)

                    markup = aeidon.markups.new(target_format)
                    for s in aeidon_subtitles:
                        s.main_text = markup.encode(s.main_text)

                    target_file = aeidon.files.new(target_format,
                                                   subtitle_path, encoding)
                    target_file.write(aeidon_subtitles, aeidon.documents.MAIN)

        scan_end = datetime.now()
        result['meta']['start'] = scan_start.isoformat()
        result['meta']['end'] = scan_end.isoformat()
        result['meta']['duration'] = str(scan_end - scan_start)
        return result
Ejemplo n.º 20
0
def get_video(tv_episode,
              video_path,
              subtitles_dir=None,
              subtitles=True,
              embedded_subtitles=None,
              release_name=None):
    """Return the subliminal video for the given path.

    The video_path is used as a key to cache the video to avoid
    scanning and parsing the video metadata all the time

    :param tv_episode:
    :type tv_episode: medusa.tv.TVEpisode
    :param video_path: the video path
    :type video_path: str
    :param subtitles_dir: the subtitles directory
    :type subtitles_dir: str or None
    :param subtitles: True if existing external subtitles should be taken into account
    :type subtitles: bool or None
    :param embedded_subtitles: True if embedded subtitles should be taken into account
    :type embedded_subtitles: bool or None
    :param release_name: the release name
    :type release_name: str or None
    :return: video
    :rtype: subliminal.video.Video
    """
    key = video_key.format(video_path=video_path)
    payload = {
        'subtitles_dir': subtitles_dir,
        'subtitles': subtitles,
        'embedded_subtitles': embedded_subtitles,
        'release_name': release_name
    }
    cached_payload = region.get(key, expiration_time=VIDEO_EXPIRATION_TIME)
    if cached_payload != NO_VALUE and {
            k: v
            for k, v in iteritems(cached_payload) if k != 'video'
    } == payload:
        logger.debug(u'Found cached video information under key %s', key)
        return cached_payload['video']

    try:
        video_path = _encode(video_path)
        subtitles_dir = _encode(subtitles_dir or get_subtitles_dir(video_path))

        logger.debug(u'Scanning video %s...', video_path)
        video = scan_video(video_path)

        # external subtitles
        if subtitles:
            video.subtitle_languages |= set(
                search_external_subtitles(video_path,
                                          directory=subtitles_dir).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(not app.EMBEDDED_SUBTITLES_ALL
                                      and video_path.endswith('.mkv'))

        refine(video,
               episode_refiners=episode_refiners,
               embedded_subtitles=embedded_subtitles,
               release_name=release_name,
               tv_episode=tv_episode)

        payload['video'] = video
        region.set(key, payload)
        logger.debug(u'Video information cached under key %s', key)

        return video
    except Exception as error:
        logger.info(u'Exception: %s', error)
Ejemplo n.º 21
0
 def get_video(self, path):
     video = scan_video(path)
     refine(video, episode_refiners=episode_refiners, movie_refiners=movie_refiners, embedded_subtitles=False)
     return video
Ejemplo n.º 22
0
def download(obj, provider, language, age, directory, encoding, single, force, hearing_impaired,
             min_score, max_workers, archives, verbose, path):
    """Download best subtitles.

    PATH can be an directory containing videos, a video file path or a video file name. It can be used multiple times.

    If an existing subtitle is detected (external or embedded) in the correct language, the download is skipped for
    the associated video.

    """
    # process parameters
    language = set(language)

    # scan videos
    videos = []
    ignored_videos = []
    errored_paths = []
    with click.progressbar(path, label='Collecting videos', item_show_func=lambda p: p or '') as bar:
        for p in bar:
            logger.debug('Collecting path %s', p)

            # non-existing
            if not os.path.exists(p):
                try:
                    video = Video.fromname(p)
                except:
                    logger.exception('Unexpected error while collecting non-existing path %s', p)
                    errored_paths.append(p)
                    continue
                if not force:
                    video.subtitle_languages |= set(search_external_subtitles(video.name, directory=directory).values())
                refine(video, embedded_subtitles=not force)
                videos.append(video)
                continue

            # directories
            if os.path.isdir(p):
                try:
                    scanned_videos = scan_videos(p, age=age, archives=archives, subtitles=not force,
                                                 subtitles_dir=directory)
                except:
                    logger.exception('Unexpected error while collecting directory path %s', p)
                    errored_paths.append(p)
                    continue
                for video in scanned_videos:
                    if check_video(video, languages=language, age=age, undefined=single):
                        if not force:
                            video.subtitle_languages |= set(search_external_subtitles(video.name,
                                                                                      directory=directory).values())
                        refine(video, embedded_subtitles=not force)
                        videos.append(video)
                    else:
                        ignored_videos.append(video)
                continue

            # other inputs
            try:
                video = scan_video(p)
            except:
                logger.exception('Unexpected error while collecting path %s', p)
                errored_paths.append(p)
                continue
            if check_video(video, languages=language, age=age, undefined=single):
                if not force:
                    video.subtitle_languages |= set(search_external_subtitles(video.name, directory=directory).values())
                refine(video, embedded_subtitles=not force)
                videos.append(video)
            else:
                ignored_videos.append(video)

    # output errored paths
    if verbose > 0:
        for p in errored_paths:
            click.secho('%s errored' % p, fg='red')

    # output ignored videos
    if verbose > 1:
        for video in ignored_videos:
            click.secho('%s ignored - subtitles: %s / age: %d day%s' % (
                os.path.split(video.name)[1],
                ', '.join(str(s) for s in video.subtitle_languages) or 'none',
                video.age.days,
                's' if video.age.days > 1 else ''
            ), fg='yellow')

    # report collected videos
    click.echo('%s video%s collected / %s video%s ignored / %s error%s' % (
        click.style(str(len(videos)), bold=True, fg='green' if videos else None),
        's' if len(videos) > 1 else '',
        click.style(str(len(ignored_videos)), bold=True, fg='yellow' if ignored_videos else None),
        's' if len(ignored_videos) > 1 else '',
        click.style(str(len(errored_paths)), bold=True, fg='red' if errored_paths else None),
        's' if len(errored_paths) > 1 else '',
    ))

    # exit if no video collected
    if not videos:
        return

    # download best subtitles
    downloaded_subtitles = defaultdict(list)
    with AsyncProviderPool(max_workers=max_workers, providers=provider, provider_configs=obj['provider_configs']) as p:
        with click.progressbar(videos, label='Downloading subtitles',
                               item_show_func=lambda v: os.path.split(v.name)[1] if v is not None else '') as bar:
            for v in bar:
                scores = get_scores(v)
                subtitles = p.download_best_subtitles(p.list_subtitles(v, language - v.subtitle_languages),
                                                      v, language, min_score=scores['hash'] * min_score / 100,
                                                      hearing_impaired=hearing_impaired, only_one=single)
                downloaded_subtitles[v] = subtitles

    # TODO: warn about discarded providers
    # save subtitles
    total_subtitles = 0
    for v, subtitles in downloaded_subtitles.items():
        saved_subtitles = save_subtitles(v, subtitles, single=single, directory=directory, encoding=encoding)
        total_subtitles += len(saved_subtitles)

        if verbose > 0:
            click.echo('%s subtitle%s downloaded for %s' % (click.style(str(len(saved_subtitles)), bold=True),
                                                            's' if len(saved_subtitles) > 1 else '',
                                                            os.path.split(v.name)[1]))

        if verbose > 1:
            for s in saved_subtitles:
                matches = s.get_matches(v)
                score = compute_score(s, v)

                # score color
                score_color = None
                scores = get_scores(v)
                if isinstance(v, Movie):
                    if score < scores['title']:
                        score_color = 'red'
                    elif score < scores['title'] + scores['year'] + scores['release_group']:
                        score_color = 'yellow'
                    else:
                        score_color = 'green'
                elif isinstance(v, Episode):
                    if score < scores['series'] + scores['season'] + scores['episode']:
                        score_color = 'red'
                    elif score < scores['series'] + scores['season'] + scores['episode'] + scores['release_group']:
                        score_color = 'yellow'
                    else:
                        score_color = 'green'

                # scale score from 0 to 100 taking out preferences
                scaled_score = score
                if s.hearing_impaired == hearing_impaired:
                    scaled_score -= scores['hearing_impaired']
                scaled_score *= 100 / scores['hash']

                # echo some nice colored output
                click.echo('  - [{score}] {language} subtitle from {provider_name} (match on {matches})'.format(
                    score=click.style('{:5.1f}'.format(scaled_score), fg=score_color, bold=score >= scores['hash']),
                    language=s.language.name if s.language.country is None else '%s (%s)' % (s.language.name,
                                                                                             s.language.country.name),
                    provider_name=s.provider_name,
                    matches=', '.join(sorted(matches, key=scores.get, reverse=True))
                ))

    if verbose == 0:
        click.echo('Downloaded %s subtitle%s' % (click.style(str(total_subtitles), bold=True),
                                                 's' if total_subtitles > 1 else ''))
Ejemplo n.º 23
0
def download(obj, provider, refiner, language, age, directory, encoding,
             single, force, hearing_impaired, min_score, max_workers, archives,
             verbose, path):
    """Download best subtitles.

    PATH can be an directory containing videos, a video file path or a video file name. It can be used multiple times.

    If an existing subtitle is detected (external or embedded) in the correct language, the download is skipped for
    the associated video.

    """
    # process parameters
    language = set(language)

    # scan videos
    videos = []
    ignored_videos = []
    errored_paths = []
    with click.progressbar(path,
                           label='Collecting videos',
                           item_show_func=lambda p: p or '') as bar:
        for p in bar:
            logger.debug('Collecting path %s', p)

            # non-existing
            if not os.path.exists(p):
                try:
                    video = Video.fromname(p)
                except:
                    logger.exception(
                        'Unexpected error while collecting non-existing path %s',
                        p)
                    errored_paths.append(p)
                    continue
                if not force:
                    video.subtitle_languages |= set(
                        search_external_subtitles(
                            video.name, directory=directory).values())
                refine(video,
                       episode_refiners=refiner,
                       movie_refiners=refiner,
                       embedded_subtitles=not force)
                videos.append(video)
                continue

            # directories
            if os.path.isdir(p):
                try:
                    scanned_videos = scan_videos(p, age=age, archives=archives)
                except:
                    logger.exception(
                        'Unexpected error while collecting directory path %s',
                        p)
                    errored_paths.append(p)
                    continue
                for video in scanned_videos:
                    if check_video(video,
                                   languages=language,
                                   age=age,
                                   undefined=single):
                        if not force:
                            video.subtitle_languages |= set(
                                search_external_subtitles(
                                    video.name, directory=directory).values())
                        refine(video,
                               episode_refiners=refiner,
                               movie_refiners=refiner,
                               embedded_subtitles=not force)
                        videos.append(video)
                    else:
                        ignored_videos.append(video)
                continue

            # other inputs
            try:
                video = scan_video(p)
            except:
                logger.exception('Unexpected error while collecting path %s',
                                 p)
                errored_paths.append(p)
                continue
            if check_video(video,
                           languages=language,
                           age=age,
                           undefined=single):
                if not force:
                    video.subtitle_languages |= set(
                        search_external_subtitles(
                            video.name, directory=directory).values())
                refine(video,
                       episode_refiners=refiner,
                       movie_refiners=refiner,
                       embedded_subtitles=not force)
                videos.append(video)
            else:
                ignored_videos.append(video)

    # output errored paths
    if verbose > 0:
        for p in errored_paths:
            click.secho('%s errored' % p, fg='red')

    # output ignored videos
    if verbose > 1:
        for video in ignored_videos:
            click.secho(
                '%s ignored - subtitles: %s / age: %d day%s' %
                (os.path.split(video.name)[1],
                 ', '.join(str(s) for s in video.subtitle_languages)
                 or 'none', video.age.days, 's' if video.age.days > 1 else ''),
                fg='yellow')

    # report collected videos
    click.echo('%s video%s collected / %s video%s ignored / %s error%s' % (
        click.style(
            str(len(videos)), bold=True, fg='green' if videos else None),
        's' if len(videos) > 1 else '',
        click.style(str(len(ignored_videos)),
                    bold=True,
                    fg='yellow' if ignored_videos else None),
        's' if len(ignored_videos) > 1 else '',
        click.style(str(len(errored_paths)),
                    bold=True,
                    fg='red' if errored_paths else None),
        's' if len(errored_paths) > 1 else '',
    ))

    # exit if no video collected
    if not videos:
        return

    # download best subtitles
    downloaded_subtitles = defaultdict(list)
    with AsyncProviderPool(max_workers=max_workers,
                           providers=provider,
                           provider_configs=obj['provider_configs']) as p:
        with click.progressbar(
                videos,
                label='Downloading subtitles',
                item_show_func=lambda v: os.path.split(v.name)[1]
                if v is not None else '') as bar:
            for v in bar:
                scores = get_scores(v)
                subtitles = p.download_best_subtitles(
                    p.list_subtitles(v, language - v.subtitle_languages),
                    v,
                    language,
                    min_score=scores['hash'] * min_score / 100,
                    hearing_impaired=hearing_impaired,
                    only_one=single)
                downloaded_subtitles[v] = subtitles

        if p.discarded_providers:
            click.secho(
                'Some providers have been discarded due to unexpected errors: %s'
                % ', '.join(p.discarded_providers),
                fg='yellow')

    # save subtitles
    total_subtitles = 0
    for v, subtitles in downloaded_subtitles.items():
        saved_subtitles = save_subtitles(v,
                                         subtitles,
                                         single=single,
                                         directory=directory,
                                         encoding=encoding)
        total_subtitles += len(saved_subtitles)

        if verbose > 0:
            click.echo(
                '%s subtitle%s downloaded for %s' %
                (click.style(str(len(saved_subtitles)), bold=True), 's' if
                 len(saved_subtitles) > 1 else '', os.path.split(v.name)[1]))

        if verbose > 1:
            for s in saved_subtitles:
                matches = s.get_matches(v)
                score = compute_score(s, v)

                # score color
                score_color = None
                scores = get_scores(v)
                if isinstance(v, Movie):
                    if score < scores['title']:
                        score_color = 'red'
                    elif score < scores['title'] + scores['year'] + scores[
                            'release_group']:
                        score_color = 'yellow'
                    else:
                        score_color = 'green'
                elif isinstance(v, Episode):
                    if score < scores['series'] + scores['season'] + scores[
                            'episode']:
                        score_color = 'red'
                    elif score < scores['series'] + scores['season'] + scores[
                            'episode'] + scores['release_group']:
                        score_color = 'yellow'
                    else:
                        score_color = 'green'

                # scale score from 0 to 100 taking out preferences
                scaled_score = score
                if s.hearing_impaired == hearing_impaired:
                    scaled_score -= scores['hearing_impaired']
                scaled_score *= 100 / scores['hash']

                # echo some nice colored output
                click.echo(
                    '  - [{score}] {language} subtitle from {provider_name} (match on {matches})'
                    .format(score=click.style('{:5.1f}'.format(scaled_score),
                                              fg=score_color,
                                              bold=score >= scores['hash']),
                            language=s.language.name
                            if s.language.country is None else '%s (%s)' %
                            (s.language.name, s.language.country.name),
                            provider_name=s.provider_name,
                            matches=', '.join(
                                sorted(matches, key=scores.get,
                                       reverse=True))))

    if verbose == 0:
        click.echo('Downloaded %s subtitle%s' %
                   (click.style(str(total_subtitles), bold=True),
                    's' if total_subtitles > 1 else ''))
Ejemplo n.º 24
0
def get_video(tv_episode, video_path, subtitles_dir=None, subtitles=True, embedded_subtitles=None, release_name=None):
    """Return the subliminal video for the given path.

    The video_path is used as a key to cache the video to avoid
    scanning and parsing the video metadata all the time

    :param tv_episode:
    :type tv_episode: medusa.tv.Episode
    :param video_path: the video path
    :type video_path: str
    :param subtitles_dir: the subtitles directory
    :type subtitles_dir: str or None
    :param subtitles: True if existing external subtitles should be taken into account
    :type subtitles: bool or None
    :param embedded_subtitles: True if embedded subtitles should be taken into account
    :type embedded_subtitles: bool or None
    :param release_name: the release name
    :type release_name: str or None
    :return: video
    :rtype: subliminal.video.Video
    """
    key = video_key.format(video_path=video_path)
    payload = {'subtitles_dir': subtitles_dir, 'subtitles': subtitles, 'embedded_subtitles': embedded_subtitles,
               'release_name': release_name}
    cached_payload = memory_cache.get(key, expiration_time=VIDEO_EXPIRATION_TIME)
    if cached_payload != NO_VALUE and {k: v for k, v in iteritems(cached_payload) if k != 'video'} == payload:
        logger.debug(u'Found cached video information under key %s', key)
        return cached_payload['video']

    video_is_mkv = video_path.endswith('.mkv')
    subtitles_dir = subtitles_dir or get_subtitles_dir(video_path)

    logger.debug(u'Scanning video %s...', video_path)

    try:
        video = scan_video(video_path)
    except ValueError as error:
        logger.warning(u'Unable to scan video: %s. Error: %r', video_path, error)
    else:

        # Add hash of our custom provider Itasa
        video.size = os.path.getsize(video_path)
        if video.size > 10485760:
            video.hashes['itasa'] = hash_itasa(video_path)

        # external subtitles
        if subtitles:
            video.subtitle_languages |= set(search_external_subtitles(video_path, directory=subtitles_dir).values())

        if embedded_subtitles is None:
            embedded_subtitles = bool(not app.IGNORE_EMBEDDED_SUBS and video_is_mkv)

        refine(video, episode_refiners=episode_refiners, embedded_subtitles=embedded_subtitles,
               release_name=release_name, tv_episode=tv_episode)

        video.alternative_series = list(tv_episode.series.aliases)

        payload['video'] = video
        memory_cache.set(key, payload)
        logger.debug(u'Video information cached under key %s', key)

        return video
    def download_callback(self, menuitem, files):
        # scan videos
        videos = []
        for f in files:
            # ignore non-writable locations
            if not f.can_write():
                continue

            # directories
            if f.is_directory():
                try:
                    scanned_videos = scan_videos(f.get_location().get_path())
                except:
                    continue
                for video in scanned_videos:
                    if check_video(video,
                                   languages=self.config.languages,
                                   age=self.config.age,
                                   undefined=self.config.single):
                        video.subtitle_languages |= set(
                            search_external_subtitles(video.name).values())
                        refine(
                            video,
                            episode_refiners=self.config.refiners,
                            movie_refiners=self.config.refiners,
                            embedded_subtitles=self.config.embedded_subtitles)
                        videos.append(video)
                continue

            # other inputs
            try:
                video = scan_video(f.get_location().get_path())
            except:
                continue
            if check_video(video,
                           languages=self.config.languages,
                           undefined=self.config.single):
                video.subtitle_languages |= set(
                    search_external_subtitles(video.name).values())
                refine(video,
                       episode_refiners=self.config.refiners,
                       movie_refiners=self.config.refiners,
                       embedded_subtitles=self.config.embedded_subtitles)
                videos.append(video)

        # download best subtitles
        downloaded_subtitles = defaultdict(list)
        with AsyncProviderPool(
                providers=self.config.providers,
                provider_configs=self.config.provider_configs) as pool:
            for v in videos:
                scores = get_scores(v)
                subtitles = pool.download_best_subtitles(
                    pool.list_subtitles(
                        v, self.config.languages - v.subtitle_languages),
                    v,
                    self.config.languages,
                    min_score=scores['hash'] * self.config.min_score / 100,
                    hearing_impaired=self.config.hearing_impaired,
                    only_one=self.config.single)
                downloaded_subtitles[v] = subtitles

        # save subtitles
        for v, subtitles in downloaded_subtitles.items():
            save_subtitles(v, subtitles, single=self.config.single)