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:
@@ -24,6 +24,7 @@ interface JobRowProps {
|
||||
refreshed?: number;
|
||||
linked?: number;
|
||||
pushed?: number;
|
||||
found?: number;
|
||||
} | null;
|
||||
progress_percent: number | null;
|
||||
processed_files: number | null;
|
||||
@@ -69,6 +70,7 @@ export function JobRow({ job, libraryName, highlighted, onCancel, onReplay, form
|
||||
const isMetadataRefresh = job.type === "metadata_refresh";
|
||||
const isReadingStatusMatch = job.type === "reading_status_match";
|
||||
const isReadingStatusPush = job.type === "reading_status_push";
|
||||
const isDownloadDetection = job.type === "download_detection";
|
||||
|
||||
// Thumbnails progress (Phase 2: extracting_pages + generating_thumbnails)
|
||||
const thumbInProgress = hasThumbnailPhase && (job.status === "running" || isPhase2);
|
||||
@@ -210,6 +212,23 @@ export function JobRow({ job, libraryName, highlighted, onCancel, onReplay, form
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{/* Download detection: total series + found count */}
|
||||
{isDownloadDetection && job.total_files != null && job.total_files > 0 && (
|
||||
<Tooltip label={t("jobRow.seriesTotal", { count: job.total_files })}>
|
||||
<span className="inline-flex items-center gap-1 text-info">
|
||||
<Icon name="series" size="sm" />
|
||||
{job.total_files}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isDownloadDetection && job.stats_json?.found != null && job.stats_json.found > 0 && (
|
||||
<Tooltip label={t("jobRow.downloadFound", { count: job.stats_json.found })}>
|
||||
<span className="inline-flex items-center gap-1 text-success">
|
||||
<Icon name="download" size="sm" />
|
||||
{job.stats_json.found}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{/* Errors */}
|
||||
{errors > 0 && (
|
||||
<Tooltip label={t("jobRow.errors", { count: errors })}>
|
||||
@@ -229,7 +248,7 @@ export function JobRow({ job, libraryName, highlighted, onCancel, onReplay, form
|
||||
</Tooltip>
|
||||
)}
|
||||
{/* Nothing to show */}
|
||||
{indexed === 0 && removed === 0 && errors === 0 && scanned === 0 && !hasThumbnailPhase && !isMetadataBatch && !isMetadataRefresh && !isReadingStatusMatch && !isReadingStatusPush && (
|
||||
{indexed === 0 && removed === 0 && errors === 0 && scanned === 0 && !hasThumbnailPhase && !isMetadataBatch && !isMetadataRefresh && !isReadingStatusMatch && !isReadingStatusPush && !isDownloadDetection && (
|
||||
<span className="text-sm text-muted-foreground">—</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -37,7 +37,8 @@ type IconName =
|
||||
| "authors"
|
||||
| "bell"
|
||||
| "link"
|
||||
| "eye";
|
||||
| "eye"
|
||||
| "download";
|
||||
|
||||
type IconSize = "sm" | "md" | "lg" | "xl";
|
||||
|
||||
@@ -94,6 +95,7 @@ const icons: Record<IconName, string> = {
|
||||
bell: "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9",
|
||||
link: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1",
|
||||
eye: "M15 12a3 3 0 11-6 0 3 3 0 016 0zm-3-9C7.477 3 3.268 6.11 1.5 12c1.768 5.89 5.977 9 10.5 9s8.732-3.11 10.5-9C20.732 6.11 16.523 3 12 3z",
|
||||
download: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4",
|
||||
};
|
||||
|
||||
const colorClasses: Partial<Record<IconName, string>> = {
|
||||
|
||||
Reference in New Issue
Block a user