diff --git a/bazarr/app/database.py b/bazarr/app/database.py index 3780befea..b931c5f0f 100644 --- a/bazarr/app/database.py +++ b/bazarr/app/database.py @@ -528,3 +528,32 @@ def upgrade_languages_profile_hi_values(): .values({"items": json.dumps(items)}) .where(TableLanguagesProfiles.profileId == languages_profile.profileId) ) + + +def fix_languages_profiles_with_duplicate_ids(): + languages_profiles = database.execute( + select(TableLanguagesProfiles.profileId, TableLanguagesProfiles.items, TableLanguagesProfiles.cutoff)).all() + for languages_profile in languages_profiles: + if languages_profile.cutoff: + # ignore profiles that have a cutoff set + continue + languages_profile_ids = [] + languages_profile_has_duplicate = False + languages_profile_items = json.loads(languages_profile.items) + for items in languages_profile_items: + if items['id'] in languages_profile_ids: + languages_profile_has_duplicate = True + break + else: + languages_profile_ids.append(items['id']) + + if languages_profile_has_duplicate: + item_id = 0 + for items in languages_profile_items: + item_id += 1 + items['id'] = item_id + database.execute( + update(TableLanguagesProfiles) + .values({"items": json.dumps(languages_profile_items)}) + .where(TableLanguagesProfiles.profileId == languages_profile.profileId) + ) diff --git a/bazarr/main.py b/bazarr/main.py index 8fe9a43fd..c86f5a7b4 100644 --- a/bazarr/main.py +++ b/bazarr/main.py @@ -35,7 +35,8 @@ else: # there's missing embedded packages after a commit check_if_new_update() -from app.database import System, database, update, migrate_db, create_db_revision, upgrade_languages_profile_hi_values # noqa E402 +from app.database import (System, database, update, migrate_db, create_db_revision, upgrade_languages_profile_hi_values, + fix_languages_profiles_with_duplicate_ids) # noqa E402 from app.notifier import update_notifier # noqa E402 from languages.get_languages import load_language_in_db # noqa E402 from app.signalr_client import sonarr_signalr_client, radarr_signalr_client # noqa E402 @@ -50,6 +51,7 @@ if args.create_db_revision: else: migrate_db(app) upgrade_languages_profile_hi_values() + fix_languages_profiles_with_duplicate_ids() configure_proxy_func() diff --git a/bazarr/utilities/health.py b/bazarr/utilities/health.py index 36b1625f1..c1d3a6a3d 100644 --- a/bazarr/utilities/health.py +++ b/bazarr/utilities/health.py @@ -1,7 +1,9 @@ # coding=utf-8 +import json + from app.config import settings -from app.database import TableShowsRootfolder, TableMoviesRootfolder, database, select +from app.database import TableShowsRootfolder, TableMoviesRootfolder, TableLanguagesProfiles, database, select from app.event_handler import event_stream from .path_mappings import path_mappings from sonarr.rootfolder import check_sonarr_rootfolder @@ -47,4 +49,21 @@ def get_health_issues(): health_issues.append({'object': path_mappings.path_replace_movie(item.path), 'issue': item.error}) + # get languages profiles duplicate ids issues when there's a cutoff set + languages_profiles = database.execute( + select(TableLanguagesProfiles.items, TableLanguagesProfiles.name, TableLanguagesProfiles.cutoff)).all() + for languages_profile in languages_profiles: + if not languages_profile.cutoff: + # ignore profiles that don't have a cutoff set + continue + languages_profile_ids = [] + for items in json.loads(languages_profile.items): + if items['id'] in languages_profile_ids: + health_issues.append({'object': languages_profile.name, + 'issue': 'This languages profile has duplicate IDs. You need to edit this profile' + ' and make sure to select the proper cutoff if required.'}) + break + else: + languages_profile_ids.append(items['id']) + return health_issues diff --git a/frontend/src/Router/index.tsx b/frontend/src/Router/index.tsx index d600fc87d..8ccea87f9 100644 --- a/frontend/src/Router/index.tsx +++ b/frontend/src/Router/index.tsx @@ -270,6 +270,7 @@ function useRoutes(): CustomRouteObject[] { { path: "status", name: "Status", + badge: data?.status, element: ( @@ -309,6 +310,7 @@ function useRoutes(): CustomRouteObject[] { data?.sonarr_signalr, data?.radarr_signalr, data?.announcements, + data?.status, radarr, sonarr, ], diff --git a/frontend/src/pages/Settings/Languages/table.tsx b/frontend/src/pages/Settings/Languages/table.tsx index 03971a5cc..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, @@ -79,10 +79,10 @@ const Table: FunctionComponent = () => { }) => { return ( - {items.map((v) => { + {items.map((v, i) => { const isCutoff = v.id === cutoff || cutoff === anyCutoff; return ( - + ); })} @@ -148,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, }); }}