diff --git a/package.json b/package.json
index a91df2b77..6a706eb42 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
"axios-rate-limit": "1.3.0",
"bcrypt": "5.1.0",
"bowser": "2.11.0",
+ "cacheable-lookup": "^7.0.0",
"connect-typeorm": "1.1.4",
"cookie-parser": "1.4.6",
"copy-to-clipboard": "3.3.3",
diff --git a/server/index.ts b/server/index.ts
index 477864c26..b62080778 100644
--- a/server/index.ts
+++ b/server/index.ts
@@ -23,6 +23,7 @@ import imageproxy from '@server/routes/imageproxy';
import { getAppVersion } from '@server/utils/appVersion';
import restartFlag from '@server/utils/restartFlag';
import { getClientIp } from '@supercharge/request-ip';
+import type CacheableLookupType from 'cacheable-lookup';
import { TypeormStore } from 'connect-typeorm/out';
import cookieParser from 'cookie-parser';
import csurf from 'csurf';
@@ -32,10 +33,14 @@ import * as OpenApiValidator from 'express-openapi-validator';
import type { Store } from 'express-session';
import session from 'express-session';
import next from 'next';
+import http from 'node:http';
+import https from 'node:https';
import path from 'path';
import swaggerUi from 'swagger-ui-express';
import YAML from 'yamljs';
+const _importDynamic = new Function('modulePath', 'return import(modulePath)');
+
const API_SPEC_PATH = path.join(__dirname, '../overseerr-api.yml');
logger.info(`Starting Overseerr version ${getAppVersion()}`);
@@ -46,6 +51,12 @@ const handle = app.getRequestHandler();
app
.prepare()
.then(async () => {
+ const CacheableLookup = (await _importDynamic('cacheable-lookup'))
+ .default as typeof CacheableLookupType;
+ const cacheable = new CacheableLookup();
+ cacheable.install(http.globalAgent);
+ cacheable.install(https.globalAgent);
+
const dbConnection = await dataSource.initialize();
// Run migrations in production
diff --git a/server/lib/scanners/jellyfin/index.ts b/server/lib/scanners/jellyfin/index.ts
index f5b0f66a2..8007e6ef3 100644
--- a/server/lib/scanners/jellyfin/index.ts
+++ b/server/lib/scanners/jellyfin/index.ts
@@ -83,13 +83,17 @@ class JellyfinScanner {
}
const has4k = metadata.MediaSources?.some((MediaSource) => {
- return MediaSource.MediaStreams.some((MediaStream) => {
+ return MediaSource.MediaStreams.filter(
+ (MediaStream) => MediaStream.Type === 'Video'
+ ).some((MediaStream) => {
return (MediaStream.Width ?? 0) > 2000;
});
});
const hasOtherResolution = metadata.MediaSources?.some((MediaSource) => {
- return MediaSource.MediaStreams.some((MediaStream) => {
+ return MediaSource.MediaStreams.filter(
+ (MediaStream) => MediaStream.Type === 'Video'
+ ).some((MediaStream) => {
return (MediaStream.Width ?? 0) <= 2000;
});
});
diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx
index b7dc59172..4ed69b6b6 100644
--- a/src/components/MovieDetails/index.tsx
+++ b/src/components/MovieDetails/index.tsx
@@ -434,33 +434,38 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
)}
- {hasPermission(Permission.MANAGE_REQUESTS) && data.mediaInfo && (
-
-
-
- )}
+ {hasPermission(Permission.MANAGE_REQUESTS) &&
+ data.mediaInfo &&
+ (data.mediaInfo.jellyfinMediaId ||
+ data.mediaInfo.jellyfinMediaId4k ||
+ data.mediaInfo.status !== MediaStatus.UNKNOWN ||
+ data.mediaInfo.status4k !== MediaStatus.UNKNOWN) && (
+
+
+
+ )}
diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx
index 960746adf..b6371e7d4 100644
--- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx
+++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx
@@ -53,6 +53,8 @@ const messages = defineMessages({
discordId: 'Discord User ID',
discordIdTip:
'The multi-digit ID number associated with your Discord user account',
+ validationemailrequired: 'Email required',
+ validationemailformat: 'Valid email required',
validationDiscordId: 'You must provide a valid Discord user ID',
plexwatchlistsyncmovies: 'Auto-Request Movies',
plexwatchlistsyncmoviestip:
@@ -88,6 +90,9 @@ const UserGeneralSettings = () => {
);
const UserGeneralSettingsSchema = Yup.object().shape({
+ email: Yup.string()
+ .email(intl.formatMessage(messages.validationemailformat))
+ .required(intl.formatMessage(messages.validationemailrequired)),
discordId: Yup.string()
.nullable()
.matches(/^\d{17,19}$/, intl.formatMessage(messages.validationDiscordId)),
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json
index 367fabc37..0b7a8cbff 100644
--- a/src/i18n/locale/en.json
+++ b/src/i18n/locale/en.json
@@ -1177,6 +1177,8 @@
"components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Settings saved successfully!",
"components.UserProfile.UserSettings.UserGeneralSettings.user": "User",
"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.discordId": "User ID",
"components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "The multi-digit ID number associated with your user account",
diff --git a/yarn.lock b/yarn.lock
index 09b5a3ca0..b94855109 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5033,6 +5033,11 @@ cacache@^16.0.0, cacache@^16.1.0, cacache@^16.1.3:
tar "^6.1.11"
unique-filename "^2.0.0"
+cacheable-lookup@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27"
+ integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==
+
cachedir@2.3.0, cachedir@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8"