Skip to content

Commit

Permalink
feat: added editable download sync schedule
Browse files Browse the repository at this point in the history
  • Loading branch information
OwsleyJr committed Feb 12, 2023
1 parent 38b9045 commit 2f1d500
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 83 deletions.
18 changes: 9 additions & 9 deletions server/job/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface ScheduledJob {
job: schedule.Job;
name: string;
type: 'process' | 'command';
interval: 'short' | 'long' | 'fixed';
interval: 'seconds' | 'minutes' | 'hours' | 'fixed';
cronSchedule: string;
running?: () => boolean;
cancelFn?: () => void;
Expand All @@ -30,7 +30,7 @@ export const startJobs = (): void => {
id: 'plex-recently-added-scan',
name: 'Plex Recently Added Scan',
type: 'process',
interval: 'short',
interval: 'minutes',
cronSchedule: jobs['plex-recently-added-scan'].schedule,
job: schedule.scheduleJob(jobs['plex-recently-added-scan'].schedule, () => {
logger.info('Starting scheduled job: Plex Recently Added Scan', {
Expand All @@ -47,7 +47,7 @@ export const startJobs = (): void => {
id: 'plex-full-scan',
name: 'Plex Full Library Scan',
type: 'process',
interval: 'long',
interval: 'hours',
cronSchedule: jobs['plex-full-scan'].schedule,
job: schedule.scheduleJob(jobs['plex-full-scan'].schedule, () => {
logger.info('Starting scheduled job: Plex Full Library Scan', {
Expand All @@ -64,7 +64,7 @@ export const startJobs = (): void => {
id: 'plex-watchlist-sync',
name: 'Plex Watchlist Sync',
type: 'process',
interval: 'short',
interval: 'minutes',
cronSchedule: jobs['plex-watchlist-sync'].schedule,
job: schedule.scheduleJob(jobs['plex-watchlist-sync'].schedule, () => {
logger.info('Starting scheduled job: Plex Watchlist Sync', {
Expand All @@ -79,7 +79,7 @@ export const startJobs = (): void => {
id: 'radarr-scan',
name: 'Radarr Scan',
type: 'process',
interval: 'long',
interval: 'hours',
cronSchedule: jobs['radarr-scan'].schedule,
job: schedule.scheduleJob(jobs['radarr-scan'].schedule, () => {
logger.info('Starting scheduled job: Radarr Scan', { label: 'Jobs' });
Expand All @@ -94,7 +94,7 @@ export const startJobs = (): void => {
id: 'sonarr-scan',
name: 'Sonarr Scan',
type: 'process',
interval: 'long',
interval: 'hours',
cronSchedule: jobs['sonarr-scan'].schedule,
job: schedule.scheduleJob(jobs['sonarr-scan'].schedule, () => {
logger.info('Starting scheduled job: Sonarr Scan', { label: 'Jobs' });
Expand All @@ -109,7 +109,7 @@ export const startJobs = (): void => {
id: 'download-sync',
name: 'Download Sync',
type: 'command',
interval: 'fixed',
interval: 'seconds',
cronSchedule: jobs['download-sync'].schedule,
job: schedule.scheduleJob(jobs['download-sync'].schedule, () => {
logger.debug('Starting scheduled job: Download Sync', {
Expand All @@ -124,7 +124,7 @@ export const startJobs = (): void => {
id: 'download-sync-reset',
name: 'Download Sync Reset',
type: 'command',
interval: 'long',
interval: 'hours',
cronSchedule: jobs['download-sync-reset'].schedule,
job: schedule.scheduleJob(jobs['download-sync-reset'].schedule, () => {
logger.info('Starting scheduled job: Download Sync Reset', {
Expand All @@ -139,7 +139,7 @@ export const startJobs = (): void => {
id: 'image-cache-cleanup',
name: 'Image Cache Cleanup',
type: 'process',
interval: 'long',
interval: 'minutes',
cronSchedule: jobs['image-cache-cleanup'].schedule,
job: schedule.scheduleJob(jobs['image-cache-cleanup'].schedule, () => {
logger.info('Starting scheduled job: Image Cache Cleanup', {
Expand Down
31 changes: 11 additions & 20 deletions src/components/CollectionDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import useSettings from '@app/hooks/useSettings';
import { Permission, useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages';
import Error from '@app/pages/_error';
import { refreshIntervalHelper } from '@app/utils/refreshIntervalHelper';
import { ArrowDownTrayIcon } from '@heroicons/react/24/outline';
import { MediaStatus } from '@server/constants/media';
import type { Collection } from '@server/models/Collection';
Expand Down Expand Up @@ -39,8 +40,8 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
const [requestModal, setRequestModal] = useState(false);
const [is4k, setIs4k] = useState(false);

const refreshIntervalChecker = (data: Collection | undefined) => {
const [tempDownloadStatus, tempDownloadStatus4k] = [
const returnCollectionDownloadItems = (data: Collection | undefined) => {
const [downloadStatus, downloadStatus4k] = [
data?.parts.flatMap((item) =>
item.mediaInfo?.downloadStatus ? item.mediaInfo?.downloadStatus : []
),
Expand All @@ -49,14 +50,7 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
),
];

if (
(tempDownloadStatus ?? []).length > 0 ||
(tempDownloadStatus4k ?? []).length > 0
) {
return 5000;
} else {
return 0;
}
return { downloadStatus, downloadStatus4k };
};

const {
Expand All @@ -66,22 +60,19 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
} = useSWR<Collection>(`/api/v1/collection/${router.query.collectionId}`, {
fallbackData: collection,
revalidateOnMount: true,
refreshInterval: refreshIntervalChecker(collection),
refreshInterval: refreshIntervalHelper(
returnCollectionDownloadItems(collection),
15000
),
});

const { data: genres } =
useSWR<{ id: number; name: string }[]>(`/api/v1/genres/movie`);

const [downloadStatus, downloadStatus4k] = useMemo(() => {
return [
data?.parts.flatMap((item) =>
item.mediaInfo?.downloadStatus ? item.mediaInfo?.downloadStatus : []
),
data?.parts.flatMap((item) =>
item.mediaInfo?.downloadStatus4k ? item.mediaInfo?.downloadStatus4k : []
),
];
}, [data?.parts]);
const downloadItems = returnCollectionDownloadItems(data);
return [downloadItems.downloadStatus, downloadItems.downloadStatus4k];
}, [data]);

const [titles, titles4k] = useMemo(() => {
return [
Expand Down
20 changes: 8 additions & 12 deletions src/components/MovieDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { Permission, useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages';
import Error from '@app/pages/_error';
import { sortCrewPriority } from '@app/utils/creditHelpers';
import { refreshIntervalHelper } from '@app/utils/refreshIntervalHelper';
import {
ArrowRightCircleIcon,
CloudIcon,
Expand Down Expand Up @@ -104,24 +105,19 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
const [showMoreStudios, setShowMoreStudios] = useState(false);
const [showIssueModal, setShowIssueModal] = useState(false);

const refreshIntervalChecker = (data: MovieDetailsType | undefined) => {
if (
(data?.mediaInfo?.downloadStatus ?? []).length > 0 ||
(data?.mediaInfo?.downloadStatus4k ?? []).length > 0
) {
return 5000;
} else {
return 0;
}
};

const {
data,
error,
mutate: revalidate,
} = useSWR<MovieDetailsType>(`/api/v1/movie/${router.query.movieId}`, {
fallbackData: movie,
refreshInterval: refreshIntervalChecker(movie),
refreshInterval: refreshIntervalHelper(
{
downloadStatus: movie?.mediaInfo?.downloadStatus,
downloadStatus4k: movie?.mediaInfo?.downloadStatus4k,
},
15000
),
});

const { data: ratingData } = useSWR<RTRating>(
Expand Down
20 changes: 8 additions & 12 deletions src/components/RequestCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import StatusBadge from '@app/components/StatusBadge';
import useDeepLinks from '@app/hooks/useDeepLinks';
import { Permission, useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages';
import { refreshIntervalHelper } from '@app/utils/refreshIntervalHelper';
import { withProperties } from '@app/utils/typeHelpers';
import {
ArrowPathIcon,
Expand Down Expand Up @@ -221,17 +222,6 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
? `/api/v1/movie/${request.media.tmdbId}`
: `/api/v1/tv/${request.media.tmdbId}`;

const refreshIntervalChecker = (data: MediaRequest) => {
if (
(data.media.downloadStatus ?? []).length > 0 ||
(data.media.downloadStatus4k ?? []).length > 0
) {
return 5000;
} else {
return 0;
}
};

const { data: title, error } = useSWR<MovieDetails | TvDetails>(
inView ? `${url}` : null
);
Expand All @@ -241,7 +231,13 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
mutate: revalidate,
} = useSWR<MediaRequest>(`/api/v1/request/${request.id}`, {
fallbackData: request,
refreshInterval: refreshIntervalChecker(request),
refreshInterval: refreshIntervalHelper(
{
downloadStatus: request.media.downloadStatus,
downloadStatus4k: request.media.downloadStatus4k,
},
15000
),
});

const { plexUrl, plexUrl4k } = useDeepLinks({
Expand Down
21 changes: 8 additions & 13 deletions src/components/RequestList/RequestItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import StatusBadge from '@app/components/StatusBadge';
import useDeepLinks from '@app/hooks/useDeepLinks';
import { Permission, useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages';
import { refreshIntervalHelper } from '@app/utils/refreshIntervalHelper';
import {
ArrowPathIcon,
CheckIcon,
Expand Down Expand Up @@ -286,26 +287,20 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
request.type === 'movie'
? `/api/v1/movie/${request.media.tmdbId}`
: `/api/v1/tv/${request.media.tmdbId}`;

const refreshIntervalChecker = (data: MediaRequest) => {
if (
(data.media.downloadStatus ?? []).length > 0 ||
(data.media.downloadStatus4k ?? []).length > 0
) {
return 5000;
} else {
return 0;
}
};

const { data: title, error } = useSWR<MovieDetails | TvDetails>(
inView ? url : null
);
const { data: requestData, mutate: revalidate } = useSWR<MediaRequest>(
`/api/v1/request/${request.id}`,
{
fallbackData: request,
refreshInterval: refreshIntervalChecker(request),
refreshInterval: refreshIntervalHelper(
{
downloadStatus: request.media.downloadStatus,
downloadStatus4k: request.media.downloadStatus4k,
},
15000
),
}
);

Expand Down
41 changes: 36 additions & 5 deletions src/components/Settings/SettingsJobsCache/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ const messages: { [messageName: string]: MessageDescriptor } = defineMessages({
'Every {jobScheduleHours, plural, one {hour} other {{jobScheduleHours} hours}}',
editJobScheduleSelectorMinutes:
'Every {jobScheduleMinutes, plural, one {minute} other {{jobScheduleMinutes} minutes}}',
editJobScheduleSelectorSeconds:
'Every {jobScheduleSeconds, plural, one {second} other {{jobScheduleSeconds} seconds}}',
imagecache: 'Image Cache',
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>.',
Expand All @@ -78,7 +80,7 @@ interface Job {
id: JobId;
name: string;
type: 'process' | 'command';
interval: 'short' | 'long' | 'fixed';
interval: 'seconds' | 'minutes' | 'hours' | 'fixed';
cronSchedule: string;
nextExecutionTime: string;
running: boolean;
Expand All @@ -89,10 +91,11 @@ type JobModalState = {
job?: Job;
scheduleHours: number;
scheduleMinutes: number;
scheduleSeconds: number;
};

type JobModalAction =
| { type: 'set'; hours?: number; minutes?: number }
| { type: 'set'; hours?: number; minutes?: number; seconds?: number }
| {
type: 'close';
}
Expand All @@ -115,13 +118,15 @@ const jobModalReducer = (
job: action.job,
scheduleHours: 1,
scheduleMinutes: 5,
scheduleSeconds: 30,
};

case 'set':
return {
...state,
scheduleHours: action.hours ?? state.scheduleHours,
scheduleMinutes: action.minutes ?? state.scheduleMinutes,
scheduleSeconds: action.seconds ?? state.scheduleSeconds,
};
}
};
Expand Down Expand Up @@ -149,6 +154,7 @@ const SettingsJobs = () => {
isOpen: false,
scheduleHours: 1,
scheduleMinutes: 5,
scheduleSeconds: 30,
});
const [isSaving, setIsSaving] = useState(false);

Expand Down Expand Up @@ -200,9 +206,11 @@ const SettingsJobs = () => {
const jobScheduleCron = ['0', '0', '*', '*', '*', '*'];

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}`;
} else if (jobModalState.job?.interval === 'long') {
} else if (jobModalState.job?.interval === 'hours') {
jobScheduleCron[2] = `*/${jobModalState.scheduleHours}`;
} else {
// jobs with interval: fixed should not be editable
Expand Down Expand Up @@ -286,7 +294,30 @@ const SettingsJobs = () => {
{intl.formatMessage(messages.editJobSchedulePrompt)}
</label>
<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
name="jobScheduleMinutes"
className="inline"
Expand Down
Loading

0 comments on commit 2f1d500

Please sign in to comment.