mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-04 21:39:49 +00:00
Refac react frontend (#790)
* Add channel config endpoint * Add channel aggs * Add playlist show subscribed only toggle * Fix refresh always on filterbar toggle * Add loadingindicator for watchstate change * Fix missing space in scheduling * Add schedule request and apprisenotifcation * Refac upgrade TypeScript target to include 2024 for Object.groupBy * WIP: Schedule page * WIP: Schedule page * Add schedule management ( - notification ) * Fix missing space * Refac show current selection in input * Add apprise notifictation url * Add Stream & Shorts channel pages * Refac autotarget input on search page * Fix input requiring 1 instead of 0 * Fix remove unused function * Chore: npm audit fix * Refac get channel_overwrites from channelById * Refac remove defaultvalues form select * Fix delay content refresh to allow the backend to update subscribed state * Fix styling selection * Fix lint * Fix spelling * Fix remove unused import * Chore: update all dependencies - React 19 & vite 6 * Add missing property to ValidatedCookieType * Refac fix complaints about JSX.Element, used ReactNode instead * Refac remove unused dependency * Refac replace react-helmet with react 19 implementation * Fix Application Settings page * Chore update dependencies * Add simple playlist autoplay feature * Refac use server provided channel images path * Refac use server provided playlistthumbnail images path * Add save and restore video playback speed
This commit is contained in:
@@ -1,462 +1,496 @@
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Notifications from '../components/Notifications';
|
||||
import SettingsNavigation from '../components/SettingsNavigation';
|
||||
import Button from '../components/Button';
|
||||
|
||||
type CronTabType = {
|
||||
minute: number;
|
||||
hour: number;
|
||||
day_of_week: number;
|
||||
};
|
||||
|
||||
type SchedulerErrorType = {
|
||||
errors: string[];
|
||||
};
|
||||
|
||||
type NotificationItemType = {
|
||||
task: string;
|
||||
notification: {
|
||||
title: string;
|
||||
urls: string[];
|
||||
};
|
||||
};
|
||||
|
||||
type SettingsSchedulingResponseType = {
|
||||
update_subscribed: {
|
||||
crontab: CronTabType;
|
||||
};
|
||||
check_reindex: {
|
||||
crontab: CronTabType;
|
||||
task_config: {
|
||||
days: 0;
|
||||
};
|
||||
};
|
||||
thumbnail_check: {
|
||||
crontab: CronTabType;
|
||||
};
|
||||
download_pending: {
|
||||
crontab: CronTabType;
|
||||
};
|
||||
run_backup: {
|
||||
crontab: CronTabType;
|
||||
task_config: {
|
||||
rotate: false;
|
||||
};
|
||||
};
|
||||
notifications: {
|
||||
items: NotificationItemType[];
|
||||
};
|
||||
scheduler_form: {
|
||||
update_subscribed: SchedulerErrorType;
|
||||
download_pending: SchedulerErrorType;
|
||||
check_reindex: SchedulerErrorType;
|
||||
thumbnail_check: SchedulerErrorType;
|
||||
run_backup: SchedulerErrorType;
|
||||
};
|
||||
};
|
||||
|
||||
const SettingsScheduling = () => {
|
||||
const response: SettingsSchedulingResponseType = {
|
||||
update_subscribed: {
|
||||
crontab: {
|
||||
minute: 0,
|
||||
hour: 0,
|
||||
day_of_week: 0,
|
||||
},
|
||||
},
|
||||
check_reindex: {
|
||||
crontab: {
|
||||
minute: 0,
|
||||
hour: 0,
|
||||
day_of_week: 0,
|
||||
},
|
||||
task_config: {
|
||||
days: 0,
|
||||
},
|
||||
},
|
||||
thumbnail_check: {
|
||||
crontab: {
|
||||
minute: 0,
|
||||
hour: 0,
|
||||
day_of_week: 0,
|
||||
},
|
||||
},
|
||||
download_pending: {
|
||||
crontab: {
|
||||
minute: 0,
|
||||
hour: 0,
|
||||
day_of_week: 0,
|
||||
},
|
||||
},
|
||||
run_backup: {
|
||||
crontab: {
|
||||
minute: 0,
|
||||
hour: 0,
|
||||
day_of_week: 0,
|
||||
},
|
||||
task_config: {
|
||||
rotate: false,
|
||||
},
|
||||
},
|
||||
notifications: {
|
||||
items: [
|
||||
{
|
||||
task: '',
|
||||
notification: {
|
||||
title: '',
|
||||
urls: [''],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
scheduler_form: {
|
||||
update_subscribed: {
|
||||
errors: ['error?'],
|
||||
},
|
||||
download_pending: {
|
||||
errors: ['error?'],
|
||||
},
|
||||
check_reindex: {
|
||||
errors: ['error?'],
|
||||
},
|
||||
thumbnail_check: {
|
||||
errors: ['error?'],
|
||||
},
|
||||
run_backup: {
|
||||
errors: ['error?'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const {
|
||||
check_reindex,
|
||||
download_pending,
|
||||
notifications,
|
||||
run_backup,
|
||||
scheduler_form,
|
||||
thumbnail_check,
|
||||
update_subscribed,
|
||||
} = response;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>TA | Scheduling Settings</title>
|
||||
</Helmet>
|
||||
<div className="boxed-content">
|
||||
<SettingsNavigation />
|
||||
<Notifications pageName={'all'} />
|
||||
|
||||
<div className="title-bar">
|
||||
<h1>Scheduler Setup</h1>
|
||||
<div className="settings-group">
|
||||
<p>
|
||||
Schedule settings expect a cron like format, where the first value is minute, second
|
||||
is hour and third is day of the week.
|
||||
</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<span className="settings-current">0 15 *</span>: Run task every day at 15:00 in the
|
||||
afternoon.
|
||||
</li>
|
||||
<li>
|
||||
<span className="settings-current">30 8 */2</span>: Run task every second day of the
|
||||
week (Sun, Tue, Thu, Sat) at 08:30 in the morning.
|
||||
</li>
|
||||
<li>
|
||||
<span className="settings-current">auto</span>: Sensible default.
|
||||
</li>
|
||||
</ul>
|
||||
<p>Note:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Avoid an unnecessary frequent schedule to not get blocked by YouTube. For that
|
||||
reason, the scheduler doesn't support schedules that trigger more than once per
|
||||
hour.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{% url 'settings_scheduling' %}" method="POST" name="scheduler-update">
|
||||
<div className="settings-group">
|
||||
<h2>Rescan Subscriptions</h2>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Become a sponsor and join{' '}
|
||||
<a href="https://members.tubearchivist.com/" target="_blank">
|
||||
members.tubearchivist.com
|
||||
</a>{' '}
|
||||
to get access to <span className="settings-current">real time</span> notifications
|
||||
for new videos uploaded by your favorite channels.
|
||||
</p>
|
||||
<p>
|
||||
Current rescan schedule:{' '}
|
||||
<span className="settings-current">
|
||||
{update_subscribed && (
|
||||
<>
|
||||
{update_subscribed.crontab.minute} {update_subscribed.crontab.hour}{' '}
|
||||
{update_subscribed.crontab.day_of_week}
|
||||
<Button
|
||||
label="Delete"
|
||||
data-schedule="update_subscribed"
|
||||
onclick="deleteSchedule(this)"
|
||||
className="danger-button"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!update_subscribed && 'False'}
|
||||
</span>
|
||||
</p>
|
||||
<p>Periodically rescan your subscriptions:</p>
|
||||
{scheduler_form.update_subscribed.errors.map(error => {
|
||||
return (
|
||||
<p key={error} className="danger-zone">
|
||||
{error}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
|
||||
<input type="text" name="update_subscribed" id="id_update_subscribed" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-group">
|
||||
<h2>Start Download</h2>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Current Download schedule:{' '}
|
||||
<span className="settings-current">
|
||||
{download_pending && (
|
||||
<>
|
||||
{download_pending.crontab.minute} {download_pending.crontab.hour}{' '}
|
||||
{download_pending.crontab.day_of_week}
|
||||
<Button
|
||||
label="Delete"
|
||||
data-schedule="download_pending"
|
||||
onclick="deleteSchedule(this)"
|
||||
className="danger-button"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!download_pending && 'False'}
|
||||
</span>
|
||||
</p>
|
||||
<p>Automatic video download schedule:</p>
|
||||
{scheduler_form.download_pending.errors.map(error => {
|
||||
return (
|
||||
<p key={error} className="danger-zone">
|
||||
{error}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
|
||||
<input type="text" name="download_pending" id="id_download_pending" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-group">
|
||||
<h2>Refresh Metadata</h2>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Current Metadata refresh schedule:{' '}
|
||||
<span className="settings-current">
|
||||
{check_reindex && (
|
||||
<>
|
||||
{check_reindex.crontab.minute} {check_reindex.crontab.hour}{' '}
|
||||
{check_reindex.crontab.day_of_week}
|
||||
<Button
|
||||
label="Delete"
|
||||
data-schedule="check_reindex"
|
||||
onclick="deleteSchedule(this)"
|
||||
className="danger-button"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!check_reindex && 'False'}
|
||||
</span>
|
||||
</p>
|
||||
<p>Daily schedule to refresh metadata from YouTube:</p>
|
||||
|
||||
<input type="text" name="check_reindex" id="id_check_reindex" />
|
||||
</div>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Current refresh for metadata older than x days:{' '}
|
||||
<span className="settings-current">{check_reindex.task_config.days}</span>
|
||||
</p>
|
||||
<p>Refresh older than x days, recommended 90:</p>
|
||||
{scheduler_form.check_reindex.errors.map(error => {
|
||||
return (
|
||||
<p key={error} className="danger-zone">
|
||||
{error}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
|
||||
<input type="number" name="check_reindex_days" id="id_check_reindex_days" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-group">
|
||||
<h2>Thumbnail Check</h2>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Current thumbnail check schedule:{' '}
|
||||
<span className="settings-current">
|
||||
{thumbnail_check && (
|
||||
<>
|
||||
{thumbnail_check.crontab.minute} {thumbnail_check.crontab.hour}{' '}
|
||||
{thumbnail_check.crontab.day_of_week}
|
||||
<Button
|
||||
label="Delete"
|
||||
data-schedule="thumbnail_check"
|
||||
onclick="deleteSchedule(this)"
|
||||
className="danger-button"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!thumbnail_check && 'False'}
|
||||
</span>
|
||||
</p>
|
||||
<p>Periodically check and cleanup thumbnails:</p>
|
||||
{scheduler_form.thumbnail_check.errors.map(error => {
|
||||
return (
|
||||
<p key={error} className="danger-zone">
|
||||
{error}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
|
||||
<input type="text" name="thumbnail_check" id="id_thumbnail_check" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-group">
|
||||
<h2>ZIP file index backup</h2>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
<i>
|
||||
Zip file backups are very slow for large archives and consistency is not
|
||||
guaranteed, use snapshots instead. Make sure no other tasks are running when
|
||||
creating a Zip file backup.
|
||||
</i>
|
||||
</p>
|
||||
<p>
|
||||
Current index backup schedule:{' '}
|
||||
<span className="settings-current">
|
||||
{run_backup && (
|
||||
<>
|
||||
{run_backup.crontab.minute} {run_backup.crontab.hour}{' '}
|
||||
{run_backup.crontab.day_of_week}
|
||||
<Button
|
||||
label="Delete"
|
||||
data-schedule="run_backup"
|
||||
onclick="deleteSchedule(this)"
|
||||
className="danger-button"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!run_backup && 'False'}
|
||||
</span>
|
||||
</p>
|
||||
<p>Automatically backup metadata to a zip file:</p>
|
||||
{scheduler_form.run_backup.errors.map(error => {
|
||||
return (
|
||||
<p key={error} className="danger-zone">
|
||||
{error}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
|
||||
<input type="text" name="run_backup" id="id_run_backup" />
|
||||
</div>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Current backup files to keep:{' '}
|
||||
<span className="settings-current">{run_backup.task_config.rotate}</span>
|
||||
</p>
|
||||
<p>Max auto backups to keep:</p>
|
||||
|
||||
<input type="number" name="run_backup_rotate" id="id_run_backup_rotate" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-group">
|
||||
<h2>Add Notification URL</h2>
|
||||
<div className="settings-item">
|
||||
{notifications && (
|
||||
<>
|
||||
<p>
|
||||
<Button
|
||||
label="Show"
|
||||
type="button"
|
||||
onclick="textReveal(this)"
|
||||
id="text-reveal-button"
|
||||
/>{' '}
|
||||
stored notification links
|
||||
</p>
|
||||
<div id="text-reveal" className="description-text">
|
||||
{notifications.items.map(({ task, notification }) => {
|
||||
return (
|
||||
<>
|
||||
<h3 key={task}>{notification.title}</h3>
|
||||
{notification.urls.map((url: string) => {
|
||||
return (
|
||||
<p>
|
||||
<Button
|
||||
type="button"
|
||||
className="danger-button"
|
||||
label="Delete"
|
||||
data-url={url}
|
||||
data-task={task}
|
||||
onclick="deleteNotificationUrl(this)"
|
||||
/>
|
||||
<span> {url}</span>
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!notifications && <p>No notifications stored</p>}
|
||||
</div>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
<i>
|
||||
Send notification on completed tasks with the help of the{' '}
|
||||
<a href="https://github.com/caronc/apprise" target="_blank">
|
||||
Apprise
|
||||
</a>{' '}
|
||||
library.
|
||||
</i>
|
||||
</p>
|
||||
<select name="task" id="id_task" defaultValue="">
|
||||
<option value="">-- select task --</option>
|
||||
<option value="update_subscribed">Rescan your Subscriptions</option>
|
||||
<option value="extract_download">Add to download queue</option>
|
||||
<option value="download_pending">Downloading</option>
|
||||
<option value="check_reindex">Reindex Documents</option>
|
||||
</select>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
name="notification_url"
|
||||
placeholder="Apprise notification URL"
|
||||
id="id_notification_url"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button type="submit" name="scheduler-settings" label="Update Scheduler Settings" />
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsScheduling;
|
||||
import Notifications from '../components/Notifications';
|
||||
import SettingsNavigation from '../components/SettingsNavigation';
|
||||
import Button from '../components/Button';
|
||||
import PaginationDummy from '../components/PaginationDummy';
|
||||
import { useEffect, useState } from 'react';
|
||||
import loadSchedule, { ScheduleResponseType } from '../api/loader/loadSchedule';
|
||||
import loadAppriseNotification, {
|
||||
AppriseNotificationType,
|
||||
} from '../api/loader/loadAppriseNotification';
|
||||
import deleteTaskSchedule from '../api/actions/deleteTaskSchedule';
|
||||
import createTaskSchedule from '../api/actions/createTaskSchedule';
|
||||
import createAppriseNotificationUrl, {
|
||||
AppriseTaskNameType,
|
||||
} from '../api/actions/createAppriseNotificationUrl';
|
||||
import deleteAppriseNotificationUrl from '../api/actions/deleteAppriseNotificationUrl';
|
||||
|
||||
const SettingsScheduling = () => {
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
|
||||
const [scheduleResponse, setScheduleResponse] = useState<ScheduleResponseType>([]);
|
||||
const [appriseNotification, setAppriseNotification] = useState<AppriseNotificationType>();
|
||||
|
||||
const [updateSubscribed, setUpdateSubscribed] = useState<string | undefined>();
|
||||
const [downloadPending, setDownloadPending] = useState<string | undefined>();
|
||||
const [checkReindex, setCheckReindex] = useState<string | undefined>();
|
||||
const [checkReindexDays, setCheckReindexDays] = useState<number | undefined>(undefined);
|
||||
const [thumbnailCheck, setThumbnailCheck] = useState<string | undefined>();
|
||||
const [zipBackup, setZipBackup] = useState<string | undefined>();
|
||||
const [zipBackupDays, setZipBackupDays] = useState<number | undefined>(undefined);
|
||||
const [notificationUrl, setNotificationUrl] = useState<string | undefined>(undefined);
|
||||
const [notificationTask, setNotificationTask] = useState<AppriseTaskNameType | string>('');
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (refresh) {
|
||||
const scheduleResponse = await loadSchedule();
|
||||
const appriseNotificationResponse = await loadAppriseNotification();
|
||||
|
||||
setScheduleResponse(scheduleResponse);
|
||||
setAppriseNotification(appriseNotificationResponse);
|
||||
|
||||
setRefresh(false);
|
||||
}
|
||||
})();
|
||||
}, [refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
setRefresh(true);
|
||||
}, []);
|
||||
|
||||
const groupedSchedules = Object.groupBy(scheduleResponse, ({ name }) => name);
|
||||
|
||||
console.log(groupedSchedules);
|
||||
|
||||
const { update_subscribed, download_pending, run_backup, check_reindex, thumbnail_check } =
|
||||
groupedSchedules;
|
||||
|
||||
const updateSubscribedSchedule = update_subscribed?.pop();
|
||||
const downloadPendingSchedule = download_pending?.pop();
|
||||
const runBackup = run_backup?.pop();
|
||||
const checkReindexSchedule = check_reindex?.pop();
|
||||
const thumbnailCheckSchedule = thumbnail_check?.pop();
|
||||
|
||||
return (
|
||||
<>
|
||||
<title>TA | Scheduling Settings</title>
|
||||
<div className="boxed-content">
|
||||
<SettingsNavigation />
|
||||
<Notifications pageName={'all'} />
|
||||
|
||||
<div className="title-bar">
|
||||
<h1>Scheduler Setup</h1>
|
||||
<div className="settings-group">
|
||||
<p>
|
||||
Schedule settings expect a cron like format, where the first value is minute, second
|
||||
is hour and third is day of the week.
|
||||
</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<span className="settings-current">0 15 *</span>: Run task every day at 15:00 in the
|
||||
afternoon.
|
||||
</li>
|
||||
<li>
|
||||
<span className="settings-current">30 8 */2</span>: Run task every second day of the
|
||||
week (Sun, Tue, Thu, Sat) at 08:30 in the morning.
|
||||
</li>
|
||||
<li>
|
||||
<span className="settings-current">auto</span>: Sensible default.
|
||||
</li>
|
||||
</ul>
|
||||
<p>Note:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Avoid an unnecessary frequent schedule to not get blocked by YouTube. For that
|
||||
reason, the scheduler doesn't support schedules that trigger more than once per
|
||||
hour.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-group">
|
||||
<h2>Rescan Subscriptions</h2>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Become a sponsor and join{' '}
|
||||
<a href="https://members.tubearchivist.com/" target="_blank">
|
||||
members.tubearchivist.com
|
||||
</a>{' '}
|
||||
to get access to <span className="settings-current">real time</span> notifications for
|
||||
new videos uploaded by your favorite channels.
|
||||
</p>
|
||||
<p>
|
||||
Current rescan schedule:{' '}
|
||||
<span className="settings-current">
|
||||
{!updateSubscribedSchedule && 'False'}
|
||||
{updateSubscribedSchedule && (
|
||||
<>
|
||||
{updateSubscribedSchedule?.schedule}{' '}
|
||||
<Button
|
||||
label="Delete"
|
||||
data-schedule="update_subscribed"
|
||||
onClick={async () => {
|
||||
await deleteTaskSchedule('update_subscribed');
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
className="danger-button"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</p>
|
||||
<p>Periodically rescan your subscriptions:</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={updateSubscribed || updateSubscribedSchedule?.schedule}
|
||||
onChange={e => {
|
||||
setUpdateSubscribed(e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Save"
|
||||
onClick={async () => {
|
||||
await createTaskSchedule('update_subscribed', {
|
||||
schedule: updateSubscribed,
|
||||
});
|
||||
|
||||
setUpdateSubscribed('');
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-group">
|
||||
<h2>Start Download</h2>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Current Download schedule:{' '}
|
||||
<span className="settings-current">
|
||||
{!download_pending && 'False'}
|
||||
{downloadPendingSchedule && (
|
||||
<>
|
||||
{downloadPendingSchedule?.schedule}{' '}
|
||||
<Button
|
||||
label="Delete"
|
||||
className="danger-button"
|
||||
onClick={async () => {
|
||||
await deleteTaskSchedule('download_pending');
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</p>
|
||||
<p>Automatic video download schedule:</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={downloadPending || downloadPendingSchedule?.schedule}
|
||||
onChange={e => {
|
||||
setDownloadPending(e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Save"
|
||||
onClick={async () => {
|
||||
await createTaskSchedule('download_pending', {
|
||||
schedule: downloadPending,
|
||||
});
|
||||
|
||||
setDownloadPending('');
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-group">
|
||||
<h2>Refresh Metadata</h2>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Current Metadata refresh schedule:{' '}
|
||||
<span className="settings-current">
|
||||
{!checkReindexSchedule && 'False'}
|
||||
{checkReindexSchedule && (
|
||||
<>
|
||||
{checkReindexSchedule?.schedule}{' '}
|
||||
<Button
|
||||
label="Delete"
|
||||
className="danger-button"
|
||||
onClick={async () => {
|
||||
await deleteTaskSchedule('check_reindex');
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</p>
|
||||
<p>Daily schedule to refresh metadata from YouTube:</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={checkReindex || checkReindexSchedule?.schedule}
|
||||
onChange={e => {
|
||||
setCheckReindex(e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Save"
|
||||
onClick={async () => {
|
||||
await createTaskSchedule('check_reindex', {
|
||||
schedule: checkReindex,
|
||||
});
|
||||
|
||||
setCheckReindex('');
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Current refresh for metadata older than x days:{' '}
|
||||
<span className="settings-current">{checkReindexSchedule?.config?.days}</span>
|
||||
</p>
|
||||
<p>Refresh older than x days, recommended 90:</p>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
value={checkReindexDays || checkReindexSchedule?.config?.days}
|
||||
onChange={e => {
|
||||
setCheckReindexDays(Number(e.currentTarget.value));
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Save"
|
||||
onClick={async () => {
|
||||
await createTaskSchedule('check_reindex', {
|
||||
config: {
|
||||
days: checkReindexDays,
|
||||
},
|
||||
});
|
||||
|
||||
setCheckReindexDays(undefined);
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-group">
|
||||
<h2>Thumbnail Check</h2>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Current thumbnail check schedule:{' '}
|
||||
<span className="settings-current">
|
||||
{!thumbnailCheckSchedule && 'False'}
|
||||
{thumbnailCheckSchedule && (
|
||||
<>
|
||||
{thumbnailCheckSchedule?.schedule}{' '}
|
||||
<Button
|
||||
label="Delete"
|
||||
className="danger-button"
|
||||
onClick={async () => {
|
||||
await deleteTaskSchedule('thumbnail_check');
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</p>
|
||||
<p>Periodically check and cleanup thumbnails:</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={thumbnailCheck || thumbnailCheckSchedule?.schedule}
|
||||
onChange={e => {
|
||||
setThumbnailCheck(e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Save"
|
||||
onClick={async () => {
|
||||
await createTaskSchedule('thumbnail_check', {
|
||||
schedule: thumbnailCheck,
|
||||
});
|
||||
|
||||
setThumbnailCheck('');
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-group">
|
||||
<h2>ZIP file index backup</h2>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
<i>
|
||||
Zip file backups are very slow for large archives and consistency is not guaranteed,
|
||||
use snapshots instead. Make sure no other tasks are running when creating a Zip file
|
||||
backup.
|
||||
</i>
|
||||
</p>
|
||||
<p>
|
||||
Current index backup schedule:{' '}
|
||||
<span className="settings-current">
|
||||
{!runBackup && 'False'}
|
||||
{runBackup && (
|
||||
<>
|
||||
{runBackup.schedule}{' '}
|
||||
<Button
|
||||
label="Delete"
|
||||
className="danger-button"
|
||||
onClick={async () => {
|
||||
await deleteTaskSchedule('run_backup');
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</p>
|
||||
<p>Automatically backup metadata to a zip file:</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={zipBackup || runBackup?.schedule}
|
||||
onChange={e => {
|
||||
setZipBackup(e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Save"
|
||||
onClick={async () => {
|
||||
await createTaskSchedule('run_backup', {
|
||||
schedule: zipBackup,
|
||||
});
|
||||
|
||||
setZipBackup('');
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
Current backup files to keep:{' '}
|
||||
<span className="settings-current">{runBackup?.config?.rotate}</span>
|
||||
</p>
|
||||
<p>Max auto backups to keep:</p>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
value={(zipBackupDays || runBackup?.config?.rotate)?.toString()}
|
||||
onChange={e => {
|
||||
setZipBackupDays(Number(e.currentTarget.value));
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Save"
|
||||
onClick={async () => {
|
||||
await createTaskSchedule('run_backup', {
|
||||
config: {
|
||||
rotate: zipBackupDays,
|
||||
},
|
||||
});
|
||||
|
||||
setZipBackupDays(undefined);
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-group">
|
||||
<h2>Add Notification URL</h2>
|
||||
<div className="settings-item">
|
||||
{!appriseNotification && <p>No notifications stored</p>}
|
||||
{appriseNotification && (
|
||||
<>
|
||||
<div className="description-text">
|
||||
{Object.entries(appriseNotification)?.map(([key, { urls, title }]) => {
|
||||
return (
|
||||
<>
|
||||
<h3 key={key}>{title}</h3>
|
||||
{urls.map((url: string) => {
|
||||
return (
|
||||
<p>
|
||||
<span>{url} </span>
|
||||
<Button
|
||||
type="button"
|
||||
className="danger-button"
|
||||
label="Delete"
|
||||
onClick={async () => {
|
||||
await deleteAppriseNotificationUrl(key as AppriseTaskNameType);
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="settings-item">
|
||||
<p>
|
||||
<i>
|
||||
Send notification on completed tasks with the help of the{' '}
|
||||
<a href="https://github.com/caronc/apprise" target="_blank">
|
||||
Apprise
|
||||
</a>{' '}
|
||||
library.
|
||||
</i>
|
||||
</p>
|
||||
<select
|
||||
defaultValue=""
|
||||
value={notificationTask}
|
||||
onChange={e => {
|
||||
setNotificationTask(e.currentTarget.value);
|
||||
}}
|
||||
>
|
||||
<option value="">-- select task --</option>
|
||||
<option value="update_subscribed">Rescan your Subscriptions</option>
|
||||
<option value="extract_download">Add to download queue</option>
|
||||
<option value="download_pending">Downloading</option>
|
||||
<option value="check_reindex">Reindex Documents</option>
|
||||
</select>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Apprise notification URL"
|
||||
value={notificationUrl}
|
||||
onChange={e => {
|
||||
setNotificationUrl(e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Save"
|
||||
onClick={async () => {
|
||||
await createAppriseNotificationUrl(
|
||||
notificationTask as AppriseTaskNameType,
|
||||
notificationUrl || '',
|
||||
);
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PaginationDummy />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsScheduling;
|
||||
|
||||
Reference in New Issue
Block a user