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 907cc8e2..47140c5d 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 47140c5d..46c946ae 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 775da757..61d4f5c4 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 0764f3aa..825e52cc 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 410036cc..30a62c16 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 f9aff8e2..2a2acd02 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 e94cb96c..b8b70fd5 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 15bf033e..f65cdebb 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 b263ec6e..c231ec0d 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 dc372420..5703cccc 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 729a40a7..b51d152c 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 b51d152c..5267ef4e 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