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"