diff options
Diffstat (limited to 'frontend/src/pages/Settings/Languages')
-rw-r--r-- | frontend/src/pages/Settings/Languages/index.tsx | 46 | ||||
-rw-r--r-- | frontend/src/pages/Settings/Languages/table.tsx | 49 |
2 files changed, 91 insertions, 4 deletions
diff --git a/frontend/src/pages/Settings/Languages/index.tsx b/frontend/src/pages/Settings/Languages/index.tsx index 9fe562920..1bd9d72a8 100644 --- a/frontend/src/pages/Settings/Languages/index.tsx +++ b/frontend/src/pages/Settings/Languages/index.tsx @@ -1,7 +1,9 @@ import { FunctionComponent } from "react"; +import { Text as MantineText } from "@mantine/core"; import { useLanguageProfiles, useLanguages } from "@/apis/hooks"; import { Check, + Chips, CollapseBox, Layout, Message, @@ -115,6 +117,50 @@ const SettingsLanguagesView: FunctionComponent = () => { <Section header="Languages Profile"> <Table></Table> </Section> + <Section header="Tag-Based Automatic Language Profile Selection Settings"> + <Message> + If enabled, Bazarr will look at the names of all tags of a Series from + Sonarr (or a Movie from Radarr) to find a matching Bazarr language + profile tag. It will use as the language profile the FIRST tag from + Sonarr/Radarr that matches the tag of a Bazarr language profile + EXACTLY. If multiple tags match, there is no guarantee as to which one + will be used, so choose your tag names carefully. Also, if you update + the tag names in Sonarr/Radarr, Bazarr will detect this and repeat the + matching process for the affected shows. However, if a show's only + matching tag is removed from Sonarr/Radarr, Bazarr will NOT remove the + show's existing language profile for that reason. But if you wish to + have language profiles removed automatically by tag value, simply + enter a list of one or more tags in the{" "} + <MantineText fw={700} span> + Remove Profile Tags + </MantineText>{" "} + entry list below. If your video tag matches one of the tags in that + list, then Bazarr will remove the language profile for that video. If + there is a conflict between profile selection and profile removal, + then profile removal wins out and is performed. + </Message> + <Check + label="Series" + settingKey="settings-general-serie_tag_enabled" + ></Check> + <Check + label="Movies" + settingKey="settings-general-movie_tag_enabled" + ></Check> + <Chips + label="Remove Profile Tags" + settingKey="settings-general-remove_profile_tags" + sanitizeFn={(values: string[] | null) => + values?.map((item) => + item.replace(/[^a-z0-9_-]/gi, "").toLowerCase(), + ) + } + ></Chips> + <Message> + Enter tag values that will trigger a language profile removal. Leave + empty if you don't want Bazarr to remove language profiles. + </Message> + </Section> <Section header="Default Settings"> <Check label="Series" diff --git a/frontend/src/pages/Settings/Languages/table.tsx b/frontend/src/pages/Settings/Languages/table.tsx index c32300628..5cfefdfa9 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 { Badge, Button, Group } from "@mantine/core"; import { faTrash, faWrench } from "@fortawesome/free-solid-svg-icons"; import { ColumnDef } from "@tanstack/react-table"; -import { cloneDeep } from "lodash"; +import { cloneDeep, includes, maxBy } from "lodash"; import { Action } from "@/components"; import { anyCutoff, @@ -66,6 +66,10 @@ const Table: FunctionComponent = () => { accessorKey: "name", }, { + header: "Tag", + accessorKey: "tag", + }, + { header: "Languages", accessorKey: "items", cell: ({ @@ -75,10 +79,10 @@ const Table: FunctionComponent = () => { }) => { return ( <Group gap="xs" wrap="nowrap"> - {items.map((v) => { + {items.map((v, i) => { const isCutoff = v.id === cutoff || cutoff === anyCutoff; return ( - <ItemBadge key={v.id} cutoff={isCutoff} item={v}></ItemBadge> + <ItemBadge key={i} cutoff={isCutoff} item={v}></ItemBadge> ); })} </Group> @@ -144,9 +148,45 @@ const Table: FunctionComponent = () => { icon={faWrench} c="gray" onClick={() => { + const lastId = maxBy(profile.items, "id")?.id || 0; + + // We once had an issue on the past where there were duplicated + // item ids that needs to become unique upon editing. + const sanitizedProfile = { + ...cloneDeep(profile), + items: profile.items.reduce( + (acc, value) => { + const { ids, duplicatedIds, items } = acc; + + // We once had an issue on the past where there were duplicated + // item ids that needs to become unique upon editing. + if (includes(ids, value.id)) { + duplicatedIds.push(value.id); + items.push({ + ...value, + id: lastId + duplicatedIds.length, + }); + + return acc; + } + + ids.push(value.id); + items.push(value); + + return acc; + }, + { + ids: [] as number[], + duplicatedIds: [] as number[], + items: [] as typeof profile.items, + }, + ).items, + tag: profile.tag || undefined, + }; + modals.openContextModal(ProfileEditModal, { languages, - profile: cloneDeep(profile), + profile: sanitizedProfile, onComplete: updateProfile, }); }} @@ -178,6 +218,7 @@ const Table: FunctionComponent = () => { const profile = { profileId: nextProfileId, name: "", + tag: undefined, items: [], cutoff: null, mustContain: [], |