mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2025-12-24 02:39:18 -05:00
Compare commits
1 Commits
4e9ba75377
...
preview-fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a23f62a02 |
@@ -289,7 +289,7 @@ class ExternalAPI {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected removeCache(endpoint: string, options?: Record<string, string>) {
|
protected removeCache(endpoint: string, options?: Record<string, unknown>) {
|
||||||
const cacheKey = this.serializeCacheKey(endpoint, {
|
const cacheKey = this.serializeCacheKey(endpoint, {
|
||||||
...this.params,
|
...this.params,
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@@ -242,10 +242,13 @@ class RadarrAPI extends ServarrBase<{ movieId: number }> {
|
|||||||
if (tmdbId) {
|
if (tmdbId) {
|
||||||
this.removeCache('/movie/lookup', {
|
this.removeCache('/movie/lookup', {
|
||||||
term: `tmdb:${tmdbId}`,
|
term: `tmdb:${tmdbId}`,
|
||||||
|
headers: this.defaultHeaders,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (externalId) {
|
if (externalId) {
|
||||||
this.removeCache(`/movie/${externalId}`);
|
this.removeCache(`/movie/${externalId}`, {
|
||||||
|
headers: this.defaultHeaders,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -368,14 +368,18 @@ class SonarrAPI extends ServarrBase<{
|
|||||||
if (tvdbId) {
|
if (tvdbId) {
|
||||||
this.removeCache('/series/lookup', {
|
this.removeCache('/series/lookup', {
|
||||||
term: `tvdb:${tvdbId}`,
|
term: `tvdb:${tvdbId}`,
|
||||||
|
headers: this.defaultHeaders,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (externalId) {
|
if (externalId) {
|
||||||
this.removeCache(`/series/${externalId}`);
|
this.removeCache(`/series/${externalId}`, {
|
||||||
|
headers: this.defaultHeaders,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (title) {
|
if (title) {
|
||||||
this.removeCache('/series/lookup', {
|
this.removeCache('/series/lookup', {
|
||||||
term: title,
|
term: title,
|
||||||
|
headers: this.defaultHeaders,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import type { MediaRequest } from '@server/entity/MediaRequest';
|
|||||||
import type { NonFunctionProperties, PaginatedResponse } from './common';
|
import type { NonFunctionProperties, PaginatedResponse } from './common';
|
||||||
|
|
||||||
export interface RequestResultsResponse extends PaginatedResponse {
|
export interface RequestResultsResponse extends PaginatedResponse {
|
||||||
results: NonFunctionProperties<MediaRequest>[];
|
results: (NonFunctionProperties<MediaRequest> & {
|
||||||
|
profileName?: string;
|
||||||
|
canRemove?: boolean;
|
||||||
|
})[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MediaRequestBody = {
|
export type MediaRequestBody = {
|
||||||
|
|||||||
@@ -237,19 +237,6 @@ mediaRoutes.delete(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isMovie) {
|
if (isMovie) {
|
||||||
// check if the movie exists
|
|
||||||
try {
|
|
||||||
await (service as RadarrAPI).getMovie({
|
|
||||||
id: parseInt(
|
|
||||||
is4k
|
|
||||||
? (media.externalServiceSlug4k as string)
|
|
||||||
: (media.externalServiceSlug as string)
|
|
||||||
),
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
return res.status(204).send();
|
|
||||||
}
|
|
||||||
// remove the movie
|
|
||||||
await (service as RadarrAPI).removeMovie(
|
await (service as RadarrAPI).removeMovie(
|
||||||
parseInt(
|
parseInt(
|
||||||
is4k
|
is4k
|
||||||
@@ -264,13 +251,6 @@ mediaRoutes.delete(
|
|||||||
if (!tvdbId) {
|
if (!tvdbId) {
|
||||||
throw new Error('TVDB ID not found');
|
throw new Error('TVDB ID not found');
|
||||||
}
|
}
|
||||||
// check if the series exists
|
|
||||||
try {
|
|
||||||
await (service as SonarrAPI).getSeriesByTvdbId(tvdbId);
|
|
||||||
} catch {
|
|
||||||
return res.status(204).send();
|
|
||||||
}
|
|
||||||
// remove the series
|
|
||||||
await (service as SonarrAPI).removeSerie(tvdbId);
|
await (service as SonarrAPI).removeSerie(tvdbId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ requestRoutes.get<Record<string, unknown>, RequestResultsResponse>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// add profile names to the media requests, with undefined if not found
|
// add profile names to the media requests, with undefined if not found
|
||||||
const requestsWithProfileNames = requests.map((r) => {
|
let mappedRequests = requests.map((r) => {
|
||||||
switch (r.type) {
|
switch (r.type) {
|
||||||
case MediaType.MOVIE: {
|
case MediaType.MOVIE: {
|
||||||
const profileName = radarrServers
|
const profileName = radarrServers
|
||||||
@@ -212,6 +212,36 @@ requestRoutes.get<Record<string, unknown>, RequestResultsResponse>(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// add canRemove prop if user has permission
|
||||||
|
if (req.user?.hasPermission(Permission.MANAGE_REQUESTS)) {
|
||||||
|
mappedRequests = mappedRequests.map((r) => {
|
||||||
|
switch (r.type) {
|
||||||
|
case MediaType.MOVIE: {
|
||||||
|
return {
|
||||||
|
...r,
|
||||||
|
// check if the radarr server for this request is configured
|
||||||
|
canRemove: radarrServers.some(
|
||||||
|
(server) =>
|
||||||
|
server.id ===
|
||||||
|
(r.is4k ? r.media.serviceId4k : r.media.serviceId)
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case MediaType.TV: {
|
||||||
|
return {
|
||||||
|
...r,
|
||||||
|
// check if the sonarr server for this request is configured
|
||||||
|
canRemove: sonarrServers.some(
|
||||||
|
(server) =>
|
||||||
|
server.id ===
|
||||||
|
(r.is4k ? r.media.serviceId4k : r.media.serviceId)
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
pageInfo: {
|
pageInfo: {
|
||||||
pages: Math.ceil(requestCount / pageSize),
|
pages: Math.ceil(requestCount / pageSize),
|
||||||
@@ -219,7 +249,7 @@ requestRoutes.get<Record<string, unknown>, RequestResultsResponse>(
|
|||||||
results: requestCount,
|
results: requestCount,
|
||||||
page: Math.ceil(skip / pageSize) + 1,
|
page: Math.ceil(skip / pageSize) + 1,
|
||||||
},
|
},
|
||||||
results: requestsWithProfileNames,
|
results: mappedRequests,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
next({ status: 500, message: e.message });
|
next({ status: 500, message: e.message });
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ import {
|
|||||||
TrashIcon,
|
TrashIcon,
|
||||||
XMarkIcon,
|
XMarkIcon,
|
||||||
} from '@heroicons/react/24/solid';
|
} from '@heroicons/react/24/solid';
|
||||||
import { MediaRequestStatus, MediaType } from '@server/constants/media';
|
import { MediaRequestStatus } from '@server/constants/media';
|
||||||
import type { MediaRequest } from '@server/entity/MediaRequest';
|
import type { MediaRequest } from '@server/entity/MediaRequest';
|
||||||
import type { NonFunctionProperties } from '@server/interfaces/api/common';
|
import type { NonFunctionProperties } from '@server/interfaces/api/common';
|
||||||
import type { RadarrSettings, SonarrSettings } from '@server/lib/settings';
|
import type { RequestResultsResponse } from '@server/interfaces/api/requestInterfaces';
|
||||||
import type { MovieDetails } from '@server/models/Movie';
|
import type { MovieDetails } from '@server/models/Movie';
|
||||||
import type { TvDetails } from '@server/models/Tv';
|
import type { TvDetails } from '@server/models/Tv';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
@@ -292,18 +292,11 @@ const RequestItemError = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface RequestItemProps {
|
interface RequestItemProps {
|
||||||
request: NonFunctionProperties<MediaRequest> & { profileName?: string };
|
request: RequestResultsResponse['results'][number];
|
||||||
revalidateList: () => void;
|
revalidateList: () => void;
|
||||||
radarrData?: RadarrSettings[];
|
|
||||||
sonarrData?: SonarrSettings[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const RequestItem = ({
|
const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
|
||||||
request,
|
|
||||||
revalidateList,
|
|
||||||
radarrData,
|
|
||||||
sonarrData,
|
|
||||||
}: RequestItemProps) => {
|
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
const { ref, inView } = useInView({
|
const { ref, inView } = useInView({
|
||||||
triggerOnce: true,
|
triggerOnce: true,
|
||||||
@@ -398,23 +391,6 @@ const RequestItem = ({
|
|||||||
iOSPlexUrl4k: requestData?.media?.iOSPlexUrl4k,
|
iOSPlexUrl4k: requestData?.media?.iOSPlexUrl4k,
|
||||||
});
|
});
|
||||||
|
|
||||||
const serviceExists = () => {
|
|
||||||
if (title?.mediaInfo) {
|
|
||||||
if (title?.mediaInfo.mediaType === MediaType.MOVIE) {
|
|
||||||
return (
|
|
||||||
radarrData?.find((radarr) => radarr.id === request.serverId) !==
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
sonarrData?.find((sonarr) => sonarr.id === request.serverId) !==
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!title && !error) {
|
if (!title && !error) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -722,30 +698,30 @@ const RequestItem = ({
|
|||||||
)}
|
)}
|
||||||
{requestData.status !== MediaRequestStatus.PENDING &&
|
{requestData.status !== MediaRequestStatus.PENDING &&
|
||||||
hasPermission(Permission.MANAGE_REQUESTS) && (
|
hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||||
<ConfirmButton
|
<>
|
||||||
onClick={() => deleteRequest()}
|
<ConfirmButton
|
||||||
confirmText={intl.formatMessage(globalMessages.areyousure)}
|
onClick={() => deleteRequest()}
|
||||||
className="w-full"
|
confirmText={intl.formatMessage(globalMessages.areyousure)}
|
||||||
>
|
className="w-full"
|
||||||
<TrashIcon />
|
>
|
||||||
<span>{intl.formatMessage(messages.deleterequest)}</span>
|
<TrashIcon />
|
||||||
</ConfirmButton>
|
<span>{intl.formatMessage(messages.deleterequest)}</span>
|
||||||
)}
|
</ConfirmButton>
|
||||||
{hasPermission(Permission.MANAGE_REQUESTS) &&
|
{request.canRemove && (
|
||||||
title?.mediaInfo?.serviceId &&
|
<ConfirmButton
|
||||||
serviceExists() && (
|
onClick={() => deleteMediaFile()}
|
||||||
<ConfirmButton
|
confirmText={intl.formatMessage(globalMessages.areyousure)}
|
||||||
onClick={() => deleteMediaFile()}
|
className="w-full"
|
||||||
confirmText={intl.formatMessage(globalMessages.areyousure)}
|
>
|
||||||
className="w-full"
|
<TrashIcon />
|
||||||
>
|
<span>
|
||||||
<TrashIcon />
|
{intl.formatMessage(messages.removearr, {
|
||||||
<span>
|
arr: request.type === 'movie' ? 'Radarr' : 'Sonarr',
|
||||||
{intl.formatMessage(messages.removearr, {
|
})}
|
||||||
arr: request.type === 'movie' ? 'Radarr' : 'Sonarr',
|
</span>
|
||||||
})}
|
</ConfirmButton>
|
||||||
</span>
|
)}
|
||||||
</ConfirmButton>
|
</>
|
||||||
)}
|
)}
|
||||||
{requestData.status === MediaRequestStatus.PENDING &&
|
{requestData.status === MediaRequestStatus.PENDING &&
|
||||||
hasPermission(Permission.MANAGE_REQUESTS) && (
|
hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ import {
|
|||||||
FunnelIcon,
|
FunnelIcon,
|
||||||
} from '@heroicons/react/24/solid';
|
} from '@heroicons/react/24/solid';
|
||||||
import type { RequestResultsResponse } from '@server/interfaces/api/requestInterfaces';
|
import type { RequestResultsResponse } from '@server/interfaces/api/requestInterfaces';
|
||||||
import { Permission } from '@server/lib/permissions';
|
|
||||||
import type { RadarrSettings, SonarrSettings } from '@server/lib/settings';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
@@ -53,7 +51,7 @@ const RequestList = () => {
|
|||||||
const { user } = useUser({
|
const { user } = useUser({
|
||||||
id: Number(router.query.userId),
|
id: Number(router.query.userId),
|
||||||
});
|
});
|
||||||
const { user: currentUser, hasPermission } = useUser();
|
const { user: currentUser } = useUser();
|
||||||
const [currentFilter, setCurrentFilter] = useState<Filter>(Filter.PENDING);
|
const [currentFilter, setCurrentFilter] = useState<Filter>(Filter.PENDING);
|
||||||
const [currentSort, setCurrentSort] = useState<Sort>('added');
|
const [currentSort, setCurrentSort] = useState<Sort>('added');
|
||||||
const [currentSortDirection, setCurrentSortDirection] =
|
const [currentSortDirection, setCurrentSortDirection] =
|
||||||
@@ -64,13 +62,6 @@ const RequestList = () => {
|
|||||||
const pageIndex = page - 1;
|
const pageIndex = page - 1;
|
||||||
const updateQueryParams = useUpdateQueryParams({ page: page.toString() });
|
const updateQueryParams = useUpdateQueryParams({ page: page.toString() });
|
||||||
|
|
||||||
const { data: radarrData } = useSWR<RadarrSettings[]>(
|
|
||||||
hasPermission(Permission.ADMIN) ? '/api/v1/settings/radarr' : null
|
|
||||||
);
|
|
||||||
const { data: sonarrData } = useSWR<SonarrSettings[]>(
|
|
||||||
hasPermission(Permission.ADMIN) ? '/api/v1/settings/sonarr' : null
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
error,
|
error,
|
||||||
@@ -254,8 +245,6 @@ const RequestList = () => {
|
|||||||
<RequestItem
|
<RequestItem
|
||||||
request={request}
|
request={request}
|
||||||
revalidateList={() => revalidate()}
|
revalidateList={() => revalidate()}
|
||||||
radarrData={radarrData}
|
|
||||||
sonarrData={sonarrData}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user