mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2025-12-24 02:39:18 -05:00
Compare commits
18 Commits
preview-cs
...
fix-radarr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dd65f8c25 | ||
|
|
f773e0fb2a | ||
|
|
767a24164d | ||
|
|
8394eb5ad4 | ||
|
|
b8425d6388 | ||
|
|
ebb7f00305 | ||
|
|
418d51590d | ||
|
|
a6dd4a8fed | ||
|
|
4d1163c343 | ||
|
|
b085e12ff9 | ||
|
|
33e7a153aa | ||
|
|
9891a7577c | ||
|
|
077e355c77 | ||
|
|
21ab20bba9 | ||
|
|
cdfb30ea16 | ||
|
|
771ecdf781 | ||
|
|
863b675c77 | ||
|
|
5b998bef82 |
1
.github/workflows/cypress.yml
vendored
1
.github/workflows/cypress.yml
vendored
@@ -37,6 +37,7 @@ jobs:
|
||||
COMMIT_INFO_MESSAGE: ${{github.event.pull_request.title}}
|
||||
COMMIT_INFO_SHA: ${{github.event.pull_request.head.sha}}
|
||||
- name: Upload video files
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cypress-videos
|
||||
|
||||
@@ -3,8 +3,8 @@ kubeVersion: ">=1.23.0-0"
|
||||
name: jellyseerr-chart
|
||||
description: Jellyseerr helm chart for Kubernetes
|
||||
type: application
|
||||
version: 2.1.1
|
||||
appVersion: "2.3.0"
|
||||
version: 2.3.0
|
||||
appVersion: "2.5.0"
|
||||
maintainers:
|
||||
- name: Jellyseerr
|
||||
url: https://github.com/Fallenbagel/jellyseerr
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# jellyseerr-chart
|
||||
|
||||
  
|
||||
  
|
||||
|
||||
Jellyseerr helm chart for Kubernetes
|
||||
|
||||
|
||||
@@ -255,7 +255,8 @@ To run jellyseerr as a service:
|
||||
1. Download the [Non-Sucking Service Manager](https://nssm.cc/download)
|
||||
2. Install NSSM:
|
||||
```powershell
|
||||
nssm install Jellyseerr "C:\Program Files\nodejs\node.exe" ["C:\jellyseerr\dist\index.js"]
|
||||
nssm install Jellyseerr "C:\Program Files\nodejs\node.exe" "C:\jellyseerr\dist\index.js"
|
||||
nssm set Jellyseerr AppDirectory "C:\jellyseerr"
|
||||
nssm set Jellyseerr AppEnvironmentExtra NODE_ENV=production
|
||||
```
|
||||
3. Start the service:
|
||||
|
||||
@@ -24,6 +24,12 @@ or for Cloudflare's DNS:
|
||||
```bash
|
||||
--dns=1.1.1.1
|
||||
```
|
||||
or for Quad9 DNS:
|
||||
```bash
|
||||
--dns=9.9.9.9
|
||||
```
|
||||
|
||||
You can try them all and see which one works for your network.
|
||||
|
||||
</TabItem>
|
||||
|
||||
@@ -45,6 +51,16 @@ services:
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
```
|
||||
or for Quad9's DNS:
|
||||
```yaml
|
||||
---
|
||||
services:
|
||||
jellyseerr:
|
||||
dns:
|
||||
- 9.9.9.9
|
||||
```
|
||||
|
||||
You can try them all and see which one works for your network.
|
||||
|
||||
</TabItem>
|
||||
|
||||
@@ -56,7 +72,7 @@ services:
|
||||
4. Click on Change adapter settings.
|
||||
5. Right-click the network interface connected to the internet and select Properties.
|
||||
6. Select Internet Protocol Version 4 (TCP/IPv4) and click Properties.
|
||||
7. Select Use the following DNS server addresses and enter `8.8.8.8` for Google's DNS or `1.1.1.1` for Cloudflare's DNS.
|
||||
7. Select Use the following DNS server addresses and enter `8.8.8.8` for Google's DNS or `1.1.1.1` for Cloudflare's DNS or `9.9.9.9` for Quad9's DNS.
|
||||
|
||||
</TabItem>
|
||||
|
||||
@@ -73,6 +89,10 @@ services:
|
||||
```bash
|
||||
nameserver 1.1.1.1
|
||||
```
|
||||
or for Quad9's DNS:
|
||||
```bash
|
||||
nameserver 9.9.9.9
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
@@ -81,7 +101,7 @@ services:
|
||||
|
||||
Sometimes there are configuration issues with IPV6 that prevent the hostname resolution from working correctly.
|
||||
|
||||
You can try to force the resolution to use IPV4 first by setting the `FORCE_IPV4_FIRST` environment variable to `true`:
|
||||
You can try to force the resolution to use IPV4 first by going to `Settings > Networking > Advanced Networking` and enabling `Force IPv4 Resolution First` setting and restarting. You can also add the environment variable, `FORCE_IPV4_FIRST=true`:
|
||||
|
||||
<Tabs groupId="methods" queryString>
|
||||
<TabItem value="docker-cli" label="Docker CLI">
|
||||
|
||||
@@ -999,7 +999,7 @@ export class MediaRequest {
|
||||
radarrMovie.id,
|
||||
[this.is4k ? 'externalServiceSlug4k' : 'externalServiceSlug']:
|
||||
radarrMovie.titleSlug,
|
||||
[this.is4k ? 'serviceId4k' : 'serviceId']: radarrMovie?.id,
|
||||
[this.is4k ? 'serviceId4k' : 'serviceId']: radarrSettings?.id,
|
||||
};
|
||||
|
||||
await mediaRepository.update({ id: this.media.id }, updateFields);
|
||||
|
||||
@@ -237,6 +237,19 @@ mediaRoutes.delete(
|
||||
}
|
||||
|
||||
if (isMovie) {
|
||||
// check if the movie exists
|
||||
try {
|
||||
await (service as RadarrAPI).getMovie({
|
||||
id: parseInt(
|
||||
is4k
|
||||
? (media.externalServiceSlug4k as string)
|
||||
: (media.externalServiceSlug as string)
|
||||
),
|
||||
});
|
||||
} catch {
|
||||
return res.status(204).send();
|
||||
}
|
||||
// remove the movie
|
||||
await (service as RadarrAPI).removeMovie(
|
||||
parseInt(
|
||||
is4k
|
||||
@@ -251,6 +264,13 @@ mediaRoutes.delete(
|
||||
if (!tvdbId) {
|
||||
throw new Error('TVDB ID not found');
|
||||
}
|
||||
// check if the series exists
|
||||
try {
|
||||
await (service as SonarrAPI).getSeriesByTvdbId(tvdbId);
|
||||
} catch {
|
||||
return res.status(204).send();
|
||||
}
|
||||
// remove the series
|
||||
await (service as SonarrAPI).removeSerie(tvdbId);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,22 +60,24 @@ router.get('/', async (req, res, next) => {
|
||||
query = query.orderBy('user.updatedAt', 'DESC');
|
||||
break;
|
||||
case 'displayname':
|
||||
query = query.orderBy(
|
||||
`CASE WHEN (user.username IS NULL OR user.username = '') THEN (
|
||||
CASE WHEN (user.plexUsername IS NULL OR user.plexUsername = '') THEN (
|
||||
CASE WHEN (user.jellyfinUsername IS NULL OR user.jellyfinUsername = '') THEN
|
||||
"user"."email"
|
||||
ELSE
|
||||
LOWER(user.jellyfinUsername)
|
||||
END)
|
||||
ELSE
|
||||
LOWER(user.jellyfinUsername)
|
||||
END)
|
||||
ELSE
|
||||
LOWER(user.username)
|
||||
END`,
|
||||
'ASC'
|
||||
);
|
||||
query = query
|
||||
.addSelect(
|
||||
`CASE WHEN (user.username IS NULL OR user.username = '') THEN (
|
||||
CASE WHEN (user.plexUsername IS NULL OR user.plexUsername = '') THEN (
|
||||
CASE WHEN (user.jellyfinUsername IS NULL OR user.jellyfinUsername = '') THEN
|
||||
"user"."email"
|
||||
ELSE
|
||||
LOWER(user.jellyfinUsername)
|
||||
END)
|
||||
ELSE
|
||||
LOWER(user.jellyfinUsername)
|
||||
END)
|
||||
ELSE
|
||||
LOWER(user.username)
|
||||
END`,
|
||||
'displayname_sort_key'
|
||||
)
|
||||
.orderBy('displayname_sort_key', 'ASC');
|
||||
break;
|
||||
case 'requests':
|
||||
query = query
|
||||
|
||||
@@ -8,8 +8,9 @@ export default async function createCustomProxyAgent(
|
||||
) {
|
||||
const defaultAgent = new Agent({ keepAliveTimeout: 5000 });
|
||||
|
||||
const skipUrl = (url: string) => {
|
||||
const hostname = new URL(url).hostname;
|
||||
const skipUrl = (url: string | URL) => {
|
||||
const hostname =
|
||||
typeof url === 'string' ? new URL(url).hostname : url.hostname;
|
||||
|
||||
if (proxySettings.bypassLocalAddresses && isLocalAddress(hostname)) {
|
||||
return true;
|
||||
@@ -38,8 +39,7 @@ export default async function createCustomProxyAgent(
|
||||
dispatch: Dispatcher['dispatch']
|
||||
): Dispatcher['dispatch'] => {
|
||||
return (opts, handler) => {
|
||||
const url = opts.origin?.toString();
|
||||
return url && skipUrl(url)
|
||||
return opts.origin && skipUrl(opts.origin)
|
||||
? defaultAgent.dispatch(opts, handler)
|
||||
: dispatch(opts, handler);
|
||||
};
|
||||
@@ -60,13 +60,10 @@ export default async function createCustomProxyAgent(
|
||||
':' +
|
||||
proxySettings.port,
|
||||
token,
|
||||
interceptors: {
|
||||
Client: [noProxyInterceptor],
|
||||
},
|
||||
keepAliveTimeout: 5000,
|
||||
});
|
||||
|
||||
setGlobalDispatcher(proxyAgent);
|
||||
setGlobalDispatcher(proxyAgent.compose(noProxyInterceptor));
|
||||
} catch (e) {
|
||||
logger.error('Failed to connect to the proxy: ' + e.message, {
|
||||
label: 'Proxy',
|
||||
@@ -95,7 +92,11 @@ export default async function createCustomProxyAgent(
|
||||
}
|
||||
|
||||
function isLocalAddress(hostname: string) {
|
||||
if (hostname === 'localhost' || hostname === '127.0.0.1') {
|
||||
if (
|
||||
hostname === 'localhost' ||
|
||||
hostname === '127.0.0.1' ||
|
||||
hostname === '::1'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,17 +14,13 @@ type AirDateBadgeProps = {
|
||||
const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
|
||||
const WEEK = 1000 * 60 * 60 * 24 * 8;
|
||||
const intl = useIntl();
|
||||
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
const dAirDate = new Date(airDate);
|
||||
const nowDate = new Date();
|
||||
const alreadyAired = dAirDate.getTime() < nowDate.getTime();
|
||||
|
||||
const compareWeek = new Date(
|
||||
alreadyAired ? Date.now() - WEEK : Date.now() + WEEK
|
||||
);
|
||||
|
||||
let showRelative = false;
|
||||
|
||||
if (
|
||||
(alreadyAired && dAirDate.getTime() > compareWeek.getTime()) ||
|
||||
(!alreadyAired && dAirDate.getTime() < compareWeek.getTime())
|
||||
@@ -32,6 +28,10 @@ const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
|
||||
showRelative = true;
|
||||
}
|
||||
|
||||
const diffInDays = Math.round(
|
||||
(dAirDate.getTime() - nowDate.getTime()) / (1000 * 60 * 60 * 24)
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Badge badgeType="light">
|
||||
@@ -39,7 +39,7 @@ const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
timeZone,
|
||||
timeZone: 'UTC',
|
||||
})}
|
||||
</Badge>
|
||||
{showRelative && (
|
||||
@@ -49,9 +49,9 @@ const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
|
||||
{
|
||||
relativeTime: (
|
||||
<FormattedRelativeTime
|
||||
value={(dAirDate.getTime() - Date.now()) / 1000}
|
||||
value={diffInDays}
|
||||
unit="day"
|
||||
numeric="auto"
|
||||
updateIntervalInSeconds={1}
|
||||
/>
|
||||
),
|
||||
}
|
||||
|
||||
@@ -161,7 +161,6 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
|
||||
data-form-type="password"
|
||||
data-1pignore="false"
|
||||
data-lpignore="false"
|
||||
data-bwignore="false"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex">
|
||||
|
||||
@@ -118,7 +118,6 @@ const LocalLogin = ({ revalidate }: LocalLoginProps) => {
|
||||
className="!bg-gray-700/80 placeholder:text-gray-400"
|
||||
data-1pignore="false"
|
||||
data-lpignore="false"
|
||||
data-bwignore="false"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex">
|
||||
|
||||
@@ -590,7 +590,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
||||
buttonSize={'md'}
|
||||
onClick={() => setShowBlacklistModal(true)}
|
||||
>
|
||||
<EyeSlashIcon className={'h-3'} />
|
||||
<EyeSlashIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
@@ -608,9 +608,9 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
||||
onClick={onClickWatchlistBtn}
|
||||
>
|
||||
{isUpdating ? (
|
||||
<Spinner className="h-3" />
|
||||
<Spinner />
|
||||
) : (
|
||||
<StarIcon className={'h-3 text-amber-300'} />
|
||||
<StarIcon className={'text-amber-300'} />
|
||||
)}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
@@ -623,17 +623,15 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
||||
buttonSize={'md'}
|
||||
onClick={onClickDeleteWatchlistBtn}
|
||||
>
|
||||
{isUpdating ? (
|
||||
<Spinner className="h-3" />
|
||||
) : (
|
||||
<MinusCircleIcon className={'h-3'} />
|
||||
)}
|
||||
{isUpdating ? <Spinner /> : <MinusCircleIcon />}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<PlayButton links={mediaLinks} />
|
||||
<div className="z-20">
|
||||
<PlayButton links={mediaLinks} />
|
||||
</div>
|
||||
<RequestButton
|
||||
mediaType="movie"
|
||||
media={data.mediaInfo}
|
||||
|
||||
@@ -17,9 +17,10 @@ import {
|
||||
TrashIcon,
|
||||
XMarkIcon,
|
||||
} from '@heroicons/react/24/solid';
|
||||
import { MediaRequestStatus } from '@server/constants/media';
|
||||
import { MediaRequestStatus, MediaType } from '@server/constants/media';
|
||||
import type { MediaRequest } from '@server/entity/MediaRequest';
|
||||
import type { NonFunctionProperties } from '@server/interfaces/api/common';
|
||||
import type { RadarrSettings, SonarrSettings } from '@server/lib/settings';
|
||||
import type { MovieDetails } from '@server/models/Movie';
|
||||
import type { TvDetails } from '@server/models/Tv';
|
||||
import Link from 'next/link';
|
||||
@@ -293,9 +294,16 @@ const RequestItemError = ({
|
||||
interface RequestItemProps {
|
||||
request: NonFunctionProperties<MediaRequest> & { profileName?: string };
|
||||
revalidateList: () => void;
|
||||
radarrData?: RadarrSettings[];
|
||||
sonarrData?: SonarrSettings[];
|
||||
}
|
||||
|
||||
const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
|
||||
const RequestItem = ({
|
||||
request,
|
||||
revalidateList,
|
||||
radarrData,
|
||||
sonarrData,
|
||||
}: RequestItemProps) => {
|
||||
const settings = useSettings();
|
||||
const { ref, inView } = useInView({
|
||||
triggerOnce: true,
|
||||
@@ -390,6 +398,23 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
|
||||
iOSPlexUrl4k: requestData?.media?.iOSPlexUrl4k,
|
||||
});
|
||||
|
||||
const serviceExists = () => {
|
||||
if (title?.mediaInfo) {
|
||||
if (title?.mediaInfo.mediaType === MediaType.MOVIE) {
|
||||
return (
|
||||
radarrData?.find((radarr) => radarr.id === request.serverId) !==
|
||||
undefined
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
sonarrData?.find((sonarr) => sonarr.id === request.serverId) !==
|
||||
undefined
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!title && !error) {
|
||||
return (
|
||||
<div
|
||||
@@ -697,28 +722,30 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
|
||||
)}
|
||||
{requestData.status !== MediaRequestStatus.PENDING &&
|
||||
hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||
<>
|
||||
<ConfirmButton
|
||||
onClick={() => deleteRequest()}
|
||||
confirmText={intl.formatMessage(globalMessages.areyousure)}
|
||||
className="w-full"
|
||||
>
|
||||
<TrashIcon />
|
||||
<span>{intl.formatMessage(messages.deleterequest)}</span>
|
||||
</ConfirmButton>
|
||||
<ConfirmButton
|
||||
onClick={() => deleteMediaFile()}
|
||||
confirmText={intl.formatMessage(globalMessages.areyousure)}
|
||||
className="w-full"
|
||||
>
|
||||
<TrashIcon />
|
||||
<span>
|
||||
{intl.formatMessage(messages.removearr, {
|
||||
arr: request.type === 'movie' ? 'Radarr' : 'Sonarr',
|
||||
})}
|
||||
</span>
|
||||
</ConfirmButton>
|
||||
</>
|
||||
<ConfirmButton
|
||||
onClick={() => deleteRequest()}
|
||||
confirmText={intl.formatMessage(globalMessages.areyousure)}
|
||||
className="w-full"
|
||||
>
|
||||
<TrashIcon />
|
||||
<span>{intl.formatMessage(messages.deleterequest)}</span>
|
||||
</ConfirmButton>
|
||||
)}
|
||||
{hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||
title?.mediaInfo?.serviceId &&
|
||||
serviceExists() && (
|
||||
<ConfirmButton
|
||||
onClick={() => deleteMediaFile()}
|
||||
confirmText={intl.formatMessage(globalMessages.areyousure)}
|
||||
className="w-full"
|
||||
>
|
||||
<TrashIcon />
|
||||
<span>
|
||||
{intl.formatMessage(messages.removearr, {
|
||||
arr: request.type === 'movie' ? 'Radarr' : 'Sonarr',
|
||||
})}
|
||||
</span>
|
||||
</ConfirmButton>
|
||||
)}
|
||||
{requestData.status === MediaRequestStatus.PENDING &&
|
||||
hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
FunnelIcon,
|
||||
} from '@heroicons/react/24/solid';
|
||||
import type { RequestResultsResponse } from '@server/interfaces/api/requestInterfaces';
|
||||
import { Permission } from '@server/lib/permissions';
|
||||
import type { RadarrSettings, SonarrSettings } from '@server/lib/settings';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -51,7 +53,7 @@ const RequestList = () => {
|
||||
const { user } = useUser({
|
||||
id: Number(router.query.userId),
|
||||
});
|
||||
const { user: currentUser } = useUser();
|
||||
const { user: currentUser, hasPermission } = useUser();
|
||||
const [currentFilter, setCurrentFilter] = useState<Filter>(Filter.PENDING);
|
||||
const [currentSort, setCurrentSort] = useState<Sort>('added');
|
||||
const [currentSortDirection, setCurrentSortDirection] =
|
||||
@@ -62,6 +64,13 @@ const RequestList = () => {
|
||||
const pageIndex = page - 1;
|
||||
const updateQueryParams = useUpdateQueryParams({ page: page.toString() });
|
||||
|
||||
const { data: radarrData } = useSWR<RadarrSettings[]>(
|
||||
hasPermission(Permission.ADMIN) ? '/api/v1/settings/radarr' : null
|
||||
);
|
||||
const { data: sonarrData } = useSWR<SonarrSettings[]>(
|
||||
hasPermission(Permission.ADMIN) ? '/api/v1/settings/sonarr' : null
|
||||
);
|
||||
|
||||
const {
|
||||
data,
|
||||
error,
|
||||
@@ -245,6 +254,8 @@ const RequestList = () => {
|
||||
<RequestItem
|
||||
request={request}
|
||||
revalidateList={() => revalidate()}
|
||||
radarrData={radarrData}
|
||||
sonarrData={sonarrData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -221,6 +221,7 @@ const NotificationsEmail = () => {
|
||||
requireTls: values.encryption === 'opportunistic',
|
||||
authUser: values.authUser,
|
||||
authPass: values.authPass,
|
||||
allowSelfSigned: values.allowSelfSigned,
|
||||
senderName: values.senderName,
|
||||
pgpPrivateKey: values.pgpPrivateKey,
|
||||
pgpPassword: values.pgpPassword,
|
||||
|
||||
@@ -373,11 +373,10 @@ const TitleCard = ({
|
||||
: intl.formatMessage(globalMessages.tvshow)}
|
||||
</div>
|
||||
</div>
|
||||
{showDetail &&
|
||||
currentStatus !== MediaStatus.BLACKLISTED &&
|
||||
user?.userType !== UserType.PLEX && (
|
||||
<div className="flex flex-col gap-1">
|
||||
{toggleWatchlist ? (
|
||||
{showDetail && currentStatus !== MediaStatus.BLACKLISTED && (
|
||||
<div className="flex flex-col gap-1">
|
||||
{user?.userType !== UserType.PLEX &&
|
||||
(toggleWatchlist ? (
|
||||
<Button
|
||||
buttonType={'ghost'}
|
||||
className="z-40"
|
||||
@@ -394,23 +393,23 @@ const TitleCard = ({
|
||||
>
|
||||
<MinusCircleIcon className={'h-3'} />
|
||||
</Button>
|
||||
))}
|
||||
{showHideButton &&
|
||||
currentStatus !== MediaStatus.PROCESSING &&
|
||||
currentStatus !== MediaStatus.AVAILABLE &&
|
||||
currentStatus !== MediaStatus.PARTIALLY_AVAILABLE &&
|
||||
currentStatus !== MediaStatus.PENDING && (
|
||||
<Button
|
||||
buttonType={'ghost'}
|
||||
className="z-40"
|
||||
buttonSize={'sm'}
|
||||
onClick={() => setShowBlacklistModal(true)}
|
||||
>
|
||||
<EyeSlashIcon className={'h-3'} />
|
||||
</Button>
|
||||
)}
|
||||
{showHideButton &&
|
||||
currentStatus !== MediaStatus.PROCESSING &&
|
||||
currentStatus !== MediaStatus.AVAILABLE &&
|
||||
currentStatus !== MediaStatus.PARTIALLY_AVAILABLE &&
|
||||
currentStatus !== MediaStatus.PENDING && (
|
||||
<Button
|
||||
buttonType={'ghost'}
|
||||
className="z-40"
|
||||
buttonSize={'sm'}
|
||||
onClick={() => setShowBlacklistModal(true)}
|
||||
>
|
||||
<EyeSlashIcon className={'h-3'} />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{showDetail &&
|
||||
showHideButton &&
|
||||
currentStatus == MediaStatus.BLACKLISTED && (
|
||||
|
||||
@@ -632,7 +632,7 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
||||
buttonSize={'md'}
|
||||
onClick={() => setShowBlacklistModal(true)}
|
||||
>
|
||||
<EyeSlashIcon className={'h-3'} />
|
||||
<EyeSlashIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
@@ -650,9 +650,9 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
||||
onClick={onClickWatchlistBtn}
|
||||
>
|
||||
{isUpdating ? (
|
||||
<Spinner className="h-3" />
|
||||
<Spinner />
|
||||
) : (
|
||||
<StarIcon className={'h-3 text-amber-300'} />
|
||||
<StarIcon className={'text-amber-300'} />
|
||||
)}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
@@ -665,17 +665,15 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
||||
buttonSize={'md'}
|
||||
onClick={onClickDeleteWatchlistBtn}
|
||||
>
|
||||
{isUpdating ? (
|
||||
<Spinner className="h-3" />
|
||||
) : (
|
||||
<MinusCircleIcon className={'h-3'} />
|
||||
)}
|
||||
{isUpdating ? <Spinner /> : <MinusCircleIcon />}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<PlayButton links={mediaLinks} />
|
||||
<div className="z-20">
|
||||
<PlayButton links={mediaLinks} />
|
||||
</div>
|
||||
<RequestButton
|
||||
mediaType="tv"
|
||||
onUpdate={() => revalidate()}
|
||||
|
||||
@@ -415,7 +415,7 @@ const UserGeneralSettings = () => {
|
||||
</span>
|
||||
</label>
|
||||
<div className="form-input-area">
|
||||
<div className="form-input-field">
|
||||
<div className="form-input-field relative z-30">
|
||||
<RegionSelector
|
||||
name="discoverRegion"
|
||||
value={values.discoverRegion ?? ''}
|
||||
@@ -451,7 +451,7 @@ const UserGeneralSettings = () => {
|
||||
</span>
|
||||
</label>
|
||||
<div className="form-input-area">
|
||||
<div className="form-input-field">
|
||||
<div className="form-input-field relative z-20">
|
||||
<RegionSelector
|
||||
name="streamingRegion"
|
||||
value={values.streamingRegion || ''}
|
||||
|
||||
Reference in New Issue
Block a user