diff --git a/bazarr/get_subtitle.py b/bazarr/get_subtitle.py index 61e2bff4e..8667f188b 100644 --- a/bazarr/get_subtitle.py +++ b/bazarr/get_subtitle.py @@ -55,7 +55,7 @@ def get_video(path, title, sceneName, providers=None, media_type="movie"): path = os.path.join(os.path.dirname(path), sceneName + os.path.splitext(path)[1]) used_scene_name = True hash_from = original_path - + try: video = parse_video(path, hints=hints, providers=providers, dry_run=used_scene_name, hash_from=hash_from) @@ -65,10 +65,10 @@ def get_video(path, title, sceneName, providers=None, media_type="movie"): refine_from_db(original_path, video) refine_from_ffprobe(original_path, video) - + logging.debug('BAZARR is using these video object properties: %s', vars(video)) return video - + except Exception as e: logging.exception("BAZARR Error trying to get video information for this file: " + path) @@ -93,30 +93,30 @@ def get_scores(video, media_type, min_score_movie_perc=60 * 100 / 120.0, min_sco scores = list(subliminal_scores.episode_scores.keys()) if video.is_special: min_score = max_score * min_score_special_ep / 100.0 - + return min_score, max_score, set(scores) -def download_subtitle(path, language, audio_language, hi, forced, providers, providers_auth, sceneName, title, media_type, - forced_minimum_score=None, is_upgrade=False): +def download_subtitle(path, language, audio_language, hi, forced, providers, providers_auth, sceneName, title, + media_type, forced_minimum_score=None, is_upgrade=False): # fixme: supply all missing languages, not only one, to hit providers only once who support multiple languages in # one query - + if settings.general.getboolean('utf8_encode'): os.environ["SZ_KEEP_ENCODING"] = "" else: os.environ["SZ_KEEP_ENCODING"] = "True" - + logging.debug('BAZARR Searching subtitles for this file: ' + path) if hi == "True": hi = "force HI" else: hi = "force non-HI" language_set = set() - + if not isinstance(language, list): language = [language] - + if forced == "True": providers_auth['podnapisi']['only_foreign'] = True ## fixme: This is also in get_providers_auth() providers_auth['subscene']['only_foreign'] = True ## fixme: This is also in get_providers_auth() @@ -125,7 +125,7 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro providers_auth['podnapisi']['only_foreign'] = False providers_auth['subscene']['only_foreign'] = False providers_auth['opensubtitles']['only_foreign'] = False - + for l in language: if l == 'pob': lang_obj = Language('por', 'BR') @@ -136,13 +136,13 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro if forced == "True": lang_obj = Language.rebuild(lang_obj, forced=True) language_set.add(lang_obj) - + minimum_score = settings.general.minimum_score minimum_score_movie = settings.general.minimum_score_movie use_postprocessing = settings.general.getboolean('use_postprocessing') postprocessing_cmd = settings.general.postprocessing_cmd single = settings.general.getboolean('single_language') - + # todo: """ AsyncProviderPool: @@ -157,7 +157,7 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro if video: min_score, max_score, scores = get_scores(video, media_type, min_score_movie_perc=int(minimum_score_movie), min_score_series_perc=int(minimum_score)) - + if providers: if forced_minimum_score: min_score = int(forced_minimum_score) + 1 @@ -176,13 +176,13 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro downloaded_subtitles = None logging.info("BAZARR All providers are throttled") return None - + saved_any = False if downloaded_subtitles: for video, subtitles in downloaded_subtitles.items(): if not subtitles: continue - + try: fld = get_target_folder(path) chmod = int(settings.general.chmod, 8) if not sys.platform.startswith( @@ -195,7 +195,8 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro path_decoder=force_unicode ) except Exception as e: - logging.exception('BAZARR Error saving Subtitles file to disk for this file:' + path + ': ' + repr(e)) + logging.exception( + 'BAZARR Error saving Subtitles file to disk for this file:' + path + ': ' + repr(e)) pass else: saved_any = True @@ -252,12 +253,12 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro pp_threshold = int(settings.general.postprocessing_threshold_movie) if not use_pp_threshold or (use_pp_threshold and percent_score < pp_threshold): - postprocessing(command, path) logging.debug("BAZARR Using post-processing command: {}".format(command)) + postprocessing(command, path) else: logging.debug("BAZARR post-processing skipped because subtitles score isn't below this " - "threshold value: " + str(pp_threshold) + "%") - + "threshold value: " + str(pp_threshold) + "%") + # fixme: support multiple languages at once if media_type == 'series': reversed_path = path_mappings.path_replace_reverse(path) @@ -267,19 +268,19 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro track_event(category=downloaded_provider, action=action, label=downloaded_language) return message, reversed_path, downloaded_language_code2, downloaded_provider, subtitle.score, subtitle.language.forced - + if not saved_any: logging.debug('BAZARR No Subtitles were found for this file: ' + path) return None - + subliminal.region.backend.sync() - + logging.debug('BAZARR Ended searching Subtitles for file: ' + path) def manual_search(path, language, hi, forced, providers, providers_auth, sceneName, title, media_type): logging.debug('BAZARR Manually searching subtitles for this file: ' + path) - + final_subtitles = [] initial_hi = True if hi == "True" else False @@ -288,7 +289,7 @@ def manual_search(path, language, hi, forced, providers, providers_auth, sceneNa else: hi = "force non-HI" language_set = set() - + if forced == "True": providers_auth['podnapisi']['only_foreign'] = True providers_auth['subscene']['only_foreign'] = True @@ -297,7 +298,7 @@ def manual_search(path, language, hi, forced, providers, providers_auth, sceneNa providers_auth['podnapisi']['only_foreign'] = False providers_auth['subscene']['only_foreign'] = False providers_auth['opensubtitles']['only_foreign'] = False - + for lang in ast.literal_eval(language): lang = alpha3_from_alpha2(lang) if lang == 'pob': @@ -309,7 +310,7 @@ def manual_search(path, language, hi, forced, providers, providers_auth, sceneNa if forced == "True": lang_obj = Language.rebuild(lang_obj, forced=True) language_set.add(lang_obj) - + minimum_score = settings.general.minimum_score minimum_score_movie = settings.general.minimum_score_movie use_postprocessing = settings.general.getboolean('use_postprocessing') @@ -323,7 +324,7 @@ def manual_search(path, language, hi, forced, providers, providers_auth, sceneNa if video: min_score, max_score, scores = get_scores(video, media_type, min_score_movie_perc=int(minimum_score_movie), min_score_series_perc=int(minimum_score)) - + try: if providers: subtitles = list_all_subtitles([video], language_set, @@ -339,19 +340,19 @@ def manual_search(path, language, hi, forced, providers, providers_auth, sceneNa logging.exception("BAZARR Error trying to get Subtitle list from provider for this file: " + path) else: subtitles_list = [] - + for s in subtitles[video]: try: matches = s.get_matches(video) except AttributeError: continue - + # skip wrong season/episodes if media_type == "series": can_verify_series = True if not s.hash_verifiable and "hash" in matches: can_verify_series = False - + if can_verify_series and not {"series", "season", "episode"}.issubset(matches): logging.debug(u"BAZARR Skipping %s, because it doesn't match our series/episode", s) continue @@ -369,7 +370,7 @@ def manual_search(path, language, hi, forced, providers, providers_auth, sceneNa for s_item in s.release_info.split(','): if s_item.strip(): releases.append(s_item) - + if len(releases) == 0: releases = ['n/a'] @@ -387,26 +388,26 @@ def manual_search(path, language, hi, forced, providers, providers_auth, sceneNa subtitle=codecs.encode(pickle.dumps(s.make_picklable()), "base64").decode(), url=s.page_link, matches=list(matches), dont_matches=list(not_matched), release_info=releases, uploader=s_uploader)) - + final_subtitles = sorted(subtitles_list, key=lambda x: (x['orig_score'], x['score_without_hash']), reverse=True) logging.debug('BAZARR ' + str(len(final_subtitles)) + " Subtitles have been found for this file: " + path) logging.debug('BAZARR Ended searching Subtitles for this file: ' + path) - + subliminal.region.backend.sync() - + return final_subtitles -def manual_download_subtitle(path, language, audio_language, hi, forced, subtitle, provider, providers_auth, sceneName, title, - media_type): +def manual_download_subtitle(path, language, audio_language, hi, forced, subtitle, provider, providers_auth, sceneName, + title, media_type): logging.debug('BAZARR Manually downloading Subtitles for this file: ' + path) - + if settings.general.getboolean('utf8_encode'): os.environ["SZ_KEEP_ENCODING"] = "" else: os.environ["SZ_KEEP_ENCODING"] = "True" - + subtitle = pickle.loads(codecs.decode(subtitle.encode(), "base64")) use_postprocessing = settings.general.getboolean('use_postprocessing') postprocessing_cmd = settings.general.postprocessing_cmd @@ -441,7 +442,7 @@ def manual_download_subtitle(path, language, audio_language, hi, forced, subtitl chmod=chmod, # formats=("srt", "vtt") path_decoder=force_unicode) - + except Exception as e: logging.exception('BAZARR Error saving Subtitles file to disk for this file:' + path) return @@ -481,10 +482,12 @@ def manual_download_subtitle(path, language, audio_language, hi, forced, subtitl srt_lang=downloaded_language_code3, media_type=media_type, percent_score=score, radarr_id=movie_metadata['radarrId']) - if use_postprocessing is True: + if use_postprocessing: + percent_score = round(subtitle.score * 100 / max_score, 2) command = pp_replace(postprocessing_cmd, path, downloaded_path, downloaded_language, downloaded_language_code2, downloaded_language_code3, audio_language, - audio_language_code2, audio_language_code3, subtitle.language.forced) + audio_language_code2, audio_language_code3, subtitle.language.forced, + percent_score) if media_type == 'series': use_pp_threshold = settings.general.getboolean('use_postprocessing_threshold') @@ -494,27 +497,29 @@ def manual_download_subtitle(path, language, audio_language, hi, forced, subtitl pp_threshold = settings.general.postprocessing_threshold_movie if not use_pp_threshold or (use_pp_threshold and score < float(pp_threshold)): + logging.debug("BAZARR Using post-processing command: {}".format(command)) postprocessing(command, path) else: logging.debug("BAZARR post-processing skipped because subtitles score isn't below this " - "threshold value: " + pp_threshold + "%") - + "threshold value: " + pp_threshold + "%") + if media_type == 'series': reversed_path = path_mappings.path_replace_reverse(path) else: reversed_path = path_mappings.path_replace_reverse_movie(path) - track_event(category=downloaded_provider, action="manually_downloaded", label=downloaded_language) - + track_event(category=downloaded_provider, action="manually_downloaded", + label=downloaded_language) + return message, reversed_path, downloaded_language_code2, downloaded_provider, subtitle.score, subtitle.language.forced else: logging.error( "BAZARR Tried to manually download a Subtitles for file: " + path + " but we weren't able to do (probably throttled by " + str( subtitle.provider_name) + ". Please retry later or select a Subtitles from another provider.") return None - + subliminal.region.backend.sync() - + logging.debug('BAZARR Ended manually downloading Subtitles for file: ' + path) @@ -588,8 +593,8 @@ def manual_upload_subtitle(path, language, forced, title, scene_name, media_type os.chmod(subtitle_path, chmod) message = language_from_alpha3(language) + (" forced" if forced else "") + " Subtitles manually uploaded." - - uploaded_language_code3 = language + + uploaded_language_code3 = language uploaded_language = language_from_alpha3(uploaded_language_code3) uploaded_language_code2 = alpha2_from_alpha3(uploaded_language_code3) audio_language_code2 = alpha2_from_language(audio_language) @@ -609,13 +614,12 @@ def manual_upload_subtitle(path, language, forced, title, scene_name, media_type sync_subtitles(video_path=path, srt_path=subtitle_path, srt_lang=uploaded_language_code3, media_type=media_type, percent_score=100, radarr_id=movie_metadata['radarrId']) - if use_postprocessing is True: + if use_postprocessing : command = pp_replace(postprocessing_cmd, path, subtitle_path, uploaded_language, uploaded_language_code2, uploaded_language_code3, audio_language, - audio_language_code2, audio_language_code3, forced) + audio_language_code2, audio_language_code3, forced, 100) postprocessing(command, path) - if media_type == 'series': reversed_path = path_mappings.path_replace_reverse(path) else: @@ -629,7 +633,7 @@ def series_download_subtitles(no): episodes_details_clause = " AND monitored='True'" else: episodes_details_clause = '' - + episodes_details = database.execute("SELECT path, missing_subtitles, sonarrEpisodeId, scene_name " "FROM table_episodes WHERE sonarrSeriesId=? and missing_subtitles!='[]'" + episodes_details_clause, (no,)) @@ -637,17 +641,18 @@ def series_download_subtitles(no): logging.debug("BAZARR no episode for that sonarrSeriesId can be found in database:", str(no)) return - series_details = database.execute("SELECT hearing_impaired, audio_language, title, forced FROM table_shows WHERE sonarrSeriesId=?", - (no,), only_one=True) + series_details = database.execute( + "SELECT hearing_impaired, audio_language, title, forced FROM table_shows WHERE sonarrSeriesId=?", + (no,), only_one=True) if not series_details: logging.debug("BAZARR no series with that sonarrSeriesId can be found in database:", str(no)) return - + providers_list = get_providers() providers_auth = get_providers_auth() - + count_episodes_details = len(episodes_details) - + for i, episode in enumerate(episodes_details, 1): if providers_list: for language in ast.literal_eval(episode['missing_subtitles']): @@ -682,7 +687,7 @@ def episode_download_subtitles(no): episodes_details_clause = " AND monitored='True'" else: episodes_details_clause = '' - + episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, " "table_episodes.sonarrEpisodeId, table_episodes.scene_name, " "table_shows.hearing_impaired, table_shows.title, table_shows.sonarrSeriesId, " @@ -693,10 +698,10 @@ def episode_download_subtitles(no): if not episodes_details: logging.debug("BAZARR no episode with that sonarrEpisodeId can be found in database:", str(no)) return - + providers_list = get_providers() providers_auth = get_providers_auth() - + for episode in episodes_details: if providers_list: for language in ast.literal_eval(episode['missing_subtitles']): @@ -719,7 +724,8 @@ def episode_download_subtitles(no): provider = result[3] score = result[4] store_subtitles(episode['path'], path_mappings.path_replace(episode['path'])) - history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path, language_code, provider, score) + history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path, + language_code, provider, score) send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message) else: logging.info("BAZARR All providers are throttled") @@ -732,13 +738,14 @@ def movies_download_subtitles(no): else: movie_details_clause = '' - movie = database.execute("SELECT path, missing_subtitles, audio_language, radarrId, sceneName, hearing_impaired, title, forced " - "FROM table_movies WHERE radarrId=?" + movie_details_clause, (no,), only_one=True) + movie = database.execute( + "SELECT path, missing_subtitles, audio_language, radarrId, sceneName, hearing_impaired, title, forced " + "FROM table_movies WHERE radarrId=?" + movie_details_clause, (no,), only_one=True) if not movie: logging.debug("BAZARR no movie with that radarrId can be found in database:", str(no)) return - + providers_list = get_providers() providers_auth = get_providers_auth() @@ -746,7 +753,7 @@ def movies_download_subtitles(no): count_movie = len(ast.literal_eval(movie['missing_subtitles'])) else: count_movie = 0 - + for i, language in enumerate(ast.literal_eval(movie['missing_subtitles']), 1): if providers_list: if language is not None: @@ -784,10 +791,10 @@ def wanted_download_subtitles(path, l, count_episodes): "table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId " "WHERE table_episodes.path=? and table_episodes.missing_subtitles!='[]'", (path_mappings.path_replace_reverse(path),)) - + providers_list = get_providers() providers_auth = get_providers_auth() - + for episode in episodes_details: attempt = episode['failedAttempts'] if type(attempt) == str: @@ -803,7 +810,7 @@ def wanted_download_subtitles(path, l, count_episodes): database.execute("UPDATE table_episodes SET failedAttempts=? WHERE sonarrEpisodeId=?", (str(attempt), episode['sonarrEpisodeId'])) - + for i in range(len(attempt)): if attempt[i][0] == language: if search_active(attempt[i][1]): @@ -825,21 +832,24 @@ def wanted_download_subtitles(path, l, count_episodes): provider = result[3] score = result[4] store_subtitles(episode['path'], path_mappings.path_replace(episode['path'])) - history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path, language_code, provider, score) + history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path, + language_code, provider, score) send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message) else: logging.debug( - 'BAZARR Search is not active for episode ' + episode['path'] + ' Language: ' + attempt[i][0]) + 'BAZARR Search is not active for episode ' + episode['path'] + ' Language: ' + attempt[i][ + 0]) def wanted_download_subtitles_movie(path, l, count_movies): - movies_details = database.execute("SELECT path, missing_subtitles, radarrId, hearing_impaired, audio_language, sceneName, " - "failedAttempts, title, forced FROM table_movies WHERE path = ? " - "AND missing_subtitles != '[]'", (path_mappings.path_replace_reverse_movie(path),)) - + movies_details = database.execute( + "SELECT path, missing_subtitles, radarrId, hearing_impaired, audio_language, sceneName, " + "failedAttempts, title, forced FROM table_movies WHERE path = ? " + "AND missing_subtitles != '[]'", (path_mappings.path_replace_reverse_movie(path),)) + providers_list = get_providers() providers_auth = get_providers_auth() - + for movie in movies_details: attempt = movie['failedAttempts'] if type(attempt) == str: @@ -852,10 +862,10 @@ def wanted_download_subtitles_movie(path, l, count_movies): att = list(zip(*attempt))[0] if language not in att: attempt.append([language, time.time()]) - + database.execute("UPDATE table_movies SET failedAttempts=? WHERE radarrId=?", (str(attempt), movie['radarrId'])) - + for i in range(len(attempt)): if attempt[i][0] == language: if search_active(attempt[i][1]) is True: @@ -881,7 +891,8 @@ def wanted_download_subtitles_movie(path, l, count_movies): send_notifications_movie(movie['radarrId'], message) else: logging.info( - 'BAZARR Search is not active for this Movie ' + movie['path'] + ' Language: ' + attempt[i][0]) + 'BAZARR Search is not active for this Movie ' + movie['path'] + ' Language: ' + attempt[i][ + 0]) def wanted_search_missing_subtitles_series(): @@ -903,7 +914,7 @@ def wanted_search_missing_subtitles_series(): else: logging.info("BAZARR All providers are throttled") return - + logging.info('BAZARR Finished searching for missing Series Subtitles. Check History for more information.') @@ -951,13 +962,14 @@ def search_active(timestamp): def refine_from_db(path, video): if isinstance(video, Episode): - data = database.execute("SELECT table_shows.title as seriesTitle, table_episodes.season, table_episodes.episode, " - "table_episodes.title as episodeTitle, table_shows.year, table_shows.tvdbId, " - "table_shows.alternateTitles, table_episodes.format, table_episodes.resolution, " - "table_episodes.video_codec, table_episodes.audio_codec, table_episodes.path " - "FROM table_episodes INNER JOIN table_shows on " - "table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId " - "WHERE table_episodes.path = ?", (path_mappings.path_replace_reverse(path),), only_one=True) + data = database.execute( + "SELECT table_shows.title as seriesTitle, table_episodes.season, table_episodes.episode, " + "table_episodes.title as episodeTitle, table_shows.year, table_shows.tvdbId, " + "table_shows.alternateTitles, table_episodes.format, table_episodes.resolution, " + "table_episodes.video_codec, table_episodes.audio_codec, table_episodes.path " + "FROM table_episodes INNER JOIN table_shows on " + "table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId " + "WHERE table_episodes.path = ?", (path_mappings.path_replace_reverse(path),), only_one=True) if data: video.series = data['seriesTitle'] @@ -995,7 +1007,7 @@ def refine_from_db(path, video): if data['video_codec']: video.video_codec = data['video_codec'] if not video.audio_codec: if data['audio_codec']: video.audio_codec = data['audio_codec'] - + return video @@ -1006,7 +1018,7 @@ def refine_from_ffprobe(path, video): return else: logging.debug('BAZARR FFprobe used is %s', exe) - + api.initialize({'provider': 'ffmpeg', 'ffmpeg': exe}) data = api.know(path) @@ -1073,7 +1085,7 @@ def upgrade_subtitles(): "table_episodes on table_episodes.sonarrEpisodeId = " "table_history.sonarrEpisodeId WHERE action IN " "(" + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND " - "score is not null" + series_monitored_only_query_string + + "score is not null" + series_monitored_only_query_string + " GROUP BY table_history.video_path, table_history.language", (minimum_timestamp,)) @@ -1125,7 +1137,7 @@ def upgrade_subtitles(): movies_to_upgrade.append(movie) count_movie_to_upgrade = len(movies_to_upgrade) - + providers_list = get_providers() providers_auth = get_providers_auth() @@ -1145,7 +1157,7 @@ def upgrade_subtitles(): forced_languages = [l + ":forced" for l in desired_languages] + desired_languages else: forced_languages = desired_languages - + if episode['language'] in forced_languages: if episode['language'].endswith('forced'): language = episode['language'].split(':')[0] @@ -1153,7 +1165,7 @@ def upgrade_subtitles(): else: language = episode['language'] is_forced = "False" - + result = download_subtitle(path_mappings.path_replace(episode['video_path']), str(alpha3_from_alpha2(language)), episode['audio_language'], @@ -1174,9 +1186,10 @@ def upgrade_subtitles(): provider = result[3] score = result[4] store_subtitles(episode['video_path'], path_mappings.path_replace(episode['video_path'])) - history_log(3, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path, language_code, provider, score) + history_log(3, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path, + language_code, provider, score) send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message) - + if settings.general.getboolean('use_radarr'): for i, movie in enumerate(movies_to_upgrade, 1): if movie['languages'] in [None, 'None', '[]']: @@ -1193,7 +1206,7 @@ def upgrade_subtitles(): forced_languages = [l + ":forced" for l in desired_languages] + desired_languages else: forced_languages = desired_languages - + if movie['language'] in forced_languages: if movie['language'].endswith('forced'): language = movie['language'].split(':')[0] @@ -1201,7 +1214,7 @@ def upgrade_subtitles(): else: language = movie['language'] is_forced = "False" - + result = download_subtitle(path_mappings.path_replace_movie(movie['video_path']), str(alpha3_from_alpha2(language)), movie['audio_language'], @@ -1221,7 +1234,8 @@ def upgrade_subtitles(): language_code = result[2] + ":forced" if forced else result[2] provider = result[3] score = result[4] - store_subtitles_movie(movie['video_path'], path_mappings.path_replace_movie(movie['video_path'])) + store_subtitles_movie(movie['video_path'], + path_mappings.path_replace_movie(movie['video_path'])) history_log_movie(3, movie['radarrId'], message, path, language_code, provider, score) send_notifications_movie(movie['radarrId'], message) diff --git a/bazarr/helper.py b/bazarr/helper.py index f4a08135c..4d7f1b432 100644 --- a/bazarr/helper.py +++ b/bazarr/helper.py @@ -95,7 +95,7 @@ class PathMappings: path_mappings = PathMappings() -def pp_replace(pp_command, episode, subtitles, language, language_code2, language_code3, episode_language, episode_language_code2, episode_language_code3, forced): +def pp_replace(pp_command, episode, subtitles, language, language_code2, language_code3, episode_language, episode_language_code2, episode_language_code3, forced, score): is_forced = ":forced" if forced else "" is_forced_string = " forced" if forced else "" pp_command = pp_command.replace('{{directory}}', os.path.dirname(episode)) @@ -108,6 +108,7 @@ def pp_replace(pp_command, episode, subtitles, language, language_code2, languag pp_command = pp_command.replace('{{episode_language}}', episode_language) pp_command = pp_command.replace('{{episode_language_code2}}', episode_language_code2) pp_command = pp_command.replace('{{episode_language_code3}}', episode_language_code3) + pp_command = pp_command.replace('{{score}}', str(score)) return pp_command diff --git a/bazarr/list_subtitles.py b/bazarr/list_subtitles.py index 20bddc995..1cf1f0494 100644 --- a/bazarr/list_subtitles.py +++ b/bazarr/list_subtitles.py @@ -364,8 +364,8 @@ def guess_external_subtitles(dest_folder, subtitles): logging.debug("BAZARR falling back to file content analysis to detect language.") detected_language = None - # to improve performance, skip detection of files larger that 5M - if os.path.getsize(subtitle_path) > 5*1024*1024: + # to improve performance, skip detection of files larger that 1M + if os.path.getsize(subtitle_path) > 1*1024*1024: logging.debug("BAZARR subtitles file is too large to be text based. Skipping this file: " + subtitle_path) continue @@ -374,16 +374,11 @@ def guess_external_subtitles(dest_folder, subtitles): text = f.read() try: - # to improve performance, use only the first 32K to detect encoding - guess = chardet.detect(text[:32768]) + guess = chardet.detect(text) logging.debug('BAZARR detected encoding %r', guess) - if guess["confidence"] < 0.6: - raise UnicodeError - if guess["encoding"] == "ascii": - guess["encoding"] = "utf-8" text = text.decode(guess["encoding"]) detected_language = guess_language(text) - except UnicodeError: + except (UnicodeDecodeError, TypeError): logging.exception("BAZARR subtitles file doesn't seems to be text based. Skipping this file: " + subtitle_path) except: diff --git a/libs/subliminal_patch/core.py b/libs/subliminal_patch/core.py index 2746522ff..f854390cf 100644 --- a/libs/subliminal_patch/core.py +++ b/libs/subliminal_patch/core.py @@ -605,6 +605,11 @@ def _search_external_subtitles(path, languages=None, only_one=False, scandir_gen if not INCLUDE_EXOTIC_SUBS and p_ext not in (".srt", ".ass", ".ssa", ".vtt"): continue + if p_root.lower() == fn_no_ext_lower: + # skip check for language code if the subtitle file name is the same as the video name + subtitles[p] = None + continue + # extract potential forced/normal/default tag # fixme: duplicate from subtitlehelpers split_tag = p_root.rsplit('.', 1) diff --git a/libs/subliminal_patch/providers/betaseries.py b/libs/subliminal_patch/providers/betaseries.py index 7115ada5e..f4c769293 100644 --- a/libs/subliminal_patch/providers/betaseries.py +++ b/libs/subliminal_patch/providers/betaseries.py @@ -139,7 +139,11 @@ class BetaSeriesProvider(Provider): def download_subtitle(self, subtitle): logger.info('Downloading subtitle %r', subtitle) r = self.session.get(subtitle.download_link, timeout=10) - r.raise_for_status() + if r.status_code == 404: + logger.error('Error 404 downloading %r', subtitle) + return + else: + r.raise_for_status() archive = _get_archive(r.content) if archive: @@ -153,7 +157,7 @@ class BetaSeriesProvider(Provider): if subtitle_content: subtitle.content = fix_line_ending(subtitle_content) else: - logger.debug('Could not extract subtitle from %r', archive) + logger.error('Could not extract subtitle from %r', archive) def _get_archive(content): diff --git a/libs/subliminal_patch/providers/titulky.py b/libs/subliminal_patch/providers/titulky.py index b462f923b..d6b9bc189 100644 --- a/libs/subliminal_patch/providers/titulky.py +++ b/libs/subliminal_patch/providers/titulky.py @@ -66,7 +66,6 @@ class TitulkySubtitle(Subtitle): self.version = version self.year = year self.download_link = download_link - self.encoding = 'UTF-8' for t in title: self.title = t if year: diff --git a/libs/subliminal_patch/subtitle.py b/libs/subliminal_patch/subtitle.py index 4ef52edbe..5429004eb 100644 --- a/libs/subliminal_patch/subtitle.py +++ b/libs/subliminal_patch/subtitle.py @@ -105,7 +105,7 @@ class Subtitle(Subtitle_): return self def get_encoding(self): - return self.encoding if self.encoding else self.guess_encoding() + return self.guess_encoding() def set_encoding(self, encoding): ge = self.get_encoding() @@ -116,7 +116,6 @@ class Subtitle(Subtitle_): logger.debug("Changing encoding: to %s, from %s", encoding, ge) self.content = unicontent.encode(encoding) self._guessed_encoding = encoding - self.encoding = encoding def normalize(self): """ @@ -141,6 +140,16 @@ class Subtitle(Subtitle_): if self._guessed_encoding: return self._guessed_encoding + if self.encoding: + # check provider encoding and use it only if it is valid + try: + self.content.decode(self.encoding) + self._guessed_encoding = self.encoding + return self._guessed_encoding + except: + # provider specified encoding is invalid, fallback to guessing + pass + logger.info('Guessing encoding for language %s', self.language) encodings = ['utf-8'] @@ -206,7 +215,7 @@ class Subtitle(Subtitle_): else: # Western European (windows-1252) / Northern European - encodings.extend(['latin-1', 'iso-8859-15', 'iso-8859-9', 'iso-8859-4', 'iso-8859-1']) + encodings.extend(['windows-1252', 'iso-8859-15', 'iso-8859-9', 'iso-8859-4', 'iso-8859-1']) # try to decode logger.debug('Trying encodings %r', encodings) diff --git a/static/css/bootstrap-slider.min.css b/static/css/bootstrap-slider.min.css new file mode 100644 index 000000000..503972f06 --- /dev/null +++ b/static/css/bootstrap-slider.min.css @@ -0,0 +1,43 @@ +/*! ======================================================= + VERSION 11.0.2 +========================================================= */ +/*! ========================================================= + * bootstrap-slider.js + * + * Maintainers: + * Kyle Kemp + * - Twitter: @seiyria + * - Github: seiyria + * Rohit Kalkur + * - Twitter: @Rovolutionary + * - Github: rovolution + * + * ========================================================= + * + * bootstrap-slider is released under the MIT License + * Copyright (c) 2019 Kyle Kemp, Rohit Kalkur, and contributors + * + * 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. + * + * ========================================================= */.slider{display:inline-block;vertical-align:middle;position:relative}.slider.slider-horizontal{width:210px;height:20px}.slider.slider-horizontal .slider-track{height:10px;width:100%;margin-top:-5px;top:50%;left:0}.slider.slider-horizontal .slider-selection,.slider.slider-horizontal .slider-track-low,.slider.slider-horizontal .slider-track-high{height:100%;top:0;bottom:0}.slider.slider-horizontal .slider-tick,.slider.slider-horizontal .slider-handle{margin-left:-10px}.slider.slider-horizontal .slider-tick.triangle,.slider.slider-horizontal .slider-handle.triangle{position:relative;top:50%;transform:translateY(-50%);border-width:0 10px 10px 10px;width:0;height:0;border-bottom-color:#036fa5;margin-top:0}.slider.slider-horizontal .slider-tick-container{white-space:nowrap;position:absolute;top:0;left:0;width:100%}.slider.slider-horizontal .slider-tick-label-container{white-space:nowrap;margin-top:20px}.slider.slider-horizontal .slider-tick-label-container .slider-tick-label{display:inline-block;text-align:center}.slider.slider-horizontal.slider-rtl .slider-track{left:initial;right:0}.slider.slider-horizontal.slider-rtl .slider-tick,.slider.slider-horizontal.slider-rtl .slider-handle{margin-left:initial;margin-right:-10px}.slider.slider-horizontal.slider-rtl .slider-tick-container{left:initial;right:0}.slider.slider-vertical{height:210px;width:20px}.slider.slider-vertical .slider-track{width:10px;height:100%;left:25%;top:0}.slider.slider-vertical .slider-selection{width:100%;left:0;top:0;bottom:0}.slider.slider-vertical .slider-track-low,.slider.slider-vertical .slider-track-high{width:100%;left:0;right:0}.slider.slider-vertical .slider-tick,.slider.slider-vertical .slider-handle{margin-top:-10px}.slider.slider-vertical .slider-tick.triangle,.slider.slider-vertical .slider-handle.triangle{border-width:10px 0 10px 10px;width:1px;height:1px;border-left-color:#036fa5;margin-left:0}.slider.slider-vertical .slider-tick-label-container{white-space:nowrap}.slider.slider-vertical .slider-tick-label-container .slider-tick-label{padding-left:4px}.slider.slider-vertical.slider-rtl .slider-track{left:initial;right:25%}.slider.slider-vertical.slider-rtl .slider-selection{left:initial;right:0}.slider.slider-vertical.slider-rtl .slider-tick.triangle,.slider.slider-vertical.slider-rtl .slider-handle.triangle{border-width:10px 10px 10px 0}.slider.slider-vertical.slider-rtl .slider-tick-label-container .slider-tick-label{padding-left:initial;padding-right:4px}.slider.slider-disabled .slider-handle{background-color:#cfcfcf;background-image:-moz-linear-gradient(top, #DFDFDF, #BEBEBE);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#DFDFDF), to(#BEBEBE));background-image:-webkit-linear-gradient(top, #DFDFDF, #BEBEBE);background-image:-o-linear-gradient(top, #DFDFDF, #BEBEBE);background-image:linear-gradient(to bottom, #DFDFDF, #BEBEBE);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#DFDFDF', endColorstr='#BEBEBE',GradientType=0)}.slider.slider-disabled .slider-track{background-color:#e7e7e7;background-image:-moz-linear-gradient(top, #E5E5E5, #E9E9E9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#E5E5E5), to(#E9E9E9));background-image:-webkit-linear-gradient(top, #E5E5E5, #E9E9E9);background-image:-o-linear-gradient(top, #E5E5E5, #E9E9E9);background-image:linear-gradient(to bottom, #E5E5E5, #E9E9E9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#E5E5E5', endColorstr='#E9E9E9',GradientType=0);cursor:not-allowed}.slider input{display:none}.slider .tooltip-inner{white-space:nowrap;max-width:none}.slider .bs-tooltip-top .tooltip-inner,.slider .bs-tooltip-bottom .tooltip-inner{position:relative;left:-50%}.slider.bs-tooltip-left .tooltip-inner,.slider.bs-tooltip-right .tooltip-inner{position:relative;top:-100%}.slider .tooltip{pointer-events:none}.slider .tooltip.bs-tooltip-top .arrow,.slider .tooltip.bs-tooltip-bottom .arrow{left:-.4rem}.slider .tooltip.bs-tooltip-top{margin-top:-44px}.slider .tooltip.bs-tooltip-bottom{margin-top:2px}.slider .tooltip.bs-tooltip-left,.slider .tooltip.bs-tooltip-right{margin-top:-14px}.slider .tooltip.bs-tooltip-left .arrow,.slider .tooltip.bs-tooltip-right .arrow{top:8px}.slider .hide{display:none}.slider-track{background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #F9F9F9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#F9F9F9));background-image:-webkit-linear-gradient(top, #f5f5f5, #F9F9F9);background-image:-o-linear-gradient(top, #f5f5f5, #F9F9F9);background-image:linear-gradient(to bottom, #f5f5f5, #F9F9F9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#F9F9F9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;position:absolute;cursor:pointer}.slider-selection{background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #F9F9F9, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#F9F9F9), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #F9F9F9, #f5f5f5);background-image:-o-linear-gradient(top, #F9F9F9, #f5f5f5);background-image:linear-gradient(to bottom, #F9F9F9, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#F9F9F9', endColorstr='#f5f5f5',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;position:absolute}.slider-selection.tick-slider-selection{background-color:#46c1fe;background-image:-moz-linear-gradient(top, #52c5ff, #3abcfd);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#52c5ff), to(#3abcfd));background-image:-webkit-linear-gradient(top, #52c5ff, #3abcfd);background-image:-o-linear-gradient(top, #52c5ff, #3abcfd);background-image:linear-gradient(to bottom, #52c5ff, #3abcfd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#52c5ff', endColorstr='#3abcfd',GradientType=0)}.slider-track-low,.slider-track-high{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;position:absolute;background:transparent}.slider-handle{background-color:#0478b2;background-image:-moz-linear-gradient(top, #0480BE, #036fa5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0480BE), to(#036fa5));background-image:-webkit-linear-gradient(top, #0480BE, #036fa5);background-image:-o-linear-gradient(top, #0480BE, #036fa5);background-image:linear-gradient(to bottom, #0480BE, #036fa5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0480BE', endColorstr='#036fa5',GradientType=0);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);position:absolute;top:0;width:20px;height:20px;background-color:#0480BE;border:0px solid transparent}.slider-handle:hover{cursor:pointer}.slider-handle.round{-webkit-border-radius:20px;-moz-border-radius:20px;border-radius:20px}.slider-handle.triangle{background:transparent none}.slider-handle.custom{background:transparent none}.slider-handle.custom::before{line-height:20px;font-size:20px;content:'\2605';color:#726204}.slider-tick{background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #F9F9F9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#F9F9F9));background-image:-webkit-linear-gradient(top, #f5f5f5, #F9F9F9);background-image:-o-linear-gradient(top, #f5f5f5, #F9F9F9);background-image:linear-gradient(to bottom, #f5f5f5, #F9F9F9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#F9F9F9',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;cursor:pointer;width:20px;height:20px;filter:none;opacity:0.8;border:0px solid transparent}.slider-tick.round{border-radius:50%}.slider-tick.triangle{background:transparent none}.slider-tick.custom{background:transparent none}.slider-tick.custom::before{line-height:20px;font-size:20px;content:'\2605';color:#726204}.slider-tick.in-selection{background-color:#46c1fe;background-image:-moz-linear-gradient(top, #52c5ff, #3abcfd);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#52c5ff), to(#3abcfd));background-image:-webkit-linear-gradient(top, #52c5ff, #3abcfd);background-image:-o-linear-gradient(top, #52c5ff, #3abcfd);background-image:linear-gradient(to bottom, #52c5ff, #3abcfd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#52c5ff', endColorstr='#3abcfd',GradientType=0);opacity:1} + +/*# sourceMappingURL=bootstrap-slider.min.css.map */ \ No newline at end of file diff --git a/static/js/bootstrap-slider.min.js b/static/js/bootstrap-slider.min.js new file mode 100644 index 000000000..9d3b869a1 --- /dev/null +++ b/static/js/bootstrap-slider.min.js @@ -0,0 +1,5 @@ +/*! ======================================================= + VERSION 11.0.2 +========================================================= */ +"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a},windowIsDefined="object"===("undefined"==typeof window?"undefined":_typeof(window));!function(a){if("function"==typeof define&&define.amd)define(["jquery"],a);else if("object"===("undefined"==typeof module?"undefined":_typeof(module))&&module.exports){var b;try{b=require("jquery")}catch(c){b=null}module.exports=a(b)}else window&&(window.Slider=a(window.jQuery))}(function(a){var b="slider",c="bootstrapSlider";windowIsDefined&&!window.console&&(window.console={}),windowIsDefined&&!window.console.log&&(window.console.log=function(){}),windowIsDefined&&!window.console.warn&&(window.console.warn=function(){});var d;return function(a){function b(){}function c(a){function c(b){b.prototype.option||(b.prototype.option=function(b){a.isPlainObject(b)&&(this.options=a.extend(!0,this.options,b))})}function e(b,c){a.fn[b]=function(e){if("string"==typeof e){for(var g=d.call(arguments,1),h=0,i=this.length;i>h;h++){var j=this[h],k=a.data(j,b);if(k)if(a.isFunction(k[e])&&"_"!==e.charAt(0)){var l=k[e].apply(k,g);if(void 0!==l&&l!==k)return l}else f("no such method '"+e+"' for "+b+" instance");else f("cannot call methods on "+b+" prior to initialization; attempted to call '"+e+"'")}return this}var m=this.map(function(){var d=a.data(this,b);return d?(d.option(e),d._init()):(d=new c(this,e),a.data(this,b,d)),a(this)});return 1===m.length?m[0]:m}}if(a){var f="undefined"==typeof console?b:function(a){console.error(a)};return a.bridget=function(a,b){c(b),e(a,b)},a.bridget}}var d=Array.prototype.slice;c(a)}(a),function(a){function e(b,c){function d(a,b){var c="data-slider-"+b.replace(/_/g,"-"),d=a.getAttribute(c);try{return JSON.parse(d)}catch(e){return d}}this._state={value:null,enabled:null,offset:null,size:null,percentage:null,inDrag:!1,over:!1,tickIndex:null},this.ticksCallbackMap={},this.handleCallbackMap={},"string"==typeof b?this.element=document.querySelector(b):b instanceof HTMLElement&&(this.element=b),c=c?c:{};for(var e=Object.keys(this.defaultOptions),f=c.hasOwnProperty("min"),g=c.hasOwnProperty("max"),i=0;i0,this.ticksAreValid||(this.options.lock_to_ticks=!1),"auto"===this.options.rtl){var l=window.getComputedStyle(this.element);null!=l?this.options.rtl="rtl"===l.direction:this.options.rtl="rtl"===this.element.style.direction}"vertical"!==this.options.orientation||"top"!==this.options.tooltip_position&&"bottom"!==this.options.tooltip_position?"horizontal"!==this.options.orientation||"left"!==this.options.tooltip_position&&"right"!==this.options.tooltip_position||(this.options.tooltip_position="top"):this.options.rtl?this.options.tooltip_position="left":this.options.tooltip_position="right";var m,n,o,p,q,r=this.element.style.width,s=!1,t=this.element.parentNode;if(this.sliderElem)s=!0;else{this.sliderElem=document.createElement("div"),this.sliderElem.className="slider";var u=document.createElement("div");u.className="slider-track",n=document.createElement("div"),n.className="slider-track-low",m=document.createElement("div"),m.className="slider-selection",o=document.createElement("div"),o.className="slider-track-high",p=document.createElement("div"),p.className="slider-handle min-slider-handle",p.setAttribute("role","slider"),p.setAttribute("aria-valuemin",this.options.min),p.setAttribute("aria-valuemax",this.options.max),q=document.createElement("div"),q.className="slider-handle max-slider-handle",q.setAttribute("role","slider"),q.setAttribute("aria-valuemin",this.options.min),q.setAttribute("aria-valuemax",this.options.max),u.appendChild(n),u.appendChild(m),u.appendChild(o),this.rangeHighlightElements=[];var v=this.options.rangeHighlights;if(Array.isArray(v)&&v.length>0)for(var w=0;w0){for(this.ticksContainer=document.createElement("div"),this.ticksContainer.className="slider-tick-container",i=0;i0)for(this.tickLabelContainer=document.createElement("div"),this.tickLabelContainer.className="slider-tick-label-container",i=0;i0&&(g||(this.options.max=Math.max.apply(Math,this.options.ticks)),f||(this.options.min=Math.min.apply(Math,this.options.ticks))),Array.isArray(this.options.value)?(this.options.range=!0,this._state.value=this.options.value):this.options.range?this._state.value=[this.options.value,this.options.max]:this._state.value=this.options.value,this.trackLow=n||this.trackLow,this.trackSelection=m||this.trackSelection,this.trackHigh=o||this.trackHigh,"none"===this.options.selection?(this._addClass(this.trackLow,"hide"),this._addClass(this.trackSelection,"hide"),this._addClass(this.trackHigh,"hide")):("after"===this.options.selection||"before"===this.options.selection)&&(this._removeClass(this.trackLow,"hide"),this._removeClass(this.trackSelection,"hide"),this._removeClass(this.trackHigh,"hide")),this.handle1=p||this.handle1,this.handle2=q||this.handle2,s===!0)for(this._removeClass(this.handle1,"round triangle"),this._removeClass(this.handle2,"round triangle hide"),i=0;ib.max?b.max:a},toValue:function(a){var b=a/100*(this.options.max-this.options.min),c=!0;if(this.options.ticks_positions.length>0){for(var d,e,f,g=0,i=1;i0){for(var b,c,d,e=0,f=0;f0?this.options.ticks[f-1]:0,d=f>0?this.options.ticks_positions[f-1]:0,c=this.options.ticks[f],e=this.options.ticks_positions[f];break}if(f>0){var g=(a-b)/(c-b);return d+g*(e-d)}}return 100*(a-this.options.min)/(this.options.max-this.options.min)}},logarithmic:{toValue:function(a){var b=1-this.options.min,c=Math.log(this.options.min+b),d=Math.log(this.options.max+b),e=Math.exp(c+(d-c)*a/100)-b;return Math.round(e)===d?d:(e=this.options.min+Math.round((e-this.options.min)/this.options.step)*this.options.step,h.linear.getValue(e,this.options))},toPercentage:function(a){if(this.options.max===this.options.min)return 0;var b=1-this.options.min,c=Math.log(this.options.max+b),d=Math.log(this.options.min+b),e=Math.log(a+b);return 100*(e-d)/(c-d)}}};d=function(a,b){return e.call(this,a,b),this},d.prototype={_init:function(){},constructor:d,defaultOptions:{id:"",min:0,max:10,step:1,precision:0,orientation:"horizontal",value:5,range:!1,selection:"before",tooltip:"show",tooltip_split:!1,lock_to_ticks:!1,handle:"round",reversed:!1,rtl:"auto",enabled:!0,formatter:function(a){return Array.isArray(a)?a[0]+" : "+a[1]:a},natural_arrow_keys:!1,ticks:[],ticks_positions:[],ticks_labels:[],ticks_snap_bounds:0,ticks_tooltip:!1,scale:"linear",focus:!1,tooltip_position:null,labelledby:null,rangeHighlights:[]},getElement:function(){return this.sliderElem},getValue:function(){return this.options.range?this._state.value:this._state.value[0]},setValue:function(a,b,c){a||(a=0);var d=this.getValue();this._state.value=this._validateInputValue(a);var e=this._applyPrecision.bind(this);this.options.range?(this._state.value[0]=e(this._state.value[0]),this._state.value[1]=e(this._state.value[1]),this.ticksAreValid&&this.options.lock_to_ticks&&(this._state.value[0]=this.options.ticks[this._getClosestTickIndex(this._state.value[0])],this._state.value[1]=this.options.ticks[this._getClosestTickIndex(this._state.value[1])]),this._state.value[0]=Math.max(this.options.min,Math.min(this.options.max,this._state.value[0])),this._state.value[1]=Math.max(this.options.min,Math.min(this.options.max,this._state.value[1]))):(this._state.value=e(this._state.value),this.ticksAreValid&&this.options.lock_to_ticks&&(this._state.value=this.options.ticks[this._getClosestTickIndex(this._state.value)]),this._state.value=[Math.max(this.options.min,Math.min(this.options.max,this._state.value))],this._addClass(this.handle2,"hide"),"after"===this.options.selection?this._state.value[1]=this.options.max:this._state.value[1]=this.options.min),this._setTickIndex(),this.options.max>this.options.min?this._state.percentage=[this._toPercentage(this._state.value[0]),this._toPercentage(this._state.value[1]),100*this.options.step/(this.options.max-this.options.min)]:this._state.percentage=[0,0,100],this._layout();var f=this.options.range?this._state.value:this._state.value[0];this._setDataVal(f),b===!0&&this._trigger("slide",f);var g=!1;return g=Array.isArray(f)?d[0]!==f[0]||d[1]!==f[1]:d!==f,g&&c===!0&&this._trigger("change",{oldValue:d,newValue:f}),this},destroy:function(){this._removeSliderEventHandlers(),this.sliderElem.parentNode.removeChild(this.sliderElem),this.element.style.display="",this._cleanUpEventCallbacksMap(),this.element.removeAttribute("data"),a&&(this._unbindJQueryEventHandlers(),f===b&&this.$element.removeData(f),this.$element.removeData(c))},disable:function(){return this._state.enabled=!1,this.handle1.removeAttribute("tabindex"),this.handle2.removeAttribute("tabindex"),this._addClass(this.sliderElem,"slider-disabled"),this._trigger("slideDisabled"),this},enable:function(){return this._state.enabled=!0,this.handle1.setAttribute("tabindex",0),this.handle2.setAttribute("tabindex",0),this._removeClass(this.sliderElem,"slider-disabled"),this._trigger("slideEnabled"),this},toggle:function(){return this._state.enabled?this.disable():this.enable(),this},isEnabled:function(){return this._state.enabled},on:function(a,b){return this._bindNonQueryEventHandler(a,b),this},off:function(b,c){a?(this.$element.off(b,c),this.$sliderElem.off(b,c)):this._unbindNonQueryEventHandler(b,c)},getAttribute:function(a){return a?this.options[a]:this.options},setAttribute:function(a,b){return this.options[a]=b,this},refresh:function(d){var g=this.getValue();return this._removeSliderEventHandlers(),e.call(this,this.element,this.options),d&&d.useCurrentValue===!0&&this.setValue(g),a&&(f===b?(a.data(this.element,b,this),a.data(this.element,c,this)):a.data(this.element,c,this)),this},relayout:function(){return this._resize(),this},_removeTooltipListener:function(a,b){this.handle1.removeEventListener(a,b,!1),this.handle2.removeEventListener(a,b,!1)},_removeSliderEventHandlers:function(){if(this.handle1.removeEventListener("keydown",this.handle1Keydown,!1),this.handle2.removeEventListener("keydown",this.handle2Keydown,!1),this.options.ticks_tooltip){for(var a=this.ticksContainer.getElementsByClassName("slider-tick"),b=0;b0&&a.options.ticks_positions[c]||a._toPercentage(a.options.ticks[c])):f=a._toPercentage(e),d.value[0]=e,d.percentage[0]=f,a._setToolTipOnMouseOver(d),a._showTooltip()};return b.addEventListener("mouseenter",d,!1),d},addMouseLeave:function(a,b){var c=function(){a._hideTooltip()};return b.addEventListener("mouseleave",c,!1),c}}},_layout:function(){var a,b;if(a=this.options.reversed?[100-this._state.percentage[0],this.options.range?100-this._state.percentage[1]:this._state.percentage[1]]:[this._state.percentage[0],this._state.percentage[1]],this.handle1.style[this.stylePos]=a[0]+"%",this.handle1.setAttribute("aria-valuenow",this._state.value[0]),b=this.options.formatter(this._state.value[0]),isNaN(b)?this.handle1.setAttribute("aria-valuetext",b):this.handle1.removeAttribute("aria-valuetext"),this.handle2.style[this.stylePos]=a[1]+"%",this.handle2.setAttribute("aria-valuenow",this._state.value[1]),b=this.options.formatter(this._state.value[1]),isNaN(b)?this.handle2.setAttribute("aria-valuetext",b):this.handle2.removeAttribute("aria-valuetext"),this.rangeHighlightElements.length>0&&Array.isArray(this.options.rangeHighlights)&&this.options.rangeHighlights.length>0)for(var c=0;c0){var h,i="vertical"===this.options.orientation?"height":"width";h="vertical"===this.options.orientation?"marginTop":this.options.rtl?"marginRight":"marginLeft";var j=this._state.size/(this.options.ticks.length-1);if(this.tickLabelContainer){var k=0;if(0===this.options.ticks_positions.length)"vertical"!==this.options.orientation&&(this.tickLabelContainer.style[h]=-j/2+"px"),k=this.tickLabelContainer.offsetHeight;else for(l=0;lk&&(k=this.tickLabelContainer.childNodes[l].offsetHeight);"horizontal"===this.options.orientation&&(this.sliderElem.style.marginBottom=k+"px")}for(var l=0;l=a[0]&&m<=a[1]&&this._addClass(this.ticks[l],"in-selection"):"after"===this.options.selection&&m>=a[0]?this._addClass(this.ticks[l],"in-selection"):"before"===this.options.selection&&m<=a[0]&&this._addClass(this.ticks[l],"in-selection"),this.tickLabels[l]&&(this.tickLabels[l].style[i]=j+"px","vertical"!==this.options.orientation&&void 0!==this.options.ticks_positions[l]?(this.tickLabels[l].style.position="absolute",this.tickLabels[l].style[this.stylePos]=m+"%",this.tickLabels[l].style[h]=-j/2+"px"):"vertical"===this.options.orientation&&(this.options.rtl?this.tickLabels[l].style.marginRight=this.sliderElem.offsetWidth+"px":this.tickLabels[l].style.marginLeft=this.sliderElem.offsetWidth+"px",this.tickLabelContainer.style[h]=this.sliderElem.offsetWidth/2*-1+"px"),this._removeClass(this.tickLabels[l],"label-in-selection label-is-selection"),this.options.range?m>=a[0]&&m<=a[1]&&(this._addClass(this.tickLabels[l],"label-in-selection"),(m===a[0]||a[1])&&this._addClass(this.tickLabels[l],"label-is-selection")):("after"===this.options.selection&&m>=a[0]?this._addClass(this.tickLabels[l],"label-in-selection"):"before"===this.options.selection&&m<=a[0]&&this._addClass(this.tickLabels[l],"label-in-selection"),m===a[0]&&this._addClass(this.tickLabels[l],"label-is-selection")))}}var n;if(this.options.range){n=this.options.formatter(this._state.value),this._setText(this.tooltipInner,n),this.tooltip.style[this.stylePos]=(a[1]+a[0])/2+"%";var o=this.options.formatter(this._state.value[0]);this._setText(this.tooltipInner_min,o);var p=this.options.formatter(this._state.value[1]);this._setText(this.tooltipInner_max,p),this.tooltip_min.style[this.stylePos]=a[0]+"%",this.tooltip_max.style[this.stylePos]=a[1]+"%"}else n=this.options.formatter(this._state.value[0]),this._setText(this.tooltipInner,n),this.tooltip.style[this.stylePos]=a[0]+"%";if("vertical"===this.options.orientation)this.trackLow.style.top="0",this.trackLow.style.height=Math.min(a[0],a[1])+"%",this.trackSelection.style.top=Math.min(a[0],a[1])+"%",this.trackSelection.style.height=Math.abs(a[0]-a[1])+"%",this.trackHigh.style.bottom="0",this.trackHigh.style.height=100-Math.min(a[0],a[1])-Math.abs(a[0]-a[1])+"%";else{"right"===this.stylePos?this.trackLow.style.right="0":this.trackLow.style.left="0",this.trackLow.style.width=Math.min(a[0],a[1])+"%","right"===this.stylePos?this.trackSelection.style.right=Math.min(a[0],a[1])+"%":this.trackSelection.style.left=Math.min(a[0],a[1])+"%",this.trackSelection.style.width=Math.abs(a[0]-a[1])+"%","right"===this.stylePos?this.trackHigh.style.left="0":this.trackHigh.style.right="0",this.trackHigh.style.width=100-Math.min(a[0],a[1])-Math.abs(a[0]-a[1])+"%";var q=this.tooltip_min.getBoundingClientRect(),r=this.tooltip_max.getBoundingClientRect();"bottom"===this.options.tooltip_position?q.right>r.left?(this._removeClass(this.tooltip_max,"bs-tooltip-bottom"),this._addClass(this.tooltip_max,"bs-tooltip-top"),this.tooltip_max.style.top="",this.tooltip_max.style.bottom="22px"):(this._removeClass(this.tooltip_max,"bs-tooltip-top"),this._addClass(this.tooltip_max,"bs-tooltip-bottom"),this.tooltip_max.style.top=this.tooltip_min.style.top,this.tooltip_max.style.bottom=""):q.right>r.left?(this._removeClass(this.tooltip_max,"bs-tooltip-top"),this._addClass(this.tooltip_max,"bs-tooltip-bottom"),this.tooltip_max.style.top="18px"):(this._removeClass(this.tooltip_max,"bs-tooltip-bottom"),this._addClass(this.tooltip_max,"bs-tooltip-top"),this.tooltip_max.style.top=this.tooltip_min.style.top)}},_createHighlightRange:function(a,b){return this._isHighlightRange(a,b)?a>b?{start:b,size:a-b}:{start:a,size:b-a}:null},_isHighlightRange:function(a,b){return a>=0&&100>=a&&b>=0&&100>=b?!0:!1},_resize:function(a){this._state.offset=this._offset(this.sliderElem),this._state.size=this.sliderElem[this.sizePos],this._layout()},_removeProperty:function(a,b){a.style.removeProperty?a.style.removeProperty(b):a.style.removeAttribute(b)},_mousedown:function(a){if(!this._state.enabled)return!1;a.preventDefault&&a.preventDefault(),this._state.offset=this._offset(this.sliderElem),this._state.size=this.sliderElem[this.sizePos];var b=this._getPercentage(a);if(this.options.range){var c=Math.abs(this._state.percentage[0]-b),d=Math.abs(this._state.percentage[1]-b);this._state.dragged=d>c?0:1,this._adjustPercentageForRangeSliders(b)}else this._state.dragged=0;this._state.percentage[this._state.dragged]=b,this.touchCapable&&(document.removeEventListener("touchmove",this.mousemove,!1),document.removeEventListener("touchend",this.mouseup,!1)),this.mousemove&&document.removeEventListener("mousemove",this.mousemove,!1),this.mouseup&&document.removeEventListener("mouseup",this.mouseup,!1),this.mousemove=this._mousemove.bind(this),this.mouseup=this._mouseup.bind(this),this.touchCapable&&(document.addEventListener("touchmove",this.mousemove,!1),document.addEventListener("touchend",this.mouseup,!1)),document.addEventListener("mousemove",this.mousemove,!1),document.addEventListener("mouseup",this.mouseup,!1),this._state.inDrag=!0;var e=this._calculateValue();return this._trigger("slideStart",e),this.setValue(e,!1,!0),a.returnValue=!1,this.options.focus&&this._triggerFocusOnHandle(this._state.dragged),!0},_touchstart:function(a){this._mousedown(a)},_triggerFocusOnHandle:function(a){0===a&&this.handle1.focus(),1===a&&this.handle2.focus()},_keydown:function(a,b){if(!this._state.enabled)return!1;var c;switch(b.keyCode){case 37:case 40:c=-1;break;case 39:case 38:c=1}if(c){if(this.options.natural_arrow_keys){var d="horizontal"===this.options.orientation,e="vertical"===this.options.orientation,f=this.options.rtl,g=this.options.reversed;d?f?g||(c=-c):g&&(c=-c):e&&(g||(c=-c))}var h;if(this.ticksAreValid&&this.options.lock_to_ticks){var i=void 0;i=this.options.ticks.indexOf(this._state.value[a]),-1===i&&(i=0,window.console.warn("(lock_to_ticks) _keydown: index should not be -1")),i+=c,i=Math.max(0,Math.min(this.options.ticks.length-1,i)),h=this.options.ticks[i]}else h=this._state.value[a]+c*this.options.step;var j=this._toPercentage(h);if(this._state.keyCtrl=a,this.options.range){this._adjustPercentageForRangeSliders(j);var k=this._state.keyCtrl?this._state.value[0]:h,l=this._state.keyCtrl?h:this._state.value[1];h=[Math.max(this.options.min,Math.min(this.options.max,k)),Math.max(this.options.min,Math.min(this.options.max,l))]}else h=Math.max(this.options.min,Math.min(this.options.max,h));return this._trigger("slideStart",h),this.setValue(h,!0,!0),this._trigger("slideStop",h),this._pauseEvent(b),delete this._state.keyCtrl,!1}},_pauseEvent:function(a){a.stopPropagation&&a.stopPropagation(), +a.preventDefault&&a.preventDefault(),a.cancelBubble=!0,a.returnValue=!1},_mousemove:function(a){if(!this._state.enabled)return!1;var b=this._getPercentage(a);this._adjustPercentageForRangeSliders(b),this._state.percentage[this._state.dragged]=b;var c=this._calculateValue(!0);return this.setValue(c,!0,!0),!1},_touchmove:function(a){void 0!==a.changedTouches&&a.preventDefault&&a.preventDefault()},_adjustPercentageForRangeSliders:function(a){if(this.options.range){var b=this._getNumDigitsAfterDecimalPlace(a);b=b?b-1:0;var c=this._applyToFixedAndParseFloat(a,b);0===this._state.dragged&&this._applyToFixedAndParseFloat(this._state.percentage[1],b)c?(this._state.percentage[1]=this._state.percentage[0],this._state.dragged=0):0===this._state.keyCtrl&&this._toPercentage(this._state.value[1])a&&(this._state.percentage[1]=this._state.percentage[0],this._state.keyCtrl=0,this.handle1.focus())}},_mouseup:function(a){if(!this._state.enabled)return!1;var b=this._getPercentage(a);this._adjustPercentageForRangeSliders(b),this._state.percentage[this._state.dragged]=b,this.touchCapable&&(document.removeEventListener("touchmove",this.mousemove,!1),document.removeEventListener("touchend",this.mouseup,!1)),document.removeEventListener("mousemove",this.mousemove,!1),document.removeEventListener("mouseup",this.mouseup,!1),this._state.inDrag=!1,this._state.over===!1&&this._hideTooltip();var c=this._calculateValue(!0);return this.setValue(c,!1,!0),this._trigger("slideStop",c),this._state.dragged=null,!1},_setValues:function(a,b){var c=0===a?0:100;this._state.percentage[a]!==c&&(b.data[a]=this._toValue(this._state.percentage[a]),b.data[a]=this._applyPrecision(b.data[a]))},_calculateValue:function(a){var b={};return this.options.range?(b.data=[this.options.min,this.options.max],this._setValues(0,b),this._setValues(1,b),a&&(b.data[0]=this._snapToClosestTick(b.data[0]),b.data[1]=this._snapToClosestTick(b.data[1]))):(b.data=this._toValue(this._state.percentage[0]),b.data=parseFloat(b.data),b.data=this._applyPrecision(b.data),a&&(b.data=this._snapToClosestTick(b.data))),b.data},_snapToClosestTick:function(a){for(var b=[a,1/0],c=0;ce&&(b=e,c=d)}return c},_setTickIndex:function(){this.ticksAreValid&&(this._state.tickIndex=[this.options.ticks.indexOf(this._state.value[0]),this.options.ticks.indexOf(this._state.value[1])])}},a&&a.fn&&(a.fn.slider?(windowIsDefined&&window.console.warn("bootstrap-slider.js - WARNING: $.fn.slider namespace is already bound. Use the $.fn.bootstrapSlider namespace instead."),f=c):(a.bridget(b,d),f=b),a.bridget(c,d),a(function(){a("input[data-provide=slider]")[f]()}))}(a),d}); \ No newline at end of file diff --git a/views/_main.html b/views/_main.html index 94bf67cbf..0cb46846f 100644 --- a/views/_main.html +++ b/views/_main.html @@ -38,6 +38,7 @@ + {% endblock head_css %} @@ -130,7 +131,7 @@ -