diff --git a/overseerr-api.yml b/overseerr-api.yml index 2c35f4906..ea4877409 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -2106,6 +2106,23 @@ paths: type: array items: $ref: '#/components/schemas/MediaInfo' + /media/{mediaId}: + delete: + summary: Delete a media item + description: Removes a media item. The `MANAGE_REQUESTS` permission is required to perform this action. + tags: + - media + parameters: + - in: path + name: mediaId + description: Media ID + required: true + example: 1 + schema: + type: string + responses: + '204': + description: Succesfully removed media item security: - cookieAuth: [] diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index 987b05393..e27f96185 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -31,6 +31,7 @@ export class MediaRequest { @ManyToOne(() => Media, (media) => media.requests, { eager: true, + onDelete: 'CASCADE', }) public media: Media; diff --git a/server/routes/media.ts b/server/routes/media.ts index ca83099aa..8bda728bf 100644 --- a/server/routes/media.ts +++ b/server/routes/media.ts @@ -2,6 +2,9 @@ import { Router } from 'express'; import { getRepository, FindOperator, FindOneOptions } from 'typeorm'; import Media from '../entity/Media'; import { MediaStatus } from '../constants/media'; +import logger from '../logger'; +import { isAuthenticated } from '../middleware/auth'; +import { Permission } from '../lib/permissions'; export interface MediaResultsResponse { pageInfo: { @@ -78,4 +81,28 @@ mediaRoutes.get('/', async (req, res, next) => { } }); +mediaRoutes.delete( + '/:id', + isAuthenticated(Permission.MANAGE_REQUESTS), + async (req, res, next) => { + try { + const mediaRepository = getRepository(Media); + + const media = await mediaRepository.findOneOrFail({ + where: { id: req.params.id }, + }); + + await mediaRepository.remove(media); + + return res.status(204).send(); + } catch (e) { + logger.error('Something went wrong fetching media in delete request', { + label: 'Media', + message: e.message, + }); + next({ status: 404, message: 'Media not found' }); + } + } +); + export default mediaRoutes; diff --git a/src/components/Common/Button/index.tsx b/src/components/Common/Button/index.tsx index b1f9d05af..4af5e0606 100644 --- a/src/components/Common/Button/index.tsx +++ b/src/components/Common/Button/index.tsx @@ -21,7 +21,7 @@ const Button: React.FC = ({ ...props }) => { const buttonStyle = [ - 'inline-flex items-center border border-transparent leading-5 font-medium rounded-md focus:outline-none transition ease-in-out duration-150', + 'inline-flex items-center justify-center border border-transparent leading-5 font-medium rounded-md focus:outline-none transition ease-in-out duration-150', ]; switch (buttonType) { case 'primary': @@ -73,7 +73,7 @@ const Button: React.FC = ({ } return ( ); }; diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index f4b19d00a..10d7311ec 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -113,6 +113,13 @@ const MovieDetails: React.FC = ({ movie }) => { } }; + const deleteMedia = async () => { + if (data?.mediaInfo?.id) { + await axios.delete(`/api/v1/media/${data?.mediaInfo?.id}`); + revalidate(); + } + }; + return (
= ({ movie }) => { )}
+ {data?.mediaInfo && ( +
+ +
+ This will remove all media data including all requests for this + item. This action is irreversible. If this item exists in your + Plex library, the media information will be recreated next sync. +
+
+ )}
diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index 70afd95ad..3f62c9240 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -116,6 +116,13 @@ const TvDetails: React.FC = ({ tv }) => { revalidate(); }; + const deleteMedia = async () => { + if (data?.mediaInfo?.id) { + await axios.delete(`/api/v1/media/${data?.mediaInfo?.id}`); + revalidate(); + } + }; + return (
= ({ tv }) => { )}
+ {data?.mediaInfo && ( +
+ +
+ This will remove all media data including all requests for this + item. This action is irreversible. If this item exists in your + Plex library, the media information will be recreated next sync. +
+
+ )}