feat: add reading_status_push auto-refresh schedule per library

- Migration 0059: reading_status_push_mode / last / next columns on libraries
- API: update_reading_status_provider accepts push_mode and calculates next_push_at
- job_poller: handles reading_status_push pending jobs
- Indexer scheduler: check_and_schedule_reading_status_push every minute
- Backoffice: schedule select in library settings modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-25 12:46:48 +01:00
parent 57ff1888eb
commit f3960666fa
11 changed files with 166 additions and 13 deletions

View File

@@ -15,6 +15,7 @@ interface LibraryActionsProps {
fallbackMetadataProvider: string | null;
metadataRefreshMode: string;
readingStatusProvider: string | null;
readingStatusPushMode: string;
onUpdate?: () => void;
}
@@ -27,6 +28,7 @@ export function LibraryActions({
fallbackMetadataProvider,
metadataRefreshMode,
readingStatusProvider,
readingStatusPushMode,
}: LibraryActionsProps) {
const { t } = useTranslation();
const [isOpen, setIsOpen] = useState(false);
@@ -43,6 +45,7 @@ export function LibraryActions({
const newFallbackProvider = (formData.get("fallback_metadata_provider") as string) || null;
const newMetadataRefreshMode = formData.get("metadata_refresh_mode") as string;
const newReadingStatusProvider = (formData.get("reading_status_provider") as string) || null;
const newReadingStatusPushMode = (formData.get("reading_status_push_mode") as string) || "manual";
try {
const [response] = await Promise.all([
@@ -64,7 +67,10 @@ export function LibraryActions({
fetch(`/api/libraries/${libraryId}/reading-status-provider`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ reading_status_provider: newReadingStatusProvider }),
body: JSON.stringify({
reading_status_provider: newReadingStatusProvider,
reading_status_push_mode: newReadingStatusPushMode,
}),
}),
]);
@@ -289,6 +295,22 @@ export function LibraryActions({
</div>
<p className="text-xs text-muted-foreground mt-1.5">{t("libraryActions.readingStatusProviderDesc")}</p>
</div>
<div>
<div className="flex items-center justify-between gap-4">
<label className="text-sm font-medium text-foreground">{t("libraryActions.readingStatusPushSchedule")}</label>
<select
name="reading_status_push_mode"
defaultValue={readingStatusPushMode}
className="text-sm border border-border rounded-lg px-3 py-1.5 bg-background min-w-[160px] shrink-0"
>
<option value="manual">{t("monitoring.manual")}</option>
<option value="hourly">{t("monitoring.hourly")}</option>
<option value="daily">{t("monitoring.daily")}</option>
<option value="weekly">{t("monitoring.weekly")}</option>
</select>
</div>
<p className="text-xs text-muted-foreground mt-1.5">{t("libraryActions.readingStatusPushScheduleDesc")}</p>
</div>
</div>
{saveError && (