From ddfc5e6aa8fc636931f495d6f23d56367466e3b5 Mon Sep 17 00:00:00 2001
From: sct
Date: Tue, 2 Mar 2021 08:18:31 +0000
Subject: [PATCH 01/77] fix: add correct permission checks to modifying user
password/permissions
---
server/routes/user/index.ts | 5 +++-
server/routes/user/usersettings.ts | 29 ++++++++++++++++---
.../UserSettings/UserPasswordChange/index.tsx | 26 +++++++++++++++--
src/i18n/locale/en.json | 2 ++
4 files changed, 54 insertions(+), 8 deletions(-)
diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts
index 803aed7c..d29567a6 100644
--- a/server/routes/user/index.ts
+++ b/server/routes/user/index.ts
@@ -167,7 +167,10 @@ router.get<{ id: string }, UserRequestsResponse>(
}
);
-const canMakePermissionsChange = (permissions: number, user?: User) =>
+export const canMakePermissionsChange = (
+ permissions: number,
+ user?: User
+): boolean =>
// Only let the owner grant admin privileges
!(hasPermission(Permission.ADMIN, permissions) && user?.id !== 1) ||
// Only let users with the manage settings permission, grant the same permission
diff --git a/server/routes/user/usersettings.ts b/server/routes/user/usersettings.ts
index c2e07511..e102e2e2 100644
--- a/server/routes/user/usersettings.ts
+++ b/server/routes/user/usersettings.ts
@@ -1,5 +1,6 @@
import { Router } from 'express';
import { getRepository } from 'typeorm';
+import { canMakePermissionsChange } from '.';
import { User } from '../../entity/User';
import { UserSettings } from '../../entity/UserSettings';
import {
@@ -21,6 +22,7 @@ const isOwnProfileOrAdmin = (): Middleware => {
message: "You do not have permission to view this user's settings.",
});
}
+
next();
};
return authMiddleware;
@@ -137,7 +139,19 @@ userSettingsRoutes.post<
if (req.body.newPassword.length < 8) {
return next({
status: 400,
- message: 'Password must be at least 8 characters',
+ message: 'Password must be at least 8 characters.',
+ });
+ }
+
+ if (
+ (user.id === 1 && req.user?.id !== 1) ||
+ (user.hasPermission(Permission.ADMIN) &&
+ user.id !== req.user?.id &&
+ req.user?.id !== 1)
+ ) {
+ return next({
+ status: 403,
+ message: "You do not have permission to modify this user's password.",
});
}
@@ -283,13 +297,20 @@ userSettingsRoutes.post<
return next({ status: 404, message: 'User not found.' });
}
- if (user.id === 1) {
+ // Only let the owner user modify themselves
+ if (user.id === 1 && req.user?.id !== 1) {
return next({
- status: 500,
- message: 'Permissions for user with ID 1 cannot be modified',
+ status: 403,
+ message: 'You do not have permission to modify this user',
});
}
+ if (!canMakePermissionsChange(req.body.permissions, req.user)) {
+ return next({
+ status: 403,
+ message: 'You do not have permission to grant this level of access',
+ });
+ }
user.permissions = req.body.permissions;
await userRepository.save(user);
diff --git a/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx b/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx
index cc973aa0..545cf539 100644
--- a/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx
+++ b/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx
@@ -5,7 +5,7 @@ import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr';
-import { useUser } from '../../../../hooks/useUser';
+import { Permission, useUser } from '../../../../hooks/useUser';
import Error from '../../../../pages/_error';
import Alert from '../../../Common/Alert';
import Button from '../../../Common/Button';
@@ -33,6 +33,9 @@ const messages = defineMessages({
nopasswordsetDescription:
'This user account currently does not have a password specifically for {applicationTitle}.\
Configure a password below to enable this account to sign in as a "local user."',
+ nopermission: 'No Permission',
+ nopermissionDescription:
+ "You do not have permission to modify this user's password.",
});
const UserPasswordChange: React.FC = () => {
@@ -41,14 +44,14 @@ const UserPasswordChange: React.FC = () => {
const { addToast } = useToasts();
const router = useRouter();
const { user: currentUser } = useUser();
- const { user } = useUser({ id: Number(router.query.userId) });
+ const { user, hasPermission } = useUser({ id: Number(router.query.userId) });
const { data, error, revalidate } = useSWR<{ hasPassword: boolean }>(
user ? `/api/v1/user/${user?.id}/settings/password` : null
);
const PasswordChangeSchema = Yup.object().shape({
currentPassword: Yup.lazy(() =>
- data?.hasPassword
+ data?.hasPassword && currentUser?.id === user?.id
? Yup.string().required(
intl.formatMessage(messages.validationCurrentPassword)
)
@@ -73,6 +76,23 @@ const UserPasswordChange: React.FC = () => {
return ;
}
+ if (
+ currentUser?.id !== user?.id &&
+ hasPermission(Permission.ADMIN) &&
+ currentUser?.id !== 1
+ ) {
+ return (
+ <>
+
+
{intl.formatMessage(messages.password)}
+
+
+ {intl.formatMessage(messages.nopermissionDescription)}
+
+ >
+ );
+ }
+
return (
<>
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json
index a7ad2a04..1a4315a8 100644
--- a/src/i18n/locale/en.json
+++ b/src/i18n/locale/en.json
@@ -708,6 +708,8 @@
"components.UserProfile.UserSettings.UserPasswordChange.newpassword": "New Password",
"components.UserProfile.UserSettings.UserPasswordChange.nopasswordset": "No Password Set",
"components.UserProfile.UserSettings.UserPasswordChange.nopasswordsetDescription": "This user account currently does not have a password specifically for {applicationTitle}. Configure a password below to enable this account to sign in as a \"local user.\"",
+ "components.UserProfile.UserSettings.UserPasswordChange.nopermission": "No Permission",
+ "components.UserProfile.UserSettings.UserPasswordChange.nopermissionDescription": "You do not have permission to modify this user's password.",
"components.UserProfile.UserSettings.UserPasswordChange.password": "Password",
"components.UserProfile.UserSettings.UserPasswordChange.save": "Save Changes",
"components.UserProfile.UserSettings.UserPasswordChange.saving": "Saving…",
From 1b55d2dfbc06d900e7370a4ddfd81789a25bf00c Mon Sep 17 00:00:00 2001
From: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Date: Tue, 2 Mar 2021 05:06:52 -0500
Subject: [PATCH 02/77] feat(ui): display "Owner" role instead of "Admin" for
user ID 1 (#1050)
* feat(ui): display "Owner" role instead of "Admin" for user ID 1
Also add role to user settings page, and fix the missing "Account Type" string and use the same
verbiage on the user list page
* feat(lang): generate translation keys
* fix: utilize hasPermission returned by useUser instead of importing from server/lib/permissions
---
src/components/UserList/index.tsx | 9 ++++--
.../UserGeneralSettings/index.tsx | 29 +++++++++++++++++--
src/i18n/locale/en.json | 8 ++++-
3 files changed, 39 insertions(+), 7 deletions(-)
diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx
index ebe88b3a..e9be2cdf 100644
--- a/src/components/UserList/index.tsx
+++ b/src/components/UserList/index.tsx
@@ -32,13 +32,14 @@ const messages = defineMessages({
'{userCount, plural, =0 {No new users} one {# new user} other {# new users}} imported from Plex.',
user: 'User',
totalrequests: 'Total Requests',
- usertype: 'User Type',
+ accounttype: 'Account Type',
role: 'Role',
created: 'Created',
lastupdated: 'Last Updated',
edit: 'Edit',
bulkedit: 'Bulk Edit',
delete: 'Delete',
+ owner: 'Owner',
admin: 'Admin',
plexuser: 'Plex User',
deleteuser: 'Delete User',
@@ -472,7 +473,7 @@ const UserList: React.FC = () => {
{intl.formatMessage(messages.user)}
{intl.formatMessage(messages.totalrequests)}
-
{intl.formatMessage(messages.usertype)}
+
{intl.formatMessage(messages.accounttype)}
{intl.formatMessage(messages.role)}
{intl.formatMessage(messages.created)}
{intl.formatMessage(messages.lastupdated)}
@@ -543,7 +544,9 @@ const UserList: React.FC = () => {
)}
- {hasPermission(Permission.ADMIN, user.permissions)
+ {user.id === 1
+ ? intl.formatMessage(messages.owner)
+ : hasPermission(Permission.ADMIN, user.permissions)
? intl.formatMessage(messages.admin)
: intl.formatMessage(messages.user)}
diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx
index d426f87c..3551aff3 100644
--- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx
+++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx
@@ -7,7 +7,7 @@ import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr';
import { Language } from '../../../../../server/lib/settings';
import useSettings from '../../../../hooks/useSettings';
-import { UserType, useUser } from '../../../../hooks/useUser';
+import { UserType, useUser, Permission } from '../../../../hooks/useUser';
import Error from '../../../../pages/_error';
import Badge from '../../../Common/Badge';
import Button from '../../../Common/Button';
@@ -19,8 +19,13 @@ const messages = defineMessages({
displayName: 'Display Name',
save: 'Save Changes',
saving: 'Saving…',
+ accounttype: 'Account Type',
plexuser: 'Plex User',
localuser: 'Local User',
+ role: 'Role',
+ owner: 'Owner',
+ admin: 'Admin',
+ user: 'User',
toastSettingsSuccess: 'Settings successfully saved!',
toastSettingsFailure: 'Something went wrong while saving settings.',
region: 'Discover Region',
@@ -37,7 +42,9 @@ const UserGeneralSettings: React.FC = () => {
const intl = useIntl();
const { addToast } = useToasts();
const router = useRouter();
- const { user, mutate } = useUser({ id: Number(router.query.userId) });
+ const { user, hasPermission, mutate } = useUser({
+ id: Number(router.query.userId),
+ });
const { currentSettings } = useSettings();
const { data, error, revalidate } = useSWR<{
username?: string;
@@ -107,7 +114,9 @@ const UserGeneralSettings: React.FC = () => {
return (
+
+
+ {intl.formatMessage(messages.botUsername)}
+
+
+
+
+
+ {errors.botUsername && touched.botUsername && (
+
{errors.botUsername}
+ )}
+
+
{intl.formatMessage(messages.botAPI)}
diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx
index 05f75b3b..9e3c9f33 100644
--- a/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx
+++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx
@@ -19,6 +19,15 @@ const messages = defineMessages({
discordIdTip:
'The ID number for your Discord user account',
validationDiscordId: 'You must provide a valid Discord user ID',
+ telegramChatId: 'Telegram Chat ID',
+ telegramChatIdTip:
+ 'The Chat ID can be aquired by adding @get_id_bot to the chat.',
+ telegramChatIdTipLong:
+ 'Start a chat by clicking here .\
+ Then get the group Chat ID by adding @get_id_bot to that chat and send /my_id to the chat',
+ sendSilently: 'Send Silently',
+ sendSilentlyDescription: 'Send telegram notifications silently',
+ validationTelegramChatId: 'You must provide a valid Telegram Chat ID',
save: 'Save Changes',
saving: 'Saving…',
plexuser: 'Plex User',
@@ -40,6 +49,12 @@ const UserNotificationSettings: React.FC = () => {
discordId: Yup.string()
.optional()
.matches(/^\d{17,18}$/, intl.formatMessage(messages.validationDiscordId)),
+ telegramChatId: Yup.string()
+ .optional()
+ .matches(
+ /^[-]?\d+$/,
+ intl.formatMessage(messages.validationTelegramChatId)
+ ),
});
if (!data && !error) {
@@ -61,6 +76,8 @@ const UserNotificationSettings: React.FC = () => {
initialValues={{
enableNotifications: data?.enableNotifications,
discordId: data?.discordId,
+ telegramChatId: data?.telegramChatId,
+ telegramSendSilently: data?.telegramSendSilently,
}}
validationSchema={UserNotificationSettingsSchema}
enableReinitialize
@@ -71,6 +88,8 @@ const UserNotificationSettings: React.FC = () => {
{
enableNotifications: values.enableNotifications,
discordId: values.discordId,
+ telegramChatId: values.telegramChatId,
+ telegramSendSilently: values.telegramSendSilently,
}
);
@@ -135,6 +154,86 @@ const UserNotificationSettings: React.FC = () => {
)}
+
+
+ {intl.formatMessage(messages.telegramChatId)}
+
+ {data?.telegramBotUsername
+ ? intl.formatMessage(messages.telegramChatIdTipLong, {
+ TelegramBotLink: function TelegramBotLink(msg) {
+ return (
+
+ {msg}
+
+ );
+ },
+ GetIdBotLink: function GetIdBotLink(msg) {
+ return (
+
+ {msg}
+
+ );
+ },
+ })
+ : intl.formatMessage(messages.telegramChatIdTip, {
+ GetIdBotLink: function GetIdBotLink(msg) {
+ return (
+
+ {msg}
+
+ );
+ },
+ })}
+
+
+
+
+
+
+ {errors.telegramChatId && touched.telegramChatId && (
+
{errors.telegramChatId}
+ )}
+
+
+
+
+
+ {intl.formatMessage(messages.sendSilently)}
+
+
+ {intl.formatMessage(messages.sendSilentlyDescription)}
+
+
+
+
+
+
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json
index 4b2b5932..695fbbe8 100644
--- a/src/i18n/locale/en.json
+++ b/src/i18n/locale/en.json
@@ -306,6 +306,7 @@
"components.Settings.Notifications.authPass": "SMTP Password",
"components.Settings.Notifications.authUser": "SMTP Username",
"components.Settings.Notifications.botAPI": "Bot Authentication Token",
+ "components.Settings.Notifications.botUsername": "Bot Username",
"components.Settings.Notifications.chatId": "Chat ID",
"components.Settings.Notifications.discordsettingsfailed": "Discord notification settings failed to save.",
"components.Settings.Notifications.discordsettingssaved": "Discord notification settings saved successfully!",
@@ -718,9 +719,15 @@
"components.UserProfile.UserSettings.UserNotificationSettings.plexuser": "Plex User",
"components.UserProfile.UserSettings.UserNotificationSettings.save": "Save Changes",
"components.UserProfile.UserSettings.UserNotificationSettings.saving": "Saving…",
+ "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "Send Silently",
+ "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "Send telegram notifications silently",
+ "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "Telegram Chat ID",
+ "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTip": "The Chat ID can be aquired by adding @get_id_bot to the chat.",
+ "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Start a chat by clicking here . Then get the group Chat ID by adding @get_id_bot to that chat and send /my_id to the chat",
"components.UserProfile.UserSettings.UserNotificationSettings.toastSettingsFailure": "Something went wrong while saving settings.",
"components.UserProfile.UserSettings.UserNotificationSettings.toastSettingsSuccess": "Settings successfully saved!",
"components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "You must provide a valid Discord user ID",
+ "components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "You must provide a valid Telegram Chat ID",
"components.UserProfile.UserSettings.UserPasswordChange.confirmpassword": "Confirm Password",
"components.UserProfile.UserSettings.UserPasswordChange.currentpassword": "Current Password",
"components.UserProfile.UserSettings.UserPasswordChange.newpassword": "New Password",
From bdf67e732b6c77cbae768a25edfc9a663ef0108b Mon Sep 17 00:00:00 2001
From: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Date: Thu, 4 Mar 2021 22:35:08 -0500
Subject: [PATCH 18/77] fix(lang): edit new Telegram-related strings to conform
to style guide (#1093)
---
.../UserNotificationSettings/index.tsx | 19 ++++++++++---------
src/i18n/locale/en.json | 10 +++++-----
2 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx
index 9e3c9f33..37213016 100644
--- a/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx
+++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx
@@ -20,14 +20,12 @@ const messages = defineMessages({
'The ID number for your Discord user account',
validationDiscordId: 'You must provide a valid Discord user ID',
telegramChatId: 'Telegram Chat ID',
- telegramChatIdTip:
- 'The Chat ID can be aquired by adding @get_id_bot to the chat.',
+ telegramChatIdTip: 'Add @get_id_bot to the chat',
telegramChatIdTipLong:
- 'Start a chat by clicking here .\
- Then get the group Chat ID by adding @get_id_bot to that chat and send /my_id to the chat',
- sendSilently: 'Send Silently',
- sendSilentlyDescription: 'Send telegram notifications silently',
- validationTelegramChatId: 'You must provide a valid Telegram Chat ID',
+ 'Start a chat , add @get_id_bot , and issue the /my_id command',
+ sendSilently: 'Send Telegram Messages Silently',
+ sendSilentlyDescription: 'Send notifications with no sound',
+ validationTelegramChatId: 'You must provide a valid Telegram chat ID',
save: 'Save Changes',
saving: 'Saving…',
plexuser: 'Plex User',
@@ -47,10 +45,10 @@ const UserNotificationSettings: React.FC = () => {
const UserNotificationSettingsSchema = Yup.object().shape({
discordId: Yup.string()
- .optional()
+ .nullable()
.matches(/^\d{17,18}$/, intl.formatMessage(messages.validationDiscordId)),
telegramChatId: Yup.string()
- .optional()
+ .nullable()
.matches(
/^[-]?\d+$/,
intl.formatMessage(messages.validationTelegramChatId)
@@ -184,6 +182,9 @@ const UserNotificationSettings: React.FC = () => {
);
},
+ code: function code(msg) {
+ return {msg};
+ },
})
: intl.formatMessage(messages.telegramChatIdTip, {
GetIdBotLink: function GetIdBotLink(msg) {
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json
index 695fbbe8..c3821d33 100644
--- a/src/i18n/locale/en.json
+++ b/src/i18n/locale/en.json
@@ -719,15 +719,15 @@
"components.UserProfile.UserSettings.UserNotificationSettings.plexuser": "Plex User",
"components.UserProfile.UserSettings.UserNotificationSettings.save": "Save Changes",
"components.UserProfile.UserSettings.UserNotificationSettings.saving": "Saving…",
- "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "Send Silently",
- "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "Send telegram notifications silently",
+ "components.UserProfile.UserSettings.UserNotificationSettings.sendSilently": "Send Telegram Messages Silently",
+ "components.UserProfile.UserSettings.UserNotificationSettings.sendSilentlyDescription": "Send notifications with no sound",
"components.UserProfile.UserSettings.UserNotificationSettings.telegramChatId": "Telegram Chat ID",
- "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTip": "The Chat ID can be aquired by adding @get_id_bot to the chat.",
- "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Start a chat by clicking here . Then get the group Chat ID by adding @get_id_bot to that chat and send /my_id to the chat",
+ "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTip": "Add @get_id_bot to the chat",
+ "components.UserProfile.UserSettings.UserNotificationSettings.telegramChatIdTipLong": "Start a chat , add @get_id_bot , and issue the /my_id command",
"components.UserProfile.UserSettings.UserNotificationSettings.toastSettingsFailure": "Something went wrong while saving settings.",
"components.UserProfile.UserSettings.UserNotificationSettings.toastSettingsSuccess": "Settings successfully saved!",
"components.UserProfile.UserSettings.UserNotificationSettings.validationDiscordId": "You must provide a valid Discord user ID",
- "components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "You must provide a valid Telegram Chat ID",
+ "components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "You must provide a valid Telegram chat ID",
"components.UserProfile.UserSettings.UserPasswordChange.confirmpassword": "Confirm Password",
"components.UserProfile.UserSettings.UserPasswordChange.currentpassword": "Current Password",
"components.UserProfile.UserSettings.UserPasswordChange.newpassword": "New Password",
From 420038d5ffdd4070df03e5c5cb6ef8d6208fddb5 Mon Sep 17 00:00:00 2001
From: Jakob Ankarhem
Date: Fri, 5 Mar 2021 08:12:48 +0100
Subject: [PATCH 19/77] fix(requests): add plex url to request item (#1088)
---
src/components/RequestList/RequestItem/index.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx
index 59cdea66..73c60e98 100644
--- a/src/components/RequestList/RequestItem/index.tsx
+++ b/src/components/RequestList/RequestItem/index.tsx
@@ -209,6 +209,8 @@ const RequestItem: React.FC = ({
).length > 0
}
is4k={requestData.is4k}
+ plexUrl={requestData.media.plexUrl}
+ plexUrl4k={requestData.media.plexUrl4k}
/>
)}
From 0c4637f779d8904037b9cbd5fe9166cf05a891c5 Mon Sep 17 00:00:00 2001
From: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Date: Fri, 5 Mar 2021 03:33:20 -0500
Subject: [PATCH 20/77] fix(ui): add alt prop to studio/network logos & fix
blinking text cursor (#1095)
---
src/components/Discover/DiscoverNetwork/index.tsx | 6 +++---
src/components/Discover/DiscoverStudio/index.tsx | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/components/Discover/DiscoverNetwork/index.tsx b/src/components/Discover/DiscoverNetwork/index.tsx
index b73171c4..66b8c1a6 100644
--- a/src/components/Discover/DiscoverNetwork/index.tsx
+++ b/src/components/Discover/DiscoverNetwork/index.tsx
@@ -49,9 +49,9 @@ const DiscoverTvNetwork: React.FC = () => {
{firstResultData?.network.logoPath ? (
) : (
diff --git a/src/components/Discover/DiscoverStudio/index.tsx b/src/components/Discover/DiscoverStudio/index.tsx
index beca4a3f..f7fd7f7a 100644
--- a/src/components/Discover/DiscoverStudio/index.tsx
+++ b/src/components/Discover/DiscoverStudio/index.tsx
@@ -49,9 +49,9 @@ const DiscoverMovieStudio: React.FC = () => {
{firstResultData?.studio.logoPath ? (
) : (
From cd21865c4d5be00c13c372e0b7a058f61ec855a2 Mon Sep 17 00:00:00 2001
From: sct
Date: Sat, 6 Mar 2021 00:46:53 +0900
Subject: [PATCH 21/77] feat(ui): request list redesign (#1099)
---
.../RequestList/RequestItem/index.tsx | 440 +++++++++---------
src/components/RequestList/index.tsx | 227 ++++-----
src/i18n/locale/en.json | 8 +-
3 files changed, 344 insertions(+), 331 deletions(-)
diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx
index 73c60e98..16a98dd3 100644
--- a/src/components/RequestList/RequestItem/index.tsx
+++ b/src/components/RequestList/RequestItem/index.tsx
@@ -1,12 +1,7 @@
import React, { useContext, useState } from 'react';
import { useInView } from 'react-intersection-observer';
import type { MediaRequest } from '../../../../server/entity/MediaRequest';
-import {
- useIntl,
- FormattedDate,
- FormattedRelativeTime,
- defineMessages,
-} from 'react-intl';
+import { useIntl, FormattedRelativeTime, defineMessages } from 'react-intl';
import { useUser, Permission } from '../../../hooks/useUser';
import { LanguageContext } from '../../../context/LanguageContext';
import type { MovieDetails } from '../../../../server/models/Movie';
@@ -14,7 +9,6 @@ import type { TvDetails } from '../../../../server/models/Tv';
import useSWR from 'swr';
import Badge from '../../Common/Badge';
import StatusBadge from '../../StatusBadge';
-import Table from '../../Common/Table';
import {
MediaRequestStatus,
MediaStatus,
@@ -25,11 +19,16 @@ import globalMessages from '../../../i18n/globalMessages';
import Link from 'next/link';
import { useToasts } from 'react-toast-notifications';
import RequestModal from '../../RequestModal';
+import ConfirmButton from '../../Common/ConfirmButton';
const messages = defineMessages({
seasons: 'Seasons',
notavailable: 'N/A',
failedretry: 'Something went wrong while retrying the request.',
+ areyousure: 'Are you sure?',
+ status: 'Status',
+ requested: 'Requested',
+ modifiedby: 'Modified By',
});
const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
@@ -101,22 +100,24 @@ const RequestItem: React.FC = ({
if (!title && !error) {
return (
-
-
-
+
);
}
if (!title || !requestData) {
return (
-
-
-
+
);
}
return (
-
+ <>
= ({
setShowEditModal(false);
}}
/>
-
-
-
+
+
+
-
-
-
-
-
+ alt=""
+ className="h-full transition duration-300 scale-100 rounded-md shadow-sm cursor-pointer w-14 lg:w-auto lg:h-full transform-gpu hover:scale-105 hover:shadow-md"
+ />
+
= ({
: `/tv/${requestData.media.tmdbId}`
}
>
-
+
{isMovie(title) ? title.title : title.name}
-
+
= ({
{requestData.seasons.length > 0 && (
-
+
{intl.formatMessage(messages.seasons)}
@@ -188,191 +185,204 @@ const RequestItem: React.FC = ({
)}
-
-
- {requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
- MediaStatus.UNKNOWN ||
- requestData.status === MediaRequestStatus.DECLINED ? (
-
- {requestData.status === MediaRequestStatus.DECLINED
- ? intl.formatMessage(globalMessages.declined)
- : intl.formatMessage(globalMessages.failed)}
-
- ) : (
- 0
- }
- is4k={requestData.is4k}
- plexUrl={requestData.media.plexUrl}
- plexUrl4k={requestData.media.plexUrl4k}
- />
- )}
-
-
-
-
-
-
-
-
-
-
- {requestData.modifiedBy ? (
-
-
-
-
- {requestData.modifiedBy.displayName} (
-
- )
-
-
+
+
+ {intl.formatMessage(messages.status)}
+ {requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
+ MediaStatus.UNKNOWN ||
+ requestData.status === MediaRequestStatus.DECLINED ? (
+
+ {requestData.status === MediaRequestStatus.DECLINED
+ ? intl.formatMessage(globalMessages.declined)
+ : intl.formatMessage(globalMessages.failed)}
+
+ ) : (
+ 0
+ }
+ is4k={requestData.is4k}
+ plexUrl={requestData.media.plexUrl}
+ plexUrl4k={requestData.media.plexUrl4k}
+ />
+ )}
+
+
+
+ {intl.formatMessage(messages.requested)}
- ) : (
- N/A
- )}
+
+ {intl.formatDate(requestData.createdAt)}
+
+
+
-
-
- {requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
- MediaStatus.UNKNOWN &&
- requestData.status !== MediaRequestStatus.DECLINED &&
- hasPermission(Permission.MANAGE_REQUESTS) && (
- retryRequest()}
- >
-
+ {requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
+ MediaStatus.UNKNOWN &&
+ requestData.status !== MediaRequestStatus.DECLINED &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
+ retryRequest()}
>
-
-
-
-
- {intl.formatMessage(globalMessages.retry)}
-
-
- )}
- {requestData.status !== MediaRequestStatus.PENDING &&
- hasPermission(Permission.MANAGE_REQUESTS) && (
- deleteRequest()}
- >
-
+
+
+
+
+ {intl.formatMessage(globalMessages.retry)}
+
+
+ )}
+ {requestData.status !== MediaRequestStatus.PENDING &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
+ deleteRequest()}
+ confirmText={intl.formatMessage(messages.areyousure)}
+ className="w-full"
>
-
-
-
- {intl.formatMessage(globalMessages.delete)}
-
-
- )}
- {requestData.status === MediaRequestStatus.PENDING &&
- hasPermission(Permission.MANAGE_REQUESTS) && (
- <>
-
- modifyRequest('approve')}
+
-
-
-
-
- {intl.formatMessage(globalMessages.approve)}
+
+
+
+ {intl.formatMessage(globalMessages.delete)}
+
+
+ )}
+ {requestData.status === MediaRequestStatus.PENDING &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
+ <>
+
+
+ modifyRequest('approve')}
+ >
+
+
+
+
+ {intl.formatMessage(globalMessages.approve)}
+
+
-
-
-
- modifyRequest('decline')}
- >
-
-
-
-
- {intl.formatMessage(globalMessages.decline)}
+
+ modifyRequest('decline')}
+ >
+
+
+
+
+ {intl.formatMessage(globalMessages.decline)}
+
+
-
-
-
- setShowEditModal(true)}
- >
-
+
+ setShowEditModal(true)}
>
-
-
-
- {intl.formatMessage(globalMessages.edit)}
-
-
-
- >
- )}
-
-
+
+
+
+
+ {intl.formatMessage(globalMessages.edit)}
+
+
+
+ >
+ )}
+
+
+ >
);
};
diff --git a/src/components/RequestList/index.tsx b/src/components/RequestList/index.tsx
index 0be3bb00..27db650c 100644
--- a/src/components/RequestList/index.tsx
+++ b/src/components/RequestList/index.tsx
@@ -1,20 +1,15 @@
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
import useSWR from 'swr';
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
import LoadingSpinner from '../Common/LoadingSpinner';
import RequestItem from './RequestItem';
import Header from '../Common/Header';
-import Table from '../Common/Table';
import Button from '../Common/Button';
import { defineMessages, useIntl } from 'react-intl';
import PageTitle from '../Common/PageTitle';
const messages = defineMessages({
requests: 'Requests',
- mediaInfo: 'Media Info',
- status: 'Status',
- requestedAt: 'Requested At',
- modifiedBy: 'Last Modified By',
showingresults:
'Showing {from} to {to} of {total} results',
resultsperpage: 'Display {pageSize} results per page',
@@ -46,6 +41,32 @@ const RequestList: React.FC = () => {
pageIndex * currentPageSize
}&filter=${currentFilter}&sort=${currentSort}`
);
+
+ // Restore last set filter values on component mount
+ useEffect(() => {
+ const filterString = window.localStorage.getItem('rl-filter-settings');
+
+ if (filterString) {
+ const filterSettings = JSON.parse(filterString);
+
+ setCurrentFilter(filterSettings.currentFilter);
+ setCurrentSort(filterSettings.currentSort);
+ setCurrentPageSize(filterSettings.currentPageSize);
+ }
+ }, []);
+
+ // Set fitler values to local storage any time they are changed
+ useEffect(() => {
+ window.localStorage.setItem(
+ 'rl-filter-settings',
+ JSON.stringify({
+ currentFilter,
+ currentSort,
+ currentPageSize,
+ })
+ );
+ }, [currentFilter, currentSort, currentPageSize]);
+
if (!data && !error) {
return ;
}
@@ -60,7 +81,7 @@ const RequestList: React.FC = () => {
return (
<>
-
+
{intl.formatMessage(messages.requests)}
@@ -140,114 +161,96 @@ const RequestList: React.FC = () => {
-
-
-
- {intl.formatMessage(messages.mediaInfo)}
- {intl.formatMessage(messages.status)}
- {intl.formatMessage(messages.requestedAt)}
- {intl.formatMessage(messages.modifiedBy)}
-
-
-
-
- {data.results.map((request) => {
- return (
- revalidate()}
- />
- );
- })}
+ {data.results.map((request) => {
+ return (
+
+ revalidate()}
+ />
+
+ );
+ })}
- {data.results.length === 0 && (
-
-
-
-
- {intl.formatMessage(messages.noresults)}
-
- {currentFilter !== 'all' && (
-
- setCurrentFilter('all')}
- >
- {intl.formatMessage(messages.showallrequests)}
-
-
- )}
-
-
-
- )}
-
-
-
+
+ {intl.formatMessage(messages.noresults)}
+
+ {currentFilter !== 'all' && (
+
+
setCurrentFilter('all')}
>
-
-
- {data.results.length > 0 &&
- intl.formatMessage(messages.showingresults, {
- from: pageIndex * currentPageSize + 1,
- to:
- data.results.length < currentPageSize
- ? pageIndex * currentPageSize + data.results.length
- : (pageIndex + 1) * currentPageSize,
- total: data.pageInfo.results,
- strong: function strong(msg) {
- return {msg} ;
- },
- })}
-
-
-
-
- {intl.formatMessage(messages.resultsperpage, {
- pageSize: (
- {
- setPageIndex(0);
- setCurrentPageSize(Number(e.target.value));
- }}
- value={currentPageSize}
- className="inline short"
- >
- 5
- 10
- 25
- 50
- 100
-
- ),
- })}
-
-
-
- setPageIndex((current) => current - 1)}
+ {intl.formatMessage(messages.showallrequests)}
+
+
+ )}
+
+ )}
+
+
+
+
+ {data.results.length > 0 &&
+ intl.formatMessage(messages.showingresults, {
+ from: pageIndex * currentPageSize + 1,
+ to:
+ data.results.length < currentPageSize
+ ? pageIndex * currentPageSize + data.results.length
+ : (pageIndex + 1) * currentPageSize,
+ total: data.pageInfo.results,
+ strong: function strong(msg) {
+ return {msg} ;
+ },
+ })}
+
+
+
+
+ {intl.formatMessage(messages.resultsperpage, {
+ pageSize: (
+ {
+ setPageIndex(0);
+ setCurrentPageSize(Number(e.target.value));
+ }}
+ value={currentPageSize}
+ className="inline short"
>
- {intl.formatMessage(messages.previous)}
-
- setPageIndex((current) => current + 1)}
- >
- {intl.formatMessage(messages.next)}
-
-
-
-
-
-
-
+
5
+
10
+
25
+
50
+
100
+
+ ),
+ })}
+
+
+
+ setPageIndex((current) => current - 1)}
+ >
+ {intl.formatMessage(messages.previous)}
+
+ setPageIndex((current) => current + 1)}
+ >
+ {intl.formatMessage(messages.next)}
+
+
+
+
>
);
};
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json
index c3821d33..eafcb692 100644
--- a/src/i18n/locale/en.json
+++ b/src/i18n/locale/en.json
@@ -165,27 +165,27 @@
"components.RequestButton.viewrequest4k": "View 4K Request",
"components.RequestCard.all": "All",
"components.RequestCard.seasons": "Seasons",
+ "components.RequestList.RequestItem.areyousure": "Are you sure?",
"components.RequestList.RequestItem.failedretry": "Something went wrong while retrying the request.",
+ "components.RequestList.RequestItem.modifiedby": "Modified By",
"components.RequestList.RequestItem.notavailable": "N/A",
+ "components.RequestList.RequestItem.requested": "Requested",
"components.RequestList.RequestItem.seasons": "Seasons",
+ "components.RequestList.RequestItem.status": "Status",
"components.RequestList.filterAll": "All",
"components.RequestList.filterApproved": "Approved",
"components.RequestList.filterAvailable": "Available",
"components.RequestList.filterPending": "Pending",
"components.RequestList.filterProcessing": "Processing",
- "components.RequestList.mediaInfo": "Media Info",
- "components.RequestList.modifiedBy": "Last Modified By",
"components.RequestList.next": "Next",
"components.RequestList.noresults": "No results.",
"components.RequestList.previous": "Previous",
- "components.RequestList.requestedAt": "Requested At",
"components.RequestList.requests": "Requests",
"components.RequestList.resultsperpage": "Display {pageSize} results per page",
"components.RequestList.showallrequests": "Show All Requests",
"components.RequestList.showingresults": "Showing {from} to {to} of {total} results",
"components.RequestList.sortAdded": "Request Date",
"components.RequestList.sortModified": "Last Modified",
- "components.RequestList.status": "Status",
"components.RequestModal.AdvancedRequester.advancedoptions": "Advanced Options",
"components.RequestModal.AdvancedRequester.animenote": "* This series is an anime.",
"components.RequestModal.AdvancedRequester.default": "(Default)",
From 778dda67d54df87347dd79577ef1bdc88d3c1d3f Mon Sep 17 00:00:00 2001
From: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Date: Fri, 5 Mar 2021 10:52:43 -0500
Subject: [PATCH 22/77] fix(frontend): check for ID instead of email after
initial setup Plex login (#1097)
---
src/components/Setup/LoginWithPlex.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/Setup/LoginWithPlex.tsx b/src/components/Setup/LoginWithPlex.tsx
index 3dc0404e..d62db786 100644
--- a/src/components/Setup/LoginWithPlex.tsx
+++ b/src/components/Setup/LoginWithPlex.tsx
@@ -25,7 +25,7 @@ const LoginWithPlex: React.FC = ({ onComplete }) => {
const login = async () => {
const response = await axios.post('/api/v1/auth/plex', { authToken });
- if (response.data?.email) {
+ if (response.data?.id) {
revalidate();
}
};
From b5ce7f0cabd8c58a768c874b00a1b21c4ddf4a0f Mon Sep 17 00:00:00 2001
From: nuro <4991309+NuroDev@users.noreply.github.com>
Date: Fri, 5 Mar 2021 16:04:51 +0000
Subject: [PATCH 23/77] docs: added Docker compose installation example (#1072)
[skip ci]
* Added Docker compose installation example
* Update docs/getting-started/installation.md
Co-authored-by: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
* Updated compose example formatting
* Added complete docker-compose file example
* Update docs/getting-started/installation.md
Co-authored-by: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
* Added Docker compose installation example
* Update docs/getting-started/installation.md
Co-authored-by: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
* Updated compose example formatting
* Added complete docker-compose file example
* Update docs/getting-started/installation.md
Co-authored-by: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Co-authored-by: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Co-authored-by: sct
---
docs/getting-started/installation.md | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md
index b0efe68d..29327b7c 100644
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -25,6 +25,30 @@ docker run -d \
{% endtab %}
+{% tab title="Compose" %}
+
+**docker-compose.yml:**
+
+```yaml
+---
+version: "3"
+
+services:
+ overseerr:
+ image: sctx/overseerr:latest
+ container_name: overseerr
+ environment:
+ - LOG_LEVEL=info
+ - TZ=Asia/Tokyo
+ ports:
+ - 5055:5055
+ volumes:
+ - /path/to/appdata/config:/app/config
+ restart: unless-stopped
+```
+
+{% endtab %}
+
{% tab title="UID/GID" %}
```text
From 4f1a8a0a7823cc385263dddb37c1861eb692482d Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Sat, 6 Mar 2021 01:05:34 +0900
Subject: [PATCH 24/77] docs: add NuroDev as a contributor (#1100) [skip ci]
* docs: update README.md [skip ci]
* docs: update .all-contributorsrc [skip ci]
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
---
.all-contributorsrc | 9 +++++++++
README.md | 3 ++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 3671ef22..523764a9 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -312,6 +312,15 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "NuroDev",
+ "name": "nuro",
+ "avatar_url": "https://avatars.githubusercontent.com/u/4991309?v=4",
+ "profile": "https://nuro.dev",
+ "contributions": [
+ "doc"
+ ]
}
],
"badgeTemplate": " -orange.svg\"/> ",
diff --git a/README.md b/README.md
index 70bd55d5..5d0fe2f4 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
@@ -140,6 +140,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
David 💻
Douglas Parker 📖
Daniel Carter 💻
+ nuro 📖
From 92508b3f42aa445ccca82db7518e75f343a97ade Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Sat, 6 Mar 2021 01:15:23 +0900
Subject: [PATCH 25/77] docs: add onedr0p as a contributor (#1101) [skip ci]
* docs: update README.md [skip ci]
* docs: update .all-contributorsrc [skip ci]
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
---
.all-contributorsrc | 9 +++++++++
README.md | 3 ++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 523764a9..d693838e 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -321,6 +321,15 @@
"contributions": [
"doc"
]
+ },
+ {
+ "login": "onedr0p",
+ "name": "ᗪєνιη ᗷυнʟ",
+ "avatar_url": "https://avatars.githubusercontent.com/u/213795?v=4",
+ "profile": "https://github.com/onedr0p",
+ "contributions": [
+ "infra"
+ ]
}
],
"badgeTemplate": " -orange.svg\"/> ",
diff --git a/README.md b/README.md
index 5d0fe2f4..be86fdf2 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
@@ -141,6 +141,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Douglas Parker 📖
Daniel Carter 💻
nuro 📖
+ ᗪєνιη ᗷυнʟ 🚇
From e98f2b96058fb9c5af77be2e8a1bd07fb8fcca06 Mon Sep 17 00:00:00 2001
From: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Date: Fri, 5 Mar 2021 20:16:56 -0500
Subject: [PATCH 26/77] fix(ui): correct language usage re: "sync" vs. "scan"
(#1079)
---
overseerr-api.yml | 12 +++----
server/job/plexsync/index.ts | 10 +++---
server/job/radarrsync/index.ts | 6 ++--
server/job/schedule.ts | 34 ++++++++++---------
server/job/sonarrsync/index.ts | 6 ++--
src/components/MovieDetails/index.tsx | 3 +-
src/components/Settings/RadarrModal/index.tsx | 2 +-
.../Settings/SettingsJobsCache/index.tsx | 12 ++++---
src/components/Settings/SettingsPlex.tsx | 8 ++---
src/components/Settings/SonarrModal/index.tsx | 2 +-
src/components/Setup/index.tsx | 7 ++--
src/components/TvDetails/index.tsx | 3 +-
src/i18n/locale/en.json | 22 ++++++------
13 files changed, 67 insertions(+), 60 deletions(-)
diff --git a/overseerr-api.yml b/overseerr-api.yml
index 03667870..fc96da70 100644
--- a/overseerr-api.yml
+++ b/overseerr-api.yml
@@ -1716,13 +1716,13 @@ paths:
$ref: '#/components/schemas/PlexLibrary'
/settings/plex/sync:
get:
- summary: Get status of full Plex library sync
- description: Returns sync progress in a JSON array.
+ summary: Get status of full Plex library scan
+ description: Returns scan progress in a JSON array.
tags:
- settings
responses:
'200':
- description: Status of Plex sync
+ description: Status of Plex scan
content:
application/json:
schema:
@@ -1744,8 +1744,8 @@ paths:
items:
$ref: '#/components/schemas/PlexLibrary'
post:
- summary: Start full Plex library sync
- description: Runs a full Plex library sync and returns the progress in a JSON array.
+ summary: Start full Plex library scan
+ description: Runs a full Plex library scan and returns the progress in a JSON array.
tags:
- settings
requestBody:
@@ -1762,7 +1762,7 @@ paths:
example: false
responses:
'200':
- description: Status of Plex sync
+ description: Status of Plex scan
content:
application/json:
schema:
diff --git a/server/job/plexsync/index.ts b/server/job/plexsync/index.ts
index f4a57c62..60840e0b 100644
--- a/server/job/plexsync/index.ts
+++ b/server/job/plexsync/index.ts
@@ -77,7 +77,7 @@ class JobPlexSync {
if (!metadata.Guid) {
logger.debug('No Guid metadata for this title. Skipping', {
- label: 'Plex Sync',
+ label: 'Plex Scan',
ratingKey: plexitem.ratingKey,
});
return;
@@ -794,7 +794,7 @@ class JobPlexSync {
level: 'info' | 'error' | 'debug' | 'warn' = 'debug',
optional?: Record
): void {
- logger[level](message, { label: 'Plex Sync', ...optional });
+ logger[level](message, { label: 'Plex Scan', ...optional });
}
// checks if any of this.libraries has Hama agent set in Plex
@@ -812,7 +812,7 @@ class JobPlexSync {
const settings = getSettings();
const sessionId = uuid();
this.sessionId = sessionId;
- logger.info('Plex Sync Starting', { sessionId, label: 'Plex Sync' });
+ logger.info('Plex scan starting', { sessionId, label: 'Plex Scan' });
try {
this.running = true;
const userRepository = getRepository(User);
@@ -822,7 +822,7 @@ class JobPlexSync {
});
if (!admin) {
- return this.log('No admin configured. Plex sync skipped.', 'warn');
+ return this.log('No admin configured. Plex scan skipped.', 'warn');
}
this.plexClient = new PlexAPI({ plexToken: admin.plexToken });
@@ -896,7 +896,7 @@ class JobPlexSync {
);
} catch (e) {
logger.error('Sync interrupted', {
- label: 'Plex Sync',
+ label: 'Plex Scan',
errorMessage: e.message,
});
} finally {
diff --git a/server/job/radarrsync/index.ts b/server/job/radarrsync/index.ts
index 57f88ee0..e8b0c890 100644
--- a/server/job/radarrsync/index.ts
+++ b/server/job/radarrsync/index.ts
@@ -32,7 +32,7 @@ class JobRadarrSync {
const settings = getSettings();
const sessionId = uuid();
this.sessionId = sessionId;
- this.log('Radarr sync starting', 'info', { sessionId });
+ this.log('Radarr scan starting', 'info', { sessionId });
try {
this.running = true;
@@ -75,7 +75,7 @@ class JobRadarrSync {
}
}
- this.log('Radarr sync complete', 'info');
+ this.log('Radarr scan complete', 'info');
} catch (e) {
this.log('Something went wrong.', 'error', { errorMessage: e.message });
} finally {
@@ -241,7 +241,7 @@ class JobRadarrSync {
level: 'info' | 'error' | 'debug' | 'warn' = 'debug',
optional?: Record
): void {
- logger[level](message, { label: 'Radarr Sync', ...optional });
+ logger[level](message, { label: 'Radarr Scan', ...optional });
}
}
diff --git a/server/job/schedule.ts b/server/job/schedule.ts
index 342f54a1..7bbf580d 100644
--- a/server/job/schedule.ts
+++ b/server/job/schedule.ts
@@ -17,13 +17,13 @@ interface ScheduledJob {
export const scheduledJobs: ScheduledJob[] = [];
export const startJobs = (): void => {
- // Run recently added plex sync every 5 minutes
+ // Run recently added plex scan every 5 minutes
scheduledJobs.push({
- id: 'plex-recently-added-sync',
- name: 'Plex Recently Added Sync',
+ id: 'plex-recently-added-scan',
+ name: 'Plex Recently Added Scan',
type: 'process',
job: schedule.scheduleJob('0 */5 * * * *', () => {
- logger.info('Starting scheduled job: Plex Recently Added Sync', {
+ logger.info('Starting scheduled job: Plex Recently Added Scan', {
label: 'Jobs',
});
jobPlexRecentSync.run();
@@ -32,39 +32,41 @@ export const startJobs = (): void => {
cancelFn: () => jobPlexRecentSync.cancel(),
});
- // Run full plex sync every 24 hours
+ // Run full plex scan every 24 hours
scheduledJobs.push({
- id: 'plex-full-sync',
- name: 'Plex Full Library Sync',
+ id: 'plex-full-scan',
+ name: 'Plex Full Library Scan',
type: 'process',
job: schedule.scheduleJob('0 0 3 * * *', () => {
- logger.info('Starting scheduled job: Plex Full Sync', { label: 'Jobs' });
+ logger.info('Starting scheduled job: Plex Full Library Scan', {
+ label: 'Jobs',
+ });
jobPlexFullSync.run();
}),
running: () => jobPlexFullSync.status().running,
cancelFn: () => jobPlexFullSync.cancel(),
});
- // Run full radarr sync every 24 hours
+ // Run full radarr scan every 24 hours
scheduledJobs.push({
- id: 'radarr-sync',
- name: 'Radarr Sync',
+ id: 'radarr-scan',
+ name: 'Radarr Scan',
type: 'process',
job: schedule.scheduleJob('0 0 4 * * *', () => {
- logger.info('Starting scheduled job: Radarr Sync', { label: 'Jobs' });
+ logger.info('Starting scheduled job: Radarr Scan', { label: 'Jobs' });
jobRadarrSync.run();
}),
running: () => jobRadarrSync.status().running,
cancelFn: () => jobRadarrSync.cancel(),
});
- // Run full sonarr sync every 24 hours
+ // Run full sonarr scan every 24 hours
scheduledJobs.push({
- id: 'sonarr-sync',
- name: 'Sonarr Sync',
+ id: 'sonarr-scan',
+ name: 'Sonarr Scan',
type: 'process',
job: schedule.scheduleJob('0 30 4 * * *', () => {
- logger.info('Starting scheduled job: Sonarr Sync', { label: 'Jobs' });
+ logger.info('Starting scheduled job: Sonarr Scan', { label: 'Jobs' });
jobSonarrSync.run();
}),
running: () => jobSonarrSync.status().running,
diff --git a/server/job/sonarrsync/index.ts b/server/job/sonarrsync/index.ts
index 3685af48..affcdbb4 100644
--- a/server/job/sonarrsync/index.ts
+++ b/server/job/sonarrsync/index.ts
@@ -35,7 +35,7 @@ class JobSonarrSync {
const settings = getSettings();
const sessionId = uuid();
this.sessionId = sessionId;
- this.log('Sonarr sync starting', 'info', { sessionId });
+ this.log('Sonarr scan starting', 'info', { sessionId });
try {
this.running = true;
@@ -78,7 +78,7 @@ class JobSonarrSync {
}
}
- this.log('Sonarr sync complete', 'info');
+ this.log('Sonarr scan complete', 'info');
} catch (e) {
this.log('Something went wrong.', 'error', { errorMessage: e.message });
} finally {
@@ -374,7 +374,7 @@ class JobSonarrSync {
level: 'info' | 'error' | 'debug' | 'warn' = 'debug',
optional?: Record
): void {
- logger[level](message, { label: 'Sonarr Sync', ...optional });
+ logger[level](message, { label: 'Sonarr Scan', ...optional });
}
}
diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx
index afedec95..c0049f8d 100644
--- a/src/components/MovieDetails/index.tsx
+++ b/src/components/MovieDetails/index.tsx
@@ -60,7 +60,8 @@ const messages = defineMessages({
manageModalNoRequests: 'No Requests',
manageModalClearMedia: 'Clear All Media Data',
manageModalClearMediaWarning:
- 'This will irreversibly remove all data for this movie, including any requests. If this item exists in your Plex library, the media information will be recreated during the next sync.',
+ 'This will irreversibly remove all data for this movie, including any requests.\
+ If this item exists in your Plex library, the media information will be recreated during the next scan.',
approve: 'Approve',
decline: 'Decline',
studio: 'Studio',
diff --git a/src/components/Settings/RadarrModal/index.tsx b/src/components/Settings/RadarrModal/index.tsx
index 967bb7ee..7932447a 100644
--- a/src/components/Settings/RadarrModal/index.tsx
+++ b/src/components/Settings/RadarrModal/index.tsx
@@ -35,7 +35,7 @@ const messages = defineMessages({
apiKeyPlaceholder: 'Your Radarr API key',
baseUrl: 'Base URL',
baseUrlPlaceholder: 'Example: /radarr',
- syncEnabled: 'Enable Sync',
+ syncEnabled: 'Enable Scan',
externalUrl: 'External URL',
externalUrlPlaceholder: 'External URL pointing to your Radarr server',
qualityprofile: 'Quality Profile',
diff --git a/src/components/Settings/SettingsJobsCache/index.tsx b/src/components/Settings/SettingsJobsCache/index.tsx
index 0e70b6b4..b327a187 100644
--- a/src/components/Settings/SettingsJobsCache/index.tsx
+++ b/src/components/Settings/SettingsJobsCache/index.tsx
@@ -19,7 +19,9 @@ import { formatBytes } from '../../../utils/numberHelpers';
const messages: { [messageName: string]: MessageDescriptor } = defineMessages({
jobs: 'Jobs',
jobsDescription:
- 'Overseerr performs certain maintenance tasks as regularly-scheduled jobs, but they can also be manually triggered below. Manually running a job will not alter its schedule.',
+ 'Overseerr performs certain maintenance tasks as regularly-scheduled jobs,\
+ but they can also be manually triggered below.\
+ Manually running a job will not alter its schedule.',
jobname: 'Job Name',
jobtype: 'Type',
nextexecution: 'Next Execution',
@@ -41,10 +43,10 @@ const messages: { [messageName: string]: MessageDescriptor } = defineMessages({
cachevsize: 'Value Size',
flushcache: 'Flush Cache',
unknownJob: 'Unknown Job',
- 'plex-recently-added-sync': 'Plex Recently Added Sync',
- 'plex-full-sync': 'Plex Full Library Sync',
- 'radarr-sync': 'Radarr Sync',
- 'sonarr-sync': 'Sonarr Sync',
+ 'plex-recently-added-scan': 'Plex Recently Added Scan',
+ 'plex-full-scan': 'Plex Full Library Scan',
+ 'radarr-scan': 'Radarr Scan',
+ 'sonarr-scan': 'Sonarr Scan',
'download-sync': 'Download Sync',
'download-sync-reset': 'Download Sync Reset',
});
diff --git a/src/components/Settings/SettingsPlex.tsx b/src/components/Settings/SettingsPlex.tsx
index 4fa58873..fb95aae8 100644
--- a/src/components/Settings/SettingsPlex.tsx
+++ b/src/components/Settings/SettingsPlex.tsx
@@ -49,8 +49,8 @@ const messages = defineMessages({
plexlibraries: 'Plex Libraries',
plexlibrariesDescription:
'The libraries Overseerr scans for titles. Set up and save your Plex connection settings, then click the button below if no libraries are listed.',
- syncing: 'Syncing',
- sync: 'Sync Plex Libraries',
+ scanning: 'Scanning…',
+ scan: 'Scan Plex Libraries',
manualscan: 'Manual Library Scan',
manualscanDescription:
"Normally, this will only be run once every 24 hours. Overseerr will check your Plex server's recently added more aggressively. If this is your first time configuring Plex, a one-time full manual library scan is recommended!",
@@ -563,8 +563,8 @@ const SettingsPlex: React.FC = ({ onComplete }) => {
/>
{isSyncing
- ? intl.formatMessage(messages.syncing)
- : intl.formatMessage(messages.sync)}
+ ? intl.formatMessage(messages.scanning)
+ : intl.formatMessage(messages.scan)}
{data?.libraries.map((library) => (
diff --git a/src/components/Settings/SonarrModal/index.tsx b/src/components/Settings/SonarrModal/index.tsx
index 65d7a157..a41fead9 100644
--- a/src/components/Settings/SonarrModal/index.tsx
+++ b/src/components/Settings/SonarrModal/index.tsx
@@ -52,7 +52,7 @@ const messages = defineMessages({
testFirstRootFolders: 'Test connection to load root folders',
loadinglanguageprofiles: 'Loading language profiles…',
testFirstLanguageProfiles: 'Test connection to load language profiles',
- syncEnabled: 'Enable Sync',
+ syncEnabled: 'Enable Scan',
externalUrl: 'External URL',
externalUrlPlaceholder: 'External URL pointing to your Sonarr server',
preventSearch: 'Disable Auto-Search',
diff --git a/src/components/Setup/index.tsx b/src/components/Setup/index.tsx
index e0a21e5f..b174887f 100644
--- a/src/components/Setup/index.tsx
+++ b/src/components/Setup/index.tsx
@@ -22,8 +22,9 @@ const messages = defineMessages({
configureplex: 'Configure Plex',
configureservices: 'Configure Services',
tip: 'Tip',
- syncingbackground:
- 'Syncing will run in the background. You can continue the setup process in the meantime.',
+ scanbackground:
+ 'Scanning will run in the background.\
+ You can continue the setup process in the meantime.',
});
const Setup: React.FC = () => {
@@ -104,7 +105,7 @@ const Setup: React.FC = () => {
{intl.formatMessage(messages.tip)}
- {intl.formatMessage(messages.syncingbackground)}
+ {intl.formatMessage(messages.scanbackground)}
diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx
index fcc12bbf..e93fa94c 100644
--- a/src/components/TvDetails/index.tsx
+++ b/src/components/TvDetails/index.tsx
@@ -56,7 +56,8 @@ const messages = defineMessages({
manageModalNoRequests: 'No Requests',
manageModalClearMedia: 'Clear All Media Data',
manageModalClearMediaWarning:
- 'This will irreversibly remove all data for this TV series, including any requests. If this item exists in your Plex library, the media information will be recreated during the next sync.',
+ 'This will irreversibly remove all data for this TV series, including any requests.\
+ If this item exists in your Plex library, the media information will be recreated during the next scan.',
approve: 'Approve',
decline: 'Decline',
showtype: 'Show Type',
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json
index eafcb692..16c261a6 100644
--- a/src/i18n/locale/en.json
+++ b/src/i18n/locale/en.json
@@ -63,7 +63,7 @@
"components.MovieDetails.decline": "Decline",
"components.MovieDetails.downloadstatus": "Download Status",
"components.MovieDetails.manageModalClearMedia": "Clear All Media Data",
- "components.MovieDetails.manageModalClearMediaWarning": "* This will irreversibly remove all data for this movie, including any requests. If this item exists in your Plex library, the media information will be recreated during the next sync.",
+ "components.MovieDetails.manageModalClearMediaWarning": "* This will irreversibly remove all data for this movie, including any requests. If this item exists in your Plex library, the media information will be recreated during the next scan.",
"components.MovieDetails.manageModalNoRequests": "No Requests",
"components.MovieDetails.manageModalRequests": "Requests",
"components.MovieDetails.manageModalTitle": "Manage Movie",
@@ -366,7 +366,7 @@
"components.Settings.RadarrModal.servername": "Server Name",
"components.Settings.RadarrModal.servernamePlaceholder": "A Radarr Server",
"components.Settings.RadarrModal.ssl": "SSL",
- "components.Settings.RadarrModal.syncEnabled": "Enable Sync",
+ "components.Settings.RadarrModal.syncEnabled": "Enable Scan",
"components.Settings.RadarrModal.test": "Test",
"components.Settings.RadarrModal.testFirstQualityProfiles": "Test connection to load quality profiles",
"components.Settings.RadarrModal.testFirstRootFolders": "Test connection to load root folders",
@@ -426,12 +426,12 @@
"components.Settings.SettingsJobsCache.jobstarted": "{jobname} started.",
"components.Settings.SettingsJobsCache.jobtype": "Type",
"components.Settings.SettingsJobsCache.nextexecution": "Next Execution",
- "components.Settings.SettingsJobsCache.plex-full-sync": "Plex Full Library Sync",
- "components.Settings.SettingsJobsCache.plex-recently-added-sync": "Plex Recently Added Sync",
+ "components.Settings.SettingsJobsCache.plex-full-scan": "Plex Full Library Scan",
+ "components.Settings.SettingsJobsCache.plex-recently-added-scan": "Plex Recently Added Scan",
"components.Settings.SettingsJobsCache.process": "Process",
- "components.Settings.SettingsJobsCache.radarr-sync": "Radarr Sync",
+ "components.Settings.SettingsJobsCache.radarr-scan": "Radarr Scan",
"components.Settings.SettingsJobsCache.runnow": "Run Now",
- "components.Settings.SettingsJobsCache.sonarr-sync": "Sonarr Sync",
+ "components.Settings.SettingsJobsCache.sonarr-scan": "Sonarr Scan",
"components.Settings.SettingsJobsCache.unknownJob": "Unknown Job",
"components.Settings.SonarrModal.add": "Add Server",
"components.Settings.SonarrModal.animelanguageprofile": "Anime Language Profile",
@@ -465,7 +465,7 @@
"components.Settings.SonarrModal.servername": "Server Name",
"components.Settings.SonarrModal.servernamePlaceholder": "A Sonarr Server",
"components.Settings.SonarrModal.ssl": "SSL",
- "components.Settings.SonarrModal.syncEnabled": "Enable Sync",
+ "components.Settings.SonarrModal.syncEnabled": "Enable Scan",
"components.Settings.SonarrModal.test": "Test",
"components.Settings.SonarrModal.testFirstLanguageProfiles": "Test connection to load language profiles",
"components.Settings.SonarrModal.testFirstQualityProfiles": "Test connection to load quality profiles",
@@ -544,6 +544,8 @@
"components.Settings.regionTip": "Filter content by region (only applies to the \"Popular\" and \"Upcoming\" categories)",
"components.Settings.save": "Save Changes",
"components.Settings.saving": "Saving…",
+ "components.Settings.scan": "Scan Plex Libraries",
+ "components.Settings.scanning": "Scanning…",
"components.Settings.serverConnected": "connected",
"components.Settings.serverLocal": "local",
"components.Settings.serverRemote": "remote",
@@ -562,8 +564,6 @@
"components.Settings.sonarrsettings": "Sonarr Settings",
"components.Settings.ssl": "SSL",
"components.Settings.startscan": "Start Scan",
- "components.Settings.sync": "Sync Plex Libraries",
- "components.Settings.syncing": "Syncing…",
"components.Settings.timeout": "Timeout",
"components.Settings.toastApiKeyFailure": "Something went wrong while generating a new API key.",
"components.Settings.toastApiKeySuccess": "New API key generated!",
@@ -589,9 +589,9 @@
"components.Setup.finish": "Finish Setup",
"components.Setup.finishing": "Finishing…",
"components.Setup.loginwithplex": "Sign in with Plex",
+ "components.Setup.scanbackground": "Scanning will run in the background. You can continue the setup process in the meantime.",
"components.Setup.setup": "Setup",
"components.Setup.signinMessage": "Get started by signing in with your Plex account",
- "components.Setup.syncingbackground": "Syncing will run in the background. You can continue the setup process in the meantime.",
"components.Setup.tip": "Tip",
"components.Setup.welcome": "Welcome to Overseerr",
"components.Slider.noresults": "No results.",
@@ -614,7 +614,7 @@
"components.TvDetails.downloadstatus": "Download Status",
"components.TvDetails.firstAirDate": "First Air Date",
"components.TvDetails.manageModalClearMedia": "Clear All Media Data",
- "components.TvDetails.manageModalClearMediaWarning": "* This will irreversibly remove all data for this TV series, including any requests. If this item exists in your Plex library, the media information will be recreated during the next sync.",
+ "components.TvDetails.manageModalClearMediaWarning": "* This will irreversibly remove all data for this TV series, including any requests. If this item exists in your Plex library, the media information will be recreated during the next scan.",
"components.TvDetails.manageModalNoRequests": "No Requests",
"components.TvDetails.manageModalRequests": "Requests",
"components.TvDetails.manageModalTitle": "Manage Series",
From 1f8b03ff6f67ce76051667de05166da54ed3dc89 Mon Sep 17 00:00:00 2001
From: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Date: Fri, 5 Mar 2021 20:54:31 -0500
Subject: [PATCH 27/77] fix(ui): improve responsive design on new request list
UI (#1105)
---
src/components/RequestCard/index.tsx | 101 ++++----
.../RequestList/RequestItem/index.tsx | 241 ++++++++++--------
src/components/RequestList/index.tsx | 11 +-
src/i18n/locale/en.json | 5 +-
src/styles/globals.css | 16 +-
5 files changed, 213 insertions(+), 161 deletions(-)
diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx
index b65df670..12692e74 100644
--- a/src/components/RequestCard/index.tsx
+++ b/src/components/RequestCard/index.tsx
@@ -5,7 +5,10 @@ import type { TvDetails } from '../../../server/models/Tv';
import type { MovieDetails } from '../../../server/models/Movie';
import useSWR from 'swr';
import { LanguageContext } from '../../context/LanguageContext';
-import { MediaRequestStatus } from '../../../server/constants/media';
+import {
+ MediaRequestStatus,
+ MediaStatus,
+} from '../../../server/constants/media';
import Badge from '../Common/Badge';
import { useUser, Permission } from '../../hooks/useUser';
import axios from 'axios';
@@ -17,6 +20,7 @@ import globalMessages from '../../i18n/globalMessages';
import StatusBadge from '../StatusBadge';
const messages = defineMessages({
+ status: 'Status',
seasons: 'Seasons',
all: 'All',
});
@@ -100,39 +104,48 @@ const RequestCard: React.FC
= ({ request, onTitleData }) => {
}}
>
-
-
-
-
-
- {requestData.requestedBy.displayName}
-
- {requestData.media.status && (
-
+
+
+
+ {intl.formatMessage(messages.status)}
+
+ {requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
+ MediaStatus.UNKNOWN ||
+ requestData.status === MediaRequestStatus.DECLINED ? (
+
+ {requestData.status === MediaRequestStatus.DECLINED
+ ? intl.formatMessage(globalMessages.declined)
+ : intl.formatMessage(globalMessages.failed)}
+
+ ) : (
= ({ request, onTitleData }) => {
] ?? []
).length > 0
}
+ is4k={requestData.is4k}
/>
-
- )}
+ )}
+
{request.seasons.length > 0 && (
-
-
{intl.formatMessage(messages.seasons)}
+
+
+ {intl.formatMessage(messages.seasons)}
+
{!isMovie(title) &&
title.seasons.filter((season) => season.seasonNumber !== 0)
.length === request.seasons.length ? (
@@ -215,15 +231,14 @@ const RequestCard: React.FC = ({ request, onTitleData }) => {
)}
-
+
+
);
};
diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx
index 16a98dd3..190b8be2 100644
--- a/src/components/RequestList/RequestItem/index.tsx
+++ b/src/components/RequestList/RequestItem/index.tsx
@@ -23,12 +23,14 @@ import ConfirmButton from '../../Common/ConfirmButton';
const messages = defineMessages({
seasons: 'Seasons',
+ all: 'All',
notavailable: 'N/A',
failedretry: 'Something went wrong while retrying the request.',
areyousure: 'Are you sure?',
status: 'Status',
requested: 'Requested',
- modifiedby: 'Modified By',
+ modified: 'Modified',
+ modifieduserdate: '{date} by {user}',
});
const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
@@ -130,114 +132,122 @@ const RequestItem: React.FC = ({
setShowEditModal(false);
}}
/>
-
+
-
-
-
-
+
+
-
- {isMovie(title) ? title.title : title.name}
-
-
-
-
-
-
- {requestData.requestedBy.displayName}
-
-
-
- {requestData.seasons.length > 0 && (
-
-
- {intl.formatMessage(messages.seasons)}
-
- {requestData.seasons.map((season) => (
-
- {season.seasonNumber}
-
- ))}
+ alt=""
+ className="h-full transition duration-300 scale-100 rounded-md shadow-sm cursor-pointer w-14 lg:w-auto lg:h-full transform-gpu hover:scale-105 hover:shadow-md"
+ />
+
+
+
+ {isMovie(title) ? title.title : title.name}
+
+
+
- )}
+ {request.seasons.length > 0 && (
+
+
+ {intl.formatMessage(messages.seasons)}
+
+ {!isMovie(title) &&
+ title.seasons.filter((season) => season.seasonNumber !== 0)
+ .length === request.seasons.length ? (
+
+ {intl.formatMessage(messages.all)}
+
+ ) : (
+
+ {request.seasons.map((season) => (
+
+ {season.seasonNumber}
+
+ ))}
+
+ )}
+
+ )}
+
-
-
-
- {intl.formatMessage(messages.status)}
- {requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
- MediaStatus.UNKNOWN ||
- requestData.status === MediaRequestStatus.DECLINED ? (
-
- {requestData.status === MediaRequestStatus.DECLINED
- ? intl.formatMessage(globalMessages.declined)
- : intl.formatMessage(globalMessages.failed)}
-
- ) : (
- 0
- }
- is4k={requestData.is4k}
- plexUrl={requestData.media.plexUrl}
- plexUrl4k={requestData.media.plexUrl4k}
- />
- )}
-
-
-
- {intl.formatMessage(messages.requested)}
-
-
- {intl.formatDate(requestData.createdAt)}
-
-
-
diff --git a/src/components/RequestList/index.tsx b/src/components/RequestList/index.tsx
index 27db650c..126b0882 100644
--- a/src/components/RequestList/index.tsx
+++ b/src/components/RequestList/index.tsx
@@ -173,14 +173,13 @@ const RequestList: React.FC = () => {
})}
{data.results.length === 0 && (
-
-
+
+
{intl.formatMessage(messages.noresults)}
{currentFilter !== 'all' && (
setCurrentFilter('all')}
>
@@ -190,9 +189,9 @@ const RequestList: React.FC = () => {
)}
)}
-
+
@@ -212,7 +211,7 @@ const RequestList: React.FC = () => {
-
+
{intl.formatMessage(messages.resultsperpage, {
pageSize: (
li {
@apply max-w-2xl mt-1 text-sm leading-5 text-gray-500;
}
+img.avatar-sm {
+ @apply w-5 mr-1 pr-0.5 rounded-full;
+}
+
+.card-field {
+ @apply flex items-center my-0.5 sm:my-1 text-sm;
+}
+
+.card-field-name {
+ @apply mr-2 font-medium;
+}
+
.section {
@apply mt-6 mb-10 text-white;
}
@@ -75,7 +87,7 @@ ul.cardList > li {
}
.actions {
- @apply pt-5 mt-8 border-t border-gray-700;
+ @apply pt-5 mt-8 text-white border-t border-gray-700;
}
input[type='checkbox'] {
@@ -104,7 +116,7 @@ select.rounded-r-only {
input.short,
select.short {
- @apply w-20;
+ width: 4.875rem;
}
.protocol {
From 7289872937d5bb94d027424760ee1ceb94095604 Mon Sep 17 00:00:00 2001
From: sct
Date: Sat, 6 Mar 2021 07:49:41 +0000
Subject: [PATCH 28/77] fix(ui): add link to poster image on request items
---
.../RequestList/RequestItem/index.tsx | 30 ++++++++++++-------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx
index 190b8be2..0a1b7503 100644
--- a/src/components/RequestList/RequestItem/index.tsx
+++ b/src/components/RequestList/RequestItem/index.tsx
@@ -139,17 +139,27 @@ const RequestItem: React.FC = ({
backgroundImage: `linear-gradient(90deg, rgba(31, 41, 55, 0.47) 0%, rgba(31, 41, 55, 1) 100%), url(//image.tmdb.org/t/p/w1920_and_h800_multi_faces/${title.backdropPath})`,
}}
/>
-
-
-
+
+
+ >
+
+
+
+
Date: Sat, 6 Mar 2021 08:15:39 +0000
Subject: [PATCH 29/77] refactor(ui): request list now adds a page query
parameter for switching pages
this helps return back to the page you were last looking at when navigated to and from the request
list.
---
.../RequestList/RequestItem/index.tsx | 4 ++-
src/components/RequestList/index.tsx | 34 +++++++++++++------
2 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx
index 0a1b7503..be99ec19 100644
--- a/src/components/RequestList/RequestItem/index.tsx
+++ b/src/components/RequestList/RequestItem/index.tsx
@@ -136,7 +136,9 @@ const RequestItem: React.FC
= ({
diff --git a/src/components/RequestList/index.tsx b/src/components/RequestList/index.tsx
index 126b0882..7d1aceff 100644
--- a/src/components/RequestList/index.tsx
+++ b/src/components/RequestList/index.tsx
@@ -7,6 +7,7 @@ import Header from '../Common/Header';
import Button from '../Common/Button';
import { defineMessages, useIntl } from 'react-intl';
import PageTitle from '../Common/PageTitle';
+import { useRouter } from 'next/router';
const messages = defineMessages({
requests: 'Requests',
@@ -30,12 +31,15 @@ type Filter = 'all' | 'pending' | 'approved' | 'processing' | 'available';
type Sort = 'added' | 'modified';
const RequestList: React.FC = () => {
+ const router = useRouter();
const intl = useIntl();
- const [pageIndex, setPageIndex] = useState(0);
const [currentFilter, setCurrentFilter] = useState
('pending');
const [currentSort, setCurrentSort] = useState('added');
const [currentPageSize, setCurrentPageSize] = useState(10);
+ const page = router.query.page ? Number(router.query.page) : 1;
+ const pageIndex = page - 1;
+
const { data, error, revalidate } = useSWR(
`/api/v1/request?take=${currentPageSize}&skip=${
pageIndex * currentPageSize
@@ -103,8 +107,8 @@ const RequestList: React.FC = () => {
id="filter"
name="filter"
onChange={(e) => {
- setPageIndex(0);
setCurrentFilter(e.target.value as Filter);
+ router.push(router.pathname);
}}
value={currentFilter}
className="rounded-r-only"
@@ -141,12 +145,8 @@ const RequestList: React.FC = () => {
id="sort"
name="sort"
onChange={(e) => {
- setPageIndex(0);
- setCurrentSort(e.target.value as Sort);
- }}
- onBlur={(e) => {
- setPageIndex(0);
setCurrentSort(e.target.value as Sort);
+ router.push(router.pathname);
}}
value={currentSort}
className="rounded-r-only"
@@ -218,8 +218,10 @@ const RequestList: React.FC = () => {
id="pageSize"
name="pageSize"
onChange={(e) => {
- setPageIndex(0);
setCurrentPageSize(Number(e.target.value));
+ router
+ .push(router.pathname)
+ .then(() => window.scrollTo(0, 0));
}}
value={currentPageSize}
className="inline short"
@@ -237,13 +239,25 @@ const RequestList: React.FC = () => {
setPageIndex((current) => current - 1)}
+ onClick={() =>
+ router
+ .push(`${router.pathname}?page=${page - 1}`, undefined, {
+ shallow: true,
+ })
+ .then(() => window.scrollTo(0, 0))
+ }
>
{intl.formatMessage(messages.previous)}
setPageIndex((current) => current + 1)}
+ onClick={() =>
+ router
+ .push(`${router.pathname}?page=${page + 1}`, undefined, {
+ shallow: true,
+ })
+ .then(() => window.scrollTo(0, 0))
+ }
>
{intl.formatMessage(messages.next)}
From 9966632c64ed08a085691498bf3625d5563d17fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E1=97=AA=D1=94=CE=BD=CE=B9=CE=B7=20=E1=97=B7=CF=85=D0=BD?=
=?UTF-8?q?=CA=9F?=
Date: Sat, 6 Mar 2021 04:50:28 -0500
Subject: [PATCH 30/77] ci: build arm64 and amd64 container images for ci
images (#1036)
---
.github/workflows/ci.yml | 1 +
Dockerfile | 9 +++++++++
2 files changed, 10 insertions(+)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ffc74754..10d27b97 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -59,6 +59,7 @@ jobs:
with:
context: .
file: ./Dockerfile
+ platforms: linux/amd64,linux/arm64
push: true
build-args: |
COMMIT_TAG=${{ github.sha }}
diff --git a/Dockerfile b/Dockerfile
index 52c23647..447734ac 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,11 +1,20 @@
FROM node:14.16-alpine AS BUILD_IMAGE
+ARG TARGETPLATFORM
+ENV TARGETPLATFORM=${TARGETPLATFORM:-linux/amd64}
+
ARG COMMIT_TAG
ENV COMMIT_TAG=${COMMIT_TAG}
COPY . /app
WORKDIR /app
+RUN \
+ case "${TARGETPLATFORM}" in \
+ 'linux/arm64') apk add --no-cache python make g++ ;; \
+ 'linux/arm/v7') apk add --no-cache python make g++ ;; \
+ esac
+
RUN yarn --frozen-lockfile && \
yarn build
From f86d907c7421c78aa055ccaf96b12d7d26040264 Mon Sep 17 00:00:00 2001
From: sct
Date: Sat, 6 Mar 2021 11:12:18 +0000
Subject: [PATCH 31/77] ci: also add arm/v7 build for develop tag
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 10d27b97..0b255ff7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -59,7 +59,7 @@ jobs:
with:
context: .
file: ./Dockerfile
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
build-args: |
COMMIT_TAG=${{ github.sha }}
From 9d0b52a2420ca7351c943ba142f8cccf6fcd9ad4 Mon Sep 17 00:00:00 2001
From: sct
Date: Sat, 6 Mar 2021 15:17:04 +0000
Subject: [PATCH 32/77] ci: add network-timeout to yarn install
---
Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dockerfile b/Dockerfile
index 447734ac..9a5af8ca 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,7 +15,7 @@ RUN \
'linux/arm/v7') apk add --no-cache python make g++ ;; \
esac
-RUN yarn --frozen-lockfile && \
+RUN yarn --frozen-lockfile --network-timeout 1000000 && \
yarn build
# remove development dependencies
From 85076919c6ccbf052699b7d5f4ba8b6e5e5af74d Mon Sep 17 00:00:00 2001
From: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Date: Sat, 6 Mar 2021 20:40:47 -0500
Subject: [PATCH 33/77] fix(ui): fix request list UI behavior when season list
is too long (#1106)
* fix(ui): fix request list UI behavior when season list is too long
* fix: add default variants to Tailwind scale
---
src/components/RequestCard/index.tsx | 2 +-
.../RequestList/RequestItem/index.tsx | 38 ++++++++++---------
src/styles/globals.css | 4 +-
tailwind.config.js | 1 +
4 files changed, 24 insertions(+), 21 deletions(-)
diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx
index 12692e74..5f537cdd 100644
--- a/src/components/RequestCard/index.tsx
+++ b/src/components/RequestCard/index.tsx
@@ -111,7 +111,7 @@ const RequestCard: React.FC = ({ request, onTitleData }) => {
: `/tv/${requestData.media.tmdbId}`
}
>
-
+
{isMovie(title) ? title.title : title.name}
diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx
index be99ec19..fb2b888d 100644
--- a/src/components/RequestList/RequestItem/index.tsx
+++ b/src/components/RequestList/RequestItem/index.tsx
@@ -132,7 +132,7 @@ const RequestItem: React.FC = ({
setShowEditModal(false);
}}
/>
-
+
= ({
: undefined,
}}
/>
-
-
+
+
= ({
/>
-
-
-
- {isMovie(title) ? title.title : title.name}
-
-
+
-
+
{intl.formatMessage(messages.status)}
@@ -275,7 +277,7 @@ const RequestItem: React.FC = ({
{requestData.modifiedBy.displayName}
@@ -292,7 +294,7 @@ const RequestItem: React.FC = ({
-
+
{requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
MediaStatus.UNKNOWN &&
requestData.status !== MediaRequestStatus.DECLINED &&
diff --git a/src/styles/globals.css b/src/styles/globals.css
index 691cd081..d845958d 100644
--- a/src/styles/globals.css
+++ b/src/styles/globals.css
@@ -55,11 +55,11 @@ ul.cardList > li {
}
img.avatar-sm {
- @apply w-5 mr-1 pr-0.5 rounded-full;
+ @apply w-5 h-5 mr-1.5 rounded-full transition duration-300 scale-100 transform-gpu group-hover:scale-105;
}
.card-field {
- @apply flex items-center my-0.5 sm:my-1 text-sm;
+ @apply flex items-center py-0.5 sm:py-1 text-sm;
}
.card-field-name {
diff --git a/tailwind.config.js b/tailwind.config.js
index 8f821bb5..39c11938 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -63,6 +63,7 @@ module.exports = {
margin: ['first', 'last', 'responsive'],
boxShadow: ['group-focus'],
opacity: ['disabled', 'hover', 'group-hover'],
+ scale: ['hover', 'focus', 'group-hover'],
zIndex: ['hover', 'responsive'],
},
plugins: [
From 239202d9c11f27410b0fa084bcc4c824b7136081 Mon Sep 17 00:00:00 2001
From: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Date: Sun, 7 Mar 2021 18:06:50 -0500
Subject: [PATCH 34/77] fix(ui): list all movie studios instead of just the
first result (#1110)
---
src/components/MovieDetails/index.tsx | 25 +++++++++++++++----------
src/components/TvDetails/index.tsx | 6 ++++--
src/i18n/locale/en.json | 4 ++--
3 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx
index c0049f8d..9d898815 100644
--- a/src/components/MovieDetails/index.tsx
+++ b/src/components/MovieDetails/index.tsx
@@ -64,7 +64,7 @@ const messages = defineMessages({
If this item exists in your Plex library, the media information will be recreated during the next scan.',
approve: 'Approve',
decline: 'Decline',
- studio: 'Studio',
+ studio: '{studioCount, plural, one {Studio} other {Studios}}',
viewfullcrew: 'View Full Crew',
view: 'View',
areyousure: 'Are you sure?',
@@ -667,19 +667,24 @@ const MovieDetails: React.FC = ({ movie }) => {
)}
- {data.productionCompanies[0] && (
+ {data.productionCompanies.length > 0 && (
- {intl.formatMessage(messages.studio)}
+ {intl.formatMessage(messages.studio, {
+ studioCount: data.productionCompanies.length,
+ })}
-
-
- {data.productionCompanies[0].name}
-
-
+ {data.productionCompanies.map((s) => {
+ return (
+
+ {s.name}
+
+ );
+ })}
)}
diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx
index e93fa94c..930f6a6a 100644
--- a/src/components/TvDetails/index.tsx
+++ b/src/components/TvDetails/index.tsx
@@ -62,7 +62,7 @@ const messages = defineMessages({
decline: 'Decline',
showtype: 'Show Type',
anime: 'Anime',
- network: 'Network',
+ network: '{networkCount, plural, one {Network} other {Networks}}',
viewfullcrew: 'View Full Crew',
areyousure: 'Are you sure?',
opensonarr: 'Open Series in Sonarr',
@@ -694,7 +694,9 @@ const TvDetails: React.FC
= ({ tv }) => {
{data.networks.length > 0 && (
- {intl.formatMessage(messages.network)}
+ {intl.formatMessage(messages.network, {
+ networkCount: data.networks.length,
+ })}
{data.networks
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json
index 87e56b6b..b31125ed 100644
--- a/src/i18n/locale/en.json
+++ b/src/i18n/locale/en.json
@@ -85,7 +85,7 @@
"components.MovieDetails.similar": "Similar Titles",
"components.MovieDetails.similarsubtext": "Other movies similar to {title}",
"components.MovieDetails.status": "Status",
- "components.MovieDetails.studio": "Studio",
+ "components.MovieDetails.studio": "{studioCount, plural, one {Studio} other {Studios}}",
"components.MovieDetails.unavailable": "Unavailable",
"components.MovieDetails.userrating": "User Rating",
"components.MovieDetails.view": "View",
@@ -623,7 +623,7 @@
"components.TvDetails.manageModalTitle": "Manage Series",
"components.TvDetails.mark4kavailable": "Mark 4K as Available",
"components.TvDetails.markavailable": "Mark as Available",
- "components.TvDetails.network": "Network",
+ "components.TvDetails.network": "{networkCount, plural, one {Network} other {Networks}}",
"components.TvDetails.nextAirDate": "Next Air Date",
"components.TvDetails.opensonarr": "Open Series in Sonarr",
"components.TvDetails.opensonarr4k": "Open Series in 4K Sonarr",
From 74e39b0b458590e4f56c9eb582a2fbe33f1563a5 Mon Sep 17 00:00:00 2001
From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
Date: Sun, 7 Mar 2021 16:41:52 -0800
Subject: [PATCH 35/77] docs(proxy): add nginx subfolder reverse proxy (#1114)
[skip ci]
---
.../reverse-proxy-examples.md | 53 +++++++++++++++++--
1 file changed, 48 insertions(+), 5 deletions(-)
diff --git a/docs/extending-overseerr/reverse-proxy-examples.md b/docs/extending-overseerr/reverse-proxy-examples.md
index 8fa120ee..659feb88 100644
--- a/docs/extending-overseerr/reverse-proxy-examples.md
+++ b/docs/extending-overseerr/reverse-proxy-examples.md
@@ -1,12 +1,12 @@
# Reverse Proxy Examples
{% hint style="warning" %}
-Base URLs cannot be configured in Overseerr. With this limitation, only subdomain configurations are supported.
+Base URLs cannot be configured in Overseerr. With this limitation, only subdomain configurations are supported. However, a Nginx subfolder workaround configuration is provided below to use at your own risk.
{% endhint %}
-## [SWAG (Secure Web Application Gateway, formerly known as `letsencrypt`)](https://github.com/linuxserver/docker-swag)
+## SWAG
-A sample proxy configuration is included in SWAG. However, this page is still the only source of truth, so the SWAG sample configuration is not guaranteed to be up-to-date. If you find an inconsistency, please [report it to the LinuxServer team](https://github.com/linuxserver/reverse-proxy-confs/issues/new) or [submit a pull request to update it](https://github.com/linuxserver/reverse-proxy-confs/pulls).
+A sample proxy configuration is included in [SWAG (Secure Web Application Gateway)](https://github.com/linuxserver/docker-swag). However, this page is still the only source of truth, so the SWAG sample configuration is not guaranteed to be up-to-date. If you find an inconsistency, please [report it to the LinuxServer team](https://github.com/linuxserver/reverse-proxy-confs/issues/new) or [submit a pull request to update it](https://github.com/linuxserver/reverse-proxy-confs/pulls).
To use the bundled configuration file, simply rename `overseerr.subdomain.conf.sample` in the `proxy-confs` folder to `overseerr.subdomain.conf`. Alternatively, create a new file `overseerr.subdomain.conf` in `proxy-confs` with the following configuration:
@@ -53,11 +53,14 @@ labels:
For more information, see the Traefik documentation for a [basic example](https://doc.traefik.io/traefik/user-guides/docker-compose/basic-example/).
-## `nginx`
+## Nginx
+
+{% tabs %}
+{% tab title="Subdomain" %}
Add the following configuration to a new file `/etc/nginx/sites-available/overseerr.example.com.conf`:
-```text
+```nginx
server {
listen 80;
server_name overseerr.example.com;
@@ -111,6 +114,46 @@ Then, create a symlink to `/etc/nginx/sites-enabled`:
```bash
sudo ln -s /etc/nginx/sites-available/overseerr.example.com.conf /etc/nginx/sites-enabled/overseerr.example.com.conf
```
+{% endtab %}
+
+{% tab title="Subfolder" %}
+
+{% hint style="warning" %}
+Nginx subfolder reverse proxy is unsupported. The sub filters may stop working when Overseerr is updated. Use at your own risk!
+{% endhint %}
+
+Add the following location block to your existing `nginx.conf` file.
+
+```nginx
+location ^~ /overseerr {
+ set $app 'overseerr';
+ # Remove /overseerr path to pass to the app
+ rewrite ^/overseerr/?(.*)$ /$1 break;
+ proxy_pass http://127.0.0.1:5055; # NO TRAILING SLASH
+ # Redirect location headers
+ proxy_redirect ^ /$app;
+ proxy_redirect /setup /$app/setup;
+ proxy_redirect /login /$app/login;
+ # Sub filters to replace hardcoded paths
+ proxy_set_header Accept-Encoding "";
+ sub_filter_once off;
+ sub_filter_types *;
+ sub_filter 'href="/"' 'href="/$app"';
+ sub_filter 'href="/login"' 'href="/$app/login"';
+ sub_filter 'href:"/"' 'href:"/$app"';
+ sub_filter '/_next' '/$app/_next';
+ sub_filter '/api/v1' '/$app/api/v1';
+ sub_filter '/login/plex/loading' '/$app/login/plex/loading';
+ sub_filter '/images/' '/$app/images/';
+ sub_filter '/android-' '/$app/android-';
+ sub_filter '/apple-' '/$app/apple-';
+ sub_filter '/favicon' '/$app/favicon';
+ sub_filter '/logo.png' '/$app/logo.png';
+ sub_filter '/site.webmanifest' '/$app/site.webmanifest';
+}
+```
+{% endtab %}
+{% endtabs %}
Next, test the configuration:
From f900d953a0e0639ce3b477fb1aae756dfd1ce4ab Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Mon, 8 Mar 2021 10:52:11 +0900
Subject: [PATCH 36/77] docs: add JonnyWong16 as a contributor (#1115) [skip
ci]
* docs: update README.md [skip ci]
* docs: update .all-contributorsrc [skip ci]
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
---
.all-contributorsrc | 9 +++++++++
README.md | 5 ++++-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index d693838e..db2b66cd 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -330,6 +330,15 @@
"contributions": [
"infra"
]
+ },
+ {
+ "login": "JonnyWong16",
+ "name": "JonnyWong16",
+ "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4",
+ "profile": "https://github.com/JonnyWong16",
+ "contributions": [
+ "doc"
+ ]
}
],
"badgeTemplate": " -orange.svg\"/> ",
diff --git a/README.md b/README.md
index be86fdf2..81f43f2c 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
@@ -143,6 +143,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
nuro 📖
ᗪєνιη ᗷυнʟ 🚇
+
+ JonnyWong16 📖
+
From d0fa5f3dbe77b5e4d1f9925979c9840237f91754 Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Mon, 8 Mar 2021 10:53:25 +0900
Subject: [PATCH 37/77] docs: add Roxedus as a contributor (#1118) [skip ci]
* docs: update README.md [skip ci]
* docs: update .all-contributorsrc [skip ci]
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
---
.all-contributorsrc | 9 +++++++++
README.md | 3 ++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index db2b66cd..e6566b7a 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -339,6 +339,15 @@
"contributions": [
"doc"
]
+ },
+ {
+ "login": "Roxedus",
+ "name": "Roxedus",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7110194?v=4",
+ "profile": "https://github.com/Roxedus",
+ "contributions": [
+ "doc"
+ ]
}
],
"badgeTemplate": "
-orange.svg\"/>",
diff --git a/README.md b/README.md
index 81f43f2c..87fc5be0 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
@@ -145,6 +145,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
JonnyWong16 📖
+ Roxedus 📖
From 1a7dc1acf57888d3d0285b58c1c97a824a232216 Mon Sep 17 00:00:00 2001
From: Brandon Cohen
Date: Mon, 8 Mar 2021 11:00:51 -0500
Subject: [PATCH 38/77] fix(frontend): status, requested by, and modified
alignment fix (#1109)
---
src/components/RequestCard/index.tsx | 48 +++++++++----------
.../RequestList/RequestItem/index.tsx | 2 +-
2 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx
index 5f537cdd..7edaa593 100644
--- a/src/components/RequestCard/index.tsx
+++ b/src/components/RequestCard/index.tsx
@@ -129,8 +129,30 @@ const RequestCard: React.FC = ({ request, onTitleData }) => {
-
-
+ {request.seasons.length > 0 && (
+
+
+ {intl.formatMessage(messages.seasons)}
+
+ {!isMovie(title) &&
+ title.seasons.filter((season) => season.seasonNumber !== 0)
+ .length === request.seasons.length ? (
+
+ {intl.formatMessage(messages.all)}
+
+ ) : (
+
+ {request.seasons.map((season) => (
+
+ {season.seasonNumber}
+
+ ))}
+
+ )}
+
+ )}
+
+
{intl.formatMessage(messages.status)}
{requestData.media[requestData.is4k ? 'status4k' : 'status'] ===
@@ -157,28 +179,6 @@ const RequestCard: React.FC = ({ request, onTitleData }) => {
/>
)}
- {request.seasons.length > 0 && (
-
-
- {intl.formatMessage(messages.seasons)}
-
- {!isMovie(title) &&
- title.seasons.filter((season) => season.seasonNumber !== 0)
- .length === request.seasons.length ? (
-
- {intl.formatMessage(messages.all)}
-
- ) : (
-
- {request.seasons.map((season) => (
-
- {season.seasonNumber}
-
- ))}
-
- )}
-
- )}
{requestData.status === MediaRequestStatus.PENDING &&
hasPermission(Permission.MANAGE_REQUESTS) && (
diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx
index fb2b888d..442a4432 100644
--- a/src/components/RequestList/RequestItem/index.tsx
+++ b/src/components/RequestList/RequestItem/index.tsx
@@ -214,7 +214,7 @@ const RequestItem: React.FC = ({
)}
-
+
{intl.formatMessage(messages.status)}
From 48387e5b2f26c0c33acd436c6e1cf902d6c32101 Mon Sep 17 00:00:00 2001
From: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Date: Mon, 8 Mar 2021 14:22:03 -0500
Subject: [PATCH 39/77] feat(notif): include poster image in Telegram
notifications (#1112)
---
server/lib/notifications/agents/telegram.ts | 55 +++++++++++++++------
1 file changed, 40 insertions(+), 15 deletions(-)
diff --git a/server/lib/notifications/agents/telegram.ts b/server/lib/notifications/agents/telegram.ts
index 1e82a04d..c3c89017 100644
--- a/server/lib/notifications/agents/telegram.ts
+++ b/server/lib/notifications/agents/telegram.ts
@@ -4,13 +4,21 @@ import logger from '../../../logger';
import { getSettings, NotificationAgentTelegram } from '../../settings';
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
-interface TelegramPayload {
+interface TelegramMessagePayload {
text: string;
parse_mode: string;
chat_id: string;
disable_notification: boolean;
}
+interface TelegramPhotoPayload {
+ photo: string;
+ caption: string;
+ parse_mode: string;
+ chat_id: string;
+ disable_notification: boolean;
+}
+
class TelegramAgent
extends BaseAgent
implements NotificationAgent {
@@ -125,14 +133,22 @@ class TelegramAgent
try {
const endpoint = `${this.baseUrl}bot${
this.getSettings().options.botAPI
- }/sendMessage`;
+ }/${payload.image ? 'sendPhoto' : 'sendMessage'}`;
- await axios.post(endpoint, {
- text: this.buildMessage(type, payload),
- parse_mode: 'MarkdownV2',
- chat_id: `${this.getSettings().options.chatId}`,
- disable_notification: this.getSettings().options.sendSilently,
- } as TelegramPayload);
+ await (payload.image
+ ? axios.post(endpoint, {
+ photo: payload.image,
+ caption: this.buildMessage(type, payload),
+ parse_mode: 'MarkdownV2',
+ chat_id: `${this.getSettings().options.chatId}`,
+ disable_notification: this.getSettings().options.sendSilently,
+ } as TelegramPhotoPayload)
+ : axios.post(endpoint, {
+ text: this.buildMessage(type, payload),
+ parse_mode: 'MarkdownV2',
+ chat_id: `${this.getSettings().options.chatId}`,
+ disable_notification: this.getSettings().options.sendSilently,
+ } as TelegramMessagePayload));
if (
payload.notifyUser.settings?.enableNotifications &&
@@ -140,13 +156,22 @@ class TelegramAgent
payload.notifyUser.settings?.telegramChatId !==
this.getSettings().options.chatId
) {
- await axios.post(endpoint, {
- text: this.buildMessage(type, payload),
- parse_mode: 'MarkdownV2',
- chat_id: `${payload.notifyUser.settings.telegramChatId}`,
- disable_notification:
- payload.notifyUser.settings.telegramSendSilently,
- } as TelegramPayload);
+ await (payload.image
+ ? axios.post(endpoint, {
+ photo: payload.image,
+ caption: this.buildMessage(type, payload),
+ parse_mode: 'MarkdownV2',
+ chat_id: `${payload.notifyUser.settings.telegramChatId}`,
+ disable_notification:
+ payload.notifyUser.settings.telegramSendSilently,
+ } as TelegramPhotoPayload)
+ : axios.post(endpoint, {
+ text: this.buildMessage(type, payload),
+ parse_mode: 'MarkdownV2',
+ chat_id: `${payload.notifyUser.settings.telegramChatId}`,
+ disable_notification:
+ payload.notifyUser.settings.telegramSendSilently,
+ } as TelegramMessagePayload));
}
return true;
From 57bc3408400a3d20e599d0735c7ee3024b471145 Mon Sep 17 00:00:00 2001
From: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
Date: Mon, 8 Mar 2021 17:56:45 -0500
Subject: [PATCH 40/77] ci(docker): don't cache first build stage, and add temp
fix for growing cache (#1124)
* ci(docker): don't cache first build stage, and add temp fix for growing cache
---
.github/workflows/ci.yml | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0b255ff7..66d2d298 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,6 +24,7 @@ jobs:
run: yarn lint
- name: build
run: yarn build
+
build_and_push:
name: Build & Publish to Docker Hub
needs: test
@@ -69,7 +70,15 @@ jobs:
ghcr.io/sct/overseerr:develop
ghcr.io/sct/overseerr:${{ github.sha }}
cache-from: type=local,src=/tmp/.buildx-cache
- cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
+ cache-to: type=local,dest=/tmp/.buildx-cache-new
+ - # Temporary fix
+ # https://github.com/docker/build-push-action/issues/252
+ # https://github.com/moby/buildkit/issues/1896
+ name: Move cache
+ run: |
+ rm -rf /tmp/.buildx-cache
+ mv /tmp/.buildx-cache-new /tmp/.buildx-cache
+
discord:
name: Send Discord Notification
needs: build_and_push
@@ -78,7 +87,6 @@ jobs:
steps:
- name: Get Build Job Status
uses: technote-space/workflow-conclusion-action@v2.1.2
-
- name: Combine Job Status
id: status
run: |
@@ -88,7 +96,6 @@ jobs:
else
echo ::set-output name=status::$WORKFLOW_CONCLUSION
fi
-
- name: Post Status to Discord
uses: sarisia/actions-status-discord@v1
with:
From 1c6914f5ce5c0d171c4609813915b50233a8e3ad Mon Sep 17 00:00:00 2001
From: sct
Date: Tue, 9 Mar 2021 02:23:29 +0000
Subject: [PATCH 41/77] feat: add studio/network sliders to discover
includes some adjustments to titlecard design
---
src/components/CompanyCard/index.tsx | 46 ++++++
.../Discover/DiscoverNetwork/index.tsx | 2 +-
.../Discover/DiscoverStudio/index.tsx | 2 +-
.../Discover/NetworkSlider/index.tsx | 151 ++++++++++++++++++
.../Discover/StudioSlider/index.tsx | 109 +++++++++++++
src/components/Discover/index.tsx | 4 +
.../MediaSlider/ShowMoreCard/index.tsx | 2 +-
src/components/PersonCard/index.tsx | 12 +-
src/components/RequestCard/index.tsx | 4 +-
.../RequestList/RequestItem/index.tsx | 6 +-
src/components/TitleCard/Placeholder.tsx | 2 +-
src/components/TitleCard/index.tsx | 12 +-
src/i18n/locale/en.json | 2 +
src/styles/globals.css | 2 +-
14 files changed, 336 insertions(+), 20 deletions(-)
create mode 100644 src/components/CompanyCard/index.tsx
create mode 100644 src/components/Discover/NetworkSlider/index.tsx
create mode 100644 src/components/Discover/StudioSlider/index.tsx
diff --git a/src/components/CompanyCard/index.tsx b/src/components/CompanyCard/index.tsx
new file mode 100644
index 00000000..dd6af067
--- /dev/null
+++ b/src/components/CompanyCard/index.tsx
@@ -0,0 +1,46 @@
+import Link from 'next/link';
+import React, { useState } from 'react';
+
+interface CompanyCardProps {
+ name: string;
+ image: string;
+ url: string;
+}
+
+const CompanyCard: React.FC = ({ image, url, name }) => {
+ const [isHovered, setHovered] = useState(false);
+
+ return (
+
+ {
+ setHovered(true);
+ }}
+ onMouseLeave={() => setHovered(false)}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ setHovered(true);
+ }
+ }}
+ role="link"
+ tabIndex={0}
+ >
+
+
+
+
+ );
+};
+
+export default CompanyCard;
diff --git a/src/components/Discover/DiscoverNetwork/index.tsx b/src/components/Discover/DiscoverNetwork/index.tsx
index 66b8c1a6..c2e96421 100644
--- a/src/components/Discover/DiscoverNetwork/index.tsx
+++ b/src/components/Discover/DiscoverNetwork/index.tsx
@@ -49,7 +49,7 @@ const DiscoverTvNetwork: React.FC = () => {
{firstResultData?.network.logoPath ? (
diff --git a/src/components/Discover/DiscoverStudio/index.tsx b/src/components/Discover/DiscoverStudio/index.tsx
index f7fd7f7a..bc7e270d 100644
--- a/src/components/Discover/DiscoverStudio/index.tsx
+++ b/src/components/Discover/DiscoverStudio/index.tsx
@@ -49,7 +49,7 @@ const DiscoverMovieStudio: React.FC = () => {
{firstResultData?.studio.logoPath ? (
diff --git a/src/components/Discover/NetworkSlider/index.tsx b/src/components/Discover/NetworkSlider/index.tsx
new file mode 100644
index 00000000..69b2b2bb
--- /dev/null
+++ b/src/components/Discover/NetworkSlider/index.tsx
@@ -0,0 +1,151 @@
+import React from 'react';
+import { defineMessages, useIntl } from 'react-intl';
+import CompanyCard from '../../CompanyCard';
+import Slider from '../../Slider';
+
+const messages = defineMessages({
+ networks: 'Networks',
+});
+
+interface Network {
+ name: string;
+ image: string;
+ url: string;
+}
+
+const networks: Network[] = [
+ {
+ name: 'Netflix',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/wwemzKWzjKYJFfCeiB57q3r4Bcm.png',
+ url: '/discover/tv/network/213',
+ },
+ {
+ name: 'Disney+',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/gJ8VX6JSu3ciXHuC2dDGAo2lvwM.png',
+ url: '/discover/tv/network/2739',
+ },
+ {
+ name: 'Prime Video',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/ifhbNuuVnlwYy5oXA5VIb2YR8AZ.png',
+ url: '/discover/tv/network/1024',
+ },
+ {
+ name: 'HBO',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/tuomPhY2UtuPTqqFnKMVHvSb724.png',
+ url: '/discover/tv/network/49',
+ },
+ {
+ name: 'ABC',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/ndAvF4JLsliGreX87jAc9GdjmJY.png',
+ url: '/discover/tv/network/2',
+ },
+ {
+ name: 'FOX',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/1DSpHrWyOORkL9N2QHX7Adt31mQ.png',
+ url: '/discover/tv/network/19',
+ },
+ {
+ name: 'Cinemax',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/6mSHSquNpfLgDdv6VnOOvC5Uz2h.png',
+ url: '/discover/tv/network/359',
+ },
+ {
+ name: 'AMC',
+ image:
+ 'https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/pmvRmATOCaDykE6JrVoeYxlFHw3.png',
+ url: '/discover/tv/network/174',
+ },
+ {
+ name: 'Showtime',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/Allse9kbjiP6ExaQrnSpIhkurEi.png',
+ url: '/discover/tv/network/67',
+ },
+ {
+ name: 'Starz',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/8GJjw3HHsAJYwIWKIPBPfqMxlEa.png',
+ url: '/discover/tv/network/318',
+ },
+ {
+ name: 'The CW',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/ge9hzeaU7nMtQ4PjkFlc68dGAJ9.png',
+ url: '/discover/tv/network/71',
+ },
+ {
+ name: 'NBC',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/o3OedEP0f9mfZr33jz2BfXOUK5.png',
+ url: '/discover/tv/network/6',
+ },
+ {
+ name: 'CBS',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/nm8d7P7MJNiBLdgIzUK0gkuEA4r.png',
+ url: '/discover/tv/network/16',
+ },
+ {
+ name: 'BBC One',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/mVn7xESaTNmjBUyUtGNvDQd3CT1.png',
+ url: '/discover/tv/network/4',
+ },
+ {
+ name: 'Cartoon Network',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/c5OC6oVCg6QP4eqzW6XIq17CQjI.png',
+ url: '/discover/tv/network/56',
+ },
+ {
+ name: 'Adult Swim',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/9AKyspxVzywuaMuZ1Bvilu8sXly.png',
+ url: '/discover/tv/network/80',
+ },
+ {
+ name: 'Nickelodeon',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/ikZXxg6GnwpzqiZbRPhJGaZapqB.png',
+ url: '/discover/tv/network/13',
+ },
+];
+
+const NetworkSlider: React.FC = () => {
+ const intl = useIntl();
+
+ return (
+ <>
+
+
+
+ {intl.formatMessage(messages.networks)}
+
+
+
+
(
+
+ ))}
+ emptyMessage=""
+ />
+ >
+ );
+};
+
+export default NetworkSlider;
diff --git a/src/components/Discover/StudioSlider/index.tsx b/src/components/Discover/StudioSlider/index.tsx
new file mode 100644
index 00000000..b3ec8fef
--- /dev/null
+++ b/src/components/Discover/StudioSlider/index.tsx
@@ -0,0 +1,109 @@
+import React from 'react';
+import { defineMessages, useIntl } from 'react-intl';
+import CompanyCard from '../../CompanyCard';
+import Slider from '../../Slider';
+
+const messages = defineMessages({
+ studios: 'Studios',
+});
+
+interface Studio {
+ name: string;
+ image: string;
+ url: string;
+}
+
+const studios: Studio[] = [
+ {
+ name: 'Disney',
+ image:
+ 'https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/wdrCwmRnLFJhEoH8GSfymY85KHT.png',
+ url: '/discover/movies/studio/2',
+ },
+ {
+ name: '20th Century Fox',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/qZCc1lty5FzX30aOCVRBLzaVmcp.png',
+ url: '/discover/movies/studio/25',
+ },
+ {
+ name: 'Sony Pictures',
+ image:
+ 'https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/GagSvqWlyPdkFHMfQ3pNq6ix9P.png',
+ url: '/discover/movies/studio/34',
+ },
+ {
+ name: 'Warner Bros. Pictures',
+ image:
+ 'https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/ky0xOc5OrhzkZ1N6KyUxacfQsCk.png',
+ url: '/discover/movies/studio/174',
+ },
+ {
+ name: 'Universal',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/8lvHyhjr8oUKOOy2dKXoALWKdp0.png',
+ url: '/discover/movies/studio/33',
+ },
+ {
+ name: 'Paramount',
+ image:
+ 'https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/fycMZt242LVjagMByZOLUGbCvv3.png',
+ url: '/discover/movies/studio/4',
+ },
+ {
+ name: 'Pixar',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/1TjvGVDMYsj6JBxOAkUHpPEwLf7.png',
+ url: '/discover/movies/studio/3',
+ },
+ {
+ name: 'Dreamworks',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/kP7t6RwGz2AvvTkvnI1uteEwHet.png',
+ url: '/discover/movies/studio/521',
+ },
+ {
+ name: 'Marvel Studios',
+ image:
+ 'http://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/hUzeosd33nzE5MCNsZxCGEKTXaQ.png',
+ url: '/discover/movies/studio/420',
+ },
+ {
+ name: 'DC',
+ image:
+ 'https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/2Tc1P3Ac8M479naPp1kYT3izLS5.png',
+ url: '/discover/movies/studio/9993',
+ },
+];
+
+const StudioSlider: React.FC = () => {
+ const intl = useIntl();
+
+ return (
+ <>
+
+
+
+ {intl.formatMessage(messages.studios)}
+
+
+
+ (
+
+ ))}
+ emptyMessage=""
+ />
+ >
+ );
+};
+
+export default StudioSlider;
diff --git a/src/components/Discover/index.tsx b/src/components/Discover/index.tsx
index 1af3b58e..bea1b414 100644
--- a/src/components/Discover/index.tsx
+++ b/src/components/Discover/index.tsx
@@ -9,6 +9,8 @@ import type { RequestResultsResponse } from '../../../server/interfaces/api/requ
import RequestCard from '../RequestCard';
import MediaSlider from '../MediaSlider';
import PageTitle from '../Common/PageTitle';
+import StudioSlider from './StudioSlider';
+import NetworkSlider from './NetworkSlider';
const messages = defineMessages({
discover: 'Discover',
@@ -112,6 +114,7 @@ const Discover: React.FC = () => {
linkUrl="/discover/movies/upcoming"
url="/api/v1/discover/movies/upcoming"
/>
+
{
url="/api/v1/discover/tv/upcoming"
linkUrl="/discover/tv/upcoming"
/>
+
>
);
};
diff --git a/src/components/MediaSlider/ShowMoreCard/index.tsx b/src/components/MediaSlider/ShowMoreCard/index.tsx
index 169675c0..d61f262e 100644
--- a/src/components/MediaSlider/ShowMoreCard/index.tsx
+++ b/src/components/MediaSlider/ShowMoreCard/index.tsx
@@ -32,7 +32,7 @@ const ShowMoreCard: React.FC = ({ url, posters }) => {
>
diff --git a/src/components/PersonCard/index.tsx b/src/components/PersonCard/index.tsx
index f0175687..5e5416f4 100644
--- a/src/components/PersonCard/index.tsx
+++ b/src/components/PersonCard/index.tsx
@@ -37,8 +37,8 @@ const PersonCard: React.FC
= ({
@@ -47,7 +47,7 @@ const PersonCard: React.FC
= ({
{profilePath ? (
) : (
@@ -79,7 +79,11 @@ const PersonCard: React.FC = ({
{subName}
)}
-
+
diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx
index 7edaa593..40c82605 100644
--- a/src/components/RequestCard/index.tsx
+++ b/src/components/RequestCard/index.tsx
@@ -31,7 +31,7 @@ const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
const RequestCardPlaceholder: React.FC = () => {
return (
-