Compare commits

..

1 Commits

Author SHA1 Message Date
Gauthier
d9b2d3ccf1 feat(api): add DNS caching
fix #387 #657 #728
2024-06-01 11:30:22 +02:00
7 changed files with 85 additions and 123 deletions

View File

@@ -35,60 +35,60 @@ jobs:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: npx semantic-release run: npx semantic-release
# build-snap: build-snap:
# name: Build Snap Package (${{ matrix.architecture }}) name: Build Snap Package (${{ matrix.architecture }})
# needs: semantic-release needs: semantic-release
# runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
# strategy: strategy:
# fail-fast: false fail-fast: false
# matrix: matrix:
# architecture: architecture:
# - amd64 - amd64
# - arm64 - arm64
# - armhf - armhf
# steps: steps:
# - name: Checkout Code - name: Checkout Code
# uses: actions/checkout@v4 uses: actions/checkout@v4
# with: with:
# fetch-depth: 0 fetch-depth: 0
# - name: Switch to main branch - name: Switch to main branch
# run: git checkout main run: git checkout main
# - name: Pull latest changes - name: Pull latest changes
# run: git pull run: git pull
# - name: Prepare - name: Prepare
# id: prepare id: prepare
# run: | run: |
# git fetch --prune --tags git fetch --prune --tags
# if [[ $GITHUB_REF == refs/tags/* || $GITHUB_REF == refs/heads/master ]]; then if [[ $GITHUB_REF == refs/tags/* || $GITHUB_REF == refs/heads/master ]]; then
# echo "RELEASE=stable" >> $GITHUB_OUTPUT echo "RELEASE=stable" >> $GITHUB_OUTPUT
# else else
# echo "RELEASE=edge" >> $GITHUB_OUTPUT echo "RELEASE=edge" >> $GITHUB_OUTPUT
# fi fi
# - name: Set Up QEMU - name: Set Up QEMU
# uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
# with: with:
# image: tonistiigi/binfmt@sha256:df15403e06a03c2f461c1f7938b171fda34a5849eb63a70e2a2109ed5a778bde image: tonistiigi/binfmt@sha256:df15403e06a03c2f461c1f7938b171fda34a5849eb63a70e2a2109ed5a778bde
# - name: Build Snap Package - name: Build Snap Package
# uses: diddlesnaps/snapcraft-multiarch-action@v1 uses: diddlesnaps/snapcraft-multiarch-action@v1
# id: build id: build
# with: with:
# architecture: ${{ matrix.architecture }} architecture: ${{ matrix.architecture }}
# - name: Upload Snap Package - name: Upload Snap Package
# uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
# with: with:
# name: jellyseerr-snap-package-${{ matrix.architecture }} name: jellyseerr-snap-package-${{ matrix.architecture }}
# path: ${{ steps.build.outputs.snap }} path: ${{ steps.build.outputs.snap }}
# - name: Review Snap Package - name: Review Snap Package
# uses: diddlesnaps/snapcraft-review-tools-action@v1 uses: diddlesnaps/snapcraft-review-tools-action@v1
# with: with:
# snap: ${{ steps.build.outputs.snap }} snap: ${{ steps.build.outputs.snap }}
# - name: Publish Snap Package - name: Publish Snap Package
# uses: snapcore/action-publish@v1 uses: snapcore/action-publish@v1
# env: env:
# SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_LOGIN }} SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_LOGIN }}
# with: with:
# snap: ${{ steps.build.outputs.snap }} snap: ${{ steps.build.outputs.snap }}
# release: ${{ steps.prepare.outputs.RELEASE }} release: ${{ steps.prepare.outputs.RELEASE }}
discord: discord:
name: Send Discord Notification name: Send Discord Notification

View File

@@ -133,11 +133,6 @@ class JellyfinAPI extends ExternalAPI {
} }
: {}; : {};
logger.debug(`Logging in to Jellyfin server: ${this.jellyfinHost}`, {
label: 'Jellyfin API',
clientIp: ClientIP,
});
const authResponse = await this.post<JellyfinLoginResponse>( const authResponse = await this.post<JellyfinLoginResponse>(
'/Users/AuthenticateByName', '/Users/AuthenticateByName',
{ {
@@ -151,12 +146,6 @@ class JellyfinAPI extends ExternalAPI {
return authResponse; return authResponse;
} catch (e) { } catch (e) {
logger.error('Failed to login to Jellyfin server', {
label: 'Jellyfin API',
clientIp: ClientIP,
error: e,
});
const status = e.response?.status; const status = e.response?.status;
const networkErrorCodes = new Set([ const networkErrorCodes = new Set([

View File

@@ -83,17 +83,13 @@ class JellyfinScanner {
} }
const has4k = metadata.MediaSources?.some((MediaSource) => { const has4k = metadata.MediaSources?.some((MediaSource) => {
return MediaSource.MediaStreams.filter( return MediaSource.MediaStreams.some((MediaStream) => {
(MediaStream) => MediaStream.Type === 'Video'
).some((MediaStream) => {
return (MediaStream.Width ?? 0) > 2000; return (MediaStream.Width ?? 0) > 2000;
}); });
}); });
const hasOtherResolution = metadata.MediaSources?.some((MediaSource) => { const hasOtherResolution = metadata.MediaSources?.some((MediaSource) => {
return MediaSource.MediaStreams.filter( return MediaSource.MediaStreams.some((MediaStream) => {
(MediaStream) => MediaStream.Type === 'Video'
).some((MediaStream) => {
return (MediaStream.Width ?? 0) <= 2000; return (MediaStream.Width ?? 0) <= 2000;
}); });
}); });

View File

@@ -14,7 +14,6 @@ import { ApiError } from '@server/types/error';
import * as EmailValidator from 'email-validator'; import * as EmailValidator from 'email-validator';
import { Router } from 'express'; import { Router } from 'express';
import gravatarUrl from 'gravatar-url'; import gravatarUrl from 'gravatar-url';
import net from 'net';
const authRoutes = Router(); const authRoutes = Router();
@@ -272,21 +271,11 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
? jellyfinHost.slice(0, -1) ? jellyfinHost.slice(0, -1)
: jellyfinHost; : jellyfinHost;
const ip = req.ip; const ip = req.ip ? req.ip.split(':').reverse()[0] : undefined;
let clientIp;
if (ip) {
if (net.isIPv4(ip)) {
clientIp = ip;
} else if (net.isIPv6(ip)) {
clientIp = ip.startsWith('::ffff:') ? ip.substring(7) : ip;
}
}
const account = await jellyfinserver.login( const account = await jellyfinserver.login(
body.username, body.username,
body.password, body.password,
clientIp ip
); );
// Next let's see if the user already exists // Next let's see if the user already exists

View File

@@ -434,38 +434,33 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
</Button> </Button>
</Tooltip> </Tooltip>
)} )}
{hasPermission(Permission.MANAGE_REQUESTS) && {hasPermission(Permission.MANAGE_REQUESTS) && data.mediaInfo && (
data.mediaInfo && <Tooltip content={intl.formatMessage(messages.managemovie)}>
(data.mediaInfo.jellyfinMediaId || <Button
data.mediaInfo.jellyfinMediaId4k || buttonType="ghost"
data.mediaInfo.status !== MediaStatus.UNKNOWN || onClick={() => setShowManager(true)}
data.mediaInfo.status4k !== MediaStatus.UNKNOWN) && ( className="relative ml-2 first:ml-0"
<Tooltip content={intl.formatMessage(messages.managemovie)}> >
<Button <CogIcon className="!mr-0" />
buttonType="ghost" {hasPermission(
onClick={() => setShowManager(true)} [Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES],
className="relative ml-2 first:ml-0" {
> type: 'or',
<CogIcon className="!mr-0" /> }
{hasPermission( ) &&
[Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES], (
{ data.mediaInfo?.issues.filter(
type: 'or', (issue) => issue.status === IssueStatus.OPEN
} ) ?? []
) && ).length > 0 && (
( <>
data.mediaInfo?.issues.filter( <div className="absolute -right-1 -top-1 h-3 w-3 rounded-full bg-red-600" />
(issue) => issue.status === IssueStatus.OPEN <div className="absolute -right-1 -top-1 h-3 w-3 animate-ping rounded-full bg-red-600" />
) ?? [] </>
).length > 0 && ( )}
<> </Button>
<div className="absolute -right-1 -top-1 h-3 w-3 rounded-full bg-red-600" /> </Tooltip>
<div className="absolute -right-1 -top-1 h-3 w-3 animate-ping rounded-full bg-red-600" /> )}
</>
)}
</Button>
</Tooltip>
)}
</div> </div>
</div> </div>
<div className="media-overview"> <div className="media-overview">

View File

@@ -53,8 +53,6 @@ const messages = defineMessages({
discordId: 'Discord User ID', discordId: 'Discord User ID',
discordIdTip: discordIdTip:
'The <FindDiscordIdLink>multi-digit ID number</FindDiscordIdLink> associated with your Discord user account', 'The <FindDiscordIdLink>multi-digit ID number</FindDiscordIdLink> associated with your Discord user account',
validationemailrequired: 'Email required',
validationemailformat: 'Valid email required',
validationDiscordId: 'You must provide a valid Discord user ID', validationDiscordId: 'You must provide a valid Discord user ID',
plexwatchlistsyncmovies: 'Auto-Request Movies', plexwatchlistsyncmovies: 'Auto-Request Movies',
plexwatchlistsyncmoviestip: plexwatchlistsyncmoviestip:
@@ -90,9 +88,6 @@ const UserGeneralSettings = () => {
); );
const UserGeneralSettingsSchema = Yup.object().shape({ const UserGeneralSettingsSchema = Yup.object().shape({
email: Yup.string()
.email(intl.formatMessage(messages.validationemailformat))
.required(intl.formatMessage(messages.validationemailrequired)),
discordId: Yup.string() discordId: Yup.string()
.nullable() .nullable()
.matches(/^\d{17,19}$/, intl.formatMessage(messages.validationDiscordId)), .matches(/^\d{17,19}$/, intl.formatMessage(messages.validationDiscordId)),

View File

@@ -1177,8 +1177,6 @@
"components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Settings saved successfully!", "components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Settings saved successfully!",
"components.UserProfile.UserSettings.UserGeneralSettings.user": "User", "components.UserProfile.UserSettings.UserGeneralSettings.user": "User",
"components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "You must provide a valid Discord user ID", "components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "You must provide a valid Discord user ID",
"components.UserProfile.UserSettings.UserGeneralSettings.validationemailformat": "Valid email required",
"components.UserProfile.UserSettings.UserGeneralSettings.validationemailrequired": "Email required",
"components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Device Default", "components.UserProfile.UserSettings.UserNotificationSettings.deviceDefault": "Device Default",
"components.UserProfile.UserSettings.UserNotificationSettings.discordId": "User ID", "components.UserProfile.UserSettings.UserNotificationSettings.discordId": "User ID",
"components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "The <FindDiscordIdLink>multi-digit ID number</FindDiscordIdLink> associated with your user account", "components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "The <FindDiscordIdLink>multi-digit ID number</FindDiscordIdLink> associated with your user account",