diff --git a/server/routes/discover.ts b/server/routes/discover.ts index b5fe22ea7..88d893cae 100644 --- a/server/routes/discover.ts +++ b/server/routes/discover.ts @@ -848,7 +848,7 @@ discoverRoutes.get, WatchlistResponse>( if (total) { return res.json({ page: page, - totalPages: total / itemsPerPage, + totalPages: Math.ceil(total / itemsPerPage), totalResults: total, results: result, }); @@ -865,7 +865,6 @@ discoverRoutes.get, WatchlistResponse>( } const plexTV = new PlexTvAPI(activeUser.plexToken); - const watchlist = await plexTV.getWatchlist({ offset }); return res.json({ diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index 046a1471d..9d9370cf2 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -717,29 +717,31 @@ router.get<{ id: string }, WatchlistResponse>( const user = await getRepository(User).findOneOrFail({ where: { id: Number(req.params.id) }, - select: { id: true, plexToken: true }, + select: ['id', 'plexToken'], }); - if (!user?.plexToken) { - if (user) { - const [result, total] = await getRepository(Watchlist).findAndCount({ - where: { requestedBy: { id: user?.id } }, - relations: { requestedBy: true }, - // loadRelationIds: true, - take: itemsPerPage, - skip: offset, + if (user) { + const [result, total] = await getRepository(Watchlist).findAndCount({ + where: { requestedBy: { id: user?.id } }, + relations: { + /*requestedBy: true,media:true*/ + }, + // loadRelationIds: true, + take: itemsPerPage, + skip: offset, + }); + if (total) { + return res.json({ + page: page, + totalPages: Math.ceil(total / itemsPerPage), + totalResults: total, + results: result, }); - if (total) { - return res.json({ - page: page, - totalPages: total / itemsPerPage, - totalResults: total, - results: result, - }); - } } + } - // We will just return an empty array if the user has no Plex token + // We will just return an empty array if the user has no Plex token + if (!user.plexToken) { return res.json({ page: 1, totalPages: 1, diff --git a/src/components/Discover/DiscoverWatchlist/index.tsx b/src/components/Discover/DiscoverWatchlist/index.tsx index 775da757a..a32fe5052 100644 --- a/src/components/Discover/DiscoverWatchlist/index.tsx +++ b/src/components/Discover/DiscoverWatchlist/index.tsx @@ -69,7 +69,7 @@ const DiscoverWatchlist = () => { title != null)} isEmpty={isEmpty} isLoading={ isLoadingInitialData || (isLoadingMore && (titles?.length ?? 0) > 0) diff --git a/src/components/UserProfile/index.tsx b/src/components/UserProfile/index.tsx index bb0f7504e..1dd466920 100644 --- a/src/components/UserProfile/index.tsx +++ b/src/components/UserProfile/index.tsx @@ -34,6 +34,7 @@ const messages = defineMessages({ seriesrequest: 'Series Requests', recentlywatched: 'Recently Watched', plexwatchlist: 'Plex Watchlist', + localWatchlist: "{username}'s Watchlist", emptywatchlist: 'Media added to your Plex Watchlist will appear here.', }); @@ -78,17 +79,17 @@ const UserProfile = () => { ? `/api/v1/user/${user.id}/watch_data` : null ); + const { data: watchlistItems, error: watchlistError } = useSWR( - user?.userType === UserType.PLEX && - (user.id === currentUser?.id || - currentHasPermission( - [Permission.MANAGE_REQUESTS, Permission.WATCHLIST_VIEW], - { - type: 'or', - } - )) - ? `/api/v1/user/${user.id}/watchlist` + user?.id === currentUser?.id || + currentHasPermission( + [Permission.MANAGE_REQUESTS, Permission.WATCHLIST_VIEW], + { + type: 'or', + } + ) + ? `/api/v1/user/${user?.id}/watchlist` : null, { revalidateOnMount: true, @@ -117,6 +118,13 @@ const UserProfile = () => { return ; } + const watchlistSliderTitle = intl.formatMessage( + user.userType === UserType.PLEX + ? messages.plexwatchlist + : messages.localWatchlist, + { username: user.displayName } + ); + return ( <> @@ -309,12 +317,11 @@ const UserProfile = () => { /> )} - {user.userType === UserType.PLEX && - (user.id === currentUser?.id || - currentHasPermission( - [Permission.MANAGE_REQUESTS, Permission.WATCHLIST_VIEW], - { type: 'or' } - )) && + {(user.id === currentUser?.id || + currentHasPermission( + [Permission.MANAGE_REQUESTS, Permission.WATCHLIST_VIEW], + { type: 'or' } + )) && (!watchlistItems || !!watchlistItems.results.length || (user.id === currentUser?.id && @@ -327,11 +334,11 @@ const UserProfile = () => { href={ user.id === currentUser?.id ? '/profile/watchlist' - : `/users/${user?.id}/watchlist` + : `/users/${user.id}/watchlist` } > - {intl.formatMessage(messages.plexwatchlist)} + {watchlistSliderTitle} diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 837ac6001..96d5926c7 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -94,7 +94,6 @@ "components.Discover.emptywatchlist": "Media added to your Plex Watchlist will appear here.", "components.Discover.moviegenres": "Movie Genres", "components.Discover.networks": "Networks", - "components.Discover.noRequests": "No requests.", "components.Discover.plexwatchlist": "Your Watchlist", "components.Discover.popularmovies": "Popular Movies", "components.Discover.populartv": "Popular Series", @@ -200,9 +199,9 @@ "components.LanguageSelector.originalLanguageDefault": "All Languages", "components.Layout.LanguagePicker.displaylanguage": "Display Language", "components.Layout.SearchInput.searchPlaceholder": "Search Movies & TV", - "components.Layout.Sidebar.dashboard": "Discover", "components.Layout.Sidebar.browsemovies": "Movies", "components.Layout.Sidebar.browsetv": "Series", + "components.Layout.Sidebar.dashboard": "Discover", "components.Layout.Sidebar.issues": "Issues", "components.Layout.Sidebar.requests": "Requests", "components.Layout.Sidebar.settings": "Settings", @@ -218,11 +217,12 @@ "components.Layout.UserWarnings.passwordRequired": "A password is required.", "components.Layout.VersionStatus.commitsbehind": "{commitsBehind} {commitsBehind, plural, one {commit} other {commits}} behind", "components.Layout.VersionStatus.outofdate": "Out of Date", - "components.Layout.VersionStatus.streamdevelop": "Jellyseerr Develop", - "components.Layout.VersionStatus.streamstable": "Jellyseerr Stable", + "components.Layout.VersionStatus.streamdevelop": "Overseerr Develop", + "components.Layout.VersionStatus.streamstable": "Overseerr Stable", "components.Login.credentialerror": "The username or password is incorrect.", "components.Login.description": "Since this is your first time logging into {applicationName}, you are required to add a valid email address.", "components.Login.email": "Email Address", + "components.Login.emailtooltip": "Address does not need to be associated with your {mediaServerName} instance.", "components.Login.forgotpassword": "Forgot Password?", "components.Login.host": "{mediaServerName} URL", "components.Login.initialsignin": "Connect", @@ -582,7 +582,7 @@ "components.Settings.Notifications.NotificationsPushbullet.validationAccessTokenRequired": "You must provide an access token", "components.Settings.Notifications.NotificationsPushbullet.validationTypes": "You must select at least one notification type", "components.Settings.Notifications.NotificationsPushover.accessToken": "Application API Token", - "components.Settings.Notifications.NotificationsPushover.accessTokenTip": "Register an application for use with Jellyseerr", + "components.Settings.Notifications.NotificationsPushover.accessTokenTip": "Register an application for use with Overseerr", "components.Settings.Notifications.NotificationsPushover.agentenabled": "Enable Agent", "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Device Default", "components.Settings.Notifications.NotificationsPushover.pushoversettingsfailed": "Pushover notification settings failed to save.", @@ -607,7 +607,7 @@ "components.Settings.Notifications.NotificationsSlack.webhookUrl": "Webhook URL", "components.Settings.Notifications.NotificationsSlack.webhookUrlTip": "Create an Incoming Webhook integration", "components.Settings.Notifications.NotificationsWebPush.agentenabled": "Enable Agent", - "components.Settings.Notifications.NotificationsWebPush.httpsRequirement": "In order to receive web push notifications, Jellyseerr must be served over HTTPS.", + "components.Settings.Notifications.NotificationsWebPush.httpsRequirement": "In order to receive web push notifications, Overseerr must be served over HTTPS.", "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestFailed": "Web push test notification failed to send.", "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSending": "Sending web push test notification…", "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSuccess": "Web push test notification sent!", @@ -633,7 +633,7 @@ "components.Settings.Notifications.authPass": "SMTP Password", "components.Settings.Notifications.authUser": "SMTP Username", "components.Settings.Notifications.botAPI": "Bot Authorization Token", - "components.Settings.Notifications.botApiTip": "Create a bot for use with Jellyseerr", + "components.Settings.Notifications.botApiTip": "Create a bot for use with Overseerr", "components.Settings.Notifications.botAvatarUrl": "Bot Avatar URL", "components.Settings.Notifications.botUsername": "Bot Username", "components.Settings.Notifications.botUsernameTip": "Allow users to also start a chat with your bot and configure their own notifications", @@ -748,10 +748,10 @@ "components.Settings.SettingsAbout.githubdiscussions": "GitHub Discussions", "components.Settings.SettingsAbout.helppaycoffee": "Help Pay for Coffee", "components.Settings.SettingsAbout.outofdate": "Out of Date", - "components.Settings.SettingsAbout.overseerrinformation": "About Jellyseerr", + "components.Settings.SettingsAbout.overseerrinformation": "About Overseerr", "components.Settings.SettingsAbout.preferredmethod": "Preferred", - "components.Settings.SettingsAbout.runningDevelop": "You are running the develop branch of Jellyseerr, which is only recommended for those contributing to development or assisting with bleeding-edge testing.", - "components.Settings.SettingsAbout.supportoverseerr": "Support Jellyseerr", + "components.Settings.SettingsAbout.runningDevelop": "You are running the develop branch of Overseerr, which is only recommended for those contributing to development or assisting with bleeding-edge testing.", + "components.Settings.SettingsAbout.supportoverseerr": "Support Overseerr", "components.Settings.SettingsAbout.timezone": "Time Zone", "components.Settings.SettingsAbout.totalmedia": "Total Media", "components.Settings.SettingsAbout.totalrequests": "Total Requests", @@ -759,7 +759,7 @@ "components.Settings.SettingsAbout.version": "Version", "components.Settings.SettingsJobsCache.availability-sync": "Media Availability Sync", "components.Settings.SettingsJobsCache.cache": "Cache", - "components.Settings.SettingsJobsCache.cacheDescription": "Jellyseerr caches requests to external API endpoints to optimize performance and avoid making unnecessary API calls.", + "components.Settings.SettingsJobsCache.cacheDescription": "Overseerr caches requests to external API endpoints to optimize performance and avoid making unnecessary API calls.", "components.Settings.SettingsJobsCache.cacheflushed": "{cachename} cache flushed.", "components.Settings.SettingsJobsCache.cachehits": "Hits", "components.Settings.SettingsJobsCache.cachekeys": "Total Keys", @@ -780,7 +780,7 @@ "components.Settings.SettingsJobsCache.flushcache": "Flush Cache", "components.Settings.SettingsJobsCache.image-cache-cleanup": "Image Cache Cleanup", "components.Settings.SettingsJobsCache.imagecache": "Image Cache", - "components.Settings.SettingsJobsCache.imagecacheDescription": "When enabled in settings, Jellyseerr will proxy and cache images from pre-configured external sources. Cached images are saved into your config folder. You can find the files in {appDataPath}/cache/images.", + "components.Settings.SettingsJobsCache.imagecacheDescription": "When enabled in settings, Overseerr will proxy and cache images from pre-configured external sources. Cached images are saved into your config folder. You can find the files in {appDataPath}/cache/images.", "components.Settings.SettingsJobsCache.imagecachecount": "Images Cached", "components.Settings.SettingsJobsCache.imagecachesize": "Total Cache Size", "components.Settings.SettingsJobsCache.jellyfin-full-scan": "Jellyfin Full Library Scan", @@ -790,7 +790,7 @@ "components.Settings.SettingsJobsCache.jobcancelled": "{jobname} canceled.", "components.Settings.SettingsJobsCache.jobname": "Job Name", "components.Settings.SettingsJobsCache.jobs": "Jobs", - "components.Settings.SettingsJobsCache.jobsDescription": "Jellyseerr 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.", + "components.Settings.SettingsJobsCache.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.", "components.Settings.SettingsJobsCache.jobsandcache": "Jobs & Cache", "components.Settings.SettingsJobsCache.jobstarted": "{jobname} started.", "components.Settings.SettingsJobsCache.jobtype": "Type", @@ -831,7 +831,7 @@ "components.Settings.SettingsMain.csrfProtectionTip": "Set external API access to read-only (requires HTTPS)", "components.Settings.SettingsMain.general": "General", "components.Settings.SettingsMain.generalsettings": "General Settings", - "components.Settings.SettingsMain.generalsettingsDescription": "Configure global and default settings for Jellyseerr.", + "components.Settings.SettingsMain.generalsettingsDescription": "Configure global and default settings for Overseerr.", "components.Settings.SettingsMain.hideAvailable": "Hide Available Media", "components.Settings.SettingsMain.locale": "Display Language", "components.Settings.SettingsMain.originallanguage": "Discover Language", @@ -844,7 +844,7 @@ "components.Settings.SettingsMain.toastSettingsFailure": "Something went wrong while saving settings.", "components.Settings.SettingsMain.toastSettingsSuccess": "Settings saved successfully!", "components.Settings.SettingsMain.trustProxy": "Enable Proxy Support", - "components.Settings.SettingsMain.trustProxyTip": "Allow Jellyseerr to correctly register client IP addresses behind a proxy", + "components.Settings.SettingsMain.trustProxyTip": "Allow Overseerr to correctly register client IP addresses behind a proxy", "components.Settings.SettingsMain.validationApplicationTitle": "You must provide an application title", "components.Settings.SettingsMain.validationApplicationUrl": "You must provide a valid URL", "components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "URL must not end in a trailing slash", @@ -946,7 +946,7 @@ "components.Settings.jellyfinsettingsDescription": "Configure the settings for your {mediaServerName} server. {mediaServerName} scans your {mediaServerName} libraries to see what content is available.", "components.Settings.librariesRemaining": "Libraries Remaining: {count}", "components.Settings.manualscan": "Manual Library Scan", - "components.Settings.manualscanDescription": "Normally, this will only be run once every 24 hours. Jellyseerr 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!", + "components.Settings.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!", "components.Settings.manualscanDescriptionJellyfin": "Normally, this will only be run once every 24 hours. Jellyseerr will check your {mediaServerName} server's recently added more aggressively. If this is your first time configuring Jellyseerr, a one-time full manual library scan is recommended!", "components.Settings.manualscanJellyfin": "Manual Library Scan", "components.Settings.mediaTypeMovie": "movie", @@ -969,12 +969,12 @@ "components.Settings.notrunning": "Not Running", "components.Settings.plex": "Plex", "components.Settings.plexlibraries": "Plex Libraries", - "components.Settings.plexlibrariesDescription": "The libraries Jellyseerr scans for titles. Set up and save your Plex connection settings, then click the button below if no libraries are listed.", + "components.Settings.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.", "components.Settings.plexsettings": "Plex Settings", - "components.Settings.plexsettingsDescription": "Configure the settings for your Plex server. Jellyseerr scans your Plex libraries to determine content availability.", + "components.Settings.plexsettingsDescription": "Configure the settings for your Plex server. Overseerr scans your Plex libraries to determine content availability.", "components.Settings.port": "Port", "components.Settings.radarrsettings": "Radarr Settings", - "components.Settings.restartrequiredTooltip": "Jellyseerr must be restarted for changes to this setting to take effect", + "components.Settings.restartrequiredTooltip": "Overseerr must be restarted for changes to this setting to take effect", "components.Settings.save": "Save Changes", "components.Settings.saving": "Saving…", "components.Settings.scan": "Sync Libraries", @@ -996,7 +996,7 @@ "components.Settings.syncing": "Syncing", "components.Settings.tautulliApiKey": "API Key", "components.Settings.tautulliSettings": "Tautulli Settings", - "components.Settings.tautulliSettingsDescription": "Optionally configure the settings for your Tautulli server. Jellyseerr fetches watch history data for your Plex media from Tautulli.", + "components.Settings.tautulliSettingsDescription": "Optionally configure the settings for your Tautulli server. Overseerr fetches watch history data for your Plex media from Tautulli.", "components.Settings.timeout": "Timeout", "components.Settings.toastPlexConnecting": "Attempting to connect to Plex…", "components.Settings.toastPlexConnectingFailure": "Failed to connect to Plex.", @@ -1042,10 +1042,15 @@ "components.StatusChecker.reloadApp": "Reload {applicationTitle}", "components.StatusChecker.restartRequired": "Server Restart Required", "components.StatusChecker.restartRequiredDescription": "Please restart the server to apply the updated settings.", + "components.TitleCard.addToWatchList": "Add to watchlist", "components.TitleCard.cleardata": "Clear Data", "components.TitleCard.mediaerror": "{mediaType} Not Found", "components.TitleCard.tmdbid": "TMDB ID", "components.TitleCard.tvdbid": "TheTVDB ID", + "components.TitleCard.watchlistCancel": "watchlist for {title} canceled.", + "components.TitleCard.watchlistDeleted": "{title} Removed from watchlist successfully!", + "components.TitleCard.watchlistError": "Something went wrong try again.", + "components.TitleCard.watchlistSuccess": "{title} added to watchlist successfully!", "components.TvDetails.Season.noepisodes": "Episode list unavailable.", "components.TvDetails.Season.somethingwentwrong": "Something went wrong while retrieving season data.", "components.TvDetails.TvCast.fullseriescast": "Full Series Cast", @@ -1229,6 +1234,7 @@ "components.UserProfile.UserSettings.unauthorizedDescription": "You do not have permission to modify this user's settings.", "components.UserProfile.emptywatchlist": "Media added to your Plex Watchlist will appear here.", "components.UserProfile.limit": "{remaining} of {limit}", + "components.UserProfile.localWatchlist": "{username}'s Watchlist", "components.UserProfile.movierequests": "Movie Requests", "components.UserProfile.pastdays": "{type} (past {days} days)", "components.UserProfile.plexwatchlist": "Plex Watchlist", @@ -1238,11 +1244,6 @@ "components.UserProfile.seriesrequest": "Series Requests", "components.UserProfile.totalrequests": "Total Requests", "components.UserProfile.unlimited": "Unlimited", - "components.TitleCard.addToWatchList": "Add to watchlist", - "components.TitleCard.watchlistCancel": "watchlist for {title} canceled.", - "components.TitleCard.watchlistDeleted": "{title} Removed from watchlist successfully!", - "components.TitleCard.watchlistError": "Something went wrong try again.", - "components.TitleCard.watchlistSuccess": "{title} added to watchlist successfully!", "i18n.advanced": "Advanced", "i18n.all": "All", "i18n.approve": "Approve",