mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat: media delete option in manage media slideover
This commit is contained in:
@@ -2106,6 +2106,23 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/MediaInfo'
|
$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:
|
security:
|
||||||
- cookieAuth: []
|
- cookieAuth: []
|
||||||
|
@@ -31,6 +31,7 @@ export class MediaRequest {
|
|||||||
|
|
||||||
@ManyToOne(() => Media, (media) => media.requests, {
|
@ManyToOne(() => Media, (media) => media.requests, {
|
||||||
eager: true,
|
eager: true,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
})
|
})
|
||||||
public media: Media;
|
public media: Media;
|
||||||
|
|
||||||
|
@@ -2,6 +2,9 @@ import { Router } from 'express';
|
|||||||
import { getRepository, FindOperator, FindOneOptions } from 'typeorm';
|
import { getRepository, FindOperator, FindOneOptions } from 'typeorm';
|
||||||
import Media from '../entity/Media';
|
import Media from '../entity/Media';
|
||||||
import { MediaStatus } from '../constants/media';
|
import { MediaStatus } from '../constants/media';
|
||||||
|
import logger from '../logger';
|
||||||
|
import { isAuthenticated } from '../middleware/auth';
|
||||||
|
import { Permission } from '../lib/permissions';
|
||||||
|
|
||||||
export interface MediaResultsResponse {
|
export interface MediaResultsResponse {
|
||||||
pageInfo: {
|
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;
|
export default mediaRoutes;
|
||||||
|
@@ -21,7 +21,7 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const buttonStyle = [
|
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) {
|
switch (buttonType) {
|
||||||
case 'primary':
|
case 'primary':
|
||||||
@@ -73,7 +73,7 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<button className={buttonStyle.join(' ')} {...props}>
|
<button className={buttonStyle.join(' ')} {...props}>
|
||||||
{children}
|
<span className="flex items-center">{children}</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -113,6 +113,13 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteMedia = async () => {
|
||||||
|
if (data?.mediaInfo?.id) {
|
||||||
|
await axios.delete(`/api/v1/media/${data?.mediaInfo?.id}`);
|
||||||
|
revalidate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="bg-cover bg-center -mx-4 -mt-2 px-4 sm:px-8 pt-4 "
|
className="bg-cover bg-center -mx-4 -mt-2 px-4 sm:px-8 pt-4 "
|
||||||
@@ -155,6 +162,22 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
{data?.mediaInfo && (
|
||||||
|
<div className="mt-8">
|
||||||
|
<Button
|
||||||
|
buttonType="danger"
|
||||||
|
className="w-full text-center"
|
||||||
|
onClick={() => deleteMedia()}
|
||||||
|
>
|
||||||
|
Clear All Media Data
|
||||||
|
</Button>
|
||||||
|
<div className="text-sm text-cool-gray-400 mt-2">
|
||||||
|
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.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</SlideOver>
|
</SlideOver>
|
||||||
<div className="flex flex-col items-center md:flex-row md:items-end pt-4">
|
<div className="flex flex-col items-center md:flex-row md:items-end pt-4">
|
||||||
<div className="mr-4 flex-shrink-0">
|
<div className="mr-4 flex-shrink-0">
|
||||||
|
@@ -116,6 +116,13 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
revalidate();
|
revalidate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteMedia = async () => {
|
||||||
|
if (data?.mediaInfo?.id) {
|
||||||
|
await axios.delete(`/api/v1/media/${data?.mediaInfo?.id}`);
|
||||||
|
revalidate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="bg-cover bg-center -mx-4 -mt-2 px-4 sm:px-8 pt-4 "
|
className="bg-cover bg-center -mx-4 -mt-2 px-4 sm:px-8 pt-4 "
|
||||||
@@ -158,6 +165,22 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
{data?.mediaInfo && (
|
||||||
|
<div className="mt-8">
|
||||||
|
<Button
|
||||||
|
buttonType="danger"
|
||||||
|
className="w-full text-center"
|
||||||
|
onClick={() => deleteMedia()}
|
||||||
|
>
|
||||||
|
Clear All Media Data
|
||||||
|
</Button>
|
||||||
|
<div className="text-sm text-cool-gray-400 mt-2">
|
||||||
|
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.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</SlideOver>
|
</SlideOver>
|
||||||
<div className="flex flex-col items-center md:flex-row md:items-end pt-4">
|
<div className="flex flex-col items-center md:flex-row md:items-end pt-4">
|
||||||
<div className="mr-4 flex-shrink-0">
|
<div className="mr-4 flex-shrink-0">
|
||||||
|
Reference in New Issue
Block a user