diff --git a/docs/extending-overseerr/reverse-proxy.md b/docs/extending-overseerr/reverse-proxy.md index 5aa6fd462..84752f7c2 100644 --- a/docs/extending-overseerr/reverse-proxy.md +++ b/docs/extending-overseerr/reverse-proxy.md @@ -138,6 +138,7 @@ location ^~ /overseerr { sub_filter 'href="/"' 'href="/$app"'; sub_filter 'href="/login"' 'href="/$app/login"'; sub_filter 'href:"/"' 'href:"/$app"'; + sub_filter '\/_next' '\/$app\/_next'; sub_filter '/_next' '/$app/_next'; sub_filter '/api/v1' '/$app/api/v1'; sub_filter '/login/plex/loading' '/$app/login/plex/loading'; diff --git a/server/api/plexapi.ts b/server/api/plexapi.ts index 90aae4852..03246c810 100644 --- a/server/api/plexapi.ts +++ b/server/api/plexapi.ts @@ -232,6 +232,10 @@ class PlexAPI { uri: `/library/sections/${id}/all?sort=addedAt%3Adesc&addedAt>>=${Math.floor( options.addedAt / 1000 )}`, + extraHeaders: { + 'X-Plex-Container-Start': `0`, + 'X-Plex-Container-Size': `500`, + }, }); return response.MediaContainer.Metadata; diff --git a/server/api/themoviedb/interfaces.ts b/server/api/themoviedb/interfaces.ts index 2282fe052..6d005dc94 100644 --- a/server/api/themoviedb/interfaces.ts +++ b/server/api/themoviedb/interfaces.ts @@ -191,7 +191,7 @@ export interface TmdbVideo { export interface TmdbTvEpisodeResult { id: number; - air_date: string; + air_date: string | null; episode_number: number; name: string; overview: string; @@ -372,7 +372,8 @@ export interface TmdbPersonCombinedCredits { crew: TmdbPersonCreditCrew[]; } -export interface TmdbSeasonWithEpisodes extends TmdbTvSeasonResult { +export interface TmdbSeasonWithEpisodes + extends Omit { episodes: TmdbTvEpisodeResult[]; external_ids: TmdbExternalIds; } diff --git a/server/job/schedule.ts b/server/job/schedule.ts index e21908052..356c475e5 100644 --- a/server/job/schedule.ts +++ b/server/job/schedule.ts @@ -111,7 +111,7 @@ export const startJobs = (): void => { id: 'plex-watchlist-sync', name: 'Plex Watchlist Sync', type: 'process', - interval: 'long', + interval: 'short', cronSchedule: jobs['plex-watchlist-sync'].schedule, job: schedule.scheduleJob(jobs['plex-watchlist-sync'].schedule, () => { logger.info('Starting scheduled job: Plex Watchlist Sync', { diff --git a/server/models/Tv.ts b/server/models/Tv.ts index 7f809cbf4..24362b504 100644 --- a/server/models/Tv.ts +++ b/server/models/Tv.ts @@ -29,7 +29,7 @@ import type { Video } from './Movie'; interface Episode { id: number; name: string; - airDate: string; + airDate: string | null; episodeNumber: number; overview: string; productionCode: string; @@ -50,7 +50,7 @@ interface Season { seasonNumber: number; } -export interface SeasonWithEpisodes extends Season { +export interface SeasonWithEpisodes extends Omit { episodes: Episode[]; externalIds: ExternalIds; } @@ -141,7 +141,6 @@ export const mapSeasonWithEpisodes = ( season: TmdbSeasonWithEpisodes ): SeasonWithEpisodes => ({ airDate: season.air_date, - episodeCount: season.episode_count, episodes: season.episodes.map(mapEpisodeResult), externalIds: mapExternalIds(season.external_ids), id: season.id, diff --git a/server/routes/person.ts b/server/routes/person.ts index 7f5d62236..009d62af7 100644 --- a/server/routes/person.ts +++ b/server/routes/person.ts @@ -1,5 +1,7 @@ import TheMovieDb from '@server/api/themoviedb'; +import { MediaStatus } from '@server/constants/media'; import Media from '@server/entity/Media'; +import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import { mapCastCredits, @@ -34,6 +36,7 @@ personRoutes.get('/:id', async (req, res, next) => { personRoutes.get('/:id/combined_credits', async (req, res, next) => { const tmdb = new TheMovieDb(); + const settings = getSettings(); try { const combinedCredits = await tmdb.getPersonCombinedCredits({ @@ -41,14 +44,30 @@ personRoutes.get('/:id/combined_credits', async (req, res, next) => { language: req.locale ?? (req.query.language as string), }); - const castMedia = await Media.getRelatedMedia( + let castMedia = await Media.getRelatedMedia( combinedCredits.cast.map((result) => result.id) ); - const crewMedia = await Media.getRelatedMedia( + let crewMedia = await Media.getRelatedMedia( combinedCredits.crew.map((result) => result.id) ); + if (settings.main.hideAvailable) { + castMedia = castMedia.filter( + (media) => + (media.mediaType === 'movie' || media.mediaType === 'tv') && + media.status !== MediaStatus.AVAILABLE && + media.status !== MediaStatus.PARTIALLY_AVAILABLE + ); + + crewMedia = crewMedia.filter( + (media) => + (media.mediaType === 'movie' || media.mediaType === 'tv') && + media.status !== MediaStatus.AVAILABLE && + media.status !== MediaStatus.PARTIALLY_AVAILABLE + ); + } + return res.status(200).json({ cast: combinedCredits.cast .map((result) => diff --git a/src/components/Common/Button/index.tsx b/src/components/Common/Button/index.tsx index d3f96ae98..7dc4e637d 100644 --- a/src/components/Common/Button/index.tsx +++ b/src/components/Common/Button/index.tsx @@ -61,7 +61,7 @@ function Button

( break; case 'warning': buttonStyle.push( - 'text-white border border-yellow-500 backdrop-blur bg-yellow-500 bg-opacity-80 hover:bg-opacity-100 hover:border-yellow-400 focus:border-yellow-700 focus:ring-yellow active:bg-opacity-100 active:border-yellow-700' + 'text-white border border-yellow-500 bg-yellow-500 bg-opacity-80 hover:bg-opacity-100 hover:border-yellow-400 focus:border-yellow-700 focus:ring-yellow active:bg-opacity-100 active:border-yellow-700' ); break; case 'success': diff --git a/src/components/Common/StatusBadgeMini/index.tsx b/src/components/Common/StatusBadgeMini/index.tsx new file mode 100644 index 000000000..0653c7d80 --- /dev/null +++ b/src/components/Common/StatusBadgeMini/index.tsx @@ -0,0 +1,45 @@ +import { + BellIcon, + CheckIcon, + ClockIcon, + MinusSmIcon, +} from '@heroicons/react/solid'; +import { MediaStatus } from '@server/constants/media'; + +interface StatusBadgeMiniProps { + status: MediaStatus; + is4k?: boolean; +} + +const StatusBadgeMini = ({ status, is4k = false }: StatusBadgeMiniProps) => { + const badgeStyle = ['w-5 rounded-full p-0.5 text-white ring-1']; + let indicatorIcon: React.ReactNode; + + switch (status) { + case MediaStatus.PROCESSING: + badgeStyle.push('bg-indigo-500 ring-indigo-400'); + indicatorIcon = ; + break; + case MediaStatus.AVAILABLE: + badgeStyle.push('bg-green-500 ring-green-400'); + indicatorIcon = ; + break; + case MediaStatus.PENDING: + badgeStyle.push('bg-yellow-500 ring-yellow-400'); + indicatorIcon = ; + break; + case MediaStatus.PARTIALLY_AVAILABLE: + badgeStyle.push('bg-green-500 ring-green-400'); + indicatorIcon = ; + break; + } + + return ( +

+
{indicatorIcon}
+ {is4k && 4K} +
+ ); +}; + +export default StatusBadgeMini; diff --git a/src/components/Common/Tooltip/index.tsx b/src/components/Common/Tooltip/index.tsx index b0c4fb2eb..82bc7a7a9 100644 --- a/src/components/Common/Tooltip/index.tsx +++ b/src/components/Common/Tooltip/index.tsx @@ -20,7 +20,7 @@ const Tooltip = ({ children, content, tooltipConfig }: TooltipProps) => { return ( <> {React.cloneElement(children, { ref: setTriggerRef })} - {visible && ( + {visible && content && (
{ > <>
-
+
), instructionsPullToRefresh: ReactDOMServer.renderToString(
), instructionsReleaseToRefresh: ReactDOMServer.renderToString(
), instructionsRefreshing: ReactDOMServer.renderToString(
), - distReload: 55, + distReload: 60, }); return () => { PR.destroyAll(); diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx index e59f164f3..9ccbcde02 100644 --- a/src/components/RequestCard/index.tsx +++ b/src/components/RequestCard/index.tsx @@ -357,20 +357,13 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => { : request.seasons.length, })} - {title.seasons.filter((season) => season.seasonNumber !== 0) - .length === request.seasons.length ? ( - - {intl.formatMessage(globalMessages.all)} - - ) : ( -
- {request.seasons.map((season) => ( - - {season.seasonNumber} - - ))} -
- )} +
+ {request.seasons.map((season) => ( + + {season.seasonNumber} + + ))} +
)}
diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx index 877d85396..6c232dc89 100644 --- a/src/components/RequestList/RequestItem/index.tsx +++ b/src/components/RequestList/RequestItem/index.tsx @@ -420,20 +420,13 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => { : request.seasons.length, })} - {title.seasons.filter((season) => season.seasonNumber !== 0) - .length === request.seasons.length ? ( - - {intl.formatMessage(globalMessages.all)} - - ) : ( -
- {request.seasons.map((season) => ( - - {season.seasonNumber} - - ))} -
- )} +
+ {request.seasons.map((season) => ( + + {season.seasonNumber} + + ))} +
)}
diff --git a/src/components/Settings/SettingsMain.tsx b/src/components/Settings/SettingsMain.tsx index 3ac7b09b5..ef0810f5e 100644 --- a/src/components/Settings/SettingsMain.tsx +++ b/src/components/Settings/SettingsMain.tsx @@ -309,7 +309,7 @@ const SettingsMain = () => {
-