# coding=utf-8 import hashlib import os import ast from urllib.parse import quote_plus from subliminal.cache import region from simpleconfigparser import simpleconfigparser, configparser, NoOptionError from .get_args import args class SimpleConfigParser(simpleconfigparser): def get(self, section, option, raw=False, vars=None): try: return configparser.get(self, section, option, raw=raw, vars=vars) except NoOptionError: return None def base_url_slash_cleaner(uri): while "//" in uri: uri = uri.replace("//", "/") return uri 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': '', 'postprocessing_threshold': '90', 'use_postprocessing_threshold': 'False', 'postprocessing_threshold_movie': '70', 'use_postprocessing_threshold_movie': 'False', 'use_sonarr': 'False', 'use_radarr': 'False', 'path_mappings_movie': '[]', 'serie_default_enabled': 'False', 'serie_default_profile': '', 'movie_default_enabled': 'False', 'movie_default_profile': '', 'page_size': '25', 'page_size_manual_search': '10', 'minimum_score_movie': '70', 'use_embedded_subs': 'True', 'embedded_subs_show_desired': 'True', 'utf8_encode': 'True', 'ignore_pgs_subs': 'False', 'ignore_vobsub_subs': 'False', 'ignore_ass_subs': 'False', 'adaptive_searching': 'False', 'adaptive_searching_delay': '3w', 'adaptive_searching_delta': '1w', 'enabled_providers': '[]', 'multithreading': 'True', 'chmod_enabled': 'False', 'chmod': '0640', 'subfolder': 'current', 'subfolder_custom': '', 'upgrade_subs': 'True', 'upgrade_frequency': '12', 'days_to_upgrade_subs': '7', 'upgrade_manual': 'True', 'anti_captcha_provider': 'None', 'wanted_search_frequency': '3', 'wanted_search_frequency_movie': '3', 'subzero_mods': '[]', 'dont_notify_manual_actions': 'False', 'hi_extension': 'hi', 'embedded_subtitles_parser': 'ffprobe' }, 'auth': { 'type': 'None', 'username': '', 'password': '' }, 'cors': { 'enabled': 'False' }, 'backup': { 'folder': os.path.join(args.config_dir, 'backup'), 'retention': '31', 'frequency': 'Weekly', 'day': '6', 'hour': '3' }, 'sonarr': { 'ip': '127.0.0.1', 'port': '8989', 'base_url': '/', 'ssl': 'False', 'apikey': '', 'full_update': 'Daily', 'full_update_day': '6', 'full_update_hour': '4', 'only_monitored': 'False', 'series_sync': '60', 'episodes_sync': '60', 'excluded_tags': '[]', 'excluded_series_types': '[]', 'use_ffprobe_cache': 'True', 'exclude_season_zero': 'False', 'defer_search_signalr': 'False' }, 'radarr': { 'ip': '127.0.0.1', 'port': '7878', 'base_url': '/', 'ssl': 'False', 'apikey': '', 'full_update': 'Daily', 'full_update_day': '6', 'full_update_hour': '5', 'only_monitored': 'False', 'movies_sync': '60', 'excluded_tags': '[]', 'use_ffprobe_cache': 'True', 'defer_search_signalr': 'False' }, 'proxy': { 'type': 'None', 'url': '', 'port': '', 'username': '', 'password': '', 'exclude': '["localhost","127.0.0.1"]' }, 'opensubtitles': { 'username': '', 'password': '', 'use_tag_search': 'False', 'vip': 'False', 'ssl': 'False', 'timeout': '15', 'skip_wrong_fps': 'False' }, 'opensubtitlescom': { 'username': '', 'password': '', 'use_hash': 'True' }, 'addic7ed': { 'username': '', 'password': '', 'cookies': '', 'user_agent': '', 'vip': 'False' }, 'podnapisi': { 'verify_ssl': 'True' }, 'legendasdivx': { 'username': '', 'password': '', 'skip_wrong_fps': 'False' }, 'ktuvit': { 'email': '', 'hashed_password': '' }, 'legendastv': { 'username': '', 'password': '', 'featured_only': 'False' }, 'xsubs': { 'username': '', 'password': '' }, 'assrt': { 'token': '' }, 'anticaptcha': { 'anti_captcha_key': '' }, 'deathbycaptcha': { 'username': '', 'password': '' }, 'napisy24': { 'username': '', 'password': '' }, 'subscene': { 'username': '', 'password': '' }, 'betaseries': { 'token': '' }, 'analytics': { 'enabled': 'True' }, 'titlovi': { 'username': '', 'password': '' }, 'titulky': { 'username': '', 'password': '', 'approved_only': 'False' }, 'embeddedsubtitles': { 'included_codecs': '[]', 'hi_fallback': 'False', 'timeout': '600', 'unknown_as_english': 'False', }, 'karagarga': { 'username': '', 'password': '', 'f_username': '', 'f_password': '', }, 'subsync': { 'use_subsync': 'False', 'use_subsync_threshold': 'False', 'subsync_threshold': '90', 'use_subsync_movie_threshold': 'False', 'subsync_movie_threshold': '70', 'debug': 'False', 'force_audio': 'False' }, 'series_scores': { "hash": 359, "series": 180, "year": 90, "season": 30, "episode": 30, "release_group": 14, "source": 7, "audio_codec": 3, "resolution": 2, "video_codec": 2, "streaming_service": 1, "hearing_impaired": 1, }, 'movie_scores': { "hash": 119, "title": 60, "year": 30, "release_group": 13, "source": 7, "audio_codec": 3, "resolution": 2, "video_codec": 2, "streaming_service": 1, "edition": 1, "hearing_impaired": 1, } } settings = SimpleConfigParser(defaults=defaults, interpolation=None) settings.read(os.path.join(args.config_dir, 'config', 'config.ini')) settings.general.base_url = settings.general.base_url if settings.general.base_url else '/' base_url = settings.general.base_url.rstrip('/') ignore_keys = ['flask_secret_key'] raw_keys = ['movie_default_forced', 'serie_default_forced'] array_keys = ['excluded_tags', 'exclude', 'included_codecs', 'subzero_mods', 'excluded_series_types', 'enabled_providers', 'path_mappings', 'path_mappings_movie'] str_keys = ['chmod'] empty_values = ['', 'None', 'null', 'undefined', None, []] # Increase Sonarr and Radarr sync interval since we now use SignalR feed to update in real time if int(settings.sonarr.series_sync) < 15: settings.sonarr.series_sync = "60" if int(settings.sonarr.episodes_sync) < 15: settings.sonarr.episodes_sync = "60" if int(settings.radarr.movies_sync) < 15: settings.radarr.movies_sync = "60" # Make sure to get of double slashes in base_url settings.general.base_url = base_url_slash_cleaner(uri=settings.general.base_url) settings.sonarr.base_url = base_url_slash_cleaner(uri=settings.sonarr.base_url) settings.radarr.base_url = base_url_slash_cleaner(uri=settings.radarr.base_url) # fixing issue with improper page_size value if settings.general.page_size not in ['25', '50', '100', '250', '500', '1000']: settings.general.page_size = defaults['general']['page_size'] # save updated settings to file if os.path.exists(os.path.join(args.config_dir, 'config', 'config.ini')): with open(os.path.join(args.config_dir, 'config', 'config.ini'), 'w+') as handle: settings.write(handle) def get_settings(): result = dict() sections = settings.sections() for sec in sections: sec_values = settings.items(sec, False) values_dict = dict() for sec_val in sec_values: key = sec_val[0] value = sec_val[1] if key in ignore_keys: continue if key not in raw_keys: # Do some postprocessings if value in empty_values: if key in array_keys: value = [] else: continue elif key in array_keys: value = get_array_from(value) elif value == 'True': value = True elif value == 'False': value = False else: if key not in str_keys: try: value = int(value) except ValueError: pass values_dict[key] = value result[sec] = values_dict return result def save_settings(settings_items): configure_debug = False configure_captcha = False update_schedule = False sonarr_changed = False radarr_changed = False update_path_map = False configure_proxy = False exclusion_updated = False sonarr_exclusion_updated = False radarr_exclusion_updated = False use_embedded_subs_changed = False # Subzero Mods update_subzero = False subzero_mods = get_array_from(settings.general.subzero_mods) if len(subzero_mods) == 1 and subzero_mods[0] == '': subzero_mods = [] for key, value in settings_items: settings_keys = key.split('-') # Make sure that text based form values aren't pass as list if isinstance(value, list) and len(value) == 1 and settings_keys[-1] not in array_keys: value = value[0] if value in empty_values and value != '': value = None # Make sure empty language list are stored correctly if settings_keys[-1] in array_keys and value[0] in empty_values: value = [] # Handle path mappings settings since they are array in array if settings_keys[-1] in ['path_mappings', 'path_mappings_movie']: value = [v.split(',') for v in value] if value == 'true': value = 'True' elif value == 'false': value = 'False' if key in ['settings-general-use_embedded_subs', 'settings-general-ignore_pgs_subs', 'settings-general-ignore_vobsub_subs', 'settings-general-ignore_ass_subs']: use_embedded_subs_changed = True if key in ['settings-general-base_url', 'settings-sonarr-base_url', 'settings-radarr-base_url']: value = base_url_slash_cleaner(value) if key == 'settings-auth-password': if value != settings.auth.password and value is not None: value = hashlib.md5(value.encode('utf-8')).hexdigest() if key == 'settings-general-debug': configure_debug = True if key == 'settings-general-hi_extension': os.environ["SZ_HI_EXTENSION"] = str(value) if key in ['settings-general-anti_captcha_provider', 'settings-anticaptcha-anti_captcha_key', 'settings-deathbycaptcha-username', 'settings-deathbycaptcha-password']: configure_captcha = True if key in ['update_schedule', 'settings-general-use_sonarr', 'settings-general-use_radarr', 'settings-general-auto_update', 'settings-general-upgrade_subs', 'settings-sonarr-series_sync', 'settings-sonarr-episodes_sync', 'settings-radarr-movies_sync', 'settings-sonarr-full_update', 'settings-sonarr-full_update_day', 'settings-sonarr-full_update_hour', 'settings-radarr-full_update', 'settings-radarr-full_update_day', 'settings-radarr-full_update_hour', 'settings-general-wanted_search_frequency', 'settings-general-wanted_search_frequency_movie', 'settings-general-upgrade_frequency', 'settings-backup-frequency', 'settings-backup-day', 'settings-backup-hour']: update_schedule = True if key in ['settings-general-use_sonarr', 'settings-sonarr-ip', 'settings-sonarr-port', 'settings-sonarr-base_url', 'settings-sonarr-ssl', 'settings-sonarr-apikey']: sonarr_changed = True if key in ['settings-general-use_radarr', 'settings-radarr-ip', 'settings-radarr-port', 'settings-radarr-base_url', 'settings-radarr-ssl', 'settings-radarr-apikey']: radarr_changed = True if key in ['settings-general-path_mappings', 'settings-general-path_mappings_movie']: update_path_map = True if key in ['settings-proxy-type', 'settings-proxy-url', 'settings-proxy-port', 'settings-proxy-username', 'settings-proxy-password']: configure_proxy = True if key in ['settings-sonarr-excluded_tags', 'settings-sonarr-only_monitored', 'settings-sonarr-excluded_series_types', 'settings-sonarr-exclude_season_zero', 'settings.radarr.excluded_tags', 'settings-radarr-only_monitored']: exclusion_updated = True if key in ['settings-sonarr-excluded_tags', 'settings-sonarr-only_monitored', 'settings-sonarr-excluded_series_types', 'settings-sonarr-exclude_season_zero']: sonarr_exclusion_updated = True if key in ['settings.radarr.excluded_tags', 'settings-radarr-only_monitored']: radarr_exclusion_updated = True if key == 'settings-addic7ed-username': if key != settings.addic7ed.username: region.delete('addic7ed_data') elif key == 'settings-addic7ed-password': if key != settings.addic7ed.password: region.delete('addic7ed_data') if key == 'settings-legendasdivx-username': if key != settings.legendasdivx.username: region.delete('legendasdivx_cookies2') elif key == 'settings-legendasdivx-password': if key != settings.legendasdivx.password: region.delete('legendasdivx_cookies2') if key == 'settings-opensubtitles-username': if key != settings.opensubtitles.username: region.delete('os_token') elif key == 'settings-opensubtitles-password': if key != settings.opensubtitles.password: region.delete('os_token') if key == 'settings-opensubtitlescom-username': if key != settings.opensubtitlescom.username: region.delete('oscom_token') elif key == 'settings-opensubtitlescom-password': if key != settings.opensubtitlescom.password: region.delete('oscom_token') if key == 'settings-subscene-username': if key != settings.subscene.username: region.delete('subscene_cookies2') elif key == 'settings-subscene-password': if key != settings.subscene.password: region.delete('subscene_cookies2') if key == 'settings-titlovi-username': if key != settings.titlovi.username: region.delete('titlovi_token') elif key == 'settings-titlovi-password': if key != settings.titlovi.password: region.delete('titlovi_token') if settings_keys[0] == 'settings': settings[settings_keys[1]][settings_keys[2]] = str(value) if settings_keys[0] == 'subzero': mod = settings_keys[1] enabled = value == 'True' if mod in subzero_mods and not enabled: subzero_mods.remove(mod) elif enabled: subzero_mods.append(mod) # Handle color if mod == 'color': previous = None for exist_mod in subzero_mods: if exist_mod.startswith('color'): previous = exist_mod break if previous is not None: subzero_mods.remove(previous) if value not in empty_values: subzero_mods.append(value) update_subzero = True if use_embedded_subs_changed: from .scheduler import scheduler from subtitles.indexer.series import list_missing_subtitles from subtitles.indexer.movies import list_missing_subtitles_movies if settings.general.getboolean('use_sonarr'): scheduler.add_job(list_missing_subtitles, kwargs={'send_event': True}) if settings.general.getboolean('use_radarr'): scheduler.add_job(list_missing_subtitles_movies, kwargs={'send_event': True}) if update_subzero: settings.set('general', 'subzero_mods', ','.join(subzero_mods)) with open(os.path.join(args.config_dir, 'config', 'config.ini'), 'w+') as handle: settings.write(handle) # Reconfigure Bazarr to reflect changes if configure_debug: from .logger import configure_logging configure_logging(settings.general.getboolean('debug') or args.debug) if configure_captcha: configure_captcha_func() if update_schedule: from .scheduler import scheduler from .event_handler import event_stream scheduler.update_configurable_tasks() event_stream(type='task') if sonarr_changed: from .signalr_client import sonarr_signalr_client try: sonarr_signalr_client.restart() except Exception: pass if radarr_changed: from .signalr_client import radarr_signalr_client try: radarr_signalr_client.restart() except Exception: pass if update_path_map: from utilities.path_mappings import path_mappings path_mappings.update() if configure_proxy: configure_proxy_func() if exclusion_updated: from .event_handler import event_stream event_stream(type='badges') if sonarr_exclusion_updated: event_stream(type='reset-episode-wanted') if radarr_exclusion_updated: event_stream(type='reset-movie-wanted') def get_array_from(property): if property: if '[' in property: return ast.literal_eval(property) elif ',' in property: return property.split(',') else: return [property] else: return [] def configure_captcha_func(): # set anti-captcha provider and key if settings.general.anti_captcha_provider == 'anti-captcha' and settings.anticaptcha.anti_captcha_key != "": os.environ["ANTICAPTCHA_CLASS"] = 'AntiCaptchaProxyLess' os.environ["ANTICAPTCHA_ACCOUNT_KEY"] = str(settings.anticaptcha.anti_captcha_key) elif settings.general.anti_captcha_provider == 'death-by-captcha' and settings.deathbycaptcha.username != "" and \ settings.deathbycaptcha.password != "": os.environ["ANTICAPTCHA_CLASS"] = 'DeathByCaptchaProxyLess' os.environ["ANTICAPTCHA_ACCOUNT_KEY"] = str(':'.join( {settings.deathbycaptcha.username, settings.deathbycaptcha.password})) else: os.environ["ANTICAPTCHA_CLASS"] = '' def configure_proxy_func(): if settings.proxy.type != 'None': if settings.proxy.username != '' and settings.proxy.password != '': proxy = settings.proxy.type + '://' + quote_plus(settings.proxy.username) + ':' + \ quote_plus(settings.proxy.password) + '@' + settings.proxy.url + ':' + settings.proxy.port else: proxy = settings.proxy.type + '://' + settings.proxy.url + ':' + settings.proxy.port os.environ['HTTP_PROXY'] = str(proxy) os.environ['HTTPS_PROXY'] = str(proxy) exclude = ','.join(get_array_from(settings.proxy.exclude)) os.environ['NO_PROXY'] = exclude def get_scores(): settings = get_settings() return {"movie": settings["movie_scores"], "episode": settings["series_scores"]}