fix(tagging): skip a damaged WD14 label list in the preview (#431)
This commit is contained in:
@@ -44,6 +44,8 @@ BATCH_JOB_BODY_MAX = 32 * 1024
|
||||
SKIPPED_PREVIEW_BODY_MAX = 64 * 1024
|
||||
SKIPPED_PREVIEW_LEASE = "Skipped tags preview"
|
||||
SKIPPED_PREVIEW_SAMPLE = 12
|
||||
# WD14 label lists the preview found missing or unreadable (logged once each).
|
||||
_MISSING_LABEL_LISTS = set()
|
||||
REVIEW_REQUIRED = (
|
||||
"Replacing tags needs a review of each image. Use Preview tagging review, "
|
||||
"or choose Skip or Merge for a batch run."
|
||||
@@ -760,8 +762,11 @@ class AutotagRoutesMixin:
|
||||
try:
|
||||
names = wd14_label_names(path)
|
||||
except (OSError, ValueError) as exc:
|
||||
self.logger.debug(f"No WD14 label list for {model_type}: {exc}")
|
||||
if path not in _MISSING_LABEL_LISTS: # logged once per list
|
||||
_MISSING_LABEL_LISTS.add(path)
|
||||
self.logger.debug(f"No WD14 label list for {model_type}: {exc}")
|
||||
continue
|
||||
_MISSING_LABEL_LISTS.discard(path)
|
||||
lists.append(MODELS[model_type]["name"])
|
||||
for name in names:
|
||||
known.setdefault(name.casefold(), (1, name))
|
||||
|
||||
@@ -204,8 +204,8 @@ def read_wd14_label_rows(path) -> List[Dict[str, str]]:
|
||||
|
||||
def wd14_label_names(path) -> Tuple[str, ...]:
|
||||
"""The names a WD14 label list can suggest (categories 0 and 4), cached
|
||||
by ``(mtime_ns, size)``. OSError and ValueError propagate (the caller
|
||||
skips a missing or unreadable list)."""
|
||||
by ``(mtime_ns, size)``. OSError (missing) and ValueError (unreadable)
|
||||
propagate; the caller skips that list."""
|
||||
key = os.fspath(path)
|
||||
stat = os.stat(key)
|
||||
stamp = (stat.st_mtime_ns, stat.st_size)
|
||||
@@ -213,9 +213,13 @@ def wd14_label_names(path) -> Tuple[str, ...]:
|
||||
cached = _label_cache.get(key)
|
||||
if cached is not None and cached[0] == stamp:
|
||||
return cached[1]
|
||||
try:
|
||||
rows = read_wd14_label_rows(key)
|
||||
except csv.Error as exc: # a damaged list (a NUL byte, an oversized field)
|
||||
raise ValueError(f"unreadable label list: {exc}") from None
|
||||
names = tuple(
|
||||
name
|
||||
for row in read_wd14_label_rows(key)
|
||||
for row in rows
|
||||
if row.get("category", "0") in WD14_SUGGESTED_CATEGORIES
|
||||
and (name := (row.get("name") or "").strip())
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ reader and the filter in ``AutoTagService.generate_tag_suggestions``. No model
|
||||
is loaded: ``_scored_tags`` is replaced by a fixed list.
|
||||
"""
|
||||
|
||||
import csv
|
||||
import os
|
||||
import tempfile
|
||||
import unicodedata
|
||||
@@ -190,6 +191,15 @@ class TestWd14Labels(unittest.TestCase):
|
||||
self.assertEqual(wd14_label_names(self.path), ("blurry", "sky"))
|
||||
self.assertEqual(reader.call_count, 2)
|
||||
|
||||
def test_a_damaged_list_is_a_value_error_for_the_caller_to_skip(self):
|
||||
# A field over the csv module's size limit raises csv.Error.
|
||||
big = "x" * (csv.field_size_limit() + 1)
|
||||
self.path.write_text(
|
||||
f"tag_id,name,category,count\n1,{big},0,1\n", encoding="utf-8"
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
wd14_label_names(self.path)
|
||||
|
||||
def test_a_missing_list_raises_for_the_caller_to_skip(self):
|
||||
with self.assertRaises(OSError):
|
||||
wd14_label_names(self.path)
|
||||
|
||||
@@ -107,7 +107,6 @@ export function skippedTagsBlock({ api, scope, drafts, delay = PREVIEW_DELAY_MS
|
||||
input.id = 'tag-skipped-input';
|
||||
input.autocomplete = 'off';
|
||||
input.placeholder = SKIPPED_COPY.placeholder;
|
||||
input.maxLength = MAX_ENTRY_CHARS + 10;
|
||||
input.setAttribute('aria-describedby', 'tag-skipped-problem');
|
||||
const add = createButton(SKIPPED_COPY.add);
|
||||
add.id = 'tag-skipped-add';
|
||||
@@ -116,6 +115,7 @@ export function skippedTagsBlock({ api, scope, drafts, delay = PREVIEW_DELAY_MS
|
||||
problem.id = 'tag-skipped-problem';
|
||||
problem.setAttribute('role', 'alert');
|
||||
const chips = el('div', undefined, 'tags skip-chips');
|
||||
chips.setAttribute('role', 'group');
|
||||
chips.setAttribute('aria-label', SKIPPED_COPY.chips);
|
||||
const preview = el('p', '', 'small skip-preview');
|
||||
preview.setAttribute('role', 'status');
|
||||
|
||||
Reference in New Issue
Block a user