fix(recipes): resolve stale LoRA hash on import and add hashInvalid state

- import: prefer A1111 Lora hashes (12-char AutoV3) over conflicting Hashes
  JSON values; recover the quote-wrapped AutoV3 from CivitAI image API meta;
  merge EXIF-parsed LoRAs when the API-only parse yields none (meta=null)
- rematch: treat entries whose hash failed CivitAI resolution (hashInvalid)
  as unresolved candidates; clear the flag on rematch/reconnect write-back
- download: persist hashInvalid and show a distinct toast when hash lookup
  returns "Model not found", so unresolvable entries become recoverable
- ui: add Unresolvable Hash badge styling and reconnect affordance
- i18n: translate the new keys across all 10 locales
This commit is contained in:
Will Miao
2026-08-28 22:24:07 +08:00
parent a7d65fe84a
commit 856c9a87ac
24 changed files with 757 additions and 28 deletions
@@ -470,6 +470,36 @@ class RecipePersistenceService:
}
)
async def mark_lora_hash_invalid(
self,
*,
recipe_scanner,
recipe_id: str,
lora_index: int,
hash_invalid: bool = True,
) -> PersistenceResult:
"""Mark a recipe LoRA entry's hash as unresolvable on CivitAI.
Called when a download attempt by hash returned "Model not found".
The flag makes the entry an unresolved rematch candidate without
altering its stored hash/file_name.
"""
recipe_data, updated_lora = await recipe_scanner.set_lora_entry_hash_invalid(
recipe_id,
lora_index,
hash_invalid=hash_invalid,
)
return PersistenceResult(
{
"success": True,
"recipe_id": recipe_id,
"hash_invalid": bool(hash_invalid),
"updated_lora": updated_lora,
}
)
async def bulk_delete(
self,
*,
@@ -793,6 +823,7 @@ class RecipePersistenceService:
"modelName": lora.get("name", ""),
"modelVersionName": lora.get("version", ""),
"isDeleted": lora.get("isDeleted", False),
"hashInvalid": lora.get("hashInvalid", False),
"exclude": lora.get("exclude", False),
}