fix: properly fetch sonarr/radarr specific override rules (#1199)

* fix: properly fetch sonarr/radarr specific override rules and fix its application

- This will fetch the proper sonarr/radarr specific override rule to apply.
- This will skip override rules for anime TV shows unless the `overrideRule`
explicitly includes the anime keyword.
- Apply the most specific override rule first (e.g., rules with multiple
conditions like `genre`, `language`, and `keywords`)
- Debug logs to for override rules

* fix(overriderules): apply overrides to "auto_approve" permission users but not "advaned_request"

This decision is done because it makes no sense to give advanced request users who gets to choose
what values to choose but then the minute they request, it gets overridden, rendering the whole
modal completely useless. In addition, admin/manage_request permission users who modify requests,
the minute they modify it will get overridden as well so it makes no sense to override their
requests

* fix: use default service instance for override rules

---------

Co-authored-by: Gauthier <mail@gauthierth.fr>
This commit is contained in:
Fallenbagel
2024-12-30 20:14:29 +08:00
committed by GitHub
parent f8a8ebdf76
commit 814a7357c0

View File

@@ -7,6 +7,7 @@ import type {
import SonarrAPI from '@server/api/servarr/sonarr'; import SonarrAPI from '@server/api/servarr/sonarr';
import TheMovieDb from '@server/api/themoviedb'; import TheMovieDb from '@server/api/themoviedb';
import { ANIME_KEYWORD_ID } from '@server/api/themoviedb/constants'; import { ANIME_KEYWORD_ID } from '@server/api/themoviedb/constants';
import type { TmdbKeyword } from '@server/api/themoviedb/interfaces';
import { import {
MediaRequestStatus, MediaRequestStatus,
MediaStatus, MediaStatus,
@@ -207,28 +208,50 @@ export class MediaRequest {
} }
} }
// Apply overrides if the user is not an admin or has the "auto approve" permission // Apply overrides if the user is not an admin or has the "advanced request" permission
const useOverrides = !user.hasPermission( const useOverrides = !user.hasPermission([Permission.MANAGE_REQUESTS], {
[ type: 'or',
requestBody.is4k ? Permission.AUTO_APPROVE_4K : Permission.AUTO_APPROVE, });
Permission.MANAGE_REQUESTS,
],
{ type: 'or' }
);
let rootFolder = requestBody.rootFolder; let rootFolder = requestBody.rootFolder;
let profileId = requestBody.profileId; let profileId = requestBody.profileId;
let tags = requestBody.tags; let tags = requestBody.tags;
if (useOverrides) { if (useOverrides) {
const defaultRadarrId = requestBody.is4k
? settings.radarr.findIndex((r) => r.is4k && r.isDefault)
: settings.radarr.findIndex((r) => !r.is4k && r.isDefault);
const defaultSonarrId = requestBody.is4k
? settings.sonarr.findIndex((s) => s.is4k && s.isDefault)
: settings.sonarr.findIndex((s) => !s.is4k && s.isDefault);
const overrideRuleRepository = getRepository(OverrideRule); const overrideRuleRepository = getRepository(OverrideRule);
const overrideRules = await overrideRuleRepository.find({ const overrideRules = await overrideRuleRepository.find({
where: where:
requestBody.mediaType === MediaType.MOVIE requestBody.mediaType === MediaType.MOVIE
? { radarrServiceId: requestBody.serverId } ? { radarrServiceId: defaultRadarrId }
: { sonarrServiceId: requestBody.serverId }, : { sonarrServiceId: defaultSonarrId },
}); });
const appliedOverrideRules = overrideRules.filter((rule) => { const appliedOverrideRules = overrideRules.filter((rule) => {
const hasAnimeKeyword =
'results' in tmdbMedia.keywords &&
tmdbMedia.keywords.results.some(
(keyword: TmdbKeyword) => keyword.id === ANIME_KEYWORD_ID
);
// Skip override rules if the media is an anime TV show as anime TV
// is handled by default and override rules do not explicitly include
// the anime keyword
if (
requestBody.mediaType === MediaType.TV &&
hasAnimeKeyword &&
(!rule.keywords ||
!rule.keywords.split(',').map(Number).includes(ANIME_KEYWORD_ID))
) {
return false;
}
if ( if (
rule.users && rule.users &&
!rule.users !rule.users
@@ -257,31 +280,59 @@ export class MediaRequest {
) { ) {
return false; return false;
} }
if (
rule.keywords &&
!rule.keywords.split(',').some((keywordId) => {
let keywordList: TmdbKeyword[] = [];
if ('keywords' in tmdbMedia.keywords) {
keywordList = tmdbMedia.keywords.keywords;
} else if ('results' in tmdbMedia.keywords) {
keywordList = tmdbMedia.keywords.results;
}
return keywordList
.map((keyword: TmdbKeyword) => keyword.id)
.includes(Number(keywordId));
})
) {
return false;
}
return true; return true;
}); });
const overrideRootFolder = appliedOverrideRules.find( // hacky way to prioritize rules
(rule) => rule.rootFolder // TODO: make this better
)?.rootFolder; const prioritizedRule = appliedOverrideRules.sort((a, b) => {
if (overrideRootFolder) { const keys: (keyof OverrideRule)[] = ['genre', 'language', 'keywords'];
rootFolder = overrideRootFolder;
}
const overrideProfileId = appliedOverrideRules.find( const aSpecificity = keys.filter((key) => a[key] !== null).length;
(rule) => rule.profileId const bSpecificity = keys.filter((key) => b[key] !== null).length;
)?.profileId;
if (overrideProfileId) {
profileId = overrideProfileId;
}
const overrideTags = appliedOverrideRules.find((rule) => rule.tags)?.tags; // Take the rule with the most specific condition first
if (overrideTags) { return bSpecificity - aSpecificity;
tags = [ })[0];
...new Set([
...(tags || []), if (prioritizedRule) {
...overrideTags.split(',').map((tag) => Number(tag)), if (prioritizedRule.rootFolder) {
]), rootFolder = prioritizedRule.rootFolder;
]; }
if (prioritizedRule.profileId) {
profileId = prioritizedRule.profileId;
}
if (prioritizedRule.tags) {
tags = [
...new Set([
...(tags || []),
...prioritizedRule.tags.split(',').map((tag) => Number(tag)),
]),
];
}
logger.debug('Override rule applied.', {
label: 'Media Request',
overrides: prioritizedRule,
});
} }
} }