Upload updates

This commit is contained in:
2026-09-21 09:01:01 -05:00
parent 98025e9e6d
commit 474403ffe2
18 changed files with 2539 additions and 796 deletions
+65
View File
@@ -164,6 +164,16 @@ class TempUpload(models.Model):
on_delete=models.SET_NULL,
related_name="temp_uploads",
)
# Background pipeline bookkeeping (see apps/library/upload_pipeline.py).
# e621_checked_at/visual_checked_at are set once the corresponding phase
# ran, so "never checked" and "checked, nothing found" stay distinct.
e621_checked_at = models.DateTimeField(null=True, blank=True)
visual_checked_at = models.DateTimeField(null=True, blank=True)
# Worker claim for cross-process mutual exclusion; stale claims are
# reaped and the row queued again.
claimed_at = models.DateTimeField(null=True, blank=True, db_index=True)
pipeline_error = models.TextField(blank=True, default="")
attempts = models.PositiveSmallIntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
@@ -174,6 +184,61 @@ class TempUpload(models.Model):
return f"{self.original_filename} ({self.status})"
class UploadRun(models.Model):
"""Per-user state of the background upload pipeline.
One row per user acts as the cheap status source the SPA polls and as a
place for batch-level failures (broken e621 credentials, outages) that
would otherwise be repeated on every row.
"""
STATUS_IDLE = "idle"
STATUS_RUNNING = "running"
STATUS_PAUSED = "paused"
STATUS_ERROR = "error"
STATUS_CHOICES = [
(STATUS_IDLE, "Idle"),
(STATUS_RUNNING, "Running"),
(STATUS_PAUSED, "Paused"),
(STATUS_ERROR, "Error"),
]
PHASE_MD5 = "md5"
PHASE_VISUAL = "visual"
PHASE_IQDB = "iqdb"
PHASE_CHOICES = [
("", "None"),
(PHASE_MD5, "e621 MD5"),
(PHASE_VISUAL, "Visual similarity"),
(PHASE_IQDB, "IQDB"),
]
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="upload_run",
)
status = models.CharField(
max_length=20, choices=STATUS_CHOICES, default=STATUS_IDLE
)
phase = models.CharField(
max_length=20, choices=PHASE_CHOICES, blank=True, default=""
)
total = models.IntegerField(default=0)
processed = models.IntegerField(default=0)
matched = models.IntegerField(default=0)
failed = models.IntegerField(default=0)
error = models.TextField(blank=True, default="")
started_at = models.DateTimeField(null=True, blank=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ["-updated_at"]
def __str__(self):
return f"Upload run for {self.user_id} ({self.status})"
class DownloadTask(models.Model):
"""A background 'Download to Library' job with progress tracking."""