diff --git a/bazarr/app/database.py b/bazarr/app/database.py index 7c1663b67..148ceede5 100644 --- a/bazarr/app/database.py +++ b/bazarr/app/database.py @@ -513,10 +513,10 @@ def upgrade_languages_profile_hi_values(): .all(): items = json.loads(languages_profile.items) for language in items: - if language['hi'] == "True": - language['hi'] = "only" - elif language['hi'] == "False": - language['hi'] = "also" + if language['hi'] == "only": + language['hi'] = "True" + elif language['hi'] == "also": + language['hi'] = "False" database.execute( update(TableLanguagesProfiles) .values({"items": json.dumps(items)}) diff --git a/bazarr/subtitles/download.py b/bazarr/subtitles/download.py index c22d6007f..5f588fbf6 100644 --- a/bazarr/subtitles/download.py +++ b/bazarr/subtitles/download.py @@ -41,6 +41,10 @@ def generate_subtitles(path, languages, audio_language, sceneName, title, media_ providers = pool.providers language_set = _get_language_obj(languages=languages) + hi_required = "force HI" if any([x.hi for x in language_set]) else False + also_forced = any([x.forced for x in language_set]) + forced_required = all([x.forced for x in language_set]) + _set_forced_providers(pool=pool, also_forced=also_forced, forced_required=forced_required) video = get_video(force_unicode(path), title, sceneName, providers=providers, media_type=media_type) @@ -56,9 +60,6 @@ def generate_subtitles(path, languages, audio_language, sceneName, title, media_ if forced_minimum_score: min_score = int(forced_minimum_score) + 1 for language in language_set: - # check if language appears more than one time which means that the languages profile requires normal - # and hi subtitles (also) - also_hi = len(set([x.hi for x in language_set if x.alpha3 == language.alpha3 and not x.forced])) > 1 # confirm if language is still missing or if cutoff has been reached if check_if_still_required and language not in check_missing_languages(path, media_type): # cutoff has been reached @@ -66,13 +67,11 @@ def generate_subtitles(path, languages, audio_language, sceneName, title, media_ f"has been reached during this search.") continue else: - hi_required = "force HI" if language.hi else "force non-HI" - _set_forced_providers(pool=pool, also_forced=language.forced, forced_required=language.forced) downloaded_subtitles = download_best_subtitles(videos={video}, languages={language}, pool_instance=pool, min_score=int(min_score), - hearing_impaired=False if also_hi else hi_required, + hearing_impaired=hi_required, compute_score=ComputeScore(get_scores())) if downloaded_subtitles: diff --git a/bazarr/subtitles/indexer/movies.py b/bazarr/subtitles/indexer/movies.py index 683530387..ba105efc3 100644 --- a/bazarr/subtitles/indexer/movies.py +++ b/bazarr/subtitles/indexer/movies.py @@ -233,26 +233,18 @@ def list_missing_subtitles_movies(no=None, send_event=True): # get difference between desired and existing subtitles missing_subtitles_list = [] for item in desired_subtitles_list: - if item['forced'] == 'True': - desired_item = {'language': item['language'], 'forced': item['forced'], 'hi': 'False'} - if desired_item not in actual_subtitles_list: - missing_subtitles_list.append(desired_item) - elif item['hi'] == 'never': - desired_item = {'language': item['language'], 'forced': item['forced'], 'hi': 'False'} - if desired_item not in actual_subtitles_list: - missing_subtitles_list.append(desired_item) - elif item['hi'] == 'only': - desired_item = {'language': item['language'], 'forced': item['forced'], 'hi': 'True'} - if desired_item not in actual_subtitles_list: - missing_subtitles_list.append(desired_item) - elif item['hi'] == 'also': - desired_items = [{'language': item['language'], 'forced': item['forced'], 'hi': 'True'}, - {'language': item['language'], 'forced': item['forced'], 'hi': 'False'}] - if [x for x in desired_items if x in actual_subtitles_list]: - continue - else: - for desired_item in desired_items: - missing_subtitles_list.append(desired_item) + if item not in actual_subtitles_list: + missing_subtitles_list.append(item) + + # remove missing that have forced or hi subtitles for this language in existing + for item in actual_subtitles_list: + if item['hi'] == 'True': + try: + missing_subtitles_list.remove({'language': item['language'], + 'forced': 'False', + 'hi': 'False'}) + except ValueError: + pass # make the missing languages list looks like expected missing_subtitles_output_list = [] diff --git a/bazarr/subtitles/indexer/series.py b/bazarr/subtitles/indexer/series.py index 33bf3f688..6b8501dfe 100644 --- a/bazarr/subtitles/indexer/series.py +++ b/bazarr/subtitles/indexer/series.py @@ -235,26 +235,18 @@ def list_missing_subtitles(no=None, epno=None, send_event=True): # get difference between desired and existing subtitles missing_subtitles_list = [] for item in desired_subtitles_list: - if item['forced'] == 'True': - desired_item = {'language': item['language'], 'forced': item['forced'], 'hi': 'False'} - if desired_item not in actual_subtitles_list: - missing_subtitles_list.append(desired_item) - elif item['hi'] == 'never': - desired_item = {'language': item['language'], 'forced': item['forced'], 'hi': 'False'} - if desired_item not in actual_subtitles_list: - missing_subtitles_list.append(desired_item) - elif item['hi'] == 'only': - desired_item = {'language': item['language'], 'forced': item['forced'], 'hi': 'True'} - if desired_item not in actual_subtitles_list: - missing_subtitles_list.append(desired_item) - elif item['hi'] == 'also': - desired_items = [{'language': item['language'], 'forced': item['forced'], 'hi': 'True'}, - {'language': item['language'], 'forced': item['forced'], 'hi': 'False'}] - if [x for x in desired_items if x in actual_subtitles_list]: - continue - else: - for desired_item in desired_items: - missing_subtitles_list.append(desired_item) + if item not in actual_subtitles_list: + missing_subtitles_list.append(item) + + # remove missing that have hi subtitles for this language in existing + for item in actual_subtitles_list: + if item['hi'] == 'True': + try: + missing_subtitles_list.remove({'language': item['language'], + 'forced': 'False', + 'hi': 'False'}) + except ValueError: + pass # make the missing languages list looks like expected missing_subtitles_output_list = [] diff --git a/bazarr/subtitles/manual.py b/bazarr/subtitles/manual.py index ca8dd42b4..ba57eb193 100644 --- a/bazarr/subtitles/manual.py +++ b/bazarr/subtitles/manual.py @@ -31,9 +31,9 @@ def manual_search(path, profile_id, providers, sceneName, title, media_type): pool = _get_pool(media_type, profile_id) - language_set, original_format = _get_language_obj(profile_id=profile_id) - also_forced = any([x.forced for x in language_set]) - forced_required = all([x.forced for x in language_set]) + language_set, initial_language_set, original_format = _get_language_obj(profile_id=profile_id) + also_forced = any([x.forced for x in initial_language_set]) + forced_required = all([x.forced for x in initial_language_set]) compute_score = ComputeScore(get_scores()) _set_forced_providers(pool=pool, also_forced=also_forced, forced_required=forced_required) @@ -57,10 +57,6 @@ def manual_search(path, profile_id, providers, sceneName, title, media_type): minimum_score_movie = settings.general.minimum_score_movie for s in subtitles[video]: - if s.language not in language_set: - # HI may not be matching what was requested - continue - try: matches = s.get_matches(video) except AttributeError: @@ -79,8 +75,20 @@ def manual_search(path, profile_id, providers, sceneName, title, media_type): logging.debug("BAZARR Ignoring invalid subtitles") continue + initial_hi = None + initial_hi_match = False + for language in initial_language_set: + if s.language.basename == language.basename and \ + s.language.forced == language.forced and \ + s.language.hi == language.hi: + initial_hi = language.hi + initial_hi_match = True + break + if not initial_hi_match: + initial_hi = None + _, max_score, scores = _get_scores(media_type, minimum_score_movie, minimum_score) - score, score_without_hash = compute_score(matches, s, video, hearing_impaired=s.language.hi) + score, score_without_hash = compute_score(matches, s, video, hearing_impaired=initial_hi) if 'hash' not in matches: not_matched = scores - matches s.score = score_without_hash @@ -88,6 +96,11 @@ def manual_search(path, profile_id, providers, sceneName, title, media_type): s.score = score not_matched = set() + if s.hearing_impaired == initial_hi: + matches.add('hearing_impaired') + else: + not_matched.add('hearing_impaired') + releases = [] if hasattr(s, 'release_info'): if s.release_info is not None: @@ -202,6 +215,7 @@ def manual_download_subtitle(path, audio_language, hi, forced, subtitle, provide def _get_language_obj(profile_id): + initial_language_set = set() language_set = set() profile = get_profiles_list(profile_id=int(profile_id)) @@ -218,13 +232,22 @@ def _get_language_obj(profile_id): lang_obj = _get_lang_obj(lang) if forced == "True": - language_set.add(Language.rebuild(lang_obj, forced=True)) - elif hi == "only": - language_set.add(Language.rebuild(lang_obj, hi=True)) - elif hi == "also": - language_set.add(lang_obj) - language_set.add(Language.rebuild(lang_obj, hi=True)) + lang_obj = Language.rebuild(lang_obj, forced=True) + + if hi == "True": + lang_obj = Language.rebuild(lang_obj, hi=True) + + initial_language_set.add(lang_obj) + + language_set = initial_language_set.copy() + for language in language_set.copy(): + lang_obj_for_hi = language + if not language.forced and not language.hi: + lang_obj_hi = Language.rebuild(lang_obj_for_hi, hi=True) + elif not language.forced and language.hi: + lang_obj_hi = Language.rebuild(lang_obj_for_hi, hi=False) else: - language_set.add(lang_obj) + continue + language_set.add(lang_obj_hi) - return language_set, original_format + return language_set, initial_language_set, original_format diff --git a/frontend/src/components/forms/ProfileEditForm.tsx b/frontend/src/components/forms/ProfileEditForm.tsx index 0a6471dc5..e994888d2 100644 --- a/frontend/src/components/forms/ProfileEditForm.tsx +++ b/frontend/src/components/forms/ProfileEditForm.tsx @@ -12,7 +12,6 @@ import { } from "@mantine/core"; import { useForm } from "@mantine/form"; import { faTrash } from "@fortawesome/free-solid-svg-icons"; -import { cond } from "lodash"; import { Action, Selector, SelectorOption, SimpleTable } from "@/components"; import ChipInput from "@/components/inputs/ChipInput"; import { useModals, withModal } from "@/modules/modals"; @@ -31,7 +30,7 @@ const defaultCutoffOptions: SelectorOption[] = [ // eslint-disable-next-line camelcase audio_exclude: "False", forced: "False", - hi: "also", + hi: "False", language: "any", }, }, @@ -39,16 +38,12 @@ const defaultCutoffOptions: SelectorOption[] = [ const subtitlesTypeOptions: SelectorOption[] = [ { - label: "Normal only", - value: "never", - }, - { - label: "Hearing-impaired only", - value: "only", + label: "Normal or hearing-impaired", + value: "normal", }, { - label: "Normal or hearing-impaired", - value: "also", + label: "Hearing-impaired required", + value: "hi", }, { label: "Forced (foreign part only)", @@ -88,14 +83,10 @@ const ProfileEditForm: FunctionComponent = ({ const itemCutoffOptions = useSelectorOptions( form.values.items, (v) => { - const suffix = cond([ - [(v: { forced: string; hi: string }) => v.hi === "only", () => ":hi"], - [(v) => v.hi === "never", () => ":normal"], - [(v) => v.forced === "True", () => ":forced"], - [() => true, () => ""], - ]); + const suffix = + v.hi === "True" ? ":hi" : v.forced === "True" ? ":forced" : ""; - return v.language + suffix(v); + return v.language + suffix; }, (v) => String(v.id), ); @@ -145,7 +136,7 @@ const ProfileEditForm: FunctionComponent = ({ language, // eslint-disable-next-line camelcase audio_exclude: "False", - hi: "also", + hi: "False", forced: "False", }; @@ -193,8 +184,10 @@ const ProfileEditForm: FunctionComponent = ({ const selectValue = useMemo(() => { if (item.forced === "True") { return "forced"; + } else if (item.hi === "True") { + return "hi"; } else { - return item.hi; + return "normal"; } }, [item.forced, item.hi]); @@ -206,9 +199,9 @@ const ProfileEditForm: FunctionComponent = ({ if (value) { action.mutate(index, { ...item, - hi: value, + hi: value === "hi" ? "True" : "False", forced: value === "forced" ? "True" : "False", - } as Language.ProfileItem); + }); } }} > diff --git a/frontend/src/pages/Settings/Languages/table.tsx b/frontend/src/pages/Settings/Languages/table.tsx index 29902a546..c2ed9d968 100644 --- a/frontend/src/pages/Settings/Languages/table.tsx +++ b/frontend/src/pages/Settings/Languages/table.tsx @@ -2,7 +2,7 @@ import { FunctionComponent, useCallback, useMemo } from "react"; import { Column } from "react-table"; import { Badge, Button, Group } from "@mantine/core"; import { faTrash, faWrench } from "@fortawesome/free-solid-svg-icons"; -import { cloneDeep, cond } from "lodash"; +import { cloneDeep } from "lodash"; import { Action, SimpleTable } from "@/components"; import { anyCutoff, @@ -194,14 +194,14 @@ interface ItemProps { const ItemBadge: FunctionComponent = ({ cutoff, item }) => { const text = useMemo(() => { - const suffix = cond([ - [(v: { forced: string; hi: string }) => v.hi === "only", () => ":HI"], - [(v) => v.hi === "never", () => ":Normal"], - [(v) => v.forced === "True", () => ":Forced"], - [() => true, () => ""], - ]); - return item.language + suffix(item); - }, [item]); + let result = item.language; + if (item.hi === "True") { + result += ":HI"; + } else if (item.forced === "True") { + result += ":Forced"; + } + return result; + }, [item.hi, item.forced, item.language]); return ( (({ language: code, hi, forced }) => { const name = data?.find((v) => v.code2 === code)?.name ?? ""; return { - hi: hi === "only", + hi: hi === "True", forced: forced === "True", code2: code, name,