Tag-casing acronym fix, Patreon resolver hardening, archive diagnostics, post-card strip #74

Merged
bvandeusen merged 6 commits from dev into main 2026-06-06 19:59:32 -04:00
2 changed files with 38 additions and 2 deletions
Showing only changes of commit 19eb4e9388 - Show all commits
+20
View File
@@ -269,6 +269,11 @@ class DownloadService:
source_row = self.sync_session.get(Source, ctx["source_id"]) source_row = self.sync_session.get(Source, ctx["source_id"])
import_summary = {"attached": 0, "skipped": 0, "errors": 0} import_summary = {"attached": 0, "skipped": 0, "errors": 0}
# Archives detected but captured WITHOUT extracting any image (probe
# rejected / corrupt / missing extractor backend). Surfaced on the event
# so a post showing "no images" beside a zip is diagnosable (plan
# follow-up 2026-06-06 — the recurring archive-association report).
unextracted_archives: list[dict] = []
bytes_downloaded = 0 bytes_downloaded = 0
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
@@ -322,6 +327,17 @@ class DownloadService:
# mirror duplicate_hash cleanup so we don't keep two copies. # mirror duplicate_hash cleanup so we don't keep two copies.
# Operator-flagged 2026-06-02 (Lustria OST zip). # Operator-flagged 2026-06-02 (Lustria OST zip).
import_summary["attached"] += 1 import_summary["attached"] += 1
# An archive captured WITHOUT extracting any image carries a
# reason on result.error — record it so the event explains the
# "no images beside a zip" symptom instead of staying silent.
if result.error:
unextracted_archives.append(
{"file": path.name, "reason": result.error}
)
log.warning(
"archive captured unextracted (%s): %s",
path.name, result.error,
)
try: try:
bytes_downloaded += path.stat().st_size # noqa: ASYNC240 bytes_downloaded += path.stat().st_size # noqa: ASYNC240
except OSError: except OSError:
@@ -403,6 +419,10 @@ class DownloadService:
"duration_seconds": dl_result.duration_seconds, "duration_seconds": dl_result.duration_seconds,
"quarantined_paths": dl_result.quarantined_paths or None, "quarantined_paths": dl_result.quarantined_paths or None,
"import_summary": import_summary, "import_summary": import_summary,
# Archives detected but captured without extracting an image — the
# recurring "post shows a zip but no images" report. Each entry is
# {file, reason}; None when every archive extracted cleanly.
"unextracted_archives": unextracted_archives or None,
} }
await self._update_source_health( await self._update_source_health(
source_id=ctx["source_id"], status=status, error_message=ev.error, source_id=ctx["source_id"], status=status, error_message=ev.error,
+18 -2
View File
@@ -430,13 +430,17 @@ class Importer:
self._capture_attachment( self._capture_attachment(
source, post=post, artist=artist_use, resolved=True, source, post=post, artist=artist_use, resolved=True,
) )
return ImportResult(status="attached") reason = f"archive probe rejected, captured unextracted: {probe.reason}"
log.warning("%s: %s", source.name, reason)
return ImportResult(status="attached", error=reason)
artist_use = artist if artist is not None else self._resolve_artist(source) artist_use = artist if artist is not None else self._resolve_artist(source)
post = self._post_for_sidecar(source, artist_use) post = self._post_for_sidecar(source, artist_use)
member_ids: list[int] = [] member_ids: list[int] = []
member_total = 0
with extract_archive(source) as members: with extract_archive(source) as members:
for _name, member_path in members: for _name, member_path in members:
member_total += 1
if not is_supported(member_path): if not is_supported(member_path):
continue # non-media preserved via the stored archive continue # non-media preserved via the stored archive
res = self._import_media( res = self._import_media(
@@ -453,7 +457,19 @@ class Importer:
status="imported", image_id=member_ids[0], status="imported", image_id=member_ids[0],
member_image_ids=member_ids, member_image_ids=member_ids,
) )
return ImportResult(status="attached") # No images landed — surface WHY so a post showing "no images" beside an
# archive is diagnosable instead of silent. Zero members usually means
# the extractor backend is missing/failed (unar for rar, py7zr for 7z)
# or the file is corrupt; non-zero-but-no-images means it held only
# non-media files.
reason = (
"archive extracted but held no supported image/video members"
if member_total
else "archive yielded no members (unsupported/corrupt, or the "
"extractor backend failed)"
)
log.warning("%s: %s", source.name, reason)
return ImportResult(status="attached", error=reason)
def _import_media( def _import_media(
self, source: Path, attribution_path: Path, self, source: Path, attribution_path: Path,