Compare commits

..

1 Commits

Author SHA1 Message Date
gauthier-th
ebb36b4c6f fix(proxy): update http proxy to accept bypass list with undici v7
With the update of undici to v7, the bypass list of addresses (no_proxy addresses) was not ignored
anymore.

fix #1454
2025-03-11 22:33:53 +01:00
20 changed files with 93 additions and 213 deletions

View File

@@ -58,27 +58,12 @@ All help is welcome and greatly appreciated! If you would like to contribute to
- Be sure to follow both the [code](#contributing-code) and [UI text](#ui-text-style) guidelines.
- Should you need to update your fork, you can do so by rebasing from `upstream`:
```bash
git fetch upstream
git rebase upstream/develop
git push origin BRANCH_NAME -f
```
### Helm Chart
Tools Required:
- [Helm](https://helm.sh/docs/intro/install/)
- [helm-docs](https://github.com/norwoodj/helm-docs)
Steps:
1. Make the necessary changes.
2. Test your changes.
3. Update the `version` in `charts/jellyseerr-chart/Chart.yaml` following [Semantic Versioning (SemVer)](https://semver.org/).
4. Run the `helm-docs` command to regenerate the chart's README.
### Contributing Code
- If you are taking on an existing bug or feature ticket, please comment on the [issue](https://github.com/fallenbagel/jellyseerr/issues) to avoid multiple people working on the same thing.

View File

@@ -3,8 +3,8 @@ kubeVersion: ">=1.23.0-0"
name: jellyseerr-chart
description: Jellyseerr helm chart for Kubernetes
type: application
version: 2.3.2
appVersion: "2.5.1"
version: 2.2.0
appVersion: "2.4.0"
maintainers:
- name: Jellyseerr
url: https://github.com/Fallenbagel/jellyseerr

View File

@@ -1,6 +1,6 @@
# jellyseerr-chart
![Version: 2.3.2](https://img.shields.io/badge/Version-2.3.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2.5.1](https://img.shields.io/badge/AppVersion-2.5.1-informational?style=flat-square)
![Version: 2.2.0](https://img.shields.io/badge/Version-2.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2.4.0](https://img.shields.io/badge/AppVersion-2.4.0-informational?style=flat-square)
Jellyseerr helm chart for Kubernetes

View File

@@ -4,10 +4,6 @@ metadata:
name: {{ include "jellyseerr.configPersistenceName" . }}
labels:
{{- include "jellyseerr.labels" . | nindent 4 }}
{{- with .Values.config.persistence.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- with .Values.config.persistence.accessModes }}
accessModes:

View File

@@ -24,12 +24,6 @@ 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>
@@ -51,16 +45,6 @@ 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>
@@ -72,7 +56,7 @@ You can try them all and see which one works for your network.
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 or `9.9.9.9` for Quad9'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.
</TabItem>
@@ -89,10 +73,6 @@ You can try them all and see which one works for your network.
```bash
nameserver 1.1.1.1
```
or for Quad9's DNS:
```bash
nameserver 9.9.9.9
```
</TabItem>
</Tabs>
@@ -101,7 +81,7 @@ You can try them all and see which one works for your network.
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 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`:
You can try to force the resolution to use IPV4 first by setting the `FORCE_IPV4_FIRST` environment variable to `true`:
<Tabs groupId="methods" queryString>
<TabItem value="docker-cli" label="Docker CLI">

View File

@@ -999,7 +999,7 @@ export class MediaRequest {
radarrMovie.id,
[this.is4k ? 'externalServiceSlug4k' : 'externalServiceSlug']:
radarrMovie.titleSlug,
[this.is4k ? 'serviceId4k' : 'serviceId']: radarrSettings?.id,
[this.is4k ? 'serviceId4k' : 'serviceId']: radarrMovie?.id,
};
await mediaRepository.update({ id: this.media.id }, updateFields);

View File

@@ -404,34 +404,6 @@ class AvailabilitySync {
});
}
if (
!showExists &&
(media.status === MediaStatus.AVAILABLE ||
media.status === MediaStatus.PARTIALLY_AVAILABLE ||
media.seasons.some(
(season) => season.status === MediaStatus.AVAILABLE
) ||
media.seasons.some(
(season) => season.status === MediaStatus.PARTIALLY_AVAILABLE
))
) {
await this.mediaUpdater(media, false, mediaServerType);
}
if (
!showExists4k &&
(media.status4k === MediaStatus.AVAILABLE ||
media.status4k === MediaStatus.PARTIALLY_AVAILABLE ||
media.seasons.some(
(season) => season.status4k === MediaStatus.AVAILABLE
) ||
media.seasons.some(
(season) => season.status4k === MediaStatus.PARTIALLY_AVAILABLE
))
) {
await this.mediaUpdater(media, true, mediaServerType);
}
// TODO: Figure out how to run seasonUpdater for each season
if ([...finalSeasons.values()].includes(false)) {
@@ -451,6 +423,22 @@ class AvailabilitySync {
mediaServerType
);
}
if (
!showExists &&
(media.status === MediaStatus.AVAILABLE ||
media.status === MediaStatus.PARTIALLY_AVAILABLE)
) {
await this.mediaUpdater(media, false, mediaServerType);
}
if (
!showExists4k &&
(media.status4k === MediaStatus.AVAILABLE ||
media.status4k === MediaStatus.PARTIALLY_AVAILABLE)
) {
await this.mediaUpdater(media, true, mediaServerType);
}
}
}
} catch (ex) {
@@ -478,10 +466,6 @@ class AvailabilitySync {
{ status: MediaStatus.PARTIALLY_AVAILABLE },
{ status4k: MediaStatus.AVAILABLE },
{ status4k: MediaStatus.PARTIALLY_AVAILABLE },
{ seasons: { status: MediaStatus.AVAILABLE } },
{ seasons: { status: MediaStatus.PARTIALLY_AVAILABLE } },
{ seasons: { status4k: MediaStatus.AVAILABLE } },
{ seasons: { status4k: MediaStatus.PARTIALLY_AVAILABLE } },
];
let mediaPage: Media[];

View File

@@ -47,11 +47,10 @@ class GotifyAgent
const title = payload.event
? `${payload.event} - ${payload.subject}`
: payload.subject;
let message = payload.message ? `${payload.message} \n\n` : '';
let message = payload.message ?? '';
if (payload.request) {
message += `\n**Requested By:** ${payload.request.requestedBy.displayName} `;
message += `\n\nRequested By: ${payload.request.requestedBy.displayName}`;
let status = '';
switch (type) {
@@ -74,18 +73,16 @@ class GotifyAgent
}
if (status) {
message += `\n**Request Status:** ${status} `;
message += `\nRequest Status: ${status}`;
}
} else if (payload.comment) {
message += `\nComment from ${payload.comment.user.displayName}:\n${payload.comment.message} `;
message += `\nComment from ${payload.comment.user.displayName}:\n${payload.comment.message}`;
} else if (payload.issue) {
message += `\n\n**Reported By:** ${payload.issue.createdBy.displayName} `;
message += `\n**Issue Type:** ${
IssueTypeName[payload.issue.issueType]
} `;
message += `\n**Issue Status:** ${
message += `\n\nReported By: ${payload.issue.createdBy.displayName}`;
message += `\nIssue Type: ${IssueTypeName[payload.issue.issueType]}`;
message += `\nIssue Status: ${
payload.issue.status === IssueStatus.OPEN ? 'Open' : 'Resolved'
} `;
}`;
if (type == Notification.ISSUE_CREATED) {
priority = 1;
@@ -93,14 +90,12 @@ class GotifyAgent
}
for (const extra of payload.extra ?? []) {
message += `\n\n**${extra.name}**\n${extra.value} `;
message += `\n\n**${extra.name}**\n${extra.value}`;
}
if (applicationUrl && payload.media) {
const actionUrl = `${applicationUrl}/${payload.media.mediaType}/${payload.media.tmdbId}`;
const displayUrl =
actionUrl.length > 40 ? `${actionUrl.slice(0, 41)}...` : actionUrl;
message += `\n\n**Open in ${applicationTitle}:** [${displayUrl}](${actionUrl}) `;
message += `\n\nOpen in ${applicationTitle}(${actionUrl})`;
}
return {

View File

@@ -237,19 +237,6 @@ 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
@@ -264,13 +251,6 @@ 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);
}

View File

@@ -14,13 +14,17 @@ 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())
@@ -28,10 +32,6 @@ 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: 'UTC',
timeZone,
})}
</Badge>
{showRelative && (
@@ -49,9 +49,9 @@ const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
{
relativeTime: (
<FormattedRelativeTime
value={diffInDays}
unit="day"
value={(dAirDate.getTime() - Date.now()) / 1000}
numeric="auto"
updateIntervalInSeconds={1}
/>
),
}

View File

@@ -161,6 +161,7 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
data-form-type="password"
data-1pignore="false"
data-lpignore="false"
data-bwignore="false"
/>
</div>
<div className="flex">

View File

@@ -118,6 +118,7 @@ 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">

View File

@@ -629,9 +629,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
)}
</>
)}
<div className="z-20">
<PlayButton links={mediaLinks} />
</div>
<PlayButton links={mediaLinks} />
<RequestButton
mediaType="movie"
media={data.mediaInfo}

View File

@@ -17,10 +17,9 @@ import {
TrashIcon,
XMarkIcon,
} from '@heroicons/react/24/solid';
import { MediaRequestStatus, MediaType } from '@server/constants/media';
import { MediaRequestStatus } 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';
@@ -294,16 +293,9 @@ const RequestItemError = ({
interface RequestItemProps {
request: NonFunctionProperties<MediaRequest> & { profileName?: string };
revalidateList: () => void;
radarrData?: RadarrSettings[];
sonarrData?: SonarrSettings[];
}
const RequestItem = ({
request,
revalidateList,
radarrData,
sonarrData,
}: RequestItemProps) => {
const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
const settings = useSettings();
const { ref, inView } = useInView({
triggerOnce: true,
@@ -398,23 +390,6 @@ const RequestItem = ({
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
@@ -722,30 +697,28 @@ const RequestItem = ({
)}
{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>
)}
{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>
<>
<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>
</>
)}
{requestData.status === MediaRequestStatus.PENDING &&
hasPermission(Permission.MANAGE_REQUESTS) && (

View File

@@ -17,8 +17,6 @@ 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';
@@ -53,7 +51,7 @@ const RequestList = () => {
const { user } = useUser({
id: Number(router.query.userId),
});
const { user: currentUser, hasPermission } = useUser();
const { user: currentUser } = useUser();
const [currentFilter, setCurrentFilter] = useState<Filter>(Filter.PENDING);
const [currentSort, setCurrentSort] = useState<Sort>('added');
const [currentSortDirection, setCurrentSortDirection] =
@@ -64,13 +62,6 @@ 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,
@@ -254,8 +245,6 @@ const RequestList = () => {
<RequestItem
request={request}
revalidateList={() => revalidate()}
radarrData={radarrData}
sonarrData={sonarrData}
/>
</div>
);

View File

@@ -217,7 +217,7 @@ const TvRequestModal = ({
mediaType: 'tv',
is4k,
seasons: settings.currentSettings.partialRequestsEnabled
? selectedSeasons.sort((a, b) => a - b)
? selectedSeasons
: getAllSeasons().filter(
(season) => !getAllRequestedSeasons().includes(season)
),

View File

@@ -221,7 +221,6 @@ const NotificationsEmail = () => {
requireTls: values.encryption === 'opportunistic',
authUser: values.authUser,
authPass: values.authPass,
allowSelfSigned: values.allowSelfSigned,
senderName: values.senderName,
pgpPrivateKey: values.pgpPrivateKey,
pgpPassword: values.pgpPassword,

View File

@@ -373,10 +373,11 @@ const TitleCard = ({
: intl.formatMessage(globalMessages.tvshow)}
</div>
</div>
{showDetail && currentStatus !== MediaStatus.BLACKLISTED && (
<div className="flex flex-col gap-1">
{user?.userType !== UserType.PLEX &&
(toggleWatchlist ? (
{showDetail &&
currentStatus !== MediaStatus.BLACKLISTED &&
user?.userType !== UserType.PLEX && (
<div className="flex flex-col gap-1">
{toggleWatchlist ? (
<Button
buttonType={'ghost'}
className="z-40"
@@ -393,23 +394,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>
)}
</div>
)}
{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>
)}
{showDetail &&
showHideButton &&
currentStatus == MediaStatus.BLACKLISTED && (

View File

@@ -671,9 +671,7 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
)}
</>
)}
<div className="z-20">
<PlayButton links={mediaLinks} />
</div>
<PlayButton links={mediaLinks} />
<RequestButton
mediaType="tv"
onUpdate={() => revalidate()}

View File

@@ -415,7 +415,7 @@ const UserGeneralSettings = () => {
</span>
</label>
<div className="form-input-area">
<div className="form-input-field relative z-[22]">
<div className="form-input-field">
<RegionSelector
name="discoverRegion"
value={values.discoverRegion ?? ''}
@@ -433,7 +433,7 @@ const UserGeneralSettings = () => {
</span>
</label>
<div className="form-input-area">
<div className="form-input-field relative z-[21]">
<div className="form-input-field">
<LanguageSelector
setFieldValue={setFieldValue}
serverValue={currentSettings.originalLanguage}
@@ -451,7 +451,7 @@ const UserGeneralSettings = () => {
</span>
</label>
<div className="form-input-area">
<div className="form-input-field relative z-20">
<div className="form-input-field">
<RegionSelector
name="streamingRegion"
value={values.streamingRegion || ''}