class ImdbIdentity(object):
    def __init__(self, imdb_id, imdb_rating):
        self._imdb_id = imdb_id
        self._imdb_rating = imdb_rating

    def get_imdb_id(self):
        return self._imdb_id

    def get_imdb_rating(self):
        return self._imdb_rating

    def get_imdb_url(self):
        try:
            imdb_id = '{:07d}'.format(int(self.get_imdb_id()))
        except ValueError:
            imdb_id = self.get_imdb_id()
        return 'http://www.imdb.com/title/tt{imdb_id}/'.format(imdb_id=imdb_id)

    def merge(self, imdb_identity):
        imdb_id = imdb_identity.get_imdb_id()
        if imdb_id is not None:
            self._imdb_id = imdb_id

        imdb_rating = imdb_identity.get_imdb_rating()
        if imdb_rating is not None:
            self._imdb_rating = imdb_rating

    def matches_identity(self, other):
        if type(self) != type(other):
            return MovieMatch.Unknown

        if self.get_imdb_id() is None or other.get_imdb_id() is None:
            return MovieMatch.Unknown
        if self.get_imdb_id() == other.get_imdb_id():
            return MovieMatch.Equal

        return MovieMatch.NonEqual

    def __repr__(self):
        return '<ImdbIdentity:id={!r} rating={!r}'.format(self._imdb_id, self._imdb_rating) class VideoIdentity(object):
    def __init__(self, name, year):
        self._name = name
        self._year = year

    def get_name(self):
        return self._name

    def get_year(self):
        return self._year

    def merge(self, video_identity):
        name = video_identity.get_name()
        if self.get_name() is None:
            self._name = name

        year = video_identity.get_year()
        if self.get_year() is None:
            self._year = year

    def matches_identity(self, other):
        if type(self) != type(other):
            return MovieMatch.Unknown

        if self.get_name() is None or other.get_name() is None:
            return MovieMatch.Unknown
        if self.get_name().lower() == other.get_name().lower():
            return MovieMatch.Equal

        return MovieMatch.Unknown

    def __repr__(self):
        return '<VideoIdentity:name={!r} year={!r}'.format(self._name, self._year) class ProviderIdentities(Identities):
    def __init__(self, provider, video_identity=None, episode_identity=None, imdb_identity=None):
        Identities.__init__(self, video_identity=video_identity, episode_identity=episode_identity,
                            imdb_identity=imdb_identity)
        self._provider = provider

    @property
    def provider(self):
        return self._provider def identificators_get():
    return iter(_IDENTIFICATORS)     def langdetect_detect(*args):
    def langdetect_detect(*args):
    def __init__(self, value, message):
    def value(self):
    def __init__(self, lang_id):
    def locale(self):
    def xx(self):
    def xxx(self):
    def name(self):
    def generic_name(self):
    def is_generic(self):
    def __eq__(self, other):
    def __hash__(self):
    def __repr__(self):
    def __str__(self):
    def raw_data(self):
    def from_raw_data(cls, raw_data):
    def from_locale(cls, locale):
    def from_xx(cls, xx):
    def from_xxx(cls, xxx):
    def from_name(cls, name):
    def _from_xyz(cls, xyzkey, xyzvalue):
    def from_unknown(cls, value, xx=False, xxx=False, locale=False, name=False):
    def from_file(cls, filepath, chunk_size=None):
    def __init__(self, code):
    def create_generic(cls):
    def name(self):
    def generic_name(self):
    def is_generic(self):
    def __eq__(self, other):
    def __hash__(self):
    def __repr__(self):
def name2xxx(name):
def legal_languages():
def all_languages():
def _(text):
class NotALanguageException(ValueError):
class Language(object):
class UnknownLanguage(Language):
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 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 class ImdbMovieMatch:
    def __init__(self, imdb_id: str, title: str, year: Optional[int]):
        self.imdb_id = imdb_id
        self.title = title
        self.year = year

    @property
    def url(self) -> str:
        return 'https://www.imdb.com/title/{}'.format(self.imdb_id)

    @property
    def title_year(self) -> str:
        return '{}{}'.format(self.title, ' ({})'.format(self.year) if self.year is not None else '')

    def serialize(self) -> str:
        year_str = '' if self.year is None else str(self.year)
        return '{},{},{}'.format(self.imdb_id, year_str, self.title)

    @classmethod
    def deserialize(cls, data: str) -> Optional['ImdbMovieMatch']:
        try:
            [imdb_id, year_str, title] = data.split(',', 2)
            year = int(year_str.strip()) if year_str else None
            return cls(imdb_id.strip(), title.strip(), year)
        except ValueError:
            return None class ImdbHistory(object):
    IMDB_HISTORY_KEY = ('upload', 'imdbHistory2',)

    def __init__(self):
        self.__data = []

    @property
    def _data(self) -> List[ImdbMovieMatch]:
        return self.__data

    def __len__(self) -> int:
        return len(self._data)

    def __getitem__(self, item: int) -> ImdbMovieMatch:
        return self._data[item]

    def insert_unique(self, index: int, item: ImdbMovieMatch) -> int:
        current_index = None
        for imdb_i, imdb in enumerate(self._data):
            if imdb.imdb_id == item.imdb_id:
                current_index = imdb_i
                break
        if current_index:
            del self._data[current_index]
        self._data.insert(index, item)
        return index

    def append(self, item: ImdbMovieMatch) -> None:
        self._data.append(item)

    def find_imdb_id(self, imdb_id: str) -> Optional[int]:
        for imdb_i, imdb in enumerate(self._data):
            if imdb.imdb_id == imdb_id:
                return imdb_i
        return None

    def sort_imdb_id(self) -> None:
        self._data.sort(key=lambda imdb: imdb.imdb_id)

    def limit(self, limit: int):
        self._data[:] = self._data[:limit]

    @classmethod
    def from_settings(cls, settings: Settings) -> 'ImdbHistory':
        imdb_str_list = settings.get_list(cls.IMDB_HISTORY_KEY, None)
        if imdb_str_list is None:
            return cls()
        result = cls()
        for imdb_str in imdb_str_list:
            imdb = ImdbMovieMatch.deserialize(imdb_str)
            if imdb is None:
                continue
            result.append(imdb)
        return result

    def save_settings(self, settings: Settings) -> None:
        imdb_str_list = list(imdb.serialize() for imdb in self._data)
        settings.set_list(self.IMDB_HISTORY_KEY, imdb_str_list) class ImdbSearchDialog(QDialog):
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self.ui = Ui_IMDBSearchDialog()
        self.setup_ui()

        self._state = None

    def setup_ui(self) -> None:
        self.ui.setupUi(self)

        self.ui.searchMovieButton.clicked.connect(self.on_search)
        self.ui.movieInfoButton.clicked.connect(self.on_button_movie_info)

        self.ui.okButton.setEnabled(False)
        self.ui.okButton.clicked.connect(self.on_button_ok)

        self.ui.cancelButton.clicked.connect(self.on_button_cancel)

        self.ui.searchResultsView.imdb_selection_changed.connect(self.on_imdb_selection_change)
        self.ui.providerSearch.set_general_visible(False)

        self.retranslate()

    def retranslate(self) -> None:
        pass

    def set_state(self, state) -> None:
        self._state = state

        self.ui.providerSearch.set_state(self._state)
        self.ui.providerSearch.setCurrentIndex(0)

    @pyqtSlot()
    def on_search(self) -> None:
        query = self.ui.movieSearch.text().strip()
        if not query:
            QMessageBox.about(self, _('Error'), _('Please fill out the search title'))
            return

        self.setCursor(Qt.WaitCursor)
        providerState = self.ui.providerSearch.get_selected_provider_state()
        provider = providerState.provider
        try:
            imdb_data = provider.imdb_search_title(title=query)
            if imdb_data is None:
                log.error('Error contacting OSDBServer.SearchMoviesOnIMDB')
            else:
                self.ui.searchResultsView.set_imdb_data(imdb_data)
        except ProviderConnectionError:
            QMessageBox.about(
                self, _("Error"), _('Error contacting the server. Please try again later'))
        self.setCursor(Qt.ArrowCursor)

    def update_buttons(self) -> None:
        identity = self.ui.searchResultsView.get_selected_imdb()
        if identity is None:
            self.ui.movieInfoButton.setEnabled(False)
            self.ui.okButton.setEnabled(False)
        else:
            self.ui.movieInfoButton.setEnabled(True)
            self.ui.okButton.setEnabled(True)

    @pyqtSlot()
    def on_imdb_selection_change(self) -> None:
        self.update_buttons()

    @pyqtSlot()
    def on_button_movie_info(self) -> None:
        imdb = self.ui.searchResultsView.get_selected_imdb()
        if imdb is None:
            QMessageBox.about(
                self, _('Error'), _('Please search and select a movie from the list'))
        else:
            webbrowser.open(imdb.url, new=2, autoraise=1)

    identity_selected = pyqtSignal(ImdbMovieMatch)

    @pyqtSlot()
    def on_button_ok(self) -> None:
        imdb = self.ui.searchResultsView.get_selected_imdb()
        if imdb is None:
            QMessageBox.about(
                self, _('Error'), _('Please search and select a movie from the list'))
        else:
            self.identity_selected.emit(imdb)
            self.accept()

    @pyqtSlot()
    def on_button_cancel(self) -> None:
        self.reject() class PreferencesDialog(QDialog):

    def __init__(self, parent, state, settings):
        QDialog.__init__(self, parent)

        self._state = state
        self._settings = settings

        self._uploadLanguage = UnknownLanguage.create_generic()

        self.providers_ui = dict()

        self.ui = Ui_PreferencesDialog()
        self.setup_ui()

    def setup_ui(self):
        self.ui.setupUi(self)

        # 0. Dialog

        self.ui.buttonApplyChanges.clicked.connect(
            self.onApplyChanges)
        self.ui.buttonCancel.clicked.connect(
            self.onCancel)
        self.ui.tabsPreferences.setCurrentIndex(0)

        # 1. Search tab

        # - Filter languages

        self._filterLanguageComboBoxes = {}

        self._search_languages = {lang: False for lang in all_languages()}
        nb_columns_languages = 4
        for lang_i, lang in enumerate(all_languages()):
            row = lang_i // nb_columns_languages
            column = lang_i % nb_columns_languages

            checkBox = QCheckBox(_(lang.generic_name()), self.ui.scrollAreaWidgetSearch)

            def createSearchLangSlot(lang):
                @pyqtSlot(bool)
                def onSearchLangCheckBoxToggled(toggled):
                    self.searchLanguageChanged.emit(lang, toggled)
                return onSearchLangCheckBoxToggled

            checkBox.toggled.connect(createSearchLangSlot(lang))
            checkBox.setChecked(self._search_languages[lang])

            self._filterLanguageComboBoxes[lang] = checkBox
            self.ui.scrollAreaWidgetLayoutSearch.addWidget(checkBox, row, column)

        self.searchLanguageChanged.connect(self.onSearchLanguageChanged)
        fontSearchItem = self._filterLanguageComboBoxes[UnknownLanguage.create_generic()].font()
        fontSearchItem.setItalic(True)
        self._filterLanguageComboBoxes[UnknownLanguage.create_generic()].setFont(fontSearchItem)

        # 2. Download tab

        # - Download Destination

        self._dlDestinationType = self.DEFAULT_DLDESTINATIONTYPE

        def create_dlDestinationTypeChangedSlot(dlDestinationType):
            @pyqtSlot(bool)
            def dlDestinationTypeChanged(toggled):
                if toggled:
                    self.dlDestinationTypeChanged.emit(dlDestinationType)
            return dlDestinationTypeChanged

        self.ui.optionDlDestinationAsk.toggled.connect(
            create_dlDestinationTypeChangedSlot(SubtitlePathStrategy.ASK))
        self.ui.optionDlDestinationSame.toggled.connect(
            create_dlDestinationTypeChangedSlot(SubtitlePathStrategy.SAME))
        self.ui.optionDlDestinationUser.toggled.connect(
            create_dlDestinationTypeChangedSlot(SubtitlePathStrategy.PREDEFINED))

        self.dlDestinationTypeChanged.connect(self.onDlDestinationTypeChange)

        self.ui.optionDlDestinationUser.toggled.connect(
            self.ui.inputDlDestinationUser.setEnabled)
        self.ui.optionDlDestinationUser.toggled.connect(
            self.ui.buttonDlDestinationUser.setEnabled)
        self.ui.optionDlDestinationUser.toggled.emit(False)

        # Always contains a valid download destination folder
        self._dlDestinationPredefined = Path()

        dlDestinationCompleter = QCompleter()
        dlDestinationCompleter.setModel(QDirModel([], QDir.Dirs | QDir.NoDotAndDotDot, QDir.Name, dlDestinationCompleter))
        self.ui.inputDlDestinationUser.setCompleter(dlDestinationCompleter)
        self.ui.inputDlDestinationUser.editingFinished.connect(
            self.onInputDlDestinationEditingFinished)

        self.ui.buttonDlDestinationUser.clicked.connect(
            self.onButtonDlDestinationClicked)

        # - Subtitle Filename

        self._subtitleFilename = self.DEFAULT_DLSUBFN

        def create_dlSubtitleFileNameChangedSlot(subtitleFilename):
            @pyqtSlot(bool)
            def subtitleFileNameChanged(toggled):
                if toggled:
                    self.subtitleFilenameChanged.emit(subtitleFilename)
            return subtitleFileNameChanged

        self.ui.optionSubFnSame.toggled.connect(
            create_dlSubtitleFileNameChangedSlot(SubtitleNamingStrategy.VIDEO))
        self.ui.optionSubFnSameLang.toggled.connect(
            create_dlSubtitleFileNameChangedSlot(SubtitleNamingStrategy.VIDEO_LANG))
        self.ui.optionSubFnSameLangUploader.toggled.connect(
            create_dlSubtitleFileNameChangedSlot(SubtitleNamingStrategy.VIDEO_LANG_UPLOADER))
        self.ui.optionSubFnOnline.toggled.connect(
            create_dlSubtitleFileNameChangedSlot(SubtitleNamingStrategy.ONLINE))

        self.subtitleFilenameChanged.connect(self.onSubtitleFilenameChange)

        # 3. Upload tab

        # - Default Subtitle Language

        self._uploadLanguage = self.DEFAULT_UL_LANG

        self.ui.optionUlDefaultLanguage.set_unknown_text(_('Auto Detect'))
        self.ui.optionUlDefaultLanguage.set_selected_language(self._uploadLanguage)

        self.ui.optionUlDefaultLanguage.selected_language_changed.connect(self.onOptionUlDefaultLanguageChange)

        # 4. Providers tab

        for providerState in self._state.providers.all_states:
            provider = providerState.provider
            provider_name = provider.get_name()
            from PyQt5.QtWidgets import QVBoxLayout, QLabel
            ui_items = {
                '_enabled': QCheckBox(),
                '_textDisabled': QLabel(_('This provider is connected and cannot be configured')),
            }
            ui_items['_textDisabled'].setVisible(providerState.provider.connected())
            ui_items['_textDisabled'].setEnabled(False)
            providerWidget = QWidget()
            providerLayout = QVBoxLayout()
            providerLayout.addWidget(ui_items['_textDisabled'])
            dataWidget = QWidget()
            providerLayout.addWidget(dataWidget)
            providerLayout.addStretch()
            dataLayout = QFormLayout()
            dataWidget.setLayout(dataLayout)
            dataLayout.addRow(_('enabled').capitalize(), ui_items['_enabled'])

            for key, key_type in provider.get_settings().key_types().items():
                if key_type == ProviderSettingsType.String:
                    widget = QLineEdit()
                elif key_type == ProviderSettingsType.Password:
                    widget = QLineEdit()
                    widget.setEchoMode(QLineEdit.Password)
                else:
                    # FIXME: generalize this warning about possible warnings?
                    log.error('Unknown provider settings type: {}: {} -> {}'.format(provider_name, key, key_type))
                    QMessageBox.warning(self, _('Unknown provider settings type'),
                                        '\n'.join((_('An unknown settings type has been passed.'),
                                                   _('Please open an issue'))))
                    continue
                dataLayout.addRow(key.capitalize(), widget)
                ui_items[key] = widget
            providerWidget.setLayout(providerLayout)
            ui_items['_stack_index'] = self.ui.providerStack.addWidget(providerWidget)
            self.providers_ui[provider_name] = ui_items

        self.ui.providerComboBox.set_state(self._state)
        self.ui.providerComboBox.set_general_visible(False)
        self.ui.providerComboBox.set_filter_enable(False)
        self.ui.providerComboBox.selected_provider_state_changed.connect(self.on_selected_provider_state_changed)
        self.ui.providerComboBox.setCurrentIndex(0)

        # 5. Others tab

        # - Interface Language

        self._original_interface_language = UnknownLanguage.create_generic()
        self.ui.optionInterfaceLanguage.set_unknown_text(_('System Language'))
        self.ui.optionUlDefaultLanguage.set_selected_language(self._original_interface_language)

        # - Video Application Location

        self.ui.buttonVideoAppLocationChoose.clicked.connect(
            self.onButtonVideoAppLocationChoose)
        self.ui.buttonHelpTranslation.clicked.connect(
            self.onHelpTranslate)

        self.readSettings()

    def readSettings(self):
        log.debug('readSettings: start')

        # 1. Search tab
        checked_languages = self._state.get_download_languages()
        for checked_language in checked_languages:
            self._filterLanguageComboBoxes[checked_language].setChecked(True)

        # 2. Download tab

        # - Download Destination

        optionWhereToDownload = self._state.get_subtitle_download_path_strategy()
        if optionWhereToDownload == SubtitlePathStrategy.ASK:
            self.ui.optionDlDestinationAsk.setChecked(True)
        elif optionWhereToDownload == SubtitlePathStrategy.SAME:
            self.ui.optionDlDestinationSame.setChecked(True)
        elif optionWhereToDownload == SubtitlePathStrategy.PREDEFINED:
            self.ui.optionDlDestinationUser.setChecked(True)

        dlDestination = self._state.get_default_download_path()
        self.ui.inputDlDestinationUser.setText(str(dlDestination))
        self.ui.inputDlDestinationUser.editingFinished.emit()

        # - Subtitle Filename

        optionSubtitleName = self._state.get_subtitle_naming_strategy()
        if optionSubtitleName == SubtitleNamingStrategy.VIDEO:
            self.ui.optionSubFnSame.setChecked(True)
        elif optionSubtitleName == SubtitleNamingStrategy.VIDEO_LANG:
            self.ui.optionSubFnSameLang.setChecked(True)
        elif optionSubtitleName == SubtitleNamingStrategy.VIDEO_LANG_UPLOADER:
            self.ui.optionSubFnSameLangUploader.setChecked(True)
        elif optionSubtitleName == SubtitleNamingStrategy.ONLINE:
            self.ui.optionSubFnOnline.setChecked(True)

        # 3. Upload tab

        # - Default Subtitle Language

        self._uploadLanguage = self._state.get_upload_language()
        self.ui.optionUlDefaultLanguage.set_selected_language(self._uploadLanguage)

        # 4. Providers' tab
        for providerState in self._state.providers.all_states:
            provider = providerState.provider
            provider_name = provider.get_name()
            provider_ui = self.providers_ui[provider_name]
            provider_ui['_enabled'].setChecked(providerState.getEnabled())

            if self._state.providers.connected():
                provider_ui['_enabled'].setEnabled(False)

            dataDict = provider.get_settings().as_dict()
            for key, key_type in provider.get_settings().key_types().items():
                if key_type == ProviderSettingsType.String:
                    provider_ui[key].setText(dataDict[key])
                elif key_type == ProviderSettingsType.Password:
                    provider_ui[key].setText(dataDict[key])
                else:
                    continue

                if self._state.providers.connected():
                    provider_ui[key].setEnabled(False)

        # 5. Others tab

        # - Interface Language

        optionInterfaceLanguage = self._state.get_interface_language()
        self._original_interface_language = optionInterfaceLanguage
        self.ui.optionInterfaceLanguage.set_selected_language(optionInterfaceLanguage)

        playerPath = self._state.get_videoplayer().get_path()
        playerParams = self._state.get_videoplayer().get_command()
        self.ui.inputVideoAppLocation.setText(str(playerPath))
        self.ui.inputVideoAppParams.setText(playerParams)

        log.debug('readSettings: finish')

    @pyqtSlot()
    def saveSettings(self):
        log.debug('saveSettings: start')

        # 1. Search tab

        checked_languages = [lang[0] for lang in filter(lambda x:x[1], self._search_languages.items())]
        self._state.set_download_languages(checked_languages)

        # 2. Downloads tab

        # - Download Destination

        self._state.set_subtitle_download_path_strategy(self._dlDestinationType)
        self._state.set_default_download_path(self._dlDestinationPredefined)

        # - Subtitle Filename

        self._state.set_subtitle_naming_strategy(self._subtitleFilename)

        # 3. Upload tab

        # - Default Subtitle Language

        self._state.set_upload_language(self._uploadLanguage)

        # 4. Providers' tab
        for providerState in self._state.providers.all_states:
            provider = providerState.provider
            provider_name = provider.get_name()
            provider_ui = self.providers_ui[provider_name]
            providerState.setEnabled(provider_ui['_enabled'].isChecked())
            dataDict = provider.get_settings().as_dict()
            for key, key_type in provider.get_settings().key_types().items():
                if key_type == ProviderSettingsType.String:
                    dataDict[key] = provider_ui[key].text()
                elif key_type == ProviderSettingsType.Password:
                    dataDict[key] = provider_ui[key].text()
                else:
                    continue
            if providerState.getEnabled() and not providerState.provider.connected():
                new_settings = provider.get_settings().load(**dataDict)
                provider.set_settings(new_settings)
        # Emit signal to update number of providers
        self._state.signals.login_status_changed.emit()

        # 5. Others tab

        # - Interface Language

        new_interface_language = self.ui.optionInterfaceLanguage.get_selected_language()
        self._state.set_interface_language(new_interface_language)
        if self._original_interface_language != new_interface_language:
            self._state.signals.interface_language_changed.emit(new_interface_language)

        # - video player

        playerPath = Path(self.ui.inputVideoAppLocation.text())
        playerParams = self.ui.inputVideoAppParams.text()
        videoPlayer = VideoPlayer(playerPath, playerParams)
        self._state.set_videoplayer(videoPlayer)

        # Finally, write to disk

        self._state.save_settings(self._settings)
        log.debug('saveSettings: finish')

    # 0. Interface

    def validate(self):
        # Download Destination Validation
        dlDestinationUser = self.ui.inputDlDestinationUser.text()
        if self._dlDestinationType == SubtitlePathStrategy.PREDEFINED and not os.path.isdir(dlDestinationUser):
            QMessageBox.about(
                self, _('Error'), _('Predefined Folder is invalid'))
            return False
        return True

    @pyqtSlot()
    def onApplyChanges(self):
        if self.validate():
            self.saveSettings()
            self.close()

    @pyqtSlot()
    def onCancel(self):
        self.reject()

    # 1. Search tab

    searchLanguageChanged = pyqtSignal(Language, bool)

    @pyqtSlot(Language, bool)
    def onSearchLanguageChanged(self, lang, toggled):
        self._search_languages[lang] = toggled

    # 2. Downloads tab

    # - Download destination

    dlDestinationTypeChanged = pyqtSignal(SubtitlePathStrategy)
    dlDestinationPredefinedChanged = pyqtSignal(Path)

    DEFAULT_DLDESTINATIONTYPE = SubtitlePathStrategy.SAME

    @pyqtSlot(SubtitlePathStrategy)
    def onDlDestinationTypeChange(self, dlDestinationType):
        self._dlDestinationType = dlDestinationType

    @pyqtSlot()
    def onButtonDlDestinationClicked(self):
        directory = QFileDialog.getExistingDirectory(self, _('Select a directory'), str(self._dlDestinationPredefined))
        if not directory:
            # Canceled
            return
        if os.path.isdir(directory):
            self.ui.inputDlDestinationUser.setText(directory)
            self._dlDestinationPredefined = Path(directory)
            self.dlDestinationPredefinedChanged.emit(self._dlDestinationPredefined)
        else:
            QMessageBox.warning(self, _('Not a directory'), _('"{path}" is not a directory').format(path=directory))

    @pyqtSlot()
    def onInputDlDestinationEditingFinished(self):
        path = self.ui.inputDlDestinationUser.text()
        if os.path.isdir(path):
            self._dlDestinationPredefined = Path(path)

    def getDownloadDestinationPredefined(self):
        return self._dlDestinationPredefined

    # - Subtitle Filename

    subtitleFilenameChanged = pyqtSignal(SubtitleNamingStrategy)

    DEFAULT_DLSUBFN = SubtitleNamingStrategy.VIDEO

    def onSubtitleFilenameChange(self, subtitleFilename):
        self._subtitleFilename = subtitleFilename

    # 3. Upload tab

    # - Default Subtitle Language

    DEFAULT_UL_LANG = UnknownLanguage.create_generic()

    @pyqtSlot(Language)
    def onOptionUlDefaultLanguageChange(self, lang):
        self._uploadLanguage = lang

    # 4. Providers tab

    @pyqtSlot(ProviderState)
    def on_selected_provider_state_changed(self, provider_state):
        if provider_state.value is None:
            return
        provider = provider_state.value.provider
        self.ui.providerStack.setCurrentIndex(self.providers_ui[provider.get_name()]['_stack_index'])

    # 5. Others tab

    interfaceLanguageChange = pyqtSignal(Language)

    def actionContextMenu(self, action, os):
        pass

    @pyqtSlot()
    def onHelpTranslate(self):
        webbrowser.open(WEBSITE_TRANSLATE, new=2, autoraise=1)

    @pyqtSlot()
    def onButtonVideoAppLocationChoose(self):
        extensions = ''
        if platform.system == 'Windows':
            extensions = '*.exe'

        fileName, t = QFileDialog.getOpenFileName(
            self, _('Select the Video Player executable file'), '', extensions)
        if fileName:
            self.ui.inputVideoAppLocation.setText(fileName) class Ui_UploadWidget(object):
    def setupUi(self, UploadWidget):
        UploadWidget.setObjectName("UploadWidget")
        UploadWidget.resize(935, 725)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(UploadWidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.groupBox_2 = QtWidgets.QGroupBox(UploadWidget)
        self.groupBox_2.setEnabled(True)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth())
        self.groupBox_2.setSizePolicy(sizePolicy)
        self.groupBox_2.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.groupBox_2.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
        self.groupBox_2.setObjectName("groupBox_2")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox_2)
        self.verticalLayout.setContentsMargins(-1, 1, -1, 1)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        self.buttonUploadBrowseFolder = QtWidgets.QToolButton(self.groupBox_2)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(":/images/openfolder.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.buttonUploadBrowseFolder.setIcon(icon)
        self.buttonUploadBrowseFolder.setIconSize(QtCore.QSize(24, 24))
        self.buttonUploadBrowseFolder.setObjectName("buttonUploadBrowseFolder")
        self.horizontalLayout_5.addWidget(self.buttonUploadBrowseFolder)
        self.line_3 = QtWidgets.QFrame(self.groupBox_2)
        self.line_3.setFrameShape(QtWidgets.QFrame.VLine)
        self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_3.setObjectName("line_3")
        self.horizontalLayout_5.addWidget(self.line_3)
        self.buttonUploadPlusRow = QtWidgets.QToolButton(self.groupBox_2)
        self.buttonUploadPlusRow.setEnabled(True)
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap(":/images/plus.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.buttonUploadPlusRow.setIcon(icon1)
        self.buttonUploadPlusRow.setIconSize(QtCore.QSize(24, 24))
        self.buttonUploadPlusRow.setObjectName("buttonUploadPlusRow")
        self.horizontalLayout_5.addWidget(self.buttonUploadPlusRow)
        self.buttonUploadMinusRow = QtWidgets.QToolButton(self.groupBox_2)
        self.buttonUploadMinusRow.setEnabled(False)
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap(":/images/minus.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.buttonUploadMinusRow.setIcon(icon2)
        self.buttonUploadMinusRow.setIconSize(QtCore.QSize(24, 24))
        self.buttonUploadMinusRow.setObjectName("buttonUploadMinusRow")
        self.horizontalLayout_5.addWidget(self.buttonUploadMinusRow)
        self.buttonUploadDeleteAllRow = QtWidgets.QToolButton(self.groupBox_2)
        self.buttonUploadDeleteAllRow.setEnabled(True)
        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap(":/images/delete_all.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.buttonUploadDeleteAllRow.setIcon(icon3)
        self.buttonUploadDeleteAllRow.setIconSize(QtCore.QSize(24, 24))
        self.buttonUploadDeleteAllRow.setObjectName("buttonUploadDeleteAllRow")
        self.horizontalLayout_5.addWidget(self.buttonUploadDeleteAllRow)
        self.line_2 = QtWidgets.QFrame(self.groupBox_2)
        self.line_2.setFrameShape(QtWidgets.QFrame.VLine)
        self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_2.setObjectName("line_2")
        self.horizontalLayout_5.addWidget(self.line_2)
        self.buttonUploadUpRow = QtWidgets.QToolButton(self.groupBox_2)
        self.buttonUploadUpRow.setEnabled(False)
        icon4 = QtGui.QIcon()
        icon4.addPixmap(QtGui.QPixmap(":/images/up.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.buttonUploadUpRow.setIcon(icon4)
        self.buttonUploadUpRow.setIconSize(QtCore.QSize(24, 24))
        self.buttonUploadUpRow.setObjectName("buttonUploadUpRow")
        self.horizontalLayout_5.addWidget(self.buttonUploadUpRow)
        self.buttonUploadDownRow = QtWidgets.QToolButton(self.groupBox_2)
        self.buttonUploadDownRow.setEnabled(False)
        icon5 = QtGui.QIcon()
        icon5.addPixmap(QtGui.QPixmap(":/images/down.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.buttonUploadDownRow.setIcon(icon5)
        self.buttonUploadDownRow.setIconSize(QtCore.QSize(24, 24))
        self.buttonUploadDownRow.setObjectName("buttonUploadDownRow")
        self.horizontalLayout_5.addWidget(self.buttonUploadDownRow)
        spacerItem = QtWidgets.QSpacerItem(401, 33, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_5.addItem(spacerItem)
        self.verticalLayout.addLayout(self.horizontalLayout_5)
        self.uploadView = UploadListView(self.groupBox_2)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.uploadView.sizePolicy().hasHeightForWidth())
        self.uploadView.setSizePolicy(sizePolicy)
        self.uploadView.setMinimumSize(QtCore.QSize(0, 0))
        self.uploadView.setAutoScrollMargin(16)
        self.uploadView.setObjectName("uploadView")
        self.verticalLayout.addWidget(self.uploadView)
        self.uploadDetailsWidget = QtWidgets.QWidget(self.groupBox_2)
        self.uploadDetailsWidget.setMaximumSize(QtCore.QSize(16777215, 16777215))
        self.uploadDetailsWidget.setObjectName("uploadDetailsWidget")
        self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.uploadDetailsWidget)
        self.verticalLayout_7.setObjectName("verticalLayout_7")
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setVerticalSpacing(0)
        self.gridLayout.setObjectName("gridLayout")
        self.labelReleaseName = QtWidgets.QLabel(self.uploadDetailsWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.labelReleaseName.sizePolicy().hasHeightForWidth())
        self.labelReleaseName.setSizePolicy(sizePolicy)
        self.labelReleaseName.setObjectName("labelReleaseName")
        self.gridLayout.addWidget(self.labelReleaseName, 0, 0, 1, 1)
        self.comboAutomaticTranslation = QtWidgets.QCheckBox(self.uploadDetailsWidget)
        self.comboAutomaticTranslation.setText("")
        self.comboAutomaticTranslation.setObjectName("comboAutomaticTranslation")
        self.gridLayout.addWidget(self.comboAutomaticTranslation, 7, 2, 1, 1)
        self.labelTranslator = QtWidgets.QLabel(self.uploadDetailsWidget)
        self.labelTranslator.setObjectName("labelTranslator")
        self.gridLayout.addWidget(self.labelTranslator, 9, 0, 1, 1)
        self.comboHearingImpaired = QtWidgets.QCheckBox(self.uploadDetailsWidget)
        self.comboHearingImpaired.setText("")
        self.comboHearingImpaired.setObjectName("comboHearingImpaired")
        self.gridLayout.addWidget(self.comboHearingImpaired, 6, 2, 1, 1)
        self.horizontalLayout_12 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_12.setObjectName("horizontalLayout_12")
        self.uploadLanguages = LanguageComboBox(self.uploadDetailsWidget)
        self.uploadLanguages.setObjectName("uploadLanguages")
        self.horizontalLayout_12.addWidget(self.uploadLanguages)
        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_12.addItem(spacerItem1)
        self.gridLayout.addLayout(self.horizontalLayout_12, 2, 2, 1, 2)
        self.labelHearingImpaired = QtWidgets.QLabel(self.uploadDetailsWidget)
        self.labelHearingImpaired.setObjectName("labelHearingImpaired")
        self.gridLayout.addWidget(self.labelHearingImpaired, 6, 0, 1, 1)
        self.label = QtWidgets.QLabel(self.uploadDetailsWidget)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 12, 0, 1, 1)
        self.buttonUploadFindIMDB = QtWidgets.QPushButton(self.uploadDetailsWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.buttonUploadFindIMDB.sizePolicy().hasHeightForWidth())
        self.buttonUploadFindIMDB.setSizePolicy(sizePolicy)
        self.buttonUploadFindIMDB.setMinimumSize(QtCore.QSize(0, 0))
        self.buttonUploadFindIMDB.setMaximumSize(QtCore.QSize(120, 16777215))
        icon6 = QtGui.QIcon()
        icon6.addPixmap(QtGui.QPixmap(":/images/search.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.buttonUploadFindIMDB.setIcon(icon6)
        self.buttonUploadFindIMDB.setObjectName("buttonUploadFindIMDB")
        self.gridLayout.addWidget(self.buttonUploadFindIMDB, 1, 3, 1, 1)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.gridLayout.addLayout(self.horizontalLayout_2, 13, 0, 1, 4)
        self.labelMovieTitle = QtWidgets.QLabel(self.uploadDetailsWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.labelMovieTitle.sizePolicy().hasHeightForWidth())
        self.labelMovieTitle.setSizePolicy(sizePolicy)
        self.labelMovieTitle.setObjectName("labelMovieTitle")
        self.gridLayout.addWidget(self.labelMovieTitle, 1, 0, 1, 1)
        self.buttonUpload = QtWidgets.QPushButton(self.uploadDetailsWidget)
        self.buttonUpload.setEnabled(True)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        self.buttonUpload.setFont(font)
        icon7 = QtGui.QIcon()
        icon7.addPixmap(QtGui.QPixmap(":/images/upload.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.buttonUpload.setIcon(icon7)
        self.buttonUpload.setIconSize(QtCore.QSize(24, 24))
        self.buttonUpload.setObjectName("buttonUpload")
        self.gridLayout.addWidget(self.buttonUpload, 12, 3, 1, 1)
        self.uploadTranslator = QtWidgets.QLineEdit(self.uploadDetailsWidget)
        self.uploadTranslator.setObjectName("uploadTranslator")
        self.gridLayout.addWidget(self.uploadTranslator, 9, 2, 1, 2)
        self.labelSubtitleLanguage = QtWidgets.QLabel(self.uploadDetailsWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.labelSubtitleLanguage.sizePolicy().hasHeightForWidth())
        self.labelSubtitleLanguage.setSizePolicy(sizePolicy)
        self.labelSubtitleLanguage.setObjectName("labelSubtitleLanguage")
        self.gridLayout.addWidget(self.labelSubtitleLanguage, 2, 0, 1, 1)
        self.comboHighDefinition = QtWidgets.QCheckBox(self.uploadDetailsWidget)
        self.comboHighDefinition.setText("")
        self.comboHighDefinition.setObjectName("comboHighDefinition")
        self.gridLayout.addWidget(self.comboHighDefinition, 5, 2, 1, 1)
        self.uploadReleaseText = QtWidgets.QLineEdit(self.uploadDetailsWidget)
        self.uploadReleaseText.setObjectName("uploadReleaseText")
        self.gridLayout.addWidget(self.uploadReleaseText, 0, 2, 1, 2)
        self.uploadComments = QtWidgets.QTextEdit(self.uploadDetailsWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.uploadComments.sizePolicy().hasHeightForWidth())
        self.uploadComments.setSizePolicy(sizePolicy)
        self.uploadComments.setMaximumSize(QtCore.QSize(16777215, 50))
        self.uploadComments.setObjectName("uploadComments")
        self.gridLayout.addWidget(self.uploadComments, 11, 2, 1, 2)
        self.uploadIMDB = QtWidgets.QComboBox(self.uploadDetailsWidget)
        self.uploadIMDB.setObjectName("uploadIMDB")
        self.uploadIMDB.addItem("")
        self.gridLayout.addWidget(self.uploadIMDB, 1, 2, 1, 1)
        self.labelAutomaticTranslation = QtWidgets.QLabel(self.uploadDetailsWidget)
        self.labelAutomaticTranslation.setObjectName("labelAutomaticTranslation")
        self.gridLayout.addWidget(self.labelAutomaticTranslation, 7, 0, 1, 1)
        self.labelHighDefinition = QtWidgets.QLabel(self.uploadDetailsWidget)
        self.labelHighDefinition.setObjectName("labelHighDefinition")
        self.gridLayout.addWidget(self.labelHighDefinition, 5, 0, 1, 1)
        self.labelComments = QtWidgets.QLabel(self.uploadDetailsWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.labelComments.sizePolicy().hasHeightForWidth())
        self.labelComments.setSizePolicy(sizePolicy)
        self.labelComments.setObjectName("labelComments")
        self.gridLayout.addWidget(self.labelComments, 11, 0, 1, 1)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.comboProvider = ProviderComboBox(self.uploadDetailsWidget)
        self.comboProvider.setObjectName("comboProvider")
        self.horizontalLayout.addWidget(self.comboProvider)
        spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem2)
        self.gridLayout.addLayout(self.horizontalLayout, 12, 2, 1, 1)
        self.verticalLayout_7.addLayout(self.gridLayout)
        self.verticalLayout.addWidget(self.uploadDetailsWidget)
        self.verticalLayout_2.addWidget(self.groupBox_2)

        self.retranslateUi(UploadWidget)
        QtCore.QMetaObject.connectSlotsByName(UploadWidget)

    def retranslateUi(self, UploadWidget):
        _translate = QtCore.QCoreApplication.translate
        UploadWidget.setWindowTitle(_("Form"))
        self.groupBox_2.setTitle(_("Select the videos and subtitles  (only subtitles will be uploaded):"))
        self.labelReleaseName.setText(_("Release name:"))
        self.labelTranslator.setText(_("Translator"))
        self.labelHearingImpaired.setText(_("Hearing impaired"))
        self.label.setText(_("Provider:"))
        self.buttonUploadFindIMDB.setText(_("Find"))
        self.labelMovieTitle.setText(_("Movie Title:"))
        self.buttonUpload.setText(_("Upload"))
        self.labelSubtitleLanguage.setText(_("Subtitle Language:"))
        self.uploadIMDB.setItemText(0, _("Click on the Find button to identify the movie"))
        self.labelAutomaticTranslation.setText(_("Automatic translation"))
        self.labelHighDefinition.setText(_("High Definition"))
        self.labelComments.setText(_("Comments:"))
import logging
from typing import Optional, Sequence
from PyQt5.QtCore import pyqtSlot, QAbstractListModel, QModelIndex, QObject, QSettings, Qt
from PyQt5.QtWidgets import QAbstractItemView, QHeaderView, QMessageBox, QWidget
from subdownloader.identification import ImdbIdentity, VideoIdentity, ProviderIdentities, identificators_get
from subdownloader.languages import language
from subdownloader.movie import LocalMovie, VideoSubtitle
from subdownloader.provider.imdb import ImdbMovieMatch, ImdbHistory
from subdownloader.client.gui.widgets.imdbSearch import ImdbSearchDialog
from subdownloader.client.gui.widgets.preferences import PreferencesDialog
from subdownloader.client.gui.generated.uploadWidget_ui import Ui_UploadWidget
    def set_state(self, state):
        self._state = state
        self._state.signals.interface_language_changed.connect(self.on_interface_language_changed)

        self._state.signals.login_status_changed.connect(self.on_login_status_changed)

        self.ui.comboProvider.set_state(self._state)
        self.ui.uploadView.set_local_movie(self._upload_movie)
        self._imdb_history_model.set_state(self._state)
        self.ui.uploadIMDB.setCurrentIndex(0)

    def get_state(self):
        return self._state

    def setup_ui(self):
        self.ui.setupUi(self)

        self.ui.uploadView.selectionModel().selectionChanged.connect(self.on_selection_change)

        self.ui.uploadView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.ui.uploadView.setAlternatingRowColors(True)
        self.ui.uploadView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.ui.uploadView.setGridStyle(Qt.DotLine)

        self.ui.buttonUploadBrowseFolder.clicked.connect(self.ui.uploadView.on_browse_folder)

        self.ui.buttonUploadPlusRow.clicked.connect(self.ui.uploadView.on_add_row)
        self.ui.buttonUploadMinusRow.clicked.connect(self.ui.uploadView.on_remove_selection)

        self.ui.buttonUploadDeleteAllRow.clicked.connect(self.on_reset)

        self.ui.buttonUploadDownRow.clicked.connect(self.ui.uploadView.on_move_selection_down)
        self.ui.buttonUploadUpRow.clicked.connect(self.ui.uploadView.on_move_selection_up)

        self.ui.uploadLanguages.set_unknown_visible(True)
        self.ui.uploadLanguages.selected_language_changed.connect(self.on_upload_language_change)

        self.ui.buttonUpload.setEnabled(False)
        self.ui.buttonUpload.clicked.connect(self.on_upload)

        self.ui.buttonUploadFindIMDB.clicked.connect(self.on_find_imdb)
        self.ui.uploadView.upload_data_changed.connect(self.on_upload_data_changed)

        self.ui.uploadIMDB.setModel(self._imdb_history_model)
        self.ui.uploadIMDB.currentIndexChanged.connect(self.onImdbComboIndexChanged)

        self.ui.uploadReleaseText.textChanged.connect(self.onReleaseNameChange)
        self.ui.uploadComments.textChanged.connect(self.onCommentsChange)
        self.ui.uploadTranslator.textChanged.connect(self.onTranslatorChange)

        self.ui.comboProvider.set_general_visible(True)
        self.ui.comboProvider.set_filter_enable(True)
        self.ui.comboProvider.selected_provider_state_changed.connect(self.onUploadProviderChanged)

        self.ui.comboHighDefinition.stateChanged.connect(self.onHighDefinitionChanged)
        self.ui.comboAutomaticTranslation.stateChanged.connect(self.onAutomaticTranslationChanged)
        self.ui.comboHearingImpaired.stateChanged.connect(self.onHearingImpairedChanged)

        self.on_reset()
        self.retranslate()

    def retranslate(self):
        self.ui.uploadLanguages.set_unknown_text(_('Unknown'))
        self.ui.uploadView.retranslate()

        self.ui.buttonUploadBrowseFolder.setToolTip(_('Load videos and subtitles from directory'))
        self.ui.buttonUploadDeleteAllRow.setToolTip(_('Reset videos and subtitles'))
        self.ui.buttonUploadPlusRow.setToolTip(_('Insert row above selected row'))
        self.ui.buttonUploadMinusRow.setToolTip(_('Remove table'))
        self.ui.buttonUploadUpRow.setToolTip(_('Move selected row up'))
        self.ui.buttonUploadDownRow.setToolTip(_('Move selected row down'))

        self.ui.comboProvider.set_general_text('({})'.format(_('none')))

    @pyqtSlot()
    def on_interface_language_changed(self):
        self.ui.retranslateUi(self)
        self.retranslate()

    def update_navigation_buttons(self):
        selected = self.ui.uploadView.selectionModel().selectedRows()
        if len(selected) == 0:
            self.ui.buttonUploadDownRow.setEnabled(False)
            self.ui.buttonUploadUpRow.setEnabled(False)
            self.ui.buttonUploadMinusRow.setEnabled(False)
        else:
            rows = [s.row() for s in selected]
            min_selected = min(rows)
            max_selected = max(rows)
            self.ui.buttonUploadDownRow.setEnabled(max_selected < len(self.ui.uploadView.get_data_collection()) - 1)
            self.ui.buttonUploadUpRow.setEnabled(min_selected > 0)
            self.ui.buttonUploadMinusRow.setEnabled(True)

    @pyqtSlot()
    def on_login_status_changed(self):
        self.check_enable_upload_button()

    @pyqtSlot()
    def on_upload_data_changed(self):
        self.update_navigation_buttons()

        # FIXME: move to UploadDataCollection or more general place
        data_collection = self.ui.uploadView.get_data_collection()

        for identificator in identificators_get():
            videos_to_identify = []
            for video in data_collection.iter_videos():
                if identificator in video.get_identities():
                    continue
                videos_to_identify.append(video)
            identificator.identify_videos(videos_to_identify)

        if self.ui.uploadLanguages.get_selected_language().is_generic():
            language_nb = {}
            for subtitle in data_collection.iter_subtitles():
                lang = subtitle.get_language()
                if not lang.is_generic():
                    language_nb[lang] = language_nb.get(lang, 0) + 1
                    continue


                lang = language.Language.from_file(subtitle.get_filepath())