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:
@@ -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,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user