From 11e97b1476fb0b96a9f3ef26f283c7d40d104fd3 Mon Sep 17 00:00:00 2001 From: thempc Date: Fri, 3 Nov 2023 17:39:23 +0100 Subject: [PATCH 1/8] fix: making the anime fields required is instance is for anime --- src/components/Settings/SonarrModal/index.tsx | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/components/Settings/SonarrModal/index.tsx b/src/components/Settings/SonarrModal/index.tsx index 11954734c9..3f3ef8c3d0 100644 --- a/src/components/Settings/SonarrModal/index.tsx +++ b/src/components/Settings/SonarrModal/index.tsx @@ -101,6 +101,7 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { name: Yup.string().required( intl.formatMessage(messages.validationNameRequired) ), + isAnime: Yup.boolean(), hostname: Yup.string() .required(intl.formatMessage(messages.validationHostnameRequired)) .matches( @@ -124,6 +125,24 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { intl.formatMessage(messages.validationLanguageProfileRequired) ) : Yup.number(), + activeAnimeRootFolder: Yup.string().when('isAnime', { + is: true, + then: Yup.string().required( + intl.formatMessage(messages.validationRootFolderRequired) + ), + }), + activeAnimeProfileId: Yup.string().when('isAnime', { + is: true, + then: Yup.string().required( + intl.formatMessage(messages.validationProfileRequired) + ), + }), + activeAnimeLanguageProfileId: Yup.string().when('isAnime', { + is: true, + then: Yup.string().required( + intl.formatMessage(messages.validationLanguageProfileRequired) + ), + }), externalUrl: Yup.string() .matches( /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}(\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*))?$/i, @@ -783,6 +802,9 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => {
@@ -823,6 +845,9 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => {
@@ -852,7 +877,9 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => {
{errors.activeAnimeRootFolder && touched.activeAnimeRootFolder && ( -
{errors.rootFolder}
+
+ {errors.activeAnimeRootFolder} +
)}
@@ -863,6 +890,9 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { className="text-label" > {intl.formatMessage(messages.animelanguageprofile)} + + {values.isAnime ? '*' : ''} +
From 339f0f98bad9c7472689caf3f71cce5f8f6ff28b Mon Sep 17 00:00:00 2001 From: thempc Date: Tue, 31 Oct 2023 23:13:47 +0100 Subject: [PATCH 2/8] fix: adding migration file for isanime field --- ...98786580184-AddMediaRequestIsAnimeField.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 server/migration/1698786580184-AddMediaRequestIsAnimeField.ts diff --git a/server/migration/1698786580184-AddMediaRequestIsAnimeField.ts b/server/migration/1698786580184-AddMediaRequestIsAnimeField.ts new file mode 100644 index 0000000000..3e53e66585 --- /dev/null +++ b/server/migration/1698786580184-AddMediaRequestIsAnimeField.ts @@ -0,0 +1,33 @@ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMediaRequestIsAnimeField1698786580184 + implements MigrationInterface +{ + name = 'AddMediaRequestIsAnimeField1698786580184'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "temporary_media_request" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "type" varchar NOT NULL, "mediaId" integer, "requestedById" integer, "modifiedById" integer, "is4k" boolean NOT NULL DEFAULT (0), "isAnime" boolean NOT NULL DEFAULT (0), "serverId" integer, "profileId" integer, "rootFolder" varchar, "languageProfileId" integer, "tags" text, "isAutoRequest" boolean NOT NULL DEFAULT (0), CONSTRAINT "FK_a1aa713f41c99e9d10c48da75a0" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_6997bee94720f1ecb7f31137095" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_f4fc4efa14c3ba2b29c4525fa15" FOREIGN KEY ("modifiedById") REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_media_request"("id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId", "tags", "isAutoRequest") SELECT "id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId", "tags", "isAutoRequest" FROM "media_request"` + ); + await queryRunner.query(`DROP TABLE "media_request"`); + await queryRunner.query( + `ALTER TABLE "temporary_media_request" RENAME TO "media_request"` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "media_request" RENAME TO "temporary_media_request"` + ); + await queryRunner.query( + `CREATE TABLE "media_request" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "type" varchar NOT NULL, "mediaId" integer, "requestedById" integer, "modifiedById" integer, "is4k" boolean NOT NULL DEFAULT (0), "serverId" integer, "profileId" integer, "rootFolder" varchar, "languageProfileId" integer, "tags" text, "isAutoRequest" boolean NOT NULL DEFAULT (0), CONSTRAINT "FK_a1aa713f41c99e9d10c48da75a0" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_6997bee94720f1ecb7f31137095" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_f4fc4efa14c3ba2b29c4525fa15" FOREIGN KEY ("modifiedById") REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "media_request"("id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId", "tags", "isAutoRequest") SELECT "id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId", "tags", "isAutoRequest" FROM "temporary_media_request"` + ); + await queryRunner.query(`DROP TABLE "temporary_media_request"`); + } +} From 4ad8bc805ce286f54bde691e4f82f0b7a94a6960 Mon Sep 17 00:00:00 2001 From: thempc Date: Tue, 31 Oct 2023 16:22:30 +0100 Subject: [PATCH 3/8] fix: fixing up labels/badges/messages in the services settings tab --- src/components/Settings/RadarrModal/index.tsx | 60 ++++--- src/components/Settings/SettingsServices.tsx | 64 +++++++- src/components/Settings/SonarrModal/index.tsx | 147 +++++++++++------- src/i18n/locale/en.json | 20 ++- 4 files changed, 202 insertions(+), 89 deletions(-) diff --git a/src/components/Settings/RadarrModal/index.tsx b/src/components/Settings/RadarrModal/index.tsx index e63f490525..cad0b9b842 100644 --- a/src/components/Settings/RadarrModal/index.tsx +++ b/src/components/Settings/RadarrModal/index.tsx @@ -20,8 +20,12 @@ type OptionType = { const messages = defineMessages('components.Settings.RadarrModal', { createradarr: 'Add New Radarr Server', create4kradarr: 'Add New 4K Radarr Server', + createAnimeradarr: 'Add New Anime Radarr Server', + create4kAnimeradarr: 'Add New 4K Anime Radarr Server', editradarr: 'Edit Radarr Server', edit4kradarr: 'Edit 4K Radarr Server', + editAnimeradarr: 'Edit Anime Radarr Server', + edit4kAnimeradarr: 'Edit 4K Anime Radarr Server', validationNameRequired: 'You must provide a server name', validationHostnameRequired: 'You must provide a valid hostname or IP address', validationPortRequired: 'You must provide a valid port number', @@ -35,6 +39,8 @@ const messages = defineMessages('components.Settings.RadarrModal', { add: 'Add Server', defaultserver: 'Default Server', default4kserver: 'Default 4K Server', + defaultAnimeserver: 'Default Anime Server', + default4kAnimeserver: 'Default 4K Anime Server', servername: 'Server Name', hostname: 'Hostname or IP Address', port: 'Port', @@ -306,8 +312,8 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => { isSubmitting ? intl.formatMessage(globalMessages.saving) : radarr - ? intl.formatMessage(globalMessages.save) - : intl.formatMessage(messages.add) + ? intl.formatMessage(globalMessages.save) + : intl.formatMessage(messages.add) } secondaryButtonType="warning" secondaryText={ @@ -341,22 +347,36 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => { title={ !radarr ? intl.formatMessage( - values.is4k - ? messages.create4kradarr - : messages.createradarr - ) + values.isAnime && values.is4k + ? messages.create4kAnimeradarr + : values.isAnime + ? messages.createAnimeradarr + : values.is4k + ? messages.create4kradarr + : messages.createradarr + ) : intl.formatMessage( - values.is4k ? messages.edit4kradarr : messages.editradarr - ) + values.isAnime && values.is4k + ? messages.edit4kAnimeradarr + : values.isAnime + ? messages.editAnimeradarr + : values.is4k + ? messages.edit4kradarr + : messages.editradarr + ) } >
@@ -534,10 +554,10 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => { {isTesting ? intl.formatMessage(messages.loadingprofiles) : !isValidated - ? intl.formatMessage( + ? intl.formatMessage( messages.testFirstQualityProfiles ) - : intl.formatMessage(messages.selectQualityProfile)} + : intl.formatMessage(messages.selectQualityProfile)} {testResponse.profiles.length > 0 && testResponse.profiles.map((profile) => ( @@ -574,8 +594,8 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => { {isTesting ? intl.formatMessage(messages.loadingrootfolders) : !isValidated - ? intl.formatMessage(messages.testFirstRootFolders) - : intl.formatMessage(messages.selectRootFolder)} + ? intl.formatMessage(messages.testFirstRootFolders) + : intl.formatMessage(messages.selectRootFolder)} {testResponse.rootFolders.length > 0 && testResponse.rootFolders.map((folder) => ( @@ -635,9 +655,9 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => { options={ isValidated ? testResponse.tags.map((tag) => ({ - label: tag.label, - value: tag.id, - })) + label: tag.label, + value: tag.id, + })) : [] } isMulti @@ -646,8 +666,8 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => { !isValidated ? intl.formatMessage(messages.testFirstTags) : isTesting - ? intl.formatMessage(messages.loadingTags) - : intl.formatMessage(messages.selecttags) + ? intl.formatMessage(messages.loadingTags) + : intl.formatMessage(messages.selecttags) } className="react-select-container" classNamePrefix="react-select" diff --git a/src/components/Settings/SettingsServices.tsx b/src/components/Settings/SettingsServices.tsx index fbe6c75d03..9314eb391e 100644 --- a/src/components/Settings/SettingsServices.tsx +++ b/src/components/Settings/SettingsServices.tsx @@ -31,7 +31,11 @@ const messages = defineMessages('components.Settings', { ssl: 'SSL', default: 'Default', default4k: 'Default 4K', + defaultAnime: 'Default Anime', + default4kAnime: 'Default 4K Anime', is4k: '4K', + isAnime: 'Anime', + is4kAnime: '4K Anime', address: 'Address', activeProfile: 'Active Profile', addradarr: 'Add Radarr Server', @@ -40,6 +44,8 @@ const messages = defineMessages('components.Settings', { 'At least one {serverType} server must be marked as default in order for {mediaType} requests to be processed.', noDefaultNon4kServer: 'If you only have a single {serverType} server for both non-4K and 4K content (or if you only download 4K content), your {serverType} server should NOT be designated as a 4K server.', + noDefaultNonAnimeServer: + 'If you only have a single {serverType} server for both non-Anime and Anime content (or if you only download Anime content), your {serverType} server should NOT be designated as a Anime server.', noDefault4kServer: 'A 4K {serverType} server must be marked as default in order to enable users to submit 4K {mediaType} requests.', mediaTypeMovie: 'movie', @@ -129,14 +135,30 @@ const ServerInstance = ({ {isDefault && !is4k && ( {intl.formatMessage(messages.default)} )} - {isDefault && is4k && ( + {isDefault && is4k && isAnime && ( + {intl.formatMessage(messages.default4kAnime)} + )} + {isDefault && is4k && !isAnime && ( {intl.formatMessage(messages.default4k)} )} - {!isDefault && is4k && ( + {isDefault && !is4k && isAnime && ( + {intl.formatMessage(messages.defaultAnime)} + )} + {!isDefault && is4k && isAnime && ( + + {intl.formatMessage(messages.is4kAnime)} + + )} + {!isDefault && is4k && !isAnime && ( {intl.formatMessage(messages.is4k)} )} + {!isDefault && !is4k && isAnime && ( + + {intl.formatMessage(messages.isAnime)} + + )} {isSSL && ( {intl.formatMessage(messages.ssl)} @@ -350,8 +372,8 @@ const SettingsServices = () => { })} /> ) : !radarrData.some( - (radarr) => radarr.isDefault && !radarr.is4k - ) ? ( + (radarr) => radarr.isDefault && !radarr.is4k + ) ? ( { ), })} /> + ) : !radarrData.some( + (radarr) => radarr.isDefault && !radarr.isAnime + ) ? ( + ( + + {msg} + + ), + })} + /> ) : ( radarrData.some((radarr) => radarr.is4k) && !radarrData.some( @@ -370,7 +405,7 @@ const SettingsServices = () => { ) @@ -386,6 +421,7 @@ const SettingsServices = () => { isSSL={radarr.useSsl} isDefault={radarr.isDefault} is4k={radarr.is4k} + isAnime={radarr.isAnime} externalUrl={radarr.externalUrl} onEdit={() => setEditRadarrModal({ open: true, radarr })} onDelete={() => @@ -438,8 +474,8 @@ const SettingsServices = () => { })} /> ) : !sonarrData.some( - (sonarr) => sonarr.isDefault && !sonarr.is4k - ) ? ( + (sonarr) => sonarr.isDefault && !sonarr.is4k + ) ? ( { ), })} /> + ) : !sonarrData.some( + (sonarr) => sonarr.isDefault && !sonarr.isAnime + ) ? ( + ( + + {msg} + + ), + })} + /> ) : ( sonarrData.some((sonarr) => sonarr.is4k) && !sonarrData.some( @@ -475,6 +524,7 @@ const SettingsServices = () => { isSonarr isDefault={sonarr.isDefault} is4k={sonarr.is4k} + isAnime={sonarr.isAnime} externalUrl={sonarr.externalUrl} onEdit={() => setEditSonarrModal({ open: true, sonarr })} onDelete={() => diff --git a/src/components/Settings/SonarrModal/index.tsx b/src/components/Settings/SonarrModal/index.tsx index 3f3ef8c3d0..6d84851607 100644 --- a/src/components/Settings/SonarrModal/index.tsx +++ b/src/components/Settings/SonarrModal/index.tsx @@ -21,8 +21,12 @@ type OptionType = { const messages = defineMessages('components.Settings.SonarrModal', { createsonarr: 'Add New Sonarr Server', create4ksonarr: 'Add New 4K Sonarr Server', + createAnimesonarr: 'Add New Anime Sonarr Server', + create4kAnimesonarr: 'Add New 4K Anime Sonarr Server', editsonarr: 'Edit Sonarr Server', edit4ksonarr: 'Edit 4K Sonarr Server', + editAnimesonarr: 'Edit Anime Sonarr Server', + edit4kAnimesonarr: 'Edit 4K Anime Sonarr Server', validationNameRequired: 'You must provide a server name', validationHostnameRequired: 'You must provide a valid hostname or IP address', validationPortRequired: 'You must provide a valid port number', @@ -35,6 +39,7 @@ const messages = defineMessages('components.Settings.SonarrModal', { add: 'Add Server', defaultserver: 'Default Server', default4kserver: 'Default 4K Server', + defaultAnimeserver: 'Default Anime Server', servername: 'Server Name', hostname: 'Hostname or IP Address', port: 'Port', @@ -358,8 +363,8 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { isSubmitting ? intl.formatMessage(globalMessages.saving) : sonarr - ? intl.formatMessage(globalMessages.save) - : intl.formatMessage(messages.add) + ? intl.formatMessage(globalMessages.save) + : intl.formatMessage(messages.add) } secondaryButtonType="warning" secondaryText={ @@ -393,22 +398,36 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { title={ !sonarr ? intl.formatMessage( - values.is4k - ? messages.create4ksonarr - : messages.createsonarr - ) + values.isAnime && values.is4k + ? messages.create4kAnimesonarr + : values.isAnime + ? messages.createAnimesonarr + : values.is4k + ? messages.create4ksonarr + : messages.createsonarr + ) : intl.formatMessage( - values.is4k ? messages.edit4ksonarr : messages.editsonarr - ) + values.isAnime && values.is4k + ? messages.edit4kAnimesonarr + : values.isAnime + ? messages.editAnimesonarr + : values.is4k + ? messages.edit4ksonarr + : messages.editsonarr + ) } >
@@ -607,10 +626,10 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { {isTesting ? intl.formatMessage(messages.loadingprofiles) : !isValidated - ? intl.formatMessage( + ? intl.formatMessage( messages.testFirstQualityProfiles ) - : intl.formatMessage(messages.selectQualityProfile)} + : intl.formatMessage(messages.selectQualityProfile)} {testResponse.profiles.length > 0 && testResponse.profiles.map((profile) => ( @@ -647,8 +666,8 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { {isTesting ? intl.formatMessage(messages.loadingrootfolders) : !isValidated - ? intl.formatMessage(messages.testFirstRootFolders) - : intl.formatMessage(messages.selectRootFolder)} + ? intl.formatMessage(messages.testFirstRootFolders) + : intl.formatMessage(messages.selectRootFolder)} {testResponse.rootFolders.length > 0 && testResponse.rootFolders.map((folder) => ( @@ -727,9 +746,9 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { options={ isValidated ? testResponse.tags.map((tag) => ({ - label: tag.label, - value: tag.id, - })) + label: tag.label, + value: tag.id, + })) : [] } isMulti @@ -738,8 +757,8 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { !isValidated ? intl.formatMessage(messages.testFirstTags) : isTesting - ? intl.formatMessage(messages.loadingTags) - : intl.formatMessage(messages.selecttags) + ? intl.formatMessage(messages.loadingTags) + : intl.formatMessage(messages.selecttags) } isLoading={isTesting} className="react-select-container" @@ -748,23 +767,23 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { isTesting ? [] : (values.tags - .map((tagId) => { - const foundTag = testResponse.tags.find( - (tag) => tag.id === tagId - ); + .map((tagId) => { + const foundTag = testResponse.tags.find( + (tag) => tag.id === tagId + ); - if (!foundTag) { - return undefined; - } + if (!foundTag) { + return undefined; + } - return { - value: foundTag.id, - label: foundTag.label, - }; - }) - .filter( - (option) => option !== undefined - ) as OptionType[]) + return { + value: foundTag.id, + label: foundTag.label, + }; + }) + .filter( + (option) => option !== undefined + ) as OptionType[]) } onChange={(value: OnChangeValue) => { setFieldValue( @@ -818,10 +837,10 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { {isTesting ? intl.formatMessage(messages.loadingprofiles) : !isValidated - ? intl.formatMessage( + ? intl.formatMessage( messages.testFirstQualityProfiles ) - : intl.formatMessage(messages.selectQualityProfile)} + : intl.formatMessage(messages.selectQualityProfile)} {testResponse.profiles.length > 0 && testResponse.profiles.map((profile) => ( @@ -861,8 +880,8 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { {isTesting ? intl.formatMessage(messages.loadingrootfolders) : !isValidated - ? intl.formatMessage(messages.testFirstRootFolders) - : intl.formatMessage(messages.selectRootFolder)} + ? intl.formatMessage(messages.testFirstRootFolders) + : intl.formatMessage(messages.selectRootFolder)} {testResponse.rootFolders.length > 0 && testResponse.rootFolders.map((folder) => ( @@ -944,9 +963,9 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { options={ isValidated ? testResponse.tags.map((tag) => ({ - label: tag.label, - value: tag.id, - })) + label: tag.label, + value: tag.id, + })) : [] } isMulti @@ -955,8 +974,8 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { !isValidated ? intl.formatMessage(messages.testFirstTags) : isTesting - ? intl.formatMessage(messages.loadingTags) - : intl.formatMessage(messages.selecttags) + ? intl.formatMessage(messages.loadingTags) + : intl.formatMessage(messages.selecttags) } isLoading={isTesting} className="react-select-container" @@ -965,23 +984,23 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { isTesting ? [] : (values.animeTags - .map((tagId) => { - const foundTag = testResponse.tags.find( - (tag) => tag.id === tagId - ); + .map((tagId) => { + const foundTag = testResponse.tags.find( + (tag) => tag.id === tagId + ); - if (!foundTag) { - return undefined; - } + if (!foundTag) { + return undefined; + } - return { - value: foundTag.id, - label: foundTag.label, - }; - }) - .filter( - (option) => option !== undefined - ) as OptionType[]) + return { + value: foundTag.id, + label: foundTag.label, + }; + }) + .filter( + (option) => option !== undefined + ) as OptionType[]) } onChange={(value) => { setFieldValue( @@ -1002,7 +1021,15 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { > {intl.formatMessage(messages.seasonfolders)} -
+
NOT be designated as a 4K server.", + "components.Settings.noDefaultNonAnimeServer": "If you only have a single {serverType} server for both non-Anime and Anime content (or if you only download Anime content), your {serverType} server should NOT be designated as a Anime server.", "components.Settings.noDefaultServer": "At least one {serverType} server must be marked as default in order for {mediaType} requests to be processed.", "components.Settings.notificationAgentSettingsDescription": "Configure and enable notification agents.", "components.Settings.notifications": "Notifications", @@ -1159,7 +1175,7 @@ "components.Setup.librarieserror": "Validation failed. Please toggle the libraries again to continue.", "components.Setup.servertype": "Choose Server Type", "components.Setup.setup": "Setup", - "components.Setup.signin": "Sign In", + "components.Setup.signin": "Sign in to your account", "components.Setup.signinMessage": "Get started by signing in", "components.Setup.signinWithEmby": "Enter your Emby details", "components.Setup.signinWithJellyfin": "Enter your Jellyfin details", From 814d76c2c6f430c8e37d7298e944f30eb5109689 Mon Sep 17 00:00:00 2001 From: thempc Date: Tue, 31 Oct 2023 00:43:41 +0100 Subject: [PATCH 4/8] fix: fixing bug in the radarr instance having the wrong comparison logic --- server/routes/settings/radarr.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/routes/settings/radarr.ts b/server/routes/settings/radarr.ts index efa5866582..b269750cc4 100644 --- a/server/routes/settings/radarr.ts +++ b/server/routes/settings/radarr.ts @@ -24,7 +24,11 @@ radarrRoutes.post('/', async (req, res) => { // and are the default if (req.body.isDefault) { settings.radarr - .filter((radarrInstance) => radarrInstance.is4k === req.body.is4k) + .filter( + (radarrInstance) => + radarrInstance.is4k === req.body.is4k && + radarrInstance.isAnime === req.body.isAnime + ) .forEach((radarrInstance) => { radarrInstance.isDefault = false; }); From 7b644b0eb59eee295ade1cd0c7e4036d803a1dd4 Mon Sep 17 00:00:00 2001 From: thempc Date: Mon, 30 Oct 2023 16:01:46 +0100 Subject: [PATCH 5/8] fix: fixing issue where there was no fallback from anime requests --- server/entity/MediaRequest.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index a06053745e..b5d0aa525d 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -823,6 +823,14 @@ export class MediaRequest { (radarr) => radarr.isDefault && radarr.is4k === this.is4k ); + // Fallback for requesting anime if there is no default anime server + // This will sent the anime request to the regular default Radarr instance for single-instance setups + if (!radarrSettings && this.isAnime) { + radarrSettings = settings.radarr.find( + (radarr) => radarr.isDefault && radarr.is4k === this.is4k + ); + } + if ( this.serverId !== null && this.serverId >= 0 && @@ -1072,6 +1080,14 @@ export class MediaRequest { (sonarr) => sonarr.isDefault && sonarr.is4k === this.is4k ); + // Fallback for requesting anime if there is no default anime server + // This will sent the anime request to the regular default Sonarr instance for single-instance setups + if (!sonarrSettings && this.isAnime) { + sonarrSettings = settings.sonarr.find( + (sonarr) => sonarr.isDefault && sonarr.is4k === this.is4k + ); + } + if ( this.serverId !== null && this.serverId >= 0 && From 7b2fbc7c904eaf84d0910808fa535d8441f705c5 Mon Sep 17 00:00:00 2001 From: thempc Date: Mon, 30 Oct 2023 14:17:24 +0100 Subject: [PATCH 6/8] feat: adding anime specific Sonarr/Radarr instance support re #2876 --- jellyseerr-api.yml | 20 +++ server/entity/MediaRequest.ts | 52 ++++-- server/interfaces/api/requestInterfaces.ts | 1 + server/interfaces/api/serviceInterfaces.ts | 1 + server/lib/settings/index.ts | 1 + server/routes/service.ts | 4 + server/routes/settings/radarr.ts | 9 +- server/routes/settings/sonarr.ts | 12 +- src/components/RequestBlock/index.tsx | 1 + src/components/RequestCard/index.tsx | 1 + .../RequestList/RequestItem/index.tsx | 1 + .../RequestModal/AdvancedRequester/index.tsx | 16 +- .../RequestModal/MovieRequestModal.tsx | 8 + .../RequestModal/TvRequestModal.tsx | 9 +- src/components/RequestModal/index.tsx | 1 + src/components/Settings/RadarrModal/index.tsx | 68 ++++---- src/components/Settings/SettingsServices.tsx | 33 ++-- src/components/Settings/SonarrModal/index.tsx | 152 +++++++++--------- src/hooks/useRequestOverride.ts | 5 +- src/i18n/locale/en.json | 4 +- 20 files changed, 242 insertions(+), 157 deletions(-) diff --git a/jellyseerr-api.yml b/jellyseerr-api.yml index 6954992d82..0307a78f13 100644 --- a/jellyseerr-api.yml +++ b/jellyseerr-api.yml @@ -467,6 +467,9 @@ components: is4k: type: boolean example: false + isAnime: + type: boolean + example: false minimumAvailability: type: string example: 'In Cinema' @@ -492,6 +495,7 @@ components: - activeProfileName - activeDirectory - is4k + - isAnime - minimumAvailability - isDefault SonarrSettings: @@ -546,6 +550,9 @@ components: is4k: type: boolean example: false + isAnime: + type: boolean + example: false enableSeasonFolders: type: boolean example: false @@ -571,6 +578,7 @@ components: - activeProfileName - activeDirectory - is4k + - isAnime - enableSeasonFolders - isDefault ServarrTag: @@ -1135,6 +1143,9 @@ components: is4k: type: boolean example: false + isAnime: + type: boolean + example: false serverId: type: number profileId: @@ -5743,6 +5754,9 @@ paths: is4k: type: boolean example: false + isAnime: + type: boolean + example: false serverId: type: number profileId: @@ -5847,6 +5861,9 @@ paths: is4k: type: boolean example: false + isAnime: + type: boolean + example: false serverId: type: number profileId: @@ -6505,6 +6522,9 @@ paths: is4k: type: boolean example: false + isAnime: + type: boolean + example: false responses: '200': description: Returned media diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index b5d0aa525d..7d6d3bb991 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -171,6 +171,7 @@ export class MediaRequest { .leftJoin('request.media', 'media') .leftJoinAndSelect('request.requestedBy', 'user') .where('request.is4k = :is4k', { is4k: requestBody.is4k }) + .andWhere('request.isAnime = :isAnime', { isAnime: requestBody.isAnime }) .andWhere('media.tmdbId = :tmdbId', { tmdbId: tmdbMedia.id }) .andWhere('media.mediaType = :mediaType', { mediaType: requestBody.mediaType, @@ -187,6 +188,7 @@ export class MediaRequest { tmdbId: tmdbMedia.id, mediaType: requestBody.mediaType, is4k: requestBody.is4k, + isAnime: requestBody.isAnime, label: 'Media Request', }); @@ -373,6 +375,7 @@ export class MediaRequest { ? user : undefined, is4k: requestBody.is4k, + isAnime: requestBody.isAnime, serverId: requestBody.serverId, profileId: profileId, rootFolder: rootFolder, @@ -404,6 +407,7 @@ export class MediaRequest { .filter( (request) => request.is4k === requestBody.is4k && + request.isAnime === requestBody.isAnime && request.status !== MediaRequestStatus.DECLINED ) .reduce((seasons, request) => { @@ -478,6 +482,7 @@ export class MediaRequest { ? user : undefined, is4k: requestBody.is4k, + isAnime: requestBody.isAnime, serverId: requestBody.serverId, profileId: profileId, rootFolder: rootFolder, @@ -558,6 +563,9 @@ export class MediaRequest { @Column({ default: false }) public is4k: boolean; + @Column({ default: false }) + public isAnime: boolean; + @Column({ nullable: true }) public serverId: number; @@ -820,7 +828,10 @@ export class MediaRequest { } let radarrSettings = settings.radarr.find( - (radarr) => radarr.isDefault && radarr.is4k === this.is4k + (radarr) => + radarr.isDefault && + radarr.is4k === this.is4k && + radarr.isAnime === this.isAnime ); // Fallback for requesting anime if there is no default anime server @@ -852,9 +863,9 @@ export class MediaRequest { if (!radarrSettings) { logger.warn( `There is no default ${ - this.is4k ? '4K ' : '' + this.isAnime ? 'Anime ' : this.is4k ? '4K ' : '' }Radarr server configured. Did you set any of your ${ - this.is4k ? '4K ' : '' + this.isAnime ? 'Anime ' : this.is4k ? '4K ' : '' }Radarr servers as default?`, { label: 'Media Request', @@ -1077,7 +1088,10 @@ export class MediaRequest { } let sonarrSettings = settings.sonarr.find( - (sonarr) => sonarr.isDefault && sonarr.is4k === this.is4k + (sonarr) => + sonarr.isDefault && + sonarr.is4k === this.is4k && + sonarr.isAnime == this.isAnime ); // Fallback for requesting anime if there is no default anime server @@ -1109,9 +1123,9 @@ export class MediaRequest { if (!sonarrSettings) { logger.warn( `There is no default ${ - this.is4k ? '4K ' : '' + this.isAnime ? 'Anime ' : this.is4k ? '4K ' : '' }Sonarr server configured. Did you set any of your ${ - this.is4k ? '4K ' : '' + this.isAnime ? 'Anime ' : this.is4k ? '4K ' : '' }Sonarr servers as default?`, { label: 'Media Request', @@ -1165,11 +1179,7 @@ export class MediaRequest { let seriesType: SonarrSeries['seriesType'] = 'standard'; // Change series type to anime if the anime keyword is present on tmdb - if ( - series.keywords.results.some( - (keyword) => keyword.id === ANIME_KEYWORD_ID - ) - ) { + if (this.isAnime) { seriesType = sonarrSettings.animeSeriesType ?? 'anime'; } @@ -1371,30 +1381,38 @@ export class MediaRequest { switch (type) { case Notification.MEDIA_APPROVED: - event = `${this.is4k ? '4K ' : ''}${mediaType} Request Approved`; + event = `${ + this.isAnime ? 'Anime ' : this.is4k ? '4K ' : '' + }${mediaType} Request Approved`; notifyAdmin = false; break; case Notification.MEDIA_DECLINED: - event = `${this.is4k ? '4K ' : ''}${mediaType} Request Declined`; + event = `${ + this.isAnime ? 'Anime ' : this.is4k ? '4K ' : '' + }${mediaType} Request Declined`; notifyAdmin = false; break; case Notification.MEDIA_PENDING: - event = `New ${this.is4k ? '4K ' : ''}${mediaType} Request`; + event = `New ${ + this.isAnime ? 'Anime ' : this.is4k ? '4K ' : '' + }${mediaType} Request`; break; case Notification.MEDIA_AUTO_REQUESTED: event = `${ - this.is4k ? '4K ' : '' + this.isAnime ? 'Anime ' : this.is4k ? '4K ' : '' }${mediaType} Request Automatically Submitted`; notifyAdmin = false; notifySystem = false; break; case Notification.MEDIA_AUTO_APPROVED: event = `${ - this.is4k ? '4K ' : '' + this.isAnime ? 'Anime ' : this.is4k ? '4K ' : '' }${mediaType} Request Automatically Approved`; break; case Notification.MEDIA_FAILED: - event = `${this.is4k ? '4K ' : ''}${mediaType} Request Failed`; + event = `${ + this.isAnime ? 'Anime ' : this.is4k ? '4K ' : '' + }${mediaType} Request Failed`; break; } diff --git a/server/interfaces/api/requestInterfaces.ts b/server/interfaces/api/requestInterfaces.ts index 88b1201de6..70ed16d1b6 100644 --- a/server/interfaces/api/requestInterfaces.ts +++ b/server/interfaces/api/requestInterfaces.ts @@ -12,6 +12,7 @@ export type MediaRequestBody = { tvdbId?: number; seasons?: number[] | 'all'; is4k?: boolean; + isAnime?: boolean; serverId?: number; profileId?: number; profileName?: string; diff --git a/server/interfaces/api/serviceInterfaces.ts b/server/interfaces/api/serviceInterfaces.ts index 3b430b0b59..6c7de584c6 100644 --- a/server/interfaces/api/serviceInterfaces.ts +++ b/server/interfaces/api/serviceInterfaces.ts @@ -5,6 +5,7 @@ export interface ServiceCommonServer { id: number; name: string; is4k: boolean; + isAnime: boolean; isDefault: boolean; activeProfileId: number; activeDirectory: string; diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts index d85f71379e..7ae1b28738 100644 --- a/server/lib/settings/index.ts +++ b/server/lib/settings/index.ts @@ -71,6 +71,7 @@ export interface DVRSettings { activeDirectory: string; tags: number[]; is4k: boolean; + isAnime: boolean; isDefault: boolean; externalUrl?: string; syncEnabled: boolean; diff --git a/server/routes/service.ts b/server/routes/service.ts index 8f6c92b0de..a353d5bc6c 100644 --- a/server/routes/service.ts +++ b/server/routes/service.ts @@ -19,6 +19,7 @@ serviceRoutes.get('/radarr', async (req, res) => { id: radarr.id, name: radarr.name, is4k: radarr.is4k, + isAnime: radarr.isAnime, isDefault: radarr.isDefault, activeDirectory: radarr.activeDirectory, activeProfileId: radarr.activeProfileId, @@ -59,6 +60,7 @@ serviceRoutes.get<{ radarrId: string }>( id: radarrSettings.id, name: radarrSettings.name, is4k: radarrSettings.is4k, + isAnime: radarrSettings.isAnime, isDefault: radarrSettings.isDefault, activeDirectory: radarrSettings.activeDirectory, activeProfileId: radarrSettings.activeProfileId, @@ -87,6 +89,7 @@ serviceRoutes.get('/sonarr', async (req, res) => { id: sonarr.id, name: sonarr.name, is4k: sonarr.is4k, + isAnime: sonarr.isAnime, isDefault: sonarr.isDefault, activeDirectory: sonarr.activeDirectory, activeProfileId: sonarr.activeProfileId, @@ -137,6 +140,7 @@ serviceRoutes.get<{ sonarrId: string }>( id: sonarrSettings.id, name: sonarrSettings.name, is4k: sonarrSettings.is4k, + isAnime: sonarrSettings.isAnime, isDefault: sonarrSettings.isDefault, activeDirectory: sonarrSettings.activeDirectory, activeProfileId: sonarrSettings.activeProfileId, diff --git a/server/routes/settings/radarr.ts b/server/routes/settings/radarr.ts index b269750cc4..e041494920 100644 --- a/server/routes/settings/radarr.ts +++ b/server/routes/settings/radarr.ts @@ -27,7 +27,8 @@ radarrRoutes.post('/', async (req, res) => { .filter( (radarrInstance) => radarrInstance.is4k === req.body.is4k && - radarrInstance.isAnime === req.body.isAnime + radarrInstance.isAnime && + req.body.isAnime ) .forEach((radarrInstance) => { radarrInstance.isDefault = false; @@ -96,7 +97,11 @@ radarrRoutes.put<{ id: string }, RadarrSettings, RadarrSettings>( // and are the default if (req.body.isDefault) { settings.radarr - .filter((radarrInstance) => radarrInstance.is4k === req.body.is4k) + .filter( + (radarrInstance) => + radarrInstance.is4k === req.body.is4k && + radarrInstance.isAnime === req.body.isAnime + ) .forEach((radarrInstance) => { radarrInstance.isDefault = false; }); diff --git a/server/routes/settings/sonarr.ts b/server/routes/settings/sonarr.ts index 84bf4d7932..98c4c16651 100644 --- a/server/routes/settings/sonarr.ts +++ b/server/routes/settings/sonarr.ts @@ -24,7 +24,11 @@ sonarrRoutes.post('/', async (req, res) => { // and are the default if (req.body.isDefault) { settings.sonarr - .filter((sonarrInstance) => sonarrInstance.is4k === req.body.is4k) + .filter( + (sonarrInstance) => + sonarrInstance.is4k === req.body.is4k && + sonarrInstance.isAnime === req.body.isAnime + ) .forEach((sonarrInstance) => { sonarrInstance.isDefault = false; }); @@ -91,7 +95,11 @@ sonarrRoutes.put<{ id: string }>('/:id', async (req, res) => { // and are the default if (req.body.isDefault) { settings.sonarr - .filter((sonarrInstance) => sonarrInstance.is4k === req.body.is4k) + .filter( + (sonarrInstance) => + sonarrInstance.is4k === req.body.is4k && + sonarrInstance.isAnime === req.body.isAnime + ) .forEach((sonarrInstance) => { sonarrInstance.isDefault = false; }); diff --git a/src/components/RequestBlock/index.tsx b/src/components/RequestBlock/index.tsx index 7c7494de89..5494ede8d5 100644 --- a/src/components/RequestBlock/index.tsx +++ b/src/components/RequestBlock/index.tsx @@ -87,6 +87,7 @@ const RequestBlock = ({ request, onUpdate }: RequestBlockProps) => { tmdbId={request.media.tmdbId} type={request.type} is4k={request.is4k} + isAnime={request.isAnime} editRequest={request} onCancel={() => setShowEditModal(false)} onComplete={() => { diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx index e936d98e12..7cc3213c82 100644 --- a/src/components/RequestCard/index.tsx +++ b/src/components/RequestCard/index.tsx @@ -337,6 +337,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => { tmdbId={request.media.tmdbId} type={request.type} is4k={request.is4k} + isAnime={request.isAnime} editRequest={request} onCancel={() => setShowEditModal(false)} onComplete={() => { diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx index 037590e5d2..517ccbc33e 100644 --- a/src/components/RequestList/RequestItem/index.tsx +++ b/src/components/RequestList/RequestItem/index.tsx @@ -440,6 +440,7 @@ const RequestItem = ({ tmdbId={request.media.tmdbId} type={request.type} is4k={request.is4k} + isAnime={request.isAnime} editRequest={request} onCancel={() => setShowEditModal(false)} onComplete={() => { diff --git a/src/components/RequestModal/AdvancedRequester/index.tsx b/src/components/RequestModal/AdvancedRequester/index.tsx index ad11db82e7..4458f2445f 100644 --- a/src/components/RequestModal/AdvancedRequester/index.tsx +++ b/src/components/RequestModal/AdvancedRequester/index.tsx @@ -154,7 +154,8 @@ const AdvancedRequester = ({ useEffect(() => { let defaultServer = data?.find( - (server) => server.isDefault && is4k === server.is4k + (server) => + server.isDefault && is4k === server.is4k && isAnime === server.isAnime ); if (!defaultServer && (data ?? []).length > 0) { @@ -295,7 +296,9 @@ const AdvancedRequester = ({ if ( (!data || selectedServer === null || - (data.filter((server) => server.is4k === is4k).length < 2 && + (data.filter( + (server) => server.is4k === is4k && server.isAnime === isAnime + ).length < 2 && (!serverData || (serverData.profiles.length < 2 && serverData.rootFolders.length < 2 && @@ -314,7 +317,9 @@ const AdvancedRequester = ({
{!!data && selectedServer !== null && (
- {data.filter((server) => server.is4k === is4k).length > 1 && ( + {data.filter( + (server) => server.is4k === is4k && server.isAnime === isAnime + ).length > 1 && (