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:
Ben Beauchamp
2025-04-11 09:48:44 -05:00
committed by GitHub
parent a488f850f3
commit 4a5ac3cc42
21 changed files with 1105 additions and 100 deletions

View File

@@ -19,39 +19,54 @@ export const blacklistAdd = z.object({
user: z.coerce.number(),
});
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', 'blacklistedTags']).optional(),
});
blacklistRoutes.get(
'/',
isAuthenticated([Permission.MANAGE_BLACKLIST, Permission.VIEW_BLACKLIST], {
type: 'or',
}),
async (req, res, next) => {
const pageSize = req.query.take ? Number(req.query.take) : 25;
const skip = req.query.skip ? Number(req.query.skip) : 0;
const search = (req.query.search as string) ?? '';
const { take, skip, search, filter } = blacklistGet.parse(req.query);
try {
let query = getRepository(Blacklist)
.createQueryBuilder('blacklist')
.leftJoinAndSelect('blacklist.user', 'user');
.leftJoinAndSelect('blacklist.user', 'user')
.where('1 = 1'); // Allow use of andWhere later
if (search.length > 0) {
query = query.where('blacklist.title like :title', {
switch (filter) {
case 'manual':
query = query.andWhere('blacklist.blacklistedTags IS NULL');
break;
case 'blacklistedTags':
query = query.andWhere('blacklist.blacklistedTags IS NOT NULL');
break;
}
if (search) {
query = query.andWhere('blacklist.title like :title', {
title: `%${search}%`,
});
}
const [blacklistedItems, itemsCount] = await query
.orderBy('blacklist.createdAt', 'DESC')
.take(pageSize)
.take(take)
.skip(skip)
.getManyAndCount();
return res.status(200).json({
pageInfo: {
pages: Math.ceil(itemsCount / pageSize),
pageSize,
pages: Math.ceil(itemsCount / take),
pageSize: take,
results: itemsCount,
page: Math.ceil(skip / pageSize) + 1,
page: Math.ceil(skip / take) + 1,
},
results: blacklistedItems,
} as BlacklistResultsResponse);