Skip to content

Commit

Permalink
feat: blacklist media items
Browse files Browse the repository at this point in the history
re #490
  • Loading branch information
JoaquinOlivero committed May 3, 2024
1 parent c67d4fc commit 0b18995
Show file tree
Hide file tree
Showing 31 changed files with 1,498 additions and 445 deletions.
76 changes: 76 additions & 0 deletions overseerr-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ components:
type: string
media:
$ref: '#/components/schemas/MediaInfo'
userId:
type: number
example: 1
Watchlist:
type: object
properties:
Expand Down Expand Up @@ -2826,6 +2829,63 @@ paths:
responses:
'204':
description: 'Flushed cache'
/settings/blacklist:
get:
summary: Returns blacklisted items
description: Returns list of all blacklisted media
tags:
- settings
parameters:
- in: query
name: take
schema:
type: number
nullable: true
example: 25
- in: query
name: skip
schema:
type: number
nullable: true
example: 0
- in: query
name: search
schema:
type: string
nullable: true
example: dune
responses:
'200':
description: Blacklisted items returned
content:
application/json:
schema:
type: object
properties:
pageInfo:
$ref: '#/components/schemas/PageInfo'
results:
type: array
items:
type: object
properties:
user:
$ref: '#/components/schemas/User'
createdAt:
type: string
example: 2024-04-21T01:55:44.000Z
id:
type: number
example: 1
mediaType:
type: string
example: movie
title:
type: string
example: Dune
tmdbId:
type: number
example: 438631
/settings/logs:
get:
summary: Returns logs
Expand Down Expand Up @@ -4068,6 +4128,22 @@ paths:
description: Item succesfully blacklisted
'412':
description: Item has already been blacklisted
/blacklist/{tmdbId}:
delete:
summary: Remove media from blacklist
tags:
- blacklist
parameters:
- in: path
name: tmdbId
description: tmdbId ID
required: true
example: '1'
schema:
type: string
responses:
'204':
description: Succesfully removed media item
/watchlist:
post:
summary: Add media to watchlist
Expand Down
1 change: 1 addition & 0 deletions server/constants/media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export enum MediaStatus {
PROCESSING,
PARTIALLY_AVAILABLE,
AVAILABLE,
BLACKLISTED,
}
50 changes: 44 additions & 6 deletions server/entity/Blacklist.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import type { MediaType } from '@server/constants/media';
import { MediaStatus, type MediaType } from '@server/constants/media';
import { getRepository } from '@server/datasource';
import Media from '@server/entity/Media';
import { User } from '@server/entity/User';
import type { BlacklistItem } from '@server/interfaces/api/discoverInterfaces';
import {
Column,
CreateDateColumn,
Entity,
Index,
JoinColumn,
ManyToOne,
OneToOne,
PrimaryGeneratedColumn,
Unique,
} from 'typeorm';
Expand All @@ -23,8 +29,20 @@ export class Blacklist implements BlacklistItem {
title?: string;

@Column()
@Index()
public tmdbId: number;

@ManyToOne(() => User, (user) => user.id, {
eager: true,
})
user: User;

@OneToOne(() => Media, (media) => media.blacklist, {
onDelete: 'CASCADE',
})
@JoinColumn()
public media: Media;

@CreateDateColumn()
public createdAt: Date;

Expand All @@ -41,13 +59,33 @@ export class Blacklist implements BlacklistItem {
tmdbId: ZodNumber['_output'];
};
}): Promise<void> {
const blacklistRepository = getRepository(this);

const blacklistItem = new this({
const blacklist = new this({
...blacklistRequest,
});

await blacklistRepository.save(blacklistItem);
return;
const mediaRepository = getRepository(Media);
let media = await mediaRepository.findOne({
where: {
tmdbId: blacklistRequest.tmdbId,
},
});

if (!media) {
media = new Media({
tmdbId: blacklistRequest.tmdbId,
status: MediaStatus.BLACKLISTED,
status4k: MediaStatus.BLACKLISTED,
mediaType: blacklistRequest.mediaType,
blacklist: blacklist,
});

const blacklistRepository = getRepository(this);

await blacklistRepository.save(blacklist);

await mediaRepository.save(media);

return;
}
}
}
9 changes: 8 additions & 1 deletion server/entity/Media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import SonarrAPI from '@server/api/servarr/sonarr';
import { MediaStatus, MediaType } from '@server/constants/media';
import { MediaServerType } from '@server/constants/server';
import { getRepository } from '@server/datasource';
import { Blacklist } from '@server/entity/Blacklist';
import type { User } from '@server/entity/User';
import { Watchlist } from '@server/entity/Watchlist';
import type { DownloadingItem } from '@server/lib/downloadtracker';
Expand All @@ -16,6 +17,7 @@ import {
Entity,
Index,
OneToMany,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
Expand Down Expand Up @@ -65,7 +67,7 @@ class Media {

try {
const media = await mediaRepository.findOne({
where: { tmdbId: id, mediaType },
where: { tmdbId: id, mediaType: mediaType },
relations: { requests: true, issues: true },
});

Expand Down Expand Up @@ -115,6 +117,11 @@ class Media {
@OneToMany(() => Issue, (issue) => issue.media, { cascade: true })
public issues: Issue[];

@OneToOne(() => Blacklist, (blacklist) => blacklist.media, {
eager: true,
})
public blacklist: Blacklist;

@CreateDateColumn()
public createdAt: Date;

Expand Down
11 changes: 11 additions & 0 deletions server/entity/MediaRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class RequestPermissionError extends Error {}
export class QuotaRestrictedError extends Error {}
export class DuplicateMediaRequestError extends Error {}
export class NoSeasonsAvailableError extends Error {}
export class BlacklistedMediaError extends Error {}

type MediaRequestOptions = {
isAutoRequest?: boolean;
Expand Down Expand Up @@ -143,6 +144,16 @@ export class MediaRequest {
mediaType: requestBody.mediaType,
});
} else {
if (media.status === MediaStatus.BLACKLISTED) {
logger.warn('Request for media blocked due to being blacklisted', {
tmdbId: tmdbMedia.id,
mediaType: requestBody.mediaType,
label: 'Media Request',
});

throw new BlacklistedMediaError('This media is blacklisted.');
}

if (media.status === MediaStatus.UNKNOWN && !requestBody.is4k) {
media.status = MediaStatus.PENDING;
}
Expand Down
1 change: 1 addition & 0 deletions server/interfaces/api/blacklistAdd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export const blacklistAdd = z.object({
tmdbId: z.coerce.number(),
mediaType: z.nativeEnum(MediaType),
title: z.coerce.string().optional(),
user: z.coerce.number(),
});
4 changes: 4 additions & 0 deletions server/interfaces/api/discoverInterfaces.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { User } from '@server/entity/User';

export interface GenreSliderItem {
id: number;
name: string;
Expand All @@ -22,4 +24,6 @@ export interface BlacklistItem {
tmdbId: number;
mediaType: 'movie' | 'tv';
title?: string;
createdAt?: Date;
user: User;
}
5 changes: 5 additions & 0 deletions server/interfaces/api/settingsInterfaces.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { BlacklistItem } from '@server/interfaces/api/discoverInterfaces';
import type { PaginatedResponse } from './common';

export type LogMessage = {
Expand Down Expand Up @@ -68,3 +69,7 @@ export interface StatusResponse {
commitsBehind: number;
restartRequired: boolean;
}

export interface BlacklistResultsResponse extends PaginatedResponse {
results: BlacklistItem[];
}
3 changes: 3 additions & 0 deletions server/lib/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export enum Permission {
AUTO_REQUEST_TV = 33554432,
RECENT_VIEW = 67108864,
WATCHLIST_VIEW = 134217728,
MANAGE_BLACKLIST = 268435456,
MEDIA_BLACKLIST = 536870912,
VIEW_BLACKLIST = 1073741824,
}

export interface PermissionCheckOptions {
Expand Down
7 changes: 6 additions & 1 deletion server/migration/1699901142442-AddBlacklist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ export class AddBlacklist1699901142442 implements MigrationInterface {

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "blacklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId"))`
`CREATE TABLE "blacklist" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "title" varchar, "tmdbId" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')),"userId" integer, "mediaId" integer,CONSTRAINT "UQ_6bbafa28411e6046421991ea21c" UNIQUE ("tmdbId", "userId"))`
);

await queryRunner.query(
`CREATE INDEX "IDX_6bbafa28411e6046421991ea21" ON "blacklist" ("tmdbId") `
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "blacklist"`);
await queryRunner.query(`DROP INDEX "IDX_6bbafa28411e6046421991ea21"`);
}
}
Loading

0 comments on commit 0b18995

Please sign in to comment.