feat: auto tagging requested media with username (#3338)

* feat: auto tagging requested media with username

Relating to discussion: https://github.com/sct/overseerr/discussions/3313

Adding an option to the Radarr and Sonarr service to enable automatic tagging
with the username requesting the media.

Current format, to reduce tag clutter if a user changes displayname:

`[user.id] - [user.displayName]`

* fix: modified new secondary tip language

---------

Co-authored-by: Brandon Cohen <brandon@z3hn.dev>
pull/3473/head
Izaac Brånn 2 years ago committed by GitHub
parent b8e3c07c47
commit 24f268b6cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -764,6 +764,38 @@ export class MediaRequest {
return; return;
} }
if (radarrSettings.tagRequests) {
let userTag = (await radarr.getTags()).find((v) =>
v.label.startsWith(this.requestedBy.id + ' - ')
);
if (!userTag) {
logger.info(`Requester has no active tag. Creating new`, {
label: 'Media Request',
requestId: this.id,
mediaId: this.media.id,
userId: this.requestedBy.id,
newTag:
this.requestedBy.id + ' - ' + this.requestedBy.displayName,
});
userTag = await radarr.createTag({
label: this.requestedBy.id + ' - ' + this.requestedBy.displayName,
});
}
if (userTag.id) {
if (!tags?.find((v) => v === userTag?.id)) {
tags?.push(userTag.id);
}
} else {
logger.warn(`Requester has no tag and failed to add one`, {
label: 'Media Request',
requestId: this.id,
mediaId: this.media.id,
userId: this.requestedBy.id,
radarrServer: radarrSettings.hostname + ':' + radarrSettings.port,
});
}
}
if ( if (
media[this.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE media[this.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE
) { ) {
@ -1022,6 +1054,38 @@ export class MediaRequest {
}); });
} }
if (sonarrSettings.tagRequests) {
let userTag = (await sonarr.getTags()).find((v) =>
v.label.startsWith(this.requestedBy.id + ' - ')
);
if (!userTag) {
logger.info(`Requester has no active tag. Creating new`, {
label: 'Media Request',
requestId: this.id,
mediaId: this.media.id,
userId: this.requestedBy.id,
newTag:
this.requestedBy.id + ' - ' + this.requestedBy.displayName,
});
userTag = await sonarr.createTag({
label: this.requestedBy.id + ' - ' + this.requestedBy.displayName,
});
}
if (userTag.id) {
if (!tags?.find((v) => v === userTag?.id)) {
tags?.push(userTag.id);
}
} else {
logger.warn(`Requester has no tag and failed to add one`, {
label: 'Media Request',
requestId: this.id,
mediaId: this.media.id,
userId: this.requestedBy.id,
sonarrServer: sonarrSettings.hostname + ':' + sonarrSettings.port,
});
}
}
const sonarrSeriesOptions: AddSeriesOptions = { const sonarrSeriesOptions: AddSeriesOptions = {
profileId: qualityProfile, profileId: qualityProfile,
languageProfileId: languageProfile, languageProfileId: languageProfile,

@ -61,6 +61,7 @@ export interface DVRSettings {
externalUrl?: string; externalUrl?: string;
syncEnabled: boolean; syncEnabled: boolean;
preventSearch: boolean; preventSearch: boolean;
tagRequests: boolean;
} }
export interface RadarrSettings extends DVRSettings { export interface RadarrSettings extends DVRSettings {

@ -57,6 +57,9 @@ const messages = defineMessages({
testFirstTags: 'Test connection to load tags', testFirstTags: 'Test connection to load tags',
tags: 'Tags', tags: 'Tags',
enableSearch: 'Enable Automatic Search', enableSearch: 'Enable Automatic Search',
tagRequests: 'Tag Requests',
tagRequestsInfo:
"Automatically add an additional tag with the requester's user ID & display name",
validationApplicationUrl: 'You must provide a valid URL', validationApplicationUrl: 'You must provide a valid URL',
validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash', validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash',
validationBaseUrlLeadingSlash: 'URL base must have a leading slash', validationBaseUrlLeadingSlash: 'URL base must have a leading slash',
@ -238,6 +241,7 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => {
externalUrl: radarr?.externalUrl, externalUrl: radarr?.externalUrl,
syncEnabled: radarr?.syncEnabled ?? false, syncEnabled: radarr?.syncEnabled ?? false,
enableSearch: !radarr?.preventSearch, enableSearch: !radarr?.preventSearch,
tagRequests: radarr?.tagRequests ?? false,
}} }}
validationSchema={RadarrSettingsSchema} validationSchema={RadarrSettingsSchema}
onSubmit={async (values) => { onSubmit={async (values) => {
@ -263,6 +267,7 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => {
externalUrl: values.externalUrl, externalUrl: values.externalUrl,
syncEnabled: values.syncEnabled, syncEnabled: values.syncEnabled,
preventSearch: !values.enableSearch, preventSearch: !values.enableSearch,
tagRequests: values.tagRequests,
}; };
if (!radarr) { if (!radarr) {
await axios.post('/api/v1/settings/radarr', submission); await axios.post('/api/v1/settings/radarr', submission);
@ -713,6 +718,21 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => {
/> />
</div> </div>
</div> </div>
<div className="form-row">
<label htmlFor="tagRequests" className="checkbox-label">
{intl.formatMessage(messages.tagRequests)}
<span className="label-tip">
{intl.formatMessage(messages.tagRequestsInfo)}
</span>
</label>
<div className="form-input-area">
<Field
type="checkbox"
id="tagRequests"
name="tagRequests"
/>
</div>
</div>
</div> </div>
</Modal> </Modal>
); );

@ -62,6 +62,9 @@ const messages = defineMessages({
syncEnabled: 'Enable Scan', syncEnabled: 'Enable Scan',
externalUrl: 'External URL', externalUrl: 'External URL',
enableSearch: 'Enable Automatic Search', enableSearch: 'Enable Automatic Search',
tagRequests: 'Tag Requests',
tagRequestsInfo:
"Automatically add an additional tag with the requester's user ID & display name",
validationApplicationUrl: 'You must provide a valid URL', validationApplicationUrl: 'You must provide a valid URL',
validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash', validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash',
validationBaseUrlLeadingSlash: 'Base URL must have a leading slash', validationBaseUrlLeadingSlash: 'Base URL must have a leading slash',
@ -252,6 +255,7 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => {
externalUrl: sonarr?.externalUrl, externalUrl: sonarr?.externalUrl,
syncEnabled: sonarr?.syncEnabled ?? false, syncEnabled: sonarr?.syncEnabled ?? false,
enableSearch: !sonarr?.preventSearch, enableSearch: !sonarr?.preventSearch,
tagRequests: sonarr?.tagRequests ?? false,
}} }}
validationSchema={SonarrSettingsSchema} validationSchema={SonarrSettingsSchema}
onSubmit={async (values) => { onSubmit={async (values) => {
@ -292,6 +296,7 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => {
externalUrl: values.externalUrl, externalUrl: values.externalUrl,
syncEnabled: values.syncEnabled, syncEnabled: values.syncEnabled,
preventSearch: !values.enableSearch, preventSearch: !values.enableSearch,
tagRequests: values.tagRequests,
}; };
if (!sonarr) { if (!sonarr) {
await axios.post('/api/v1/settings/sonarr', submission); await axios.post('/api/v1/settings/sonarr', submission);
@ -960,6 +965,21 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => {
/> />
</div> </div>
</div> </div>
<div className="form-row">
<label htmlFor="tagRequests" className="checkbox-label">
{intl.formatMessage(messages.tagRequests)}
<span className="label-tip">
{intl.formatMessage(messages.tagRequestsInfo)}
</span>
</label>
<div className="form-input-area">
<Field
type="checkbox"
id="tagRequests"
name="tagRequests"
/>
</div>
</div>
</div> </div>
</Modal> </Modal>
); );

@ -684,6 +684,8 @@
"components.Settings.RadarrModal.servername": "Server Name", "components.Settings.RadarrModal.servername": "Server Name",
"components.Settings.RadarrModal.ssl": "Use SSL", "components.Settings.RadarrModal.ssl": "Use SSL",
"components.Settings.RadarrModal.syncEnabled": "Enable Scan", "components.Settings.RadarrModal.syncEnabled": "Enable Scan",
"components.Settings.RadarrModal.tagRequests": "Tag Requests",
"components.Settings.RadarrModal.tagRequestsInfo": "Automatically add an additional tag with the requester's user ID & display name",
"components.Settings.RadarrModal.tags": "Tags", "components.Settings.RadarrModal.tags": "Tags",
"components.Settings.RadarrModal.testFirstQualityProfiles": "Test connection to load quality profiles", "components.Settings.RadarrModal.testFirstQualityProfiles": "Test connection to load quality profiles",
"components.Settings.RadarrModal.testFirstRootFolders": "Test connection to load root folders", "components.Settings.RadarrModal.testFirstRootFolders": "Test connection to load root folders",
@ -861,6 +863,8 @@
"components.Settings.SonarrModal.servername": "Server Name", "components.Settings.SonarrModal.servername": "Server Name",
"components.Settings.SonarrModal.ssl": "Use SSL", "components.Settings.SonarrModal.ssl": "Use SSL",
"components.Settings.SonarrModal.syncEnabled": "Enable Scan", "components.Settings.SonarrModal.syncEnabled": "Enable Scan",
"components.Settings.SonarrModal.tagRequests": "Tag Requests",
"components.Settings.SonarrModal.tagRequestsInfo": "Automatically add an additional tag with the requester's user ID & display name",
"components.Settings.SonarrModal.tags": "Tags", "components.Settings.SonarrModal.tags": "Tags",
"components.Settings.SonarrModal.testFirstLanguageProfiles": "Test connection to load language profiles", "components.Settings.SonarrModal.testFirstLanguageProfiles": "Test connection to load language profiles",
"components.Settings.SonarrModal.testFirstQualityProfiles": "Test connection to load quality profiles", "components.Settings.SonarrModal.testFirstQualityProfiles": "Test connection to load quality profiles",
@ -1178,6 +1182,7 @@
"i18n.cancel": "Cancel", "i18n.cancel": "Cancel",
"i18n.canceling": "Canceling…", "i18n.canceling": "Canceling…",
"i18n.close": "Close", "i18n.close": "Close",
"i18n.collection": "Collection",
"i18n.decline": "Decline", "i18n.decline": "Decline",
"i18n.declined": "Declined", "i18n.declined": "Declined",
"i18n.delete": "Delete", "i18n.delete": "Delete",

Loading…
Cancel
Save