Compare commits
2 Commits
develop
...
feat/disco
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3369adfb9a | ||
|
|
76f57550fa |
@@ -26,6 +26,8 @@ export interface PublicSettingsResponse {
|
|||||||
applicationUrl: string;
|
applicationUrl: string;
|
||||||
hideAvailable: boolean;
|
hideAvailable: boolean;
|
||||||
localLogin: boolean;
|
localLogin: boolean;
|
||||||
|
movieEnabled: boolean;
|
||||||
|
seriesEnabled: boolean;
|
||||||
movie4kEnabled: boolean;
|
movie4kEnabled: boolean;
|
||||||
series4kEnabled: boolean;
|
series4kEnabled: boolean;
|
||||||
region: string;
|
region: string;
|
||||||
|
|||||||
@@ -115,6 +115,8 @@ interface FullPublicSettings extends PublicSettings {
|
|||||||
applicationUrl: string;
|
applicationUrl: string;
|
||||||
hideAvailable: boolean;
|
hideAvailable: boolean;
|
||||||
localLogin: boolean;
|
localLogin: boolean;
|
||||||
|
movieEnabled: boolean;
|
||||||
|
seriesEnabled: boolean;
|
||||||
movie4kEnabled: boolean;
|
movie4kEnabled: boolean;
|
||||||
series4kEnabled: boolean;
|
series4kEnabled: boolean;
|
||||||
region: string;
|
region: string;
|
||||||
@@ -491,6 +493,12 @@ class Settings {
|
|||||||
applicationUrl: this.data.main.applicationUrl,
|
applicationUrl: this.data.main.applicationUrl,
|
||||||
hideAvailable: this.data.main.hideAvailable,
|
hideAvailable: this.data.main.hideAvailable,
|
||||||
localLogin: this.data.main.localLogin,
|
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(
|
movie4kEnabled: this.data.radarr.some(
|
||||||
(radarr) => radarr.is4k && radarr.isDefault
|
(radarr) => radarr.is4k && radarr.isDefault
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasRequestable =
|
const hasRequestable =
|
||||||
|
settings.currentSettings.movieEnabled &&
|
||||||
hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
||||||
type: 'or',
|
type: 'or',
|
||||||
}) &&
|
}) &&
|
||||||
@@ -237,14 +238,19 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="media-title">
|
<div className="media-title">
|
||||||
<div className="media-status">
|
<div className="media-status">
|
||||||
<StatusBadge
|
{settings.currentSettings.movieEnabled &&
|
||||||
status={collectionStatus}
|
hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
||||||
downloadItem={downloadStatus}
|
type: 'or',
|
||||||
title={titles}
|
}) && (
|
||||||
inProgress={data.parts.some(
|
<StatusBadge
|
||||||
(part) => (part.mediaInfo?.downloadStatus ?? []).length > 0
|
status={collectionStatus}
|
||||||
|
downloadItem={downloadStatus}
|
||||||
|
title={titles}
|
||||||
|
inProgress={data.parts.some(
|
||||||
|
(part) => (part.mediaInfo?.downloadStatus ?? []).length > 0
|
||||||
|
)}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
|
||||||
{settings.currentSettings.movie4kEnabled &&
|
{settings.currentSettings.movie4kEnabled &&
|
||||||
hasPermission(
|
hasPermission(
|
||||||
[Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
|
[Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
|
||||||
@@ -340,6 +346,7 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
|
|||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.mediaInfo?.status}
|
status={title.mediaInfo?.status}
|
||||||
|
status4k={title.mediaInfo?.status4k}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.title}
|
title={title.title}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ const ListView = ({
|
|||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.mediaInfo?.status}
|
status={title.mediaInfo?.status}
|
||||||
|
status4k={title.mediaInfo?.status4k}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.title}
|
title={title.title}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
@@ -79,6 +80,7 @@ const ListView = ({
|
|||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.mediaInfo?.status}
|
status={title.mediaInfo?.status}
|
||||||
|
status4k={title.mediaInfo?.status4k}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.name}
|
title={title.name}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
|
|||||||
@@ -72,8 +72,12 @@ const CreateIssueModal = ({
|
|||||||
const availableSeasons = (data?.mediaInfo?.seasons ?? [])
|
const availableSeasons = (data?.mediaInfo?.seasons ?? [])
|
||||||
.filter(
|
.filter(
|
||||||
(season) =>
|
(season) =>
|
||||||
season.status === MediaStatus.AVAILABLE ||
|
(settings.currentSettings.seriesEnabled &&
|
||||||
season.status === MediaStatus.PARTIALLY_AVAILABLE ||
|
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], {
|
||||||
|
type: 'or',
|
||||||
|
}) &&
|
||||||
|
(season.status === MediaStatus.AVAILABLE ||
|
||||||
|
season.status === MediaStatus.PARTIALLY_AVAILABLE)) ||
|
||||||
(settings.currentSettings.series4kEnabled &&
|
(settings.currentSettings.series4kEnabled &&
|
||||||
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], {
|
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], {
|
||||||
type: 'or',
|
type: 'or',
|
||||||
|
|||||||
@@ -468,22 +468,23 @@ const ManageSlideOver = ({
|
|||||||
{intl.formatMessage(messages.manageModalAdvanced)}
|
{intl.formatMessage(messages.manageModalAdvanced)}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{data?.mediaInfo.status !== MediaStatus.AVAILABLE && (
|
{data?.mediaInfo.status !== MediaStatus.AVAILABLE &&
|
||||||
<Button
|
settings.currentSettings.seriesEnabled && (
|
||||||
onClick={() => markAvailable()}
|
<Button
|
||||||
className="w-full"
|
onClick={() => markAvailable()}
|
||||||
buttonType="success"
|
className="w-full"
|
||||||
>
|
buttonType="success"
|
||||||
<CheckCircleIcon />
|
>
|
||||||
<span>
|
<CheckCircleIcon />
|
||||||
{intl.formatMessage(
|
<span>
|
||||||
mediaType === 'movie'
|
{intl.formatMessage(
|
||||||
? messages.markavailable
|
mediaType === 'movie'
|
||||||
: messages.markallseasonsavailable
|
? messages.markavailable
|
||||||
)}
|
: messages.markallseasonsavailable
|
||||||
</span>
|
)}
|
||||||
</Button>
|
</span>
|
||||||
)}
|
</Button>
|
||||||
|
)}
|
||||||
{data?.mediaInfo.status4k !== MediaStatus.AVAILABLE &&
|
{data?.mediaInfo.status4k !== MediaStatus.AVAILABLE &&
|
||||||
settings.currentSettings.series4kEnabled && (
|
settings.currentSettings.series4kEnabled && (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ const MediaSlider = ({
|
|||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.mediaInfo?.status}
|
status={title.mediaInfo?.status}
|
||||||
|
status4k={title.mediaInfo?.status4k}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.title}
|
title={title.title}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
@@ -112,6 +113,7 @@ const MediaSlider = ({
|
|||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.mediaInfo?.status}
|
status={title.mediaInfo?.status}
|
||||||
|
status4k={title.mediaInfo?.status4k}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.name}
|
title={title.name}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
|||||||
const mediaLinks: PlayButtonLink[] = [];
|
const mediaLinks: PlayButtonLink[] = [];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
settings.currentSettings.movieEnabled &&
|
||||||
plexUrl &&
|
plexUrl &&
|
||||||
hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
||||||
type: 'or',
|
type: 'or',
|
||||||
@@ -314,16 +315,28 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="media-title">
|
<div className="media-title">
|
||||||
<div className="media-status">
|
<div className="media-status">
|
||||||
<StatusBadge
|
{settings.currentSettings.movieEnabled &&
|
||||||
status={data.mediaInfo?.status}
|
hasPermission(
|
||||||
downloadItem={data.mediaInfo?.downloadStatus}
|
[
|
||||||
title={data.title}
|
Permission.MANAGE_REQUESTS,
|
||||||
inProgress={(data.mediaInfo?.downloadStatus ?? []).length > 0}
|
Permission.REQUEST,
|
||||||
tmdbId={data.mediaInfo?.tmdbId}
|
Permission.REQUEST_MOVIE,
|
||||||
mediaType="movie"
|
],
|
||||||
plexUrl={plexUrl}
|
{
|
||||||
serviceUrl={data.mediaInfo?.serviceUrl}
|
type: 'or',
|
||||||
/>
|
}
|
||||||
|
) && (
|
||||||
|
<StatusBadge
|
||||||
|
status={data.mediaInfo?.status}
|
||||||
|
downloadItem={data.mediaInfo?.downloadStatus}
|
||||||
|
title={data.title}
|
||||||
|
inProgress={(data.mediaInfo?.downloadStatus ?? []).length > 0}
|
||||||
|
tmdbId={data.mediaInfo?.tmdbId}
|
||||||
|
mediaType="movie"
|
||||||
|
plexUrl={plexUrl}
|
||||||
|
serviceUrl={data.mediaInfo?.serviceUrl}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{settings.currentSettings.movie4kEnabled &&
|
{settings.currentSettings.movie4kEnabled &&
|
||||||
hasPermission(
|
hasPermission(
|
||||||
[
|
[
|
||||||
@@ -379,7 +392,8 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
|||||||
tmdbId={data.id}
|
tmdbId={data.id}
|
||||||
onUpdate={() => revalidate()}
|
onUpdate={() => revalidate()}
|
||||||
/>
|
/>
|
||||||
{(data.mediaInfo?.status === MediaStatus.AVAILABLE ||
|
{((settings.currentSettings.movieEnabled &&
|
||||||
|
data.mediaInfo?.status === MediaStatus.AVAILABLE) ||
|
||||||
(settings.currentSettings.movie4kEnabled &&
|
(settings.currentSettings.movie4kEnabled &&
|
||||||
hasPermission(
|
hasPermission(
|
||||||
[Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
|
[Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE],
|
||||||
|
|||||||
@@ -159,17 +159,19 @@ const NotificationTypeSelector = ({
|
|||||||
{ type: 'or' }
|
{ type: 'or' }
|
||||||
) ||
|
) ||
|
||||||
// Cannot submit non-4K movie requests OR has Auto-Approve perms for non-4K movies
|
// Cannot submit non-4K movie requests OR has Auto-Approve perms for non-4K movies
|
||||||
((!hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
((!settings.currentSettings.movieEnabled ||
|
||||||
type: 'or',
|
!hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
||||||
}) ||
|
type: 'or',
|
||||||
|
}) ||
|
||||||
hasPermission(
|
hasPermission(
|
||||||
[Permission.AUTO_APPROVE, Permission.AUTO_APPROVE_MOVIE],
|
[Permission.AUTO_APPROVE, Permission.AUTO_APPROVE_MOVIE],
|
||||||
{ type: 'or' }
|
{ type: 'or' }
|
||||||
)) &&
|
)) &&
|
||||||
// Cannot submit non-4K series requests OR has Auto-Approve perms for non-4K series
|
// Cannot submit non-4K series requests OR has Auto-Approve perms for non-4K series
|
||||||
(!hasPermission([Permission.REQUEST, Permission.REQUEST_TV], {
|
(!settings.currentSettings.seriesEnabled ||
|
||||||
type: 'or',
|
!hasPermission([Permission.REQUEST, Permission.REQUEST_TV], {
|
||||||
}) ||
|
type: 'or',
|
||||||
|
}) ||
|
||||||
hasPermission(
|
hasPermission(
|
||||||
[Permission.AUTO_APPROVE, Permission.AUTO_APPROVE_TV],
|
[Permission.AUTO_APPROVE, Permission.AUTO_APPROVE_TV],
|
||||||
{ type: 'or' }
|
{ type: 'or' }
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ const PersonDetails = () => {
|
|||||||
summary={media.overview}
|
summary={media.overview}
|
||||||
mediaType={media.mediaType as 'movie' | 'tv'}
|
mediaType={media.mediaType as 'movie' | 'tv'}
|
||||||
status={media.mediaInfo?.status}
|
status={media.mediaInfo?.status}
|
||||||
|
status4k={media.mediaInfo?.status4k}
|
||||||
canExpand
|
canExpand
|
||||||
/>
|
/>
|
||||||
{media.character && (
|
{media.character && (
|
||||||
@@ -185,6 +186,7 @@ const PersonDetails = () => {
|
|||||||
summary={media.overview}
|
summary={media.overview}
|
||||||
mediaType={media.mediaType as 'movie' | 'tv'}
|
mediaType={media.mediaType as 'movie' | 'tv'}
|
||||||
status={media.mediaInfo?.status}
|
status={media.mediaInfo?.status}
|
||||||
|
status4k={media.mediaInfo?.status}
|
||||||
canExpand
|
canExpand
|
||||||
/>
|
/>
|
||||||
{media.job && (
|
{media.job && (
|
||||||
|
|||||||
@@ -273,7 +273,9 @@ const RequestButton = ({
|
|||||||
: Permission.REQUEST_TV,
|
: Permission.REQUEST_TV,
|
||||||
],
|
],
|
||||||
{ type: 'or' }
|
{ type: 'or' }
|
||||||
)
|
) &&
|
||||||
|
((settings.currentSettings.movieEnabled && mediaType === 'movie') ||
|
||||||
|
(settings.currentSettings.seriesEnabled && mediaType === 'tv'))
|
||||||
) {
|
) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
id: 'request',
|
id: 'request',
|
||||||
@@ -292,7 +294,8 @@ const RequestButton = ({
|
|||||||
}) &&
|
}) &&
|
||||||
media &&
|
media &&
|
||||||
media.status !== MediaStatus.AVAILABLE &&
|
media.status !== MediaStatus.AVAILABLE &&
|
||||||
!isShowComplete
|
!isShowComplete &&
|
||||||
|
settings.currentSettings.seriesEnabled
|
||||||
) {
|
) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
id: 'request-more',
|
id: 'request-more',
|
||||||
|
|||||||
@@ -73,7 +73,10 @@ const StatusBadge = ({
|
|||||||
type: 'or',
|
type: 'or',
|
||||||
}
|
}
|
||||||
) &&
|
) &&
|
||||||
(!is4k ||
|
((!is4k &&
|
||||||
|
(mediaType === 'movie'
|
||||||
|
? settings.currentSettings.movieEnabled
|
||||||
|
: settings.currentSettings.seriesEnabled)) ||
|
||||||
(mediaType === 'movie'
|
(mediaType === 'movie'
|
||||||
? settings.currentSettings.movie4kEnabled
|
? settings.currentSettings.movie4kEnabled
|
||||||
: settings.currentSettings.series4kEnabled))
|
: settings.currentSettings.series4kEnabled))
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ const TmdbTitleCard = ({
|
|||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.mediaInfo?.status}
|
status={title.mediaInfo?.status}
|
||||||
|
status4k={title.mediaInfo?.status4k}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.title}
|
title={title.title}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
@@ -71,6 +72,7 @@ const TmdbTitleCard = ({
|
|||||||
id={title.id}
|
id={title.id}
|
||||||
image={title.posterPath}
|
image={title.posterPath}
|
||||||
status={title.mediaInfo?.status}
|
status={title.mediaInfo?.status}
|
||||||
|
status4k={title.mediaInfo?.status4k}
|
||||||
summary={title.overview}
|
summary={title.overview}
|
||||||
title={title.name}
|
title={title.name}
|
||||||
userScore={title.voteAverage}
|
userScore={title.voteAverage}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import RequestModal from '@app/components/RequestModal';
|
|||||||
import ErrorCard from '@app/components/TitleCard/ErrorCard';
|
import ErrorCard from '@app/components/TitleCard/ErrorCard';
|
||||||
import Placeholder from '@app/components/TitleCard/Placeholder';
|
import Placeholder from '@app/components/TitleCard/Placeholder';
|
||||||
import { useIsTouch } from '@app/hooks/useIsTouch';
|
import { useIsTouch } from '@app/hooks/useIsTouch';
|
||||||
|
import useSettings from '@app/hooks/useSettings';
|
||||||
import { Permission, useUser } from '@app/hooks/useUser';
|
import { Permission, useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { withProperties } from '@app/utils/typeHelpers';
|
import { withProperties } from '@app/utils/typeHelpers';
|
||||||
@@ -26,6 +27,7 @@ interface TitleCardProps {
|
|||||||
userScore?: number;
|
userScore?: number;
|
||||||
mediaType: MediaType;
|
mediaType: MediaType;
|
||||||
status?: MediaStatus;
|
status?: MediaStatus;
|
||||||
|
status4k?: MediaStatus;
|
||||||
canExpand?: boolean;
|
canExpand?: boolean;
|
||||||
inProgress?: boolean;
|
inProgress?: boolean;
|
||||||
}
|
}
|
||||||
@@ -37,6 +39,7 @@ const TitleCard = ({
|
|||||||
year,
|
year,
|
||||||
title,
|
title,
|
||||||
status,
|
status,
|
||||||
|
status4k,
|
||||||
mediaType,
|
mediaType,
|
||||||
inProgress = false,
|
inProgress = false,
|
||||||
canExpand = false,
|
canExpand = false,
|
||||||
@@ -46,8 +49,11 @@ const TitleCard = ({
|
|||||||
const { hasPermission } = useUser();
|
const { hasPermission } = useUser();
|
||||||
const [isUpdating, setIsUpdating] = useState(false);
|
const [isUpdating, setIsUpdating] = useState(false);
|
||||||
const [currentStatus, setCurrentStatus] = useState(status);
|
const [currentStatus, setCurrentStatus] = useState(status);
|
||||||
|
const [currentStatus4k, setCurrentStatus4k] = useState(status4k);
|
||||||
const [showDetail, setShowDetail] = useState(false);
|
const [showDetail, setShowDetail] = useState(false);
|
||||||
const [showRequestModal, setShowRequestModal] = useState(false);
|
const [showRequestModal, setShowRequestModal] = useState(false);
|
||||||
|
const [showRequestModal4k, setShowRequestModal4k] = useState(false);
|
||||||
|
const settings = useSettings();
|
||||||
|
|
||||||
// Just to get the year from the date
|
// Just to get the year from the date
|
||||||
if (year) {
|
if (year) {
|
||||||
@@ -56,7 +62,8 @@ const TitleCard = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentStatus(status);
|
setCurrentStatus(status);
|
||||||
}, [status]);
|
setCurrentStatus4k(status4k);
|
||||||
|
}, [status, status4k]);
|
||||||
|
|
||||||
const requestComplete = useCallback((newStatus: MediaStatus) => {
|
const requestComplete = useCallback((newStatus: MediaStatus) => {
|
||||||
setCurrentStatus(newStatus);
|
setCurrentStatus(newStatus);
|
||||||
@@ -70,15 +77,44 @@ const TitleCard = ({
|
|||||||
|
|
||||||
const closeModal = useCallback(() => setShowRequestModal(false), []);
|
const closeModal = useCallback(() => setShowRequestModal(false), []);
|
||||||
|
|
||||||
const showRequestButton = hasPermission(
|
const requestComplete4k = useCallback((newStatus: MediaStatus) => {
|
||||||
[
|
setCurrentStatus4k(newStatus);
|
||||||
Permission.REQUEST,
|
setShowRequestModal4k(false);
|
||||||
mediaType === 'movie' || mediaType === 'collection'
|
}, []);
|
||||||
? Permission.REQUEST_MOVIE
|
|
||||||
: Permission.REQUEST_TV,
|
const closeModal4k = useCallback(() => setShowRequestModal4k(false), []);
|
||||||
],
|
|
||||||
{ type: 'or' }
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -99,6 +135,21 @@ const TitleCard = ({
|
|||||||
onUpdating={requestUpdating}
|
onUpdating={requestUpdating}
|
||||||
onCancel={closeModal}
|
onCancel={closeModal}
|
||||||
/>
|
/>
|
||||||
|
<RequestModal
|
||||||
|
tmdbId={id}
|
||||||
|
show={showRequestModal4k}
|
||||||
|
type={
|
||||||
|
mediaType === 'movie'
|
||||||
|
? 'movie'
|
||||||
|
: mediaType === 'collection'
|
||||||
|
? 'collection'
|
||||||
|
: 'tv'
|
||||||
|
}
|
||||||
|
onComplete={requestComplete4k}
|
||||||
|
onUpdating={requestUpdating}
|
||||||
|
onCancel={closeModal4k}
|
||||||
|
is4k
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
className={`relative transform-gpu cursor-default overflow-hidden rounded-xl bg-gray-800 bg-cover outline-none ring-1 transition duration-300 ${
|
className={`relative transform-gpu cursor-default overflow-hidden rounded-xl bg-gray-800 bg-cover outline-none ring-1 transition duration-300 ${
|
||||||
showDetail
|
showDetail
|
||||||
@@ -151,15 +202,24 @@ const TitleCard = ({
|
|||||||
: intl.formatMessage(globalMessages.tvshow)}
|
: intl.formatMessage(globalMessages.tvshow)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{currentStatus && currentStatus !== MediaStatus.UNKNOWN && (
|
<div className="pointer-events-none z-40 flex items-center">
|
||||||
<div className="pointer-events-none z-40 flex items-center">
|
{currentStatus && currentStatus !== MediaStatus.UNKNOWN ? (
|
||||||
<StatusBadgeMini
|
<StatusBadgeMini
|
||||||
status={currentStatus}
|
status={currentStatus}
|
||||||
inProgress={inProgress}
|
inProgress={inProgress}
|
||||||
shrink
|
shrink
|
||||||
/>
|
/>
|
||||||
</div>
|
) : (
|
||||||
)}
|
currentStatus4k &&
|
||||||
|
currentStatus4k !== MediaStatus.UNKNOWN && (
|
||||||
|
<StatusBadgeMini
|
||||||
|
status={currentStatus4k}
|
||||||
|
inProgress={inProgress}
|
||||||
|
shrink
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Transition
|
<Transition
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
@@ -178,7 +238,9 @@ const TitleCard = ({
|
|||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
show={!image || showDetail || showRequestModal}
|
show={
|
||||||
|
!image || showDetail || showRequestModal || showRequestModal4k
|
||||||
|
}
|
||||||
enter="transition-opacity"
|
enter="transition-opacity"
|
||||||
enterFrom="opacity-0"
|
enterFrom="opacity-0"
|
||||||
enterTo="opacity-100"
|
enterTo="opacity-100"
|
||||||
@@ -206,10 +268,7 @@ const TitleCard = ({
|
|||||||
<div className="flex h-full w-full items-end">
|
<div className="flex h-full w-full items-end">
|
||||||
<div
|
<div
|
||||||
className={`px-2 text-white ${
|
className={`px-2 text-white ${
|
||||||
!showRequestButton ||
|
!isRequestable ? 'pb-2' : 'pb-11'
|
||||||
(currentStatus && currentStatus !== MediaStatus.UNKNOWN)
|
|
||||||
? 'pb-2'
|
|
||||||
: 'pb-11'
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{year && (
|
{year && (
|
||||||
@@ -232,12 +291,7 @@ const TitleCard = ({
|
|||||||
<div
|
<div
|
||||||
className="whitespace-normal text-xs"
|
className="whitespace-normal text-xs"
|
||||||
style={{
|
style={{
|
||||||
WebkitLineClamp:
|
WebkitLineClamp: !isRequestable ? 5 : 3,
|
||||||
!showRequestButton ||
|
|
||||||
(currentStatus &&
|
|
||||||
currentStatus !== MediaStatus.UNKNOWN)
|
|
||||||
? 5
|
|
||||||
: 3,
|
|
||||||
display: '-webkit-box',
|
display: '-webkit-box',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
WebkitBoxOrient: 'vertical',
|
WebkitBoxOrient: 'vertical',
|
||||||
@@ -253,20 +307,39 @@ const TitleCard = ({
|
|||||||
|
|
||||||
<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">
|
||||||
{showRequestButton &&
|
{showRequestButton &&
|
||||||
(!currentStatus || currentStatus === MediaStatus.UNKNOWN) && (
|
(!currentStatus || currentStatus === MediaStatus.UNKNOWN) ? (
|
||||||
|
<Button
|
||||||
|
buttonType="primary"
|
||||||
|
buttonSize="sm"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setShowRequestModal(true);
|
||||||
|
}}
|
||||||
|
className="h-7 w-full"
|
||||||
|
>
|
||||||
|
<ArrowDownTrayIcon />
|
||||||
|
<span>{intl.formatMessage(globalMessages.request)}</span>
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
showRequestButton4k &&
|
||||||
|
(!currentStatus4k ||
|
||||||
|
currentStatus4k === MediaStatus.UNKNOWN) && (
|
||||||
<Button
|
<Button
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
buttonSize="sm"
|
buttonSize="sm"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setShowRequestModal(true);
|
setShowRequestModal4k(true);
|
||||||
}}
|
}}
|
||||||
className="h-7 w-full"
|
className="h-7 w-full"
|
||||||
>
|
>
|
||||||
<ArrowDownTrayIcon />
|
<ArrowDownTrayIcon />
|
||||||
<span>{intl.formatMessage(globalMessages.request)}</span>
|
<span>
|
||||||
|
{intl.formatMessage(globalMessages.request4k)}
|
||||||
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|||||||
@@ -149,7 +149,13 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
|||||||
|
|
||||||
const mediaLinks: PlayButtonLink[] = [];
|
const mediaLinks: PlayButtonLink[] = [];
|
||||||
|
|
||||||
if (plexUrl) {
|
if (
|
||||||
|
settings.currentSettings.seriesEnabled &&
|
||||||
|
plexUrl &&
|
||||||
|
hasPermission([Permission.REQUEST, Permission.REQUEST_TV], {
|
||||||
|
type: 'or',
|
||||||
|
})
|
||||||
|
) {
|
||||||
mediaLinks.push({
|
mediaLinks.push({
|
||||||
text: intl.formatMessage(messages.playonplex),
|
text: intl.formatMessage(messages.playonplex),
|
||||||
url: plexUrl,
|
url: plexUrl,
|
||||||
@@ -337,16 +343,28 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="media-title">
|
<div className="media-title">
|
||||||
<div className="media-status">
|
<div className="media-status">
|
||||||
<StatusBadge
|
{settings.currentSettings.seriesEnabled &&
|
||||||
status={data.mediaInfo?.status}
|
hasPermission(
|
||||||
downloadItem={data.mediaInfo?.downloadStatus}
|
[
|
||||||
title={data.name}
|
Permission.MANAGE_REQUESTS,
|
||||||
inProgress={(data.mediaInfo?.downloadStatus ?? []).length > 0}
|
Permission.REQUEST,
|
||||||
tmdbId={data.mediaInfo?.tmdbId}
|
Permission.REQUEST_TV,
|
||||||
mediaType="tv"
|
],
|
||||||
plexUrl={plexUrl}
|
{
|
||||||
serviceUrl={data.mediaInfo?.serviceUrl}
|
type: 'or',
|
||||||
/>
|
}
|
||||||
|
) && (
|
||||||
|
<StatusBadge
|
||||||
|
status={data.mediaInfo?.status}
|
||||||
|
downloadItem={data.mediaInfo?.downloadStatus}
|
||||||
|
title={data.name}
|
||||||
|
inProgress={(data.mediaInfo?.downloadStatus ?? []).length > 0}
|
||||||
|
tmdbId={data.mediaInfo?.tmdbId}
|
||||||
|
mediaType="tv"
|
||||||
|
plexUrl={plexUrl}
|
||||||
|
serviceUrl={data.mediaInfo?.serviceUrl}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{settings.currentSettings.series4kEnabled &&
|
{settings.currentSettings.series4kEnabled &&
|
||||||
hasPermission(
|
hasPermission(
|
||||||
[
|
[
|
||||||
@@ -404,8 +422,12 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
|||||||
isShowComplete={isComplete}
|
isShowComplete={isComplete}
|
||||||
is4kShowComplete={is4kComplete}
|
is4kShowComplete={is4kComplete}
|
||||||
/>
|
/>
|
||||||
{(data.mediaInfo?.status === MediaStatus.AVAILABLE ||
|
{((settings.currentSettings.seriesEnabled &&
|
||||||
data.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE ||
|
hasPermission([Permission.REQUEST, Permission.REQUEST_TV], {
|
||||||
|
type: 'or',
|
||||||
|
}) &&
|
||||||
|
(data.mediaInfo?.status === MediaStatus.AVAILABLE ||
|
||||||
|
data?.mediaInfo?.status === MediaStatus.PARTIALLY_AVAILABLE)) ||
|
||||||
(settings.currentSettings.series4kEnabled &&
|
(settings.currentSettings.series4kEnabled &&
|
||||||
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], {
|
hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], {
|
||||||
type: 'or',
|
type: 'or',
|
||||||
@@ -524,6 +546,18 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
|||||||
.reverse()
|
.reverse()
|
||||||
.filter((season) => season.seasonNumber !== 0)
|
.filter((season) => season.seasonNumber !== 0)
|
||||||
.map((season) => {
|
.map((season) => {
|
||||||
|
const showNon4k =
|
||||||
|
settings.currentSettings.seriesEnabled &&
|
||||||
|
hasPermission(
|
||||||
|
[
|
||||||
|
Permission.MANAGE_REQUESTS,
|
||||||
|
Permission.REQUEST,
|
||||||
|
Permission.REQUEST_TV,
|
||||||
|
],
|
||||||
|
{
|
||||||
|
type: 'or',
|
||||||
|
}
|
||||||
|
);
|
||||||
const show4k =
|
const show4k =
|
||||||
settings.currentSettings.series4kEnabled &&
|
settings.currentSettings.series4kEnabled &&
|
||||||
hasPermission(
|
hasPermission(
|
||||||
@@ -588,65 +622,75 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
|||||||
</div>
|
</div>
|
||||||
{((!mSeason &&
|
{((!mSeason &&
|
||||||
request?.status === MediaRequestStatus.APPROVED) ||
|
request?.status === MediaRequestStatus.APPROVED) ||
|
||||||
mSeason?.status === MediaStatus.PROCESSING) && (
|
mSeason?.status === MediaStatus.PROCESSING) &&
|
||||||
<>
|
showNon4k && (
|
||||||
<div className="hidden md:flex">
|
<>
|
||||||
<Badge badgeType="primary">
|
<div className="hidden md:flex">
|
||||||
{intl.formatMessage(globalMessages.requested)}
|
<Badge badgeType="primary">
|
||||||
</Badge>
|
{intl.formatMessage(
|
||||||
</div>
|
globalMessages.requested
|
||||||
<div className="flex md:hidden">
|
)}
|
||||||
<StatusBadgeMini
|
</Badge>
|
||||||
status={MediaStatus.PROCESSING}
|
</div>
|
||||||
/>
|
<div className="flex md:hidden">
|
||||||
</div>
|
<StatusBadgeMini
|
||||||
</>
|
status={MediaStatus.PROCESSING}
|
||||||
)}
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{((!mSeason &&
|
{((!mSeason &&
|
||||||
request?.status === MediaRequestStatus.PENDING) ||
|
request?.status === MediaRequestStatus.PENDING) ||
|
||||||
mSeason?.status === MediaStatus.PENDING) && (
|
mSeason?.status === MediaStatus.PENDING) &&
|
||||||
<>
|
showNon4k && (
|
||||||
<div className="hidden md:flex">
|
<>
|
||||||
<Badge badgeType="warning">
|
<div className="hidden md:flex">
|
||||||
{intl.formatMessage(globalMessages.pending)}
|
<Badge badgeType="warning">
|
||||||
</Badge>
|
{intl.formatMessage(globalMessages.pending)}
|
||||||
</div>
|
</Badge>
|
||||||
<div className="flex md:hidden">
|
</div>
|
||||||
<StatusBadgeMini status={MediaStatus.PENDING} />
|
<div className="flex md:hidden">
|
||||||
</div>
|
<StatusBadgeMini
|
||||||
</>
|
status={MediaStatus.PENDING}
|
||||||
)}
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{mSeason?.status ===
|
{mSeason?.status ===
|
||||||
MediaStatus.PARTIALLY_AVAILABLE && (
|
MediaStatus.PARTIALLY_AVAILABLE &&
|
||||||
<>
|
showNon4k && (
|
||||||
<div className="hidden md:flex">
|
<>
|
||||||
<Badge badgeType="success">
|
<div className="hidden md:flex">
|
||||||
{intl.formatMessage(
|
<Badge badgeType="success">
|
||||||
globalMessages.partiallyavailable
|
{intl.formatMessage(
|
||||||
)}
|
globalMessages.partiallyavailable
|
||||||
</Badge>
|
)}
|
||||||
</div>
|
</Badge>
|
||||||
<div className="flex md:hidden">
|
</div>
|
||||||
<StatusBadgeMini
|
<div className="flex md:hidden">
|
||||||
status={MediaStatus.PARTIALLY_AVAILABLE}
|
<StatusBadgeMini
|
||||||
/>
|
status={MediaStatus.PARTIALLY_AVAILABLE}
|
||||||
</div>
|
/>
|
||||||
</>
|
</div>
|
||||||
)}
|
</>
|
||||||
{mSeason?.status === MediaStatus.AVAILABLE && (
|
)}
|
||||||
<>
|
{mSeason?.status === MediaStatus.AVAILABLE &&
|
||||||
<div className="hidden md:flex">
|
showNon4k && (
|
||||||
<Badge badgeType="success">
|
<>
|
||||||
{intl.formatMessage(globalMessages.available)}
|
<div className="hidden md:flex">
|
||||||
</Badge>
|
<Badge badgeType="success">
|
||||||
</div>
|
{intl.formatMessage(
|
||||||
<div className="flex md:hidden">
|
globalMessages.available
|
||||||
<StatusBadgeMini
|
)}
|
||||||
status={MediaStatus.AVAILABLE}
|
</Badge>
|
||||||
/>
|
</div>
|
||||||
</div>
|
<div className="flex md:hidden">
|
||||||
</>
|
<StatusBadgeMini
|
||||||
)}
|
status={MediaStatus.AVAILABLE}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{((!mSeason4k &&
|
{((!mSeason4k &&
|
||||||
request4k?.status ===
|
request4k?.status ===
|
||||||
MediaRequestStatus.APPROVED) ||
|
MediaRequestStatus.APPROVED) ||
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ const defaultSettings = {
|
|||||||
applicationUrl: '',
|
applicationUrl: '',
|
||||||
hideAvailable: false,
|
hideAvailable: false,
|
||||||
localLogin: true,
|
localLogin: true,
|
||||||
|
movieEnabled: false,
|
||||||
|
seriesEnabled: false,
|
||||||
movie4kEnabled: false,
|
movie4kEnabled: false,
|
||||||
series4kEnabled: false,
|
series4kEnabled: false,
|
||||||
region: '',
|
region: '',
|
||||||
|
|||||||
@@ -174,6 +174,8 @@ CoreApp.getInitialProps = async (initialProps) => {
|
|||||||
applicationTitle: '',
|
applicationTitle: '',
|
||||||
applicationUrl: '',
|
applicationUrl: '',
|
||||||
hideAvailable: false,
|
hideAvailable: false,
|
||||||
|
movieEnabled: false,
|
||||||
|
seriesEnabled: false,
|
||||||
movie4kEnabled: false,
|
movie4kEnabled: false,
|
||||||
series4kEnabled: false,
|
series4kEnabled: false,
|
||||||
localLogin: true,
|
localLogin: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user