feat(blocklist): add support for collections (#1841)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { MediaType } from '@server/constants/media';
|
||||
import { getRepository } from '@server/datasource';
|
||||
import TheMovieDb from '@server/api/themoviedb';
|
||||
import { MediaStatus, MediaType } from '@server/constants/media';
|
||||
import dataSource, { getRepository } from '@server/datasource';
|
||||
import { Blocklist } from '@server/entity/Blocklist';
|
||||
import Media from '@server/entity/Media';
|
||||
import type { BlocklistResultsResponse } from '@server/interfaces/api/blocklistInterfaces';
|
||||
@@ -7,7 +8,7 @@ import { Permission } from '@server/lib/permissions';
|
||||
import logger from '@server/logger';
|
||||
import { isAuthenticated } from '@server/middleware/auth';
|
||||
import { Router } from 'express';
|
||||
import { EntityNotFoundError, QueryFailedError } from 'typeorm';
|
||||
import { EntityNotFoundError, In, QueryFailedError } from 'typeorm';
|
||||
import { z } from 'zod';
|
||||
|
||||
const blocklistRoutes = Router();
|
||||
@@ -17,6 +18,7 @@ export const blocklistAdd = z.object({
|
||||
mediaType: z.nativeEnum(MediaType),
|
||||
title: z.coerce.string().optional(),
|
||||
user: z.coerce.number(),
|
||||
blocklistedTags: z.string().optional(),
|
||||
});
|
||||
|
||||
const blocklistGet = z.object({
|
||||
@@ -158,6 +160,107 @@ blocklistRoutes.post(
|
||||
}
|
||||
);
|
||||
|
||||
blocklistRoutes.post(
|
||||
'/collection/:id',
|
||||
isAuthenticated([Permission.MANAGE_BLOCKLIST], {
|
||||
type: 'or',
|
||||
}),
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const tmdb = new TheMovieDb();
|
||||
const collection = await tmdb.getCollection({
|
||||
collectionId: Number(req.params.id),
|
||||
language: req.locale,
|
||||
});
|
||||
|
||||
const uniqueParts = [
|
||||
...new Map(collection.parts.map((p) => [p.id, p])).values(),
|
||||
];
|
||||
const partIds = uniqueParts.map((p) => p.id);
|
||||
if (partIds.length === 0) {
|
||||
return res.status(201).send();
|
||||
}
|
||||
|
||||
await dataSource.transaction(async (em) => {
|
||||
const blocklistRepository = em.getRepository(Blocklist);
|
||||
const mediaRepository = em.getRepository(Media);
|
||||
|
||||
const [existingBlocklists, existingMedia] = await Promise.all([
|
||||
blocklistRepository.find({
|
||||
where: { tmdbId: In(partIds), mediaType: MediaType.MOVIE },
|
||||
}),
|
||||
mediaRepository.find({
|
||||
where: { tmdbId: In(partIds), mediaType: MediaType.MOVIE },
|
||||
}),
|
||||
]);
|
||||
const blocklistByTmdbId = new Map(
|
||||
existingBlocklists.map((b) => [b.tmdbId, b])
|
||||
);
|
||||
const mediaByTmdbId = new Map(existingMedia.map((m) => [m.tmdbId, m]));
|
||||
|
||||
await Promise.all(
|
||||
uniqueParts.map(async (part) => {
|
||||
if (blocklistByTmdbId.has(part.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let blocklist = new Blocklist({
|
||||
tmdbId: part.id,
|
||||
mediaType: MediaType.MOVIE,
|
||||
title: part.title,
|
||||
user: req.user,
|
||||
});
|
||||
|
||||
try {
|
||||
await blocklistRepository.save(blocklist);
|
||||
} catch (error) {
|
||||
if (
|
||||
!(error instanceof QueryFailedError) ||
|
||||
error.driverError.errno !== 19
|
||||
) {
|
||||
throw error;
|
||||
}
|
||||
const row = await blocklistRepository.findOne({
|
||||
where: { tmdbId: part.id, mediaType: MediaType.MOVIE },
|
||||
});
|
||||
if (!row) {
|
||||
throw error;
|
||||
}
|
||||
blocklist = row;
|
||||
}
|
||||
|
||||
let media = mediaByTmdbId.get(part.id);
|
||||
if (!media) {
|
||||
media = new Media({
|
||||
tmdbId: part.id,
|
||||
status: MediaStatus.BLOCKLISTED,
|
||||
status4k: MediaStatus.BLOCKLISTED,
|
||||
mediaType: MediaType.MOVIE,
|
||||
blocklist: Promise.resolve(blocklist),
|
||||
});
|
||||
} else {
|
||||
media.status = MediaStatus.BLOCKLISTED;
|
||||
media.status4k = MediaStatus.BLOCKLISTED;
|
||||
media.blocklist = Promise.resolve(blocklist);
|
||||
}
|
||||
|
||||
await mediaRepository.save(media);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return res.status(201).send();
|
||||
} catch (e) {
|
||||
logger.error('Error blocklisting collection', {
|
||||
label: 'Blocklist',
|
||||
errorMessage: e.message,
|
||||
collectionId: req.params.id,
|
||||
});
|
||||
return next({ status: 500, message: e.message });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
blocklistRoutes.delete(
|
||||
'/:id',
|
||||
isAuthenticated([Permission.MANAGE_BLOCKLIST], {
|
||||
@@ -208,4 +311,54 @@ blocklistRoutes.delete(
|
||||
}
|
||||
);
|
||||
|
||||
blocklistRoutes.delete(
|
||||
'/collection/:id',
|
||||
isAuthenticated([Permission.MANAGE_BLOCKLIST], {
|
||||
type: 'or',
|
||||
}),
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const tmdb = new TheMovieDb();
|
||||
const collection = await tmdb.getCollection({
|
||||
collectionId: Number(req.params.id),
|
||||
language: req.locale,
|
||||
});
|
||||
|
||||
await dataSource.transaction(async (em) => {
|
||||
const blocklistRepository = em.getRepository(Blocklist);
|
||||
const mediaRepository = em.getRepository(Media);
|
||||
|
||||
await Promise.all(
|
||||
collection.parts.map(async (part) => {
|
||||
const blocklistItem = await blocklistRepository.findOne({
|
||||
where: { tmdbId: part.id, mediaType: MediaType.MOVIE },
|
||||
});
|
||||
|
||||
if (blocklistItem) {
|
||||
await blocklistRepository.remove(blocklistItem);
|
||||
|
||||
const mediaItem = await mediaRepository.findOne({
|
||||
where: { tmdbId: part.id, mediaType: MediaType.MOVIE },
|
||||
});
|
||||
|
||||
if (mediaItem) {
|
||||
await mediaRepository.remove(mediaItem);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return res.status(204).send();
|
||||
} catch (e) {
|
||||
logger.error('Error unblocklisting collection', {
|
||||
label: 'Blocklist',
|
||||
errorMessage: e.message,
|
||||
collectionId: req.params.id,
|
||||
});
|
||||
return next({ status: 500, message: e.message });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default blocklistRoutes;
|
||||
|
||||
Reference in New Issue
Block a user