Tag-casing acronym fix, Patreon resolver hardening, archive diagnostics, post-card strip #74
@@ -269,6 +269,11 @@ class DownloadService:
|
||||
source_row = self.sync_session.get(Source, ctx["source_id"])
|
||||
|
||||
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
|
||||
|
||||
loop = asyncio.get_running_loop()
|
||||
@@ -322,6 +327,17 @@ class DownloadService:
|
||||
# mirror duplicate_hash cleanup so we don't keep two copies.
|
||||
# Operator-flagged 2026-06-02 (Lustria OST zip).
|
||||
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:
|
||||
bytes_downloaded += path.stat().st_size # noqa: ASYNC240
|
||||
except OSError:
|
||||
@@ -403,6 +419,10 @@ class DownloadService:
|
||||
"duration_seconds": dl_result.duration_seconds,
|
||||
"quarantined_paths": dl_result.quarantined_paths or None,
|
||||
"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(
|
||||
source_id=ctx["source_id"], status=status, error_message=ev.error,
|
||||
|
||||
@@ -430,13 +430,17 @@ class Importer:
|
||||
self._capture_attachment(
|
||||
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)
|
||||
post = self._post_for_sidecar(source, artist_use)
|
||||
member_ids: list[int] = []
|
||||
member_total = 0
|
||||
with extract_archive(source) as members:
|
||||
for _name, member_path in members:
|
||||
member_total += 1
|
||||
if not is_supported(member_path):
|
||||
continue # non-media preserved via the stored archive
|
||||
res = self._import_media(
|
||||
@@ -453,7 +457,19 @@ class Importer:
|
||||
status="imported", image_id=member_ids[0],
|
||||
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(
|
||||
self, source: Path, attribution_path: Path,
|
||||
|
||||
Reference in New Issue
Block a user