From 76f57550fa1b44f7ae958c605a5a16245341af31 Mon Sep 17 00:00:00 2001 From: Brandon Cohen Date: Sat, 30 Sep 2023 02:15:47 -0400 Subject: [PATCH 1/2] feat: added the ability to request 4k media on the titlecard --- server/interfaces/api/settingsInterfaces.ts | 2 + server/lib/settings.ts | 8 ++ src/components/MediaSlider/index.tsx | 2 + src/components/PersonDetails/index.tsx | 1 + src/components/TitleCard/TmdbTitleCard.tsx | 2 + src/components/TitleCard/index.tsx | 131 +++++++++++++++----- src/context/SettingsContext.tsx | 2 + src/pages/_app.tsx | 2 + 8 files changed, 121 insertions(+), 29 deletions(-) diff --git a/server/interfaces/api/settingsInterfaces.ts b/server/interfaces/api/settingsInterfaces.ts index 0cd2f171..e5ec544d 100644 --- a/server/interfaces/api/settingsInterfaces.ts +++ b/server/interfaces/api/settingsInterfaces.ts @@ -26,6 +26,8 @@ export interface PublicSettingsResponse { applicationUrl: string; hideAvailable: boolean; localLogin: boolean; + movieEnabled: boolean; + seriesEnabled: boolean; movie4kEnabled: boolean; series4kEnabled: boolean; region: string; diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 082e733f..9fd83a1d 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -115,6 +115,8 @@ interface FullPublicSettings extends PublicSettings { applicationUrl: string; hideAvailable: boolean; localLogin: boolean; + movieEnabled: boolean; + seriesEnabled: boolean; movie4kEnabled: boolean; series4kEnabled: boolean; region: string; @@ -491,6 +493,12 @@ class Settings { applicationUrl: this.data.main.applicationUrl, hideAvailable: this.data.main.hideAvailable, localLogin: this.data.main.localLogin, + movieEnabled: this.data.radarr.some( + (radarr) => !radarr.is4k && radarr.isDefault + ), + seriesEnabled: this.data.sonarr.some( + (sonarr) => !sonarr.is4k && sonarr.isDefault + ), movie4kEnabled: this.data.radarr.some( (radarr) => radarr.is4k && radarr.isDefault ), diff --git a/src/components/MediaSlider/index.tsx b/src/components/MediaSlider/index.tsx index 54b5cc80..020a2515 100644 --- a/src/components/MediaSlider/index.tsx +++ b/src/components/MediaSlider/index.tsx @@ -98,6 +98,7 @@ const MediaSlider = ({ id={title.id} image={title.posterPath} status={title.mediaInfo?.status} + status4k={title.mediaInfo?.status4k} summary={title.overview} title={title.title} userScore={title.voteAverage} @@ -112,6 +113,7 @@ const MediaSlider = ({ id={title.id} image={title.posterPath} status={title.mediaInfo?.status} + status4k={title.mediaInfo?.status4k} summary={title.overview} title={title.name} userScore={title.voteAverage} diff --git a/src/components/PersonDetails/index.tsx b/src/components/PersonDetails/index.tsx index f4d489d5..7d758679 100644 --- a/src/components/PersonDetails/index.tsx +++ b/src/components/PersonDetails/index.tsx @@ -185,6 +185,7 @@ const PersonDetails = () => { summary={media.overview} mediaType={media.mediaType as 'movie' | 'tv'} status={media.mediaInfo?.status} + status4k={media.mediaInfo?.status} canExpand /> {media.job && ( diff --git a/src/components/TitleCard/TmdbTitleCard.tsx b/src/components/TitleCard/TmdbTitleCard.tsx index f031c997..50b348a4 100644 --- a/src/components/TitleCard/TmdbTitleCard.tsx +++ b/src/components/TitleCard/TmdbTitleCard.tsx @@ -59,6 +59,7 @@ const TmdbTitleCard = ({ id={title.id} image={title.posterPath} status={title.mediaInfo?.status} + status4k={title.mediaInfo?.status4k} summary={title.overview} title={title.title} userScore={title.voteAverage} @@ -71,6 +72,7 @@ const TmdbTitleCard = ({ id={title.id} image={title.posterPath} status={title.mediaInfo?.status} + status4k={title.mediaInfo?.status4k} summary={title.overview} title={title.name} userScore={title.voteAverage} diff --git a/src/components/TitleCard/index.tsx b/src/components/TitleCard/index.tsx index 9056fd57..6cc7eea9 100644 --- a/src/components/TitleCard/index.tsx +++ b/src/components/TitleCard/index.tsx @@ -6,6 +6,7 @@ import RequestModal from '@app/components/RequestModal'; import ErrorCard from '@app/components/TitleCard/ErrorCard'; import Placeholder from '@app/components/TitleCard/Placeholder'; import { useIsTouch } from '@app/hooks/useIsTouch'; +import useSettings from '@app/hooks/useSettings'; import { Permission, useUser } from '@app/hooks/useUser'; import globalMessages from '@app/i18n/globalMessages'; import { withProperties } from '@app/utils/typeHelpers'; @@ -26,6 +27,7 @@ interface TitleCardProps { userScore?: number; mediaType: MediaType; status?: MediaStatus; + status4k?: MediaStatus; canExpand?: boolean; inProgress?: boolean; } @@ -37,6 +39,7 @@ const TitleCard = ({ year, title, status, + status4k, mediaType, inProgress = false, canExpand = false, @@ -46,8 +49,11 @@ const TitleCard = ({ const { hasPermission } = useUser(); const [isUpdating, setIsUpdating] = useState(false); const [currentStatus, setCurrentStatus] = useState(status); + const [currentStatus4k, setCurrentStatus4k] = useState(status4k); const [showDetail, setShowDetail] = useState(false); const [showRequestModal, setShowRequestModal] = useState(false); + const [showRequestModal4k, setShowRequestModal4k] = useState(false); + const settings = useSettings(); // Just to get the year from the date if (year) { @@ -56,7 +62,8 @@ const TitleCard = ({ useEffect(() => { setCurrentStatus(status); - }, [status]); + setCurrentStatus4k(status4k); + }, [status, status4k]); const requestComplete = useCallback((newStatus: MediaStatus) => { setCurrentStatus(newStatus); @@ -70,15 +77,44 @@ const TitleCard = ({ const closeModal = useCallback(() => setShowRequestModal(false), []); - const showRequestButton = hasPermission( - [ - Permission.REQUEST, - mediaType === 'movie' || mediaType === 'collection' - ? Permission.REQUEST_MOVIE - : Permission.REQUEST_TV, - ], - { type: 'or' } - ); + const requestComplete4k = useCallback((newStatus: MediaStatus) => { + setCurrentStatus4k(newStatus); + setShowRequestModal4k(false); + }, []); + + const closeModal4k = useCallback(() => setShowRequestModal4k(false), []); + + const showRequestButton = + hasPermission( + [ + Permission.REQUEST, + mediaType === 'movie' || mediaType === 'collection' + ? Permission.REQUEST_MOVIE + : Permission.REQUEST_TV, + ], + { type: 'or' } + ) && + ((settings.currentSettings.movieEnabled && mediaType === 'movie') || + (settings.currentSettings.seriesEnabled && mediaType === 'tv')); + + const showRequestButton4k = + hasPermission( + [ + Permission.REQUEST_4K, + mediaType === 'movie' || mediaType === 'collection' + ? Permission.REQUEST_4K_MOVIE + : Permission.REQUEST_4K_TV, + ], + { type: 'or' } + ) && + ((settings.currentSettings.movie4kEnabled && mediaType === 'movie') || + (settings.currentSettings.series4kEnabled && mediaType === 'tv')); + + const isRequestable = + (showRequestButton && + (!currentStatus || currentStatus === MediaStatus.UNKNOWN)) || + (showRequestButton4k && + (!currentStatus4k || currentStatus4k === MediaStatus.UNKNOWN)); return (
+
- {currentStatus && currentStatus !== MediaStatus.UNKNOWN && ( -
+
+ {currentStatus && currentStatus !== MediaStatus.UNKNOWN ? ( -
- )} + ) : ( + currentStatus4k && + currentStatus4k !== MediaStatus.UNKNOWN && ( + + ) + )} +
{year && ( @@ -232,12 +291,7 @@ const TitleCard = ({
{showRequestButton && - (!currentStatus || currentStatus === MediaStatus.UNKNOWN) && ( + (!currentStatus || currentStatus === MediaStatus.UNKNOWN) ? ( + + ) : ( + showRequestButton4k && + (!currentStatus4k || + currentStatus4k === MediaStatus.UNKNOWN) && ( - )} + ) + )}
diff --git a/src/context/SettingsContext.tsx b/src/context/SettingsContext.tsx index d50add4d..dd94cfc8 100644 --- a/src/context/SettingsContext.tsx +++ b/src/context/SettingsContext.tsx @@ -13,6 +13,8 @@ const defaultSettings = { applicationUrl: '', hideAvailable: false, localLogin: true, + movieEnabled: false, + seriesEnabled: false, movie4kEnabled: false, series4kEnabled: false, region: '', diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index ceb5734e..ce7cab94 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -174,6 +174,8 @@ CoreApp.getInitialProps = async (initialProps) => { applicationTitle: '', applicationUrl: '', hideAvailable: false, + movieEnabled: false, + seriesEnabled: false, movie4kEnabled: false, series4kEnabled: false, localLogin: true, From 3369adfb9ad4283b17c83b62c4ca2a6ad3dae452 Mon Sep 17 00:00:00 2001 From: Brandon Cohen Date: Mon, 2 Oct 2023 13:18:31 -0400 Subject: [PATCH 2/2] fix: modified all instances of request button show state --- src/components/CollectionDetails/index.tsx | 21 +- src/components/Common/ListView/index.tsx | 2 + .../IssueModal/CreateIssueModal/index.tsx | 8 +- src/components/ManageSlideOver/index.tsx | 33 ++-- src/components/MovieDetails/index.tsx | 36 ++-- .../NotificationTypeSelector/index.tsx | 14 +- src/components/PersonDetails/index.tsx | 1 + src/components/RequestButton/index.tsx | 7 +- src/components/StatusBadge/index.tsx | 5 +- src/components/TvDetails/index.tsx | 182 +++++++++++------- 10 files changed, 195 insertions(+), 114 deletions(-) diff --git a/src/components/CollectionDetails/index.tsx b/src/components/CollectionDetails/index.tsx index ff22ee10..a180eac8 100644 --- a/src/components/CollectionDetails/index.tsx +++ b/src/components/CollectionDetails/index.tsx @@ -129,6 +129,7 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => { } const hasRequestable = + settings.currentSettings.movieEnabled && hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], { type: 'or', }) && @@ -237,14 +238,19 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
- (part.mediaInfo?.downloadStatus ?? []).length > 0 + {settings.currentSettings.movieEnabled && + hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], { + type: 'or', + }) && ( + (part.mediaInfo?.downloadStatus ?? []).length > 0 + )} + /> )} - /> {settings.currentSettings.movie4kEnabled && hasPermission( [Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE], @@ -340,6 +346,7 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => { id={title.id} image={title.posterPath} status={title.mediaInfo?.status} + status4k={title.mediaInfo?.status4k} summary={title.overview} title={title.title} userScore={title.voteAverage} diff --git a/src/components/Common/ListView/index.tsx b/src/components/Common/ListView/index.tsx index b4608686..35c3a298 100644 --- a/src/components/Common/ListView/index.tsx +++ b/src/components/Common/ListView/index.tsx @@ -61,6 +61,7 @@ const ListView = ({ id={title.id} image={title.posterPath} status={title.mediaInfo?.status} + status4k={title.mediaInfo?.status4k} summary={title.overview} title={title.title} userScore={title.voteAverage} @@ -79,6 +80,7 @@ const ListView = ({ id={title.id} image={title.posterPath} status={title.mediaInfo?.status} + status4k={title.mediaInfo?.status4k} summary={title.overview} title={title.name} userScore={title.voteAverage} diff --git a/src/components/IssueModal/CreateIssueModal/index.tsx b/src/components/IssueModal/CreateIssueModal/index.tsx index 397322ca..dd0e7c3e 100644 --- a/src/components/IssueModal/CreateIssueModal/index.tsx +++ b/src/components/IssueModal/CreateIssueModal/index.tsx @@ -72,8 +72,12 @@ const CreateIssueModal = ({ const availableSeasons = (data?.mediaInfo?.seasons ?? []) .filter( (season) => - season.status === MediaStatus.AVAILABLE || - season.status === MediaStatus.PARTIALLY_AVAILABLE || + (settings.currentSettings.seriesEnabled && + hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], { + type: 'or', + }) && + (season.status === MediaStatus.AVAILABLE || + season.status === MediaStatus.PARTIALLY_AVAILABLE)) || (settings.currentSettings.series4kEnabled && hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], { type: 'or', diff --git a/src/components/ManageSlideOver/index.tsx b/src/components/ManageSlideOver/index.tsx index 103781d1..4d7ee27b 100644 --- a/src/components/ManageSlideOver/index.tsx +++ b/src/components/ManageSlideOver/index.tsx @@ -468,22 +468,23 @@ const ManageSlideOver = ({ {intl.formatMessage(messages.manageModalAdvanced)}
- {data?.mediaInfo.status !== MediaStatus.AVAILABLE && ( - - )} + {data?.mediaInfo.status !== MediaStatus.AVAILABLE && + settings.currentSettings.seriesEnabled && ( + + )} {data?.mediaInfo.status4k !== MediaStatus.AVAILABLE && settings.currentSettings.series4kEnabled && (
- 0} - tmdbId={data.mediaInfo?.tmdbId} - mediaType="movie" - plexUrl={plexUrl} - serviceUrl={data.mediaInfo?.serviceUrl} - /> + {settings.currentSettings.movieEnabled && + hasPermission( + [ + Permission.MANAGE_REQUESTS, + Permission.REQUEST, + Permission.REQUEST_MOVIE, + ], + { + type: 'or', + } + ) && ( + 0} + tmdbId={data.mediaInfo?.tmdbId} + mediaType="movie" + plexUrl={plexUrl} + serviceUrl={data.mediaInfo?.serviceUrl} + /> + )} {settings.currentSettings.movie4kEnabled && hasPermission( [ @@ -379,7 +392,8 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { tmdbId={data.id} onUpdate={() => revalidate()} /> - {(data.mediaInfo?.status === MediaStatus.AVAILABLE || + {((settings.currentSettings.movieEnabled && + data.mediaInfo?.status === MediaStatus.AVAILABLE) || (settings.currentSettings.movie4kEnabled && hasPermission( [Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE], diff --git a/src/components/NotificationTypeSelector/index.tsx b/src/components/NotificationTypeSelector/index.tsx index 149c2757..868bf391 100644 --- a/src/components/NotificationTypeSelector/index.tsx +++ b/src/components/NotificationTypeSelector/index.tsx @@ -159,17 +159,19 @@ const NotificationTypeSelector = ({ { type: 'or' } ) || // Cannot submit non-4K movie requests OR has Auto-Approve perms for non-4K movies - ((!hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], { - type: 'or', - }) || + ((!settings.currentSettings.movieEnabled || + !hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], { + type: 'or', + }) || hasPermission( [Permission.AUTO_APPROVE, Permission.AUTO_APPROVE_MOVIE], { type: 'or' } )) && // Cannot submit non-4K series requests OR has Auto-Approve perms for non-4K series - (!hasPermission([Permission.REQUEST, Permission.REQUEST_TV], { - type: 'or', - }) || + (!settings.currentSettings.seriesEnabled || + !hasPermission([Permission.REQUEST, Permission.REQUEST_TV], { + type: 'or', + }) || hasPermission( [Permission.AUTO_APPROVE, Permission.AUTO_APPROVE_TV], { type: 'or' } diff --git a/src/components/PersonDetails/index.tsx b/src/components/PersonDetails/index.tsx index 7d758679..ef762aab 100644 --- a/src/components/PersonDetails/index.tsx +++ b/src/components/PersonDetails/index.tsx @@ -145,6 +145,7 @@ const PersonDetails = () => { summary={media.overview} mediaType={media.mediaType as 'movie' | 'tv'} status={media.mediaInfo?.status} + status4k={media.mediaInfo?.status4k} canExpand /> {media.character && ( diff --git a/src/components/RequestButton/index.tsx b/src/components/RequestButton/index.tsx index 56e91810..8e77b787 100644 --- a/src/components/RequestButton/index.tsx +++ b/src/components/RequestButton/index.tsx @@ -273,7 +273,9 @@ const RequestButton = ({ : Permission.REQUEST_TV, ], { type: 'or' } - ) + ) && + ((settings.currentSettings.movieEnabled && mediaType === 'movie') || + (settings.currentSettings.seriesEnabled && mediaType === 'tv')) ) { buttons.push({ id: 'request', @@ -292,7 +294,8 @@ const RequestButton = ({ }) && media && media.status !== MediaStatus.AVAILABLE && - !isShowComplete + !isShowComplete && + settings.currentSettings.seriesEnabled ) { buttons.push({ id: 'request-more', diff --git a/src/components/StatusBadge/index.tsx b/src/components/StatusBadge/index.tsx index b60b7af0..0a025d1d 100644 --- a/src/components/StatusBadge/index.tsx +++ b/src/components/StatusBadge/index.tsx @@ -73,7 +73,10 @@ const StatusBadge = ({ type: 'or', } ) && - (!is4k || + ((!is4k && + (mediaType === 'movie' + ? settings.currentSettings.movieEnabled + : settings.currentSettings.seriesEnabled)) || (mediaType === 'movie' ? settings.currentSettings.movie4kEnabled : settings.currentSettings.series4kEnabled)) diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index 2f44a7d8..d5a4b7dd 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -149,7 +149,13 @@ const TvDetails = ({ tv }: TvDetailsProps) => { const mediaLinks: PlayButtonLink[] = []; - if (plexUrl) { + if ( + settings.currentSettings.seriesEnabled && + plexUrl && + hasPermission([Permission.REQUEST, Permission.REQUEST_TV], { + type: 'or', + }) + ) { mediaLinks.push({ text: intl.formatMessage(messages.playonplex), url: plexUrl, @@ -337,16 +343,28 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
- 0} - tmdbId={data.mediaInfo?.tmdbId} - mediaType="tv" - plexUrl={plexUrl} - serviceUrl={data.mediaInfo?.serviceUrl} - /> + {settings.currentSettings.seriesEnabled && + hasPermission( + [ + Permission.MANAGE_REQUESTS, + Permission.REQUEST, + Permission.REQUEST_TV, + ], + { + type: 'or', + } + ) && ( + 0} + tmdbId={data.mediaInfo?.tmdbId} + mediaType="tv" + plexUrl={plexUrl} + serviceUrl={data.mediaInfo?.serviceUrl} + /> + )} {settings.currentSettings.series4kEnabled && hasPermission( [ @@ -404,8 +422,12 @@ const TvDetails = ({ tv }: TvDetailsProps) => { isShowComplete={isComplete} is4kShowComplete={is4kComplete} /> - {(data.mediaInfo?.status === MediaStatus.AVAILABLE || - data.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE || + {((settings.currentSettings.seriesEnabled && + hasPermission([Permission.REQUEST, Permission.REQUEST_TV], { + type: 'or', + }) && + (data.mediaInfo?.status === MediaStatus.AVAILABLE || + data?.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE)) || (settings.currentSettings.series4kEnabled && hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], { type: 'or', @@ -524,6 +546,18 @@ const TvDetails = ({ tv }: TvDetailsProps) => { .reverse() .filter((season) => season.seasonNumber !== 0) .map((season) => { + const showNon4k = + settings.currentSettings.seriesEnabled && + hasPermission( + [ + Permission.MANAGE_REQUESTS, + Permission.REQUEST, + Permission.REQUEST_TV, + ], + { + type: 'or', + } + ); const show4k = settings.currentSettings.series4kEnabled && hasPermission( @@ -588,65 +622,75 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
{((!mSeason && request?.status === MediaRequestStatus.APPROVED) || - mSeason?.status === MediaStatus.PROCESSING) && ( - <> -
- - {intl.formatMessage(globalMessages.requested)} - -
-
- -
- - )} + mSeason?.status === MediaStatus.PROCESSING) && + showNon4k && ( + <> +
+ + {intl.formatMessage( + globalMessages.requested + )} + +
+
+ +
+ + )} {((!mSeason && request?.status === MediaRequestStatus.PENDING) || - mSeason?.status === MediaStatus.PENDING) && ( - <> -
- - {intl.formatMessage(globalMessages.pending)} - -
-
- -
- - )} + mSeason?.status === MediaStatus.PENDING) && + showNon4k && ( + <> +
+ + {intl.formatMessage(globalMessages.pending)} + +
+
+ +
+ + )} {mSeason?.status === - MediaStatus.PARTIALLY_AVAILABLE && ( - <> -
- - {intl.formatMessage( - globalMessages.partiallyavailable - )} - -
-
- -
- - )} - {mSeason?.status === MediaStatus.AVAILABLE && ( - <> -
- - {intl.formatMessage(globalMessages.available)} - -
-
- -
- - )} + MediaStatus.PARTIALLY_AVAILABLE && + showNon4k && ( + <> +
+ + {intl.formatMessage( + globalMessages.partiallyavailable + )} + +
+
+ +
+ + )} + {mSeason?.status === MediaStatus.AVAILABLE && + showNon4k && ( + <> +
+ + {intl.formatMessage( + globalMessages.available + )} + +
+
+ +
+ + )} {((!mSeason4k && request4k?.status === MediaRequestStatus.APPROVED) ||