diff --git a/docs/i18n-translation-guidelines.md b/docs/i18n-translation-guidelines.md index bcc124a0..72120d7a 100644 --- a/docs/i18n-translation-guidelines.md +++ b/docs/i18n-translation-guidelines.md @@ -137,7 +137,7 @@ and must be normalized. `en` = keep the English word as-is. | Term | Use | Fix | |---|---|---| -| recipe | Rezept/Rezepte | 5 leftover English "Recipe" keys → Rezept (e.g. `globalContextMenu.repairRecipes.label`, `toast.recipes.recipeSaved`) | +| recipe | Rezept/Rezepte | leftover English "Recipe" keys → Rezept (e.g. `toast.recipes.recipeSaved`) | | base model | pick Basis-Modell or Basismodell | currently 27× hyphenated vs 15× closed | | metadata | Metadaten | 4 keys use "Modelldaten" (`onboarding.steps.fetch.title/content`) → Metadaten | | bulk | pick Massen- or Sammelmodus | `loras.controls.bulk.action` = "Massen" reads as "crowds" — use "Massenbearbeitung"/"Mehrfachauswahl" | @@ -193,7 +193,7 @@ and must be normalized. `en` = keep the English word as-is. | Checkpoint | Checkpoint or チェックポイント (pick one) | 3 variants: Checkpoint (~14), checkpoint lowercase (4), チェックポイント (4, e.g. `settings.priorityTags.modelTypes.checkpoint`) | | Embedding | Embedding | 4 keys lowercase "embedding" mid-sentence | | bulk | 一括 | `modals.checkUpdates.tip` "バルクモード" → 一括モード | -| recipe counter | 件 or 個 | `repairRecipes.success` uses 件, `.cancelled` uses 個 — unify | +| recipe counter | 件 or 個 | `globalContextMenu.rematchRecipes.success` uses 件, `.cancelled` uses 個 — unify | ### ko diff --git a/locales/de.json b/locales/de.json index 7ed6fb3f..886eedc2 100644 --- a/locales/de.json +++ b/locales/de.json @@ -212,13 +212,6 @@ "none": "Alle {typePlural} verfügen bereits über Lizenzmetadaten", "error": "Lizenzmetadaten für {typePlural} konnten nicht aktualisiert werden: {message}" }, - "repairRecipes": { - "label": "Rezept-Daten reparieren", - "loading": "Rezept-Daten werden repariert...", - "success": "{count} Rezepte erfolgreich repariert.", - "cancelled": "Reparatur abgebrochen. {count} Rezepte wurden repariert.", - "error": "Rezept-Reparatur fehlgeschlagen: {message}" - }, "rematchRecipes": { "label": "Rezepte lokalen Modellen neu zuordnen", "loading": "Rezepte werden lokalen Modellen neu zugeordnet...", @@ -821,7 +814,6 @@ "setContentRating": "Inhaltsbewertung für alle festlegen", "copyAll": "Alle Syntax kopieren", "refreshAll": "Alle Metadaten aktualisieren", - "repairMetadata": "Metadaten der Auswahl reparieren", "rematchMetadata": "Ausgewählte mit lokalen Modellen abgleichen", "reimportMetadata": "Aus Quelle neu importieren", "checkUpdates": "Auswahl auf Updates prüfen", @@ -877,7 +869,6 @@ "replacePreview": "Vorschau ersetzen", "setContentRating": "Inhaltsbewertung festlegen", "moveToFolder": "In Ordner verschieben", - "repairMetadata": "Metadaten reparieren", "rematchMetadata": "Mit lokalen Modellen abgleichen", "reimportMetadata": "Aus Quelle neu importieren", "excludeModel": "Modell ausschließen", @@ -1130,13 +1121,6 @@ "getInfoFailed": "Fehler beim Abrufen der Informationen für fehlende LoRAs", "prepareError": "Fehler beim Vorbereiten der LoRAs für den Download: {message}" }, - "repair": { - "starting": "Rezept-Metadaten werden repariert...", - "success": "Rezept-Metadaten erfolgreich repariert", - "skipped": "Rezept bereits in der neuesten Version, keine Reparatur erforderlich", - "failed": "Rezept-Reparatur fehlgeschlagen: {message}", - "missingId": "Rezept kann nicht repariert werden: Fehlende Rezept-ID" - }, "reimport": { "starting": "Rezept wird aus Quelle neu importiert...", "success": "Rezept erfolgreich neu importiert", @@ -2238,9 +2222,6 @@ "batchImportBrowseFailed": "Ordner konnte nicht durchsucht werden: {message}", "batchImportDirectorySelected": "Verzeichnis ausgewählt: {path}", "noRecipesSelected": "Keine Rezepte ausgewählt", - "repairBulkComplete": "Reparatur abgeschlossen: {repaired} repariert, {skipped} übersprungen (von {total})", - "repairBulkSkipped": "Keine Reparatur für die {total} ausgewählten Rezepte erforderlich", - "repairBulkFailed": "Reparatur der ausgewählten Rezepte fehlgeschlagen: {message}", "rematchComplete": "{entries} Einträge in {recipes} Rezepten zugeordnet", "rematchCompleteErrors": "{entries} Einträge in {recipes} Rezepten zugeordnet, {failures} fehlgeschlagen", "rematchAllFailed": "Zuordnung fehlgeschlagen für {failures} von {total} ausgewählten Rezepten", diff --git a/locales/en.json b/locales/en.json index c0a7f850..cc94991f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -212,13 +212,6 @@ "none": "All {typePlural} already have license metadata", "error": "Failed to refresh license metadata for {typePlural}: {message}" }, - "repairRecipes": { - "label": "Repair recipes data", - "loading": "Repairing recipe data...", - "success": "Successfully repaired {count} recipes.", - "cancelled": "Repair cancelled. {count} recipes were repaired.", - "error": "Recipe repair failed: {message}" - }, "rematchRecipes": { "label": "Rematch recipes to local models", "loading": "Rematching recipes to local models...", @@ -821,7 +814,6 @@ "setContentRating": "Set Content Rating for Selected", "copyAll": "Copy Selected Syntax", "refreshAll": "Refresh Selected Metadata", - "repairMetadata": "Repair Metadata for Selected", "rematchMetadata": "Rematch Selected to Local Models", "reimportMetadata": "Re-import from Source", "checkUpdates": "Check Updates for Selected", @@ -877,7 +869,6 @@ "replacePreview": "Replace Preview", "setContentRating": "Set Content Rating", "moveToFolder": "Move to Folder", - "repairMetadata": "Repair metadata", "rematchMetadata": "Rematch to local models", "reimportMetadata": "Re-import from Source", "excludeModel": "Exclude Model", @@ -1130,13 +1121,6 @@ "getInfoFailed": "Failed to get information for missing LoRAs", "prepareError": "Error preparing LoRAs for download: {message}" }, - "repair": { - "starting": "Repairing recipe metadata...", - "success": "Recipe metadata repaired successfully", - "skipped": "Recipe already at latest version, no repair needed", - "failed": "Failed to repair recipe: {message}", - "missingId": "Cannot repair recipe: Missing recipe ID" - }, "reimport": { "starting": "Re-importing recipe from source...", "success": "Recipe re-imported successfully", @@ -2238,9 +2222,6 @@ "batchImportBrowseFailed": "Failed to browse directory: {message}", "batchImportDirectorySelected": "Directory selected: {path}", "noRecipesSelected": "No recipes selected", - "repairBulkComplete": "Repair complete: {repaired} repaired, {skipped} skipped (of {total})", - "repairBulkSkipped": "No repair needed for any of the {total} selected recipes", - "repairBulkFailed": "Failed to repair selected recipes: {message}", "rematchComplete": "Matched {entries} entries across {recipes} recipes", "rematchCompleteErrors": "Matched {entries} entries across {recipes} recipes, {failures} failed", "rematchAllFailed": "Rematch failed for {failures} of {total} selected recipes", diff --git a/locales/es.json b/locales/es.json index 6ab4159f..0441d33b 100644 --- a/locales/es.json +++ b/locales/es.json @@ -212,13 +212,6 @@ "none": "Todos los {typePlural} ya tienen metadatos de licencia", "error": "No se pudieron actualizar los metadatos de licencia de los {typePlural}: {message}" }, - "repairRecipes": { - "label": "Reparar datos de recetas", - "loading": "Reparando datos de recetas...", - "success": "Se repararon con éxito {count} recetas.", - "cancelled": "Reparación cancelada. {count} recetas fueron reparadas.", - "error": "Error al reparar recetas: {message}" - }, "rematchRecipes": { "label": "Reasociar recetas con modelos locales", "loading": "Reasociando recetas con modelos locales...", @@ -821,7 +814,6 @@ "setContentRating": "Establecer clasificación de contenido para todos", "copyAll": "Copiar toda la sintaxis", "refreshAll": "Actualizar todos los metadatos", - "repairMetadata": "Reparar metadatos de la selección", "rematchMetadata": "Reasociar los seleccionados con modelos locales", "reimportMetadata": "Reimportar desde origen", "checkUpdates": "Comprobar actualizaciones para la selección", @@ -877,7 +869,6 @@ "replacePreview": "Reemplazar vista previa", "setContentRating": "Establecer clasificación de contenido", "moveToFolder": "Mover a carpeta", - "repairMetadata": "Reparar metadatos", "rematchMetadata": "Reasociar con modelos locales", "reimportMetadata": "Reimportar desde origen", "excludeModel": "Excluir modelo", @@ -1130,13 +1121,6 @@ "getInfoFailed": "Error al obtener información de LoRAs faltantes", "prepareError": "Error preparando LoRAs para descarga: {message}" }, - "repair": { - "starting": "Reparando metadatos de la receta...", - "success": "Metadatos de la receta reparados con éxito", - "skipped": "La receta ya está en la última versión, no se necesita reparación", - "failed": "Error al reparar la receta: {message}", - "missingId": "No se puede reparar la receta: falta el ID de la receta" - }, "reimport": { "starting": "Reimportando receta desde origen...", "success": "Receta reimportada exitosamente", @@ -2238,9 +2222,6 @@ "batchImportBrowseFailed": "No se pudo examinar el directorio: {message}", "batchImportDirectorySelected": "Directorio seleccionado: {path}", "noRecipesSelected": "No se han seleccionado recetas", - "repairBulkComplete": "Reparación completa: {repaired} reparadas, {skipped} omitidas (de {total})", - "repairBulkSkipped": "No se necesita reparación para ninguna de las {total} recetas seleccionadas", - "repairBulkFailed": "Error al reparar las recetas seleccionadas: {message}", "rematchComplete": "{entries} entradas asociadas en {recipes} recetas", "rematchCompleteErrors": "{entries} entradas asociadas en {recipes} recetas, {failures} fallidas", "rematchAllFailed": "Falló la reasociación de {failures} de {total} recetas seleccionadas", diff --git a/locales/fr.json b/locales/fr.json index 57dbe4bd..05a5346b 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -212,13 +212,6 @@ "none": "Tous les {typePlural} possèdent déjà des métadonnées de licence", "error": "Échec de l'actualisation des métadonnées de licence pour les {typePlural} : {message}" }, - "repairRecipes": { - "label": "Réparer les données de Recipes", - "loading": "Réparation des données de Recipes...", - "success": "{count} Recipes réparées avec succès.", - "cancelled": "Réparation annulée. {count} Recipes ont été réparées.", - "error": "Échec de la réparation des Recipes : {message}" - }, "rematchRecipes": { "label": "Réassocier les Recipes aux modèles locaux", "loading": "Réassociation des Recipes aux modèles locaux...", @@ -821,7 +814,6 @@ "setContentRating": "Définir la classification du contenu pour tous", "copyAll": "Copier toute la syntaxe", "refreshAll": "Actualiser toutes les métadonnées", - "repairMetadata": "Réparer les métadonnées de la sélection", "rematchMetadata": "Réassocier la sélection aux modèles locaux", "reimportMetadata": "Ré-importer depuis la source", "checkUpdates": "Vérifier les mises à jour pour la sélection", @@ -877,7 +869,6 @@ "replacePreview": "Remplacer l'aperçu", "setContentRating": "Définir la classification du contenu", "moveToFolder": "Déplacer vers un dossier", - "repairMetadata": "Réparer les métadonnées", "rematchMetadata": "Réassocier aux modèles locaux", "reimportMetadata": "Ré-importer depuis la source", "excludeModel": "Exclure le modèle", @@ -1130,13 +1121,6 @@ "getInfoFailed": "Échec de l'obtention des informations pour les LoRAs manquants", "prepareError": "Erreur lors de la préparation des LoRAs pour le téléchargement : {message}" }, - "repair": { - "starting": "Réparation des métadonnées de la Recipe...", - "success": "Métadonnées de la Recipe réparées avec succès", - "skipped": "Recette déjà à la version la plus récente, aucune réparation nécessaire", - "failed": "Échec de la réparation de la Recipe : {message}", - "missingId": "Impossible de réparer la Recipe : ID de Recipe manquant" - }, "reimport": { "starting": "Ré-import de la Recipe depuis la source...", "success": "Recette ré-importée avec succès", @@ -2238,9 +2222,6 @@ "batchImportBrowseFailed": "Échec de la navigation dans le dossier : {message}", "batchImportDirectorySelected": "Dossier sélectionné : {path}", "noRecipesSelected": "Aucune Recipe sélectionnée", - "repairBulkComplete": "Réparation terminée : {repaired} réparée(s), {skipped} ignorée(s) (sur {total})", - "repairBulkSkipped": "Aucune réparation nécessaire parmi les {total} Recipes sélectionnées", - "repairBulkFailed": "Échec de la réparation des Recipes sélectionnées : {message}", "rematchComplete": "{entries} entrées associées dans {recipes} Recipes", "rematchCompleteErrors": "{entries} entrées associées dans {recipes} Recipes, {failures} échecs", "rematchAllFailed": "Échec de la réassociation de {failures} Recipes sélectionnées sur {total}", diff --git a/locales/he.json b/locales/he.json index 8d4c96af..b39373da 100644 --- a/locales/he.json +++ b/locales/he.json @@ -212,13 +212,6 @@ "none": "לכל ה-{typePlural} כבר יש מטא-נתוני רישיון", "error": "לא ניתן היה לרענן את מטא-נתוני הרישיון עבור {typePlural}: {message}" }, - "repairRecipes": { - "label": "תיקון נתוני מתכונים", - "loading": "מתקן נתוני מתכונים...", - "success": "תוקנו בהצלחה {count} מתכונים.", - "cancelled": "תיקון בוטל. {count} מתכונים תוקנו.", - "error": "תיקון המתכונים נכשל: {message}" - }, "rematchRecipes": { "label": "התאמה מחדש של מתכונים למודלים מקומיים", "loading": "מתבצעת התאמה מחדש של מתכונים למודלים מקומיים...", @@ -821,7 +814,6 @@ "setContentRating": "הגדר דירוג תוכן לכל המודלים", "copyAll": "העתק את כל התחבירים", "refreshAll": "רענן את כל המטא-נתונים", - "repairMetadata": "תקן מטא-נתונים עבור הנבחרים", "rematchMetadata": "התאמה מחדש של הנבחרים למודלים מקומיים", "reimportMetadata": "ייבא מחדש ממקור", "checkUpdates": "בדוק עדכונים לבחירה", @@ -877,7 +869,6 @@ "replacePreview": "החלף תצוגה מקדימה", "setContentRating": "הגדר דירוג תוכן", "moveToFolder": "העבר לתיקייה", - "repairMetadata": "תיקון מטא-נתונים", "rematchMetadata": "התאמה מחדש למודלים מקומיים", "reimportMetadata": "ייבא מחדש ממקור", "excludeModel": "החרג מודל", @@ -1130,13 +1121,6 @@ "getInfoFailed": "קבלת מידע עבור LoRAs חסרים נכשלה", "prepareError": "שגיאה בהכנת LoRAs להורדה: {message}" }, - "repair": { - "starting": "מתקן מטא-נתונים של מתכון...", - "success": "מטא-נתונים של מתכון תוקן בהצלחה", - "skipped": "המתכון כבר בגרסה העדכנית ביותר, אין צורך בתיקון", - "failed": "תיקון המתכון נכשל: {message}", - "missingId": "לא ניתן לתקן את המתכון: חסר מזהה מתכון" - }, "reimport": { "starting": "מייבא מתכון מחדש מהמקור...", "success": "המתכון יובא מחדש בהצלחה", @@ -2238,9 +2222,6 @@ "batchImportBrowseFailed": "לא ניתן היה לעיין בתיקייה: {message}", "batchImportDirectorySelected": "נבחרה תיקייה: {path}", "noRecipesSelected": "לא נבחרו מתכונים", - "repairBulkComplete": "התיקון הושלם: {repaired} תוקנו, {skipped} דולגו (מתוך {total})", - "repairBulkSkipped": "אין צורך בתיקון עבור {total} המתכונים הנבחרים", - "repairBulkFailed": "תיקון המתכונים הנבחרים נכשל: {message}", "rematchComplete": "הותאמו {entries} פריטים ב־{recipes} מתכונים", "rematchCompleteErrors": "הותאמו {entries} פריטים ב־{recipes} מתכונים, {failures} נכשלו", "rematchAllFailed": "ההתאמה נכשלה עבור {failures} מתוך {total} מתכונים שנבחרו", diff --git a/locales/ja.json b/locales/ja.json index 4493c43e..3cbeab33 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -212,13 +212,6 @@ "none": "すべての{typePlural}には既にライセンスメタデータがあります", "error": "{typePlural}のライセンスメタデータを更新できませんでした: {message}" }, - "repairRecipes": { - "label": "レシピデータの修復", - "loading": "レシピデータを修復中...", - "success": "{count} 件のレシピを正常に修復しました。", - "cancelled": "修復がキャンセルされました。{count}件のレシピが修復されました。", - "error": "レシピの修復に失敗しました: {message}" - }, "rematchRecipes": { "label": "レシピをローカルモデルに再マッチング", "loading": "レシピをローカルモデルに再マッチングしています...", @@ -821,7 +814,6 @@ "setContentRating": "すべてのモデルのコンテンツレーティングを設定", "copyAll": "すべての構文をコピー", "refreshAll": "すべてのメタデータを更新", - "repairMetadata": "選択したレシピのメタデータを修復", "rematchMetadata": "選択したモデルをローカルモデルに再マッチング", "reimportMetadata": "ソースから再インポート", "checkUpdates": "選択項目の更新を確認", @@ -877,7 +869,6 @@ "replacePreview": "プレビューを置換", "setContentRating": "コンテンツレーティングを設定", "moveToFolder": "フォルダに移動", - "repairMetadata": "メタデータを修復", "rematchMetadata": "ローカルモデルに再マッチング", "reimportMetadata": "ソースから再インポート", "excludeModel": "モデルを除外", @@ -1130,13 +1121,6 @@ "getInfoFailed": "不足LoRAの情報取得に失敗しました", "prepareError": "ダウンロード用LoRAの準備中にエラー:{message}" }, - "repair": { - "starting": "レシピのメタデータを修復中...", - "success": "レシピのメタデータが正常に修復されました", - "skipped": "レシピはすでに最新バージョンです。修復は不要です", - "failed": "レシピの修復に失敗しました: {message}", - "missingId": "レシピを修復できません: レシピIDがありません" - }, "reimport": { "starting": "ソースからレシピを再インポート中...", "success": "レシピの再インポートが完了しました", @@ -2238,9 +2222,6 @@ "batchImportBrowseFailed": "フォルダを参照できませんでした: {message}", "batchImportDirectorySelected": "選択されたフォルダ: {path}", "noRecipesSelected": "レシピが選択されていません", - "repairBulkComplete": "修復完了:{repaired} 件修復、{skipped} 件スキップ(合計 {total} 件)", - "repairBulkSkipped": "選択した {total} 件のレシピは修復不要です", - "repairBulkFailed": "選択したレシピの修復に失敗しました:{message}", "rematchComplete": "{recipes} 件のレシピで {entries} エントリをマッチングしました", "rematchCompleteErrors": "{recipes} 件のレシピで {entries} エントリをマッチングしました({failures} 件失敗)", "rematchAllFailed": "選択した {total} 件中 {failures} 件のレシピの再マッチングに失敗しました", diff --git a/locales/ko.json b/locales/ko.json index 2c90b946..c9865b10 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -212,13 +212,6 @@ "none": "모든 {typePlural}에 이미 라이선스 메타데이터가 있습니다", "error": "{typePlural}의 라이선스 메타데이터를 새로고침하지 못했습니다: {message}" }, - "repairRecipes": { - "label": "레시피 데이터 복구", - "loading": "레시피 데이터 복구 중...", - "success": "{count}개의 레시피가 성공적으로 복구되었습니다.", - "cancelled": "수리가 취소되었습니다. {count}개의 레시피가 수리되었습니다.", - "error": "레시피 복구 실패: {message}" - }, "rematchRecipes": { "label": "레시피를 로컬 모델에 다시 매칭", "loading": "레시피를 로컬 모델에 다시 매칭하는 중...", @@ -821,7 +814,6 @@ "setContentRating": "모든 모델에 콘텐츠 등급 설정", "copyAll": "모든 문법 복사", "refreshAll": "모든 메타데이터 새로고침", - "repairMetadata": "선택한 레시피 메타데이터 복구", "rematchMetadata": "선택 항목을 로컬 모델에 다시 매칭", "reimportMetadata": "소스에서 다시 가져오기", "checkUpdates": "선택 항목 업데이트 확인", @@ -877,7 +869,6 @@ "replacePreview": "미리보기 교체", "setContentRating": "콘텐츠 등급 설정", "moveToFolder": "폴더로 이동", - "repairMetadata": "메타데이터 복구", "rematchMetadata": "로컬 모델에 다시 매칭", "reimportMetadata": "소스에서 다시 가져오기", "excludeModel": "모델 제외", @@ -1130,13 +1121,6 @@ "getInfoFailed": "누락된 LoRA 정보를 가져오는데 실패했습니다", "prepareError": "LoRA 다운로드 준비 중 오류: {message}" }, - "repair": { - "starting": "레시피 메타데이터 복구 중...", - "success": "레시피 메타데이터가 성공적으로 복구되었습니다", - "skipped": "레시피가 이미 최신 버전입니다. 복구가 필요하지 않습니다", - "failed": "레시피 복구 실패: {message}", - "missingId": "레시피를 복구할 수 없음: 레시피 ID 누락" - }, "reimport": { "starting": "소스에서 레시피를 다시 가져오는 중...", "success": "레시피를 다시 가져왔습니다", @@ -2238,9 +2222,6 @@ "batchImportBrowseFailed": "폴더를 찾아보지 못했습니다: {message}", "batchImportDirectorySelected": "선택한 폴더: {path}", "noRecipesSelected": "선택한 레시피가 없습니다", - "repairBulkComplete": "복구 완료: {repaired}개 복구, {skipped}개 건너뜀 (총 {total}개)", - "repairBulkSkipped": "선택한 {total}개 레시피는 복구가 필요하지 않습니다", - "repairBulkFailed": "선택한 레시피 복구 실패: {message}", "rematchComplete": "{recipes}개 레시피에서 {entries}개 항목이 매칭되었습니다", "rematchCompleteErrors": "{recipes}개 레시피에서 {entries}개 항목이 매칭되었습니다. {failures}개 실패", "rematchAllFailed": "선택한 {total}개 레시피 중 {failures}개 재매칭 실패", diff --git a/locales/ru.json b/locales/ru.json index 7a5acb2c..8d48840c 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -212,13 +212,6 @@ "none": "У всех {typePlural} уже есть метаданные лицензии", "error": "Не удалось обновить метаданные лицензии для {typePlural}: {message}" }, - "repairRecipes": { - "label": "Восстановить данные рецептов", - "loading": "Восстановление данных рецептов...", - "success": "Успешно восстановлено {count} рецептов.", - "cancelled": "Восстановление отменено. {count} рецептов было восстановлено.", - "error": "Ошибка восстановления рецептов: {message}" - }, "rematchRecipes": { "label": "Повторное сопоставление рецептов с локальными моделями", "loading": "Повторное сопоставление рецептов с локальными моделями...", @@ -821,7 +814,6 @@ "setContentRating": "Установить рейтинг контента для всех", "copyAll": "Копировать весь синтаксис", "refreshAll": "Обновить все метаданные", - "repairMetadata": "Восстановить метаданные для выбранных", "rematchMetadata": "Сопоставить выбранные с локальными моделями", "reimportMetadata": "Переимпортировать из источника", "checkUpdates": "Проверить обновления для выбранных", @@ -877,7 +869,6 @@ "replacePreview": "Заменить превью", "setContentRating": "Установить рейтинг контента", "moveToFolder": "Переместить в папку", - "repairMetadata": "Восстановить метаданные", "rematchMetadata": "Сопоставить с локальными моделями", "reimportMetadata": "Переимпортировать из источника", "excludeModel": "Исключить модель", @@ -1130,13 +1121,6 @@ "getInfoFailed": "Не удалось получить информацию для отсутствующих LoRAs", "prepareError": "Ошибка подготовки LoRAs для загрузки: {message}" }, - "repair": { - "starting": "Восстановление метаданных рецепта...", - "success": "Метаданные рецепта успешно восстановлены", - "skipped": "Рецепт уже последней версии, восстановление не требуется", - "failed": "Не удалось восстановить рецепт: {message}", - "missingId": "Не удалось восстановить рецепт: отсутствует ID рецепта" - }, "reimport": { "starting": "Переимпорт рецепта из источника...", "success": "Рецепт успешно переимпортирован", @@ -2238,9 +2222,6 @@ "batchImportBrowseFailed": "Не удалось открыть папку: {message}", "batchImportDirectorySelected": "Выбрана папка: {path}", "noRecipesSelected": "Рецепты не выбраны", - "repairBulkComplete": "Восстановление завершено: {repaired} восстановлено, {skipped} пропущено (из {total})", - "repairBulkSkipped": "Ни один из {total} выбранных рецептов не требует восстановления", - "repairBulkFailed": "Не удалось восстановить выбранные рецепты: {message}", "rematchComplete": "Сопоставлено записей: {entries} в рецептах: {recipes}", "rematchCompleteErrors": "Сопоставлено записей: {entries} в рецептах: {recipes}, ошибок: {failures}", "rematchAllFailed": "Не удалось сопоставить: {failures} из {total} выбранных рецептов", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 5a635214..50712a5b 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -212,13 +212,6 @@ "none": "所有 {typePlural} 都已具备许可证元数据", "error": "刷新 {typePlural} 的许可证元数据失败:{message}" }, - "repairRecipes": { - "label": "修复配方数据", - "loading": "正在修复配方数据...", - "success": "成功修复了 {count} 个配方。", - "cancelled": "修复已取消。已修复 {count} 个配方。", - "error": "配方修复失败:{message}" - }, "rematchRecipes": { "label": "将配方重新匹配到本地模型", "loading": "正在将配方重新匹配到本地模型...", @@ -821,7 +814,6 @@ "setContentRating": "为所选中设置内容评级", "copyAll": "复制所选中语法", "refreshAll": "刷新所选中元数据", - "repairMetadata": "修复所选中元数据", "rematchMetadata": "将所选中重新匹配到本地模型", "reimportMetadata": "从源重新导入", "checkUpdates": "检查所选更新", @@ -877,7 +869,6 @@ "replacePreview": "替换预览", "setContentRating": "设置内容评级", "moveToFolder": "移动到文件夹", - "repairMetadata": "修复元数据", "rematchMetadata": "重新匹配到本地模型", "reimportMetadata": "从源重新导入", "excludeModel": "排除模型", @@ -1130,13 +1121,6 @@ "getInfoFailed": "获取缺失 LoRA 信息失败", "prepareError": "准备下载 LoRA 时出错:{message}" }, - "repair": { - "starting": "正在修复配方元数据...", - "success": "配方元数据修复成功", - "skipped": "配方已是最新版本,无需修复", - "failed": "修复配方失败:{message}", - "missingId": "无法修复配方:缺少配方 ID" - }, "reimport": { "starting": "正在从源重新导入配方...", "success": "配方已从源重新导入成功", @@ -2238,9 +2222,6 @@ "batchImportBrowseFailed": "浏览目录失败:{message}", "batchImportDirectorySelected": "已选择目录:{path}", "noRecipesSelected": "未选择任何配方", - "repairBulkComplete": "修复完成:{repaired} 个已修复,{skipped} 个已跳过(共 {total} 个)", - "repairBulkSkipped": "所选 {total} 个配方无需修复", - "repairBulkFailed": "修复所选配方失败:{message}", "rematchComplete": "已匹配 {entries} 个条目,涉及 {recipes} 个配方", "rematchCompleteErrors": "已匹配 {entries} 个条目,涉及 {recipes} 个配方,{failures} 个失败", "rematchAllFailed": "{failures}/{total} 个所选配方重新匹配失败", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index e3e88d16..633e1d59 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -212,13 +212,6 @@ "none": "所有 {typePlural} 已具備授權中繼資料", "error": "重新整理 {typePlural} 授權中繼資料失敗:{message}" }, - "repairRecipes": { - "label": "修復配方資料", - "loading": "正在修復配方資料...", - "success": "成功修復 {count} 個配方。", - "cancelled": "修復已取消。已修復 {count} 個配方。", - "error": "配方修復失敗:{message}" - }, "rematchRecipes": { "label": "將配方重新匹配到本地模型", "loading": "正在將配方重新匹配到本地模型...", @@ -821,7 +814,6 @@ "setContentRating": "為全部設定內容分級", "copyAll": "複製全部語法", "refreshAll": "刷新全部 metadata", - "repairMetadata": "修復所選中元數據", "rematchMetadata": "將所選中重新匹配到本地模型", "reimportMetadata": "從來源重新匯入", "checkUpdates": "檢查所選更新", @@ -877,7 +869,6 @@ "replacePreview": "更換預覽圖", "setContentRating": "設定內容分級", "moveToFolder": "移動到資料夾", - "repairMetadata": "修復元數據", "rematchMetadata": "重新匹配到本地模型", "reimportMetadata": "從來源重新匯入", "excludeModel": "排除模型", @@ -1130,13 +1121,6 @@ "getInfoFailed": "取得缺少 LoRA 資訊失敗", "prepareError": "準備下載 LoRA 時發生錯誤:{message}" }, - "repair": { - "starting": "正在修復配方元數據...", - "success": "配方元數據修復成功", - "skipped": "配方已是最新版本,無需修復", - "failed": "修復配方失敗:{message}", - "missingId": "無法修復配方:缺少配方 ID" - }, "reimport": { "starting": "正在從來源重新匯入配方...", "success": "配方已從來源重新匯入成功", @@ -2238,9 +2222,6 @@ "batchImportBrowseFailed": "瀏覽目錄失敗:{message}", "batchImportDirectorySelected": "已選擇目錄:{path}", "noRecipesSelected": "未選取任何配方", - "repairBulkComplete": "修復完成:{repaired} 個已修復,{skipped} 個已跳過(共 {total} 個)", - "repairBulkSkipped": "所選 {total} 個配方無需修復", - "repairBulkFailed": "修復所選配方失敗:{message}", "rematchComplete": "已匹配 {entries} 個條目,涉及 {recipes} 個配方", "rematchCompleteErrors": "已匹配 {entries} 個條目,涉及 {recipes} 個配方,{failures} 個失敗", "rematchAllFailed": "{failures}/{total} 個所選配方重新匹配失敗", diff --git a/py/routes/handlers/recipe_handlers.py b/py/routes/handlers/recipe_handlers.py index 9732b404..e21334eb 100644 --- a/py/routes/handlers/recipe_handlers.py +++ b/py/routes/handlers/recipe_handlers.py @@ -129,11 +129,6 @@ class RecipeHandlerSet: "get_recipes_for_checkpoint": self.query.get_recipes_for_checkpoint, "scan_recipes": self.query.scan_recipes, "move_recipe": self.management.move_recipe, - "repair_recipes": self.management.repair_recipes, - "cancel_repair": self.management.cancel_repair, - "repair_recipe": self.management.repair_recipe, - "repair_recipes_bulk": self.management.repair_recipes_bulk, - "get_repair_progress": self.management.get_repair_progress, "rematch_recipes": self.management.rematch_recipes, "cancel_rematch": self.management.cancel_rematch, "rematch_recipe": self.management.rematch_recipe, @@ -796,157 +791,6 @@ class RecipeManagementHandler: self._logger.error("Error saving recipe: %s", exc, exc_info=True) return web.json_response({"error": str(exc)}, status=500) - async def repair_recipes(self, request: web.Request) -> web.Response: - try: - await self._ensure_dependencies_ready() - recipe_scanner = self._recipe_scanner_getter() - if recipe_scanner is None: - return web.json_response( - {"success": False, "error": "Recipe scanner unavailable"}, - status=503, - ) - - # Check if already running - if self._ws_manager.is_recipe_repair_running(): - return web.json_response( - {"success": False, "error": "Recipe repair already in progress"}, - status=409, - ) - - recipe_scanner.reset_cancellation() - - async def progress_callback(data): - await self._ws_manager.broadcast_recipe_repair_progress(data) - - # Run in background to avoid timeout - async def run_repair(): - try: - await recipe_scanner.repair_all_recipes( - progress_callback=progress_callback - ) - except Exception as e: - self._logger.error( - f"Error in recipe repair task: {e}", exc_info=True - ) - await self._ws_manager.broadcast_recipe_repair_progress( - {"status": "error", "error": str(e)} - ) - finally: - # Keep the final status for a while so the UI can see it - await asyncio.sleep(5) - # Don't cleanup if it was cancelled, let the UI see the cancelled state for a bit? - # Actually cleanup_recipe_repair_progress is fine as long as we waited enough. - self._ws_manager.cleanup_recipe_repair_progress() - - asyncio.create_task(run_repair()) - - return web.json_response( - {"success": True, "message": "Recipe repair started"} - ) - except Exception as exc: - self._logger.error("Error starting recipe repair: %s", exc, exc_info=True) - return web.json_response({"success": False, "error": str(exc)}, status=500) - - async def cancel_repair(self, request: web.Request) -> web.Response: - try: - await self._ensure_dependencies_ready() - recipe_scanner = self._recipe_scanner_getter() - if recipe_scanner is None: - return web.json_response( - {"success": False, "error": "Recipe scanner unavailable"}, - status=503, - ) - - recipe_scanner.cancel_task() - return web.json_response( - {"success": True, "message": "Cancellation requested"} - ) - except Exception as exc: - self._logger.error("Error cancelling recipe repair: %s", exc, exc_info=True) - return web.json_response({"success": False, "error": str(exc)}, status=500) - - async def repair_recipes_bulk(self, request: web.Request) -> web.Response: - """Bulk repair metadata for multiple recipes by their IDs. - - Accepts a JSON body with a "recipe_ids" array and iterates - repair_recipe_by_id over each entry, collecting statistics. - """ - try: - await self._ensure_dependencies_ready() - recipe_scanner = self._recipe_scanner_getter() - if recipe_scanner is None: - return web.json_response( - {"success": False, "error": "Recipe scanner unavailable"}, - status=503, - ) - - data = await request.json() - recipe_ids = data.get("recipe_ids", []) - if not recipe_ids: - return web.json_response( - {"success": False, "error": "recipe_ids are required"}, - status=400, - ) - - total = len(recipe_ids) - repaired = 0 - skipped = 0 - errors = 0 - recipes = [] - - for recipe_id in recipe_ids: - try: - result = await recipe_scanner.repair_recipe_by_id(recipe_id) - if result.get("success"): - repaired += result.get("repaired", 0) - skipped += result.get("skipped", 0) - if result.get("recipe"): - recipes.append(result["recipe"]) - else: - errors += 1 - except RecipeNotFoundError: - skipped += 1 - except Exception as exc: - self._logger.error( - "Error repairing recipe %s: %s", recipe_id, exc - ) - errors += 1 - - return web.json_response({ - "success": True, - "total": total, - "repaired": repaired, - "skipped": skipped, - "errors": errors, - "recipes": recipes, - }) - except Exception as exc: - self._logger.error( - "Error performing bulk repair: %s", exc, exc_info=True - ) - return web.json_response( - {"success": False, "error": str(exc)}, status=500 - ) - - async def repair_recipe(self, request: web.Request) -> web.Response: - try: - await self._ensure_dependencies_ready() - recipe_scanner = self._recipe_scanner_getter() - if recipe_scanner is None: - return web.json_response( - {"success": False, "error": "Recipe scanner unavailable"}, - status=503, - ) - - recipe_id = request.match_info["recipe_id"] - result = await recipe_scanner.repair_recipe_by_id(recipe_id) - return web.json_response(result) - except RecipeNotFoundError as exc: - return web.json_response({"success": False, "error": str(exc)}, status=404) - except Exception as exc: - self._logger.error("Error repairing single recipe: %s", exc, exc_info=True) - return web.json_response({"success": False, "error": str(exc)}, status=500) - async def rematch_recipes(self, request: web.Request) -> web.Response: try: await self._ensure_dependencies_ready() @@ -958,12 +802,9 @@ class RecipeManagementHandler: ) # Mutual exclusion: a global rematch cannot start while a rematch - # OR a repair is already running — both mutate recipes under the - # same mutation lock. - if ( - self._ws_manager.is_recipe_rematch_running() - or self._ws_manager.is_recipe_repair_running() - ): + # is already running — both mutate recipes under the same + # mutation lock. + if self._ws_manager.is_recipe_rematch_running(): return web.json_response( {"success": False, "error": "Recipe rematch already in progress"}, status=409, @@ -1231,18 +1072,6 @@ class RecipeManagementHandler: ) return web.json_response({"success": False, "error": str(exc)}, status=500) - async def get_repair_progress(self, request: web.Request) -> web.Response: - try: - progress = self._ws_manager.get_recipe_repair_progress() - if progress: - return web.json_response({"success": True, "progress": progress}) - return web.json_response( - {"success": False, "message": "No repair in progress"}, status=404 - ) - except Exception as exc: - self._logger.error("Error getting repair progress: %s", exc, exc_info=True) - return web.json_response({"success": False, "error": str(exc)}, status=500) - async def import_remote_recipe(self, request: web.Request) -> web.Response: try: await self._ensure_dependencies_ready() diff --git a/py/routes/recipe_route_registrar.py b/py/routes/recipe_route_registrar.py index 2e1b33a1..52df45a0 100644 --- a/py/routes/recipe_route_registrar.py +++ b/py/routes/recipe_route_registrar.py @@ -84,11 +84,6 @@ ROUTE_DEFINITIONS: tuple[RouteDefinition, ...] = ( "GET", "/api/lm/recipes/for-checkpoint", "get_recipes_for_checkpoint" ), RouteDefinition("GET", "/api/lm/recipes/scan", "scan_recipes"), - RouteDefinition("POST", "/api/lm/recipes/repair", "repair_recipes"), - RouteDefinition("POST", "/api/lm/recipes/cancel-repair", "cancel_repair"), - RouteDefinition("POST", "/api/lm/recipe/{recipe_id}/repair", "repair_recipe"), - RouteDefinition("POST", "/api/lm/recipes/repair-bulk", "repair_recipes_bulk"), - RouteDefinition("GET", "/api/lm/recipes/repair-progress", "get_repair_progress"), RouteDefinition("POST", "/api/lm/recipes/rematch", "rematch_recipes"), RouteDefinition("POST", "/api/lm/recipes/rematch-bulk", "rematch_recipes_bulk"), RouteDefinition("POST", "/api/lm/recipe/{recipe_id}/rematch", "rematch_recipe"), diff --git a/py/services/persistent_recipe_cache.py b/py/services/persistent_recipe_cache.py index 8981ad57..0da9d2c6 100644 --- a/py/services/persistent_recipe_cache.py +++ b/py/services/persistent_recipe_cache.py @@ -52,7 +52,6 @@ class PersistentRecipeCache: "file_mtime", "file_size", "favorite", - "repair_version", "preview_nsfw_level", "loras_json", "checkpoint_json", @@ -442,7 +441,6 @@ class PersistentRecipeCache: file_mtime REAL, file_size INTEGER, favorite INTEGER DEFAULT 0, - repair_version INTEGER DEFAULT 0, preview_nsfw_level INTEGER DEFAULT 0, loras_json TEXT, checkpoint_json TEXT, @@ -541,7 +539,6 @@ class PersistentRecipeCache: file_mtime, file_size, 1 if recipe.get("favorite") else 0, - int(recipe.get("repair_version") or 0), int(recipe.get("preview_nsfw_level") or 0), loras_json, checkpoint_json, @@ -599,7 +596,6 @@ class PersistentRecipeCache: "created_date": row["created_date"] or 0.0, "modified": row["modified"] or 0.0, "favorite": bool(row["favorite"]), - "repair_version": row["repair_version"] or 0, "preview_nsfw_level": row["preview_nsfw_level"] or 0, "has_workflow": bool(row["has_workflow"]), "loras": loras, diff --git a/py/services/recipe_scanner.py b/py/services/recipe_scanner.py index d6c79d51..ea86a591 100644 --- a/py/services/recipe_scanner.py +++ b/py/services/recipe_scanner.py @@ -94,8 +94,6 @@ class RecipeScanner: cls._instance._civitai_client = None # Will be lazily initialized return cls._instance - REPAIR_VERSION = 4 - def __init__( self, lora_scanner: Optional[LoraScanner] = None, @@ -811,207 +809,6 @@ class RecipeScanner: """Check if cancellation has been requested.""" return self._cancel_requested - async def repair_all_recipes( - self, progress_callback: Optional[Callable[[Dict[str, Any]], Any]] = None - ) -> Dict[str, Any]: - """Repair all recipes by enrichment with Civitai and embedded metadata. - - Args: - persistence_service: Service for saving updated recipes - progress_callback: Optional callback for progress updates - - Returns: - Dict summary of repair results - """ - if progress_callback: - await progress_callback({"status": "started"}) - async with self._mutation_lock: - cache = await self.get_cached_data() - all_recipes = list(cache.raw_data) - total = len(all_recipes) - repaired_count = 0 - skipped_count = 0 - errors_count = 0 - - civitai_client = await self._get_civitai_client() - self.reset_cancellation() - - for i, recipe in enumerate(all_recipes): - if self.is_cancelled(): - logger.info("Recipe repair cancelled by user") - if progress_callback: - await progress_callback( - { - "status": "cancelled", - "current": i, - "total": total, - "repaired": repaired_count, - "skipped": skipped_count, - "errors": errors_count, - } - ) - return { - "success": False, - "status": "cancelled", - "repaired": repaired_count, - "skipped": skipped_count, - "errors": errors_count, - "total": total, - } - - try: - # Report progress - if progress_callback: - await progress_callback( - { - "status": "processing", - "current": i + 1, - "total": total, - "recipe_name": recipe.get("name", "Unknown"), - } - ) - - if await self._repair_single_recipe(recipe, civitai_client): - repaired_count += 1 - else: - skipped_count += 1 - - except Exception as e: - logger.error( - f"Error repairing recipe {recipe.get('file_path')}: {e}" - ) - errors_count += 1 - - # Final progress update - if progress_callback: - await progress_callback( - { - "status": "completed", - "repaired": repaired_count, - "skipped": skipped_count, - "errors": errors_count, - "total": total, - } - ) - - return { - "success": True, - "repaired": repaired_count, - "skipped": skipped_count, - "errors": errors_count, - "total": total, - } - - async def repair_recipe_by_id(self, recipe_id: str) -> Dict[str, Any]: - """Repair a single recipe by its ID. - - Args: - recipe_id: ID of the recipe to repair - - Returns: - Dict summary of repair result - """ - async with self._mutation_lock: - # Get raw recipe from cache directly to avoid formatted fields - cache = await self.get_cached_data() - recipe = next( - (r for r in cache.raw_data if str(r.get("id", "")) == recipe_id), None - ) - - if not recipe: - raise RecipeNotFoundError(f"Recipe {recipe_id} not found") - - civitai_client = await self._get_civitai_client() - success = await self._repair_single_recipe(recipe, civitai_client) - - # If successfully repaired, we should return the formatted version for the UI - return { - "success": True, - "repaired": 1 if success else 0, - "skipped": 0 if success else 1, - "recipe": await self.get_recipe_by_id(recipe_id) if success else recipe, - } - - async def _repair_single_recipe( - self, recipe: Dict[str, Any], civitai_client: Any - ) -> bool: - """Internal helper to repair a single recipe object. - - Args: - recipe: The recipe dictionary to repair (modified in-place) - civitai_client: Authenticated Civitai client - - Returns: - bool: True if recipe was repaired or updated, False if skipped - """ - # 1. Skip if already at latest repair version - if recipe.get("repair_version", 0) >= self.REPAIR_VERSION: - return False - - # 1.5 Detect and clear corrupted checkpoint (LoRA data saved as checkpoint). - # A checkpoint whose modelVersionId also appears in a LoRA entry is - # definitely wrong — the CivitAI import code used to pick - # modelVersionIds[0] as the checkpoint, which was often a LoRA. - # Clearing it lets the enrichment flow re-resolve the correct - # checkpoint from CivitAI image metadata. - cp = recipe.get("checkpoint") - lora_mvids = { - l.get("modelVersionId") - for l in recipe.get("loras", []) - if l.get("modelVersionId") - } - if cp and cp.get("modelVersionId") and cp["modelVersionId"] in lora_mvids: - cp_mvid = cp["modelVersionId"] - logger.info( - "Recipe %s: checkpoint modelVersionId %s matches a LoRA — " - "clearing corrupted checkpoint and removing matching LoRA entry", - recipe.get("id"), - cp_mvid, - ) - recipe["checkpoint"] = None - recipe["loras"] = [ - l for l in recipe.get("loras", []) - if l.get("modelVersionId") != cp_mvid - ] - - # 2. Identification: Is repair needed? - has_checkpoint = ( - "checkpoint" in recipe - and recipe["checkpoint"] - and recipe["checkpoint"].get("name") - ) - gen_params = recipe.get("gen_params", {}) - has_prompt = bool(gen_params.get("prompt")) - - needs_repair = not has_checkpoint or not has_prompt - - if not needs_repair: - # Even if no repair needed, we mark it with version if it was processed - # Always update and save because if we are here, the version is old (checked in step 1) - recipe["repair_version"] = self.REPAIR_VERSION - await self._save_recipe_persistently(recipe) - return True - - # 3. Use Enricher to repair/enrich - try: - from ..recipes.enrichment import RecipeEnricher - - updated = await RecipeEnricher.enrich_recipe(recipe, civitai_client) - except Exception as e: - logger.error(f"Error enriching recipe {recipe.get('id')}: {e}") - updated = False - - # 4. Mark version and save if updated or just marking version - # If we updated it, OR if the version is old (which we know it is if we are here), save it. - # Actually, if we are here and updated is False, it means we tried to repair but couldn't/didn't need to. - # But we still want to mark it as processed so we don't try again until version bump. - if updated or recipe.get("repair_version", 0) < self.REPAIR_VERSION: - recipe["repair_version"] = self.REPAIR_VERSION - await self._save_recipe_persistently(recipe) - return True - - return False - async def rematch_recipe_by_id(self, recipe_id: str) -> Dict[str, Any]: """Rematch a single recipe's deleted lora/checkpoint entries locally. diff --git a/py/services/websocket_manager.py b/py/services/websocket_manager.py index 72a0a6de..d287d8b3 100644 --- a/py/services/websocket_manager.py +++ b/py/services/websocket_manager.py @@ -20,8 +20,6 @@ class WebSocketManager: self._last_init_progress: Dict[str, Dict[str, Any]] = {} # Add auto-organize progress tracking self._auto_organize_progress: Optional[Dict[str, Any]] = None - # Add recipe repair progress tracking - self._recipe_repair_progress: Optional[Dict[str, Any]] = None # Add recipe rematch progress tracking self._recipe_rematch_progress: Optional[Dict[str, Any]] = None self._auto_organize_lock = asyncio.Lock() @@ -193,14 +191,6 @@ class WebSocketManager: # Broadcast via WebSocket await self.broadcast(data) - async def broadcast_recipe_repair_progress(self, data: Dict[str, Any]): - """Broadcast recipe repair progress to connected clients""" - # Store progress data in memory - self._recipe_repair_progress = data - - # Broadcast via WebSocket - await self.broadcast(data) - def get_auto_organize_progress(self) -> Optional[Dict[str, Any]]: """Get current auto-organize progress""" return self._auto_organize_progress @@ -209,22 +199,6 @@ class WebSocketManager: """Clear auto-organize progress data""" self._auto_organize_progress = None - def get_recipe_repair_progress(self) -> Optional[Dict[str, Any]]: - """Get current recipe repair progress""" - return self._recipe_repair_progress - - def cleanup_recipe_repair_progress(self): - """Clear recipe repair progress data if it is in a finished state""" - if self._recipe_repair_progress and self._recipe_repair_progress.get('status') in ['completed', 'cancelled', 'error']: - self._recipe_repair_progress = None - - def is_recipe_repair_running(self) -> bool: - """Check if recipe repair is currently running""" - if not self._recipe_repair_progress: - return False - status = self._recipe_repair_progress.get('status') - return status in ['started', 'processing'] - async def broadcast_recipe_rematch_progress(self, data: Dict[str, Any]): """Broadcast recipe rematch progress to connected clients""" # Store progress data in memory diff --git a/static/js/api/recipeApi.js b/static/js/api/recipeApi.js index 405f3e02..bf7f8689 100644 --- a/static/js/api/recipeApi.js +++ b/static/js/api/recipeApi.js @@ -20,7 +20,6 @@ const RECIPE_ENDPOINTS = { move: '/api/lm/recipe/move', moveBulk: '/api/lm/recipes/move-bulk', bulkDelete: '/api/lm/recipes/bulk-delete', - repairBulk: '/api/lm/recipes/repair-bulk', rematchBulk: '/api/lm/recipes/rematch-bulk', rematchSingle: '/api/lm/recipe/{recipe_id}/rematch', }; @@ -678,38 +677,6 @@ export class RecipeSidebarApiClient { }; } - async repairBulkModels(filePaths) { - if (!filePaths || filePaths.length === 0) { - throw new Error('No file paths provided'); - } - - const recipeIds = filePaths - .map((path) => extractRecipeId(path)) - .filter((id) => !!id); - - if (recipeIds.length === 0) { - throw new Error('No recipe IDs could be derived from file paths'); - } - - const response = await fetch(this.apiConfig.endpoints.repairBulk, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - recipe_ids: recipeIds, - }), - }); - - const result = await response.json(); - - if (!response.ok || !result.success) { - throw new Error(result.error || 'Failed to repair recipes'); - } - - return result; - } - async rematchBulkModels(filePaths) { if (!filePaths || filePaths.length === 0) { throw new Error('No file paths provided'); diff --git a/static/js/components/ContextMenu/BulkContextMenu.js b/static/js/components/ContextMenu/BulkContextMenu.js index 72a13a76..b9194a66 100644 --- a/static/js/components/ContextMenu/BulkContextMenu.js +++ b/static/js/components/ContextMenu/BulkContextMenu.js @@ -41,13 +41,9 @@ export class BulkContextMenu extends BaseContextMenu { const autoOrganizeItem = this.menu.querySelector('[data-action="auto-organize"]'); const deleteAllItem = this.menu.querySelector('[data-action="delete-all"]'); const downloadMissingLorasItem = this.menu.querySelector('[data-action="download-missing-loras"]'); - const repairMetadataItem = this.menu.querySelector('[data-action="repair-metadata"]'); const reimportMetadataItem = this.menu.querySelector('[data-action="reimport-metadata"]'); const rematchMetadataItem = this.menu.querySelector('[data-action="rematch-metadata"]'); - if (repairMetadataItem) { - repairMetadataItem.style.display = config.repairMetadata ? 'flex' : 'none'; - } if (reimportMetadataItem) { reimportMetadataItem.style.display = config.reimportMetadata ? 'flex' : 'none'; } @@ -283,9 +279,6 @@ export class BulkContextMenu extends BaseContextMenu { case 'delete-all': bulkManager.showBulkDeleteModal(); break; - case 'repair-metadata': - bulkManager.repairSelectedRecipes(); - break; case 'rematch-metadata': bulkManager.rematchSelectedRecipes(); break; diff --git a/static/js/components/ContextMenu/GlobalContextMenu.js b/static/js/components/ContextMenu/GlobalContextMenu.js index 26fb8eb3..70f6fe4b 100644 --- a/static/js/components/ContextMenu/GlobalContextMenu.js +++ b/static/js/components/ContextMenu/GlobalContextMenu.js @@ -23,7 +23,6 @@ export class GlobalContextMenu extends BaseContextMenu { const downloadExamplesItem = this.menu.querySelector('[data-action="download-example-images"]'); const cleanupExamplesItem = this.menu.querySelector('[data-action="cleanup-example-images-folders"]'); const excludedModelsItem = this.menu.querySelector('[data-action="manage-excluded-models"]'); - const repairRecipesItem = this.menu.querySelector('[data-action="repair-recipes"]'); const rematchRecipesItem = this.menu.querySelector('[data-action="rematch-recipes"]'); const groupByModelItem = this.menu.querySelector('[data-action="toggle-group-by-model"]'); const groupByModelCheck = groupByModelItem?.querySelector('.check-indicator'); @@ -41,7 +40,6 @@ export class GlobalContextMenu extends BaseContextMenu { cleanupExamplesItem?.classList.add('hidden'); excludedModelsItem?.classList.add('hidden'); groupByModelItem?.classList.add('hidden'); - repairRecipesItem?.classList.remove('hidden'); rematchRecipesItem?.classList.remove('hidden'); } else { modelUpdateItem?.classList.remove('hidden'); @@ -50,7 +48,6 @@ export class GlobalContextMenu extends BaseContextMenu { cleanupExamplesItem?.classList.remove('hidden'); excludedModelsItem?.classList.remove('hidden'); groupByModelItem?.classList.remove('hidden'); - repairRecipesItem?.classList.add('hidden'); rematchRecipesItem?.classList.add('hidden'); } @@ -95,11 +92,6 @@ export class GlobalContextMenu extends BaseContextMenu { console.error('Failed to refresh missing license metadata:', error); }); break; - case 'repair-recipes': - this.repairRecipes(menuItem).catch((error) => { - console.error('Failed to repair recipes:', error); - }); - break; case 'rematch-recipes': this.rematchRecipes(menuItem).catch((error) => { console.error('Failed to rematch recipes:', error); @@ -371,99 +363,6 @@ export class GlobalContextMenu extends BaseContextMenu { return `${displayName}s`; } - async repairRecipes(menuItem) { - if (this._repairInProgress) { - return; - } - - this._repairInProgress = true; - menuItem?.classList.add('disabled'); - - const loadingMessage = translate( - 'globalContextMenu.repairRecipes.loading', - {}, - 'Repairing recipe data...' - ); - - const progressUI = state.loadingManager?.showEnhancedProgress(loadingMessage); - progressUI?.showCancelButton(() => this.cancelRepair()); - - try { - const response = await fetch('/api/lm/recipes/repair', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - }); - - const result = await response.json(); - if (!response.ok || !result.success) { - throw new Error(result.error || 'Failed to start repair'); - } - - // Poll for progress (or wait for WebSocket if preferred, but polling is simpler for this implementation) - let isComplete = false; - while (!isComplete && this._repairInProgress) { - const progressResponse = await fetch('/api/lm/recipes/repair-progress'); - if (progressResponse.ok) { - const progressResult = await progressResponse.json(); - if (progressResult.success && progressResult.progress) { - const p = progressResult.progress; - if (p.status === 'processing') { - const percent = (p.current / p.total) * 100; - progressUI?.updateProgress(percent, p.recipe_name, `${loadingMessage} (${p.current}/${p.total})`); - } else if (p.status === 'completed') { - isComplete = true; - progressUI?.complete(translate( - 'globalContextMenu.repairRecipes.success', - { count: p.repaired }, - `Repaired ${p.repaired} recipes.` - )); - showToast('globalContextMenu.repairRecipes.success', { count: p.repaired }, 'success'); - // Refresh recipes page if active - if (window.recipesPage) { - window.recipesPage.refresh(); - } - } else if (p.status === 'error') { - throw new Error(p.error || 'Repair failed'); - } else if (p.status === 'cancelled') { - isComplete = true; - progressUI?.complete(translate( - 'globalContextMenu.repairRecipes.cancelled', - { count: p.repaired }, - `Repair cancelled. ${p.repaired} recipes were repaired.` - )); - showToast('globalContextMenu.repairRecipes.cancelled', { count: p.repaired }, 'info'); - } - } else if (progressResponse.status === 404) { - // Progress might have finished quickly and been cleaned up - isComplete = true; - progressUI?.complete(); - } - } - - if (!isComplete) { - await new Promise(resolve => setTimeout(resolve, 1000)); - } - } - } catch (error) { - console.error('Recipe repair failed:', error); - progressUI?.complete(translate('globalContextMenu.repairRecipes.error', { message: error.message }, 'Repair failed: {message}')); - showToast('globalContextMenu.repairRecipes.error', { message: error.message }, 'error'); - } finally { - this._repairInProgress = false; - menuItem?.classList.remove('disabled'); - } - } - - async cancelRepair() { - try { - await fetch('/api/lm/recipes/cancel-repair', { - method: 'POST', - }); - } catch (error) { - console.error('Failed to cancel recipe repair:', error); - } - } - async rematchRecipes(menuItem) { if (this._rematchInProgress) { return; diff --git a/static/js/components/ContextMenu/RecipeContextMenu.js b/static/js/components/ContextMenu/RecipeContextMenu.js index 1e22a2b3..53ce06ee 100644 --- a/static/js/components/ContextMenu/RecipeContextMenu.js +++ b/static/js/components/ContextMenu/RecipeContextMenu.js @@ -93,10 +93,6 @@ export class RecipeContextMenu extends BaseContextMenu { // Download missing LoRAs this.downloadMissingLoRAs(recipeId); break; - case 'repair': - // Repair recipe metadata - this.repairRecipe(recipeId); - break; case 'rematch': // Rematch recipe resources to local models this.rematchRecipe(recipeId); @@ -297,44 +293,6 @@ export class RecipeContextMenu extends BaseContextMenu { } } - // Repair recipe metadata - async repairRecipe(recipeId) { - if (!recipeId) { - showToast('recipes.contextMenu.repair.missingId', {}, 'error'); - return; - } - - try { - showToast('recipes.contextMenu.repair.starting', {}, 'info'); - - const response = await fetch(`/api/lm/recipe/${recipeId}/repair`, { - method: 'POST' - }); - const result = await response.json(); - - if (result.success) { - if (result.repaired > 0) { - showToast('recipes.contextMenu.repair.success', {}, 'success'); - const detailResponse = await fetch(`/api/lm/recipe/${recipeId}`); - if (detailResponse.ok) { - const updatedRecipe = await detailResponse.json(); - const filePath = this.currentCard?.dataset?.filepath; - if (filePath && state.virtualScroller) { - state.virtualScroller.updateSingleItem(filePath, updatedRecipe); - } - } - } else { - showToast('recipes.contextMenu.repair.skipped', {}, 'info'); - } - } else { - throw new Error(result.error || 'Repair failed'); - } - } catch (error) { - console.error('Error repairing recipe:', error); - showToast('recipes.contextMenu.repair.failed', { message: error.message }, 'error'); - } - } - async rematchRecipe(recipeId) { if (!recipeId) { showToast('toast.recipes.rematchFailed', { message: 'Missing recipe ID' }, 'error'); diff --git a/static/js/managers/BulkManager.js b/static/js/managers/BulkManager.js index 7bdc6286..fdcc413b 100644 --- a/static/js/managers/BulkManager.js +++ b/static/js/managers/BulkManager.js @@ -103,7 +103,6 @@ export class BulkManager { skipMetadataRefresh: false, setFavorite: true, unfavorite: true, - repairMetadata: true, reimportMetadata: true, rematchMetadata: true } @@ -910,76 +909,6 @@ export class BulkManager { } } - async repairSelectedRecipes() { - if (state.selectedModels.size === 0) { - showToast('toast.recipes.noRecipesSelected', {}, 'warning'); - return; - } - - if (state.currentPageType !== 'recipes') { - showToast('This operation is only available for recipes', {}, 'warning'); - return; - } - - try { - const apiClient = this.getActiveApiClient(); - const filePaths = Array.from(state.selectedModels); - - if (typeof apiClient.repairBulkModels !== 'function') { - showToast('Bulk repair is not supported for this model type', {}, 'error'); - return; - } - - state.loadingManager.showSimpleLoading('Repairing recipe metadata...'); - - const result = await apiClient.repairBulkModels(filePaths); - - if (result.success) { - const total = result.total || filePaths.length; - const repaired = result.repaired || 0; - const skipped = result.skipped || 0; - - const recipes = result.recipes || []; - for (const recipe of recipes) { - if (recipe.file_path) { - state.virtualScroller.updateSingleItem( - recipe.file_path, - recipe - ); - } - } - - if (repaired > 0) { - showToast( - 'toast.recipes.repairBulkComplete', - { repaired, skipped, total }, - 'success' - ); - } else { - showToast( - 'toast.recipes.repairBulkSkipped', - { total }, - 'info' - ); - } - - if (state.bulkMode) this.toggleBulkMode(); - } else { - throw new Error(result.error || 'Bulk repair failed'); - } - } catch (error) { - console.error('Error during bulk recipe repair:', error); - showToast('toast.recipes.repairBulkFailed', { message: error.message }, 'error'); - } finally { - if (state.loadingManager?.hide) { - state.loadingManager.hide(); - } - if (typeof state.loadingManager?.restoreProgressBar === 'function') { - state.loadingManager.restoreProgressBar(); - } - } - } - async rematchSelectedRecipes() { if (state.selectedModels.size === 0) { showToast('toast.recipes.noRecipesSelected', {}, 'warning'); diff --git a/templates/components/context_menu.html b/templates/components/context_menu.html index 0daadcbc..96b09e17 100644 --- a/templates/components/context_menu.html +++ b/templates/components/context_menu.html @@ -94,9 +94,6 @@
- @@ -202,9 +199,6 @@ {{ t('globalContextMenu.groupByModel.label') }} - diff --git a/templates/recipes.html b/templates/recipes.html index b8c4dd6a..ec24086e 100644 --- a/templates/recipes.html +++ b/templates/recipes.html @@ -18,9 +18,6 @@