|
|
|
@ -1,10 +1,12 @@
|
|
|
|
|
import Spinner from '@app/assets/spinner.svg';
|
|
|
|
|
import Badge from '@app/components/Common/Badge';
|
|
|
|
|
import Tooltip from '@app/components/Common/Tooltip';
|
|
|
|
|
import DownloadBlock from '@app/components/DownloadBlock';
|
|
|
|
|
import useSettings from '@app/hooks/useSettings';
|
|
|
|
|
import { Permission, useUser } from '@app/hooks/useUser';
|
|
|
|
|
import globalMessages from '@app/i18n/globalMessages';
|
|
|
|
|
import { MediaStatus } from '@server/constants/media';
|
|
|
|
|
import type { DownloadingItem } from '@server/lib/downloadtracker';
|
|
|
|
|
import { defineMessages, useIntl } from 'react-intl';
|
|
|
|
|
|
|
|
|
|
const messages = defineMessages({
|
|
|
|
@ -13,26 +15,31 @@ const messages = defineMessages({
|
|
|
|
|
playonplex: 'Play on Plex',
|
|
|
|
|
openinarr: 'Open in {arr}',
|
|
|
|
|
managemedia: 'Manage {mediaType}',
|
|
|
|
|
seasonepisodenumber: 'S{seasonNumber}E{episodeNumber}',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
interface StatusBadgeProps {
|
|
|
|
|
status?: MediaStatus;
|
|
|
|
|
downloadItem?: DownloadingItem[];
|
|
|
|
|
is4k?: boolean;
|
|
|
|
|
inProgress?: boolean;
|
|
|
|
|
plexUrl?: string;
|
|
|
|
|
serviceUrl?: string;
|
|
|
|
|
tmdbId?: number;
|
|
|
|
|
mediaType?: 'movie' | 'tv';
|
|
|
|
|
title?: string | string[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const StatusBadge = ({
|
|
|
|
|
status,
|
|
|
|
|
downloadItem = [],
|
|
|
|
|
is4k = false,
|
|
|
|
|
inProgress = false,
|
|
|
|
|
plexUrl,
|
|
|
|
|
serviceUrl,
|
|
|
|
|
tmdbId,
|
|
|
|
|
mediaType,
|
|
|
|
|
title,
|
|
|
|
|
}: StatusBadgeProps) => {
|
|
|
|
|
const intl = useIntl();
|
|
|
|
|
const { hasPermission } = useUser();
|
|
|
|
@ -41,6 +48,10 @@ const StatusBadge = ({
|
|
|
|
|
let mediaLink: string | undefined;
|
|
|
|
|
let mediaLinkDescription: string | undefined;
|
|
|
|
|
|
|
|
|
|
const calculateDownloadProgress = (media: DownloadingItem) => {
|
|
|
|
|
return Math.round(((media?.size - media?.sizeLeft) / media?.size) * 100);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
mediaType &&
|
|
|
|
|
plexUrl &&
|
|
|
|
@ -85,21 +96,87 @@ const StatusBadge = ({
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const tooltipContent = downloadItem ? (
|
|
|
|
|
<ul>
|
|
|
|
|
{downloadItem.map((status, index) => (
|
|
|
|
|
<li
|
|
|
|
|
key={`dl-status-${status.externalId}-${index}`}
|
|
|
|
|
className="border-b border-gray-700 last:border-b-0"
|
|
|
|
|
>
|
|
|
|
|
<DownloadBlock
|
|
|
|
|
downloadItem={status}
|
|
|
|
|
title={Array.isArray(title) ? title[index] : title}
|
|
|
|
|
is4k={is4k}
|
|
|
|
|
/>
|
|
|
|
|
</li>
|
|
|
|
|
))}
|
|
|
|
|
</ul>
|
|
|
|
|
) : (
|
|
|
|
|
mediaLinkDescription
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const badgeDownloadProgress = (
|
|
|
|
|
<div
|
|
|
|
|
className={`
|
|
|
|
|
absolute top-0 left-0 z-10 flex h-full ${
|
|
|
|
|
status === MediaStatus.PROCESSING ? 'bg-indigo-500' : 'bg-green-500'
|
|
|
|
|
} transition-all duration-200 ease-in-out
|
|
|
|
|
`}
|
|
|
|
|
style={{
|
|
|
|
|
width: `${
|
|
|
|
|
downloadItem ? calculateDownloadProgress(downloadItem[0]) : 0
|
|
|
|
|
}%`,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
switch (status) {
|
|
|
|
|
case MediaStatus.AVAILABLE:
|
|
|
|
|
return (
|
|
|
|
|
<Tooltip content={mediaLinkDescription}>
|
|
|
|
|
<Badge badgeType="success" href={mediaLink}>
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Tooltip
|
|
|
|
|
content={inProgress && tooltipContent}
|
|
|
|
|
className={`${
|
|
|
|
|
inProgress && 'hidden max-h-96 w-96 overflow-scroll sm:block'
|
|
|
|
|
}`}
|
|
|
|
|
tooltipConfig={{ interactive: true, delayHide: 100 }}
|
|
|
|
|
>
|
|
|
|
|
<Badge
|
|
|
|
|
badgeType="success"
|
|
|
|
|
href={mediaLink}
|
|
|
|
|
className={`${
|
|
|
|
|
inProgress &&
|
|
|
|
|
'relative !bg-gray-700 !bg-opacity-80 !px-0 hover:!bg-gray-700'
|
|
|
|
|
} overflow-hidden`}
|
|
|
|
|
>
|
|
|
|
|
{inProgress && badgeDownloadProgress}
|
|
|
|
|
<div
|
|
|
|
|
className={`relative z-20 flex items-center ${
|
|
|
|
|
inProgress && 'px-2'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<span>
|
|
|
|
|
{intl.formatMessage(
|
|
|
|
|
is4k ? messages.status4k : messages.status,
|
|
|
|
|
{
|
|
|
|
|
status: intl.formatMessage(globalMessages.available),
|
|
|
|
|
status: inProgress
|
|
|
|
|
? intl.formatMessage(globalMessages.processing)
|
|
|
|
|
: intl.formatMessage(globalMessages.available),
|
|
|
|
|
}
|
|
|
|
|
)}
|
|
|
|
|
</span>
|
|
|
|
|
{inProgress && <Spinner className="ml-1 h-3 w-3" />}
|
|
|
|
|
{inProgress && (
|
|
|
|
|
<>
|
|
|
|
|
{mediaType === 'tv' && (
|
|
|
|
|
<span className="ml-1">
|
|
|
|
|
{intl.formatMessage(messages.seasonepisodenumber, {
|
|
|
|
|
seasonNumber: downloadItem[0].episode?.seasonNumber,
|
|
|
|
|
episodeNumber: downloadItem[0].episode?.episodeNumber,
|
|
|
|
|
})}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
<Spinner className="ml-1 h-3 w-3" />
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</Badge>
|
|
|
|
|
</Tooltip>
|
|
|
|
@ -107,20 +184,50 @@ const StatusBadge = ({
|
|
|
|
|
|
|
|
|
|
case MediaStatus.PARTIALLY_AVAILABLE:
|
|
|
|
|
return (
|
|
|
|
|
<Tooltip content={mediaLinkDescription}>
|
|
|
|
|
<Badge badgeType="success" href={mediaLink}>
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Tooltip
|
|
|
|
|
content={inProgress && tooltipContent}
|
|
|
|
|
className={`${
|
|
|
|
|
inProgress && 'hidden max-h-96 w-96 overflow-scroll sm:block'
|
|
|
|
|
}`}
|
|
|
|
|
tooltipConfig={{ interactive: true, delayHide: 100 }}
|
|
|
|
|
>
|
|
|
|
|
<Badge
|
|
|
|
|
badgeType="success"
|
|
|
|
|
href={mediaLink}
|
|
|
|
|
className={`${
|
|
|
|
|
inProgress &&
|
|
|
|
|
'relative !bg-gray-700 !bg-opacity-80 !px-0 hover:!bg-gray-700'
|
|
|
|
|
} overflow-hidden`}
|
|
|
|
|
>
|
|
|
|
|
{inProgress && badgeDownloadProgress}
|
|
|
|
|
<div
|
|
|
|
|
className={`relative z-20 flex items-center ${
|
|
|
|
|
inProgress && 'px-2'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<span>
|
|
|
|
|
{intl.formatMessage(
|
|
|
|
|
is4k ? messages.status4k : messages.status,
|
|
|
|
|
{
|
|
|
|
|
status: intl.formatMessage(
|
|
|
|
|
globalMessages.partiallyavailable
|
|
|
|
|
),
|
|
|
|
|
status: inProgress
|
|
|
|
|
? intl.formatMessage(globalMessages.processing)
|
|
|
|
|
: intl.formatMessage(globalMessages.partiallyavailable),
|
|
|
|
|
}
|
|
|
|
|
)}
|
|
|
|
|
</span>
|
|
|
|
|
{inProgress && <Spinner className="ml-1 h-3 w-3" />}
|
|
|
|
|
{inProgress && (
|
|
|
|
|
<>
|
|
|
|
|
{mediaType === 'tv' && (
|
|
|
|
|
<span className="ml-1">
|
|
|
|
|
{intl.formatMessage(messages.seasonepisodenumber, {
|
|
|
|
|
seasonNumber: downloadItem[0].episode?.seasonNumber,
|
|
|
|
|
episodeNumber: downloadItem[0].episode?.episodeNumber,
|
|
|
|
|
})}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
<Spinner className="ml-1 h-3 w-3" />
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</Badge>
|
|
|
|
|
</Tooltip>
|
|
|
|
@ -128,9 +235,27 @@ const StatusBadge = ({
|
|
|
|
|
|
|
|
|
|
case MediaStatus.PROCESSING:
|
|
|
|
|
return (
|
|
|
|
|
<Tooltip content={mediaLinkDescription}>
|
|
|
|
|
<Badge badgeType="primary" href={mediaLink}>
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<Tooltip
|
|
|
|
|
content={inProgress && tooltipContent}
|
|
|
|
|
className={`${
|
|
|
|
|
inProgress && 'hidden max-h-96 w-96 overflow-scroll sm:block'
|
|
|
|
|
}`}
|
|
|
|
|
tooltipConfig={{ interactive: true, delayHide: 100 }}
|
|
|
|
|
>
|
|
|
|
|
<Badge
|
|
|
|
|
badgeType="primary"
|
|
|
|
|
href={mediaLink}
|
|
|
|
|
className={`${
|
|
|
|
|
inProgress &&
|
|
|
|
|
'relative !bg-gray-700 !bg-opacity-80 !px-0 hover:!bg-gray-700'
|
|
|
|
|
} overflow-hidden`}
|
|
|
|
|
>
|
|
|
|
|
{inProgress && badgeDownloadProgress}
|
|
|
|
|
<div
|
|
|
|
|
className={`relative z-20 flex items-center ${
|
|
|
|
|
inProgress && 'px-2'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<span>
|
|
|
|
|
{intl.formatMessage(
|
|
|
|
|
is4k ? messages.status4k : messages.status,
|
|
|
|
@ -141,7 +266,19 @@ const StatusBadge = ({
|
|
|
|
|
}
|
|
|
|
|
)}
|
|
|
|
|
</span>
|
|
|
|
|
{inProgress && <Spinner className="ml-1 h-3 w-3" />}
|
|
|
|
|
{inProgress && (
|
|
|
|
|
<>
|
|
|
|
|
{mediaType === 'tv' && (
|
|
|
|
|
<span className="ml-1">
|
|
|
|
|
{intl.formatMessage(messages.seasonepisodenumber, {
|
|
|
|
|
seasonNumber: downloadItem[0].episode?.seasonNumber,
|
|
|
|
|
episodeNumber: downloadItem[0].episode?.episodeNumber,
|
|
|
|
|
})}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
<Spinner className="ml-1 h-3 w-3" />
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</Badge>
|
|
|
|
|
</Tooltip>
|
|
|
|
|