Skip to content

fix: stop browsing from re-importing tagless comics and reloading the page (#992) - #993

Merged
ajslater merged 2 commits into
developfrom
fix-lazy-import-stamp-992
Oct 22, 2026
Merged

ajslater merged 2 commits into
developfrom
fix-lazy-import-stamp-992

Conversation

@ajslater

Copy link
Copy Markdown
Owner

Fixes #992: browsing a folder of CBR/CBZ files re-imports comics one at a time and reloads the page.

Cause

Hovering a browser card's (invisible) tag button lazy-imports a comic whose has_metadata is false, i.e. metadata_imported_at and metadata_mtime are both NULL. metadata_mtime is NULL for any archive with no embedded tags file. metadata_imported_at was only stamped by forced imports. A library scan with Import Tags on Library Scan on (the default) read every comic but stamped none, so on any install scanned since 0043 every comic without embedded tags looked un-imported.

Each hover then forced an import that found nothing new but still rewrote the row, purged and rebuilt the cover and broadcast LIBRARY_CHANGED. The browser reloaded once per comic, which matches the reporter's log line for line.

Changes

1. Stamp on tag-reading scans (finish.py, migration 0060)

  • _stamp_metadata_imported now runs when the pass read tags: forced, or the IMPORT_METADATA flag on. A flag-off scan still leaves rows NULL for the lazy importer. extract.py and finish.py share the flag read.
  • The stamp's path__in is batched by IMPORTER_LINK_FK_BATCH_SIZE. A first scan now reaches it with every path in the library.
  • Migration 0060 backfills NULL rows with 0043's seed when the IMPORT_METADATA flag is on (a missing row counts as on). Caveat: a library scanned with the flag off and switched on later loses the hover fallback for those rows. Force Update still re-reads them.

2. Skip no-op re-imports (create/comics.py)

update_comics skips a comic only when all of these hold:

  • no scalar diff and no FK or m2m link change remain after pruning;
  • no TagWriter series_attrs ride along;
  • presave derives the same stat and derived values from the file on disk;
  • the chunk's update_all_fks changed no rows (new chunk_fks_updated flag). FK bulk updates don't write updated_at, so a corrected tag or collection row reaches the browser and the search index only through a rewritten comic.

With nothing written, finish neither clears caches nor broadcasts.

Also:

  • import_search_index now drains its empty inputs on exit.
  • test_update_none's snapshots now expect no FTS update entry for the unchanged comic.

Force Update of an unchanged comic no longer rebuilds its cover. Remove All Covers is still the way to regenerate covers.

Tests

  • tests/test_lazy_import_marker.py
    • flag on vs off for scans; forced import with the flag off;
    • batching;
    • migration backfill: on, off, and missing flag row;
    • an end-to-end scan of a CBZ with no tags file.
  • tests/importer/test_noop_update.py, all end to end:
    • lazy import of a tagless comic and Force Update of an unchanged tagged one are silent (no LIBRARY_CHANGED, no CoverCreateTask, no updated_at bump);
    • a changed file still updates;
    • Force Update still repairs a wrong publisher link, a stray genre link and a corrected universe designation.
  • Each guard condition is backed by a test that fails without it: the two scan tests fail on the pre-fix finish.py; the two silent tests fail without the guard; the genre and designation tests fail without the m2m and FK-chunk checks.
  • make fix, make lint, make ty, make complexity are clean. make test: 1753 passed, 1 xfailed; frontend 1033 passed.

🤖 Generated with Claude Code

ajslater and others added 2 commits October 21, 2026 19:32
Hovering a browser card's tag button lazy-imports a comic whose
has_metadata is false, i.e. metadata_imported_at and metadata_mtime are
both NULL. metadata_mtime stays NULL for any archive with no embedded
tags file. metadata_imported_at was only stamped by forced imports, so
a library scan with "Import Tags on Library Scan" on (the default) read
every comic but stamped none. On any install scanned since 0043, every
comic without embedded tags read as un-imported. Each hover then forced
an import that found nothing but still rewrote the row, purged and
rebuilt the cover and broadcast LIBRARY_CHANGED, reloading the browser
once per comic.

- Stamp at finish whenever the pass read tags: forced, or the
  IMPORT_METADATA flag on. A flag-off scan still leaves rows NULL for
  the lazy importer. The flag read moves to FinishImporter so extract
  and finish share it.
- Batch the stamp's path__in by IMPORTER_LINK_FK_BATCH_SIZE. First
  scans of large libraries now reach it, and a single IN list would
  exceed SQLite's default 32766-variable cap.
- Migration 0060 backfills NULL rows with 0043's seed when the
  IMPORT_METADATA flag is on (a missing row counts as on).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A forced or lazy import of an unchanged comic, typically one with no
embedded tags, still rewrote the row. The query phase kept it in
UPDATE_COMICS with an empty diff "to set stat", so update_comics wrote
the six always-update fields, purged and rebuilt the cover and bumped
updated_at. TimestampUpdater then restamped its collections and finish
broadcast LIBRARY_CHANGED, reloading every open browser.

update_comics now skips a comic when the update would write nothing new:
- no scalar diff and no FK link change remain after the query phase's
  pruning, so Force Update still relinks a wrong publisher;
- no m2m link to create or delete, so a stray genre still gets the row
  write that re-syncs its search entry and timestamp;
- no TagWriter series_attrs ride along;
- presave derives the same stat, size, date, decade and age-rating
  index from the file on disk, so a replaced file with the same tags
  still updates and gets a new cover;
- the chunk's update_all_fks changed no rows. FK bulk updates don't
  write updated_at, and the FK search re-sync only runs beside
  per-comic FTS entries. A corrected tag or collection row reaches the
  browser and the index only through the comics the import rewrites,
  so such a chunk keeps the old full update.

With nothing written, counts stay zero, so finish neither clears caches
nor broadcasts. The metadata_imported_at stamp is path-scoped and still
lands. An import that rewrote no comics left the empty FTS inputs
behind, because an operate with no entries returns before consuming
them; import_search_index now drains them on exit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ajslater
ajslater merged commit 00d8971 into develop Oct 22, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant