From fcc663af91292d6174f270b7b44b0c87ca6b5f9a Mon Sep 17 00:00:00 2001 From: Halali Date: Thu, 27 Dec 2018 20:19:59 +0100 Subject: [PATCH] Continue development for new config backend --- bazarr/check_update.py | 14 +- bazarr/config.py | 107 ++++--- bazarr/get_movies.py | 2 +- bazarr/get_series.py | 2 +- bazarr/get_subtitle.py | 26 +- bazarr/list_subtitles.py | 4 +- bazarr/main.py | 281 ++++++++---------- bazarr/scheduler.py | 10 +- bazarr/update_db.py | 8 +- libs/backports/__init__.py | 12 +- .../__init__.py | 216 ++++++-------- .../helpers.py | 0 libs/configparser.py | 14 +- libs/simpleconfigparser/__init__.py | 131 ++++++++ libs/version.txt | 3 +- views/episodes.tpl | 4 +- views/history.tpl | 4 +- views/menu.tpl | 10 +- views/movie.tpl | 4 +- views/settings.tpl | 38 +-- views/system.tpl | 4 +- views/wanted.tpl | 6 +- views/wantedmovies.tpl | 2 +- views/wantedseries.tpl | 2 +- views/wizard.tpl | 6 +- 25 files changed, 493 insertions(+), 417 deletions(-) rename libs/backports/{configparser => configparser2}/__init__.py (90%) rename libs/backports/{configparser => configparser2}/helpers.py (100%) create mode 100644 libs/simpleconfigparser/__init__.py diff --git a/bazarr/check_update.py b/bazarr/check_update.py index 96afedf96..dea32a3e4 100644 --- a/bazarr/check_update.py +++ b/bazarr/check_update.py @@ -1,22 +1,19 @@ -from get_argv import config_dir - -from get_settings import get_general_settings - +# coding=utf-8 import os import logging import sqlite3 import json import requests +from get_argv import config_dir, no_update +from config import settings + if not no_update: import git -from get_argv import config_dir - -from config import settings - current_working_directory = os.path.dirname(os.path.dirname(__file__)) + def gitconfig(): g = git.Repo.init(current_working_directory) config_read = g.config_reader() @@ -34,6 +31,7 @@ def gitconfig(): logging.debug('BAZARR Settings git email') config_write.set_value("user", "email", "bazarr@fake.email") + def check_and_apply_update(): gitconfig() check_releases() diff --git a/bazarr/config.py b/bazarr/config.py index 33719cd18..d13829455 100644 --- a/bazarr/config.py +++ b/bazarr/config.py @@ -1,53 +1,76 @@ # coding=utf-8 import os -import configparser - -from get_argv import config_dir - - -class _ConfigSection(object): - """Hold settings for a section of the configuration file.""" - - def __getattr__(self, attr): - if attr in self.__dict__: - return self.__dict__[attr] - else: - raise AttributeError('No "%s" setting in section.' % attr) - - def __getitem__(self, item): - return getattr(self, item) - - def __repr__(self): - return str(tuple(self.__dict__.keys())) +from simpleconfigparser import simpleconfigparser -def read(config_file): - """Read settings from file and return a dot notation object.""" - config = configparser.ConfigParser() - config.read(unicode(config_file)) - - dotini = _ConfigSection() - - for section in config.sections(): - - s = _ConfigSection() - for key, value in config.items(section): - if value == "True": - value = True - elif value == "False": - value = False - setattr(s, key, value) - - setattr(dotini, section, s) - - return dotini +from get_argv import config_dir +defaults = { + 'general': { + 'ip': '0.0.0.0', + 'port': '6767', + 'base_url': '/', + 'path_mappings': '[]', + 'debug': 'False', + 'branch': 'master', + 'auto_update': 'True', + 'single_language': 'False', + 'minimum_score': '90', + 'use_scenename': 'True', + 'use_postprocessing': 'False', + 'postprocessing_cmd': '', + 'use_sonarr': 'False', + 'use_radarr': 'False', + 'path_mappings_movie': '[]', + 'serie_default_enabled': 'False', + 'serie_default_language': '[]', + 'serie_default_hi': 'False', + 'movie_default_enabled': 'False', + 'movie_default_language': [], + 'movie_default_hi': 'False', + 'page_size': '25', + 'minimum_score_movie': '70', + 'use_embedded_subs': 'True', + 'only_monitored': 'False', + 'adaptive_searching': 'False' +}, + 'auth': { + 'type': 'None', + 'username': '', + 'password': '' +}, + 'sonarr': { + 'ip': '127.0.0.1', + 'port': '8989', + 'base_url': '/', + 'ssl': 'False', + 'apikey': '', + 'full_update': 'Daily' +}, + 'radarr': { + 'ip': '127.0.0.1', + 'port': '7878', + 'base_url': '/', + 'ssl': 'False', + 'apikey': '', + 'full_update': 'Daily' +}, + 'proxy': { + 'type': 'None', + 'url': '', + 'port': '', + 'username': '', + 'password': '', + 'exclude': 'localhost,127.0.0.1' +}} + +settings = simpleconfigparser(defaults=defaults) +settings.read(os.path.join(config_dir, 'config', 'config.ini')) -settings = read(os.path.join(os.path.join(config_dir, 'config', 'config.ini'))) base_url = settings.general.base_url # sonarr url -if settings.sonarr.ssl: +if settings.sonarr.getboolean('ssl'): protocol_sonarr = "https" else: protocol_sonarr = "http" @@ -63,7 +86,7 @@ url_sonarr = protocol_sonarr + "://" + settings.sonarr.ip + ":" + settings.sonar url_sonarr_short = protocol_sonarr + "://" + settings.sonarr.ip + ":" + settings.sonarr.port # radarr url -if settings.radarr.ssl: +if settings.radarr.getboolean('ssl'): protocol_radarr = "https" else: protocol_radarr = "http" diff --git a/bazarr/get_movies.py b/bazarr/get_movies.py index e0fa55ee2..f36e5fac1 100644 --- a/bazarr/get_movies.py +++ b/bazarr/get_movies.py @@ -13,7 +13,7 @@ from list_subtitles import store_subtitles_movie, list_missing_subtitles_movies def update_movies(): logging.debug('BAZARR Starting movie sync from Radarr.') apikey_radarr = settings.radarr.apikey - movie_default_enabled = settings.general.movie_default_enabled + movie_default_enabled = settings.general.getboolean('movie_default_enabled') movie_default_language = settings.general.movie_default_language movie_default_hi = settings.general.movie_default_hi diff --git a/bazarr/get_series.py b/bazarr/get_series.py index 51941c731..7a68a6205 100644 --- a/bazarr/get_series.py +++ b/bazarr/get_series.py @@ -11,7 +11,7 @@ from list_subtitles import list_missing_subtitles def update_series(): apikey_sonarr = settings.sonarr.apikey - serie_default_enabled = settings.general.serie_default_enabled + serie_default_enabled = settings.general.getboolean('serie_default_enabled') serie_default_language = settings.general.serie_default_language serie_default_hi = settings.general.serie_default_hi diff --git a/bazarr/get_subtitle.py b/bazarr/get_subtitle.py index a36b7a62f..c14e41cb8 100644 --- a/bazarr/get_subtitle.py +++ b/bazarr/get_subtitle.py @@ -42,10 +42,10 @@ def download_subtitle(path, language, hi, providers, providers_auth, sceneName, else: language_set.add(Language(language)) - use_scenename = settings.general.use_scenename + use_scenename = settings.general.getboolean('use_scenename') minimum_score = settings.general.minimum_score minimum_score_movie = settings.general.minimum_score_movie - use_postprocessing = settings.general.use_postprocessing + use_postprocessing = settings.general.getboolean('use_postprocessing') postprocessing_cmd = settings.general.postprocessing_cmd try: @@ -122,7 +122,7 @@ def download_subtitle(path, language, hi, providers, providers_auth, sceneName, calculated_score = round(float(compute_score(subtitle, video, hearing_impaired=hi)) / max_score * 100, 2) if used_sceneName: video = scan_video(path) - single = settings.general.single_language + single = settings.general.getboolean('single_language') if single is True: result = save_subtitles(video, [subtitle], single=True, encoding='utf-8') else: @@ -193,8 +193,8 @@ def manual_search(path, language, hi, providers, providers_auth, sceneName, medi else: language_set.add(Language(lang)) - use_scenename = settings.general.use_scenename - use_postprocessing = settings.general.use_postprocessing + use_scenename = settings.general.getboolean('use_scenename') + use_postprocessing = settings.general.getboolean('use_postprocessing') postprocessing_cmd = settings.general.postprocessing_cmd try: @@ -265,8 +265,8 @@ def manual_download_subtitle(path, language, hi, subtitle, provider, providers_a type_of_score = 360 elif media_type == 'movie': type_of_score = 120 - use_scenename = settings.general.use_scenename - use_postprocessing = settings.general.use_postprocessing + use_scenename = settings.general.getboolean('use_scenename') + use_postprocessing = settings.general.getboolean('use_postprocessing') postprocessing_cmd = settings.general.postprocessing_cmd language = alpha3_from_alpha2(language) @@ -293,7 +293,7 @@ def manual_download_subtitle(path, language, hi, subtitle, provider, providers_a logging.exception('BAZARR Error downloading subtitles for this file ' + path) return None else: - single = settings.general.single_language + single = settings.general.getboolean('single_language') try: score = round(float(compute_score(subtitle, video, hearing_impaired=hi)) / type_of_score * 100, 2) if used_sceneName == True: @@ -350,7 +350,7 @@ def manual_download_subtitle(path, language, hi, subtitle, provider, providers_a def series_download_subtitles(no): - if settings.general.only_monitored: + if settings.general.getboolean('only_monitored'): monitored_only_query_string = ' AND monitored = "True"' else: monitored_only_query_string = "" @@ -482,7 +482,7 @@ def wanted_search_missing_subtitles(): db.create_function("path_substitution_movie", 1, path_replace_movie) c = db.cursor() - if settings.general.only_monitored: + if settings.general.getboolean('only_monitored'): monitored_only_query_string = ' AND monitored = "True"' else: monitored_only_query_string = "" @@ -495,11 +495,11 @@ def wanted_search_missing_subtitles(): c.close() - if settings.general.use_sonarr: + if settings.general.getboolean('use_sonarr'): for episode in episodes: wanted_download_subtitles(episode[0]) - if settings.general.use_radarr: + if settings.general.getboolean('use_radarr'): for movie in movies: wanted_download_subtitles_movie(movie[0]) @@ -507,7 +507,7 @@ def wanted_search_missing_subtitles(): def search_active(timestamp): - if settings.general.only_monitored: + if settings.general.getboolean('only_monitored'): search_deadline = timedelta(weeks=3) search_delta = timedelta(weeks=1) aa = datetime.fromtimestamp(float(timestamp)) diff --git a/bazarr/list_subtitles.py b/bazarr/list_subtitles.py index 51c9129ba..dde52c3c3 100644 --- a/bazarr/list_subtitles.py +++ b/bazarr/list_subtitles.py @@ -175,7 +175,7 @@ def list_missing_subtitles(*no): c_db.close() missing_subtitles_global = [] - use_embedded_subs = settings.general.use_embedded_subs + use_embedded_subs = settings.general.getboolean('use_embedded_subs') for episode_subtitles in episodes_subtitles: actual_subtitles_temp = [] actual_subtitles = [] @@ -222,7 +222,7 @@ def list_missing_subtitles_movies(*no): c_db.close() missing_subtitles_global = [] - use_embedded_subs = settings.general.use_embedded_subs + use_embedded_subs = settings.general.getboolean('use_embedded_subs') for movie_subtitles in movies_subtitles: actual_subtitles_temp = [] actual_subtitles = [] diff --git a/bazarr/main.py b/bazarr/main.py index 1ce703150..ddb368e61 100644 --- a/bazarr/main.py +++ b/bazarr/main.py @@ -23,7 +23,7 @@ import logging from logger import configure_logging, empty_log from config import settings, url_sonarr, url_radarr, url_radarr_short, url_sonarr_short, base_url -configure_logging(settings.general.debug) +configure_logging(settings.general.getboolean('debug')) import requests if settings.proxy.type != 'None': @@ -264,48 +264,38 @@ def save_wizard(): else: settings_general_use_radarr = 'True' - cfg = ConfigParser() - if not cfg.has_section('general'): - cfg.add_section('general') + settings.general.ip = text_type(settings_general_ip) + settings.general.port = text_type(settings_general_port) + settings.general.base_url = text_type(settings_general_baseurl) + settings.general.path_mappings = text_type(settings_general_pathmapping) + settings.general.debug = text_type(settings.general.getboolean('debug')) + settings.general.branch = text_type(settings.general.branch) + settings.general.auto_update = text_type(settings.general.getboolean('auto_update')) + settings.general.single_language = text_type(settings_general_single_language) + settings.general.minimum_score = text_type(settings.general.minimum_score) + settings.general.use_scenename = text_type(text_type(settings.general.getboolean('use_scenename'))) + settings.general.use_postprocessing = text_type(settings.general.getboolean('use_postprocessing')) + settings.general.postprocessing_cmd = text_type(settings.general.postprocessing_cmd) + settings.general.use_sonarr = text_type(settings_general_use_sonarr) + settings.general.use_radarr = text_type(settings_general_use_radarr) + settings.general.path_mappings_movie = text_type(settings_general_pathmapping_movie) + settings.general.page_size = text_type(settings.general.page_size) + settings.general.minimum_score_movie = text_type(settings.general.minimum_score_movie) + settings.general.use_embedded_subs = text_type(settings.general.getboolean('use_embedded_subs')) + settings.general.only_monitored = text_type(settings.general.getboolean('only_monitored')) + settings.general.adaptive_searching = text_type(settings_general_adaptive_searching) - cfg.set('general', 'ip', text_type(settings_general_ip)) - cfg.set('general', 'port', text_type(settings_general_port)) - cfg.set('general', 'base_url', text_type(settings_general_baseurl)) - cfg.set('general', 'path_mappings', text_type(settings_general_pathmapping)) - cfg.set('general', 'debug', text_type(settings.general.debug)) - cfg.set('general', 'branch', text_type(settings.general.branch)) - cfg.set('general', 'auto_update', text_type(settings.general.auto_update)) - cfg.set('general', 'single_language', text_type(settings_general_single_language)) - cfg.set('general', 'minimum_score', text_type(settings.general.minimum_score)) - cfg.set('general', 'use_scenename', text_type(text_type(settings.general.use_scenename))) - cfg.set('general', 'use_postprocessing', text_type(settings.general.use_postprocessing)) - cfg.set('general', 'postprocessing_cmd', text_type(settings.general.postprocessing_cmd)) - cfg.set('general', 'use_sonarr', text_type(settings_general_use_sonarr)) - cfg.set('general', 'use_radarr', text_type(settings_general_use_radarr)) - cfg.set('general', 'path_mappings_movie', text_type(settings_general_pathmapping_movie)) - cfg.set('general', 'page_size', text_type(settings.general.page_size)) - cfg.set('general', 'minimum_score_movie', text_type(settings.general.minimum_score_movie)) - cfg.set('general', 'use_embedded_subs', text_type(settings.general.use_embedded_subs)) - cfg.set('general', 'only_monitored', text_type(settings.general.only_monitored)) - cfg.set('general', 'adaptive_searching', text_type(settings_general_adaptive_searching)) + settings.proxy.type = text_type(settings.proxy.type) + settings.proxy.url = text_type(settings.proxy.url) + settings.proxy.port = text_type(settings.proxy.port) + settings.proxy.username = text_type(settings.proxy.username) + settings.proxy.password = text_type(settings.proxy.password) + settings.proxy.exclude = text_type(settings.proxy.exclude) - if not cfg.has_section('proxy'): - cfg.add_section('proxy') - - cfg.set('proxy', 'type', text_type(settings.proxy.type)) - cfg.set('proxy', 'url', text_type(settings.proxy.url)) - cfg.set('proxy', 'port', text_type(settings.proxy.port)) - cfg.set('proxy', 'username', text_type(settings.proxy.username)) - cfg.set('proxy', 'password', text_type(settings.proxy.password)) - cfg.set('proxy', 'exclude', text_type(settings.proxy.exclude)) - - if not cfg.has_section('auth'): - cfg.add_section('auth') - - cfg.set('auth', 'type', text_type(settings.auth.type)) - cfg.set('auth', 'username', text_type(settings.auth.username)) - cfg.set('auth', 'password', text_type(settings.auth.password)) + settings.auth.type = text_type(settings.auth.type) + settings.auth.username = text_type(settings.auth.username) + settings.auth.password = text_type(settings.auth.password) settings_sonarr_ip = request.forms.get('settings_sonarr_ip') settings_sonarr_port = request.forms.get('settings_sonarr_port') @@ -317,15 +307,12 @@ def save_wizard(): settings_sonarr_ssl = 'True' settings_sonarr_apikey = request.forms.get('settings_sonarr_apikey') - if not cfg.has_section('sonarr'): - cfg.add_section('sonarr') - - cfg.set('sonarr', 'ip', text_type(settings_sonarr_ip)) - cfg.set('sonarr', 'port', text_type(settings_sonarr_port)) - cfg.set('sonarr', 'base_url', text_type(settings_sonarr_baseurl)) - cfg.set('sonarr', 'ssl', text_type(settings_sonarr_ssl)) - cfg.set('sonarr', 'apikey', text_type(settings_sonarr_apikey)) - cfg.set('sonarr', 'full_update', text_type(settings.sonarr.full_update)) + settings.sonarr.ip = text_type(settings_sonarr_ip) + settings.sonarr.port = text_type(settings_sonarr_port) + settings.sonarr.base_url = text_type(settings_sonarr_baseurl) + settings.sonarr.ssl = text_type(settings_sonarr_ssl) + settings.sonarr.apikey = text_type(settings_sonarr_apikey) + settings.sonarr.full_update = text_type(settings.sonarr.full_update) settings_radarr_ip = request.forms.get('settings_radarr_ip') settings_radarr_port = request.forms.get('settings_radarr_port') @@ -337,19 +324,16 @@ def save_wizard(): settings_radarr_ssl = 'True' settings_radarr_apikey = request.forms.get('settings_radarr_apikey') if settings_radarr_apikey != '': - cfg.set('general', 'use_radarr', 'True') + settings.general.use_radarr = 'True' else: - cfg.set('general', 'use_radarr', 'False') + settings.general.use_radarr = 'False' - if not cfg.has_section('radarr'): - cfg.add_section('radarr') - - cfg.set('radarr', 'ip', text_type(settings_radarr_ip)) - cfg.set('radarr', 'port', text_type(settings_radarr_port)) - cfg.set('radarr', 'base_url', text_type(settings_radarr_baseurl)) - cfg.set('radarr', 'ssl', text_type(settings_radarr_ssl)) - cfg.set('radarr', 'apikey', text_type(settings_radarr_apikey)) - cfg.set('radarr', 'full_update', text_type(settings.radarr.full_update)) + settings.radarr.ip = text_type(settings_radarr_ip) + settings.radarr.port = text_type(settings_radarr_port) + settings.radarr.base_url = text_type(settings_radarr_baseurl) + settings.radarr.ssl = text_type(settings_radarr_ssl) + settings.radarr.apikey = text_type(settings_radarr_apikey) + settings.radarr.full_update = text_type(settings.radarr.full_update) settings_subliminal_providers = request.forms.getall('settings_subliminal_providers') c.execute("UPDATE table_settings_providers SET enabled = 0") @@ -366,41 +350,41 @@ def save_wizard(): settings_serie_default_enabled = 'False' else: settings_serie_default_enabled = 'True' - cfg.set('general', 'serie_default_enabled', text_type(settings_serie_default_enabled)) + settings.general.serie_default_enabled = text_type(settings_serie_default_enabled) settings_serie_default_languages = str(request.forms.getall('settings_serie_default_languages')) if settings_serie_default_languages == "['None']": settings_serie_default_languages = 'None' - cfg.set('general', 'serie_default_language', text_type(settings_serie_default_languages)) + settings.general.serie_default_language = text_type(settings_serie_default_languages) settings_serie_default_hi = request.forms.get('settings_serie_default_hi') if settings_serie_default_hi is None: settings_serie_default_hi = 'False' else: settings_serie_default_hi = 'True' - cfg.set('general', 'serie_default_hi', text_type(settings_serie_default_hi)) + settings.general.serie_default_hi = text_type(settings_serie_default_hi) settings_movie_default_enabled = request.forms.get('settings_movie_default_enabled') if settings_movie_default_enabled is None: settings_movie_default_enabled = 'False' else: settings_movie_default_enabled = 'True' - cfg.set('general', 'movie_default_enabled', text_type(settings_movie_default_enabled)) + settings.general.movie_default_enabled = text_type(settings_movie_default_enabled) settings_movie_default_languages = str(request.forms.getall('settings_movie_default_languages')) if settings_movie_default_languages == "['None']": settings_movie_default_languages = 'None' - cfg.set('general', 'movie_default_language', text_type(settings_movie_default_languages)) + settings.general.movie_default_language = text_type(settings_movie_default_languages) settings_movie_default_hi = request.forms.get('settings_movie_default_hi') if settings_movie_default_hi is None: settings_movie_default_hi = 'False' else: settings_movie_default_hi = 'True' - cfg.set('general', 'movie_default_hi', text_type(settings_movie_default_hi)) + settings.general.movie_default_hi = text_type(settings_movie_default_hi) - with open(config_file, 'w+') as f: - cfg.write(f) + with open(os.path.join(config_dir, 'config', 'config.ini'), 'w+') as handle: + settings.write(handle) logging.info('Config file created successfully') @@ -472,11 +456,11 @@ def image_proxy_movies(url): @custom_auth_basic(check_credentials) def redirect_root(): authorize() - if settings.general.use_sonarr is True: + if settings.general.getboolean('use_sonarr'): redirect(base_url + 'series') - elif settings.general.use_radarr is True: + elif settings.general.getboolean('use_radarr'): redirect(base_url + 'movies') - elif os.path.exists(os.path.join(config_dir, 'config/config.ini')) is False: + elif not os.path.exists(os.path.join(config_dir, 'config/config.ini')): redirect(base_url + 'wizard') else: redirect(base_url + 'settings') @@ -486,7 +470,7 @@ def redirect_root(): @custom_auth_basic(check_credentials) def series(): authorize() - single_language = settings.general.single_language + single_language = settings.general.getboolean('single_language') db = sqlite3.connect(os.path.join(config_dir, 'db', 'bazarr.db'), timeout=30) db.create_function("path_substitution", 1, path_replace) @@ -502,7 +486,7 @@ def series(): offset = (int(page) - 1) * page_size max_page = int(math.ceil(missing_count / (page_size + 0.0))) - if settings.general.only_monitored is True: + if settings.general.getboolean('only_monitored'): monitored_only_query_string = ' AND monitored = "True"' else: monitored_only_query_string = "" @@ -527,7 +511,7 @@ def series(): @custom_auth_basic(check_credentials) def serieseditor(): authorize() - single_language = settings.general.single_language + single_language = settings.general.getboolean('single_language') db = sqlite3.connect(os.path.join(config_dir, 'db', 'bazarr.db'), timeout=30) db.create_function("path_substitution", 1, path_replace) @@ -554,14 +538,14 @@ def search_json(query): c = db.cursor() search_list = [] - if settings.general.use_sonarr is True: + if settings.general.getboolean('use_sonarr'): c.execute("SELECT title, sonarrSeriesId FROM table_shows WHERE title LIKE ? ORDER BY title", ('%' + query + '%',)) series = c.fetchall() for serie in series: search_list.append(dict([('name', serie[0]), ('url', base_url + 'episodes/' + str(serie[1]))])) - if settings.general.use_radarr is True: + if settings.general.getboolean('use_radarr'): c.execute("SELECT title, radarrId FROM table_movies WHERE title LIKE ? ORDER BY title", ('%' + query + '%',)) movies = c.fetchall() for movie in movies: @@ -584,7 +568,7 @@ def edit_series(no): else: lang = 'None' - single_language = settings.general.single_language + single_language = settings.general.getboolean('single_language') if single_language is True: if str(lang) == "['None']": lang = 'None' @@ -649,7 +633,7 @@ def edit_serieseditor(): @custom_auth_basic(check_credentials) def episodes(no): authorize() - # single_language = settings.general.single_language + # single_language = settings.general.getboolean('single_language') conn = sqlite3.connect(os.path.join(config_dir, 'db', 'bazarr.db'), timeout=30) conn.create_function("path_substitution", 1, path_replace) @@ -675,7 +659,7 @@ def episodes(no): @custom_auth_basic(check_credentials) def movies(): authorize() - single_language = settings.general.single_language + single_language = settings.general.getboolean('single_language') db = sqlite3.connect(os.path.join(config_dir, 'db', 'bazarr.db'), timeout=30) db.create_function("path_substitution", 1, path_replace_movie) @@ -704,7 +688,7 @@ def movies(): @custom_auth_basic(check_credentials) def movieseditor(): authorize() - single_language = settings.general.single_language + single_language = settings.general.getboolean('single_language') db = sqlite3.connect(os.path.join(config_dir, 'db', 'bazarr.db'), timeout=30) db.create_function("path_substitution", 1, path_replace_movie) @@ -793,7 +777,7 @@ def edit_movie(no): @custom_auth_basic(check_credentials) def movie(no): authorize() - # single_language = settings.general.single_language + # single_language = settings.general.getboolean('single_language') conn = sqlite3.connect(os.path.join(config_dir, 'db', 'bazarr.db'), timeout=30) conn.create_function("path_substitution", 1, path_replace_movie) @@ -952,7 +936,7 @@ def wantedseries(): db.create_function("path_substitution", 1, path_replace) c = db.cursor() - if settings.general.only_monitored is True: + if settings.general.getboolean('only_monitored'): monitored_only_query_string = ' AND monitored = "True"' else: monitored_only_query_string = "" @@ -981,7 +965,7 @@ def wantedmovies(): db.create_function("path_substitution", 1, path_replace_movie) c = db.cursor() - if settings.general.only_monitored is True: + if settings.general.getboolean('only_monitored'): monitored_only_query_string = ' AND monitored = "True"' else: monitored_only_query_string = "" @@ -1109,42 +1093,33 @@ def save_settings(): settings_general_use_radarr = 'True' settings_page_size = request.forms.get('settings_page_size') - before = (unicode(settings.general.ip), int(settings.general.port), unicode(settings.general.base_url), unicode(settings.general.path_mappings), unicode(settings.general.use_sonarr), unicode(settings.general.use_radarr), unicode(settings.general.path_mappings_movie)) + before = (unicode(settings.general.ip), int(settings.general.port), unicode(settings.general.base_url), unicode(settings.general.path_mappings), unicode(settings.general.getboolean('use_sonarr')), unicode(settings.general.getboolean('use_radarr')), unicode(settings.general.path_mappings_movie)) after = (unicode(settings_general_ip), int(settings_general_port), unicode(settings_general_baseurl), unicode(settings_general_pathmapping), unicode(settings_general_use_sonarr), unicode(settings_general_use_radarr), unicode(settings_general_pathmapping_movie)) - from six import text_type - - cfg = ConfigParser() - - with open(config_file, 'r') as f: - cfg.read_file(f) - - cfg.set('general', 'ip', text_type(settings_general_ip)) - cfg.set('general', 'port', text_type(settings_general_port)) - cfg.set('general', 'base_url', text_type(settings_general_baseurl)) - cfg.set('general', 'path_mappings', text_type(settings_general_pathmapping)) - cfg.set('general', 'debug', text_type(settings_general_debug)) - cfg.set('general', 'branch', text_type(settings_general_branch)) - cfg.set('general', 'auto_update', text_type(settings_general_automatic)) - cfg.set('general', 'single_language', text_type(settings_general_single_language)) - cfg.set('general', 'minimum_score', text_type(settings_general_minimum_score)) - cfg.set('general', 'use_scenename', text_type(settings_general_scenename)) - cfg.set('general', 'use_postprocessing', text_type(settings_general_use_postprocessing)) - cfg.set('general', 'postprocessing_cmd', text_type(settings_general_postprocessing_cmd)) - cfg.set('general', 'use_sonarr', text_type(settings_general_use_sonarr)) - cfg.set('general', 'use_radarr', text_type(settings_general_use_radarr)) - cfg.set('general', 'path_mappings_movie', text_type(settings_general_pathmapping_movie)) - cfg.set('general', 'page_size', text_type(settings_page_size)) - cfg.set('general', 'minimum_score_movie', text_type(settings_general_minimum_score_movies)) - cfg.set('general', 'use_embedded_subs', text_type(settings_general_embedded)) - cfg.set('general', 'only_monitored', text_type(settings_general_only_monitored)) - cfg.set('general', 'adaptive_searching', text_type(settings_general_adaptive_searching)) + + settings.general.ip = text_type(settings_general_ip) + settings.general.port = text_type(settings_general_port) + settings.general.base_url = text_type(settings_general_baseurl) + settings.general.path_mappings = text_type(settings_general_pathmapping) + settings.general.debug = text_type(settings_general_debug) + settings.general.branch = text_type(settings_general_branch) + settings.general.auto_update = text_type(settings_general_automatic) + settings.general.single_language = text_type(settings_general_single_language) + settings.general.minimum_score = text_type(settings_general_minimum_score) + settings.general.use_scenename = text_type(settings_general_scenename) + settings.general.use_postprocessing = text_type(settings_general_use_postprocessing) + settings.general.postprocessing_cmd = text_type(settings_general_postprocessing_cmd) + settings.general.use_sonarr = text_type(settings_general_use_sonarr) + settings.general.use_radarr = text_type(settings_general_use_radarr) + settings.general.path_mappings_movie = text_type(settings_general_pathmapping_movie) + settings.general.page_size = text_type(settings_page_size) + settings.general.minimum_score_movie = text_type(settings_general_minimum_score_movies) + settings.general.use_embedded_subs = text_type(settings_general_embedded) + settings.general.only_monitored = text_type(settings_general_only_monitored) + settings.general.adaptive_searching = text_type(settings_general_adaptive_searching) if after != before: configured() - if not cfg.has_section('proxy'): - cfg.add_section('proxy') - settings_proxy_type = request.forms.get('settings_proxy_type') settings_proxy_url = request.forms.get('settings_proxy_url') settings_proxy_port = request.forms.get('settings_proxy_port') @@ -1156,18 +1131,18 @@ def save_settings(): if before_proxy_password[0] != settings_proxy_type: configured() if before_proxy_password[1] == settings_proxy_password: - cfg.set('proxy', 'type', text_type(settings_proxy_type)) - cfg.set('proxy', 'url', text_type(settings_proxy_url)) - cfg.set('proxy', 'port', text_type(settings_proxy_port)) - cfg.set('proxy', 'username', text_type(settings_proxy_username)) - cfg.set('proxy', 'exclude', text_type(settings_proxy_exclude)) + settings.proxy.type = text_type(settings_proxy_type) + settings.proxy.url = text_type(settings_proxy_url) + settings.proxy.port = text_type(settings_proxy_port) + settings.proxy.username = text_type(settings_proxy_username) + settings.proxy.exclude = text_type(settings_proxy_exclude) else: - cfg.set('proxy', 'type', text_type(settings_proxy_type)) - cfg.set('proxy', 'url', text_type(settings_proxy_url)) - cfg.set('proxy', 'port', text_type(settings_proxy_port)) - cfg.set('proxy', 'username', text_type(settings_proxy_username)) - cfg.set('proxy', 'password', text_type(settings_proxy_password)) - cfg.set('proxy', 'exclude', text_type(settings_proxy_exclude)) + settings.proxy.type = text_type(settings_proxy_type) + settings.proxy.url = text_type(settings_proxy_url) + settings.proxy.port = text_type(settings_proxy_port) + settings.proxy.username = text_type(settings_proxy_username) + settings.proxy.password = text_type(settings_proxy_password) + settings.proxy.exclude = text_type(settings_proxy_exclude) settings_auth_type = request.forms.get('settings_auth_type') settings_auth_username = request.forms.get('settings_auth_username') @@ -1176,12 +1151,12 @@ def save_settings(): if settings.auth.type != settings_auth_type: configured() if settings.auth.password == settings_auth_password: - cfg.set('auth', 'type', text_type(settings_auth_type)) - cfg.set('auth', 'username', text_type(settings_auth_username)) + settings.auth.type = text_type(settings_auth_type) + settings.auth.username = text_type(settings_auth_username) else: - cfg.set('auth', 'type', text_type(settings_auth_type)) - cfg.set('auth', 'username', text_type(settings_auth_username)) - cfg.set('auth', 'password', hashlib.md5(settings_auth_password).hexdigest()) + settings.auth.type = text_type(settings_auth_type) + settings.auth.username = text_type(settings_auth_username) + settings.auth.password = hashlib.md5(settings_auth_password).hexdigest() if settings_auth_username not in aaa._store.users: cork = Cork(os.path.normpath(os.path.join(config_dir, 'config')), initialize=True) cork._store.roles[''] = 100 @@ -1217,12 +1192,12 @@ def save_settings(): settings_sonarr_apikey = request.forms.get('settings_sonarr_apikey') settings_sonarr_sync = request.forms.get('settings_sonarr_sync') - cfg.set('sonarr', 'ip', text_type(settings_sonarr_ip)) - cfg.set('sonarr', 'port', text_type(settings_sonarr_port)) - cfg.set('sonarr', 'base_url', text_type(settings_sonarr_baseurl)) - cfg.set('sonarr', 'ssl', text_type(settings_sonarr_ssl)) - cfg.set('sonarr', 'apikey', text_type(settings_sonarr_apikey)) - cfg.set('sonarr', 'full_update', text_type(settings_sonarr_sync)) + settings.sonarr.ip = text_type(settings_sonarr_ip) + settings.sonarr.port = text_type(settings_sonarr_port) + settings.sonarr.base_url = text_type(settings_sonarr_baseurl) + settings.sonarr.ssl = text_type(settings_sonarr_ssl) + settings.sonarr.apikey = text_type(settings_sonarr_apikey) + settings.sonarr.full_update = text_type(settings_sonarr_sync) settings_radarr_ip = request.forms.get('settings_radarr_ip') settings_radarr_port = request.forms.get('settings_radarr_port') @@ -1235,12 +1210,12 @@ def save_settings(): settings_radarr_apikey = request.forms.get('settings_radarr_apikey') settings_radarr_sync = request.forms.get('settings_radarr_sync') - cfg.set('radarr', 'ip', text_type(settings_radarr_ip)) - cfg.set('radarr', 'port', text_type(settings_radarr_port)) - cfg.set('radarr', 'base_url', text_type(settings_radarr_baseurl)) - cfg.set('radarr', 'ssl', text_type(settings_radarr_ssl)) - cfg.set('radarr', 'apikey', text_type(settings_radarr_apikey)) - cfg.set('radarr', 'full_update', text_type(settings_radarr_sync)) + settings.radarr.ip = text_type(settings_radarr_ip) + settings.radarr.port = text_type(settings_radarr_port) + settings.radarr.base_url = text_type(settings_radarr_baseurl) + settings.radarr.ssl = text_type(settings_radarr_ssl) + settings.radarr.apikey = text_type(settings_radarr_apikey) + settings.radarr.full_update = text_type(settings_radarr_sync) settings_subliminal_providers = request.forms.getall('settings_subliminal_providers') c.execute("UPDATE table_settings_providers SET enabled = 0") @@ -1267,43 +1242,43 @@ def save_settings(): settings_serie_default_enabled = 'False' else: settings_serie_default_enabled = 'True' - cfg.set('general', 'serie_default_enabled', text_type(settings_serie_default_enabled)) + settings.general.serie_default_enabled = text_type(settings_serie_default_enabled) settings_serie_default_languages = str(request.forms.getall('settings_serie_default_languages')) if settings_serie_default_languages == "['None']": settings_serie_default_languages = 'None' - cfg.set('general', 'serie_default_language', text_type(settings_serie_default_languages)) + settings.general.serie_default_language = text_type(settings_serie_default_languages) settings_serie_default_hi = request.forms.get('settings_serie_default_hi') if settings_serie_default_hi is None: settings_serie_default_hi = 'False' else: settings_serie_default_hi = 'True' - cfg.set('general', 'serie_default_hi', text_type(settings_serie_default_hi)) + settings.general.serie_default_hi = text_type(settings_serie_default_hi) settings_movie_default_enabled = request.forms.get('settings_movie_default_enabled') if settings_movie_default_enabled is None: settings_movie_default_enabled = 'False' else: settings_movie_default_enabled = 'True' - cfg.set('general', 'movie_default_enabled', text_type(settings_movie_default_enabled)) + settings.general.movie_default_enabled = text_type(settings_movie_default_enabled) settings_movie_default_languages = str(request.forms.getall('settings_movie_default_languages')) if settings_movie_default_languages == "['None']": settings_movie_default_languages = 'None' - cfg.set('general', 'movie_default_language', text_type(settings_movie_default_languages)) + settings.general.movie_default_language = text_type(settings_movie_default_languages) settings_movie_default_hi = request.forms.get('settings_movie_default_hi') if settings_movie_default_hi is None: settings_movie_default_hi = 'False' else: settings_movie_default_hi = 'True' - cfg.set('general', 'movie_default_hi', text_type(settings_movie_default_hi)) + settings.general.movie_default_hi = text_type(settings_movie_default_hi) - with open(config_file, 'wb') as f: - cfg.write(f) + with open(os.path.join(config_dir, 'config', 'config.ini'), 'w+') as handle: + settings.write(handle) - configure_logging(settings.general.debug) + configure_logging(settings.general.getboolean('debug')) notifiers = c.execute("SELECT * FROM table_settings_notifier ORDER BY name").fetchall() for notifier in notifiers: @@ -1450,7 +1425,7 @@ def system(): releases = ast.literal_eval(f.read()) import platform - use_sonarr = settings.general.use_sonarr + use_sonarr = settings.general.getboolean('use_sonarr') apikey_sonarr = settings.sonarr.apikey sv = url_sonarr + "/api/system/status?apikey=" + apikey_sonarr sonarr_version = '' @@ -1460,7 +1435,7 @@ def system(): except: pass - use_radarr = settings.general.use_radarr + use_radarr = settings.general.getboolean('use_radarr') apikey_radarr = settings.radarr.apikey rv = url_radarr + "/api/system/status?apikey=" + apikey_radarr radarr_version = '' diff --git a/bazarr/scheduler.py b/bazarr/scheduler.py index a5348ec38..4e1b59d52 100644 --- a/bazarr/scheduler.py +++ b/bazarr/scheduler.py @@ -19,7 +19,7 @@ from tzlocal import get_localzone def sonarr_full_update(): - if settings.general.use_sonarr: + if settings.general.getboolean('use_sonarr'): full_update = settings.sonarr.full_update if full_update == "Daily": scheduler.add_job(update_all_episodes, CronTrigger(hour=4), max_instances=1, coalesce=True, @@ -36,7 +36,7 @@ def sonarr_full_update(): def radarr_full_update(): - if settings.general.use_radarr: + if settings.general.getboolean('use_radarr'): full_update = settings.radarr.full_update if full_update == "Daily": scheduler.add_job(update_all_movies, CronTrigger(hour=5), max_instances=1, coalesce=True, misfire_grace_time=15, @@ -70,14 +70,14 @@ else: scheduler.add_job(check_releases, IntervalTrigger(hours=6), max_instances=1, coalesce=True, misfire_grace_time=15, id='update_release', name='Update release info') -if settings.general.use_sonarr: +if settings.general.getboolean('use_sonarr'): scheduler.add_job(update_series, IntervalTrigger(minutes=1), max_instances=1, coalesce=True, misfire_grace_time=15, id='update_series', name='Update series list from Sonarr') scheduler.add_job(sync_episodes, IntervalTrigger(minutes=5), max_instances=1, coalesce=True, misfire_grace_time=15, id='sync_episodes', name='Sync episodes with Sonarr') -if settings.general.use_radarr: +if settings.general.getboolean('use_radarr'): scheduler.add_job(update_movies, IntervalTrigger(minutes=5), max_instances=1, coalesce=True, misfire_grace_time=15, id='update_movies', name='Update movies list from Radarr') -if settings.general.use_sonarr or settings.general.use_radarr: +if settings.general.getboolean('use_sonarr') or settings.general.getboolean('use_radarr'): scheduler.add_job(wanted_search_missing_subtitles, IntervalTrigger(hours=3), max_instances=1, coalesce=True, misfire_grace_time=15, id='wanted_search_missing_subtitles', name='Search for wanted subtitles') sonarr_full_update() diff --git a/bazarr/update_db.py b/bazarr/update_db.py index e60356803..f05116157 100644 --- a/bazarr/update_db.py +++ b/bazarr/update_db.py @@ -73,9 +73,9 @@ if os.path.exists(os.path.join(config_dir, 'db', 'bazarr.db')): except: pass else: - if settings.general.use_sonarr: + if settings.general.getboolean('use_sonarr'): execute_now('sync_episodes') - if settings.general.use_radarr: + if settings.general.getboolean('use_radarr'): execute_now('update_movies') try: @@ -84,7 +84,7 @@ if os.path.exists(os.path.join(config_dir, 'db', 'bazarr.db')): except: pass else: - if settings.general.use_sonarr: + if settings.general.getboolean('use_sonarr'): execute_now('sync_episodes') try: @@ -93,7 +93,7 @@ if os.path.exists(os.path.join(config_dir, 'db', 'bazarr.db')): except: pass else: - if settings.general.use_radarr: + if settings.general.getboolean('use_radarr'): execute_now('update_movies') db.close() diff --git a/libs/backports/__init__.py b/libs/backports/__init__.py index 69e3be50d..f84d25cf9 100644 --- a/libs/backports/__init__.py +++ b/libs/backports/__init__.py @@ -1 +1,11 @@ -__path__ = __import__('pkgutil').extend_path(__path__, __name__) +# A Python "namespace package" http://www.python.org/dev/peps/pep-0382/ +# This always goes inside of a namespace package's __init__.py + +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) + +try: + import pkg_resources + pkg_resources.declare_namespace(__name__) +except ImportError: + pass diff --git a/libs/backports/configparser/__init__.py b/libs/backports/configparser2/__init__.py similarity index 90% rename from libs/backports/configparser/__init__.py rename to libs/backports/configparser2/__init__.py index 06d7a0855..972413562 100644 --- a/libs/backports/configparser/__init__.py +++ b/libs/backports/configparser2/__init__.py @@ -20,8 +20,7 @@ ConfigParser -- responsible for parsing a list of __init__(defaults=None, dict_type=_default_dict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=('#', ';'), inline_comment_prefixes=None, strict=True, - empty_lines_in_values=True, default_section='DEFAULT', - interpolation=, converters=): + empty_lines_in_values=True): Create the parser. When `defaults' is given, it is initialized into the dictionary or intrinsic defaults. The keys must be strings, the values must be appropriate for %()s string interpolation. @@ -135,17 +134,15 @@ import re import sys import warnings -from backports.configparser.helpers import OrderedDict as _default_dict -from backports.configparser.helpers import ChainMap as _ChainMap -from backports.configparser.helpers import from_none, open, str, PY2 +from backports.configparser2.helpers import OrderedDict as _default_dict +from backports.configparser2.helpers import ChainMap as _ChainMap +from backports.configparser2.helpers import from_none, open, str, PY2 __all__ = ["NoSectionError", "DuplicateOptionError", "DuplicateSectionError", "NoOptionError", "InterpolationError", "InterpolationDepthError", - "InterpolationMissingOptionError", "InterpolationSyntaxError", - "ParsingError", "MissingSectionHeaderError", + "InterpolationSyntaxError", "ParsingError", + "MissingSectionHeaderError", "ConfigParser", "SafeConfigParser", "RawConfigParser", - "Interpolation", "BasicInterpolation", "ExtendedInterpolation", - "LegacyInterpolation", "SectionProxy", "ConverterMapping", "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] DEFAULTSECT = "DEFAULT" @@ -254,9 +251,12 @@ class InterpolationMissingOptionError(InterpolationError): """A string substitution required a setting which was not available.""" def __init__(self, option, section, rawval, reference): - msg = ("Bad value substitution: option {0!r} in section {1!r} contains " - "an interpolation key {2!r} which is not a valid option name. " - "Raw value: {3!r}".format(option, section, reference, rawval)) + msg = ("Bad value substitution:\n" + "\tsection: [%s]\n" + "\toption : %s\n" + "\tkey : %s\n" + "\trawval : %s\n" + % (section, option, reference, rawval)) InterpolationError.__init__(self, option, section, msg) self.reference = reference self.args = (option, section, rawval, reference) @@ -274,11 +274,11 @@ class InterpolationDepthError(InterpolationError): """Raised when substitutions are nested too deeply.""" def __init__(self, option, section, rawval): - msg = ("Recursion limit exceeded in value substitution: option {0!r} " - "in section {1!r} contains an interpolation key which " - "cannot be substituted in {2} steps. Raw value: {3!r}" - "".format(option, section, MAX_INTERPOLATION_DEPTH, - rawval)) + msg = ("Value interpolation too deeply recursive:\n" + "\tsection: [%s]\n" + "\toption : %s\n" + "\trawval : %s\n" + % (section, option, rawval)) InterpolationError.__init__(self, option, section, msg) self.args = (option, section, rawval) @@ -374,7 +374,7 @@ class BasicInterpolation(Interpolation): would resolve the "%(dir)s" to the value of dir. All reference expansions are done late, on demand. If a user needs to use a bare % in - a configuration file, she can escape it by writing %%. Other % usage + a configuration file, she can escape it by writing %%. Other other % usage is considered a user error and raises `InterpolationSyntaxError'.""" _KEYCRE = re.compile(r"%\(([^)]+)\)s") @@ -394,9 +394,8 @@ class BasicInterpolation(Interpolation): def _interpolate_some(self, parser, option, accum, rest, section, map, depth): - rawval = parser.get(section, option, raw=True, fallback=rest) if depth > MAX_INTERPOLATION_DEPTH: - raise InterpolationDepthError(option, section, rawval) + raise InterpolationDepthError(option, section, rest) while rest: p = rest.find("%") if p < 0: @@ -421,7 +420,7 @@ class BasicInterpolation(Interpolation): v = map[var] except KeyError: raise from_none(InterpolationMissingOptionError( - option, section, rawval, var)) + option, section, rest, var)) if "%" in v: self._interpolate_some(parser, option, accum, v, section, map, depth + 1) @@ -455,9 +454,8 @@ class ExtendedInterpolation(Interpolation): def _interpolate_some(self, parser, option, accum, rest, section, map, depth): - rawval = parser.get(section, option, raw=True, fallback=rest) if depth > MAX_INTERPOLATION_DEPTH: - raise InterpolationDepthError(option, section, rawval) + raise InterpolationDepthError(option, section, rest) while rest: p = rest.find("$") if p < 0: @@ -494,7 +492,7 @@ class ExtendedInterpolation(Interpolation): "More than one ':' found: %r" % (rest,)) except (KeyError, NoSectionError, NoOptionError): raise from_none(InterpolationMissingOptionError( - option, section, rawval, ":".join(path))) + option, section, rest, ":".join(path))) if "$" in v: self._interpolate_some(parser, opt, accum, v, sect, dict(parser.items(sect, raw=True)), @@ -598,12 +596,10 @@ class RawConfigParser(MutableMapping): empty_lines_in_values = kwargs.get('empty_lines_in_values', True) default_section = kwargs.get('default_section', DEFAULTSECT) interpolation = kwargs.get('interpolation', _UNSET) - converters = kwargs.get('converters', _UNSET) self._dict = dict_type self._sections = self._dict() self._defaults = self._dict() - self._converters = ConverterMapping(self) self._proxies = self._dict() self._proxies[default_section] = SectionProxy(self, default_section) if defaults: @@ -631,8 +627,6 @@ class RawConfigParser(MutableMapping): self._interpolation = self._DEFAULT_INTERPOLATION if self._interpolation is None: self._interpolation = Interpolation() - if converters is not _UNSET: - self._converters.update(converters) def defaults(self): return self._defaults @@ -813,40 +807,48 @@ class RawConfigParser(MutableMapping): def _get(self, section, conv, option, **kwargs): return conv(self.get(section, option, **kwargs)) - def _get_conv(self, section, option, conv, **kwargs): + def getint(self, section, option, **kwargs): # keyword-only arguments - kwargs.setdefault('raw', False) - kwargs.setdefault('vars', None) - fallback = kwargs.pop('fallback', _UNSET) + raw = kwargs.get('raw', False) + vars = kwargs.get('vars', None) + fallback = kwargs.get('fallback', _UNSET) + try: - return self._get(section, conv, option, **kwargs) + return self._get(section, int, option, raw=raw, vars=vars) except (NoSectionError, NoOptionError): if fallback is _UNSET: raise - return fallback - - # getint, getfloat and getboolean provided directly for backwards compat - def getint(self, section, option, **kwargs): - # keyword-only arguments - kwargs.setdefault('raw', False) - kwargs.setdefault('vars', None) - kwargs.setdefault('fallback', _UNSET) - return self._get_conv(section, option, int, **kwargs) + else: + return fallback def getfloat(self, section, option, **kwargs): # keyword-only arguments - kwargs.setdefault('raw', False) - kwargs.setdefault('vars', None) - kwargs.setdefault('fallback', _UNSET) - return self._get_conv(section, option, float, **kwargs) + raw = kwargs.get('raw', False) + vars = kwargs.get('vars', None) + fallback = kwargs.get('fallback', _UNSET) + + try: + return self._get(section, float, option, raw=raw, vars=vars) + except (NoSectionError, NoOptionError): + if fallback is _UNSET: + raise + else: + return fallback def getboolean(self, section, option, **kwargs): # keyword-only arguments - kwargs.setdefault('raw', False) - kwargs.setdefault('vars', None) - kwargs.setdefault('fallback', _UNSET) - return self._get_conv(section, option, self._convert_to_boolean, - **kwargs) + raw = kwargs.get('raw', False) + vars = kwargs.get('vars', None) + fallback = kwargs.get('fallback', _UNSET) + + try: + return self._get(section, self._convert_to_boolean, option, + raw=raw, vars=vars) + except (NoSectionError, NoOptionError): + if fallback is _UNSET: + raise + else: + return fallback def items(self, section=_UNSET, raw=False, vars=None): """Return a list of (name, value) tuples for each option in a section. @@ -1222,10 +1224,6 @@ class RawConfigParser(MutableMapping): return section, option, value - @property - def converters(self): - return self._converters - class ConfigParser(RawConfigParser): """ConfigParser implementing interpolation.""" @@ -1266,10 +1264,6 @@ class SectionProxy(MutableMapping): """Creates a view on a section of the specified `name` in `parser`.""" self._parser = parser self._name = name - for conv in parser.converters: - key = 'get' + conv - getter = functools.partial(self.get, _impl=getattr(parser, key)) - setattr(self, key, getter) def __repr__(self): return ''.format(self._name) @@ -1303,88 +1297,44 @@ class SectionProxy(MutableMapping): else: return self._parser.defaults() - @property - def parser(self): - # The parser object of the proxy is read-only. - return self._parser - - @property - def name(self): - # The name of the section on a proxy is read-only. - return self._name - def get(self, option, fallback=None, **kwargs): - """Get an option value. + # keyword-only arguments + raw = kwargs.get('raw', False) + vars = kwargs.get('vars', None) - Unless `fallback` is provided, `None` will be returned if the option - is not found. + return self._parser.get(self._name, option, raw=raw, vars=vars, + fallback=fallback) - """ + def getint(self, option, fallback=None, **kwargs): # keyword-only arguments - kwargs.setdefault('raw', False) - kwargs.setdefault('vars', None) - _impl = kwargs.pop('_impl', None) - # If `_impl` is provided, it should be a getter method on the parser - # object that provides the desired type conversion. - if not _impl: - _impl = self._parser.get - return _impl(self._name, option, fallback=fallback, **kwargs) - - -class ConverterMapping(MutableMapping): - """Enables reuse of get*() methods between the parser and section proxies. - - If a parser class implements a getter directly, the value for the given - key will be ``None``. The presence of the converter name here enables - section proxies to find and use the implementation on the parser class. - """ + raw = kwargs.get('raw', False) + vars = kwargs.get('vars', None) - GETTERCRE = re.compile(r"^get(?P.+)$") + return self._parser.getint(self._name, option, raw=raw, vars=vars, + fallback=fallback) - def __init__(self, parser): - self._parser = parser - self._data = {} - for getter in dir(self._parser): - m = self.GETTERCRE.match(getter) - if not m or not callable(getattr(self._parser, getter)): - continue - self._data[m.group('name')] = None # See class docstring. + def getfloat(self, option, fallback=None, **kwargs): + # keyword-only arguments + raw = kwargs.get('raw', False) + vars = kwargs.get('vars', None) - def __getitem__(self, key): - return self._data[key] + return self._parser.getfloat(self._name, option, raw=raw, vars=vars, + fallback=fallback) - def __setitem__(self, key, value): - try: - k = 'get' + key - except TypeError: - raise ValueError('Incompatible key: {} (type: {})' - ''.format(key, type(key))) - if k == 'get': - raise ValueError('Incompatible key: cannot use "" as a name') - self._data[key] = value - func = functools.partial(self._parser._get_conv, conv=value) - func.converter = value - setattr(self._parser, k, func) - for proxy in self._parser.values(): - getter = functools.partial(proxy.get, _impl=func) - setattr(proxy, k, getter) + def getboolean(self, option, fallback=None, **kwargs): + # keyword-only arguments + raw = kwargs.get('raw', False) + vars = kwargs.get('vars', None) - def __delitem__(self, key): - try: - k = 'get' + (key or None) - except TypeError: - raise KeyError(key) - del self._data[key] - for inst in itertools.chain((self._parser,), self._parser.values()): - try: - delattr(inst, k) - except AttributeError: - # don't raise since the entry was present in _data, silently - # clean up - continue + return self._parser.getboolean(self._name, option, raw=raw, vars=vars, + fallback=fallback) - def __iter__(self): - return iter(self._data) + @property + def parser(self): + # The parser object of the proxy is read-only. + return self._parser - def __len__(self): - return len(self._data) + @property + def name(self): + # The name of the section on a proxy is read-only. + return self._name diff --git a/libs/backports/configparser/helpers.py b/libs/backports/configparser2/helpers.py similarity index 100% rename from libs/backports/configparser/helpers.py rename to libs/backports/configparser2/helpers.py diff --git a/libs/configparser.py b/libs/configparser.py index b899f9e32..08f1480e8 100644 --- a/libs/configparser.py +++ b/libs/configparser.py @@ -9,7 +9,7 @@ from __future__ import print_function from __future__ import unicode_literals -from backports.configparser import ( +from backports.configparser2 import ( RawConfigParser, ConfigParser, SafeConfigParser, @@ -31,7 +31,6 @@ from backports.configparser import ( InterpolationDepthError, ParsingError, MissingSectionHeaderError, - ConverterMapping, _UNSET, DEFAULTSECT, @@ -39,14 +38,3 @@ from backports.configparser import ( _default_dict, _ChainMap, ) - -__all__ = ["NoSectionError", "DuplicateOptionError", "DuplicateSectionError", - "NoOptionError", "InterpolationError", "InterpolationDepthError", - "InterpolationMissingOptionError", "InterpolationSyntaxError", - "ParsingError", "MissingSectionHeaderError", - "ConfigParser", "SafeConfigParser", "RawConfigParser", - "Interpolation", "BasicInterpolation", "ExtendedInterpolation", - "LegacyInterpolation", "SectionProxy", "ConverterMapping", - "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] - -# NOTE: names missing from __all__ imported anyway for backwards compatibility. diff --git a/libs/simpleconfigparser/__init__.py b/libs/simpleconfigparser/__init__.py new file mode 100644 index 000000000..6e6d3d385 --- /dev/null +++ b/libs/simpleconfigparser/__init__.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +""" +The MIT License + +Copyright (c) 2013 Helgi Þorbjörnsson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +try: + from configparser import ConfigParser as configparser, NoOptionError, NoSectionError +except ImportError: + from ConfigParser import SafeConfigParser as configparser, NoOptionError, NoSectionError + + +class simpleconfigparser(configparser): + class Section(dict): + """ + Contain the section specific items that can be accessed via object properties + """ + parser = None + section = None + + def __init__(self, section, parser): + self.section = section + self.parser = parser + + def __getitem__(self, name, raw=False, vars=None): + """Fetch a value via the dict handler""" + if name not in simpleconfigparser.Section.__dict__: + return self.parser.get(self.section, name, raw, vars) + + def __setitem__(self, name, value): + """Set a value via the dict handler""" + if name in simpleconfigparser.Section.__dict__: + return dict.__setitem__(self, name, value) + + return self.parser.set(self.section, name, value) + + def __getattr__(self, name, raw=False, vars=None): + """Fetch a value via the object handler""" + if name not in simpleconfigparser.Section.__dict__: + return self.parser.get(self.section, name, raw, vars) + + def __setattr__(self, name, value): + """Set a value via the object handler""" + if name in simpleconfigparser.Section.__dict__: + return object.__setattr__(self, name, value) + + return self.parser.set(self.section, name, value) + + def getboolean(self, name): + if not self.section: + return None + + return self.parser.getboolean(self.section, name) + + def items(self): + if not self.section: + return None + + items = [] + for key, value in self.parser.items(self.section): + # strip quotes + items.append((key, value.strip('"\''))) + + return items + + def __init__(self, defaults=None, *args, **kwargs): + configparser.__init__(self, defaults=None, *args, **kwargs) + # Improved defaults handling + if isinstance(defaults, dict): + for section, values in defaults.items(): + # Break out original format defaults was passed in + if not isinstance(values, dict): + break + + if section not in self.sections(): + self.add_section(section) + + for name, value in values.items(): + self.set(section, name, str(value)) + + def __getitem__(self, name): + """Access a section via a dict handler""" + if name not in simpleconfigparser.__dict__: + if name not in self.sections(): + self.add_section(name) + + return simpleconfigparser.Section(name, self) + + return None + + def __getattr__(self, name, raw=False, vars=None): + """Access a section via a object handler""" + if name not in simpleconfigparser.__dict__: + if name not in self.sections(): + self.add_section(name) + + return simpleconfigparser.Section(name, self) + + return None + + def set(self, section, option, value=None): + try: + return configparser.set(self, section, option, value) + except NoSectionError: + return None + + def get(self, section, option, raw=False, vars=None): + try: + # Strip out quotes from the edges + return configparser.get(self, section, option, raw=raw, vars=vars).strip('"\'') + except NoOptionError: + return None diff --git a/libs/version.txt b/libs/version.txt index 2eef8aba4..a068ef849 100644 --- a/libs/version.txt +++ b/libs/version.txt @@ -6,7 +6,7 @@ Beaker=1.10.0 bottle=0.12.13 bottle-fdsend=0.1.1 chardet=3.0.4 -configparser=3.5.0 +configparser2=4.0.0 dogpile.cache=0.6.5 enzyme=0.4.1 gitpython=2.1.9 @@ -19,6 +19,7 @@ pytz=2018.4 rarfile=3.0 requests=2.18.4 six=1.11.0 +SimpleConfigParser=0.1.0 stevedore=1.28.0 subliminal=2.1.0dev tzlocal=1.5.1 diff --git a/views/episodes.tpl b/views/episodes.tpl index 8d0dd237f..68ff96c5c 100644 --- a/views/episodes.tpl +++ b/views/episodes.tpl @@ -91,7 +91,7 @@ %from get_languages import * %from config import settings %from helper import path_replace - %single_language = settings.general.single_language + %single_language = settings.general.getboolean('single_language')
Loading...
@@ -222,7 +222,7 @@ if missing_languages is not None: from get_subtitle import search_active for language in missing_languages: - if episode[10] is not None and settings.general.adaptive_searching and language in episode[10]: + if episode[10] is not None and settings.general.getboolean('adaptive_searching') and language in episode[10]: for lang in ast.literal_eval(episode[10]): if language in lang: if search_active(lang[1]): diff --git a/views/history.tpl b/views/history.tpl index a1e62383f..83721376b 100644 --- a/views/history.tpl +++ b/views/history.tpl @@ -51,8 +51,8 @@
diff --git a/views/menu.tpl b/views/menu.tpl index 1b008a027..61b72a713 100644 --- a/views/menu.tpl +++ b/views/menu.tpl @@ -26,7 +26,7 @@ % import sqlite3 % from config import settings - %if settings.general.only_monitored: + %if settings.general.getboolean('only_monitored'): % monitored_only_query_string = ' AND monitored = "True"' %else: % monitored_only_query_string = "" @@ -50,13 +50,13 @@