feat: add download detection job with Prowlarr integration

For each series with missing volumes and an approved metadata link,
calls Prowlarr to find available matching releases and stores them in
a report (no auto-download). Includes per-series detail page, Telegram
notifications with per-event toggles, and stats display in the jobs table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-25 13:47:29 +01:00
parent e5e4993e7b
commit d2c9f28227
15 changed files with 1033 additions and 13 deletions

View File

@@ -1141,6 +1141,53 @@ export async function getReadingStatusPushResults(jobId: string) {
return apiFetch<ReadingStatusPushResultDto[]>(`/reading-status/push/${jobId}/results`);
}
export async function startDownloadDetection(libraryId: string) {
return apiFetch<{ id: string; status: string }>("/download-detection/start", {
method: "POST",
body: JSON.stringify({ library_id: libraryId }),
});
}
export type AvailableReleaseDto = {
title: string;
size: number;
download_url: string | null;
indexer: string | null;
seeders: number | null;
matched_missing_volumes: number[];
};
export type DownloadDetectionReportDto = {
job_id: string;
status: string;
total_series: number;
found: number;
not_found: number;
no_missing: number;
no_metadata: number;
errors: number;
};
export type DownloadDetectionResultDto = {
id: string;
series_name: string;
status: "found" | "not_found" | "no_missing" | "no_metadata" | "error";
missing_count: number;
available_releases: AvailableReleaseDto[] | null;
error_message: string | null;
};
export async function getDownloadDetectionReport(jobId: string) {
return apiFetch<DownloadDetectionReportDto>(`/download-detection/${jobId}/report`);
}
export async function getDownloadDetectionResults(jobId: string, status?: string) {
const url = status
? `/download-detection/${jobId}/results?status=${encodeURIComponent(status)}`
: `/download-detection/${jobId}/results`;
return apiFetch<DownloadDetectionResultDto[]>(url);
}
export type RefreshFieldDiff = {
field: string;
old?: unknown;