From a0ec992028093257e9fa043622e236014f02dea3 Mon Sep 17 00:00:00 2001 From: fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Sat, 18 Nov 2023 06:03:32 +0500 Subject: [PATCH 1/6] fix(watchlist): added missing prop for watchlist item removal button in watchlist page This fix resolves a Watchlist page bug where the isAddedToWatchlist prop was missing. Without this prop, the removal button for watchlist items was absent. In this fix, the isAddedToWatchlist prop is re-added and set to true, allowing users to remove items from their local watchlist directly on the Watchlist page. --- src/components/Common/ListView/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Common/ListView/index.tsx b/src/components/Common/ListView/index.tsx index 907cc8e24..47140c5db 100644 --- a/src/components/Common/ListView/index.tsx +++ b/src/components/Common/ListView/index.tsx @@ -46,6 +46,7 @@ const ListView = ({ id={title.tmdbId} tmdbId={title.tmdbId} type={title.mediaType} + isAddedToWatchlist={true} canExpand /> From b85d7f37b931735ca2ad955dccb6599bf445fc73 Mon Sep 17 00:00:00 2001 From: fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:25:34 +0500 Subject: [PATCH 2/6] fix: ensure watchlist updates are immediately reflected This fix addresses an issue on the Watchlist page where changes to the watchlist were not immediately reflected. Previously, after removing an item from the watchlist, the update required a full page reload or revalidating upon focusing the window or tab. With this fix, the watchlist now correctly mutates and updates in real-time, providing a seamless user experience. --- src/components/Common/ListView/index.tsx | 3 +++ src/components/Discover/DiscoverWatchlist/index.tsx | 2 ++ src/components/TitleCard/TmdbTitleCard.tsx | 4 ++++ src/components/TitleCard/index.tsx | 5 +++++ src/hooks/useDiscover.ts | 4 +++- 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/components/Common/ListView/index.tsx b/src/components/Common/ListView/index.tsx index 47140c5db..46c946ae2 100644 --- a/src/components/Common/ListView/index.tsx +++ b/src/components/Common/ListView/index.tsx @@ -19,6 +19,7 @@ type ListViewProps = { isLoading?: boolean; isReachingEnd?: boolean; onScrollBottom: () => void; + mutateParent?: () => void; }; const ListView = ({ @@ -28,6 +29,7 @@ const ListView = ({ onScrollBottom, isReachingEnd, plexItems, + mutateParent, }: ListViewProps) => { const intl = useIntl(); useVerticalScroll(onScrollBottom, !isLoading && !isEmpty && !isReachingEnd); @@ -48,6 +50,7 @@ const ListView = ({ type={title.mediaType} isAddedToWatchlist={true} canExpand + mutateParent={mutateParent} /> ); diff --git a/src/components/Discover/DiscoverWatchlist/index.tsx b/src/components/Discover/DiscoverWatchlist/index.tsx index 775da757a..61d4f5c44 100644 --- a/src/components/Discover/DiscoverWatchlist/index.tsx +++ b/src/components/Discover/DiscoverWatchlist/index.tsx @@ -30,6 +30,7 @@ const DiscoverWatchlist = () => { titles, fetchMore, error, + mutate, } = useDiscover( `/api/v1/${ router.pathname.startsWith('/profile') @@ -76,6 +77,7 @@ const DiscoverWatchlist = () => { } isReachingEnd={isReachingEnd} onScrollBottom={fetchMore} + mutateParent={mutate} /> ); diff --git a/src/components/TitleCard/TmdbTitleCard.tsx b/src/components/TitleCard/TmdbTitleCard.tsx index 0764f3aa1..825e52ccf 100644 --- a/src/components/TitleCard/TmdbTitleCard.tsx +++ b/src/components/TitleCard/TmdbTitleCard.tsx @@ -12,6 +12,7 @@ export interface TmdbTitleCardProps { type: 'movie' | 'tv'; canExpand?: boolean; isAddedToWatchlist?: boolean; + mutateParent?: () => void; } const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => { @@ -25,6 +26,7 @@ const TmdbTitleCard = ({ type, canExpand, isAddedToWatchlist = false, + mutateParent, }: TmdbTitleCardProps) => { const { hasPermission } = useUser(); @@ -71,6 +73,7 @@ const TmdbTitleCard = ({ year={title.releaseDate} mediaType={'movie'} canExpand={canExpand} + mutateParent={mutateParent} /> ) : ( ); }; diff --git a/src/components/TitleCard/index.tsx b/src/components/TitleCard/index.tsx index 410036cca..30a62c16e 100644 --- a/src/components/TitleCard/index.tsx +++ b/src/components/TitleCard/index.tsx @@ -38,6 +38,7 @@ interface TitleCardProps { canExpand?: boolean; inProgress?: boolean; isAddedToWatchlist?: number | boolean; + mutateParent?: () => void; } const messages = defineMessages({ @@ -61,6 +62,7 @@ const TitleCard = ({ isAddedToWatchlist = false, inProgress = false, canExpand = false, + mutateParent, }: TitleCardProps) => { const isTouch = useIsTouch(); const intl = useIntl(); @@ -148,6 +150,9 @@ const TitleCard = ({ } finally { setIsUpdating(false); mutate('/api/v1/discover/watchlist'); + if (mutateParent) { + mutateParent(); + } setToggleWatchlist((prevState) => !prevState); } }; diff --git a/src/hooks/useDiscover.ts b/src/hooks/useDiscover.ts index f9aff8e29..2a2acd02f 100644 --- a/src/hooks/useDiscover.ts +++ b/src/hooks/useDiscover.ts @@ -25,6 +25,7 @@ interface DiscoverResult { error: unknown; titles: T[]; firstResultData?: BaseSearchResult & S; + mutate?: () => void; } const extraEncodes: [RegExp, string][] = [ @@ -54,7 +55,7 @@ const useDiscover = < { hideAvailable = true } = {} ): DiscoverResult => { const settings = useSettings(); - const { data, error, size, setSize, isValidating } = useSWRInfinite< + const { data, error, size, setSize, isValidating, mutate } = useSWRInfinite< BaseSearchResult & S >( (pageIndex: number, previousPageData) => { @@ -119,6 +120,7 @@ const useDiscover = < error, titles, firstResultData: data?.[0], + mutate, }; }; From f564cddff4525ccebffbf304672d49c57aefe635 Mon Sep 17 00:00:00 2001 From: fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Sun, 19 Nov 2023 18:08:06 +0500 Subject: [PATCH 3/6] fix: correct width issue in datepicker of filterSliderOver This commit addresses a rendering issue with the date picker component. The problem was traced back to a misconfiguration in the tailwindcss settings, resulting in an incorrect width for the popup. fix #415 --- tailwind.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tailwind.config.js b/tailwind.config.js index e94cb96c5..b8b70fd54 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -3,7 +3,6 @@ const defaultTheme = require('tailwindcss/defaultTheme'); /** @type {import('tailwindcss').Config} */ module.exports = { - important: true, mode: 'jit', content: [ './node_modules/react-tailwindcss-datepicker-sct/dist/index.esm.js', From 91f97f96ab12381328a118d3f813e288899aca99 Mon Sep 17 00:00:00 2001 From: fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Mon, 27 Nov 2023 11:09:45 +0500 Subject: [PATCH 4/6] refactor: jellyfin scan jobs moved from server/jobs to server/libs/scanners --- server/job/schedule.ts | 25 +++++++++++-------- .../scanners/jellyfin}/index.ts | 6 ++--- server/routes/settings/index.ts | 10 ++++---- 3 files changed, 22 insertions(+), 19 deletions(-) rename server/{job/jellyfinsync => lib/scanners/jellyfin}/index.ts (99%) diff --git a/server/job/schedule.ts b/server/job/schedule.ts index 15bf033ed..f65cdebbd 100644 --- a/server/job/schedule.ts +++ b/server/job/schedule.ts @@ -1,6 +1,10 @@ import { MediaServerType } from '@server/constants/server'; import downloadTracker from '@server/lib/downloadtracker'; import ImageProxy from '@server/lib/imageproxy'; +import { + jellyfinFullScanner, + jellyfinRecentScanner, +} from '@server/lib/scanners/jellyfin'; import { plexFullScanner, plexRecentScanner } from '@server/lib/scanners/plex'; import { radarrScanner } from '@server/lib/scanners/radarr'; import { sonarrScanner } from '@server/lib/scanners/sonarr'; @@ -10,7 +14,6 @@ import watchlistSync from '@server/lib/watchlistsync'; import logger from '@server/logger'; import random from 'lodash/random'; import schedule from 'node-schedule'; -import { jobJellyfinFullSync, jobJellyfinRecentSync } from './jellyfinsync'; interface ScheduledJob { id: JobId; @@ -73,38 +76,38 @@ export const startJobs = (): void => { // Run recently added jellyfin sync every 5 minutes scheduledJobs.push({ id: 'jellyfin-recently-added-scan', - name: 'Jellyfin Recently Added Sync', + name: 'Jellyfin Recently Added Scan', type: 'process', interval: 'minutes', cronSchedule: jobs['jellyfin-recently-added-scan'].schedule, job: schedule.scheduleJob( jobs['jellyfin-recently-added-scan'].schedule, () => { - logger.info('Starting scheduled job: Jellyfin Recently Added Sync', { + logger.info('Starting scheduled job: Jellyfin Recently Added Scan', { label: 'Jobs', }); - jobJellyfinRecentSync.run(); + jellyfinRecentScanner.run(); } ), - running: () => jobJellyfinRecentSync.status().running, - cancelFn: () => jobJellyfinRecentSync.cancel(), + running: () => jellyfinRecentScanner.status().running, + cancelFn: () => jellyfinRecentScanner.cancel(), }); // Run full jellyfin sync every 24 hours scheduledJobs.push({ id: 'jellyfin-full-scan', - name: 'Jellyfin Full Library Sync', + name: 'Jellyfin Full Library Scan', type: 'process', interval: 'hours', cronSchedule: jobs['jellyfin-full-scan'].schedule, job: schedule.scheduleJob(jobs['jellyfin-full-scan'].schedule, () => { - logger.info('Starting scheduled job: Jellyfin Full Sync', { + logger.info('Starting scheduled job: Jellyfin Full Scan', { label: 'Jobs', }); - jobJellyfinFullSync.run(); + jellyfinFullScanner.run(); }), - running: () => jobJellyfinFullSync.status().running, - cancelFn: () => jobJellyfinFullSync.cancel(), + running: () => jellyfinFullScanner.status().running, + cancelFn: () => jellyfinFullScanner.cancel(), }); } diff --git a/server/job/jellyfinsync/index.ts b/server/lib/scanners/jellyfin/index.ts similarity index 99% rename from server/job/jellyfinsync/index.ts rename to server/lib/scanners/jellyfin/index.ts index b263ec6e4..c231ec0dc 100644 --- a/server/job/jellyfinsync/index.ts +++ b/server/lib/scanners/jellyfin/index.ts @@ -26,7 +26,7 @@ interface SyncStatus { libraries: Library[]; } -class JobJellyfinSync { +class JellyfinScanner { private sessionId: string; private tmdb: TheMovieDb; private jfClient: JellyfinAPI; @@ -675,7 +675,7 @@ class JobJellyfinSync { } } -export const jobJellyfinFullSync = new JobJellyfinSync(); -export const jobJellyfinRecentSync = new JobJellyfinSync({ +export const jellyfinFullScanner = new JellyfinScanner(); +export const jellyfinRecentScanner = new JellyfinScanner({ isRecentOnly: true, }); diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index dc3724207..5703cccc0 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -12,12 +12,12 @@ import type { LogsResultsResponse, SettingsAboutResponse, } from '@server/interfaces/api/settingsInterfaces'; -import { jobJellyfinFullSync } from '@server/job/jellyfinsync'; import { scheduledJobs } from '@server/job/schedule'; import type { AvailableCacheIds } from '@server/lib/cache'; import cacheManager from '@server/lib/cache'; import ImageProxy from '@server/lib/imageproxy'; import { Permission } from '@server/lib/permissions'; +import { jellyfinFullScanner } from '@server/lib/scanners/jellyfin'; import { plexFullScanner } from '@server/lib/scanners/plex'; import type { JobId, Library, MainSettings } from '@server/lib/settings'; import { getSettings } from '@server/lib/settings'; @@ -345,16 +345,16 @@ settingsRoutes.get('/jellyfin/users', async (req, res) => { }); settingsRoutes.get('/jellyfin/sync', (_req, res) => { - return res.status(200).json(jobJellyfinFullSync.status()); + return res.status(200).json(jellyfinFullScanner.status()); }); settingsRoutes.post('/jellyfin/sync', (req, res) => { if (req.body.cancel) { - jobJellyfinFullSync.cancel(); + jellyfinFullScanner.cancel(); } else if (req.body.start) { - jobJellyfinFullSync.run(); + jellyfinFullScanner.run(); } - return res.status(200).json(jobJellyfinFullSync.status()); + return res.status(200).json(jellyfinFullScanner.status()); }); settingsRoutes.get('/tautulli', (_req, res) => { const settings = getSettings(); From 8ec8f2ac5730aad3b12dcd8ed95bb553b46b399c Mon Sep 17 00:00:00 2001 From: fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:58:46 +0500 Subject: [PATCH 5/6] fix: disable seasonfolder option in sonarr for jellyfin/Emby users This disables seasonfolder option in sonarr for jellyfin/emby users as physical seasonFolders are necessary as virtualFolders are ignored since #126 fix #575 --- src/components/Settings/SonarrModal/index.tsx | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/components/Settings/SonarrModal/index.tsx b/src/components/Settings/SonarrModal/index.tsx index 729a40a7b..b51d152c8 100644 --- a/src/components/Settings/SonarrModal/index.tsx +++ b/src/components/Settings/SonarrModal/index.tsx @@ -1,8 +1,10 @@ import Modal from '@app/components/Common/Modal'; import SensitiveInput from '@app/components/Common/SensitiveInput'; +import useSettings from '@app/hooks/useSettings'; import globalMessages from '@app/i18n/globalMessages'; import { Transition } from '@headlessui/react'; -import type { SonarrSettings } from '@server/lib/settings'; +import { MediaServerType } from '@server/constants/server'; +import { type SonarrSettings } from '@server/lib/settings'; import axios from 'axios'; import { Field, Formik } from 'formik'; import { useCallback, useEffect, useRef, useState } from 'react'; @@ -109,6 +111,7 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { const { addToast } = useToasts(); const [isValidated, setIsValidated] = useState(sonarr ? true : false); const [isTesting, setIsTesting] = useState(false); + const settings = useSettings(); const [testResponse, setTestResponse] = useState({ profiles: [], rootFolders: [], @@ -255,7 +258,9 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { animeTags: sonarr?.animeTags ?? [], isDefault: sonarr?.isDefault ?? false, is4k: sonarr?.is4k ?? false, - enableSeasonFolders: sonarr?.enableSeasonFolders ?? false, + enableSeasonFolders: + sonarr?.enableSeasonFolders ?? + settings.currentSettings.mediaServerType !== MediaServerType.PLEX, externalUrl: sonarr?.externalUrl, syncEnabled: sonarr?.syncEnabled ?? false, enableSearch: !sonarr?.preventSearch, @@ -961,11 +966,25 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { > {intl.formatMessage(messages.seasonfolders)} -
+
From ccfcdea1f65ba5ce6637caf180d8843a774584e1 Mon Sep 17 00:00:00 2001 From: fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:11:10 +0500 Subject: [PATCH 6/6] refactor: clean out commented code --- src/components/Settings/SonarrModal/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Settings/SonarrModal/index.tsx b/src/components/Settings/SonarrModal/index.tsx index b51d152c8..5267ef4e3 100644 --- a/src/components/Settings/SonarrModal/index.tsx +++ b/src/components/Settings/SonarrModal/index.tsx @@ -980,7 +980,6 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { type="checkbox" id="enableSeasonFolders" name="enableSeasonFolders" - // checked={true} disabled={ settings.currentSettings.mediaServerType !== MediaServerType.PLEX