feat: gestion des téléchargements qBittorrent avec import automatique
- Nouvelle table `torrent_downloads` pour suivre les téléchargements gérés - API : endpoint POST /torrent-downloads/notify (webhook optionnel) et GET /torrent-downloads - Poller background toutes les 30s qui interroge qBittorrent pour détecter les torrents terminés — aucune config "run external program" nécessaire - Import automatique : déplacement des fichiers vers la série cible, renommage selon le pattern existant (détection de la largeur des digits), support packs multi-volumes, scan job déclenché après import - Page /downloads dans le backoffice : filtres, auto-refresh, carte par download - Toggle auto-import intégré dans la card qBittorrent des settings - Erreurs de détection download affichées dans le détail des jobs - Volume /downloads monté dans docker-compose Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,30 @@ export function DownloadDetectionReportCard({ report, t }: { report: DownloadDet
|
||||
);
|
||||
}
|
||||
|
||||
export function DownloadDetectionErrorsCard({ results, t }: {
|
||||
results: DownloadDetectionResultDto[];
|
||||
t: TranslateFunction;
|
||||
}) {
|
||||
if (results.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Card className="lg:col-span-2">
|
||||
<CardHeader>
|
||||
<CardTitle>{t("jobDetail.downloadErrors")}</CardTitle>
|
||||
<CardDescription>{t("jobDetail.downloadErrorsDesc", { count: String(results.length) })}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2 max-h-80 overflow-y-auto">
|
||||
{results.map((r) => (
|
||||
<div key={r.id} className="p-3 bg-destructive/10 rounded-lg border border-destructive/20">
|
||||
<p className="text-sm font-semibold text-destructive mb-1">{r.series_name}</p>
|
||||
<p className="text-sm text-destructive/80">{r.error_message ?? "Erreur inconnue"}</p>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export function DownloadDetectionResultsCard({ results, libraryId, t }: {
|
||||
results: DownloadDetectionResultDto[];
|
||||
libraryId: string | null;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { JobTimelineCard } from "./components/JobTimelineCard";
|
||||
import { JobProgressCard, IndexStatsCard, ThumbnailStatsCard } from "./components/JobProgressCard";
|
||||
import { MetadataBatchReportCard, MetadataBatchResultsCard, MetadataRefreshReportCard, MetadataRefreshChangesCard } from "./components/MetadataReportCards";
|
||||
import { ReadingStatusMatchReportCard, ReadingStatusMatchResultsCard, ReadingStatusPushReportCard, ReadingStatusPushResultsCard } from "./components/ReadingStatusReportCards";
|
||||
import { DownloadDetectionReportCard, DownloadDetectionResultsCard } from "./components/DownloadDetectionCards";
|
||||
import { DownloadDetectionReportCard, DownloadDetectionResultsCard, DownloadDetectionErrorsCard } from "./components/DownloadDetectionCards";
|
||||
import { JobErrorsCard } from "./components/JobErrorsCard";
|
||||
|
||||
interface JobDetailPageProps {
|
||||
@@ -148,10 +148,12 @@ export default async function JobDetailPage({ params }: JobDetailPageProps) {
|
||||
|
||||
let downloadDetectionReport: DownloadDetectionReportDto | null = null;
|
||||
let downloadDetectionResults: DownloadDetectionResultDto[] = [];
|
||||
let downloadDetectionErrors: DownloadDetectionResultDto[] = [];
|
||||
if (isDownloadDetection) {
|
||||
[downloadDetectionReport, downloadDetectionResults] = await Promise.all([
|
||||
[downloadDetectionReport, downloadDetectionResults, downloadDetectionErrors] = await Promise.all([
|
||||
getDownloadDetectionReport(id).catch(() => null),
|
||||
getDownloadDetectionResults(id, "found").catch(() => []),
|
||||
getDownloadDetectionResults(id, "error").catch(() => []),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -270,6 +272,7 @@ export default async function JobDetailPage({ params }: JobDetailPageProps) {
|
||||
|
||||
{/* Download detection */}
|
||||
{isDownloadDetection && downloadDetectionReport && <DownloadDetectionReportCard report={downloadDetectionReport} t={t} />}
|
||||
{isDownloadDetection && <DownloadDetectionErrorsCard results={downloadDetectionErrors} t={t} />}
|
||||
{isDownloadDetection && <DownloadDetectionResultsCard results={downloadDetectionResults} libraryId={job.library_id} t={t} />}
|
||||
|
||||
{/* Metadata batch results */}
|
||||
|
||||
Reference in New Issue
Block a user