feat(blacklist): Automatically add media with blacklisted tags to the blacklist (#1306)
* feat(blacklist): add blacktag settings to main settings page * feat(blacklist): create blacktag logic and infrastructure * feat(blacklist): add scheduling for blacktags job * feat(blacklist): create blacktag ui badge for blacklist * docs(blacklist): document blacktags in using-jellyseerr * fix(blacklist): batch blacklist and media db removes to avoid expression tree too large error * feat(blacklist): allow easy import and export of blacktag configuration * fix(settings): don't copy the API key every time you press enter on the main settings * fix(blacklist): move filter inline with page title to match all the other pages * feat(blacklist): allow filtering between manually blacklisted and automatically blacklisted entries * docs(blacklist): reword blacktag documentation a little * refactor(blacklist): remove blacktag settings from public settings interfaces There's no reason for it to be there * refactor(blacklist): remove unused variable from processResults in blacktagsProcessor * refactor(blacklist): change all instances of blacktag to blacklistedTag and update doc to match * docs(blacklist): update general documentation for blacklisted tag settings * fix(blacklist): update setting use of "blacklisted tag" to match between modals * perf(blacklist): remove media type constraint from existing blacklist entry query Doesn't make sense to keep it because tmdbid has a unique constraint on it * fix(blacklist): remove whitespace line causing prettier to fail in CI * refactor(blacklist): swap out some != and == for !s and _s * fix(blacklist): merge back CopyButton changes, disable button when there's nothing to copy * refactor(blacklist): use axios instead of fetch for blacklisted tag queries * style(blacklist): use templated axios types and remove redundant try-catches
This commit is contained in:
@@ -68,11 +68,14 @@ const messages: { [messageName: string]: MessageDescriptor } = defineMessages(
|
||||
'download-sync': 'Download Sync',
|
||||
'download-sync-reset': 'Download Sync Reset',
|
||||
'image-cache-cleanup': 'Image Cache Cleanup',
|
||||
'process-blacklisted-tags': 'Process Blacklisted Tags',
|
||||
editJobSchedule: 'Modify Job',
|
||||
jobScheduleEditSaved: 'Job edited successfully!',
|
||||
jobScheduleEditFailed: 'Something went wrong while saving the job.',
|
||||
editJobScheduleCurrent: 'Current Frequency',
|
||||
editJobSchedulePrompt: 'New Frequency',
|
||||
editJobScheduleSelectorDays:
|
||||
'Every {jobScheduleDays, plural, one {day} other {{jobScheduleDays} days}}',
|
||||
editJobScheduleSelectorHours:
|
||||
'Every {jobScheduleHours, plural, one {hour} other {{jobScheduleHours} hours}}',
|
||||
editJobScheduleSelectorMinutes:
|
||||
@@ -92,7 +95,7 @@ interface Job {
|
||||
id: JobId;
|
||||
name: string;
|
||||
type: 'process' | 'command';
|
||||
interval: 'seconds' | 'minutes' | 'hours' | 'fixed';
|
||||
interval: 'seconds' | 'minutes' | 'hours' | 'days' | 'fixed';
|
||||
cronSchedule: string;
|
||||
nextExecutionTime: string;
|
||||
running: boolean;
|
||||
@@ -101,13 +104,20 @@ interface Job {
|
||||
type JobModalState = {
|
||||
isOpen?: boolean;
|
||||
job?: Job;
|
||||
scheduleDays: number;
|
||||
scheduleHours: number;
|
||||
scheduleMinutes: number;
|
||||
scheduleSeconds: number;
|
||||
};
|
||||
|
||||
type JobModalAction =
|
||||
| { type: 'set'; hours?: number; minutes?: number; seconds?: number }
|
||||
| {
|
||||
type: 'set';
|
||||
days?: number;
|
||||
hours?: number;
|
||||
minutes?: number;
|
||||
seconds?: number;
|
||||
}
|
||||
| {
|
||||
type: 'close';
|
||||
}
|
||||
@@ -128,6 +138,7 @@ const jobModalReducer = (
|
||||
return {
|
||||
isOpen: true,
|
||||
job: action.job,
|
||||
scheduleDays: 1,
|
||||
scheduleHours: 1,
|
||||
scheduleMinutes: 5,
|
||||
scheduleSeconds: 30,
|
||||
@@ -136,6 +147,7 @@ const jobModalReducer = (
|
||||
case 'set':
|
||||
return {
|
||||
...state,
|
||||
scheduleDays: action.days ?? state.scheduleDays,
|
||||
scheduleHours: action.hours ?? state.scheduleHours,
|
||||
scheduleMinutes: action.minutes ?? state.scheduleMinutes,
|
||||
scheduleSeconds: action.seconds ?? state.scheduleSeconds,
|
||||
@@ -164,6 +176,7 @@ const SettingsJobs = () => {
|
||||
|
||||
const [jobModalState, dispatch] = useReducer(jobModalReducer, {
|
||||
isOpen: false,
|
||||
scheduleDays: 1,
|
||||
scheduleHours: 1,
|
||||
scheduleMinutes: 5,
|
||||
scheduleSeconds: 30,
|
||||
@@ -239,6 +252,9 @@ const SettingsJobs = () => {
|
||||
jobScheduleCron[1] = `*/${jobModalState.scheduleMinutes}`;
|
||||
} else if (jobModalState.job?.interval === 'hours') {
|
||||
jobScheduleCron[2] = `*/${jobModalState.scheduleHours}`;
|
||||
} else if (jobModalState.job?.interval === 'days') {
|
||||
jobScheduleCron[2] = '1';
|
||||
jobScheduleCron[3] = `*/${jobModalState.scheduleDays}`;
|
||||
} else {
|
||||
// jobs with interval: fixed should not be editable
|
||||
throw new Error();
|
||||
@@ -367,6 +383,29 @@ const SettingsJobs = () => {
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : jobModalState.job?.interval === 'days' ? (
|
||||
<select
|
||||
name="jobScheduleDays"
|
||||
className="inline"
|
||||
value={jobModalState.scheduleDays}
|
||||
onChange={(e) =>
|
||||
dispatch({
|
||||
type: 'set',
|
||||
days: Number(e.target.value),
|
||||
})
|
||||
}
|
||||
>
|
||||
{[1, 2, 3, 4, 5, 6, 7, 10, 14, 21].map((v) => (
|
||||
<option value={v} key={`jobScheduleDays-${v}`}>
|
||||
{intl.formatMessage(
|
||||
messages.editJobScheduleSelectorDays,
|
||||
{
|
||||
jobScheduleDays: v,
|
||||
}
|
||||
)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : (
|
||||
<select
|
||||
name="jobScheduleHours"
|
||||
|
||||
Reference in New Issue
Block a user