diff --git a/.all-contributorsrc b/.all-contributorsrc
index 5468b39c..8dbd97d6 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -94,7 +94,8 @@
"avatar_url": "https://avatars.githubusercontent.com/u/345752?v=4",
"profile": "https://github.com/jab416171",
"contributions": [
- "doc"
+ "doc",
+ "code"
]
},
{
@@ -338,7 +339,8 @@
"avatar_url": "https://avatars.githubusercontent.com/u/37781713?v=4",
"profile": "https://gauthierth.fr/",
"contributions": [
- "code"
+ "code",
+ "maintenance"
]
},
{
@@ -619,6 +621,87 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "j0srisk",
+ "name": "Joseph Risk",
+ "avatar_url": "https://avatars.githubusercontent.com/u/18372584?v=4",
+ "profile": "http://josephrisk.com",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "Loetwiek",
+ "name": "Loetwiek",
+ "avatar_url": "https://avatars.githubusercontent.com/u/79059734?v=4",
+ "profile": "https://github.com/Loetwiek",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "Fuochi",
+ "name": "Fuochi",
+ "avatar_url": "https://avatars.githubusercontent.com/u/4720478?v=4",
+ "profile": "https://github.com/Fuochi",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "demrich",
+ "name": "David Emrich",
+ "avatar_url": "https://avatars.githubusercontent.com/u/30092389?v=4",
+ "profile": "https://github.com/demrich",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "maxnatamo",
+ "name": "Max T. Kristiansen",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5898152?v=4",
+ "profile": "https://maxtrier.dk",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "DamsDev1",
+ "name": "Damien Fajole",
+ "avatar_url": "https://avatars.githubusercontent.com/u/60252259?v=4",
+ "profile": "https://damsdev.me",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "AhmedNSidd",
+ "name": "Ahmed Siddiqui",
+ "avatar_url": "https://avatars.githubusercontent.com/u/36286128?v=4",
+ "profile": "https://github.com/AhmedNSidd",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "JackW6809",
+ "name": "JackOXI",
+ "avatar_url": "https://avatars.githubusercontent.com/u/53652452?v=4",
+ "profile": "https://github.com/JackW6809",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "StancuFlorin",
+ "name": "Stancu Florin",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1199404?v=4",
+ "profile": "http://indicus.ro",
+ "contributions": [
+ "code"
+ ]
}
]
}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ab5f59da..1835e949 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -97,7 +97,7 @@ When adding new UI text, please try to adhere to the following guidelines:
## Translation
-We use [Weblate](https://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/) for our translations, and your help with localizing Overseerr would be greatly appreciated! If your language is not listed below, please [open a feature request](https://github.com/fallenbagel/jellyseerr/issues/new/choose).
+We use [Weblate](https://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/) for our translations, and your help with localizing Jellyseerr would be greatly appreciated! If your language is not listed below, please [open a feature request](https://github.com/fallenbagel/jellyseerr/issues/new/choose).
diff --git a/README.md b/README.md
index 6ed6bff7..8bb9c7c9 100644
--- a/README.md
+++ b/README.md
@@ -131,7 +131,7 @@ Thanks goes to these wonderful people from Overseerr ([emoji key](https://allcon
 Joshua M. Boniface 💻 |
-  Gauthier 💻 |
+  Gauthier 💻 🚧 |
 Kara 🚇 |
 Joaquin Olivero 💻 |
 Julian Behr 🌍 |
@@ -288,7 +288,7 @@ Thanks goes to these wonderful people from Overseerr ([emoji key](https://allcon
 byakurau 🌍 |
 miknii 🌍 |
 Mackenzie 💻 |
-  soup 📖 |
+  soup 📖 💻 |
 ceptonit 📖 |
 aedelbro 💻 |
@@ -321,6 +321,8 @@ Thanks goes to these wonderful people from Overseerr ([emoji key](https://allcon
 Ahmed Siddiqui 💻 |
+  JackOXI 💻 |
+  Stancu Florin 💻 |
diff --git a/cypress/config/settings.cypress.json b/cypress/config/settings.cypress.json
index e49d8888..f376d880 100644
--- a/cypress/config/settings.cypress.json
+++ b/cypress/config/settings.cypress.json
@@ -4,7 +4,7 @@
"vapidPublic": "BK_EpP8NDm9waor2zn6_S28o3ZYv4kCkJOfYpO3pt3W6jnPmxrgTLANUBNbbyaNatPnSQ12De9CeqSYQrqWzHTs",
"main": {
"apiKey": "testkey",
- "applicationTitle": "Overseerr",
+ "applicationTitle": "Jellyseerr",
"applicationUrl": "",
"csrfProtection": false,
"cacheImages": false,
@@ -71,7 +71,7 @@
"ignoreTls": false,
"requireTls": false,
"allowSelfSigned": false,
- "senderName": "Overseerr"
+ "senderName": "Jellyseerr"
}
},
"discord": {
diff --git a/overseerr-api.yml b/jellyseerr-api.yml
similarity index 99%
rename from overseerr-api.yml
rename to jellyseerr-api.yml
index a713a5a1..ddd94202 100644
--- a/overseerr-api.yml
+++ b/jellyseerr-api.yml
@@ -1,19 +1,19 @@
openapi: '3.0.2'
info:
- title: 'Overseerr API'
+ title: 'Jellyseerr API'
version: '1.0.0'
description: |
- This is the documentation for the Overseerr API backend.
+ This is the documentation for the Jellyseerr API backend.
Two primary authentication methods are supported:
- **Cookie Authentication**: A valid sign-in to the `/auth/plex` or `/auth/local` will generate a valid authentication cookie.
- - **API Key Authentication**: Sign-in is also possible by passing an `X-Api-Key` header along with a valid API Key generated by Overseerr.
+ - **API Key Authentication**: Sign-in is also possible by passing an `X-Api-Key` header along with a valid API Key generated by Jellyseerr.
tags:
- name: public
description: Public API endpoints requiring no authentication.
- name: settings
- description: Endpoints related to Overseerr's settings and configuration.
+ description: Endpoints related to Jellyseerr's settings and configuration.
- name: auth
description: Endpoints related to logging in or out, and the currently authenticated user.
- name: users
@@ -160,7 +160,7 @@ components:
example: en
applicationTitle:
type: string
- example: Overseerr
+ example: Jellyseerr
applicationUrl:
type: string
example: https://os.example.com
@@ -1438,7 +1438,7 @@ components:
example: no-reply@example.com
senderName:
type: string
- example: Overseerr
+ example: Jellyseerr
smtpHost:
type: string
example: 127.0.0.1
@@ -1969,8 +1969,8 @@ components:
paths:
/status:
get:
- summary: Get Overseerr status
- description: Returns the current Overseerr status in a JSON object.
+ summary: Get Jellyseerr status
+ description: Returns the current Jellyseerr status in a JSON object.
security: []
tags:
- public
diff --git a/package.json b/package.json
index 74512020..ce508dd3 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"scripts": {
"preinstall": "npx only-allow pnpm",
"postinstall": "node postinstall-win.js",
- "dev": "nodemon -e ts --watch server --watch overseerr-api.yml -e .json,.ts,.yml -x ts-node -r tsconfig-paths/register --files --project server/tsconfig.json server/index.ts",
+ "dev": "nodemon -e ts --watch server --watch jellyseerr-api.yml -e .json,.ts,.yml -x ts-node -r tsconfig-paths/register --files --project server/tsconfig.json server/index.ts",
"build:server": "tsc --project server/tsconfig.json && copyfiles -u 2 server/templates/**/*.{html,pug} dist/templates && tsc-alias -p server/tsconfig.json",
"build:next": "next build",
"build": "pnpm build:next && pnpm build:server",
diff --git a/public/images/overseerr_poster_not_found.png b/public/images/jellyseerr_poster_not_found.png
similarity index 100%
rename from public/images/overseerr_poster_not_found.png
rename to public/images/jellyseerr_poster_not_found.png
diff --git a/public/images/overseerr_poster_not_found_logo_center.png b/public/images/jellyseerr_poster_not_found_logo_center.png
similarity index 100%
rename from public/images/overseerr_poster_not_found_logo_center.png
rename to public/images/jellyseerr_poster_not_found_logo_center.png
diff --git a/public/images/overseerr_poster_not_found_logo_top.png b/public/images/jellyseerr_poster_not_found_logo_top.png
similarity index 100%
rename from public/images/overseerr_poster_not_found_logo_top.png
rename to public/images/jellyseerr_poster_not_found_logo_top.png
diff --git a/public/sw.js b/public/sw.js
index 6a89315a..3aec6343 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -3,7 +3,7 @@
// previously cached resources to be updated from the network.
// This variable is intentionally declared and unused.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-const OFFLINE_VERSION = 3;
+const OFFLINE_VERSION = 4;
const CACHE_NAME = 'offline';
// Customize this with a different URL if needed.
const OFFLINE_URL = '/offline.html';
@@ -107,6 +107,25 @@ self.addEventListener('push', (event) => {
);
}
+ // Set the badge with the amount of pending requests
+ // Only update the badge if the payload confirms they are the admin
+ if (
+ (payload.notificationType === 'MEDIA_APPROVED' ||
+ payload.notificationType === 'MEDIA_DECLINED') &&
+ payload.isAdmin
+ ) {
+ if ('setAppBadge' in navigator) {
+ navigator.setAppBadge(payload.pendingRequestsCount);
+ }
+ return;
+ }
+
+ if (payload.notificationType === 'MEDIA_PENDING') {
+ if ('setAppBadge' in navigator) {
+ navigator.setAppBadge(payload.pendingRequestsCount);
+ }
+ }
+
event.waitUntil(self.registration.showNotification(payload.subject, options));
});
diff --git a/server/api/github.ts b/server/api/github.ts
index c2c3fe6a..50027218 100644
--- a/server/api/github.ts
+++ b/server/api/github.ts
@@ -72,7 +72,7 @@ class GithubAPI extends ExternalAPI {
);
}
- public async getOverseerrReleases({
+ public async getJellyseerrReleases({
take = 20,
}: {
take?: number;
@@ -88,14 +88,14 @@ class GithubAPI extends ExternalAPI {
return data;
} catch (e) {
logger.warn(
- "Failed to retrieve GitHub releases. This may be an issue on GitHub's end. Overseerr can't check if it's on the latest version.",
+ "Failed to retrieve GitHub releases. This may be an issue on GitHub's end. Jellyseerr can't check if it's on the latest version.",
{ label: 'GitHub API', errorMessage: e.message }
);
return [];
}
}
- public async getOverseerrCommits({
+ public async getJellyseerrCommits({
take = 20,
branch = 'develop',
}: {
@@ -114,7 +114,7 @@ class GithubAPI extends ExternalAPI {
return data;
} catch (e) {
logger.warn(
- "Failed to retrieve GitHub commits. This may be an issue on GitHub's end. Overseerr can't check if it's on the latest version.",
+ "Failed to retrieve GitHub commits. This may be an issue on GitHub's end. Jellyseerr can't check if it's on the latest version.",
{ label: 'GitHub API', errorMessage: e.message }
);
return [];
diff --git a/server/api/plexapi.ts b/server/api/plexapi.ts
index 977d367b..5007fe05 100644
--- a/server/api/plexapi.ts
+++ b/server/api/plexapi.ts
@@ -124,9 +124,9 @@ class PlexAPI {
// },
options: {
identifier: settings.clientId,
- product: 'Overseerr',
- deviceName: 'Overseerr',
- platform: 'Overseerr',
+ product: 'Jellyseerr',
+ deviceName: 'Jellyseerr',
+ platform: 'Jellyseerr',
},
});
}
diff --git a/server/api/themoviedb/index.ts b/server/api/themoviedb/index.ts
index 5cf449ea..cb4d4071 100644
--- a/server/api/themoviedb/index.ts
+++ b/server/api/themoviedb/index.ts
@@ -256,6 +256,7 @@ class TheMovieDb extends ExternalAPI {
language,
append_to_response:
'credits,external_ids,videos,keywords,release_dates,watch/providers',
+ include_video_language: language + ', en',
},
43200
);
@@ -280,6 +281,7 @@ class TheMovieDb extends ExternalAPI {
language,
append_to_response:
'aggregate_credits,credits,external_ids,keywords,videos,content_ratings,watch/providers',
+ include_video_language: language + ', en',
},
43200
);
diff --git a/server/index.ts b/server/index.ts
index e4e872ab..88baedb8 100644
--- a/server/index.ts
+++ b/server/index.ts
@@ -41,9 +41,9 @@ import path from 'path';
import swaggerUi from 'swagger-ui-express';
import YAML from 'yamljs';
-const API_SPEC_PATH = path.join(__dirname, '../overseerr-api.yml');
+const API_SPEC_PATH = path.join(__dirname, '../jellyseerr-api.yml');
-logger.info(`Starting Overseerr version ${getAppVersion()}`);
+logger.info(`Starting Jellyseerr version ${getAppVersion()}`);
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
diff --git a/server/lib/notifications/agents/agent.ts b/server/lib/notifications/agents/agent.ts
index d2b0b165..952e1acf 100644
--- a/server/lib/notifications/agents/agent.ts
+++ b/server/lib/notifications/agents/agent.ts
@@ -19,6 +19,8 @@ export interface NotificationPayload {
request?: MediaRequest;
issue?: Issue;
comment?: IssueComment;
+ pendingRequestsCount?: number;
+ isAdmin?: boolean;
}
export abstract class BaseAgent {
diff --git a/server/lib/notifications/agents/slack.ts b/server/lib/notifications/agents/slack.ts
index 7a3b9790..1d6485cc 100644
--- a/server/lib/notifications/agents/slack.ts
+++ b/server/lib/notifications/agents/slack.ts
@@ -188,7 +188,7 @@ class SlackAgent
type: 'actions',
elements: [
{
- action_id: 'open-in-overseerr',
+ action_id: 'open-in-jellyseerr',
type: 'button',
url,
text: {
diff --git a/server/lib/notifications/agents/webpush.ts b/server/lib/notifications/agents/webpush.ts
index 275a77e8..143961ec 100644
--- a/server/lib/notifications/agents/webpush.ts
+++ b/server/lib/notifications/agents/webpush.ts
@@ -1,6 +1,7 @@
import { IssueType, IssueTypeName } from '@server/constants/issue';
-import { MediaType } from '@server/constants/media';
+import { MediaRequestStatus, MediaType } from '@server/constants/media';
import { getRepository } from '@server/datasource';
+import MediaRequest from '@server/entity/MediaRequest';
import { User } from '@server/entity/User';
import { UserPushSubscription } from '@server/entity/UserPushSubscription';
import type { NotificationAgentConfig } from '@server/lib/settings';
@@ -19,6 +20,8 @@ interface PushNotificationPayload {
actionUrl?: string;
actionUrlTitle?: string;
requestId?: number;
+ pendingRequestsCount?: number;
+ isAdmin?: boolean;
}
class WebPushAgent
@@ -129,6 +132,8 @@ class WebPushAgent
requestId: payload.request?.id,
actionUrl,
actionUrlTitle,
+ pendingRequestsCount: payload.pendingRequestsCount,
+ isAdmin: payload.isAdmin,
};
}
@@ -152,6 +157,51 @@ class WebPushAgent
const mainUser = await userRepository.findOne({ where: { id: 1 } });
+ const requestRepository = getRepository(MediaRequest);
+
+ const pendingRequests = await requestRepository.find({
+ where: { status: MediaRequestStatus.PENDING },
+ });
+
+ const webPushNotification = async (
+ pushSub: UserPushSubscription,
+ notificationPayload: Buffer
+ ) => {
+ logger.debug('Sending web push notification', {
+ label: 'Notifications',
+ recipient: pushSub.user.displayName,
+ type: Notification[type],
+ subject: payload.subject,
+ });
+
+ try {
+ await webpush.sendNotification(
+ {
+ endpoint: pushSub.endpoint,
+ keys: {
+ auth: pushSub.auth,
+ p256dh: pushSub.p256dh,
+ },
+ },
+ notificationPayload
+ );
+ } catch (e) {
+ logger.error(
+ 'Error sending web push notification; removing subscription',
+ {
+ label: 'Notifications',
+ recipient: pushSub.user.displayName,
+ type: Notification[type],
+ subject: payload.subject,
+ errorMessage: e.message,
+ }
+ );
+
+ // Failed to send notification so we need to remove the subscription
+ userPushSubRepository.remove(pushSub);
+ }
+ };
+
if (
payload.notifyUser &&
// Check if user has webpush notifications enabled and fallback to true if undefined
@@ -169,7 +219,11 @@ class WebPushAgent
pushSubs.push(...notifySubs);
}
- if (payload.notifyAdmin) {
+ if (
+ payload.notifyAdmin ||
+ type === Notification.MEDIA_APPROVED ||
+ type === Notification.MEDIA_DECLINED
+ ) {
const users = await userRepository.find();
const manageUsers = users.filter(
@@ -192,7 +246,42 @@ class WebPushAgent
})
.getMany();
- pushSubs.push(...allSubs);
+ // We only want to send the custom notification when type is approved or declined
+ // Otherwise, default to the normal notification
+ if (
+ type === Notification.MEDIA_APPROVED ||
+ type === Notification.MEDIA_DECLINED
+ ) {
+ if (mainUser && allSubs.length > 0) {
+ webpush.setVapidDetails(
+ `mailto:${mainUser.email}`,
+ settings.vapidPublic,
+ settings.vapidPrivate
+ );
+
+ // Custom payload only for updating the app badge
+ const notificationBadgePayload = Buffer.from(
+ JSON.stringify(
+ this.getNotificationPayload(type, {
+ subject: payload.subject,
+ notifySystem: false,
+ notifyAdmin: true,
+ isAdmin: true,
+ pendingRequestsCount: pendingRequests.length,
+ })
+ ),
+ 'utf-8'
+ );
+
+ await Promise.all(
+ allSubs.map(async (sub) => {
+ webPushNotification(sub, notificationBadgePayload);
+ })
+ );
+ }
+ } else {
+ pushSubs.push(...allSubs);
+ }
}
if (mainUser && pushSubs.length > 0) {
@@ -202,6 +291,10 @@ class WebPushAgent
settings.vapidPrivate
);
+ if (type === Notification.MEDIA_PENDING) {
+ payload = { ...payload, pendingRequestsCount: pendingRequests.length };
+ }
+
const notificationPayload = Buffer.from(
JSON.stringify(this.getNotificationPayload(type, payload)),
'utf-8'
@@ -209,39 +302,7 @@ class WebPushAgent
await Promise.all(
pushSubs.map(async (sub) => {
- logger.debug('Sending web push notification', {
- label: 'Notifications',
- recipient: sub.user.displayName,
- type: Notification[type],
- subject: payload.subject,
- });
-
- try {
- await webpush.sendNotification(
- {
- endpoint: sub.endpoint,
- keys: {
- auth: sub.auth,
- p256dh: sub.p256dh,
- },
- },
- notificationPayload
- );
- } catch (e) {
- logger.error(
- 'Error sending web push notification; removing subscription',
- {
- label: 'Notifications',
- recipient: sub.user.displayName,
- type: Notification[type],
- subject: payload.subject,
- errorMessage: e.message,
- }
- );
-
- // Failed to send notification so we need to remove the subscription
- userPushSubRepository.remove(sub);
- }
+ webPushNotification(sub, notificationPayload);
})
);
}
diff --git a/server/lib/watchlistsync.ts b/server/lib/watchlistsync.ts
index 4919bf70..ed488767 100644
--- a/server/lib/watchlistsync.ts
+++ b/server/lib/watchlistsync.ts
@@ -130,7 +130,7 @@ class WatchlistSync {
switch (e.constructor) {
// During watchlist sync, these errors aren't necessarily
- // a problem with Overseerr. Since we are auto syncing these constantly, it's
+ // a problem with Jellyseerr. Since we are auto syncing these constantly, it's
// possible they are unexpectedly at their quota limit, for example. So we'll
// instead log these as debug messages.
case RequestPermissionError:
diff --git a/server/logger.ts b/server/logger.ts
index d5809a0e..4708dd20 100644
--- a/server/logger.ts
+++ b/server/logger.ts
@@ -43,14 +43,14 @@ const logger = winston.createLogger({
}),
new winston.transports.DailyRotateFile({
filename: process.env.CONFIG_DIRECTORY
- ? `${process.env.CONFIG_DIRECTORY}/logs/overseerr-%DATE%.log`
- : path.join(__dirname, '../config/logs/overseerr-%DATE%.log'),
+ ? `${process.env.CONFIG_DIRECTORY}/logs/jellyseerr-%DATE%.log`
+ : path.join(__dirname, '../config/logs/jellyseerr-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '7d',
createSymlink: true,
- symlinkName: 'overseerr.log',
+ symlinkName: 'jellyseerr.log',
}),
new winston.transports.DailyRotateFile({
filename: process.env.CONFIG_DIRECTORY
diff --git a/server/routes/auth.ts b/server/routes/auth.ts
index cec19e11..45eea256 100644
--- a/server/routes/auth.ts
+++ b/server/routes/auth.ts
@@ -157,7 +157,7 @@ authRoutes.post('/plex', async (req, res, next) => {
});
} else {
logger.info(
- 'Sign-in attempt from Plex user with access to the media server; creating new Overseerr user',
+ 'Sign-in attempt from Plex user with access to the media server; creating new Jellyseerr user',
{
label: 'API',
ip: req.ip,
@@ -611,7 +611,7 @@ authRoutes.post('/local', async (req, res, next) => {
.getOne();
if (!user || !(await user.passwordMatch(body.password))) {
- logger.warn('Failed sign-in attempt using invalid Overseerr password', {
+ logger.warn('Failed sign-in attempt using invalid Jellyseerr password', {
label: 'API',
ip: req.ip,
email: body.email,
@@ -701,7 +701,7 @@ authRoutes.post('/local', async (req, res, next) => {
return res.status(200).json(user?.filter() ?? {});
} catch (e) {
logger.error(
- 'Something went wrong authenticating with Overseerr password',
+ 'Something went wrong authenticating with Jellyseerr password',
{
label: 'API',
errorMessage: e.message,
diff --git a/server/routes/discover.ts b/server/routes/discover.ts
index 4bb12740..79ae7f28 100644
--- a/server/routes/discover.ts
+++ b/server/routes/discover.ts
@@ -837,7 +837,8 @@ discoverRoutes.get, WatchlistResponse>(
select: ['id', 'plexToken'],
});
- if (activeUser) {
+ if (activeUser && !activeUser?.plexToken) {
+ // Non-Plex users can only see their own watchlist
const [result, total] = await getRepository(Watchlist).findAndCount({
where: { requestedBy: { id: activeUser?.id } },
relations: {
@@ -866,6 +867,7 @@ discoverRoutes.get, WatchlistResponse>(
});
}
+ // List watchlist from Plex
const plexTV = new PlexTvAPI(activeUser.plexToken);
const watchlist = await plexTV.getWatchlist({ offset });
diff --git a/server/routes/index.ts b/server/routes/index.ts
index f064e603..7d0ad5d8 100644
--- a/server/routes/index.ts
+++ b/server/routes/index.ts
@@ -55,7 +55,7 @@ router.get('/status', async (req, res) => {
let commitsBehind = 0;
if (currentVersion.startsWith('develop-') && commitTag !== 'local') {
- const commits = await githubApi.getOverseerrCommits();
+ const commits = await githubApi.getJellyseerrCommits();
if (commits.length) {
const filteredCommits = commits.filter(
@@ -74,7 +74,7 @@ router.get('/status', async (req, res) => {
}
}
} else if (commitTag !== 'local') {
- const releases = await githubApi.getOverseerrReleases();
+ const releases = await githubApi.getJellyseerrReleases();
if (releases.length) {
const latestVersion = releases[0];
@@ -403,7 +403,7 @@ router.get('/watchproviders/tv', async (req, res, next) => {
router.get('/', (_req, res) => {
return res.status(200).json({
- api: 'Overseerr API',
+ api: 'Jellyseerr API',
version: '1.0',
});
});
diff --git a/server/routes/user/usersettings.ts b/server/routes/user/usersettings.ts
index 6ee0f893..ab6bd737 100644
--- a/server/routes/user/usersettings.ts
+++ b/server/routes/user/usersettings.ts
@@ -119,28 +119,10 @@ userSettingsRoutes.post<
}
const oldEmail = user.email;
- const oldUsername = user.username;
user.username = req.body.username;
- if (user.jellyfinUsername) {
+ if (user.userType !== UserType.PLEX) {
user.email = req.body.email || user.jellyfinUsername || user.email;
}
- // Edge case for local users, because they have no Jellyfin username to fall back on
- // if the email is not provided
- if (user.userType === UserType.LOCAL) {
- if (req.body.email) {
- user.email = req.body.email;
- if (
- !user.username &&
- user.email !== oldEmail &&
- !oldEmail.includes('@')
- ) {
- user.username = oldEmail;
- }
- } else if (req.body.username) {
- user.email = oldUsername || user.email;
- user.username = req.body.username;
- }
- }
const existingUser = await userRepository.findOne({
where: { email: user.email },
@@ -437,7 +419,7 @@ userSettingsRoutes.post<{ username: string; password: string }>(
const hostname = getHostname();
const deviceId = Buffer.from(
- `BOT_overseerr_${req.user.username ?? ''}`
+ `BOT_jellyseerr_${req.user.username ?? ''}`
).toString('base64');
const jellyfinserver = new JellyfinAPI(hostname, undefined, deviceId);
diff --git a/src/assets/services/plex.svg b/src/assets/services/plex.svg
index 14c5abd9..53f28d1c 100644
--- a/src/assets/services/plex.svg
+++ b/src/assets/services/plex.svg
@@ -1,85 +1,43 @@
-
-
+
+
\ No newline at end of file
diff --git a/src/components/AirDateBadge/index.tsx b/src/components/AirDateBadge/index.tsx
index d4e438a6..a51f39fc 100644
--- a/src/components/AirDateBadge/index.tsx
+++ b/src/components/AirDateBadge/index.tsx
@@ -14,6 +14,7 @@ type AirDateBadgeProps = {
const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
const WEEK = 1000 * 60 * 60 * 24 * 8;
const intl = useIntl();
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const dAirDate = new Date(airDate);
const nowDate = new Date();
const alreadyAired = dAirDate.getTime() < nowDate.getTime();
@@ -38,7 +39,7 @@ const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
year: 'numeric',
month: 'long',
day: 'numeric',
- timeZone: 'UTC',
+ timeZone,
})}
{showRelative && (
diff --git a/src/components/Blacklist/index.tsx b/src/components/Blacklist/index.tsx
index a752e95f..a8b8dae7 100644
--- a/src/components/Blacklist/index.tsx
+++ b/src/components/Blacklist/index.tsx
@@ -298,7 +298,7 @@ const BlacklistedItem = ({ item, revalidateList }: BlacklistedItemProps) => {
src={
title?.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${title.posterPath}`
- : '/images/overseerr_poster_not_found.png'
+ : '/images/jellyseerr_poster_not_found.png'
}
alt=""
sizes="100vw"
diff --git a/src/components/CollectionDetails/index.tsx b/src/components/CollectionDetails/index.tsx
index d9c9a813..c56781e8 100644
--- a/src/components/CollectionDetails/index.tsx
+++ b/src/components/CollectionDetails/index.tsx
@@ -233,7 +233,7 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
src={
data.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${data.posterPath}`
- : '/images/overseerr_poster_not_found.png'
+ : '/images/jellyseerr_poster_not_found.png'
}
alt=""
sizes="100vw"
diff --git a/src/components/Common/SensitiveInput/index.tsx b/src/components/Common/SensitiveInput/index.tsx
index ae11b951..9a92d254 100644
--- a/src/components/Common/SensitiveInput/index.tsx
+++ b/src/components/Common/SensitiveInput/index.tsx
@@ -25,6 +25,10 @@ const SensitiveInput = ({ as = 'input', ...props }: SensitiveInputProps) => {
return (
<>
{
autoDismiss: true,
});
revalidateIssue();
+ mutate('/api/v1/issue/count');
} catch (e) {
addToast(intl.formatMessage(messages.toaststatusupdatefailed), {
appearance: 'error',
@@ -169,6 +170,7 @@ const IssueDetails = () => {
method: 'DELETE',
});
if (!res.ok) throw new Error();
+ mutate('/api/v1/issue/count');
addToast(intl.formatMessage(messages.toastissuedeleted), {
appearance: 'success',
@@ -240,7 +242,7 @@ const IssueDetails = () => {
src={
data.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${data.posterPath}`
- : '/images/overseerr_poster_not_found.png'
+ : '/images/jellyseerr_poster_not_found.png'
}
alt=""
sizes="100vw"
diff --git a/src/components/IssueList/IssueItem/index.tsx b/src/components/IssueList/IssueItem/index.tsx
index 7fd58b5e..53c618fe 100644
--- a/src/components/IssueList/IssueItem/index.tsx
+++ b/src/components/IssueList/IssueItem/index.tsx
@@ -142,7 +142,7 @@ const IssueItem = ({ issue }: IssueItemProps) => {
src={
title.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${title.posterPath}`
- : '/images/overseerr_poster_not_found.png'
+ : '/images/jellyseerr_poster_not_found.png'
}
alt=""
sizes="100vw"
diff --git a/src/components/IssueModal/CreateIssueModal/index.tsx b/src/components/IssueModal/CreateIssueModal/index.tsx
index 8d880385..58836ef8 100644
--- a/src/components/IssueModal/CreateIssueModal/index.tsx
+++ b/src/components/IssueModal/CreateIssueModal/index.tsx
@@ -15,7 +15,7 @@ import { Field, Formik } from 'formik';
import Link from 'next/link';
import { useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
-import useSWR from 'swr';
+import useSWR, { mutate } from 'swr';
import * as Yup from 'yup';
const messages = defineMessages('components.IssueModal.CreateIssueModal', {
@@ -138,6 +138,8 @@ const CreateIssueModal = ({
autoDismiss: true,
}
);
+
+ mutate('/api/v1/issue/count');
}
if (onCancel) {
diff --git a/src/components/Layout/MobileMenu/index.tsx b/src/components/Layout/MobileMenu/index.tsx
index fe1e2e40..52e84d3d 100644
--- a/src/components/Layout/MobileMenu/index.tsx
+++ b/src/components/Layout/MobileMenu/index.tsx
@@ -1,3 +1,4 @@
+import Badge from '@app/components/Common/Badge';
import { menuMessages } from '@app/components/Layout/Sidebar';
import useClickOutside from '@app/hooks/useClickOutside';
import { Permission, useUser } from '@app/hooks/useUser';
@@ -26,9 +27,16 @@ import {
} from '@heroicons/react/24/solid';
import Link from 'next/link';
import { useRouter } from 'next/router';
-import { cloneElement, useRef, useState } from 'react';
+import { cloneElement, useEffect, useRef, useState } from 'react';
import { useIntl } from 'react-intl';
+interface MobileMenuProps {
+ pendingRequestsCount: number;
+ openIssuesCount: number;
+ revalidateIssueCount: () => void;
+ revalidateRequestsCount: () => void;
+}
+
interface MenuLink {
href: string;
svgIcon: JSX.Element;
@@ -41,7 +49,12 @@ interface MenuLink {
dataTestId?: string;
}
-const MobileMenu = () => {
+const MobileMenu = ({
+ pendingRequestsCount,
+ openIssuesCount,
+ revalidateIssueCount,
+ revalidateRequestsCount,
+}: MobileMenuProps) => {
const ref = useRef(null);
const intl = useIntl();
const [isOpen, setIsOpen] = useState(false);
@@ -139,6 +152,21 @@ const MobileMenu = () => {
})
);
+ useEffect(() => {
+ if (openIssuesCount) {
+ revalidateIssueCount();
+ }
+
+ if (pendingRequestsCount) {
+ revalidateRequestsCount();
+ }
+ }, [
+ revalidateIssueCount,
+ revalidateRequestsCount,
+ pendingRequestsCount,
+ openIssuesCount,
+ ]);
+
return (
{
{
@@ -174,7 +202,25 @@ const MobileMenu = () => {
{cloneElement(isActive ? link.svgIconSelected : link.svgIcon, {
className: 'h-5 w-5',
})}
- {link.content}
+ {link.content}
+ {link.href === '/requests' &&
+ pendingRequestsCount > 0 &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
+
+
+ {pendingRequestsCount}
+
+
+ )}
+ {link.href === '/issues' &&
+ openIssuesCount > 0 &&
+ hasPermission(Permission.MANAGE_ISSUES) && (
+
+
+ {openIssuesCount}
+
+
+ )}
);
})}
@@ -190,7 +236,7 @@ const MobileMenu = () => {
@@ -200,6 +246,23 @@ const MobileMenu = () => {
className: 'h-6 w-6',
}
)}
+ {link.href === '/requests' &&
+ pendingRequestsCount > 0 &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
+
+
+ {pendingRequestsCount > 99
+ ? '99+'
+ : pendingRequestsCount}
+
+
+ )}
);
})}
diff --git a/src/components/Layout/Sidebar/index.tsx b/src/components/Layout/Sidebar/index.tsx
index a947e262..d578bef8 100644
--- a/src/components/Layout/Sidebar/index.tsx
+++ b/src/components/Layout/Sidebar/index.tsx
@@ -1,3 +1,4 @@
+import Badge from '@app/components/Common/Badge';
import UserWarnings from '@app/components/Layout/UserWarnings';
import VersionStatus from '@app/components/Layout/VersionStatus';
import useClickOutside from '@app/hooks/useClickOutside';
@@ -18,7 +19,7 @@ import {
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/router';
-import { Fragment, useRef } from 'react';
+import { Fragment, useEffect, useRef } from 'react';
import { useIntl } from 'react-intl';
export const menuMessages = defineMessages('components.Layout.Sidebar', {
@@ -35,6 +36,10 @@ export const menuMessages = defineMessages('components.Layout.Sidebar', {
interface SidebarProps {
open?: boolean;
setClosed: () => void;
+ pendingRequestsCount: number;
+ openIssuesCount: number;
+ revalidateIssueCount: () => void;
+ revalidateRequestsCount: () => void;
}
interface SidebarLinkProps {
@@ -114,13 +119,35 @@ const SidebarLinks: SidebarLinkProps[] = [
},
];
-const Sidebar = ({ open, setClosed }: SidebarProps) => {
+const Sidebar = ({
+ open,
+ setClosed,
+ pendingRequestsCount,
+ openIssuesCount,
+ revalidateIssueCount,
+ revalidateRequestsCount,
+}: SidebarProps) => {
const navRef = useRef(null);
const router = useRouter();
const intl = useIntl();
const { hasPermission } = useUser();
useClickOutside(navRef, () => setClosed());
+ useEffect(() => {
+ if (openIssuesCount) {
+ revalidateIssueCount();
+ }
+
+ if (pendingRequestsCount) {
+ revalidateRequestsCount();
+ }
+ }, [
+ revalidateIssueCount,
+ revalidateRequestsCount,
+ pendingRequestsCount,
+ openIssuesCount,
+ ]);
+
return (
<>
@@ -253,18 +280,48 @@ const Sidebar = ({ open, setClosed }: SidebarProps) => {
href={sidebarLink.href}
as={sidebarLink.as}
className={`group flex items-center rounded-md px-2 py-2 text-lg font-medium leading-6 text-white transition duration-150 ease-in-out focus:outline-none
- ${
- router.pathname.match(sidebarLink.activeRegExp)
- ? 'bg-gradient-to-br from-indigo-600 to-purple-600 hover:from-indigo-500 hover:to-purple-500'
- : 'hover:bg-gray-700 focus:bg-gray-700'
- }
- `}
+ ${
+ router.pathname.match(sidebarLink.activeRegExp)
+ ? 'bg-gradient-to-br from-indigo-600 to-purple-600 hover:from-indigo-500 hover:to-purple-500'
+ : 'hover:bg-gray-700 focus:bg-gray-700'
+ }
+ `}
data-testid={sidebarLink.dataTestId}
>
{sidebarLink.svgIcon}
{intl.formatMessage(
menuMessages[sidebarLink.messagesKey]
)}
+ {sidebarLink.messagesKey === 'requests' &&
+ pendingRequestsCount > 0 &&
+ hasPermission(Permission.MANAGE_REQUESTS) && (
+
+
+ {pendingRequestsCount}
+
+
+ )}
+ {sidebarLink.messagesKey === 'issues' &&
+ openIssuesCount > 0 &&
+ hasPermission(Permission.MANAGE_ISSUES) && (
+
+
+ {openIssuesCount}
+
+
+ )}
);
})}
diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx
index a1964b0b..50d463cf 100644
--- a/src/components/Layout/index.tsx
+++ b/src/components/Layout/index.tsx
@@ -10,6 +10,7 @@ import { useUser } from '@app/hooks/useUser';
import { ArrowLeftIcon, Bars3BottomLeftIcon } from '@heroicons/react/24/solid';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
+import useSWR from 'swr';
type LayoutProps = {
children: React.ReactNode;
@@ -22,6 +23,18 @@ const Layout = ({ children }: LayoutProps) => {
const router = useRouter();
const { currentSettings } = useSettings();
const { setLocale } = useLocale();
+ const { data: requestResponse, mutate: revalidateRequestsCount } = useSWR(
+ '/api/v1/request/count',
+ {
+ revalidateOnMount: true,
+ }
+ );
+ const { data: issueResponse, mutate: revalidateIssueCount } = useSWR(
+ '/api/v1/issue/count',
+ {
+ revalidateOnMount: true,
+ }
+ );
useEffect(() => {
if (setLocale && user) {
@@ -55,10 +68,21 @@ const Layout = ({ children }: LayoutProps) => {
-
-
setSidebarOpen(false)} />
+ setSidebarOpen(false)}
+ pendingRequestsCount={requestResponse?.pending ?? 0}
+ openIssuesCount={issueResponse?.open ?? 0}
+ revalidateIssueCount={() => revalidateIssueCount()}
+ revalidateRequestsCount={() => revalidateRequestsCount()}
+ />
-
+ revalidateIssueCount()}
+ revalidateRequestsCount={() => revalidateRequestsCount()}
+ />
diff --git a/src/components/Login/LocalLogin.tsx b/src/components/Login/LocalLogin.tsx
index 2372bc7f..74bc1e39 100644
--- a/src/components/Login/LocalLogin.tsx
+++ b/src/components/Login/LocalLogin.tsx
@@ -114,6 +114,9 @@ const LocalLogin = ({ revalidate }: LocalLoginProps) => {
autoComplete="current-password"
data-testid="password"
className="!bg-gray-700/80 placeholder:text-gray-400"
+ data-1pignore="false"
+ data-lpignore="false"
+ data-bwignore="false"
/>
diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx
index 72a598f0..2340cb2e 100644
--- a/src/components/MovieDetails/index.tsx
+++ b/src/components/MovieDetails/index.tsx
@@ -25,7 +25,7 @@ import StatusBadge from '@app/components/StatusBadge';
import useDeepLinks from '@app/hooks/useDeepLinks';
import useLocale from '@app/hooks/useLocale';
import useSettings from '@app/hooks/useSettings';
-import { Permission, useUser } from '@app/hooks/useUser';
+import { Permission, UserType, useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages';
import ErrorPage from '@app/pages/_error';
import { sortCrewPriority } from '@app/utils/creditHelpers';
@@ -505,7 +505,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
src={
data.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${data.posterPath}`
- : '/images/overseerr_poster_not_found.png'
+ : '/images/jellyseerr_poster_not_found.png'
}
alt=""
sizes="100vw"
@@ -594,42 +594,45 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
)}
- {data?.mediaInfo?.status !== MediaStatus.BLACKLISTED && (
- <>
- {toggleWatchlist ? (
-
-
-
- ) : (
-
-
+
+ ) : (
+
- {isUpdating ? (
-
- ) : (
-
- )}
-
-
- )}
- >
- )}
+
+
+ )}
+ >
+ )}
{
+const PWAHeader = ({ applicationTitle = 'Jellyseerr' }: PWAHeaderProps) => {
return (
<>
{
if (onUpdate) {
onUpdate();
+ mutate('/api/v1/request/count');
}
setIsUpdating(false);
};
@@ -72,6 +74,7 @@ const RequestBlock = ({ request, onUpdate }: RequestBlockProps) => {
if (onUpdate) {
onUpdate();
+ mutate('/api/v1/request/count');
}
setIsUpdating(false);
diff --git a/src/components/RequestButton/index.tsx b/src/components/RequestButton/index.tsx
index cbe04fe3..957a3390 100644
--- a/src/components/RequestButton/index.tsx
+++ b/src/components/RequestButton/index.tsx
@@ -15,6 +15,7 @@ import type Media from '@server/entity/Media';
import type { MediaRequest } from '@server/entity/MediaRequest';
import { useMemo, useState } from 'react';
import { useIntl } from 'react-intl';
+import { mutate } from 'swr';
const messages = defineMessages('components.RequestButton', {
viewrequest: 'View Request',
@@ -101,6 +102,7 @@ const RequestButton = ({
if (data) {
onUpdate();
+ mutate('/api/v1/request/count');
}
};
@@ -123,6 +125,7 @@ const RequestButton = ({
);
onUpdate();
+ mutate('/api/v1/request/count');
};
const buttons: ButtonOption[] = [];
diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx
index 7f08044e..e936d98e 100644
--- a/src/components/RequestCard/index.tsx
+++ b/src/components/RequestCard/index.tsx
@@ -80,6 +80,7 @@ const RequestCardError = ({ requestData }: RequestCardErrorProps) => {
if (!res.ok) throw new Error();
mutate('/api/v1/media?filter=allavailable&take=20&sort=mediaAdded');
mutate('/api/v1/request?filter=all&take=10&sort=modified&skip=0');
+ mutate('/api/v1/request/count');
};
return (
@@ -271,6 +272,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
if (data) {
revalidate();
+ mutate('/api/v1/request/count');
}
};
@@ -280,6 +282,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
});
if (!res.ok) throw new Error();
mutate('/api/v1/request?filter=all&take=10&sort=modified&skip=0');
+ mutate('/api/v1/request/count');
};
const retryRequest = async () => {
@@ -618,7 +621,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
src={
title.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${title.posterPath}`
- : '/images/overseerr_poster_not_found.png'
+ : '/images/jellyseerr_poster_not_found.png'
}
alt=""
sizes="100vw"
diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx
index 018fa915..5e764ecb 100644
--- a/src/components/RequestList/RequestItem/index.tsx
+++ b/src/components/RequestList/RequestItem/index.tsx
@@ -27,7 +27,7 @@ import { useState } from 'react';
import { useInView } from 'react-intersection-observer';
import { FormattedRelativeTime, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
-import useSWR from 'swr';
+import useSWR, { mutate } from 'swr';
const messages = defineMessages('components.RequestList.RequestItem', {
seasons: '{seasonCount, plural, one {Season} other {Seasons}}',
@@ -69,6 +69,7 @@ const RequestItemError = ({
});
if (!res.ok) throw new Error();
revalidateList();
+ mutate('/api/v1/request/count');
};
const { mediaUrl: plexUrl, mediaUrl4k: plexUrl4k } = useDeepLinks({
@@ -334,6 +335,7 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
if (data) {
revalidate();
+ mutate('/api/v1/request/count');
}
};
@@ -344,6 +346,7 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
if (!res.ok) throw new Error();
revalidateList();
+ mutate('/api/v1/request/count');
};
const deleteMediaFile = async () => {
@@ -453,7 +456,7 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
src={
title.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${title.posterPath}`
- : '/images/overseerr_poster_not_found.png'
+ : '/images/jellyseerr_poster_not_found.png'
}
alt=""
sizes="100vw"
diff --git a/src/components/RequestModal/CollectionRequestModal.tsx b/src/components/RequestModal/CollectionRequestModal.tsx
index 28aae73a..0f83bea7 100644
--- a/src/components/RequestModal/CollectionRequestModal.tsx
+++ b/src/components/RequestModal/CollectionRequestModal.tsx
@@ -16,7 +16,7 @@ import type { Collection } from '@server/models/Collection';
import { useCallback, useEffect, useState } from 'react';
import { useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
-import useSWR from 'swr';
+import useSWR, { mutate } from 'swr';
const messages = defineMessages('components.RequestModal', {
requestadmin: 'This request will be approved automatically.',
@@ -220,6 +220,7 @@ const CollectionRequestModal = ({
? MediaStatus.UNKNOWN
: MediaStatus.PARTIALLY_AVAILABLE
);
+ mutate('/api/v1/request/count');
}
addToast(
@@ -239,7 +240,16 @@ const CollectionRequestModal = ({
} finally {
setIsUpdating(false);
}
- }, [requestOverrides, data, onComplete, addToast, intl, selectedParts, is4k]);
+ }, [
+ requestOverrides,
+ data?.parts,
+ data?.name,
+ onComplete,
+ addToast,
+ intl,
+ selectedParts,
+ is4k,
+ ]);
const hasAutoApprove = hasPermission(
[
@@ -441,7 +451,7 @@ const CollectionRequestModal = ({
src={
part.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${part.posterPath}`
- : '/images/overseerr_poster_not_found.png'
+ : '/images/jellyseerr_poster_not_found.png'
}
alt=""
sizes="100vw"
diff --git a/src/components/RequestModal/MovieRequestModal.tsx b/src/components/RequestModal/MovieRequestModal.tsx
index 85af7aef..75638586 100644
--- a/src/components/RequestModal/MovieRequestModal.tsx
+++ b/src/components/RequestModal/MovieRequestModal.tsx
@@ -104,6 +104,7 @@ const MovieRequestModal = ({
if (!res.ok) throw new Error();
const mediaRequest: MediaRequest = await res.json();
mutate('/api/v1/request?filter=all&take=10&sort=modified&skip=0');
+ mutate('/api/v1/request/count');
if (mediaRequest) {
if (onComplete) {
@@ -138,7 +139,16 @@ const MovieRequestModal = ({
} finally {
setIsUpdating(false);
}
- }, [data, onComplete, addToast, requestOverrides, hasPermission, intl, is4k]);
+ }, [
+ requestOverrides,
+ data?.id,
+ data?.title,
+ is4k,
+ onComplete,
+ addToast,
+ intl,
+ hasPermission,
+ ]);
const cancelRequest = async () => {
setIsUpdating(true);
@@ -150,6 +160,7 @@ const MovieRequestModal = ({
if (!res.ok) throw new Error();
mutate('/api/v1/request?filter=all&take=10&sort=modified&skip=0');
+ mutate('/api/v1/request/count');
if (res.status === 204) {
if (onComplete) {
@@ -197,6 +208,7 @@ const MovieRequestModal = ({
if (!res.ok) throw new Error();
}
mutate('/api/v1/request?filter=all&take=10&sort=modified&skip=0');
+ mutate('/api/v1/request/count');
addToast(
diff --git a/src/components/RequestModal/SearchByNameModal/index.tsx b/src/components/RequestModal/SearchByNameModal/index.tsx
index 1b86b614..1aa55f3f 100644
--- a/src/components/RequestModal/SearchByNameModal/index.tsx
+++ b/src/components/RequestModal/SearchByNameModal/index.tsx
@@ -92,7 +92,7 @@ const SearchByNameModal = ({
@@ -189,6 +191,7 @@ const TvRequestModal = ({
if (onUpdating) {
onUpdating(true);
+ mutate('/api/v1/request/count');
}
try {
diff --git a/src/components/Settings/Notifications/NotificationsDiscord.tsx b/src/components/Settings/Notifications/NotificationsDiscord.tsx
index 82ac6840..d34edb64 100644
--- a/src/components/Settings/Notifications/NotificationsDiscord.tsx
+++ b/src/components/Settings/Notifications/NotificationsDiscord.tsx
@@ -238,6 +238,10 @@ const NotificationsDiscord = () => {
name="botUsername"
type="text"
placeholder={settings.currentSettings.applicationTitle}
+ autoComplete="off"
+ data-1pignore="true"
+ data-lpignore="true"
+ data-bwignore="true"
/>
{errors.botUsername &&
diff --git a/src/components/Settings/Notifications/NotificationsEmail.tsx b/src/components/Settings/Notifications/NotificationsEmail.tsx
index fdb292d3..6daff08d 100644
--- a/src/components/Settings/Notifications/NotificationsEmail.tsx
+++ b/src/components/Settings/Notifications/NotificationsEmail.tsx
@@ -104,7 +104,7 @@ const NotificationsEmail = () => {
otherwise: Yup.string().nullable(),
})
.matches(
- /-----BEGIN PGP PRIVATE KEY BLOCK-----.+-----END PGP PRIVATE KEY BLOCK-----/s,
+ /-----BEGIN PGP PRIVATE KEY BLOCK-----.+-----END PGP PRIVATE KEY BLOCK-----/,
intl.formatMessage(messages.validationPgpPrivateKey)
),
pgpPassword: Yup.string().when('pgpPrivateKey', {
@@ -295,6 +295,10 @@ const NotificationsEmail = () => {
name="emailFrom"
type="text"
inputMode="email"
+ autoComplete="off"
+ data-1pignore="true"
+ data-lpignore="true"
+ data-bwignore="true"
/>
{errors.emailFrom &&
@@ -316,6 +320,10 @@ const NotificationsEmail = () => {
name="smtpHost"
type="text"
inputMode="url"
+ autoComplete="off"
+ data-1pignore="true"
+ data-lpignore="true"
+ data-bwignore="true"
/>
{errors.smtpHost &&
@@ -337,6 +345,10 @@ const NotificationsEmail = () => {
type="text"
inputMode="numeric"
className="short"
+ autoComplete="off"
+ data-1pignore="true"
+ data-lpignore="true"
+ data-bwignore="true"
/>
{errors.smtpPort &&
touched.smtpPort &&
@@ -390,7 +402,15 @@ const NotificationsEmail = () => {
@@ -400,12 +420,7 @@ const NotificationsEmail = () => {
@@ -430,6 +445,10 @@ const NotificationsEmail = () => {
type="textarea"
rows="10"
className="font-mono text-xs"
+ autoComplete="off"
+ data-1pignore="true"
+ data-lpignore="true"
+ data-bwignore="true"
/>
{errors.pgpPrivateKey &&
@@ -457,7 +476,10 @@ const NotificationsEmail = () => {
as="field"
id="pgpPassword"
name="pgpPassword"
- autoComplete="one-time-code"
+ autoComplete="off"
+ data-1pignore="true"
+ data-lpignore="true"
+ data-bwignore="true"
/>
{errors.pgpPassword &&
diff --git a/src/components/Settings/Notifications/NotificationsTelegram.tsx b/src/components/Settings/Notifications/NotificationsTelegram.tsx
index 6636c6b4..bfac39fe 100644
--- a/src/components/Settings/Notifications/NotificationsTelegram.tsx
+++ b/src/components/Settings/Notifications/NotificationsTelegram.tsx
@@ -245,7 +245,7 @@ const NotificationsTelegram = () => {
as="field"
id="botAPI"
name="botAPI"
- autoComplete="one-time-code"
+ type="text"
/>
{errors.botAPI &&
@@ -264,7 +264,15 @@ const NotificationsTelegram = () => {
-
+
{errors.botUsername &&
touched.botUsername &&
@@ -294,7 +302,15 @@ const NotificationsTelegram = () => {
-
+
{errors.chatId &&
touched.chatId &&
diff --git a/src/components/Settings/RadarrModal/index.tsx b/src/components/Settings/RadarrModal/index.tsx
index fbeb2dec..b1ae42f7 100644
--- a/src/components/Settings/RadarrModal/index.tsx
+++ b/src/components/Settings/RadarrModal/index.tsx
@@ -382,6 +382,10 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => {
id="name"
name="name"
type="text"
+ autoComplete="off"
+ data-1pignore="true"
+ data-lpignore="true"
+ data-bwignore="true"
onChange={(e: React.ChangeEvent
) => {
setIsValidated(false);
setFieldValue('name', e.target.value);
@@ -475,7 +479,6 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => {
as="field"
id="apiKey"
name="apiKey"
- autoComplete="one-time-code"
onChange={(e: React.ChangeEvent) => {
setIsValidated(false);
setFieldValue('apiKey', e.target.value);
diff --git a/src/components/Settings/SettingsLogs/index.tsx b/src/components/Settings/SettingsLogs/index.tsx
index ef7a396f..4fbcafc0 100644
--- a/src/components/Settings/SettingsLogs/index.tsx
+++ b/src/components/Settings/SettingsLogs/index.tsx
@@ -35,7 +35,7 @@ import useSWR from 'swr';
const messages = defineMessages('components.Settings.SettingsLogs', {
logs: 'Logs',
logsDescription:
- 'You can also view these logs directly via stdout, or in {appDataPath}/logs/overseerr.log.',
+ 'You can also view these logs directly via stdout, or in {appDataPath}/logs/jellyseerr.log.',
time: 'Timestamp',
level: 'Severity',
label: 'Label',
diff --git a/src/components/Settings/SettingsPlex.tsx b/src/components/Settings/SettingsPlex.tsx
index 780dc0ee..2a2c6167 100644
--- a/src/components/Settings/SettingsPlex.tsx
+++ b/src/components/Settings/SettingsPlex.tsx
@@ -872,6 +872,10 @@ const SettingsPlex = ({ onComplete }: SettingsPlexProps) => {
id="tautulliPort"
name="tautulliPort"
className="short"
+ autoComplete="off"
+ data-1pignore="true"
+ data-lpignore="true"
+ data-bwignore="true"
/>
{errors.tautulliPort &&
touched.tautulliPort &&
@@ -909,6 +913,10 @@ const SettingsPlex = ({ onComplete }: SettingsPlexProps) => {
inputMode="url"
id="tautulliUrlBase"
name="tautulliUrlBase"
+ autoComplete="off"
+ data-1pignore="true"
+ data-lpignore="true"
+ data-bwignore="true"
/>
{errors.tautulliUrlBase &&
@@ -929,7 +937,6 @@ const SettingsPlex = ({ onComplete }: SettingsPlexProps) => {
as="field"
id="tautulliApiKey"
name="tautulliApiKey"
- autoComplete="one-time-code"
/>
{errors.tautulliApiKey &&
@@ -950,6 +957,10 @@ const SettingsPlex = ({ onComplete }: SettingsPlexProps) => {
inputMode="url"
id="tautulliExternalUrl"
name="tautulliExternalUrl"
+ autoComplete="off"
+ data-1pignore="true"
+ data-lpignore="true"
+ data-bwignore="true"
/>
{errors.tautulliExternalUrl &&
diff --git a/src/components/Settings/SettingsServices.tsx b/src/components/Settings/SettingsServices.tsx
index fc058c0b..65180cac 100644
--- a/src/components/Settings/SettingsServices.tsx
+++ b/src/components/Settings/SettingsServices.tsx
@@ -119,6 +119,8 @@ const ServerInstance = ({