mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2026-01-01 04:08:45 -05:00
fix: added a refresh interval if download status is in progress (#3275)
* fix: added a refresh interval if download status is in progress * refactor: switched to a function instead of useEffect * feat: added editable download sync schedule
This commit is contained in:
@@ -14,7 +14,7 @@ interface ScheduledJob {
|
|||||||
job: schedule.Job;
|
job: schedule.Job;
|
||||||
name: string;
|
name: string;
|
||||||
type: 'process' | 'command';
|
type: 'process' | 'command';
|
||||||
interval: 'short' | 'long' | 'fixed';
|
interval: 'seconds' | 'minutes' | 'hours' | 'fixed';
|
||||||
cronSchedule: string;
|
cronSchedule: string;
|
||||||
running?: () => boolean;
|
running?: () => boolean;
|
||||||
cancelFn?: () => void;
|
cancelFn?: () => void;
|
||||||
@@ -30,7 +30,7 @@ export const startJobs = (): void => {
|
|||||||
id: 'plex-recently-added-scan',
|
id: 'plex-recently-added-scan',
|
||||||
name: 'Plex Recently Added Scan',
|
name: 'Plex Recently Added Scan',
|
||||||
type: 'process',
|
type: 'process',
|
||||||
interval: 'short',
|
interval: 'minutes',
|
||||||
cronSchedule: jobs['plex-recently-added-scan'].schedule,
|
cronSchedule: jobs['plex-recently-added-scan'].schedule,
|
||||||
job: schedule.scheduleJob(jobs['plex-recently-added-scan'].schedule, () => {
|
job: schedule.scheduleJob(jobs['plex-recently-added-scan'].schedule, () => {
|
||||||
logger.info('Starting scheduled job: Plex Recently Added Scan', {
|
logger.info('Starting scheduled job: Plex Recently Added Scan', {
|
||||||
@@ -47,7 +47,7 @@ export const startJobs = (): void => {
|
|||||||
id: 'plex-full-scan',
|
id: 'plex-full-scan',
|
||||||
name: 'Plex Full Library Scan',
|
name: 'Plex Full Library Scan',
|
||||||
type: 'process',
|
type: 'process',
|
||||||
interval: 'long',
|
interval: 'hours',
|
||||||
cronSchedule: jobs['plex-full-scan'].schedule,
|
cronSchedule: jobs['plex-full-scan'].schedule,
|
||||||
job: schedule.scheduleJob(jobs['plex-full-scan'].schedule, () => {
|
job: schedule.scheduleJob(jobs['plex-full-scan'].schedule, () => {
|
||||||
logger.info('Starting scheduled job: Plex Full Library Scan', {
|
logger.info('Starting scheduled job: Plex Full Library Scan', {
|
||||||
@@ -64,7 +64,7 @@ export const startJobs = (): void => {
|
|||||||
id: 'plex-watchlist-sync',
|
id: 'plex-watchlist-sync',
|
||||||
name: 'Plex Watchlist Sync',
|
name: 'Plex Watchlist Sync',
|
||||||
type: 'process',
|
type: 'process',
|
||||||
interval: 'short',
|
interval: 'minutes',
|
||||||
cronSchedule: jobs['plex-watchlist-sync'].schedule,
|
cronSchedule: jobs['plex-watchlist-sync'].schedule,
|
||||||
job: schedule.scheduleJob(jobs['plex-watchlist-sync'].schedule, () => {
|
job: schedule.scheduleJob(jobs['plex-watchlist-sync'].schedule, () => {
|
||||||
logger.info('Starting scheduled job: Plex Watchlist Sync', {
|
logger.info('Starting scheduled job: Plex Watchlist Sync', {
|
||||||
@@ -79,7 +79,7 @@ export const startJobs = (): void => {
|
|||||||
id: 'radarr-scan',
|
id: 'radarr-scan',
|
||||||
name: 'Radarr Scan',
|
name: 'Radarr Scan',
|
||||||
type: 'process',
|
type: 'process',
|
||||||
interval: 'long',
|
interval: 'hours',
|
||||||
cronSchedule: jobs['radarr-scan'].schedule,
|
cronSchedule: jobs['radarr-scan'].schedule,
|
||||||
job: schedule.scheduleJob(jobs['radarr-scan'].schedule, () => {
|
job: schedule.scheduleJob(jobs['radarr-scan'].schedule, () => {
|
||||||
logger.info('Starting scheduled job: Radarr Scan', { label: 'Jobs' });
|
logger.info('Starting scheduled job: Radarr Scan', { label: 'Jobs' });
|
||||||
@@ -94,7 +94,7 @@ export const startJobs = (): void => {
|
|||||||
id: 'sonarr-scan',
|
id: 'sonarr-scan',
|
||||||
name: 'Sonarr Scan',
|
name: 'Sonarr Scan',
|
||||||
type: 'process',
|
type: 'process',
|
||||||
interval: 'long',
|
interval: 'hours',
|
||||||
cronSchedule: jobs['sonarr-scan'].schedule,
|
cronSchedule: jobs['sonarr-scan'].schedule,
|
||||||
job: schedule.scheduleJob(jobs['sonarr-scan'].schedule, () => {
|
job: schedule.scheduleJob(jobs['sonarr-scan'].schedule, () => {
|
||||||
logger.info('Starting scheduled job: Sonarr Scan', { label: 'Jobs' });
|
logger.info('Starting scheduled job: Sonarr Scan', { label: 'Jobs' });
|
||||||
@@ -109,7 +109,7 @@ export const startJobs = (): void => {
|
|||||||
id: 'download-sync',
|
id: 'download-sync',
|
||||||
name: 'Download Sync',
|
name: 'Download Sync',
|
||||||
type: 'command',
|
type: 'command',
|
||||||
interval: 'fixed',
|
interval: 'seconds',
|
||||||
cronSchedule: jobs['download-sync'].schedule,
|
cronSchedule: jobs['download-sync'].schedule,
|
||||||
job: schedule.scheduleJob(jobs['download-sync'].schedule, () => {
|
job: schedule.scheduleJob(jobs['download-sync'].schedule, () => {
|
||||||
logger.debug('Starting scheduled job: Download Sync', {
|
logger.debug('Starting scheduled job: Download Sync', {
|
||||||
@@ -124,7 +124,7 @@ export const startJobs = (): void => {
|
|||||||
id: 'download-sync-reset',
|
id: 'download-sync-reset',
|
||||||
name: 'Download Sync Reset',
|
name: 'Download Sync Reset',
|
||||||
type: 'command',
|
type: 'command',
|
||||||
interval: 'long',
|
interval: 'hours',
|
||||||
cronSchedule: jobs['download-sync-reset'].schedule,
|
cronSchedule: jobs['download-sync-reset'].schedule,
|
||||||
job: schedule.scheduleJob(jobs['download-sync-reset'].schedule, () => {
|
job: schedule.scheduleJob(jobs['download-sync-reset'].schedule, () => {
|
||||||
logger.info('Starting scheduled job: Download Sync Reset', {
|
logger.info('Starting scheduled job: Download Sync Reset', {
|
||||||
@@ -134,12 +134,12 @@ export const startJobs = (): void => {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run image cache cleanup every 5 minutes
|
// Run image cache cleanup every 24 hours
|
||||||
scheduledJobs.push({
|
scheduledJobs.push({
|
||||||
id: 'image-cache-cleanup',
|
id: 'image-cache-cleanup',
|
||||||
name: 'Image Cache Cleanup',
|
name: 'Image Cache Cleanup',
|
||||||
type: 'process',
|
type: 'process',
|
||||||
interval: 'long',
|
interval: 'hours',
|
||||||
cronSchedule: jobs['image-cache-cleanup'].schedule,
|
cronSchedule: jobs['image-cache-cleanup'].schedule,
|
||||||
job: schedule.scheduleJob(jobs['image-cache-cleanup'].schedule, () => {
|
job: schedule.scheduleJob(jobs['image-cache-cleanup'].schedule, () => {
|
||||||
logger.info('Starting scheduled job: Image Cache Cleanup', {
|
logger.info('Starting scheduled job: Image Cache Cleanup', {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import useSettings from '@app/hooks/useSettings';
|
|||||||
import { Permission, useUser } from '@app/hooks/useUser';
|
import { Permission, useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import Error from '@app/pages/_error';
|
import Error from '@app/pages/_error';
|
||||||
|
import { refreshIntervalHelper } from '@app/utils/refreshIntervalHelper';
|
||||||
import { ArrowDownTrayIcon } from '@heroicons/react/24/outline';
|
import { ArrowDownTrayIcon } from '@heroicons/react/24/outline';
|
||||||
import { MediaStatus } from '@server/constants/media';
|
import { MediaStatus } from '@server/constants/media';
|
||||||
import type { Collection } from '@server/models/Collection';
|
import type { Collection } from '@server/models/Collection';
|
||||||
@@ -39,20 +40,8 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
|
|||||||
const [requestModal, setRequestModal] = useState(false);
|
const [requestModal, setRequestModal] = useState(false);
|
||||||
const [is4k, setIs4k] = useState(false);
|
const [is4k, setIs4k] = useState(false);
|
||||||
|
|
||||||
const {
|
const returnCollectionDownloadItems = (data: Collection | undefined) => {
|
||||||
data,
|
const [downloadStatus, downloadStatus4k] = [
|
||||||
error,
|
|
||||||
mutate: revalidate,
|
|
||||||
} = useSWR<Collection>(`/api/v1/collection/${router.query.collectionId}`, {
|
|
||||||
fallbackData: collection,
|
|
||||||
revalidateOnMount: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data: genres } =
|
|
||||||
useSWR<{ id: number; name: string }[]>(`/api/v1/genres/movie`);
|
|
||||||
|
|
||||||
const [downloadStatus, downloadStatus4k] = useMemo(() => {
|
|
||||||
return [
|
|
||||||
data?.parts.flatMap((item) =>
|
data?.parts.flatMap((item) =>
|
||||||
item.mediaInfo?.downloadStatus ? item.mediaInfo?.downloadStatus : []
|
item.mediaInfo?.downloadStatus ? item.mediaInfo?.downloadStatus : []
|
||||||
),
|
),
|
||||||
@@ -60,7 +49,30 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
|
|||||||
item.mediaInfo?.downloadStatus4k ? item.mediaInfo?.downloadStatus4k : []
|
item.mediaInfo?.downloadStatus4k ? item.mediaInfo?.downloadStatus4k : []
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}, [data?.parts]);
|
|
||||||
|
return { downloadStatus, downloadStatus4k };
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
error,
|
||||||
|
mutate: revalidate,
|
||||||
|
} = useSWR<Collection>(`/api/v1/collection/${router.query.collectionId}`, {
|
||||||
|
fallbackData: collection,
|
||||||
|
revalidateOnMount: true,
|
||||||
|
refreshInterval: refreshIntervalHelper(
|
||||||
|
returnCollectionDownloadItems(collection),
|
||||||
|
15000
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: genres } =
|
||||||
|
useSWR<{ id: number; name: string }[]>(`/api/v1/genres/movie`);
|
||||||
|
|
||||||
|
const [downloadStatus, downloadStatus4k] = useMemo(() => {
|
||||||
|
const downloadItems = returnCollectionDownloadItems(data);
|
||||||
|
return [downloadItems.downloadStatus, downloadItems.downloadStatus4k];
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
const [titles, titles4k] = useMemo(() => {
|
const [titles, titles4k] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { Permission, useUser } from '@app/hooks/useUser';
|
|||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import Error from '@app/pages/_error';
|
import Error from '@app/pages/_error';
|
||||||
import { sortCrewPriority } from '@app/utils/creditHelpers';
|
import { sortCrewPriority } from '@app/utils/creditHelpers';
|
||||||
|
import { refreshIntervalHelper } from '@app/utils/refreshIntervalHelper';
|
||||||
import {
|
import {
|
||||||
ArrowRightCircleIcon,
|
ArrowRightCircleIcon,
|
||||||
CloudIcon,
|
CloudIcon,
|
||||||
@@ -110,6 +111,13 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
|||||||
mutate: revalidate,
|
mutate: revalidate,
|
||||||
} = useSWR<MovieDetailsType>(`/api/v1/movie/${router.query.movieId}`, {
|
} = useSWR<MovieDetailsType>(`/api/v1/movie/${router.query.movieId}`, {
|
||||||
fallbackData: movie,
|
fallbackData: movie,
|
||||||
|
refreshInterval: refreshIntervalHelper(
|
||||||
|
{
|
||||||
|
downloadStatus: movie?.mediaInfo?.downloadStatus,
|
||||||
|
downloadStatus4k: movie?.mediaInfo?.downloadStatus4k,
|
||||||
|
},
|
||||||
|
15000
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: ratingData } = useSWR<RTRating>(
|
const { data: ratingData } = useSWR<RTRating>(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import StatusBadge from '@app/components/StatusBadge';
|
|||||||
import useDeepLinks from '@app/hooks/useDeepLinks';
|
import useDeepLinks from '@app/hooks/useDeepLinks';
|
||||||
import { Permission, useUser } from '@app/hooks/useUser';
|
import { Permission, useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
|
import { refreshIntervalHelper } from '@app/utils/refreshIntervalHelper';
|
||||||
import { withProperties } from '@app/utils/typeHelpers';
|
import { withProperties } from '@app/utils/typeHelpers';
|
||||||
import {
|
import {
|
||||||
ArrowPathIcon,
|
ArrowPathIcon,
|
||||||
@@ -220,6 +221,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
|
|||||||
request.type === 'movie'
|
request.type === 'movie'
|
||||||
? `/api/v1/movie/${request.media.tmdbId}`
|
? `/api/v1/movie/${request.media.tmdbId}`
|
||||||
: `/api/v1/tv/${request.media.tmdbId}`;
|
: `/api/v1/tv/${request.media.tmdbId}`;
|
||||||
|
|
||||||
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
|
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
|
||||||
inView ? `${url}` : null
|
inView ? `${url}` : null
|
||||||
);
|
);
|
||||||
@@ -229,6 +231,13 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
|
|||||||
mutate: revalidate,
|
mutate: revalidate,
|
||||||
} = useSWR<MediaRequest>(`/api/v1/request/${request.id}`, {
|
} = useSWR<MediaRequest>(`/api/v1/request/${request.id}`, {
|
||||||
fallbackData: request,
|
fallbackData: request,
|
||||||
|
refreshInterval: refreshIntervalHelper(
|
||||||
|
{
|
||||||
|
downloadStatus: request.media.downloadStatus,
|
||||||
|
downloadStatus4k: request.media.downloadStatus4k,
|
||||||
|
},
|
||||||
|
15000
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { plexUrl, plexUrl4k } = useDeepLinks({
|
const { plexUrl, plexUrl4k } = useDeepLinks({
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import StatusBadge from '@app/components/StatusBadge';
|
|||||||
import useDeepLinks from '@app/hooks/useDeepLinks';
|
import useDeepLinks from '@app/hooks/useDeepLinks';
|
||||||
import { Permission, useUser } from '@app/hooks/useUser';
|
import { Permission, useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
|
import { refreshIntervalHelper } from '@app/utils/refreshIntervalHelper';
|
||||||
import {
|
import {
|
||||||
ArrowPathIcon,
|
ArrowPathIcon,
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
@@ -293,6 +294,13 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
|
|||||||
`/api/v1/request/${request.id}`,
|
`/api/v1/request/${request.id}`,
|
||||||
{
|
{
|
||||||
fallbackData: request,
|
fallbackData: request,
|
||||||
|
refreshInterval: refreshIntervalHelper(
|
||||||
|
{
|
||||||
|
downloadStatus: request.media.downloadStatus,
|
||||||
|
downloadStatus4k: request.media.downloadStatus4k,
|
||||||
|
},
|
||||||
|
15000
|
||||||
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ const messages: { [messageName: string]: MessageDescriptor } = defineMessages({
|
|||||||
'Every {jobScheduleHours, plural, one {hour} other {{jobScheduleHours} hours}}',
|
'Every {jobScheduleHours, plural, one {hour} other {{jobScheduleHours} hours}}',
|
||||||
editJobScheduleSelectorMinutes:
|
editJobScheduleSelectorMinutes:
|
||||||
'Every {jobScheduleMinutes, plural, one {minute} other {{jobScheduleMinutes} minutes}}',
|
'Every {jobScheduleMinutes, plural, one {minute} other {{jobScheduleMinutes} minutes}}',
|
||||||
|
editJobScheduleSelectorSeconds:
|
||||||
|
'Every {jobScheduleSeconds, plural, one {second} other {{jobScheduleSeconds} seconds}}',
|
||||||
imagecache: 'Image Cache',
|
imagecache: 'Image Cache',
|
||||||
imagecacheDescription:
|
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 <code>{appDataPath}/cache/images</code>.',
|
'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 <code>{appDataPath}/cache/images</code>.',
|
||||||
@@ -78,7 +80,7 @@ interface Job {
|
|||||||
id: JobId;
|
id: JobId;
|
||||||
name: string;
|
name: string;
|
||||||
type: 'process' | 'command';
|
type: 'process' | 'command';
|
||||||
interval: 'short' | 'long' | 'fixed';
|
interval: 'seconds' | 'minutes' | 'hours' | 'fixed';
|
||||||
cronSchedule: string;
|
cronSchedule: string;
|
||||||
nextExecutionTime: string;
|
nextExecutionTime: string;
|
||||||
running: boolean;
|
running: boolean;
|
||||||
@@ -89,10 +91,11 @@ type JobModalState = {
|
|||||||
job?: Job;
|
job?: Job;
|
||||||
scheduleHours: number;
|
scheduleHours: number;
|
||||||
scheduleMinutes: number;
|
scheduleMinutes: number;
|
||||||
|
scheduleSeconds: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type JobModalAction =
|
type JobModalAction =
|
||||||
| { type: 'set'; hours?: number; minutes?: number }
|
| { type: 'set'; hours?: number; minutes?: number; seconds?: number }
|
||||||
| {
|
| {
|
||||||
type: 'close';
|
type: 'close';
|
||||||
}
|
}
|
||||||
@@ -115,6 +118,7 @@ const jobModalReducer = (
|
|||||||
job: action.job,
|
job: action.job,
|
||||||
scheduleHours: 1,
|
scheduleHours: 1,
|
||||||
scheduleMinutes: 5,
|
scheduleMinutes: 5,
|
||||||
|
scheduleSeconds: 30,
|
||||||
};
|
};
|
||||||
|
|
||||||
case 'set':
|
case 'set':
|
||||||
@@ -122,6 +126,7 @@ const jobModalReducer = (
|
|||||||
...state,
|
...state,
|
||||||
scheduleHours: action.hours ?? state.scheduleHours,
|
scheduleHours: action.hours ?? state.scheduleHours,
|
||||||
scheduleMinutes: action.minutes ?? state.scheduleMinutes,
|
scheduleMinutes: action.minutes ?? state.scheduleMinutes,
|
||||||
|
scheduleSeconds: action.seconds ?? state.scheduleSeconds,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -149,6 +154,7 @@ const SettingsJobs = () => {
|
|||||||
isOpen: false,
|
isOpen: false,
|
||||||
scheduleHours: 1,
|
scheduleHours: 1,
|
||||||
scheduleMinutes: 5,
|
scheduleMinutes: 5,
|
||||||
|
scheduleSeconds: 30,
|
||||||
});
|
});
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|
||||||
@@ -200,9 +206,11 @@ const SettingsJobs = () => {
|
|||||||
const jobScheduleCron = ['0', '0', '*', '*', '*', '*'];
|
const jobScheduleCron = ['0', '0', '*', '*', '*', '*'];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (jobModalState.job?.interval === 'short') {
|
if (jobModalState.job?.interval === 'seconds') {
|
||||||
|
jobScheduleCron.splice(0, 2, `*/${jobModalState.scheduleSeconds}`, '*');
|
||||||
|
} else if (jobModalState.job?.interval === 'minutes') {
|
||||||
jobScheduleCron[1] = `*/${jobModalState.scheduleMinutes}`;
|
jobScheduleCron[1] = `*/${jobModalState.scheduleMinutes}`;
|
||||||
} else if (jobModalState.job?.interval === 'long') {
|
} else if (jobModalState.job?.interval === 'hours') {
|
||||||
jobScheduleCron[2] = `*/${jobModalState.scheduleHours}`;
|
jobScheduleCron[2] = `*/${jobModalState.scheduleHours}`;
|
||||||
} else {
|
} else {
|
||||||
// jobs with interval: fixed should not be editable
|
// jobs with interval: fixed should not be editable
|
||||||
@@ -286,7 +294,30 @@ const SettingsJobs = () => {
|
|||||||
{intl.formatMessage(messages.editJobSchedulePrompt)}
|
{intl.formatMessage(messages.editJobSchedulePrompt)}
|
||||||
</label>
|
</label>
|
||||||
<div className="form-input-area">
|
<div className="form-input-area">
|
||||||
{jobModalState.job?.interval === 'short' ? (
|
{jobModalState.job?.interval === 'seconds' ? (
|
||||||
|
<select
|
||||||
|
name="jobScheduleSeconds"
|
||||||
|
className="inline"
|
||||||
|
value={jobModalState.scheduleSeconds}
|
||||||
|
onChange={(e) =>
|
||||||
|
dispatch({
|
||||||
|
type: 'set',
|
||||||
|
seconds: Number(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{[30, 45, 60].map((v) => (
|
||||||
|
<option value={v} key={`jobScheduleSeconds-${v}`}>
|
||||||
|
{intl.formatMessage(
|
||||||
|
messages.editJobScheduleSelectorSeconds,
|
||||||
|
{
|
||||||
|
jobScheduleSeconds: v,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
) : jobModalState.job?.interval === 'minutes' ? (
|
||||||
<select
|
<select
|
||||||
name="jobScheduleMinutes"
|
name="jobScheduleMinutes"
|
||||||
className="inline"
|
className="inline"
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { Permission, useUser } from '@app/hooks/useUser';
|
|||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import Error from '@app/pages/_error';
|
import Error from '@app/pages/_error';
|
||||||
import { sortCrewPriority } from '@app/utils/creditHelpers';
|
import { sortCrewPriority } from '@app/utils/creditHelpers';
|
||||||
|
import { refreshIntervalHelper } from '@app/utils/refreshIntervalHelper';
|
||||||
import { Disclosure, Transition } from '@headlessui/react';
|
import { Disclosure, Transition } from '@headlessui/react';
|
||||||
import {
|
import {
|
||||||
ArrowRightCircleIcon,
|
ArrowRightCircleIcon,
|
||||||
@@ -109,6 +110,13 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
|||||||
mutate: revalidate,
|
mutate: revalidate,
|
||||||
} = useSWR<TvDetailsType>(`/api/v1/tv/${router.query.tvId}`, {
|
} = useSWR<TvDetailsType>(`/api/v1/tv/${router.query.tvId}`, {
|
||||||
fallbackData: tv,
|
fallbackData: tv,
|
||||||
|
refreshInterval: refreshIntervalHelper(
|
||||||
|
{
|
||||||
|
downloadStatus: tv?.mediaInfo?.downloadStatus,
|
||||||
|
downloadStatus4k: tv?.mediaInfo?.downloadStatus4k,
|
||||||
|
},
|
||||||
|
15000
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: ratingData } = useSWR<RTRating>(
|
const { data: ratingData } = useSWR<RTRating>(
|
||||||
|
|||||||
@@ -739,6 +739,7 @@
|
|||||||
"components.Settings.SettingsJobsCache.editJobSchedulePrompt": "New Frequency",
|
"components.Settings.SettingsJobsCache.editJobSchedulePrompt": "New Frequency",
|
||||||
"components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "Every {jobScheduleHours, plural, one {hour} other {{jobScheduleHours} hours}}",
|
"components.Settings.SettingsJobsCache.editJobScheduleSelectorHours": "Every {jobScheduleHours, plural, one {hour} other {{jobScheduleHours} hours}}",
|
||||||
"components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "Every {jobScheduleMinutes, plural, one {minute} other {{jobScheduleMinutes} minutes}}",
|
"components.Settings.SettingsJobsCache.editJobScheduleSelectorMinutes": "Every {jobScheduleMinutes, plural, one {minute} other {{jobScheduleMinutes} minutes}}",
|
||||||
|
"components.Settings.SettingsJobsCache.editJobScheduleSelectorSeconds": "Every {jobScheduleSeconds, plural, one {second} other {{jobScheduleSeconds} seconds}}",
|
||||||
"components.Settings.SettingsJobsCache.flushcache": "Flush Cache",
|
"components.Settings.SettingsJobsCache.flushcache": "Flush Cache",
|
||||||
"components.Settings.SettingsJobsCache.image-cache-cleanup": "Image Cache Cleanup",
|
"components.Settings.SettingsJobsCache.image-cache-cleanup": "Image Cache Cleanup",
|
||||||
"components.Settings.SettingsJobsCache.imagecache": "Image Cache",
|
"components.Settings.SettingsJobsCache.imagecache": "Image Cache",
|
||||||
|
|||||||
18
src/utils/refreshIntervalHelper.ts
Normal file
18
src/utils/refreshIntervalHelper.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { DownloadingItem } from '@server/lib/downloadtracker';
|
||||||
|
|
||||||
|
export const refreshIntervalHelper = (
|
||||||
|
downloadItem: {
|
||||||
|
downloadStatus: DownloadingItem[] | undefined;
|
||||||
|
downloadStatus4k: DownloadingItem[] | undefined;
|
||||||
|
},
|
||||||
|
timer: number
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
(downloadItem.downloadStatus ?? []).length > 0 ||
|
||||||
|
(downloadItem.downloadStatus4k ?? []).length > 0
|
||||||
|
) {
|
||||||
|
return timer;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user