class ProgressBarCallback(ClientCallback):
    """
    This callback will use the progressbar library to show the progress of some action.
    """
    def __init__(self, minimum=None, maximum=None, widgets=DEFAULT_WIDGETS, fd=None):
        """
        Initialize a new ProgressBarCallback (and thus show a progressbar).
        :param minimum: minimum value as integer
        :param maximum: maximum value as integer
        :param widgets: widgets to show as list of progressbar.Widget
        """
        ClientCallback.__init__(self, minimum=minimum, maximum=maximum)
        if fd is  None:
            fd = sys.stderr
        self._bar = progressbar.ProgressBar(widgets=widgets, fd=fd)

    def on_show(self):
        self._bar.start()

    def on_update(self, value, *args, **kwargs):
        """
        Update the progressbar with the new value and percentage
        :param value: value as integer
        :param args: extra positional arguments to pass on
        :param kwargs: extra keyword arguments to pass on
        """
        self._bar.update(value)

    def on_finish(self, *args, **kwargs):
        """
        Inform this ProgressBarCallback that the action is finished
        """
        self._bar.finish()

    def on_rangeChange(self, minimum, maximum):
        self._bar.maxval = maximum class CliState(BaseState):
    def __init__(self):
        BaseState.__init__(self)

        self._interactive = False
        self._console = False
        self._recursive = False

        self.set_subtitle_download_path_strategy(SubtitlePathStrategy.SAME)

        # FIXME: log state

    def load_settings(self, settings):
        # BaseState.load_settings(settings)
        # Do not load settings from file in cli
        pass

    def load_options(self, options):
        BaseState.load_options(self, options)

        self._console = options.program.client.cli.console
        self._interactive = options.program.client.cli.interactive
        self._list_languages = options.program.client.cli.list_languages

        self._recursive = options.search.recursive

        self.set_subtitle_naming_strategy(options.download.naming_strategy)

        self.set_videoplayer(VideoPlayer.find())

    def get_console(self):
        return self._console

    def get_interactive(self):
        return self._interactive

    def get_list_languages(self):
        return self._list_languages

    def get_recursive(self):
        return self._recursive

    def set_recursive(self, recursive):
        self._recursive = recursive class SubtitleNamingStrategy(Enum):
    VIDEO = 'SAME_VIDEO'
    VIDEO_LANG = 'SAME_VIDEOPLUSLANG'
    VIDEO_LANG_UPLOADER = 'SAME_VIDEOPLUSLANGANDUPLOADER'
    ONLINE = 'SAME_ONLINE'

    @classmethod
    def from_str(cls, s):
        try:
            return next(c for c in cls if s == c.value)
        except StopIteration:
            raise ValueError(s) class IllegalPathException(Exception):
    def __init__(self, path):
        self._path = path

    def path(self):
        return self._path def scan_videopaths(videopaths, callback, recursive=False):
    callback.set_range(0, len(videopaths))
    all_videos = []
    all_subtitles = []
    for videopath_i, videopath in enumerate(videopaths):
        callback.update(videopath_i)
        subcallback = callback.get_child_progress(videopath_i, videopath_i + 1)
        videos, subtitles = scan_videopath(videopath, subcallback, recursive)
        all_videos.extend(videos)
        all_subtitles.extend(subtitles)
    return all_videos, all_subtitles class ProviderConnectionError(Exception):
    def __init__(self, msg, extra_data=None):
        Exception.__init__(self)
        self._msg = msg
        self._extra_data = extra_data

    def get_msg(self):
        return self._msg

    def get_extra_data(self):
        return self._extra_data

    def __repr__(self):
        return '<ProviderConnectionError:msg={m}{e}>'.format(
            m=self._msg,
            e=(';extra={!r}'.format(self._extra_data) if self._extra_data else '')) class Language(object):
    """
    Instances of the class represent a language.
    """

    def __init__(self, lang_id):
        """
        Create a new Language object. Values should be strings.
        :param lang_id: index in LANGUAGES list of dicts having keys: ('locale', 'ISO639', 'LanguageID', 'LanguageName')
        """
        self._id = lang_id
        str.__init__(self)

    def locale(self):
        """
        Get locale of Language
        :return: locale as string
        """
        return LANGUAGES[self._id]['locale'][0]

    def xx(self):
        """
        Get ISO639 of Language
        :return: ISO639 as string
        """
        return LANGUAGES[self._id]['ISO639'][0]

    def xxx(self):
        """
        Get LanguageID of Language
        :return: LanguageID as string
        """
        return LANGUAGES[self._id]['LanguageID'][0]

    def name(self):
        """
        Return readable name of Language
        :return: Readable name as string
        """
        return _(LANGUAGES[self._id]['LanguageName'][0])

    def generic_name(self):
        """
        Return readable name of Language
        :return: Readable name as string
        """
        return self.name()

    def is_generic(self):
        return False

    def __eq__(self, other):
        """
        Check if other is the same as self.
        :param other: other object
        :return: True if other is the same as self
        """
        if other is None:
            return False
        if type(self) != type(other):
            return False
        return self.raw_data() == other.raw_data()

    def __hash__(self):
        """
        Return hash of this object.
        :return: hash integer
        """
        return hash(self._id)

    def __repr__(self):
        """
        Return string representation of this instance.
        :return: string representation of self
        """
        return '<Language:xx={}>'.format(LANGUAGES[self._id]['ISO639'][0])

    def __str__(self):
        """
        Return short string representation of this instance.
        :return: short string representation of self
        """
        return self.xx()

    def raw_data(self):
        """
        Return raw internal represenation of this instance.
        :return: raw internal representation
        """
        return self._id

    @classmethod
    def from_raw_data(cls, raw_data):
        """
        Return Language Instance from raw_data.
        :return: Language instance
        """
        if raw_data == 0:
            return UnknownLanguage.create_generic()
        return cls(raw_data)

    @classmethod
    def from_locale(cls, locale):
        """
        Create a new Language instance from a locale string
        :param locale: locale as string
        :return: Language instance with instance.locale() == locale if locale is valid else instance of Unknown Language
        """
        locale = str(locale)
        if locale == 'unknown':
            return UnknownLanguage(locale)
        try:
            return cls._from_xyz('locale', locale)
        except NotALanguageException:
            log.warning('Unknown locale: {}'.format(locale))
            return UnknownLanguage(locale)

    @classmethod
    def from_xx(cls, xx):
        """
        Create a new Language instance from a ISO639 string
        :param xx: ISO639 as string
        :return: Language instance with instance.xx() == xx if xx is valid else instance of UnknownLanguage
        """
        xx = str(xx).lower()
        if xx == 'unknown':
            return UnknownLanguage(xx)
        try:
            return cls._from_xyz('ISO639', xx)
        except NotALanguageException:
            log.warning('Unknown ISO639: {}'.format(xx))
            return UnknownLanguage(xx)

    @classmethod
    def from_xxx(cls, xxx):
        """
        Create a new Language instance from a LanguageID string
        :param xxx: LanguageID as string
        :return: Language instance with instance.xxx() == xxx if xxx is valid else instance of UnknownLanguage
        """
        xxx = str(xxx).lower()
        if xxx == 'unknown':
            return UnknownLanguage(xxx)
        try:
            return cls._from_xyz('LanguageID', xxx)
        except NotALanguageException:
            log.warning('Unknown LanguageId: {}'.format(xxx))
            return UnknownLanguage(xxx)

    @classmethod
    def from_name(cls, name):
        """
        Create a new Language instance from a name as string
        :param name: name as string
        :return: Language instance with instance.name() == name if name is valid else instance of UnknownLanguage
        """
        name = str(name).lower()
        if name == 'unknown' or name == _('unknown'):
            return UnknownLanguage(name)
        try:
            return cls._from_xyz('LanguageName', name)
        except NotALanguageException:
            log.warning('Unknown LanguageName: {}'.format(name))
            return UnknownLanguage(name)

    @classmethod
    def _from_xyz(cls, xyzkey, xyzvalue):
        """
        Private helper function to create new Language instance.
        :param xyzkey: one of ('locale', 'ISO639', 'LanguageID', 'LanguageName')
        :param xyzvalue: corresponding value of xyzkey
        :return: Language instance
        """
        xyzvalue = xyzvalue.lower().replace('-', '_')
        if xyzvalue == 'unknown' or xyzvalue == _('unknown'):
            return UnknownLanguage(xyzvalue)
        for lang_id, lang_data in enumerate(LANGUAGES):
            for data_value in lang_data[xyzkey]:
                if xyzvalue == data_value.lower():
                    return cls(lang_id)
        raise NotALanguageException(xyzvalue, 'Illegal language {}: {}'.format(xyzkey, xyzvalue))

    @classmethod
    def from_unknown(cls, value, xx=False, xxx=False, locale=False, name=False):
        """
        Try to create a Language instance having only some limited data about the Language.
        If no corresponding Language is found, a NotALanguageException is thrown.
        :param value: data known about the language as string
        :param xx: True if the value may be a locale
        :param xxx: True if the value may be a LanguageID
        :param locale: True if the value may be a locale
        :param name: True if the value may be a LanguageName
        :return: Language Instance if a matching Language was found
        """
        # Use 2 lists instead of dict ==> order known
        keys = ['ISO639', 'LanguageID', 'locale', 'LanguageName']
        truefalses = [xx, xxx, locale, name]
        value = value.lower().replace('-', '_')
        for key, doKey in zip(keys, truefalses):
            if doKey:
                try:
                    return cls._from_xyz(key, value)
                except NotALanguageException:
                    pass
        raise NotALanguageException(value, 'Illegal language "{}"'.format(value))

    @classmethod
    def from_file(cls, filepath, chunk_size=None):
        """
        Try do determine the language of a text file.
        :param filepath: string file path
        :param chunk_size: amount of bytes of file to read to determine language
        :return: Language instance if detection succeeded, otherwise return UnknownLanguage
        """
        log.debug('Language.from_file: "{}", chunk={} ...'.format(filepath, chunk_size))
        with filepath.open('rb') as f:
            data = f.read(-1 if chunk_size is None else chunk_size)
        data_ascii = asciify(data)
        lang_xx = langdetect_detect(data_ascii)
        lang = cls.from_xx(lang_xx)
        log.debug('... result language={}'.format(lang))
        return lang class NotALanguageException(ValueError):
    """
    Exception to inform that some value is not a valid Language.
    """
    def __init__(self, value, message):
        ValueError.__init__(self, message)
        self._value = value

    @property
    def value(self):
        return self._value class UnknownLanguage(Language):
    def __init__(self, code):
        Language.__init__(self, 0)
        self._code = code

    # FIXME: better name for & check generic logic

    @classmethod
    def create_generic(cls):
        """
        Return a UnknownLanguage instance..
        :return: UnknownLanguage instance.
        """
        return cls('unknown')  # NO internationalization!

    def name(self):
        """
        Return readable name of Language. Contains info about the unknown Language.
        :return: Readable name as string
        """
        return _(self._code)

    def generic_name(self):
        """
        Return readable name of Language. Contains no info about the unknown Language.
        :return: Readable name as string
        """
        return Language.name(self)

    def is_generic(self):
        return self._code == 'unknown'  # NO internationalization!

    def __eq__(self, other):
        """
        Tests if other has the same type of this and the same unknown language code.
        :return: True if other is of the same type and has the same unknown language code
        """
        if not Language.__eq__(self, other):
            return False
        return self._code == other._code

    def __hash__(self):
        """
        Return hash of this object.
        :return: hash integer
        """
        return hash((self._id, self._code))

    def __repr__(self):
        """
        Return representation of this instance
        :return: string representation of self
        """
        return '<UnknownLanguage:code={code}>'.format(code=self._code) LANGUAGES = [
    {
        'locale': ['und', 'unknown'],
        'ISO639': ['und', 'unknown'],
        'LanguageID': ['unknown'],
        'LanguageName': [_('Unknown')],
    }, {
        'locale': ['af'],
        'ISO639': ['af'],
        'LanguageID': ['afr'],
        'LanguageName': [_('Afrikaans')],
    }, {
        'locale': ['sq'],
        'ISO639': ['sq'],
        'LanguageID': ['alb'],
        'LanguageName': [_('Albanian')],
    }, {
        'locale': ['ar'],
        'ISO639': ['ar'],
        'LanguageID': ['ara'],
        'LanguageName': [_('Arabic')],
    }, {
        'locale': ['hy'],
        'ISO639': ['hy'],
        'LanguageID': ['arm'],
        'LanguageName': [_('Armenian')],
    }, {
        'locale': ['bn'],
        'ISO639': ['bn'],
        'LanguageID': ['ben'],
        'LanguageName': [_('Bengali')],
    }, {
        'locale': ['bs'],
        'ISO639': ['bs'],
        'LanguageID': ['bos'],
        'LanguageName': [_('Bosnian')],
    }, {
        'locale': ['bg'],
        'ISO639': ['bg'],
        'LanguageID': ['bul'],
        'LanguageName': [_('Bulgarian')],
    }, {
        'locale': ['ca'],
        'ISO639': ['ca'],
        'LanguageID': ['cat'],
        'LanguageName': [_('Catalan')],
    }, {
        'locale': ['eu'],
        'ISO639': ['eu'],
        'LanguageID': ['eus'],
        'LanguageName': [_('Basque')],
    }, {
        'locale': ['zh_CN'],
        'ISO639': ['zh'],
        'LanguageID': ['chi'],
        'LanguageName': [_('Chinese (China)')],
    }, {
        'locale': ['zh', 'zt'],
        'ISO639': ['zh', 'zt'],
        'LanguageID': ['zht'],
        'LanguageName': [_('Chinese (traditional)')],
    }, {
        'locale': ['hr'],
        'ISO639': ['hr'],
        'LanguageID': ['hrv'],
        'LanguageName': [_('Croatian')],
    }, {
        'locale': ['cs'],
        'ISO639': ['cs'],
        'LanguageID': ['cze'],
        'LanguageName': [_('Czech')],
    }, {
        'locale': ['da'],
        'ISO639': ['da'],
        'LanguageID': ['dan'],
        'LanguageName': [_('Danish')],
    }, {
        'locale': ['nl'],
        'ISO639': ['nl'],
        'LanguageID': ['dut'],
        'LanguageName': [_('Dutch')],
    }, {
        'locale': ['en'],
        'ISO639': ['en'],
        'LanguageID': ['eng'],
        'LanguageName': [_('English (US)')],
    }, {
        'locale': ['en_GB'],
        'ISO639': ['en'],
        'LanguageID': ['bre'],
        'LanguageName': [_('English (UK)')],
    }, {
        'locale': ['eo'],
        'ISO639': ['eo'],
        'LanguageID': ['epo'],
        'LanguageName': [_('Esperanto')],
    }, {
        'locale': ['et'],
        'ISO639': ['et'],
        'LanguageID': ['est'],
        'LanguageName': [_('Estonian')],
    }, {
        'locale': ['fi'],
        'ISO639': ['fi'],
        'LanguageID': ['fin'],
        'LanguageName': [_('Finnish')],
    }, {
        'locale': ['fr'],
        'ISO639': ['fr'],
        'LanguageID': ['fre'],
        'LanguageName': [_('French')],
    }, {
        'locale': ['gl'],
        'ISO639': ['gl'],
        'LanguageID': ['glg'],
        'LanguageName': [_('Galician')],
    }, {
        'locale': ['hi'],
        'ISO639': ['hi'],
        'LanguageID': ['hin'],
        'LanguageName': [_('Hindi')],
    }, {
        'locale': ['ka'],
        'ISO639': ['ka'],
        'LanguageID': ['geo'],
        'LanguageName': [_('Georgian')],
    }, {
        'locale': ['de'],
        'ISO639': ['de'],
        'LanguageID': ['ger'],
        'LanguageName': [_('German')],
    }, {
        'locale': ['el', 'gr'],
        'ISO639': ['el', 'gr'],
        'LanguageID': ['ell'],
        'LanguageName': [_('Greek')],
    }, {
        'locale': ['he'],
        'ISO639': ['he'],
        'LanguageID': ['heb'],
        'LanguageName': [_('Hebrew')],
    }, {
        'locale': ['hu'],
        'ISO639': ['hu'],
        'LanguageID': ['hun'],
        'LanguageName': [_('Hungarian')],
    }, {
        'locale': ['id'],
        'ISO639': ['id'],
        'LanguageID': ['ind'],
        'LanguageName': [_('Indonesian')],
    }, {
        'locale': ['it'],
        'ISO639': ['it'],
        'LanguageID': ['ita'],
        'LanguageName': [_('Italian')],
    }, {
        'locale': ['ja'],
        'ISO639': ['ja'],
        'LanguageID': ['jpn'],
        'LanguageName': [_('Japanese')],
    }, {
        'locale': ['kk'],
        'ISO639': ['kk'],
        'LanguageID': ['kaz'],
        'LanguageName': [_('Kazakh')],
    }, {
        'locale': ['ko'],
        'ISO639': ['ko'],
        'LanguageID': ['kor'],
        'LanguageName': [_('Korean')],
    }, {
        'locale': ['lv'],
        'ISO639': ['lv'],
        'LanguageID': ['lav'],
        'LanguageName': [_('Latvian')],
    }, {
        'locale': ['lt'],
        'ISO639': ['lt'],
        'LanguageID': ['lit'],
        'LanguageName': [_('Lithuanian')],
    }, {
        'locale': ['lb'],
        'ISO639': ['lb'],
        'LanguageID': ['ltz'],
        'LanguageName': [_('Luxembourgish')],
    }, {
        'locale': ['mk'],
        'ISO639': ['mk'],
        'LanguageID': ['mac'],
        'LanguageName': [_('Macedonian')],
    }, {
        'locale': ['ml'],
        'ISO639': ['ml'],
        'LanguageID': ['mal'],
        'LanguageName': [_('Malayalam')],
    }, {
        'locale': ['ms'],
        'ISO639': ['ms'],
        'LanguageID': ['may'],
        'LanguageName': [_('Malay')],
    }, {
        'locale': ['ne', 'ne_NP', ],
        'ISO639': ['ne'],
        'LanguageID': ['nep'],
        'LanguageName': [_('Nepali')],
    }, {
        'locale': ['no'],
        'ISO639': ['no'],
        'LanguageID': ['nor'],
        'LanguageName': [_('Norwegian')],
    }, {
        'locale': ['oc'],
        'ISO639': ['oc'],
        'LanguageID': ['oci'],
        'LanguageName': [_('Occitan')],
    }, {
        'locale': ['fa'],
        'ISO639': ['fa'],
        'LanguageID': ['per'],
        'LanguageName': [_('Persian')],
    }, {
        'locale': ['pl'],
        'ISO639': ['pl'],
        'LanguageID': ['pol'],
        'LanguageName': [_('Polish')],
    }, {
        'locale': ['pt_PT', 'pt'],
        'ISO639': ['pt'],
        'LanguageID': ['por'],
        'LanguageName': [_('Portuguese (Portugal)')],
    }, {
        'locale': ['pt_BR'],
        'ISO639': ['pb'],
        'LanguageID': ['pob'],
        'LanguageName': [_('Portuguese (Brazil)')],
    }, {
        'locale': ['ro'],
        'ISO639': ['ro'],
        'LanguageID': ['rum'],
        'LanguageName': [_('Romanian')],
    }, {
        'locale': ['ru'],
        'ISO639': ['ru'],
        'LanguageID': ['rus'],
        'LanguageName': [_('Russian')],
    }, {
        'locale': ['si'],
        'ISO639': ['si'],
        'LanguageID': ['sin'],
        'LanguageName': [_('Sinhalese')],
    }, {
        'locale': ['sk'],
        'ISO639': ['sk'],
        'LanguageID': ['slo'],
        'LanguageName': [_('Slovak')],
    }, {
        'locale': ['sl'],
        'ISO639': ['sl'],
        'LanguageID': ['slv'],
        'LanguageName': [_('Slovenian')],
    }, {
        'locale': ['sr'],
        'ISO639': ['sr'],
        'LanguageID': ['scc'],
        'LanguageName': [_('Serbian')],
    }, {
        'locale': ['es', 'es_ES'],
        'ISO639': ['es'],
        'LanguageID': ['spa'],
        'LanguageName': [_('Spanish (Spain)')],
    }, {
        'locale': ['sv'],
        'ISO639': ['sv'],
        'LanguageID': ['swe'],
        'LanguageName': [_('Swedish')],
    }, {
        'locale': ['th'],
        'ISO639': ['th'],
        'LanguageID': ['tha'],
        'LanguageName': [_('Thai')],
    }, {
        'locale': ['tr'],
        'ISO639': ['tr'],
        'LanguageID': ['tur'],
        'LanguageName': [_('Turkish')],
    }, {
        'locale': ['uk'],
        'ISO639': ['uk'],
        'LanguageID': ['ukr'],
        'LanguageName': [_('Ukrainian')],
    }, {
        'locale': ['vi'],
        'ISO639': ['vi'],
        'LanguageID': ['vie'],
        'LanguageName': [_('Vietnamese')],
    }
] class RemoteSubtitleFile(SubtitleFileStorage):
    def __init__(self, filename, language, file_size, md5_hash):
        SubtitleFileStorage.__init__(self, parent=None, language=language, file_size=file_size, md5_hash=md5_hash)
        self._filename = filename

    def get_filename(self):
        return self._filename

    def get_uploader(self):  # FIXME: abstractmethod
        raise NotImplementedError

    def get_rating(self):  # FIXME: abstractmethod
        raise NotImplementedError()

    def get_link(self):  # FIXME: abstractmethod
        raise NotImplementedError()

    def get_provider(self):  # FIXME: abstractmethod
        raise NotImplementedError

    def download(self, target_path, provider_instance, callback):  # FIXME: abstractmethod
        raise NotImplementedError()

    def is_remote(self):
        return True

    def is_local(self):
        return False class LocalMovie(object):
    def __init__(self):
        self._movie_name = None
        self._imdb_id = None
        self._language = UnknownLanguage.create_generic()
        self._release_name = None
        self._comments = None
        self._author = None

        self._hearing_impaired = None
        self._high_definition = None
        self._foreign_only = None
        self._automatic_translation = None

        self._data = []

    def add_video_subtitle(self, video, subtitle):
        self._data.append(VideoSubtitle(video, subtitle))

    def check(self):
        video_paths = set()
        subtitle_paths = set()
        for data in self._data:
            if not data.check():
                return False
            vfp = str(data.video.get_filepath().resolve())
            if vfp in video_paths:
                return False
            video_paths.add(vfp)

            sfp = str(data.subtitle.get_filepath().resolve())
            if sfp in subtitle_paths:
                return False
        if self._language.is_generic():
            return False
        return True

    def get_data(self):
        return self._data

    def set_data(self, data):
        self._data = data

    def iter_video_subtitles(self):
        for d in self._data:
            yield d.video, d.subtitle

    def set_movie_name(self, movie_name):
        self._movie_name = movie_name

    def get_movie_name(self):
        return self._movie_name

    def set_imdb_id(self, imdb_id):
        self._imdb_id = imdb_id

    def get_imdb_id(self):
        return self._imdb_id

    def set_language(self, language):
        self._language = language

    def get_language(self):
        return self._language

    def set_release_name(self, release_name):
        self._release_name = release_name

    def get_release_name(self):
        return self._release_name

    def set_comments(self, comments):
        self._comments = comments

    def get_comments(self):
        return self._comments

    def set_author(self, author):
        self._author = author

    def get_author(self):
        return self._author

    def set_hearing_impaired(self, hearing_impaired):
        self._hearing_impaired = hearing_impaired

    def is_hearing_impaired(self):
        return self._hearing_impaired

    def set_high_definition(self, high_definition):
        self._high_definition = high_definition

    def is_high_definition(self):
        return self._high_definition

    def set_foreign_only(self, foreign_only):
        self._foreign_only = foreign_only

    def is_foreign_only(self):
        return self._foreign_only

    def set_automatic_translation(self, automatic_translation):
        self._automatic_translation = automatic_translation

    def is_automatic_translation(self):
        return self._automatic_translation class VideoSubtitle(object):
    def __init__(self, video=None, subtitle=None):
        self.video = video
        self.subtitle = subtitle

    def check(self):
        if not self.video or not self.video.exists():
            return False
        if not self.subtitle or not self.subtitle.exists():
            return False
        return True
from cmd import Cmd
from os import linesep
from pathlib import Path
from subdownloader.client.cli.callback import ProgressBarCallback
from subdownloader.client.cli.state import CliState
from subdownloader.client.state import SubtitleNamingStrategy
from subdownloader.util import IllegalPathException
from subdownloader.filescan import scan_videopaths
from subdownloader.provider.provider import ProviderConnectionError
from subdownloader.languages.language import Language, NotALanguageException, UnknownLanguage
from subdownloader.languages.language import LANGUAGES as ALL_LANGUAGES
from subdownloader.subtitle2 import RemoteSubtitleFile
from subdownloader.movie import LocalMovie, VideoSubtitle
import logging
import re
import shlex
import subdownloader.project
        if not self._text_query.more_movies_available():
            self.echo(_('No more videos are available'))
            return

        nb_before = len(self._text_query.movies)

        try:
            if fetch_all:
                while self._text_query.more_movies_available():
                    self._text_query.search_more_movies()
            else:
                self._text_query.search_more_movies()
        except ProviderConnectionError:
            self.echo(_('An error occured'))

        nb_after = len(self._text_query.movies)

        self.echo(_('Found {} extra videos.').format(nb_after - nb_before))

    def help_queryhasmore(self):
        self.echo(_('Check whether more videos can be found using querymore.'))
        self.echo()
        self.echo('queryhasmore')
        self.echo()

    def do_queryhasmore(self, arg):
        if arg:
            self.echo(_('Unknown arguments: {}').format(arg))
            return
        if not self.query_active():
            self.echo(_('No query active'))
            return
        self.echo(_('Yes') if self._text_query.more_movies_available() else _('No'))

    def help_querysubsearch(self):
        self.echo(_('Look for more subtitles for a particular video.'))
        self.echo(_('The optional argument \'all\' will fetch all subtitles.'))
        self.echo()
        self.echo('querysubsearch INDEX [all]')
        self.echo()

    def do_querysubsearch(self, arg):
        if not self.query_active():
            self.echo(_('No query active'))
            return
        args = self.shlex_parse_argstr(arg)

        if len(args) == 0:
            self.echo(_('Need an argument'))
            return

        fetch_all = False
        if len(args) > 1:
            if args[1] != 'all':
                self.echo(_('Invalid argument'))
                return
            fetch_all = True

        if len(args) > 2:
            self.echo(_('Too many arguments'))
            return

        try:
            rmovie_network_i = int(args[0])
            rmovie_network = self._text_query.movies[rmovie_network_i]
        except ValueError:
            self.echo(_('Invalid argument'))
            return
        except IndexError:
            self.echo(_('Movie not available'))
            return

        nb_subtitles_before = len(rmovie_network.get_subtitles())

        if not rmovie_network.more_subtitles_available():
            self.echo(_('No more subtitles are available'))
            return

        if fetch_all:
            while rmovie_network.more_subtitles_available():
                rmovie_network.search_more_subtitles()
        else:
            rmovie_network.search_more_subtitles()

        nb_subtitles_after = len(rmovie_network.get_subtitles())

        self.echo(_('Found {} extra subtitles').format(nb_subtitles_after-nb_subtitles_before))

    def help_queryshow(self):
        self.echo(_('Display the current videos and subtitles, found by query'))
        self.echo()
        self.echo('queryshow')
        self.echo()

    def do_queryshow(self, arg):
        if arg:
            self.echo(_('Unknown arguments: {}').format(arg))
            return
        if not self.query_active():
            self.echo(_('No query active'))
            return
        sub_i = 0
        for rmovie_network_i, rmovie_network in enumerate(self._text_query.movies):
            rmovie_identity = rmovie_network.get_identities()
            video_identity = rmovie_identity.video_identity
            name = video_identity.get_name()
            year = video_identity.get_year()
            subs_avail = rmovie_network.get_nb_subs_available()
            subs_total = rmovie_network.get_nb_subs_total()
            print('[{rmov_i}] {name} ({year}) [{nb_avail}/{nb_total}]'.format(
                rmov_i=rmovie_network_i, name=name, year=year, nb_avail=subs_avail, nb_total=subs_total))
            for rsub_network in rmovie_network.get_subtitles():
                if not self.download_filter_language_object(rsub_network):
                    continue
                print('  [{xx}] {nb} {subtitles_str} ({full_lang_str})'.format(
                    xx=rsub_network.get_language().xx(),
                    nb=len(rsub_network),
                    subtitles_str=ngettext('subtitle', 'subtitles', len(rsub_network)),
                    full_lang_str=_(rsub_network.get_language().name())))
                for rsub in rsub_network:

                    if not isinstance(rsub, RemoteSubtitleFile):