refactor(blacklist): change all instances of blacktag to blacklistedTag and update doc to match
This commit is contained in:
@@ -19,8 +19,8 @@
|
||||
"discoverRegion": "",
|
||||
"streamingRegion": "",
|
||||
"originalLanguage": "",
|
||||
"blacktags": "",
|
||||
"blacktagsLimit": 50,
|
||||
"blacklistedTags": "",
|
||||
"blacklistedTagsLimit": 50,
|
||||
"trustProxy": false,
|
||||
"mediaServerType": 1,
|
||||
"partialRequestsEnabled": true,
|
||||
|
||||
@@ -64,9 +64,9 @@ These settings filter content shown on the "Discover" home page based on regiona
|
||||
|
||||
## Blacklist Content with Tags and Limit Content Blacklisted per Tag
|
||||
|
||||
These settings blacklist any TV shows or movies that have one of the entered tags. Entries are added to the blacklist using the "Process Blacktags" job. Removing blacktags will remove media from the blacklist if it was blacklisted for that blacktag. To clear all blacklist entries created with blacktags, remove all blacktags in the setting and run the "Process Blacktags" job.
|
||||
These settings blacklist any TV shows or movies that have one of the entered tags. Entries are added to the blacklist using the "Process Blacklisted Tags" job. Removing blacklisted tags will remove media from the blacklist if it was blacklisted for that tag. To clear all blacklist entries created with tag, remove all tags in the setting and run the "Process Blacklisted Tags" job.
|
||||
The limit configures the number of pages per tag the job will blacklist, where each page is 20 entires. The job cycles through each of the 16 available discovery sort options and queries the defined number of pages to blacklist media most likely to appear at the top of a sort. Increasing the limit will create a more accurate blacklist, but will use more space.
|
||||
Blacktags are disabled until tags to blacklist are entered. These settings cannot be overriden in user settings.
|
||||
Blacklisted tags are disabled until tags to blacklist are entered. These settings cannot be overriden in user settings.
|
||||
|
||||
## Hide Available Media
|
||||
|
||||
|
||||
@@ -4160,7 +4160,7 @@ paths:
|
||||
name: filter
|
||||
schema:
|
||||
type: string
|
||||
enum: [all, manual, blacktags]
|
||||
enum: [all, manual, blacklistedTags]
|
||||
default: manual
|
||||
responses:
|
||||
'200':
|
||||
|
||||
@@ -45,7 +45,7 @@ export class Blacklist implements BlacklistItem {
|
||||
public media: Media;
|
||||
|
||||
@Column({ nullable: true, type: 'varchar' })
|
||||
public blacktags?: string;
|
||||
public blacklistedTags?: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
public createdAt: Date;
|
||||
@@ -62,7 +62,7 @@ export class Blacklist implements BlacklistItem {
|
||||
mediaType: MediaType;
|
||||
title?: ZodOptional<ZodString>['_output'];
|
||||
tmdbId: ZodNumber['_output'];
|
||||
blacktags?: string;
|
||||
blacklistedTags?: string;
|
||||
};
|
||||
},
|
||||
entityManager?: EntityManager
|
||||
|
||||
@@ -7,7 +7,7 @@ export interface BlacklistItem {
|
||||
title?: string;
|
||||
createdAt?: Date;
|
||||
user?: User;
|
||||
blacktags?: string;
|
||||
blacklistedTags?: string;
|
||||
}
|
||||
|
||||
export interface BlacklistResultsResponse extends PaginatedResponse {
|
||||
|
||||
@@ -20,7 +20,7 @@ import type { EntityManager } from 'typeorm';
|
||||
const TMDB_API_DELAY_MS = 250;
|
||||
class AbortTransaction extends Error {}
|
||||
|
||||
class BlacktagProcessor implements RunnableScanner<StatusBase> {
|
||||
class BlacklistedTagProcessor implements RunnableScanner<StatusBase> {
|
||||
private running = false;
|
||||
private progress = 0;
|
||||
private total = 0;
|
||||
@@ -35,7 +35,7 @@ class BlacktagProcessor implements RunnableScanner<StatusBase> {
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof AbortTransaction) {
|
||||
logger.info('Aborting job: Process Blacktags', {
|
||||
logger.info('Aborting job: Process Blacklisted Tags', {
|
||||
label: 'Jobs',
|
||||
});
|
||||
} else {
|
||||
@@ -68,25 +68,25 @@ class BlacktagProcessor implements RunnableScanner<StatusBase> {
|
||||
const tmdb = createTmdbWithRegionLanguage();
|
||||
|
||||
const settings = getSettings();
|
||||
const blacktags = settings.main.blacktags;
|
||||
const blacktagsArr = blacktags.split(',');
|
||||
const blacklistedTags = settings.main.blacklistedTags;
|
||||
const blacklistedTagsArr = blacklistedTags.split(',');
|
||||
|
||||
const pageLimit = settings.main.blacktagsLimit;
|
||||
const pageLimit = settings.main.blacklistedTagsLimit;
|
||||
|
||||
if (blacktags.length === 0) {
|
||||
if (blacklistedTags.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The maximum number of queries we're expected to execute
|
||||
this.total =
|
||||
2 * blacktagsArr.length * pageLimit * SortOptionsIterable.length;
|
||||
2 * blacklistedTagsArr.length * pageLimit * SortOptionsIterable.length;
|
||||
|
||||
for (const type of [MediaType.MOVIE, MediaType.TV]) {
|
||||
const getDiscover =
|
||||
type == MediaType.MOVIE ? tmdb.getDiscoverMovies : tmdb.getDiscoverTv;
|
||||
|
||||
// Iterate for each tag
|
||||
for (const tag of blacktagsArr) {
|
||||
for (const tag of blacklistedTagsArr) {
|
||||
let queryMax = pageLimit * SortOptionsIterable.length;
|
||||
let fixedSortMode = false; // Set to true when the page limit allows for getting every page of tag
|
||||
|
||||
@@ -139,11 +139,11 @@ class BlacktagProcessor implements RunnableScanner<StatusBase> {
|
||||
// Don't mark manual blacklists with tags
|
||||
// If media wasn't previously blacklisted for this tag, add the tag to the media's blacklist
|
||||
if (
|
||||
blacklistEntry.blacktags != null &&
|
||||
!blacklistEntry.blacktags.includes(`,${keywordId},`)
|
||||
blacklistEntry.blacklistedTags != null &&
|
||||
!blacklistEntry.blacklistedTags.includes(`,${keywordId},`)
|
||||
) {
|
||||
await blacklistRepository.update(blacklistEntry.id, {
|
||||
blacktags: `${blacklistEntry.blacktags}${keywordId},`,
|
||||
blacklistedTags: `${blacklistEntry.blacklistedTags}${keywordId},`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ class BlacktagProcessor implements RunnableScanner<StatusBase> {
|
||||
mediaType,
|
||||
title: 'title' in entry ? entry.title : entry.name,
|
||||
tmdbId: entry.id,
|
||||
blacktags: `,${keywordId},`,
|
||||
blacklistedTags: `,${keywordId},`,
|
||||
},
|
||||
},
|
||||
em
|
||||
@@ -166,12 +166,12 @@ class BlacktagProcessor implements RunnableScanner<StatusBase> {
|
||||
}
|
||||
|
||||
private async cleanBlacklist(em: EntityManager) {
|
||||
// Remove blacklist and media entries blacklisted by blacktags
|
||||
// Remove blacklist and media entries blacklisted by tags
|
||||
const mediaRepository = em.getRepository(Media);
|
||||
const mediaToRemove = await mediaRepository
|
||||
.createQueryBuilder('media')
|
||||
.innerJoinAndSelect(Blacklist, 'blist', 'blist.tmdbId = media.tmdbId')
|
||||
.where(`blist.blacktags IS NOT NULL`)
|
||||
.where(`blist.blacklistedTags IS NOT NULL`)
|
||||
.getMany();
|
||||
|
||||
// Batch removes so the query doesn't get too large
|
||||
@@ -181,6 +181,6 @@ class BlacktagProcessor implements RunnableScanner<StatusBase> {
|
||||
}
|
||||
}
|
||||
|
||||
const blacktagsProcessor = new BlacktagProcessor();
|
||||
const blacklistedTagsProcessor = new BlacklistedTagProcessor();
|
||||
|
||||
export default blacktagsProcessor;
|
||||
export default blacklistedTagsProcessor;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MediaServerType } from '@server/constants/server';
|
||||
import blacktagsProcessor from '@server/job/blacktagsProcessor';
|
||||
import blacklistedTagsProcessor from '@server/job/blacklistedTagsProcessor';
|
||||
import availabilitySync from '@server/lib/availabilitySync';
|
||||
import downloadTracker from '@server/lib/downloadtracker';
|
||||
import ImageProxy from '@server/lib/imageproxy';
|
||||
@@ -240,18 +240,18 @@ export const startJobs = (): void => {
|
||||
|
||||
scheduledJobs.push({
|
||||
id: 'process-blacklisted-tags',
|
||||
name: 'Process Blacktags',
|
||||
name: 'Process Blacklisted Tags',
|
||||
type: 'process',
|
||||
interval: 'days',
|
||||
cronSchedule: jobs['process-blacklisted-tags'].schedule,
|
||||
job: schedule.scheduleJob(jobs['process-blacklisted-tags'].schedule, () => {
|
||||
logger.info('Starting scheduled job: Process Blacktags', {
|
||||
logger.info('Starting scheduled job: Process Blacklisted Tags', {
|
||||
label: 'Jobs',
|
||||
});
|
||||
blacktagsProcessor.run();
|
||||
blacklistedTagsProcessor.run();
|
||||
}),
|
||||
running: () => blacktagsProcessor.status().running,
|
||||
cancelFn: () => blacktagsProcessor.cancel(),
|
||||
running: () => blacklistedTagsProcessor.status().running,
|
||||
cancelFn: () => blacklistedTagsProcessor.cancel(),
|
||||
});
|
||||
|
||||
logger.info('Scheduled jobs loaded', { label: 'Jobs' });
|
||||
|
||||
@@ -128,8 +128,8 @@ export interface MainSettings {
|
||||
discoverRegion: string;
|
||||
streamingRegion: string;
|
||||
originalLanguage: string;
|
||||
blacktags: string;
|
||||
blacktagsLimit: number;
|
||||
blacklistedTags: string;
|
||||
blacklistedTagsLimit: number;
|
||||
mediaServerType: number;
|
||||
partialRequestsEnabled: boolean;
|
||||
enableSpecialEpisodes: boolean;
|
||||
@@ -353,8 +353,8 @@ class Settings {
|
||||
discoverRegion: '',
|
||||
streamingRegion: '',
|
||||
originalLanguage: '',
|
||||
blacktags: '',
|
||||
blacktagsLimit: 50,
|
||||
blacklistedTags: '',
|
||||
blacklistedTagsLimit: 50,
|
||||
mediaServerType: MediaServerType.NOT_CONFIGURED,
|
||||
partialRequestsEnabled: true,
|
||||
enableSpecialEpisodes: false,
|
||||
|
||||
@@ -5,11 +5,13 @@ export class AddBlacklistTagsColumn1737320080282 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "blacklist" ADD blacktags character varying`
|
||||
`ALTER TABLE "blacklist" ADD blacklistedTags character varying`
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "blacklist" DROP COLUMN blacktags`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "blacklist" DROP COLUMN blacklistedTags`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ export class AddBlacklistTagsColumn1737320080282 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "temporary_blacklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "blacktags" varchar, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "userId" integer, "mediaId" integer, CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId"), CONSTRAINT "REL_62b7ade94540f9f8d8bede54b9" UNIQUE ("mediaId"), CONSTRAINT "FK_53c1ab62c3e5875bc3ac474823e" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_62b7ade94540f9f8d8bede54b99" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
|
||||
`CREATE TABLE "temporary_blacklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "blacklistedTags" varchar, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "userId" integer, "mediaId" integer, CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId"), CONSTRAINT "REL_62b7ade94540f9f8d8bede54b9" UNIQUE ("mediaId"), CONSTRAINT "FK_53c1ab62c3e5875bc3ac474823e" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_62b7ade94540f9f8d8bede54b99" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "temporary_blacklist"("id", "mediaType", "title", "tmdbId", "blacktags", "createdAt", "userId", "mediaId") SELECT "id", "mediaType", "title", "tmdbId", "blacktags", "createdAt", "userId", "mediaId" FROM "blacklist"`
|
||||
`INSERT INTO "temporary_blacklist"("id", "mediaType", "title", "tmdbId", "blacklistedTags", "createdAt", "userId", "mediaId") SELECT "id", "mediaType", "title", "tmdbId", "blacklistedTags", "createdAt", "userId", "mediaId" FROM "blacklist"`
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "blacklist"`);
|
||||
await queryRunner.query(
|
||||
|
||||
@@ -23,7 +23,7 @@ const blacklistGet = z.object({
|
||||
take: z.coerce.number().int().positive().default(25),
|
||||
skip: z.coerce.number().int().nonnegative().default(0),
|
||||
search: z.string().optional(),
|
||||
filter: z.enum(['all', 'manual', 'blacktags']).optional(),
|
||||
filter: z.enum(['all', 'manual', 'blacklistedTags']).optional(),
|
||||
});
|
||||
|
||||
blacklistRoutes.get(
|
||||
@@ -42,10 +42,10 @@ blacklistRoutes.get(
|
||||
|
||||
switch (filter) {
|
||||
case 'manual':
|
||||
query = query.andWhere('blacklist.blacktags IS NULL');
|
||||
query = query.andWhere('blacklist.blacklistedTags IS NULL');
|
||||
break;
|
||||
case 'blacktags':
|
||||
query = query.andWhere('blacklist.blacktags IS NOT NULL');
|
||||
case 'blacklistedTags':
|
||||
query = query.andWhere('blacklist.blacklistedTags IS NOT NULL');
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import BlacktagsBadge from '@app/components/BlacktagsBadge';
|
||||
import BlacklistedTagsBadge from '@app/components/BlacklistedTagsBadge';
|
||||
import Badge from '@app/components/Common/Badge';
|
||||
import Button from '@app/components/Common/Button';
|
||||
import CachedImage from '@app/components/Common/CachedImage';
|
||||
@@ -44,14 +44,14 @@ const messages = defineMessages('components.Blacklist', {
|
||||
blacklistedby: '{date} by {user}',
|
||||
blacklistNotFoundError: '<strong>{title}</strong> is not blacklisted.',
|
||||
filterManual: 'Manual',
|
||||
filterBlacktags: 'Blacktags',
|
||||
filterBlacklistedTags: 'Blacklisted Tags',
|
||||
showAllBlacklisted: 'Show All Blacklisted Media',
|
||||
});
|
||||
|
||||
enum Filter {
|
||||
ALL = 'all',
|
||||
MANUAL = 'manual',
|
||||
BLACKTAGS = 'blacktags',
|
||||
BLACKLISTEDTAGS = 'blacklistedTags',
|
||||
}
|
||||
|
||||
const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
|
||||
@@ -136,8 +136,8 @@ const Blacklist = () => {
|
||||
<option value="manual">
|
||||
{intl.formatMessage(messages.filterManual)}
|
||||
</option>
|
||||
<option value="blacktags">
|
||||
{intl.formatMessage(messages.filterBlacktags)}
|
||||
<option value="blacklistedTags">
|
||||
{intl.formatMessage(messages.filterBlacklistedTags)}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -428,9 +428,9 @@ const BlacklistedItem = ({ item, revalidateList }: BlacklistedItemProps) => {
|
||||
</span>
|
||||
</span>
|
||||
</Link>
|
||||
) : item.blacktags ? (
|
||||
) : item.blacklistedTags ? (
|
||||
<span className="ml-1">
|
||||
<BlacktagsBadge data={item} />
|
||||
<BlacklistedTagsBadge data={item} />
|
||||
</span>
|
||||
) : (
|
||||
<span className="ml-1 truncate text-sm font-semibold">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import BlacktagsBadge from '@app/components/BlacktagsBadge';
|
||||
import BlacklistedTagsBadge from '@app/components/BlacklistedTagsBadge';
|
||||
import Badge from '@app/components/Common/Badge';
|
||||
import Button from '@app/components/Common/Button';
|
||||
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
|
||||
@@ -98,12 +98,12 @@ const BlacklistBlock = ({
|
||||
</Link>
|
||||
</span>
|
||||
</>
|
||||
) : data.blacktags != null && data.blacktags != '' ? (
|
||||
) : data.blacklistedTags != null && data.blacklistedTags != '' ? (
|
||||
<>
|
||||
<span className="w-40 truncate md:w-auto">
|
||||
{intl.formatMessage(messages.blacklistedby)}:
|
||||
</span>
|
||||
<BlacktagsBadge data={data} />
|
||||
<BlacklistedTagsBadge data={data} />
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
import Badge from '@app/components/Common/Badge';
|
||||
import Tooltip from '@app/components/Common/Tooltip';
|
||||
import defineMessages from '@app/utils/defineMessages';
|
||||
import { TagIcon } from '@heroicons/react/20/solid';
|
||||
import type { BlacklistItem } from '@server/interfaces/api/blacklistInterfaces';
|
||||
import type { Keyword } from '@server/models/common';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
interface BlacktagsBadgeProps {
|
||||
const messages = defineMessages('components.Settings', {
|
||||
blacklistedTagsText: 'Blacklisted Tags',
|
||||
});
|
||||
|
||||
interface BlacklistedTagsBadgeProps {
|
||||
data: BlacklistItem;
|
||||
}
|
||||
|
||||
const BlacktagsBadge = ({ data }: BlacktagsBadgeProps) => {
|
||||
const BlacklistedTagsBadge = ({ data }: BlacklistedTagsBadgeProps) => {
|
||||
const [tagNamesBlacklistedFor, setTagNamesBlacklistedFor] =
|
||||
useState<string>('Loading...');
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
if (data.blacktags == null) {
|
||||
if (data.blacklistedTags == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const keywordIds = data.blacktags.slice(1, -1).split(',');
|
||||
const keywordIds = data.blacklistedTags.slice(1, -1).split(',');
|
||||
Promise.all(
|
||||
keywordIds.map(async (keywordId) => {
|
||||
const res = await fetch(`/api/v1/keyword/${keywordId}`);
|
||||
@@ -32,7 +39,7 @@ const BlacktagsBadge = ({ data }: BlacktagsBadgeProps) => {
|
||||
).then((keywords) => {
|
||||
setTagNamesBlacklistedFor(keywords.join(', '));
|
||||
});
|
||||
}, [data.blacktags]);
|
||||
}, [data.blacklistedTags]);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
@@ -44,10 +51,10 @@ const BlacktagsBadge = ({ data }: BlacktagsBadgeProps) => {
|
||||
className="items-center border border-red-500 !text-red-400"
|
||||
>
|
||||
<TagIcon className="mr-1 h-4" />
|
||||
Blacktags
|
||||
{intl.formatMessage(messages.blacklistedTagsText)}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default BlacktagsBadge;
|
||||
export default BlacklistedTagsBadge;
|
||||
@@ -26,17 +26,18 @@ import { useToasts } from 'react-toast-notifications';
|
||||
import useClipboard from 'react-use-clipboard';
|
||||
|
||||
const messages = defineMessages('components.Settings', {
|
||||
copyBlacktags: 'Copied blacktags to clipboard.',
|
||||
copyBlacktagsTip: 'Copy blacktag configuration',
|
||||
importBlacktagsTip: 'Import blacktag configuration',
|
||||
clearBlacktagsConfirm: 'Are you sure you want to clear the blacktags?',
|
||||
copyBlacklistedTags: 'Copied blacklisted tags to clipboard.',
|
||||
copyBlacklistedTagsTip: 'Copy blacklisted tags configuration',
|
||||
importBlacklistedTagsTip: 'Import blacklisted tags configuration',
|
||||
clearBlacklistedTagsConfirm:
|
||||
'Are you sure you want to clear the blacklisted tags?',
|
||||
yes: 'Yes',
|
||||
no: 'No',
|
||||
searchKeywords: 'Search keywords…',
|
||||
starttyping: 'Starting typing to search.',
|
||||
nooptions: 'No results.',
|
||||
blacktagImportTitle: 'Import Blacktag Configuration',
|
||||
blacktagImportInstructions: 'Paste blacktag configuration below.',
|
||||
blacklistedTagImportTitle: 'Import Blacklist Tag Configuration',
|
||||
blacklistedTagImportInstructions: 'Paste blacklist tag configuration below.',
|
||||
valueRequired: 'You must provide a value.',
|
||||
noSpecialCharacters:
|
||||
'Configuration must be a comma delimited list of TMDB keyword ids, and must not start or end with a comma.',
|
||||
@@ -48,11 +49,13 @@ type SingleVal = {
|
||||
value: number;
|
||||
};
|
||||
|
||||
type BlacktagsSelectorProps = {
|
||||
type BlacklistedTagsSelectorProps = {
|
||||
defaultValue?: string;
|
||||
};
|
||||
|
||||
const BlacktagsSelector = ({ defaultValue }: BlacktagsSelectorProps) => {
|
||||
const BlacklistedTagsSelector = ({
|
||||
defaultValue,
|
||||
}: BlacklistedTagsSelectorProps) => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const [value, setValue] = useState<string | undefined>(defaultValue);
|
||||
const [selectorValue, setSelectorValue] =
|
||||
@@ -63,7 +66,7 @@ const BlacktagsSelector = ({ defaultValue }: BlacktagsSelectorProps) => {
|
||||
const strVal = value?.map((v) => v.value).join(',');
|
||||
setSelectorValue(value);
|
||||
setValue(strVal);
|
||||
setFieldValue('blacktags', strVal);
|
||||
setFieldValue('blacklistedTags', strVal);
|
||||
},
|
||||
[setSelectorValue, setValue, setFieldValue]
|
||||
);
|
||||
@@ -81,8 +84,8 @@ const BlacktagsSelector = ({ defaultValue }: BlacktagsSelectorProps) => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<BlacktagsCopyButton value={value ?? ''} />
|
||||
<BlacktagsImportButton setSelector={update} />
|
||||
<BlacklistedTagsCopyButton value={value ?? ''} />
|
||||
<BlacklistedTagsImportButton setSelector={update} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -148,7 +151,7 @@ const ControlledKeywordSelector = ({
|
||||
|
||||
return (
|
||||
<AsyncSelect
|
||||
key={`keyword-select-blacktags`}
|
||||
key={`keyword-select-blacklistedTags`}
|
||||
inputId="data"
|
||||
isMulti
|
||||
className="react-select-container"
|
||||
@@ -167,11 +170,13 @@ const ControlledKeywordSelector = ({
|
||||
);
|
||||
};
|
||||
|
||||
type BlacktagsCopyButtonProps = {
|
||||
type BlacklistedTagsCopyButtonProps = {
|
||||
value: string;
|
||||
};
|
||||
|
||||
const BlacktagsCopyButton = ({ value }: BlacktagsCopyButtonProps) => {
|
||||
const BlacklistedTagsCopyButton = ({
|
||||
value,
|
||||
}: BlacklistedTagsCopyButtonProps) => {
|
||||
const intl = useIntl();
|
||||
const [isCopied, setCopied] = useClipboard(value, {
|
||||
successDuration: 1000,
|
||||
@@ -180,7 +185,7 @@ const BlacktagsCopyButton = ({ value }: BlacktagsCopyButtonProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (isCopied) {
|
||||
addToast(intl.formatMessage(messages.copyBlacktags), {
|
||||
addToast(intl.formatMessage(messages.copyBlacklistedTags), {
|
||||
appearance: 'info',
|
||||
autoDismiss: true,
|
||||
});
|
||||
@@ -189,7 +194,7 @@ const BlacktagsCopyButton = ({ value }: BlacktagsCopyButtonProps) => {
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
content={intl.formatMessage(messages.copyBlacktagsTip)}
|
||||
content={intl.formatMessage(messages.copyBlacklistedTagsTip)}
|
||||
tooltipConfig={{ followCursor: false }}
|
||||
>
|
||||
<button
|
||||
@@ -206,11 +211,13 @@ const BlacktagsCopyButton = ({ value }: BlacktagsCopyButtonProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
type BlacktagsImportButton = {
|
||||
type BlacklistedTagsImportButton = {
|
||||
setSelector: (value: MultiValue<SingleVal>) => void;
|
||||
};
|
||||
|
||||
const BlacktagsImportButton = ({ setSelector }: BlacktagsImportButton) => {
|
||||
const BlacklistedTagsImportButton = ({
|
||||
setSelector,
|
||||
}: BlacklistedTagsImportButton) => {
|
||||
const [show, setShow] = useState(false);
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
const intl = useIntl();
|
||||
@@ -241,17 +248,17 @@ const BlacktagsImportButton = ({ setSelector }: BlacktagsImportButton) => {
|
||||
show={show}
|
||||
>
|
||||
<Modal
|
||||
title={intl.formatMessage(messages.blacktagImportTitle)}
|
||||
title={intl.formatMessage(messages.blacklistedTagImportTitle)}
|
||||
okText="Confirm"
|
||||
onOk={onConfirm}
|
||||
onCancel={() => setShow(false)}
|
||||
>
|
||||
<BlacktagImportForm ref={formRef} setSelector={setSelector} />
|
||||
<BlacklistedTagImportForm ref={formRef} setSelector={setSelector} />
|
||||
</Modal>
|
||||
</Transition>
|
||||
|
||||
<Tooltip
|
||||
content={intl.formatMessage(messages.importBlacktagsTip)}
|
||||
content={intl.formatMessage(messages.importBlacklistedTagsTip)}
|
||||
tooltipConfig={{ followCursor: false }}
|
||||
>
|
||||
<button className="input-action" onClick={onClick} type="button">
|
||||
@@ -262,11 +269,11 @@ const BlacktagsImportButton = ({ setSelector }: BlacktagsImportButton) => {
|
||||
);
|
||||
};
|
||||
|
||||
type BlacktagImportFormProps = BlacktagsImportButton;
|
||||
type BlacklistedTagImportFormProps = BlacklistedTagsImportButton;
|
||||
|
||||
const BlacktagImportForm = forwardRef<
|
||||
const BlacklistedTagImportForm = forwardRef<
|
||||
Partial<HTMLFormElement>,
|
||||
BlacktagImportFormProps
|
||||
BlacklistedTagImportFormProps
|
||||
>((props, ref) => {
|
||||
const { setSelector } = props;
|
||||
const intl = useIntl();
|
||||
@@ -328,7 +335,7 @@ const BlacktagImportForm = forwardRef<
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor="value">
|
||||
{intl.formatMessage(messages.blacktagImportInstructions)}
|
||||
{intl.formatMessage(messages.blacklistedTagImportInstructions)}
|
||||
</label>
|
||||
<textarea
|
||||
id="value"
|
||||
@@ -414,7 +421,7 @@ const VerifyClearIndicator = <
|
||||
show={show}
|
||||
>
|
||||
<Modal
|
||||
subTitle={intl.formatMessage(messages.clearBlacktagsConfirm)}
|
||||
subTitle={intl.formatMessage(messages.clearBlacklistedTagsConfirm)}
|
||||
okText={intl.formatMessage(messages.yes)}
|
||||
cancelText={intl.formatMessage(messages.no)}
|
||||
onOk={clearValue}
|
||||
@@ -428,4 +435,4 @@ const VerifyClearIndicator = <
|
||||
);
|
||||
};
|
||||
|
||||
export default BlacktagsSelector;
|
||||
export default BlacklistedTagsSelector;
|
||||
@@ -67,7 +67,7 @@ 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 Blacktags',
|
||||
'process-blacklisted-tags': 'Process Blacklisted Tags',
|
||||
editJobSchedule: 'Modify Job',
|
||||
jobScheduleEditSaved: 'Job edited successfully!',
|
||||
jobScheduleEditFailed: 'Something went wrong while saving the job.',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import BlacktagsSelector from '@app/components/BlacktagsSelector';
|
||||
import BlacklistedTagsSelector from '@app/components/BlacklistedTagsSelector';
|
||||
import Button from '@app/components/Common/Button';
|
||||
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
|
||||
import PageTitle from '@app/components/Common/PageTitle';
|
||||
@@ -35,12 +35,12 @@ const messages = defineMessages('components.Settings.SettingsMain', {
|
||||
discoverRegionTip: 'Filter content by regional availability',
|
||||
originallanguage: 'Discover Language',
|
||||
originallanguageTip: 'Filter content by original language',
|
||||
blacktags: 'Blacklist Content with Tags',
|
||||
blacktagsTip:
|
||||
'Automatically add content with tags to the blacklist using the "Process Blacktags" job',
|
||||
blacktagsLimit: 'Limit Content Blacklisted per Tag',
|
||||
blacktagsLimitTip:
|
||||
'The "Process Blacktags" job will blacklist this many pages into each sort. Larger numbers will create a more accurate blacklist, but use more space.',
|
||||
blacklistedTags: 'Blacklist Content with Tags',
|
||||
blacklistedTagsTip:
|
||||
'Automatically add content with tags to the blacklist using the "Process Blacklisted Tags" job',
|
||||
blacklistedTagsLimit: 'Limit Content Blacklisted per Tag',
|
||||
blacklistedTagsLimitTip:
|
||||
'The "Process Blacklisted Tags" job will blacklist this many pages into each sort. Larger numbers will create a more accurate blacklist, but use more space.',
|
||||
streamingRegion: 'Streaming Region',
|
||||
streamingRegionTip: 'Show streaming sites by regional availability',
|
||||
toastApiKeySuccess: 'New API key generated successfully!',
|
||||
@@ -87,7 +87,7 @@ const SettingsMain = () => {
|
||||
intl.formatMessage(messages.validationApplicationUrlTrailingSlash),
|
||||
(value) => !value || !value.endsWith('/')
|
||||
),
|
||||
blacktagsLimit: Yup.number()
|
||||
blacklistedTagsLimit: Yup.number()
|
||||
.test(
|
||||
'positive',
|
||||
'Number must be greater than 0.',
|
||||
@@ -150,8 +150,8 @@ const SettingsMain = () => {
|
||||
discoverRegion: data?.discoverRegion,
|
||||
originalLanguage: data?.originalLanguage,
|
||||
streamingRegion: data?.streamingRegion || 'US',
|
||||
blacktags: data?.blacktags,
|
||||
blacktagsLimit: data?.blacktagsLimit || 50,
|
||||
blacklistedTags: data?.blacklistedTags,
|
||||
blacklistedTagsLimit: data?.blacklistedTagsLimit || 50,
|
||||
partialRequestsEnabled: data?.partialRequestsEnabled,
|
||||
enableSpecialEpisodes: data?.enableSpecialEpisodes,
|
||||
cacheImages: data?.cacheImages,
|
||||
@@ -173,8 +173,8 @@ const SettingsMain = () => {
|
||||
discoverRegion: values.discoverRegion,
|
||||
streamingRegion: values.streamingRegion,
|
||||
originalLanguage: values.originalLanguage,
|
||||
blacktags: values.blacktags,
|
||||
blacktagsLimit: values.blacktagsLimit,
|
||||
blacklistedTags: values.blacklistedTags,
|
||||
blacklistedTagsLimit: values.blacklistedTagsLimit,
|
||||
partialRequestsEnabled: values.partialRequestsEnabled,
|
||||
enableSpecialEpisodes: values.enableSpecialEpisodes,
|
||||
cacheImages: values.cacheImages,
|
||||
@@ -385,41 +385,45 @@ const SettingsMain = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
<label htmlFor="blacktags" className="text-label">
|
||||
<span>{intl.formatMessage(messages.blacktags)}</span>
|
||||
<label htmlFor="blacklistedTags" className="text-label">
|
||||
<span>{intl.formatMessage(messages.blacklistedTags)}</span>
|
||||
<span className="label-tip">
|
||||
{intl.formatMessage(messages.blacktagsTip)}
|
||||
{intl.formatMessage(messages.blacklistedTagsTip)}
|
||||
</span>
|
||||
</label>
|
||||
<div className="form-input-area">
|
||||
<div className="form-input-field relative z-10">
|
||||
<BlacktagsSelector defaultValue={values.blacktags} />
|
||||
<BlacklistedTagsSelector
|
||||
defaultValue={values.blacklistedTags}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
<label htmlFor="blacktagsLimit" className="text-label">
|
||||
<label htmlFor="blacklistedTagsLimit" className="text-label">
|
||||
<span className="mr-2">
|
||||
{intl.formatMessage(messages.blacktagsLimit)}
|
||||
{intl.formatMessage(messages.blacklistedTagsLimit)}
|
||||
</span>
|
||||
<SettingsBadge badgeType="advanced" />
|
||||
<span className="label-tip">
|
||||
{intl.formatMessage(messages.blacktagsLimitTip)}
|
||||
{intl.formatMessage(messages.blacklistedTagsLimitTip)}
|
||||
</span>
|
||||
</label>
|
||||
<div className="form-input-area">
|
||||
<Field
|
||||
id="blacktagsLimit"
|
||||
name="blacktagsLimit"
|
||||
id="blacklistedTagsLimit"
|
||||
name="blacklistedTagsLimit"
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
className="short"
|
||||
placeholder={50}
|
||||
/>
|
||||
{errors.blacktagsLimit &&
|
||||
touched.blacktagsLimit &&
|
||||
typeof errors.blacktagsLimit === 'string' && (
|
||||
<div className="error">{errors.blacktagsLimit}</div>
|
||||
{errors.blacklistedTagsLimit &&
|
||||
touched.blacklistedTagsLimit &&
|
||||
typeof errors.blacklistedTagsLimit === 'string' && (
|
||||
<div className="error">
|
||||
{errors.blacklistedTagsLimit}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"components.Blacklist.blacklistdate": "date",
|
||||
"components.Blacklist.blacklistedby": "{date} by {user}",
|
||||
"components.Blacklist.blacklistsettings": "Blacklist Settings",
|
||||
"components.Blacklist.filterBlacktags": "Blacktags",
|
||||
"components.Blacklist.filterBlacklistedTags": "Blacklisted Tags",
|
||||
"components.Blacklist.filterManual": "Manual",
|
||||
"components.Blacklist.mediaName": "Name",
|
||||
"components.Blacklist.mediaTmdbId": "tmdb Id",
|
||||
@@ -892,7 +892,7 @@
|
||||
"components.Settings.SettingsJobsCache.plex-refresh-token": "Plex Refresh Token",
|
||||
"components.Settings.SettingsJobsCache.plex-watchlist-sync": "Plex Watchlist Sync",
|
||||
"components.Settings.SettingsJobsCache.process": "Process",
|
||||
"components.Settings.SettingsJobsCache.process-blacklisted-tags": "Process Blacktags",
|
||||
"components.Settings.SettingsJobsCache.process-blacklisted-tags": "Process Blacklisted Tags",
|
||||
"components.Settings.SettingsJobsCache.radarr-scan": "Radarr Scan",
|
||||
"components.Settings.SettingsJobsCache.runnow": "Run Now",
|
||||
"components.Settings.SettingsJobsCache.sonarr-scan": "Sonarr Scan",
|
||||
@@ -919,10 +919,10 @@
|
||||
"components.Settings.SettingsMain.apikey": "API Key",
|
||||
"components.Settings.SettingsMain.applicationTitle": "Application Title",
|
||||
"components.Settings.SettingsMain.applicationurl": "Application URL",
|
||||
"components.Settings.SettingsMain.blacktags": "Blacklist Content with Tags",
|
||||
"components.Settings.SettingsMain.blacktagsLimit": "Limit Content Blacklisted per Tag",
|
||||
"components.Settings.SettingsMain.blacktagsLimitTip": "The \"Process Blacktags\" job will blacklist this many pages into each sort. Larger numbers will create a more accurate blacklist, but use more space.",
|
||||
"components.Settings.SettingsMain.blacktagsTip": "Automatically add content with tags to the blacklist using the \"Process Blacktags\" job",
|
||||
"components.Settings.SettingsMain.blacklistedTags": "Blacklist Content with Tags",
|
||||
"components.Settings.SettingsMain.blacklistedTagsLimit": "Limit Content Blacklisted per Tag",
|
||||
"components.Settings.SettingsMain.blacklistedTagsLimitTip": "The \"Process Blacklisted Tags\" job will blacklist this many pages into each sort. Larger numbers will create a more accurate blacklist, but use more space.",
|
||||
"components.Settings.SettingsMain.blacklistedTagsTip": "Automatically add content with tags to the blacklist using the \"Process Blacklisted Tags\" job",
|
||||
"components.Settings.SettingsMain.cacheImages": "Enable Image Caching",
|
||||
"components.Settings.SettingsMain.cacheImagesTip": "Cache externally sourced images (requires a significant amount of disk space)",
|
||||
"components.Settings.SettingsMain.discoverRegion": "Discover Region",
|
||||
@@ -1053,13 +1053,13 @@
|
||||
"components.Settings.addsonarr": "Add Sonarr Server",
|
||||
"components.Settings.advancedTooltip": "Incorrectly configuring this setting may result in broken functionality",
|
||||
"components.Settings.apiKey": "API key",
|
||||
"components.Settings.blacktagImportInstructions": "Paste blacktag configuration below.",
|
||||
"components.Settings.blacktagImportTitle": "Import Blacktag Configuration",
|
||||
"components.Settings.blacklistedTagImportInstructions": "Paste blacklist tag configuration below.",
|
||||
"components.Settings.blacklistedTagImportTitle": "Import Blacklist Tag Configuration",
|
||||
"components.Settings.cancelscan": "Cancel Scan",
|
||||
"components.Settings.clearBlacktagsConfirm": "Are you sure you want to clear the blacktags?",
|
||||
"components.Settings.clearBlacklistedTagsConfirm": "Are you sure you want to clear the blacklisted tags?",
|
||||
"components.Settings.copied": "Copied API key to clipboard.",
|
||||
"components.Settings.copyBlacktags": "Copied blacktags to clipboard.",
|
||||
"components.Settings.copyBlacktagsTip": "Copy blacktag configuration",
|
||||
"components.Settings.copyBlacklistedTags": "Copied blacklisted tags to clipboard.",
|
||||
"components.Settings.copyBlacklistedTagsTip": "Copy blacklisted tags configuration",
|
||||
"components.Settings.currentlibrary": "Current Library: {name}",
|
||||
"components.Settings.default": "Default",
|
||||
"components.Settings.default4k": "Default 4K",
|
||||
@@ -1070,7 +1070,7 @@
|
||||
"components.Settings.experimentalTooltip": "Enabling this setting may result in unexpected application behavior",
|
||||
"components.Settings.externalUrl": "External URL",
|
||||
"components.Settings.hostname": "Hostname or IP Address",
|
||||
"components.Settings.importBlacktagsTip": "Import blacktag configuration",
|
||||
"components.Settings.importBlacklistedTagsTip": "Import blacklisted tags configuration",
|
||||
"components.Settings.invalidKeyword": "{keywordId} is not a TMDB keyword.",
|
||||
"components.Settings.invalidurlerror": "Unable to connect to {mediaServerName} server.",
|
||||
"components.Settings.is4k": "4K",
|
||||
|
||||
Reference in New Issue
Block a user