diff --git a/backend/app/api/settings.py b/backend/app/api/settings.py index 15c52e9..145890e 100644 --- a/backend/app/api/settings.py +++ b/backend/app/api/settings.py @@ -141,11 +141,23 @@ async def system_stats(): ).all() integrity_counts = {row[0]: row[1] for row in integrity_rows} - # Active batch (most recent running) + # Active batch = running batch that still has outstanding work. + # Plain "most recent running" picks a freshly-created scan that + # enqueued zero new files and hides the older batch that's + # actually being processed; the EXISTS clause filters those + # empty batches out. active_batch_row = ( await session.execute( select(ImportBatch) - .where(ImportBatch.status == "running") + .where( + ImportBatch.status == "running", + select(ImportTask.id) + .where( + ImportTask.batch_id == ImportBatch.id, + ImportTask.status.in_(["pending", "queued", "processing"]), + ) + .exists(), + ) .order_by(ImportBatch.started_at.desc()) .limit(1) ) diff --git a/backend/app/tasks/scan.py b/backend/app/tasks/scan.py index c4581f7..6aacde0 100644 --- a/backend/app/tasks/scan.py +++ b/backend/app/tasks/scan.py @@ -95,6 +95,15 @@ def scan_directory(self, triggered_by: str = "manual", files_seen += 1 batch.total_files = files_seen + # If the walk enqueued nothing (every file was already on a + # non-failed ImportTask from a prior scan), there's no + # import_media_file message that would ever flip this batch to + # 'complete' — finalize it now so the active-batch query in + # /api/system/stats doesn't get stuck reporting all-zero + # counters from an empty scan. + if files_seen == 0: + batch.status = "complete" + batch.finished_at = datetime.now(UTC) session.commit() # Now enqueue import_media_file for each pending task.