Example #1
0
    def saveNewznabProvider(name, url, api_key=''):
        """
        Save a Newznab Provider
        """

        if not name or not url:
            return '0'

        provider_dict = dict(
            zip([x.name for x in app.newznabProviderList],
                app.newznabProviderList))

        if name in provider_dict:
            if not provider_dict[name].default:
                provider_dict[name].name = name
                provider_dict[name].url = config.clean_url(url)

            provider_dict[name].api_key = api_key
            # a 0 in the api key spot indicates that no api key is needed
            if api_key == '0':
                provider_dict[name].needs_auth = False
            else:
                provider_dict[name].needs_auth = True

            return '|'.join([
                provider_dict[name].get_id(),
                provider_dict[name].config_string()
            ])

        else:
            new_provider = NewznabProvider(name, url, api_key=api_key)
            app.newznabProviderList.append(new_provider)
            return '|'.join(
                [new_provider.get_id(),
                 new_provider.config_string()])
Example #2
0
    def getNewznabCategories(name, url, api_key):
        """
        Retrieves a list of possible categories with category id's
        Using the default url/api?cat
        http://yournewznaburl.com/api?t=caps&apikey=yourapikey
        """
        error = ''

        if not name:
            error += '\nNo Provider Name specified'
        if not url:
            error += '\nNo Provider Url specified'
        if not api_key:
            error += '\nNo Provider Api key specified'

        if error != '':
            return json.dumps({'success': False, 'error': error})

        # Get list with Newznabproviders
        # providerDict = dict(zip([x.get_id() for x in api.newznabProviderList], api.newznabProviderList))

        # Get newznabprovider obj with provided name
        temp_provider = NewznabProvider(name, url, api_key)

        success, tv_categories, error = temp_provider.get_newznab_categories()

        return json.dumps({
            'success': success,
            'tv_categories': tv_categories,
            'error': error
        })
Example #3
0
    def saveNewznabProvider(name, url, api_key=''):
        """Save a Newznab Provider."""
        if not name or not url:
            return '0'

        provider_dict = dict(list(zip([x.name for x in app.newznabProviderList], app.newznabProviderList)))

        if name in provider_dict:
            if not provider_dict[name].default:
                provider_dict[name].name = name
                provider_dict[name].url = config.clean_url(url)

            provider_dict[name].api_key = api_key
            # a 0 in the api key spot indicates that no api key is needed
            if api_key == '0':
                provider_dict[name].needs_auth = False
            else:
                provider_dict[name].needs_auth = True

            return '|'.join([provider_dict[name].get_id(), provider_dict[name].config_string()])

        else:
            new_provider = NewznabProvider(name, url, api_key=api_key)
            app.newznabProviderList.append(new_provider)
            return '|'.join([new_provider.get_id(), new_provider.config_string()])
Example #4
0
    def getZnabCategories(kind, name, url, api_key):
        """
        Retrieve a list of possible categories with category ids.

        Using the default url/api?cat
        http://yournewznaburl.com/api?t=caps&apikey=yourapikey
        """
        error = ''

        if not name:
            error += '\nNo Provider Name specified'
        if not url:
            error += '\nNo Provider Url specified'
        if not api_key:
            error += '\nNo Provider Api key specified'

        if error != '':
            return json.dumps({'success': False, 'message': error})

        if kind == 'newznab':
            temp_provider = NewznabProvider(name, url, api_key)
        elif kind == 'torznab':
            temp_provider = TorznabProvider(name, url, api_key)

        capabilities = temp_provider.get_capabilities()

        return json.dumps(capabilities._asdict())
Example #5
0
    def getZnabCategories(kind, name, url, api_key):
        """
        Retrieve a list of possible categories with category ids.

        Using the default url/api?cat
        http://yournewznaburl.com/api?t=caps&apikey=yourapikey
        """
        error = ''

        if not name:
            error += '\nNo Provider Name specified'
        if not url:
            error += '\nNo Provider Url specified'
        if not api_key:
            error += '\nNo Provider Api key specified'

        if error != '':
            return json.dumps({'success': False, 'message': error})

        if kind == 'newznab':
            temp_provider = NewznabProvider(name, url, api_key)
        elif kind == 'torznab':
            temp_provider = TorznabProvider(name, url, api_key)

        capabilities = temp_provider.get_capabilities()

        return json.dumps(capabilities._asdict())
Example #6
0
    def _get_categories(self, sub_type, data):
        """
        Retrieve a list of possible categories with category ids.

        Using the default url/api?cat
        http://yournewznaburl.com/api?t=caps&apikey=yourapikey
        """
        if not data.get('name'):
            return self._bad_request('No provider name provided')

        if not data.get('url'):
            return self._bad_request('No provider url provided')

        if not data.get('apikey'):
            return self._bad_request('No provider api key provided')

        if sub_type == 'newznab':
            provider = NewznabProvider(data.get('name'), data.get('url'),
                                       data.get('apikey'))
        elif sub_type == 'torznab':
            provider = TorznabProvider(data.get('name'), data.get('url'),
                                       data.get('apikey'))

        capabilities = provider.get_capabilities()
        return self._created(data={'result': capabilities._asdict()})
Example #7
0
    def canAddNewznabProvider(name):
        """
        See if a Newznab provider can be added
        """

        if not name:
            return json.dumps({'error': 'No Provider Name specified'})

        provider_dict = dict(zip([x.get_id() for x in app.newznabProviderList], app.newznabProviderList))

        temp_provider = NewznabProvider(name, '')

        if temp_provider.get_id() in provider_dict:
            return json.dumps({'error': 'Provider Name already exists as {name}'.format(name=provider_dict[temp_provider.get_id()].name)})
        else:
            return json.dumps({'success': temp_provider.get_id()})
Example #8
0
    def _save_newznab_providers(providers_settings):
        providers = []
        settings = providers_settings.split('!!!')
        providers_dict = dict(
            list(
                zip([x.get_id() for x in app.newznabProviderList],
                    app.newznabProviderList)))

        for provider_settings in settings:
            if not provider_settings:
                continue

            name, url, api_key, categories = provider_settings.split('|')
            url = config.clean_url(url)
            categories = split_and_strip(categories)

            new_provider = NewznabProvider(name,
                                           url=url,
                                           api_key=api_key,
                                           cat_ids=categories)
            provider_id = new_provider.get_id()

            # if it already exists then update it
            if provider_id in providers_dict:
                providers_dict[provider_id].name = name
                providers_dict[provider_id].url = url
                providers_dict[provider_id].api_key = api_key
                providers_dict[provider_id].cat_ids = categories
                # a 0 in the key spot indicates that no key is needed
                if api_key == '0':
                    providers_dict[provider_id].needs_auth = False
                else:
                    providers_dict[provider_id].needs_auth = True
            else:
                app.newznabProviderList.append(new_provider)

            providers.append(provider_id)

        # delete anything that is missing
        for provider in app.newznabProviderList:
            if provider.get_id() not in providers:
                app.newznabProviderList.remove(provider)

        # Update the custom newznab provider list
        NewznabProvider.save_newznab_providers()
Example #9
0
    def _add_newznab_provider(self, data):
        if not data.get('name'):
            return self._bad_request('No provider name provided')

        if not data.get('url'):
            return self._bad_request('No provider url provided')

        if not data.get('apikey'):
            return self._bad_request('No provider api key provided')

        new_provider = NewznabProvider(data.get('name'),
                                       data.get('url'),
                                       api_key=data.get('apikey'))
        new_provider = self.provider_name_auto_numbered(new_provider)

        app.newznabProviderList.append(new_provider)
        NewznabProvider.save_newznab_providers()
        app.instance.save_config()
        return self._created(data={'result': new_provider.to_json()})
Example #10
0
    def _add_prowlarr_provider(self, data):
        if not data.get('subType'):
            return self._bad_request(
                "Missing subtype ('newznab' or 'torznab')")

        if data.get('subType') not in ('newznab', 'torznab'):
            return self._bad_request('Subtype needs to be newznab or torznab')

        if not data.get('id'):
            return self._bad_request('No provider id provided')

        if not data.get('name'):
            return self._bad_request('No provider name provided')

        if not app.PROWLARR_URL or not app.PROWLARR_APIKEY:
            return self._bad_request(
                'Missing prowlarr url and/or api. Cannot build the url.')

        provider_url = f"{urljoin(app.PROWLARR_URL, str(data.get('id')))}/api"

        provider_class = None
        if data.get('subType') == 'torznab':
            provider_class = TorznabProvider
        else:
            provider_class = NewznabProvider

        new_provider = provider_class(data.get('name'),
                                      provider_url,
                                      api_key=app.PROWLARR_APIKEY,
                                      manager=GenericProvider.PROWLARR)
        new_provider = self.provider_name_auto_numbered(new_provider)

        if data.get('subType') == 'torznab':
            app.torznab_providers_list.append(new_provider)
            app.TORZNAB_PROVIDERS = [
                provider.name for provider in app.torznab_providers_list
            ]
        else:
            app.newznabProviderList.append(new_provider)
            NewznabProvider.save_newznab_providers()

        app.instance.save_config()
        return self._created(data={'result': new_provider.to_json()})
Example #11
0
    def _save_newznab_providers(providers_settings):
        providers = []
        settings = providers_settings.split('!!!')
        providers_dict = dict(
            list(zip([x.get_id() for x in app.newznabProviderList], app.newznabProviderList)))

        for provider_settings in settings:
            if not provider_settings:
                continue

            name, url, api_key, categories = provider_settings.split('|')
            url = config.clean_url(url)
            categories = split_and_strip(categories)

            new_provider = NewznabProvider(name, url=url, api_key=api_key, cat_ids=categories)
            provider_id = new_provider.get_id()

            # if it already exists then update it
            if provider_id in providers_dict:
                providers_dict[provider_id].name = name
                providers_dict[provider_id].url = url
                providers_dict[provider_id].api_key = api_key
                providers_dict[provider_id].cat_ids = categories
                # a 0 in the key spot indicates that no key is needed
                if api_key == '0':
                    providers_dict[provider_id].needs_auth = False
                else:
                    providers_dict[provider_id].needs_auth = True
            else:
                app.newznabProviderList.append(new_provider)

            providers.append(provider_id)

        # delete anything that is missing
        for provider in app.newznabProviderList:
            if provider.get_id() not in providers:
                app.newznabProviderList.remove(provider)

        # Update the custom newznab provider list
        NewznabProvider.save_newznab_providers()
Example #12
0
    def canAddProvider(kind, name, url, api_key=None):
        """See if a Newznab or Torznab provider can be added."""
        if not name:
            return json.dumps({'error': 'No Provider Name specified'})

        found_chars = [c for c in INVALID_CHARS if c in name]
        if found_chars:
            return json.dumps({
                'error':
                'Invalid character in provider name: {0}'.format(
                    ', '.join(found_chars))
            })

        if kind == 'newznab':
            provider_dict = dict(
                list(
                    zip([x.get_id() for x in app.newznabProviderList],
                        app.newznabProviderList)))
            temp_provider = NewznabProvider(name, url)
        elif kind == 'torznab':
            provider_dict = dict(
                list(
                    zip([x.get_id() for x in app.torznab_providers_list],
                        app.torznab_providers_list)))
            temp_provider = TorznabProvider(name, url, api_key)

        if temp_provider.get_id() in provider_dict:
            return json.dumps({
                'error':
                'Provider name already exists as {name}'.format(
                    name=provider_dict[temp_provider.get_id()].name)
            })
        else:
            return json.dumps({'success': temp_provider.get_id()})
Example #13
0
    def canAddProvider(kind, name, url, api_key=None):
        """See if a Newznab or Torznab provider can be added."""
        if not name:
            return json.dumps({'error': 'No Provider Name specified'})

        found_chars = [c for c in INVALID_CHARS if c in name]
        if found_chars:
            return json.dumps({'error': 'Invalid character in provider name: {0}'.format(', '.join(found_chars))})

        if kind == 'newznab':
            provider_dict = dict(list(zip([x.get_id() for x in app.newznabProviderList],
                                          app.newznabProviderList)))
            temp_provider = NewznabProvider(name, url)
        elif kind == 'torznab':
            provider_dict = dict(list(zip([x.get_id() for x in app.torznab_providers_list],
                                          app.torznab_providers_list)))
            temp_provider = TorznabProvider(name, url, api_key)

        if temp_provider.get_id() in provider_dict:
            return json.dumps({'error': 'Provider name already exists as {name}'.format(
                              name=provider_dict[temp_provider.get_id()].name)})
        else:
            return json.dumps({'success': temp_provider.get_id()})
Example #14
0
    def canAddProvider(kind, name, url, api_key=None):
        """See if a Newznab or Torznab provider can be added."""
        if not name:
            return json.dumps({'error': 'No Provider Name specified'})

        provider_dict = dict(zip([x.get_id() for x in app.newznabProviderList], app.newznabProviderList))

        if kind == 'newznab':
            temp_provider = NewznabProvider(name, url)
        elif kind == 'torznab':
            temp_provider = TorznabProvider(name, url, api_key)

        if temp_provider.get_id() in provider_dict:
            return json.dumps({'error': 'Provider name already exists as {name}'.format(
                              name=provider_dict[temp_provider.get_id()].name)})
        else:
            return json.dumps({'success': temp_provider.get_id()})
Example #15
0
    def _migrate_v10(self):
        """
        Convert all csv stored items as 'real' lists. ConfigObj provides a way for storing lists. These are saved
        as comma separated values, using this the format documented here:
        http://configobj.readthedocs.io/en/latest/configobj.html?highlight=lists#list-values
        """

        def get_providers_from_data(providers_string):
            """Split the provider string into providers, and get the provider names."""
            return [provider.split('|')[0].upper() for provider in providers_string.split('!!!') if provider]

        def make_id(name):
            """Make ID of the provider."""
            if not name:
                return ''

            return re.sub(r'[^\w\d_]', '_', str(name).strip().upper())

        def get_rss_torrent_providers_list(data):
            """Get RSS torrent provider list."""
            providers_list = [_ for _ in (make_rss_torrent_provider(_) for _ in data.split('!!!')) if _]
            seen_values = set()
            providers_set = []

            for provider in providers_list:
                value = provider.name

                if value not in seen_values:
                    providers_set.append(provider)
                    seen_values.add(value)

            return [_ for _ in providers_set if _]

        def make_rss_torrent_provider(config):
            """Create new RSS provider."""
            if not config:
                return None

            cookies = ''
            enable_backlog = 0
            enable_daily = 0
            enable_manualsearch = 0
            search_fallback = 0
            search_mode = 'eponly'
            title_tag = 'title'

            try:
                values = config.split('|')

                if len(values) == 9:
                    name, url, cookies, title_tag, enabled, search_mode, search_fallback, enable_daily, enable_backlog = values
                elif len(values) == 10:
                    name, url, cookies, title_tag, enabled, search_mode, search_fallback, enable_daily, enable_backlog, enable_manualsearch = values
                elif len(values) == 8:
                    name, url, cookies, enabled, search_mode, search_fallback, enable_daily, enable_backlog = values
                else:
                    enabled = values[4]
                    name = values[0]
                    url = values[1]
            except ValueError:
                log.error('Skipping RSS Torrent provider string: {config}, incorrect format', {'config': config})
                return None

            new_provider = TorrentRssProvider(
                name, url, cookies=cookies, title_tag=title_tag, search_mode=search_mode,
                search_fallback=search_fallback,
                enable_daily=enable_daily, enable_backlog=enable_backlog, enable_manualsearch=enable_manualsearch
            )
            new_provider.enabled = enabled == '1'

            return new_provider

        # General
        app.GIT_RESET_BRANCHES = convert_csv_string_to_list(self.config_obj['General']['git_reset_branches'])
        app.ALLOWED_EXTENSIONS = convert_csv_string_to_list(self.config_obj['General']['allowed_extensions'])
        app.PROVIDER_ORDER = convert_csv_string_to_list(self.config_obj['General']['provider_order'], ' ')
        app.ROOT_DIRS = convert_csv_string_to_list(self.config_obj['General']['root_dirs'], '|')
        app.SYNC_FILES = convert_csv_string_to_list(self.config_obj['General']['sync_files'])
        app.IGNORE_WORDS = convert_csv_string_to_list(self.config_obj['General']['ignore_words'])
        app.PREFERRED_WORDS = convert_csv_string_to_list(self.config_obj['General']['preferred_words'])
        app.UNDESIRED_WORDS = convert_csv_string_to_list(self.config_obj['General']['undesired_words'])
        app.TRACKERS_LIST = convert_csv_string_to_list(self.config_obj['General']['trackers_list'])
        app.REQUIRE_WORDS = convert_csv_string_to_list(self.config_obj['General']['require_words'])
        app.IGNORED_SUBS_LIST = convert_csv_string_to_list(self.config_obj['General']['ignored_subs_list'])
        app.BROKEN_PROVIDERS = convert_csv_string_to_list(self.config_obj['General']['broken_providers'])
        app.EXTRA_SCRIPTS = convert_csv_string_to_list(self.config_obj['General']['extra_scripts'], '|')

        # Metadata
        app.METADATA_KODI = convert_csv_string_to_list(self.config_obj['General']['metadata_kodi'], '|')
        app.METADATA_KODI_12PLUS = convert_csv_string_to_list(self.config_obj['General']['metadata_kodi_12plus'], '|')
        app.METADATA_MEDIABROWSER = convert_csv_string_to_list(self.config_obj['General']['metadata_mediabrowser'], '|')
        app.METADATA_PS3 = convert_csv_string_to_list(self.config_obj['General']['metadata_ps3'], '|')
        app.METADATA_WDTV = convert_csv_string_to_list(self.config_obj['General']['metadata_wdtv'], '|')
        app.METADATA_TIVO = convert_csv_string_to_list(self.config_obj['General']['metadata_tivo'], '|')
        app.METADATA_MEDE8ER = convert_csv_string_to_list(self.config_obj['General']['metadata_mede8er'], '|')

        # Subtitles
        app.SUBTITLES_LANGUAGES = convert_csv_string_to_list(self.config_obj['Subtitles']['subtitles_languages'])
        app.SUBTITLES_SERVICES_LIST = convert_csv_string_to_list(self.config_obj['Subtitles']['SUBTITLES_SERVICES_LIST'])
        app.SUBTITLES_SERVICES_ENABLED = convert_csv_string_to_list(self.config_obj['Subtitles']['SUBTITLES_SERVICES_ENABLED'], '|')
        app.SUBTITLES_EXTRA_SCRIPTS = convert_csv_string_to_list(self.config_obj['Subtitles']['subtitles_extra_scripts'], '|')
        app.SUBTITLES_PRE_SCRIPTS = convert_csv_string_to_list(self.config_obj['Subtitles']['subtitles_pre_scripts'], '|')

        # Notifications
        app.KODI_HOST = convert_csv_string_to_list(self.config_obj['KODI']['kodi_host'])
        app.PLEX_SERVER_HOST = convert_csv_string_to_list(self.config_obj['Plex']['plex_server_host'])
        app.PLEX_CLIENT_HOST = convert_csv_string_to_list(self.config_obj['Plex']['plex_client_host'])
        app.PROWL_API = convert_csv_string_to_list(self.config_obj['Prowl']['prowl_api'])
        app.PUSHOVER_DEVICE = convert_csv_string_to_list(self.config_obj['Pushover']['pushover_device'])
        app.EMAIL_LIST = convert_csv_string_to_list(self.config_obj['Email']['email_list'])

        try:
            # migrate rsstorrent providers
            from medusa.providers.torrent.rss.rsstorrent import TorrentRssProvider

            # Create the new list of torrent rss providers, with only the id stored.
            app.TORRENTRSS_PROVIDERS = get_providers_from_data(self.config_obj['TorrentRss']['torrentrss_data'])

            # Create the torrent providers from the old rsstorrent piped separated data.
            app.torrentRssProviderList = get_rss_torrent_providers_list(self.config_obj['TorrentRss']['torrentrss_data'])
        except KeyError:
            app.TORRENTRSS_PROVIDERS = []

        try:
            # migrate newznab providers.
            # Newznabprovider needs to be imported lazy, as the module will also import other providers to early.
            from medusa.providers.nzb.newznab import NewznabProvider

            # Create the newznab providers from the old newznab piped separated data.
            app.newznabProviderList = NewznabProvider.get_providers_list(
                self.config_obj['Newznab']['newznab_data']
            )

            app.NEWZNAB_PROVIDERS = [make_id(provider.name) for provider in app.newznabProviderList if not provider.default]
        except KeyError:
            app.NEWZNAB_PROVIDERS = []
Example #16
0
#  app globals
# =================
app.SYS_ENCODING = 'UTF-8'

app.showList = []
app.QUALITY_DEFAULT = 4  # hdtv
app.FLATTEN_FOLDERS_DEFAULT = 0

app.NAMING_PATTERN = ''
app.NAMING_ABD_PATTERN = ''
app.NAMING_SPORTS_PATTERN = ''
app.NAMING_MULTI_EP = 1

app.PROVIDER_ORDER = ["app_index"]
app.newznabProviderList = NewznabProvider.get_providers_list(
    "'Application Index|http://lolo.medusa.com/|0|5030,5040|0|eponly|0|0|0!!!NZBs.org|"
    "https://nzbs.org/||5030,5040,5060,5070,5090|0|eponly|0|0|0!!!Usenet-Crawler|"
    "https://www.usenet-crawler.com/||5030,5040,5060|0|eponly|0|0|0'")
app.providerList = providers.make_provider_list()

app.PROG_DIR = os.path.abspath(os.path.join(TEST_DIR, '..'))
app.DATA_DIR = TEST_DIR
app.CONFIG_FILE = os.path.join(app.DATA_DIR, "config.ini")
app.CFG = ConfigObj(app.CONFIG_FILE)

app.BRANCH = config.check_setting_str(app.CFG, 'General', 'branch', '')
app.CUR_COMMIT_HASH = config.check_setting_str(app.CFG, 'General',
                                               'cur_commit_hash', '')
app.GIT_USERNAME = config.check_setting_str(app.CFG, 'General', 'git_username',
                                            '')
app.GIT_PASSWORD = config.check_setting_str(app.CFG,
                                            'General',
Example #17
0
app.SYS_ENCODING = 'UTF-8'

app.showList = []
app.QUALITY_DEFAULT = 4  # hdtv
app.SEASON_FOLDERS_DEFAULT = 1

app.NAMING_PATTERN = ''
app.NAMING_ABD_PATTERN = ''
app.NAMING_SPORTS_PATTERN = ''
app.NAMING_MULTI_EP = 1


app.PROVIDER_ORDER = ["app_index"]
app.newznabProviderList = NewznabProvider.get_providers_list(
    'Application Index|http://lolo.medusa.com/|0|5030,5040|0|eponly|0|0|0|0!!!'
    'NZBs.org|https://nzbs.org/||5030,5040,5060,5070,5090|0|eponly|0|0|0|0!!!'
    'Usenet-Crawler|https://api.usenet-crawler.com/||5030,5040,5060|0|eponly|0|0|0|0'
)
app.providerList = providers.make_provider_list()

app.PROG_DIR = os.path.abspath(os.path.join(TEST_DIR, '..'))
app.DATA_DIR = TEST_DIR
app.CONFIG_FILE = os.path.join(app.DATA_DIR, "config.ini")
app.CFG = ConfigObj(app.CONFIG_FILE)

app.BRANCH = config.check_setting_str(app.CFG, 'General', 'branch', '')
app.CUR_COMMIT_HASH = config.check_setting_str(app.CFG, 'General', 'cur_commit_hash', '')
app.GIT_USERNAME = config.check_setting_str(app.CFG, 'General', 'git_username', '')
app.GIT_PASSWORD = config.check_setting_str(app.CFG, 'General', 'git_password', '', censor_log='low')

app.LOG_DIR = os.path.join(TEST_DIR, 'Logs')
Example #18
0
    def saveProviders(self,
                      newznab_string='',
                      torrentrss_string='',
                      provider_order=None,
                      **kwargs):
        """
        Save Provider related settings
        """
        results = []

        provider_str_list = provider_order.split()
        provider_list = []

        newznab_provider_dict = dict(
            zip([x.get_id() for x in app.newznabProviderList],
                app.newznabProviderList))

        finished_names = []

        # add all the newznab info we got into our list
        if newznab_string:
            for curNewznabProviderStr in newznab_string.split('!!!'):

                if not curNewznabProviderStr:
                    continue

                cur_name, cur_url, cur_key, cur_cat = curNewznabProviderStr.split(
                    '|')
                cur_url = config.clean_url(cur_url)

                new_provider = NewznabProvider(cur_name,
                                               cur_url,
                                               api_key=cur_key,
                                               cat_ids=cur_cat)

                cur_id = new_provider.get_id()

                # if it already exists then update it
                if cur_id in newznab_provider_dict:
                    newznab_provider_dict[cur_id].name = cur_name
                    newznab_provider_dict[cur_id].url = cur_url
                    newznab_provider_dict[cur_id].api_key = cur_key
                    newznab_provider_dict[cur_id].cat_ids = split_and_strip(
                        cur_cat)
                    # a 0 in the key spot indicates that no key is needed
                    if cur_key == '0':
                        newznab_provider_dict[cur_id].needs_auth = False
                    else:
                        newznab_provider_dict[cur_id].needs_auth = True

                    try:
                        newznab_provider_dict[cur_id].search_mode = str(
                            kwargs['{id}_search_mode'.format(
                                id=cur_id)]).strip()
                    except (AttributeError, KeyError):
                        pass  # these exceptions are actually catching unselected checkboxes

                    try:
                        newznab_provider_dict[
                            cur_id].search_fallback = config.checkbox_to_value(
                                kwargs['{id}_search_fallback'.format(
                                    id=cur_id)])
                    except (AttributeError, KeyError):
                        newznab_provider_dict[
                            cur_id].search_fallback = 0  # these exceptions are actually catching unselected checkboxes

                    try:
                        newznab_provider_dict[
                            cur_id].enable_daily = config.checkbox_to_value(
                                kwargs['{id}_enable_daily'.format(id=cur_id)])
                    except (AttributeError, KeyError):
                        newznab_provider_dict[
                            cur_id].enable_daily = 0  # these exceptions are actually catching unselected checkboxes

                    try:
                        newznab_provider_dict[
                            cur_id].enable_manualsearch = config.checkbox_to_value(
                                kwargs['{id}_enable_manualsearch'.format(
                                    id=cur_id)])
                    except (AttributeError, KeyError):
                        newznab_provider_dict[
                            cur_id].enable_manualsearch = 0  # these exceptions are actually catching unselected checkboxes

                    try:
                        newznab_provider_dict[
                            cur_id].enable_backlog = config.checkbox_to_value(
                                kwargs['{id}_enable_backlog'.format(
                                    id=cur_id)])
                    except (AttributeError, KeyError):
                        newznab_provider_dict[
                            cur_id].enable_backlog = 0  # these exceptions are actually catching unselected checkboxes

                else:
                    app.newznabProviderList.append(new_provider)

                finished_names.append(cur_id)

        # delete anything that is missing
        for cur_provider in app.newznabProviderList:
            if cur_provider.get_id() not in finished_names:
                app.newznabProviderList.remove(cur_provider)

        # Update the custom newznab provider list
        NewznabProvider.save_newnab_providers()

        torrent_rss_provider_dict = dict(
            zip([x.get_id() for x in app.torrentRssProviderList],
                app.torrentRssProviderList))
        finished_names = []

        if torrentrss_string:
            for curTorrentRssProviderStr in torrentrss_string.split('!!!'):

                if not curTorrentRssProviderStr:
                    continue

                cur_name, cur_url, cur_cookies, cur_title_tag = curTorrentRssProviderStr.split(
                    '|')
                cur_url = config.clean_url(cur_url)

                new_provider = TorrentRssProvider(cur_name, cur_url,
                                                  cur_cookies, cur_title_tag)

                cur_id = new_provider.get_id()

                # if it already exists then update it
                if cur_id in torrent_rss_provider_dict:
                    torrent_rss_provider_dict[cur_id].name = cur_name
                    torrent_rss_provider_dict[cur_id].url = cur_url
                    torrent_rss_provider_dict[cur_id].cookies = cur_cookies
                    torrent_rss_provider_dict[
                        cur_id].curTitleTAG = cur_title_tag
                else:
                    app.torrentRssProviderList.append(new_provider)

                finished_names.append(cur_id)

        # delete anything that is missing
        for cur_provider in app.torrentRssProviderList:
            if cur_provider.get_id() not in finished_names:
                app.torrentRssProviderList.remove(cur_provider)

        # Update the torrentrss provider list
        app.TORRENTRSS_PROVIDERS = [
            provider.name for provider in app.torrentRssProviderList
        ]

        disabled_list = []
        # do the enable/disable
        for cur_providerStr in provider_str_list:
            cur_provider, cur_enabled = cur_providerStr.split(':')
            cur_enabled = try_int(cur_enabled)

            cur_prov_obj = [
                x for x in providers.sorted_provider_list()
                if x.get_id() == cur_provider and hasattr(x, 'enabled')
            ]
            if cur_prov_obj:
                cur_prov_obj[0].enabled = bool(cur_enabled)

            if cur_enabled:
                provider_list.append(cur_provider)
            else:
                disabled_list.append(cur_provider)

            if cur_provider in newznab_provider_dict:
                newznab_provider_dict[cur_provider].enabled = bool(cur_enabled)
            elif cur_provider in torrent_rss_provider_dict:
                torrent_rss_provider_dict[cur_provider].enabled = bool(
                    cur_enabled)

        provider_list.extend(disabled_list)

        # dynamically load provider settings
        for cur_torrent_provider in [
                prov for prov in providers.sorted_provider_list()
                if prov.provider_type == GenericProvider.TORRENT
        ]:

            if hasattr(cur_torrent_provider, 'custom_url'):
                try:
                    cur_torrent_provider.custom_url = str(
                        kwargs['{id}_custom_url'.format(
                            id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_torrent_provider.custom_url = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'minseed'):
                try:
                    cur_torrent_provider.minseed = int(
                        str(kwargs['{id}_minseed'.format(
                            id=cur_torrent_provider.get_id())]).strip())
                except (AttributeError, KeyError):
                    cur_torrent_provider.minseed = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'minleech'):
                try:
                    cur_torrent_provider.minleech = int(
                        str(kwargs['{id}_minleech'.format(
                            id=cur_torrent_provider.get_id())]).strip())
                except (AttributeError, KeyError):
                    cur_torrent_provider.minleech = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'ratio'):
                try:
                    ratio = float(
                        str(kwargs['{id}_ratio'.format(
                            id=cur_torrent_provider.get_id())]).strip())
                    cur_torrent_provider.ratio = (ratio, -1)[ratio < 0]
                except (AttributeError, KeyError, ValueError):
                    cur_torrent_provider.ratio = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'digest'):
                try:
                    cur_torrent_provider.digest = str(
                        kwargs['{id}_digest'.format(
                            id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_torrent_provider.digest = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'hash'):
                try:
                    cur_torrent_provider.hash = str(kwargs['{id}_hash'.format(
                        id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_torrent_provider.hash = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'api_key'):
                try:
                    cur_torrent_provider.api_key = str(
                        kwargs['{id}_api_key'.format(
                            id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_torrent_provider.api_key = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'username'):
                try:
                    cur_torrent_provider.username = str(
                        kwargs['{id}_username'.format(
                            id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_torrent_provider.username = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'password'):
                try:
                    cur_torrent_provider.password = str(
                        kwargs['{id}_password'.format(
                            id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_torrent_provider.password = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'passkey'):
                try:
                    cur_torrent_provider.passkey = str(
                        kwargs['{id}_passkey'.format(
                            id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_torrent_provider.passkey = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'pin'):
                try:
                    cur_torrent_provider.pin = str(kwargs['{id}_pin'.format(
                        id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_torrent_provider.pin = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'confirmed'):
                try:
                    cur_torrent_provider.confirmed = config.checkbox_to_value(
                        kwargs['{id}_confirmed'.format(
                            id=cur_torrent_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_torrent_provider.confirmed = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'ranked'):
                try:
                    cur_torrent_provider.ranked = config.checkbox_to_value(
                        kwargs['{id}_ranked'.format(
                            id=cur_torrent_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_torrent_provider.ranked = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'engrelease'):
                try:
                    cur_torrent_provider.engrelease = config.checkbox_to_value(
                        kwargs['{id}_engrelease'.format(
                            id=cur_torrent_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_torrent_provider.engrelease = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'onlyspasearch'):
                try:
                    cur_torrent_provider.onlyspasearch = config.checkbox_to_value(
                        kwargs['{id}_onlyspasearch'.format(
                            id=cur_torrent_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_torrent_provider.onlyspasearch = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'sorting'):
                try:
                    cur_torrent_provider.sorting = str(
                        kwargs['{id}_sorting'.format(
                            id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_torrent_provider.sorting = 'seeders'  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'freeleech'):
                try:
                    cur_torrent_provider.freeleech = config.checkbox_to_value(
                        kwargs['{id}_freeleech'.format(
                            id=cur_torrent_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_torrent_provider.freeleech = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'search_mode'):
                try:
                    cur_torrent_provider.search_mode = str(
                        kwargs['{id}_search_mode'.format(
                            id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_torrent_provider.search_mode = 'eponly'  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'search_fallback'):
                try:
                    cur_torrent_provider.search_fallback = config.checkbox_to_value(
                        kwargs['{id}_search_fallback'.format(
                            id=cur_torrent_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_torrent_provider.search_fallback = 0  # these exceptions are catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'enable_daily'):
                try:
                    cur_torrent_provider.enable_daily = config.checkbox_to_value(
                        kwargs['{id}_enable_daily'.format(
                            id=cur_torrent_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_torrent_provider.enable_daily = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'enable_manualsearch'):
                try:
                    cur_torrent_provider.enable_manualsearch = config.checkbox_to_value(
                        kwargs['{id}_enable_manualsearch'.format(
                            id=cur_torrent_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_torrent_provider.enable_manualsearch = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'enable_backlog'):
                try:
                    cur_torrent_provider.enable_backlog = config.checkbox_to_value(
                        kwargs['{id}_enable_backlog'.format(
                            id=cur_torrent_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_torrent_provider.enable_backlog = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'cat'):
                try:
                    cur_torrent_provider.cat = int(
                        str(kwargs['{id}_cat'.format(
                            id=cur_torrent_provider.get_id())]).strip())
                except (AttributeError, KeyError):
                    cur_torrent_provider.cat = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_torrent_provider, 'subtitle'):
                try:
                    cur_torrent_provider.subtitle = config.checkbox_to_value(
                        kwargs['{id}_subtitle'.format(
                            id=cur_torrent_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_torrent_provider.subtitle = 0  # these exceptions are actually catching unselected checkboxes

            if cur_torrent_provider.enable_cookies:
                try:
                    cur_torrent_provider.cookies = str(
                        kwargs['{id}_cookies'.format(
                            id=cur_torrent_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    pass  # I don't want to configure a default value here, as it can also be configured intially as a custom rss torrent provider

        for cur_nzb_provider in [
                prov for prov in providers.sorted_provider_list()
                if prov.provider_type == GenericProvider.NZB
        ]:

            # We don't want to overwrite the api key, as that's not available in the second tab for newznab providers.
            if hasattr(cur_nzb_provider, 'api_key') and not isinstance(
                    cur_nzb_provider, NewznabProvider):
                try:
                    cur_nzb_provider.api_key = str(
                        kwargs['{id}_api_key'.format(
                            id=cur_nzb_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_nzb_provider.api_key = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_nzb_provider, 'username'):
                try:
                    cur_nzb_provider.username = str(
                        kwargs['{id}_username'.format(
                            id=cur_nzb_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_nzb_provider.username = None  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_nzb_provider, 'search_mode'):
                try:
                    cur_nzb_provider.search_mode = str(
                        kwargs['{id}_search_mode'.format(
                            id=cur_nzb_provider.get_id())]).strip()
                except (AttributeError, KeyError):
                    cur_nzb_provider.search_mode = 'eponly'  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_nzb_provider, 'search_fallback'):
                try:
                    cur_nzb_provider.search_fallback = config.checkbox_to_value(
                        kwargs['{id}_search_fallback'.format(
                            id=cur_nzb_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_nzb_provider.search_fallback = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_nzb_provider, 'enable_daily'):
                try:
                    cur_nzb_provider.enable_daily = config.checkbox_to_value(
                        kwargs['{id}_enable_daily'.format(
                            id=cur_nzb_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_nzb_provider.enable_daily = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_nzb_provider, 'enable_manualsearch'):
                try:
                    cur_nzb_provider.enable_manualsearch = config.checkbox_to_value(
                        kwargs['{id}_enable_manualsearch'.format(
                            id=cur_nzb_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_nzb_provider.enable_manualsearch = 0  # these exceptions are actually catching unselected checkboxes

            if hasattr(cur_nzb_provider, 'enable_backlog'):
                try:
                    cur_nzb_provider.enable_backlog = config.checkbox_to_value(
                        kwargs['{id}_enable_backlog'.format(
                            id=cur_nzb_provider.get_id())])
                except (AttributeError, KeyError):
                    cur_nzb_provider.enable_backlog = 0  # these exceptions are actually catching unselected checkboxes

        # app.NEWZNAB_DATA = '!!!'.join([x.config_string() for x in app.newznabProviderList])
        app.PROVIDER_ORDER = provider_list

        app.instance.save_config()

        if results:
            for x in results:
                logger.log(x, logger.ERROR)
            ui.notifications.error('Error(s) Saving Configuration',
                                   '<br>\n'.join(results))
        else:
            ui.notifications.message('Configuration Saved',
                                     os.path.join(app.CONFIG_FILE))

        return self.redirect('/config/providers/')