mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat(perms): add separate REQUEST_MOVIE and REQUEST_TV permissions (#1474)
* feat(perms): add separate REQUEST_MOVIE and REQUEST_TV permissions * fix(perms): do not require regular request perms for 4K requests
This commit is contained in:
@@ -17,6 +17,8 @@ export enum Permission {
|
|||||||
AUTO_APPROVE_4K = 32768,
|
AUTO_APPROVE_4K = 32768,
|
||||||
AUTO_APPROVE_4K_MOVIE = 65536,
|
AUTO_APPROVE_4K_MOVIE = 65536,
|
||||||
AUTO_APPROVE_4K_TV = 131072,
|
AUTO_APPROVE_4K_TV = 131072,
|
||||||
|
REQUEST_MOVIE = 262144,
|
||||||
|
REQUEST_TV = 524288,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PermissionCheckOptions {
|
export interface PermissionCheckOptions {
|
||||||
|
@@ -139,10 +139,7 @@ requestRoutes.get('/', async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
requestRoutes.post(
|
requestRoutes.post('/', async (req, res, next) => {
|
||||||
'/',
|
|
||||||
isAuthenticated(Permission.REQUEST),
|
|
||||||
async (req, res, next) => {
|
|
||||||
const tmdb = new TheMovieDb();
|
const tmdb = new TheMovieDb();
|
||||||
const mediaRepository = getRepository(Media);
|
const mediaRepository = getRepository(Media);
|
||||||
const requestRepository = getRepository(MediaRequest);
|
const requestRepository = getRepository(MediaRequest);
|
||||||
@@ -175,11 +172,12 @@ requestRoutes.post(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.is4k) {
|
|
||||||
if (
|
if (
|
||||||
req.body.mediaType === MediaType.MOVIE &&
|
req.body.mediaType === MediaType.MOVIE &&
|
||||||
!req.user?.hasPermission(
|
!req.user?.hasPermission(
|
||||||
[Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
|
req.body.is4k
|
||||||
|
? [Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE]
|
||||||
|
: [Permission.REQUEST, Permission.REQUEST_MOVIE],
|
||||||
{
|
{
|
||||||
type: 'or',
|
type: 'or',
|
||||||
}
|
}
|
||||||
@@ -187,12 +185,16 @@ requestRoutes.post(
|
|||||||
) {
|
) {
|
||||||
return next({
|
return next({
|
||||||
status: 403,
|
status: 403,
|
||||||
message: 'You do not have permission to make 4K movie requests.',
|
message: `You do not have permission to make ${
|
||||||
|
req.body.is4k ? '4K ' : ''
|
||||||
|
}movie requests.`,
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
req.body.mediaType === MediaType.TV &&
|
req.body.mediaType === MediaType.TV &&
|
||||||
!req.user?.hasPermission(
|
!req.user?.hasPermission(
|
||||||
[Permission.REQUEST_4K, Permission.REQUEST_4K_TV],
|
req.body.is4k
|
||||||
|
? [Permission.REQUEST_4K, Permission.REQUEST_4K_TV]
|
||||||
|
: [Permission.REQUEST, Permission.REQUEST_TV],
|
||||||
{
|
{
|
||||||
type: 'or',
|
type: 'or',
|
||||||
}
|
}
|
||||||
@@ -200,10 +202,11 @@ requestRoutes.post(
|
|||||||
) {
|
) {
|
||||||
return next({
|
return next({
|
||||||
status: 403,
|
status: 403,
|
||||||
message: 'You do not have permission to make 4K series requests.',
|
message: `You do not have permission to make ${
|
||||||
|
req.body.is4k ? '4K ' : ''
|
||||||
|
}series requests.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const quotas = await requestUser.getQuota();
|
const quotas = await requestUser.getQuota();
|
||||||
|
|
||||||
@@ -418,8 +421,7 @@ requestRoutes.post(
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
next({ status: 500, message: e.message });
|
next({ status: 500, message: e.message });
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
requestRoutes.get('/count', async (_req, res, next) => {
|
requestRoutes.get('/count', async (_req, res, next) => {
|
||||||
const requestRepository = getRepository(MediaRequest);
|
const requestRepository = getRepository(MediaRequest);
|
||||||
|
@@ -108,11 +108,18 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasRequestable =
|
const hasRequestable =
|
||||||
|
hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
||||||
|
type: 'or',
|
||||||
|
}) &&
|
||||||
data.parts.filter(
|
data.parts.filter(
|
||||||
(part) => !part.mediaInfo || part.mediaInfo.status === MediaStatus.UNKNOWN
|
(part) => !part.mediaInfo || part.mediaInfo.status === MediaStatus.UNKNOWN
|
||||||
).length > 0;
|
).length > 0;
|
||||||
|
|
||||||
const hasRequestable4k =
|
const hasRequestable4k =
|
||||||
|
settings.currentSettings.movie4kEnabled &&
|
||||||
|
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE], {
|
||||||
|
type: 'or',
|
||||||
|
}) &&
|
||||||
data.parts.filter(
|
data.parts.filter(
|
||||||
(part) =>
|
(part) =>
|
||||||
!part.mediaInfo || part.mediaInfo.status4k === MediaStatus.UNKNOWN
|
!part.mediaInfo || part.mediaInfo.status4k === MediaStatus.UNKNOWN
|
||||||
@@ -323,14 +330,7 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="media-actions">
|
<div className="media-actions">
|
||||||
{hasPermission(Permission.REQUEST) &&
|
{(hasRequestable || hasRequestable4k) && (
|
||||||
(hasRequestable ||
|
|
||||||
(settings.currentSettings.movie4kEnabled &&
|
|
||||||
hasPermission(
|
|
||||||
[Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
|
|
||||||
{ type: 'or' }
|
|
||||||
) &&
|
|
||||||
hasRequestable4k)) && (
|
|
||||||
<ButtonWithDropdown
|
<ButtonWithDropdown
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -350,13 +350,7 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{settings.currentSettings.movie4kEnabled &&
|
{hasRequestable && hasRequestable4k && (
|
||||||
hasPermission(
|
|
||||||
[Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
|
|
||||||
{ type: 'or' }
|
|
||||||
) &&
|
|
||||||
hasRequestable &&
|
|
||||||
hasRequestable4k && (
|
|
||||||
<ButtonWithDropdown.Item
|
<ButtonWithDropdown.Item
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@@ -70,9 +70,9 @@ const messages = defineMessages({
|
|||||||
openradarr4k: 'Open Movie in 4K Radarr',
|
openradarr4k: 'Open Movie in 4K Radarr',
|
||||||
downloadstatus: 'Download Status',
|
downloadstatus: 'Download Status',
|
||||||
playonplex: 'Play on Plex',
|
playonplex: 'Play on Plex',
|
||||||
play4konplex: 'Play 4K on Plex',
|
play4konplex: 'Play in 4K on Plex',
|
||||||
markavailable: 'Mark as Available',
|
markavailable: 'Mark as Available',
|
||||||
mark4kavailable: 'Mark 4K as Available',
|
mark4kavailable: 'Mark as Available in 4K',
|
||||||
});
|
});
|
||||||
|
|
||||||
interface MovieDetailsProps {
|
interface MovieDetailsProps {
|
||||||
@@ -112,7 +112,12 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
|
|
||||||
const mediaLinks: PlayButtonLink[] = [];
|
const mediaLinks: PlayButtonLink[] = [];
|
||||||
|
|
||||||
if (data.mediaInfo?.plexUrl) {
|
if (
|
||||||
|
data.mediaInfo?.plexUrl &&
|
||||||
|
hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
||||||
|
type: 'or',
|
||||||
|
})
|
||||||
|
) {
|
||||||
mediaLinks.push({
|
mediaLinks.push({
|
||||||
text: intl.formatMessage(messages.playonplex),
|
text: intl.formatMessage(messages.playonplex),
|
||||||
url: data.mediaInfo?.plexUrl,
|
url: data.mediaInfo?.plexUrl,
|
||||||
|
@@ -12,42 +12,41 @@ export const messages = defineMessages({
|
|||||||
'Grant permission to manage Overseerr users. Users with this permission cannot modify users with or grant the Admin privilege.',
|
'Grant permission to manage Overseerr users. Users with this permission cannot modify users with or grant the Admin privilege.',
|
||||||
settings: 'Manage Settings',
|
settings: 'Manage Settings',
|
||||||
settingsDescription:
|
settingsDescription:
|
||||||
'Grant permission to modify all Overseerr settings. A user must have this permission to grant it to others.',
|
'Grant permission to modify Overseerr settings. A user must have this permission to grant it to others.',
|
||||||
managerequests: 'Manage Requests',
|
managerequests: 'Manage Requests',
|
||||||
managerequestsDescription:
|
managerequestsDescription:
|
||||||
'Grant permission to manage Overseerr requests (includes approving and denying requests). All requests made by a user with this permission will be automatically approved.',
|
'Grant permission to manage Overseerr requests. All requests made by a user with this permission will be automatically approved.',
|
||||||
request: 'Request',
|
request: 'Request',
|
||||||
requestDescription: 'Grant permission to request movies and series.',
|
requestDescription: 'Grant permission to request non-4K media.',
|
||||||
vote: 'Vote',
|
requestMovies: 'Request Movies',
|
||||||
voteDescription:
|
requestMoviesDescription: 'Grant permission to request non-4K movies.',
|
||||||
'Grant permission to vote on requests (voting not yet implemented).',
|
requestTv: 'Request Series',
|
||||||
|
requestTvDescription: 'Grant permission to request non-4K series.',
|
||||||
autoapprove: 'Auto-Approve',
|
autoapprove: 'Auto-Approve',
|
||||||
autoapproveDescription:
|
autoapproveDescription: 'Grant automatic approval for all non-4K requests.',
|
||||||
'Grant automatic approval for all non-4K requests made by this user.',
|
|
||||||
autoapproveMovies: 'Auto-Approve Movies',
|
autoapproveMovies: 'Auto-Approve Movies',
|
||||||
autoapproveMoviesDescription:
|
autoapproveMoviesDescription:
|
||||||
'Grant automatic approval for non-4K movie requests made by this user.',
|
'Grant automatic approval for non-4K movie requests.',
|
||||||
autoapproveSeries: 'Auto-Approve Series',
|
autoapproveSeries: 'Auto-Approve Series',
|
||||||
autoapproveSeriesDescription:
|
autoapproveSeriesDescription:
|
||||||
'Grant automatic approval for non-4K series requests made by this user.',
|
'Grant automatic approval for non-4K series requests.',
|
||||||
autoapprove4k: 'Auto-Approve 4K',
|
autoapprove4k: 'Auto-Approve 4K',
|
||||||
autoapprove4kDescription:
|
autoapprove4kDescription: 'Grant automatic approval for all 4K requests.',
|
||||||
'Grant automatic approval for all 4K requests made by this user.',
|
|
||||||
autoapprove4kMovies: 'Auto-Approve 4K Movies',
|
autoapprove4kMovies: 'Auto-Approve 4K Movies',
|
||||||
autoapprove4kMoviesDescription:
|
autoapprove4kMoviesDescription:
|
||||||
'Grant automatic approval for 4K movie requests made by this user.',
|
'Grant automatic approval for 4K movie requests.',
|
||||||
autoapprove4kSeries: 'Auto-Approve 4K Series',
|
autoapprove4kSeries: 'Auto-Approve 4K Series',
|
||||||
autoapprove4kSeriesDescription:
|
autoapprove4kSeriesDescription:
|
||||||
'Grant automatic approval for 4K series requests made by this user.',
|
'Grant automatic approval for 4K series requests.',
|
||||||
request4k: 'Request 4K',
|
request4k: 'Request 4K',
|
||||||
request4kDescription: 'Grant permission to request 4K movies and series.',
|
request4kDescription: 'Grant permission to request 4K media.',
|
||||||
request4kMovies: 'Request 4K Movies',
|
request4kMovies: 'Request 4K Movies',
|
||||||
request4kMoviesDescription: 'Grant permission to request 4K movies.',
|
request4kMoviesDescription: 'Grant permission to request 4K movies.',
|
||||||
request4kTv: 'Request 4K Series',
|
request4kTv: 'Request 4K Series',
|
||||||
request4kTvDescription: 'Grant permission to request 4K Series.',
|
request4kTvDescription: 'Grant permission to request 4K series.',
|
||||||
advancedrequest: 'Advanced Requests',
|
advancedrequest: 'Advanced Requests',
|
||||||
advancedrequestDescription:
|
advancedrequestDescription:
|
||||||
'Grant permission to use advanced request options (e.g., changing servers, profiles, or paths).',
|
'Grant permission to use advanced request options.',
|
||||||
viewrequests: 'View Requests',
|
viewrequests: 'View Requests',
|
||||||
viewrequestsDescription: "Grant permission to view other users' requests.",
|
viewrequestsDescription: "Grant permission to view other users' requests.",
|
||||||
});
|
});
|
||||||
@@ -111,27 +110,18 @@ export const PermissionEdit: React.FC<PermissionEditProps> = ({
|
|||||||
name: intl.formatMessage(messages.request),
|
name: intl.formatMessage(messages.request),
|
||||||
description: intl.formatMessage(messages.requestDescription),
|
description: intl.formatMessage(messages.requestDescription),
|
||||||
permission: Permission.REQUEST,
|
permission: Permission.REQUEST,
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'request4k',
|
|
||||||
name: intl.formatMessage(messages.request4k),
|
|
||||||
description: intl.formatMessage(messages.request4kDescription),
|
|
||||||
permission: Permission.REQUEST_4K,
|
|
||||||
requires: [{ permissions: [Permission.REQUEST] }],
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 'request4k-movies',
|
id: 'request-movies',
|
||||||
name: intl.formatMessage(messages.request4kMovies),
|
name: intl.formatMessage(messages.requestMovies),
|
||||||
description: intl.formatMessage(messages.request4kMoviesDescription),
|
description: intl.formatMessage(messages.requestMoviesDescription),
|
||||||
permission: Permission.REQUEST_4K_MOVIE,
|
permission: Permission.REQUEST_MOVIE,
|
||||||
requires: [{ permissions: [Permission.REQUEST] }],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'request4k-tv',
|
id: 'request-tv',
|
||||||
name: intl.formatMessage(messages.request4kTv),
|
name: intl.formatMessage(messages.requestTv),
|
||||||
description: intl.formatMessage(messages.request4kTvDescription),
|
description: intl.formatMessage(messages.requestTvDescription),
|
||||||
permission: Permission.REQUEST_4K_TV,
|
permission: Permission.REQUEST_TV,
|
||||||
requires: [{ permissions: [Permission.REQUEST] }],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -149,7 +139,12 @@ export const PermissionEdit: React.FC<PermissionEditProps> = ({
|
|||||||
messages.autoapproveMoviesDescription
|
messages.autoapproveMoviesDescription
|
||||||
),
|
),
|
||||||
permission: Permission.AUTO_APPROVE_MOVIE,
|
permission: Permission.AUTO_APPROVE_MOVIE,
|
||||||
requires: [{ permissions: [Permission.REQUEST] }],
|
requires: [
|
||||||
|
{
|
||||||
|
permissions: [Permission.REQUEST, Permission.REQUEST_MOVIE],
|
||||||
|
type: 'or',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'autoapprovetv',
|
id: 'autoapprovetv',
|
||||||
@@ -158,7 +153,32 @@ export const PermissionEdit: React.FC<PermissionEditProps> = ({
|
|||||||
messages.autoapproveSeriesDescription
|
messages.autoapproveSeriesDescription
|
||||||
),
|
),
|
||||||
permission: Permission.AUTO_APPROVE_TV,
|
permission: Permission.AUTO_APPROVE_TV,
|
||||||
requires: [{ permissions: [Permission.REQUEST] }],
|
requires: [
|
||||||
|
{
|
||||||
|
permissions: [Permission.REQUEST, Permission.REQUEST_TV],
|
||||||
|
type: 'or',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'request4k',
|
||||||
|
name: intl.formatMessage(messages.request4k),
|
||||||
|
description: intl.formatMessage(messages.request4kDescription),
|
||||||
|
permission: Permission.REQUEST_4K,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'request4k-movies',
|
||||||
|
name: intl.formatMessage(messages.request4kMovies),
|
||||||
|
description: intl.formatMessage(messages.request4kMoviesDescription),
|
||||||
|
permission: Permission.REQUEST_4K_MOVIE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'request4k-tv',
|
||||||
|
name: intl.formatMessage(messages.request4kTv),
|
||||||
|
description: intl.formatMessage(messages.request4kTvDescription),
|
||||||
|
permission: Permission.REQUEST_4K_TV,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -169,8 +189,7 @@ export const PermissionEdit: React.FC<PermissionEditProps> = ({
|
|||||||
permission: Permission.AUTO_APPROVE_4K,
|
permission: Permission.AUTO_APPROVE_4K,
|
||||||
requires: [
|
requires: [
|
||||||
{
|
{
|
||||||
permissions: [Permission.REQUEST, Permission.REQUEST_4K],
|
permissions: [Permission.REQUEST_4K],
|
||||||
type: 'and',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
children: [
|
children: [
|
||||||
@@ -182,9 +201,6 @@ export const PermissionEdit: React.FC<PermissionEditProps> = ({
|
|||||||
),
|
),
|
||||||
permission: Permission.AUTO_APPROVE_4K_MOVIE,
|
permission: Permission.AUTO_APPROVE_4K_MOVIE,
|
||||||
requires: [
|
requires: [
|
||||||
{
|
|
||||||
permissions: [Permission.REQUEST],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
permissions: [Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
|
permissions: [Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
|
||||||
type: 'or',
|
type: 'or',
|
||||||
@@ -199,9 +215,6 @@ export const PermissionEdit: React.FC<PermissionEditProps> = ({
|
|||||||
),
|
),
|
||||||
permission: Permission.AUTO_APPROVE_4K_TV,
|
permission: Permission.AUTO_APPROVE_4K_TV,
|
||||||
requires: [
|
requires: [
|
||||||
{
|
|
||||||
permissions: [Permission.REQUEST],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
permissions: [Permission.REQUEST_4K, Permission.REQUEST_4K_TV],
|
permissions: [Permission.REQUEST_4K, Permission.REQUEST_4K_TV],
|
||||||
type: 'or',
|
type: 'or',
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { hasPermission } from '../../../server/lib/permissions';
|
import { hasPermission } from '../../../server/lib/permissions';
|
||||||
import { Permission, User } from '../../hooks/useUser';
|
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
|
import { Permission, User } from '../../hooks/useUser';
|
||||||
|
|
||||||
export interface PermissionItem {
|
export interface PermissionItem {
|
||||||
id: string;
|
id: string;
|
||||||
|
@@ -123,7 +123,15 @@ const RequestButton: React.FC<RequestButtonProps> = ({
|
|||||||
const buttons: ButtonOption[] = [];
|
const buttons: ButtonOption[] = [];
|
||||||
if (
|
if (
|
||||||
(!media || media.status === MediaStatus.UNKNOWN) &&
|
(!media || media.status === MediaStatus.UNKNOWN) &&
|
||||||
hasPermission(Permission.REQUEST)
|
hasPermission(
|
||||||
|
[
|
||||||
|
Permission.REQUEST,
|
||||||
|
mediaType === 'movie'
|
||||||
|
? Permission.REQUEST_MOVIE
|
||||||
|
: Permission.REQUEST_TV,
|
||||||
|
],
|
||||||
|
{ type: 'or' }
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
id: 'request',
|
id: 'request',
|
||||||
@@ -138,9 +146,15 @@ const RequestButton: React.FC<RequestButtonProps> = ({
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
(!media || media.status4k === MediaStatus.UNKNOWN) &&
|
(!media || media.status4k === MediaStatus.UNKNOWN) &&
|
||||||
(hasPermission(Permission.REQUEST_4K) ||
|
hasPermission(
|
||||||
(mediaType === 'movie' && hasPermission(Permission.REQUEST_4K_MOVIE)) ||
|
[
|
||||||
(mediaType === 'tv' && hasPermission(Permission.REQUEST_4K_TV))) &&
|
Permission.REQUEST_4K,
|
||||||
|
mediaType === 'movie'
|
||||||
|
? Permission.REQUEST_4K_MOVIE
|
||||||
|
: Permission.REQUEST_4K_TV,
|
||||||
|
],
|
||||||
|
{ type: 'or' }
|
||||||
|
) &&
|
||||||
((settings.currentSettings.movie4kEnabled && mediaType === 'movie') ||
|
((settings.currentSettings.movie4kEnabled && mediaType === 'movie') ||
|
||||||
(settings.currentSettings.series4kEnabled && mediaType === 'tv'))
|
(settings.currentSettings.series4kEnabled && mediaType === 'tv'))
|
||||||
) {
|
) {
|
||||||
@@ -302,7 +316,9 @@ const RequestButton: React.FC<RequestButtonProps> = ({
|
|||||||
if (
|
if (
|
||||||
mediaType === 'tv' &&
|
mediaType === 'tv' &&
|
||||||
(!activeRequest || activeRequest.requestedBy.id !== user?.id) &&
|
(!activeRequest || activeRequest.requestedBy.id !== user?.id) &&
|
||||||
hasPermission(Permission.REQUEST) &&
|
hasPermission([Permission.REQUEST, Permission.REQUEST_TV], {
|
||||||
|
type: 'or',
|
||||||
|
}) &&
|
||||||
media &&
|
media &&
|
||||||
media.status !== MediaStatus.AVAILABLE &&
|
media.status !== MediaStatus.AVAILABLE &&
|
||||||
media.status !== MediaStatus.UNKNOWN &&
|
media.status !== MediaStatus.UNKNOWN &&
|
||||||
|
@@ -69,6 +69,14 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
|||||||
|
|
||||||
const closeModal = useCallback(() => setShowRequestModal(false), []);
|
const closeModal = useCallback(() => setShowRequestModal(false), []);
|
||||||
|
|
||||||
|
const showRequestButton = hasPermission(
|
||||||
|
[
|
||||||
|
Permission.REQUEST,
|
||||||
|
mediaType === 'movie' ? Permission.REQUEST_MOVIE : Permission.REQUEST_TV,
|
||||||
|
],
|
||||||
|
{ type: 'or' }
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={canExpand ? 'w-full' : 'w-36 sm:w-36 md:w-44'}>
|
<div className={canExpand ? 'w-full' : 'w-36 sm:w-36 md:w-44'}>
|
||||||
<RequestModal
|
<RequestModal
|
||||||
@@ -185,7 +193,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
|||||||
<div className="flex items-end w-full h-full">
|
<div className="flex items-end w-full h-full">
|
||||||
<div
|
<div
|
||||||
className={`px-2 text-white ${
|
className={`px-2 text-white ${
|
||||||
!hasPermission(Permission.REQUEST) ||
|
!showRequestButton ||
|
||||||
(currentStatus && currentStatus !== MediaStatus.UNKNOWN)
|
(currentStatus && currentStatus !== MediaStatus.UNKNOWN)
|
||||||
? 'pb-2'
|
? 'pb-2'
|
||||||
: 'pb-11'
|
: 'pb-11'
|
||||||
@@ -209,7 +217,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
|||||||
className="text-xs whitespace-normal"
|
className="text-xs whitespace-normal"
|
||||||
style={{
|
style={{
|
||||||
WebkitLineClamp:
|
WebkitLineClamp:
|
||||||
!hasPermission(Permission.REQUEST) ||
|
!showRequestButton ||
|
||||||
(currentStatus &&
|
(currentStatus &&
|
||||||
currentStatus !== MediaStatus.UNKNOWN)
|
currentStatus !== MediaStatus.UNKNOWN)
|
||||||
? 5
|
? 5
|
||||||
@@ -228,7 +236,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
|||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="absolute bottom-0 left-0 right-0 flex justify-between px-2 py-2">
|
<div className="absolute bottom-0 left-0 right-0 flex justify-between px-2 py-2">
|
||||||
{hasPermission(Permission.REQUEST) &&
|
{showRequestButton &&
|
||||||
(!currentStatus || currentStatus === MediaStatus.UNKNOWN) && (
|
(!currentStatus || currentStatus === MediaStatus.UNKNOWN) && (
|
||||||
<Button
|
<Button
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
|
@@ -63,7 +63,7 @@ const messages = defineMessages({
|
|||||||
manageModalNoRequests: 'No requests.',
|
manageModalNoRequests: 'No requests.',
|
||||||
manageModalClearMedia: 'Clear Media Data',
|
manageModalClearMedia: 'Clear Media Data',
|
||||||
manageModalClearMediaWarning:
|
manageModalClearMediaWarning:
|
||||||
'* This will irreversibly remove all data for this TV series, including any requests. If this item exists in your Plex library, the media information will be recreated during the next scan.',
|
'* This will irreversibly remove all data for this series, including any requests. If this item exists in your Plex library, the media information will be recreated during the next scan.',
|
||||||
originaltitle: 'Original Title',
|
originaltitle: 'Original Title',
|
||||||
showtype: 'Series Type',
|
showtype: 'Series Type',
|
||||||
anime: 'Anime',
|
anime: 'Anime',
|
||||||
@@ -73,9 +73,9 @@ const messages = defineMessages({
|
|||||||
opensonarr4k: 'Open Series in 4K Sonarr',
|
opensonarr4k: 'Open Series in 4K Sonarr',
|
||||||
downloadstatus: 'Download Status',
|
downloadstatus: 'Download Status',
|
||||||
playonplex: 'Play on Plex',
|
playonplex: 'Play on Plex',
|
||||||
play4konplex: 'Play 4K on Plex',
|
play4konplex: 'Play in 4K on Plex',
|
||||||
markavailable: 'Mark as Available',
|
markavailable: 'Mark as Available',
|
||||||
mark4kavailable: 'Mark 4K as Available',
|
mark4kavailable: 'Mark as Available in 4K',
|
||||||
allseasonsmarkedavailable: '* All seasons will be marked as available.',
|
allseasonsmarkedavailable: '* All seasons will be marked as available.',
|
||||||
seasons: '{seasonCount, plural, one {# Season} other {# Seasons}}',
|
seasons: '{seasonCount, plural, one {# Season} other {# Seasons}}',
|
||||||
episodeRuntime: 'Episode Runtime',
|
episodeRuntime: 'Episode Runtime',
|
||||||
|
@@ -69,7 +69,7 @@
|
|||||||
"components.MovieDetails.manageModalNoRequests": "No requests.",
|
"components.MovieDetails.manageModalNoRequests": "No requests.",
|
||||||
"components.MovieDetails.manageModalRequests": "Requests",
|
"components.MovieDetails.manageModalRequests": "Requests",
|
||||||
"components.MovieDetails.manageModalTitle": "Manage Movie",
|
"components.MovieDetails.manageModalTitle": "Manage Movie",
|
||||||
"components.MovieDetails.mark4kavailable": "Mark 4K as Available",
|
"components.MovieDetails.mark4kavailable": "Mark as Available in 4K",
|
||||||
"components.MovieDetails.markavailable": "Mark as Available",
|
"components.MovieDetails.markavailable": "Mark as Available",
|
||||||
"components.MovieDetails.openradarr": "Open Movie in Radarr",
|
"components.MovieDetails.openradarr": "Open Movie in Radarr",
|
||||||
"components.MovieDetails.openradarr4k": "Open Movie in 4K Radarr",
|
"components.MovieDetails.openradarr4k": "Open Movie in 4K Radarr",
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
"components.MovieDetails.originaltitle": "Original Title",
|
"components.MovieDetails.originaltitle": "Original Title",
|
||||||
"components.MovieDetails.overview": "Overview",
|
"components.MovieDetails.overview": "Overview",
|
||||||
"components.MovieDetails.overviewunavailable": "Overview unavailable.",
|
"components.MovieDetails.overviewunavailable": "Overview unavailable.",
|
||||||
"components.MovieDetails.play4konplex": "Play 4K on Plex",
|
"components.MovieDetails.play4konplex": "Play in 4K on Plex",
|
||||||
"components.MovieDetails.playonplex": "Play on Plex",
|
"components.MovieDetails.playonplex": "Play on Plex",
|
||||||
"components.MovieDetails.recommendations": "Recommendations",
|
"components.MovieDetails.recommendations": "Recommendations",
|
||||||
"components.MovieDetails.releasedate": "Release Date",
|
"components.MovieDetails.releasedate": "Release Date",
|
||||||
@@ -103,37 +103,39 @@
|
|||||||
"components.PermissionEdit.admin": "Admin",
|
"components.PermissionEdit.admin": "Admin",
|
||||||
"components.PermissionEdit.adminDescription": "Full administrator access. Bypasses all other permission checks.",
|
"components.PermissionEdit.adminDescription": "Full administrator access. Bypasses all other permission checks.",
|
||||||
"components.PermissionEdit.advancedrequest": "Advanced Requests",
|
"components.PermissionEdit.advancedrequest": "Advanced Requests",
|
||||||
"components.PermissionEdit.advancedrequestDescription": "Grant permission to use advanced request options (e.g., changing servers, profiles, or paths).",
|
"components.PermissionEdit.advancedrequestDescription": "Grant permission to use advanced request options.",
|
||||||
"components.PermissionEdit.autoapprove": "Auto-Approve",
|
"components.PermissionEdit.autoapprove": "Auto-Approve",
|
||||||
"components.PermissionEdit.autoapprove4k": "Auto-Approve 4K",
|
"components.PermissionEdit.autoapprove4k": "Auto-Approve 4K",
|
||||||
"components.PermissionEdit.autoapprove4kDescription": "Grant automatic approval for all 4K requests made by this user.",
|
"components.PermissionEdit.autoapprove4kDescription": "Grant automatic approval for all 4K requests.",
|
||||||
"components.PermissionEdit.autoapprove4kMovies": "Auto-Approve 4K Movies",
|
"components.PermissionEdit.autoapprove4kMovies": "Auto-Approve 4K Movies",
|
||||||
"components.PermissionEdit.autoapprove4kMoviesDescription": "Grant automatic approval for 4K movie requests made by this user.",
|
"components.PermissionEdit.autoapprove4kMoviesDescription": "Grant automatic approval for 4K movie requests.",
|
||||||
"components.PermissionEdit.autoapprove4kSeries": "Auto-Approve 4K Series",
|
"components.PermissionEdit.autoapprove4kSeries": "Auto-Approve 4K Series",
|
||||||
"components.PermissionEdit.autoapprove4kSeriesDescription": "Grant automatic approval for 4K series requests made by this user.",
|
"components.PermissionEdit.autoapprove4kSeriesDescription": "Grant automatic approval for 4K series requests.",
|
||||||
"components.PermissionEdit.autoapproveDescription": "Grant automatic approval for all non-4K requests made by this user.",
|
"components.PermissionEdit.autoapproveDescription": "Grant automatic approval for all non-4K requests.",
|
||||||
"components.PermissionEdit.autoapproveMovies": "Auto-Approve Movies",
|
"components.PermissionEdit.autoapproveMovies": "Auto-Approve Movies",
|
||||||
"components.PermissionEdit.autoapproveMoviesDescription": "Grant automatic approval for non-4K movie requests made by this user.",
|
"components.PermissionEdit.autoapproveMoviesDescription": "Grant automatic approval for non-4K movie requests.",
|
||||||
"components.PermissionEdit.autoapproveSeries": "Auto-Approve Series",
|
"components.PermissionEdit.autoapproveSeries": "Auto-Approve Series",
|
||||||
"components.PermissionEdit.autoapproveSeriesDescription": "Grant automatic approval for non-4K series requests made by this user.",
|
"components.PermissionEdit.autoapproveSeriesDescription": "Grant automatic approval for non-4K series requests.",
|
||||||
"components.PermissionEdit.managerequests": "Manage Requests",
|
"components.PermissionEdit.managerequests": "Manage Requests",
|
||||||
"components.PermissionEdit.managerequestsDescription": "Grant permission to manage Overseerr requests (includes approving and denying requests). All requests made by a user with this permission will be automatically approved.",
|
"components.PermissionEdit.managerequestsDescription": "Grant permission to manage Overseerr requests. All requests made by a user with this permission will be automatically approved.",
|
||||||
"components.PermissionEdit.request": "Request",
|
"components.PermissionEdit.request": "Request",
|
||||||
"components.PermissionEdit.request4k": "Request 4K",
|
"components.PermissionEdit.request4k": "Request 4K",
|
||||||
"components.PermissionEdit.request4kDescription": "Grant permission to request 4K movies and series.",
|
"components.PermissionEdit.request4kDescription": "Grant permission to request 4K media.",
|
||||||
"components.PermissionEdit.request4kMovies": "Request 4K Movies",
|
"components.PermissionEdit.request4kMovies": "Request 4K Movies",
|
||||||
"components.PermissionEdit.request4kMoviesDescription": "Grant permission to request 4K movies.",
|
"components.PermissionEdit.request4kMoviesDescription": "Grant permission to request 4K movies.",
|
||||||
"components.PermissionEdit.request4kTv": "Request 4K Series",
|
"components.PermissionEdit.request4kTv": "Request 4K Series",
|
||||||
"components.PermissionEdit.request4kTvDescription": "Grant permission to request 4K Series.",
|
"components.PermissionEdit.request4kTvDescription": "Grant permission to request 4K series.",
|
||||||
"components.PermissionEdit.requestDescription": "Grant permission to request movies and series.",
|
"components.PermissionEdit.requestDescription": "Grant permission to request non-4K media.",
|
||||||
|
"components.PermissionEdit.requestMovies": "Request Movies",
|
||||||
|
"components.PermissionEdit.requestMoviesDescription": "Grant permission to request non-4K movies.",
|
||||||
|
"components.PermissionEdit.requestTv": "Request Series",
|
||||||
|
"components.PermissionEdit.requestTvDescription": "Grant permission to request non-4K series.",
|
||||||
"components.PermissionEdit.settings": "Manage Settings",
|
"components.PermissionEdit.settings": "Manage Settings",
|
||||||
"components.PermissionEdit.settingsDescription": "Grant permission to modify all Overseerr settings. A user must have this permission to grant it to others.",
|
"components.PermissionEdit.settingsDescription": "Grant permission to modify Overseerr settings. A user must have this permission to grant it to others.",
|
||||||
"components.PermissionEdit.users": "Manage Users",
|
"components.PermissionEdit.users": "Manage Users",
|
||||||
"components.PermissionEdit.usersDescription": "Grant permission to manage Overseerr users. Users with this permission cannot modify users with or grant the Admin privilege.",
|
"components.PermissionEdit.usersDescription": "Grant permission to manage Overseerr users. Users with this permission cannot modify users with or grant the Admin privilege.",
|
||||||
"components.PermissionEdit.viewrequests": "View Requests",
|
"components.PermissionEdit.viewrequests": "View Requests",
|
||||||
"components.PermissionEdit.viewrequestsDescription": "Grant permission to view other users' requests.",
|
"components.PermissionEdit.viewrequestsDescription": "Grant permission to view other users' requests.",
|
||||||
"components.PermissionEdit.vote": "Vote",
|
|
||||||
"components.PermissionEdit.voteDescription": "Grant permission to vote on requests (voting not yet implemented).",
|
|
||||||
"components.PersonDetails.alsoknownas": "Also Known As: {names}",
|
"components.PersonDetails.alsoknownas": "Also Known As: {names}",
|
||||||
"components.PersonDetails.appearsin": "Appearances",
|
"components.PersonDetails.appearsin": "Appearances",
|
||||||
"components.PersonDetails.ascharacter": "as {character}",
|
"components.PersonDetails.ascharacter": "as {character}",
|
||||||
@@ -658,11 +660,11 @@
|
|||||||
"components.TvDetails.episodeRuntimeMinutes": "{runtime} minutes",
|
"components.TvDetails.episodeRuntimeMinutes": "{runtime} minutes",
|
||||||
"components.TvDetails.firstAirDate": "First Air Date",
|
"components.TvDetails.firstAirDate": "First Air Date",
|
||||||
"components.TvDetails.manageModalClearMedia": "Clear Media Data",
|
"components.TvDetails.manageModalClearMedia": "Clear Media Data",
|
||||||
"components.TvDetails.manageModalClearMediaWarning": "* This will irreversibly remove all data for this TV series, including any requests. If this item exists in your Plex library, the media information will be recreated during the next scan.",
|
"components.TvDetails.manageModalClearMediaWarning": "* This will irreversibly remove all data for this series, including any requests. If this item exists in your Plex library, the media information will be recreated during the next scan.",
|
||||||
"components.TvDetails.manageModalNoRequests": "No requests.",
|
"components.TvDetails.manageModalNoRequests": "No requests.",
|
||||||
"components.TvDetails.manageModalRequests": "Requests",
|
"components.TvDetails.manageModalRequests": "Requests",
|
||||||
"components.TvDetails.manageModalTitle": "Manage Series",
|
"components.TvDetails.manageModalTitle": "Manage Series",
|
||||||
"components.TvDetails.mark4kavailable": "Mark 4K as Available",
|
"components.TvDetails.mark4kavailable": "Mark as Available in 4K",
|
||||||
"components.TvDetails.markavailable": "Mark as Available",
|
"components.TvDetails.markavailable": "Mark as Available",
|
||||||
"components.TvDetails.network": "{networkCount, plural, one {Network} other {Networks}}",
|
"components.TvDetails.network": "{networkCount, plural, one {Network} other {Networks}}",
|
||||||
"components.TvDetails.nextAirDate": "Next Air Date",
|
"components.TvDetails.nextAirDate": "Next Air Date",
|
||||||
@@ -672,7 +674,7 @@
|
|||||||
"components.TvDetails.originaltitle": "Original Title",
|
"components.TvDetails.originaltitle": "Original Title",
|
||||||
"components.TvDetails.overview": "Overview",
|
"components.TvDetails.overview": "Overview",
|
||||||
"components.TvDetails.overviewunavailable": "Overview unavailable.",
|
"components.TvDetails.overviewunavailable": "Overview unavailable.",
|
||||||
"components.TvDetails.play4konplex": "Play 4K on Plex",
|
"components.TvDetails.play4konplex": "Play in 4K on Plex",
|
||||||
"components.TvDetails.playonplex": "Play on Plex",
|
"components.TvDetails.playonplex": "Play on Plex",
|
||||||
"components.TvDetails.recommendations": "Recommendations",
|
"components.TvDetails.recommendations": "Recommendations",
|
||||||
"components.TvDetails.seasons": "{seasonCount, plural, one {# Season} other {# Seasons}}",
|
"components.TvDetails.seasons": "{seasonCount, plural, one {# Season} other {# Seasons}}",
|
||||||
|
Reference in New Issue
Block a user