feat: enhance skip metadata refresh with smart UI and subtle badges, #790

This commit is contained in:
Will Miao
2026-02-09 09:15:29 +08:00
parent 2b74b2373d
commit 765c1c42a9
23 changed files with 283 additions and 18 deletions

View File

@@ -131,7 +131,8 @@
}, },
"badges": { "badges": {
"update": "Update", "update": "Update",
"updateAvailable": "Update verfügbar" "updateAvailable": "Update verfügbar",
"skipRefresh": "[TODO: Translate] Metadata refresh skipped"
}, },
"usage": { "usage": {
"timesUsed": "Verwendungsanzahl" "timesUsed": "Verwendungsanzahl"
@@ -527,8 +528,12 @@
"checkUpdates": "Auswahl auf Updates prüfen", "checkUpdates": "Auswahl auf Updates prüfen",
"moveAll": "Alle in Ordner verschieben", "moveAll": "Alle in Ordner verschieben",
"autoOrganize": "Automatisch organisieren", "autoOrganize": "Automatisch organisieren",
"skipMetadataRefresh": "[TODO: Translate] Skip Metadata Refresh for Selected",
"resumeMetadataRefresh": "[TODO: Translate] Resume Metadata Refresh for Selected",
"deleteAll": "Alle Modelle löschen", "deleteAll": "Alle Modelle löschen",
"clear": "Auswahl löschen", "clear": "Auswahl löschen",
"skipMetadataRefreshCount": "[TODO: Translate] Skip ({count} models)",
"resumeMetadataRefreshCount": "[TODO: Translate] Resume ({count} models)",
"autoOrganizeProgress": { "autoOrganizeProgress": {
"initializing": "Automatische Organisation wird initialisiert...", "initializing": "Automatische Organisation wird initialisiert...",
"starting": "Automatische Organisation für {type} wird gestartet...", "starting": "Automatische Organisation für {type} wird gestartet...",
@@ -1379,6 +1384,11 @@
"bulkBaseModelUpdateSuccess": "Basis-Modell erfolgreich für {count} Modell(e) aktualisiert", "bulkBaseModelUpdateSuccess": "Basis-Modell erfolgreich für {count} Modell(e) aktualisiert",
"bulkBaseModelUpdatePartial": "{success} Modelle aktualisiert, {failed} fehlgeschlagen", "bulkBaseModelUpdatePartial": "{success} Modelle aktualisiert, {failed} fehlgeschlagen",
"bulkBaseModelUpdateFailed": "Aktualisierung des Basis-Modells für ausgewählte Modelle fehlgeschlagen", "bulkBaseModelUpdateFailed": "Aktualisierung des Basis-Modells für ausgewählte Modelle fehlgeschlagen",
"skipMetadataRefreshUpdating": "[TODO: Translate] Updating metadata refresh flag for {count} model(s)...",
"skipMetadataRefreshSet": "[TODO: Translate] Metadata refresh skipped for {count} model(s)",
"skipMetadataRefreshCleared": "[TODO: Translate] Metadata refresh resumed for {count} model(s)",
"skipMetadataRefreshPartial": "[TODO: Translate] Updated {success} model(s), {failed} failed",
"skipMetadataRefreshFailed": "[TODO: Translate] Failed to update metadata refresh flag for selected models",
"bulkContentRatingUpdating": "Inhaltsbewertung wird für {count} Modell(e) aktualisiert...", "bulkContentRatingUpdating": "Inhaltsbewertung wird für {count} Modell(e) aktualisiert...",
"bulkContentRatingSet": "Inhaltsbewertung auf {level} für {count} Modell(e) gesetzt", "bulkContentRatingSet": "Inhaltsbewertung auf {level} für {count} Modell(e) gesetzt",
"bulkContentRatingPartial": "Inhaltsbewertung auf {level} für {success} Modell(e) gesetzt, {failed} fehlgeschlagen", "bulkContentRatingPartial": "Inhaltsbewertung auf {level} für {success} Modell(e) gesetzt, {failed} fehlgeschlagen",

View File

@@ -131,7 +131,8 @@
}, },
"badges": { "badges": {
"update": "Update", "update": "Update",
"updateAvailable": "Update available" "updateAvailable": "Update available",
"skipRefresh": "Metadata refresh skipped"
}, },
"usage": { "usage": {
"timesUsed": "Times used" "timesUsed": "Times used"
@@ -527,8 +528,12 @@
"checkUpdates": "Check Updates for Selected", "checkUpdates": "Check Updates for Selected",
"moveAll": "Move Selected to Folder", "moveAll": "Move Selected to Folder",
"autoOrganize": "Auto-Organize Selected", "autoOrganize": "Auto-Organize Selected",
"skipMetadataRefresh": "Skip Metadata Refresh for Selected",
"resumeMetadataRefresh": "Resume Metadata Refresh for Selected",
"deleteAll": "Delete Selected Models", "deleteAll": "Delete Selected Models",
"clear": "Clear Selection", "clear": "Clear Selection",
"skipMetadataRefreshCount": "Skip ({count} models)",
"resumeMetadataRefreshCount": "Resume ({count} models)",
"autoOrganizeProgress": { "autoOrganizeProgress": {
"initializing": "Initializing auto-organize...", "initializing": "Initializing auto-organize...",
"starting": "Starting auto-organize for {type}...", "starting": "Starting auto-organize for {type}...",
@@ -1379,6 +1384,11 @@
"bulkBaseModelUpdateSuccess": "Successfully updated base model for {count} model(s)", "bulkBaseModelUpdateSuccess": "Successfully updated base model for {count} model(s)",
"bulkBaseModelUpdatePartial": "Updated {success} model(s), failed {failed} model(s)", "bulkBaseModelUpdatePartial": "Updated {success} model(s), failed {failed} model(s)",
"bulkBaseModelUpdateFailed": "Failed to update base model for selected models", "bulkBaseModelUpdateFailed": "Failed to update base model for selected models",
"skipMetadataRefreshUpdating": "Updating metadata refresh flag for {count} model(s)...",
"skipMetadataRefreshSet": "Metadata refresh skipped for {count} model(s)",
"skipMetadataRefreshCleared": "Metadata refresh resumed for {count} model(s)",
"skipMetadataRefreshPartial": "Updated {success} model(s), {failed} failed",
"skipMetadataRefreshFailed": "Failed to update metadata refresh flag for selected models",
"bulkContentRatingUpdating": "Updating content rating for {count} model(s)...", "bulkContentRatingUpdating": "Updating content rating for {count} model(s)...",
"bulkContentRatingSet": "Set content rating to {level} for {count} model(s)", "bulkContentRatingSet": "Set content rating to {level} for {count} model(s)",
"bulkContentRatingPartial": "Set content rating to {level} for {success} model(s), {failed} failed", "bulkContentRatingPartial": "Set content rating to {level} for {success} model(s), {failed} failed",

View File

@@ -131,7 +131,8 @@
}, },
"badges": { "badges": {
"update": "Actualización", "update": "Actualización",
"updateAvailable": "Actualización disponible" "updateAvailable": "Actualización disponible",
"skipRefresh": "[TODO: Translate] Metadata refresh skipped"
}, },
"usage": { "usage": {
"timesUsed": "Veces usado" "timesUsed": "Veces usado"
@@ -527,8 +528,12 @@
"checkUpdates": "Comprobar actualizaciones para la selección", "checkUpdates": "Comprobar actualizaciones para la selección",
"moveAll": "Mover todos a carpeta", "moveAll": "Mover todos a carpeta",
"autoOrganize": "Auto-organizar seleccionados", "autoOrganize": "Auto-organizar seleccionados",
"skipMetadataRefresh": "[TODO: Translate] Skip Metadata Refresh for Selected",
"resumeMetadataRefresh": "[TODO: Translate] Resume Metadata Refresh for Selected",
"deleteAll": "Eliminar todos los modelos", "deleteAll": "Eliminar todos los modelos",
"clear": "Limpiar selección", "clear": "Limpiar selección",
"skipMetadataRefreshCount": "[TODO: Translate] Skip ({count} models)",
"resumeMetadataRefreshCount": "[TODO: Translate] Resume ({count} models)",
"autoOrganizeProgress": { "autoOrganizeProgress": {
"initializing": "Inicializando auto-organización...", "initializing": "Inicializando auto-organización...",
"starting": "Iniciando auto-organización para {type}...", "starting": "Iniciando auto-organización para {type}...",
@@ -1379,6 +1384,11 @@
"bulkBaseModelUpdateSuccess": "Modelo base actualizado exitosamente para {count} modelo(s)", "bulkBaseModelUpdateSuccess": "Modelo base actualizado exitosamente para {count} modelo(s)",
"bulkBaseModelUpdatePartial": "Actualizados {success} modelo(s), fallaron {failed} modelo(s)", "bulkBaseModelUpdatePartial": "Actualizados {success} modelo(s), fallaron {failed} modelo(s)",
"bulkBaseModelUpdateFailed": "Error al actualizar el modelo base para los modelos seleccionados", "bulkBaseModelUpdateFailed": "Error al actualizar el modelo base para los modelos seleccionados",
"skipMetadataRefreshUpdating": "[TODO: Translate] Updating metadata refresh flag for {count} model(s)...",
"skipMetadataRefreshSet": "[TODO: Translate] Metadata refresh skipped for {count} model(s)",
"skipMetadataRefreshCleared": "[TODO: Translate] Metadata refresh resumed for {count} model(s)",
"skipMetadataRefreshPartial": "[TODO: Translate] Updated {success} model(s), {failed} failed",
"skipMetadataRefreshFailed": "[TODO: Translate] Failed to update metadata refresh flag for selected models",
"bulkContentRatingUpdating": "Actualizando la clasificación de contenido para {count} modelo(s)...", "bulkContentRatingUpdating": "Actualizando la clasificación de contenido para {count} modelo(s)...",
"bulkContentRatingSet": "Clasificación de contenido establecida en {level} para {count} modelo(s)", "bulkContentRatingSet": "Clasificación de contenido establecida en {level} para {count} modelo(s)",
"bulkContentRatingPartial": "Clasificación de contenido establecida en {level} para {success} modelo(s), {failed} fallaron", "bulkContentRatingPartial": "Clasificación de contenido establecida en {level} para {success} modelo(s), {failed} fallaron",

View File

@@ -131,7 +131,8 @@
}, },
"badges": { "badges": {
"update": "Mise à jour", "update": "Mise à jour",
"updateAvailable": "Mise à jour disponible" "updateAvailable": "Mise à jour disponible",
"skipRefresh": "[TODO: Translate] Metadata refresh skipped"
}, },
"usage": { "usage": {
"timesUsed": "Nombre d'utilisations" "timesUsed": "Nombre d'utilisations"
@@ -527,8 +528,12 @@
"checkUpdates": "Vérifier les mises à jour pour la sélection", "checkUpdates": "Vérifier les mises à jour pour la sélection",
"moveAll": "Déplacer tout vers un dossier", "moveAll": "Déplacer tout vers un dossier",
"autoOrganize": "Auto-organiser la sélection", "autoOrganize": "Auto-organiser la sélection",
"skipMetadataRefresh": "[TODO: Translate] Skip Metadata Refresh for Selected",
"resumeMetadataRefresh": "[TODO: Translate] Resume Metadata Refresh for Selected",
"deleteAll": "Supprimer tous les modèles", "deleteAll": "Supprimer tous les modèles",
"clear": "Effacer la sélection", "clear": "Effacer la sélection",
"skipMetadataRefreshCount": "[TODO: Translate] Skip ({count} models)",
"resumeMetadataRefreshCount": "[TODO: Translate] Resume ({count} models)",
"autoOrganizeProgress": { "autoOrganizeProgress": {
"initializing": "Initialisation de l'auto-organisation...", "initializing": "Initialisation de l'auto-organisation...",
"starting": "Démarrage de l'auto-organisation pour {type}...", "starting": "Démarrage de l'auto-organisation pour {type}...",
@@ -1379,6 +1384,11 @@
"bulkBaseModelUpdateSuccess": "Modèle de base mis à jour avec succès pour {count} modèle(s)", "bulkBaseModelUpdateSuccess": "Modèle de base mis à jour avec succès pour {count} modèle(s)",
"bulkBaseModelUpdatePartial": "{success} modèle(s) mis à jour, {failed} modèle(s) en échec", "bulkBaseModelUpdatePartial": "{success} modèle(s) mis à jour, {failed} modèle(s) en échec",
"bulkBaseModelUpdateFailed": "Échec de la mise à jour du modèle de base pour les modèles sélectionnés", "bulkBaseModelUpdateFailed": "Échec de la mise à jour du modèle de base pour les modèles sélectionnés",
"skipMetadataRefreshUpdating": "[TODO: Translate] Updating metadata refresh flag for {count} model(s)...",
"skipMetadataRefreshSet": "[TODO: Translate] Metadata refresh skipped for {count} model(s)",
"skipMetadataRefreshCleared": "[TODO: Translate] Metadata refresh resumed for {count} model(s)",
"skipMetadataRefreshPartial": "[TODO: Translate] Updated {success} model(s), {failed} failed",
"skipMetadataRefreshFailed": "[TODO: Translate] Failed to update metadata refresh flag for selected models",
"bulkContentRatingUpdating": "Mise à jour de la classification du contenu pour {count} modèle(s)...", "bulkContentRatingUpdating": "Mise à jour de la classification du contenu pour {count} modèle(s)...",
"bulkContentRatingSet": "Classification du contenu définie sur {level} pour {count} modèle(s)", "bulkContentRatingSet": "Classification du contenu définie sur {level} pour {count} modèle(s)",
"bulkContentRatingPartial": "Classification du contenu définie sur {level} pour {success} modèle(s), {failed} échec(s)", "bulkContentRatingPartial": "Classification du contenu définie sur {level} pour {success} modèle(s), {failed} échec(s)",

View File

@@ -131,7 +131,8 @@
}, },
"badges": { "badges": {
"update": "עדכון", "update": "עדכון",
"updateAvailable": "עדכון זמין" "updateAvailable": "עדכון זמין",
"skipRefresh": "[TODO: Translate] Metadata refresh skipped"
}, },
"usage": { "usage": {
"timesUsed": "מספר שימושים" "timesUsed": "מספר שימושים"
@@ -527,8 +528,12 @@
"checkUpdates": "בדוק עדכונים לבחירה", "checkUpdates": "בדוק עדכונים לבחירה",
"moveAll": "העבר הכל לתיקייה", "moveAll": "העבר הכל לתיקייה",
"autoOrganize": "ארגן אוטומטית נבחרים", "autoOrganize": "ארגן אוטומטית נבחרים",
"skipMetadataRefresh": "[TODO: Translate] Skip Metadata Refresh for Selected",
"resumeMetadataRefresh": "[TODO: Translate] Resume Metadata Refresh for Selected",
"deleteAll": "מחק את כל המודלים", "deleteAll": "מחק את כל המודלים",
"clear": "נקה בחירה", "clear": "נקה בחירה",
"skipMetadataRefreshCount": "[TODO: Translate] Skip ({count} models)",
"resumeMetadataRefreshCount": "[TODO: Translate] Resume ({count} models)",
"autoOrganizeProgress": { "autoOrganizeProgress": {
"initializing": "מאתחל ארגון אוטומטי...", "initializing": "מאתחל ארגון אוטומטי...",
"starting": "מתחיל ארגון אוטומטי עבור {type}...", "starting": "מתחיל ארגון אוטומטי עבור {type}...",
@@ -1379,6 +1384,11 @@
"bulkBaseModelUpdateSuccess": "עודכן בהצלחה מודל הבסיס עבור {count} מודל(ים)", "bulkBaseModelUpdateSuccess": "עודכן בהצלחה מודל הבסיס עבור {count} מודל(ים)",
"bulkBaseModelUpdatePartial": "עודכנו {success} מודל(ים), נכשלו {failed} מודל(ים)", "bulkBaseModelUpdatePartial": "עודכנו {success} מודל(ים), נכשלו {failed} מודל(ים)",
"bulkBaseModelUpdateFailed": "עדכון מודל הבסיס עבור המודלים שנבחרו נכשל", "bulkBaseModelUpdateFailed": "עדכון מודל הבסיס עבור המודלים שנבחרו נכשל",
"skipMetadataRefreshUpdating": "[TODO: Translate] Updating metadata refresh flag for {count} model(s)...",
"skipMetadataRefreshSet": "[TODO: Translate] Metadata refresh skipped for {count} model(s)",
"skipMetadataRefreshCleared": "[TODO: Translate] Metadata refresh resumed for {count} model(s)",
"skipMetadataRefreshPartial": "[TODO: Translate] Updated {success} model(s), {failed} failed",
"skipMetadataRefreshFailed": "[TODO: Translate] Failed to update metadata refresh flag for selected models",
"bulkContentRatingUpdating": "מעדכן דירוג תוכן עבור {count} מודלים...", "bulkContentRatingUpdating": "מעדכן דירוג תוכן עבור {count} מודלים...",
"bulkContentRatingSet": "דירוג התוכן הוגדר ל-{level} עבור {count} מודלים", "bulkContentRatingSet": "דירוג התוכן הוגדר ל-{level} עבור {count} מודלים",
"bulkContentRatingPartial": "דירוג התוכן הוגדר ל-{level} עבור {success} מודלים, {failed} נכשלו", "bulkContentRatingPartial": "דירוג התוכן הוגדר ל-{level} עבור {success} מודלים, {failed} נכשלו",

View File

@@ -131,7 +131,8 @@
}, },
"badges": { "badges": {
"update": "アップデート", "update": "アップデート",
"updateAvailable": "アップデートがあります" "updateAvailable": "アップデートがあります",
"skipRefresh": "[TODO: Translate] Metadata refresh skipped"
}, },
"usage": { "usage": {
"timesUsed": "使用回数" "timesUsed": "使用回数"
@@ -527,8 +528,12 @@
"checkUpdates": "選択項目の更新を確認", "checkUpdates": "選択項目の更新を確認",
"moveAll": "すべてをフォルダに移動", "moveAll": "すべてをフォルダに移動",
"autoOrganize": "自動整理を実行", "autoOrganize": "自動整理を実行",
"skipMetadataRefresh": "[TODO: Translate] Skip Metadata Refresh for Selected",
"resumeMetadataRefresh": "[TODO: Translate] Resume Metadata Refresh for Selected",
"deleteAll": "すべてのモデルを削除", "deleteAll": "すべてのモデルを削除",
"clear": "選択をクリア", "clear": "選択をクリア",
"skipMetadataRefreshCount": "[TODO: Translate] Skip ({count} models)",
"resumeMetadataRefreshCount": "[TODO: Translate] Resume ({count} models)",
"autoOrganizeProgress": { "autoOrganizeProgress": {
"initializing": "自動整理を初期化中...", "initializing": "自動整理を初期化中...",
"starting": "{type}の自動整理を開始中...", "starting": "{type}の自動整理を開始中...",
@@ -1379,6 +1384,11 @@
"bulkBaseModelUpdateSuccess": "{count} モデルのベースモデルが正常に更新されました", "bulkBaseModelUpdateSuccess": "{count} モデルのベースモデルが正常に更新されました",
"bulkBaseModelUpdatePartial": "{success} モデルを更新、{failed} モデルは失敗しました", "bulkBaseModelUpdatePartial": "{success} モデルを更新、{failed} モデルは失敗しました",
"bulkBaseModelUpdateFailed": "選択したモデルのベースモデルの更新に失敗しました", "bulkBaseModelUpdateFailed": "選択したモデルのベースモデルの更新に失敗しました",
"skipMetadataRefreshUpdating": "[TODO: Translate] Updating metadata refresh flag for {count} model(s)...",
"skipMetadataRefreshSet": "[TODO: Translate] Metadata refresh skipped for {count} model(s)",
"skipMetadataRefreshCleared": "[TODO: Translate] Metadata refresh resumed for {count} model(s)",
"skipMetadataRefreshPartial": "[TODO: Translate] Updated {success} model(s), {failed} failed",
"skipMetadataRefreshFailed": "[TODO: Translate] Failed to update metadata refresh flag for selected models",
"bulkContentRatingUpdating": "{count} 件のモデルのコンテンツレーティングを更新中...", "bulkContentRatingUpdating": "{count} 件のモデルのコンテンツレーティングを更新中...",
"bulkContentRatingSet": "{count} 件のモデルのコンテンツレーティングを {level} に設定しました", "bulkContentRatingSet": "{count} 件のモデルのコンテンツレーティングを {level} に設定しました",
"bulkContentRatingPartial": "{success} 件のモデルのコンテンツレーティングを {level} に設定、{failed} 件は失敗しました", "bulkContentRatingPartial": "{success} 件のモデルのコンテンツレーティングを {level} に設定、{failed} 件は失敗しました",

View File

@@ -131,7 +131,8 @@
}, },
"badges": { "badges": {
"update": "업데이트", "update": "업데이트",
"updateAvailable": "업데이트 가능" "updateAvailable": "업데이트 가능",
"skipRefresh": "[TODO: Translate] Metadata refresh skipped"
}, },
"usage": { "usage": {
"timesUsed": "사용 횟수" "timesUsed": "사용 횟수"
@@ -527,8 +528,12 @@
"checkUpdates": "선택 항목 업데이트 확인", "checkUpdates": "선택 항목 업데이트 확인",
"moveAll": "모두 폴더로 이동", "moveAll": "모두 폴더로 이동",
"autoOrganize": "자동 정리 선택", "autoOrganize": "자동 정리 선택",
"skipMetadataRefresh": "[TODO: Translate] Skip Metadata Refresh for Selected",
"resumeMetadataRefresh": "[TODO: Translate] Resume Metadata Refresh for Selected",
"deleteAll": "모든 모델 삭제", "deleteAll": "모든 모델 삭제",
"clear": "선택 지우기", "clear": "선택 지우기",
"skipMetadataRefreshCount": "[TODO: Translate] Skip ({count} models)",
"resumeMetadataRefreshCount": "[TODO: Translate] Resume ({count} models)",
"autoOrganizeProgress": { "autoOrganizeProgress": {
"initializing": "자동 정리 초기화 중...", "initializing": "자동 정리 초기화 중...",
"starting": "{type}에 대한 자동 정리 시작...", "starting": "{type}에 대한 자동 정리 시작...",
@@ -1379,6 +1384,11 @@
"bulkBaseModelUpdateSuccess": "{count}개의 모델에 베이스 모델이 성공적으로 업데이트되었습니다", "bulkBaseModelUpdateSuccess": "{count}개의 모델에 베이스 모델이 성공적으로 업데이트되었습니다",
"bulkBaseModelUpdatePartial": "{success}개의 모델이 업데이트되었고, {failed}개의 모델이 실패했습니다", "bulkBaseModelUpdatePartial": "{success}개의 모델이 업데이트되었고, {failed}개의 모델이 실패했습니다",
"bulkBaseModelUpdateFailed": "선택한 모델의 베이스 모델 업데이트에 실패했습니다", "bulkBaseModelUpdateFailed": "선택한 모델의 베이스 모델 업데이트에 실패했습니다",
"skipMetadataRefreshUpdating": "[TODO: Translate] Updating metadata refresh flag for {count} model(s)...",
"skipMetadataRefreshSet": "[TODO: Translate] Metadata refresh skipped for {count} model(s)",
"skipMetadataRefreshCleared": "[TODO: Translate] Metadata refresh resumed for {count} model(s)",
"skipMetadataRefreshPartial": "[TODO: Translate] Updated {success} model(s), {failed} failed",
"skipMetadataRefreshFailed": "[TODO: Translate] Failed to update metadata refresh flag for selected models",
"bulkContentRatingUpdating": "{count}개 모델의 콘텐츠 등급을 업데이트하는 중...", "bulkContentRatingUpdating": "{count}개 모델의 콘텐츠 등급을 업데이트하는 중...",
"bulkContentRatingSet": "{count}개 모델의 콘텐츠 등급을 {level}(으)로 설정했습니다", "bulkContentRatingSet": "{count}개 모델의 콘텐츠 등급을 {level}(으)로 설정했습니다",
"bulkContentRatingPartial": "{success}개 모델의 콘텐츠 등급을 {level}(으)로 설정했고, {failed}개는 실패했습니다", "bulkContentRatingPartial": "{success}개 모델의 콘텐츠 등급을 {level}(으)로 설정했고, {failed}개는 실패했습니다",

View File

@@ -131,7 +131,8 @@
}, },
"badges": { "badges": {
"update": "Обновление", "update": "Обновление",
"updateAvailable": "Доступно обновление" "updateAvailable": "Доступно обновление",
"skipRefresh": "[TODO: Translate] Metadata refresh skipped"
}, },
"usage": { "usage": {
"timesUsed": "Количество использований" "timesUsed": "Количество использований"
@@ -527,8 +528,12 @@
"checkUpdates": "Проверить обновления для выбранных", "checkUpdates": "Проверить обновления для выбранных",
"moveAll": "Переместить все в папку", "moveAll": "Переместить все в папку",
"autoOrganize": "Автоматически организовать выбранные", "autoOrganize": "Автоматически организовать выбранные",
"skipMetadataRefresh": "[TODO: Translate] Skip Metadata Refresh for Selected",
"resumeMetadataRefresh": "[TODO: Translate] Resume Metadata Refresh for Selected",
"deleteAll": "Удалить все модели", "deleteAll": "Удалить все модели",
"clear": "Очистить выбор", "clear": "Очистить выбор",
"skipMetadataRefreshCount": "[TODO: Translate] Skip ({count} models)",
"resumeMetadataRefreshCount": "[TODO: Translate] Resume ({count} models)",
"autoOrganizeProgress": { "autoOrganizeProgress": {
"initializing": "Инициализация автоматической организации...", "initializing": "Инициализация автоматической организации...",
"starting": "Запуск автоматической организации для {type}...", "starting": "Запуск автоматической организации для {type}...",
@@ -1379,6 +1384,11 @@
"bulkBaseModelUpdateSuccess": "Базовая модель успешно обновлена для {count} моделей", "bulkBaseModelUpdateSuccess": "Базовая модель успешно обновлена для {count} моделей",
"bulkBaseModelUpdatePartial": "Обновлено {success} моделей, не удалось обновить {failed} моделей", "bulkBaseModelUpdatePartial": "Обновлено {success} моделей, не удалось обновить {failed} моделей",
"bulkBaseModelUpdateFailed": "Не удалось обновить базовую модель для выбранных моделей", "bulkBaseModelUpdateFailed": "Не удалось обновить базовую модель для выбранных моделей",
"skipMetadataRefreshUpdating": "[TODO: Translate] Updating metadata refresh flag for {count} model(s)...",
"skipMetadataRefreshSet": "[TODO: Translate] Metadata refresh skipped for {count} model(s)",
"skipMetadataRefreshCleared": "[TODO: Translate] Metadata refresh resumed for {count} model(s)",
"skipMetadataRefreshPartial": "[TODO: Translate] Updated {success} model(s), {failed} failed",
"skipMetadataRefreshFailed": "[TODO: Translate] Failed to update metadata refresh flag for selected models",
"bulkContentRatingUpdating": "Обновление рейтинга контента для {count} модель(ей)...", "bulkContentRatingUpdating": "Обновление рейтинга контента для {count} модель(ей)...",
"bulkContentRatingSet": "Рейтинг контента установлен на {level} для {count} модель(ей)", "bulkContentRatingSet": "Рейтинг контента установлен на {level} для {count} модель(ей)",
"bulkContentRatingPartial": "Рейтинг контента {level} установлен для {success} модель(ей), {failed} не удалось", "bulkContentRatingPartial": "Рейтинг контента {level} установлен для {success} модель(ей), {failed} не удалось",

View File

@@ -131,7 +131,8 @@
}, },
"badges": { "badges": {
"update": "更新", "update": "更新",
"updateAvailable": "有可用更新" "updateAvailable": "有可用更新",
"skipRefresh": "[TODO: Translate] Metadata refresh skipped"
}, },
"usage": { "usage": {
"timesUsed": "使用次数" "timesUsed": "使用次数"
@@ -527,8 +528,12 @@
"checkUpdates": "检查所选更新", "checkUpdates": "检查所选更新",
"moveAll": "移动所选中到文件夹", "moveAll": "移动所选中到文件夹",
"autoOrganize": "自动整理所选模型", "autoOrganize": "自动整理所选模型",
"skipMetadataRefresh": "[TODO: Translate] Skip Metadata Refresh for Selected",
"resumeMetadataRefresh": "[TODO: Translate] Resume Metadata Refresh for Selected",
"deleteAll": "删除选中模型", "deleteAll": "删除选中模型",
"clear": "清除选择", "clear": "清除选择",
"skipMetadataRefreshCount": "[TODO: Translate] Skip ({count} models)",
"resumeMetadataRefreshCount": "[TODO: Translate] Resume ({count} models)",
"autoOrganizeProgress": { "autoOrganizeProgress": {
"initializing": "正在初始化自动整理...", "initializing": "正在初始化自动整理...",
"starting": "正在为 {type} 启动自动整理...", "starting": "正在为 {type} 启动自动整理...",
@@ -1379,6 +1384,11 @@
"bulkBaseModelUpdateSuccess": "成功为 {count} 个模型更新基础模型", "bulkBaseModelUpdateSuccess": "成功为 {count} 个模型更新基础模型",
"bulkBaseModelUpdatePartial": "更新了 {success} 个模型,{failed} 个失败", "bulkBaseModelUpdatePartial": "更新了 {success} 个模型,{failed} 个失败",
"bulkBaseModelUpdateFailed": "为选中模型更新基础模型失败", "bulkBaseModelUpdateFailed": "为选中模型更新基础模型失败",
"skipMetadataRefreshUpdating": "[TODO: Translate] Updating metadata refresh flag for {count} model(s)...",
"skipMetadataRefreshSet": "[TODO: Translate] Metadata refresh skipped for {count} model(s)",
"skipMetadataRefreshCleared": "[TODO: Translate] Metadata refresh resumed for {count} model(s)",
"skipMetadataRefreshPartial": "[TODO: Translate] Updated {success} model(s), {failed} failed",
"skipMetadataRefreshFailed": "[TODO: Translate] Failed to update metadata refresh flag for selected models",
"bulkContentRatingUpdating": "正在为 {count} 个模型更新内容评级...", "bulkContentRatingUpdating": "正在为 {count} 个模型更新内容评级...",
"bulkContentRatingSet": "已将 {count} 个模型的内容评级设置为 {level}", "bulkContentRatingSet": "已将 {count} 个模型的内容评级设置为 {level}",
"bulkContentRatingPartial": "已将 {success} 个模型的内容评级设置为 {level}{failed} 个失败", "bulkContentRatingPartial": "已将 {success} 个模型的内容评级设置为 {level}{failed} 个失败",

View File

@@ -131,7 +131,8 @@
}, },
"badges": { "badges": {
"update": "更新", "update": "更新",
"updateAvailable": "有可用更新" "updateAvailable": "有可用更新",
"skipRefresh": "[TODO: Translate] Metadata refresh skipped"
}, },
"usage": { "usage": {
"timesUsed": "使用次數" "timesUsed": "使用次數"
@@ -527,8 +528,12 @@
"checkUpdates": "檢查所選更新", "checkUpdates": "檢查所選更新",
"moveAll": "全部移動到資料夾", "moveAll": "全部移動到資料夾",
"autoOrganize": "自動整理所選模型", "autoOrganize": "自動整理所選模型",
"skipMetadataRefresh": "[TODO: Translate] Skip Metadata Refresh for Selected",
"resumeMetadataRefresh": "[TODO: Translate] Resume Metadata Refresh for Selected",
"deleteAll": "刪除全部模型", "deleteAll": "刪除全部模型",
"clear": "清除選取", "clear": "清除選取",
"skipMetadataRefreshCount": "[TODO: Translate] Skip ({count} models)",
"resumeMetadataRefreshCount": "[TODO: Translate] Resume ({count} models)",
"autoOrganizeProgress": { "autoOrganizeProgress": {
"initializing": "正在初始化自動整理...", "initializing": "正在初始化自動整理...",
"starting": "正在開始自動整理 {type}...", "starting": "正在開始自動整理 {type}...",
@@ -1379,6 +1384,11 @@
"bulkBaseModelUpdateSuccess": "已成功為 {count} 個模型更新基礎模型", "bulkBaseModelUpdateSuccess": "已成功為 {count} 個模型更新基礎模型",
"bulkBaseModelUpdatePartial": "已更新 {success} 個模型,{failed} 個模型失敗", "bulkBaseModelUpdatePartial": "已更新 {success} 個模型,{failed} 個模型失敗",
"bulkBaseModelUpdateFailed": "更新所選模型的基礎模型失敗", "bulkBaseModelUpdateFailed": "更新所選模型的基礎模型失敗",
"skipMetadataRefreshUpdating": "[TODO: Translate] Updating metadata refresh flag for {count} model(s)...",
"skipMetadataRefreshSet": "[TODO: Translate] Metadata refresh skipped for {count} model(s)",
"skipMetadataRefreshCleared": "[TODO: Translate] Metadata refresh resumed for {count} model(s)",
"skipMetadataRefreshPartial": "[TODO: Translate] Updated {success} model(s), {failed} failed",
"skipMetadataRefreshFailed": "[TODO: Translate] Failed to update metadata refresh flag for selected models",
"bulkContentRatingUpdating": "正在為 {count} 個模型更新內容分級...", "bulkContentRatingUpdating": "正在為 {count} 個模型更新內容分級...",
"bulkContentRatingSet": "已將 {count} 個模型的內容分級設定為 {level}", "bulkContentRatingSet": "已將 {count} 個模型的內容分級設定為 {level}",
"bulkContentRatingPartial": "已將 {success} 個模型的內容分級設定為 {level}{failed} 個失敗", "bulkContentRatingPartial": "已將 {success} 個模型的內容分級設定為 {level}{failed} 個失敗",

View File

@@ -43,6 +43,7 @@ class CheckpointService(BaseModelService):
"sub_type": sub_type, "sub_type": sub_type,
"favorite": checkpoint_data.get("favorite", False), "favorite": checkpoint_data.get("favorite", False),
"update_available": bool(checkpoint_data.get("update_available", False)), "update_available": bool(checkpoint_data.get("update_available", False)),
"skip_metadata_refresh": bool(checkpoint_data.get("skip_metadata_refresh", False)),
"civitai": self.filter_civitai_data(checkpoint_data.get("civitai", {}), minimal=True) "civitai": self.filter_civitai_data(checkpoint_data.get("civitai", {}), minimal=True)
} }

View File

@@ -43,6 +43,7 @@ class EmbeddingService(BaseModelService):
"sub_type": sub_type, "sub_type": sub_type,
"favorite": embedding_data.get("favorite", False), "favorite": embedding_data.get("favorite", False),
"update_available": bool(embedding_data.get("update_available", False)), "update_available": bool(embedding_data.get("update_available", False)),
"skip_metadata_refresh": bool(embedding_data.get("skip_metadata_refresh", False)),
"civitai": self.filter_civitai_data(embedding_data.get("civitai", {}), minimal=True) "civitai": self.filter_civitai_data(embedding_data.get("civitai", {}), minimal=True)
} }

View File

@@ -48,6 +48,7 @@ class LoraService(BaseModelService):
"notes": lora_data.get("notes", ""), "notes": lora_data.get("notes", ""),
"favorite": lora_data.get("favorite", False), "favorite": lora_data.get("favorite", False),
"update_available": bool(lora_data.get("update_available", False)), "update_available": bool(lora_data.get("update_available", False)),
"skip_metadata_refresh": bool(lora_data.get("skip_metadata_refresh", False)),
"sub_type": sub_type, "sub_type": sub_type,
"civitai": self.filter_civitai_data( "civitai": self.filter_civitai_data(
lora_data.get("civitai", {}), minimal=True lora_data.get("civitai", {}), minimal=True

View File

@@ -248,6 +248,7 @@ class ModelScanner:
'tags': tags_list, 'tags': tags_list,
'civitai': civitai_slim, 'civitai': civitai_slim,
'civitai_deleted': bool(get_value('civitai_deleted', False)), 'civitai_deleted': bool(get_value('civitai_deleted', False)),
'skip_metadata_refresh': bool(get_value('skip_metadata_refresh', False)),
} }
license_source: Dict[str, Any] = {} license_source: Dict[str, Any] = {}

View File

@@ -52,6 +52,7 @@ class PersistentModelCache:
"trained_words", "trained_words",
"license_flags", "license_flags",
"civitai_deleted", "civitai_deleted",
"skip_metadata_refresh",
"exclude", "exclude",
"db_checked", "db_checked",
"last_checked_at", "last_checked_at",
@@ -183,6 +184,7 @@ class PersistentModelCache:
"tags": tags.get(file_path, []), "tags": tags.get(file_path, []),
"civitai": civitai, "civitai": civitai,
"civitai_deleted": bool(row["civitai_deleted"]), "civitai_deleted": bool(row["civitai_deleted"]),
"skip_metadata_refresh": bool(row["skip_metadata_refresh"]),
"license_flags": int(license_value), "license_flags": int(license_value),
} }
raw_data.append(item) raw_data.append(item)
@@ -491,6 +493,7 @@ class PersistentModelCache:
"civitai_creator_username": "TEXT", "civitai_creator_username": "TEXT",
"civitai_model_type": "TEXT", "civitai_model_type": "TEXT",
"civitai_deleted": "INTEGER DEFAULT 0", "civitai_deleted": "INTEGER DEFAULT 0",
"skip_metadata_refresh": "INTEGER DEFAULT 0",
# Persisting without explicit flags should assume CivitAI's documented defaults (0b111001 == 57). # Persisting without explicit flags should assume CivitAI's documented defaults (0b111001 == 57).
"license_flags": f"INTEGER DEFAULT {DEFAULT_LICENSE_FLAGS}", "license_flags": f"INTEGER DEFAULT {DEFAULT_LICENSE_FLAGS}",
} }
@@ -563,6 +566,7 @@ class PersistentModelCache:
trained_words_json, trained_words_json,
int(license_flags), int(license_flags),
1 if item.get("civitai_deleted") else 0, 1 if item.get("civitai_deleted") else 0,
1 if item.get("skip_metadata_refresh") else 0,
1 if item.get("exclude") else 0, 1 if item.get("exclude") else 0,
1 if item.get("db_checked") else 0, 1 if item.get("db_checked") else 0,
float(item.get("last_checked_at") or 0.0), float(item.get("last_checked_at") or 0.0),

View File

@@ -47,6 +47,7 @@ class BulkMetadataRefreshUseCase:
model model
for model in cache.raw_data for model in cache.raw_data
if model.get("sha256") if model.get("sha256")
and not model.get("skip_metadata_refresh", False)
and (not model.get("civitai") or not model["civitai"].get("id")) and (not model.get("civitai") or not model["civitai"].get("id"))
and not ( and not (
# Skip models confirmed not on CivitAI when no need to retry # Skip models confirmed not on CivitAI when no need to retry

View File

@@ -25,6 +25,7 @@ class BaseModelMetadata:
favorite: bool = False # Whether the model is a favorite favorite: bool = False # Whether the model is a favorite
exclude: bool = False # Whether to exclude this model from the cache exclude: bool = False # Whether to exclude this model from the cache
db_checked: bool = False # Whether checked in archive DB db_checked: bool = False # Whether checked in archive DB
skip_metadata_refresh: bool = False # Whether to skip this model during bulk metadata refresh
metadata_source: Optional[str] = None # Last provider that supplied metadata metadata_source: Optional[str] = None # Last provider that supplied metadata
last_checked_at: float = 0 # Last checked timestamp last_checked_at: float = 0 # Last checked timestamp
_unknown_fields: Dict[str, Any] = field(default_factory=dict, repr=False, compare=False) # Store unknown fields _unknown_fields: Dict[str, Any] = field(default_factory=dict, repr=False, compare=False) # Store unknown fields

View File

@@ -60,6 +60,9 @@ body {
--badge-update-bg: oklch(72% 0.2 220); --badge-update-bg: oklch(72% 0.2 220);
--badge-update-text: oklch(28% 0.03 220); --badge-update-text: oklch(28% 0.03 220);
--badge-update-glow: oklch(72% 0.2 220 / 0.28); --badge-update-glow: oklch(72% 0.2 220 / 0.28);
--badge-skip-refresh-bg: oklch(82% 0.12 45);
--badge-skip-refresh-text: oklch(35% 0.02 45);
--badge-skip-refresh-glow: oklch(82% 0.12 45 / 0.15);
/* Spacing Scale */ /* Spacing Scale */
--space-1: calc(8px * 1); --space-1: calc(8px * 1);
@@ -114,6 +117,9 @@ html[data-theme="light"] {
--badge-update-bg: oklch(62% 0.18 220); --badge-update-bg: oklch(62% 0.18 220);
--badge-update-text: oklch(98% 0.02 240); --badge-update-text: oklch(98% 0.02 240);
--badge-update-glow: oklch(62% 0.18 220 / 0.4); --badge-update-glow: oklch(62% 0.18 220 / 0.4);
--badge-skip-refresh-bg: oklch(82% 0.12 45);
--badge-skip-refresh-text: oklch(98% 0.02 45);
--badge-skip-refresh-glow: oklch(82% 0.12 45 / 0.15);
} }
body { body {

View File

@@ -658,3 +658,25 @@
margin-left: 1px; margin-left: 1px;
line-height: 1; line-height: 1;
} }
.model-skip-refresh-badge {
width: 16px;
height: 16px;
padding: 0;
border-radius: 3px;
background: var(--badge-skip-refresh-bg);
color: var(--badge-skip-refresh-text);
font-size: 0.65rem;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
box-shadow: 0 1px 3px var(--badge-skip-refresh-glow);
border: 1px solid color-mix(in oklab, var(--badge-skip-refresh-bg) 70%, transparent);
opacity: 0.85;
}
.model-skip-refresh-badge i {
margin-left: 0;
line-height: 1;
}

View File

@@ -1,7 +1,7 @@
import { BaseContextMenu } from './BaseContextMenu.js'; import { BaseContextMenu } from './BaseContextMenu.js';
import { state } from '../../state/index.js'; import { state } from '../../state/index.js';
import { bulkManager } from '../../managers/BulkManager.js'; import { bulkManager } from '../../managers/BulkManager.js';
import { updateElementText } from '../../utils/i18nHelpers.js'; import { updateElementText, translate } from '../../utils/i18nHelpers.js';
export class BulkContextMenu extends BaseContextMenu { export class BulkContextMenu extends BaseContextMenu {
constructor() { constructor() {
@@ -71,6 +71,40 @@ export class BulkContextMenu extends BaseContextMenu {
if (setContentRatingItem) { if (setContentRatingItem) {
setContentRatingItem.style.display = config.setContentRating ? 'flex' : 'none'; setContentRatingItem.style.display = config.setContentRating ? 'flex' : 'none';
} }
const skipMetadataRefreshItem = this.menu.querySelector('[data-action="skip-metadata-refresh"]');
const resumeMetadataRefreshItem = this.menu.querySelector('[data-action="resume-metadata-refresh"]');
if (skipMetadataRefreshItem && resumeMetadataRefreshItem) {
const skipCount = this.countSkipStatus(true);
const resumeCount = this.countSkipStatus(false);
const totalCount = skipCount + resumeCount;
if (skipCount === totalCount) {
skipMetadataRefreshItem.style.display = 'none';
resumeMetadataRefreshItem.style.display = 'flex';
resumeMetadataRefreshItem.querySelector('span').textContent = translate(
'loras.bulkOperations.resumeMetadataRefresh'
);
} else if (resumeCount === totalCount) {
skipMetadataRefreshItem.style.display = 'flex';
resumeMetadataRefreshItem.style.display = 'none';
skipMetadataRefreshItem.querySelector('span').textContent = translate(
'loras.bulkOperations.skipMetadataRefresh'
);
} else {
skipMetadataRefreshItem.style.display = 'flex';
resumeMetadataRefreshItem.style.display = 'flex';
skipMetadataRefreshItem.querySelector('span').textContent = translate(
'loras.bulkOperations.skipMetadataRefreshCount',
{ count: resumeCount }
);
resumeMetadataRefreshItem.querySelector('span').textContent = translate(
'loras.bulkOperations.resumeMetadataRefreshCount',
{ count: skipCount }
);
}
}
} }
updateSelectedCountHeader() { updateSelectedCountHeader() {
@@ -80,6 +114,20 @@ export class BulkContextMenu extends BaseContextMenu {
} }
} }
countSkipStatus(skipState) {
let count = 0;
for (const filePath of state.selectedModels) {
const card = document.querySelector(`.model-card[data-filepath="${filePath}"]`);
if (card) {
const isSkipped = card.dataset.skip_metadata_refresh === 'true';
if (isSkipped === skipState) {
count++;
}
}
}
return count;
}
showMenu(x, y, card) { showMenu(x, y, card) {
this.updateMenuItemsForModelType(); this.updateMenuItemsForModelType();
this.updateSelectedCountHeader(); this.updateSelectedCountHeader();
@@ -118,6 +166,12 @@ export class BulkContextMenu extends BaseContextMenu {
case 'auto-organize': case 'auto-organize':
bulkManager.autoOrganizeSelectedModels(); bulkManager.autoOrganizeSelectedModels();
break; break;
case 'skip-metadata-refresh':
bulkManager.setSkipMetadataRefresh(true);
break;
case 'resume-metadata-refresh':
bulkManager.setSkipMetadataRefresh(false);
break;
case 'delete-all': case 'delete-all':
bulkManager.showBulkDeleteModal(); bulkManager.showBulkDeleteModal();
break; break;

View File

@@ -433,9 +433,10 @@ export function createModelCard(model, modelType) {
card.dataset.usage_count = String(model.usage_count); card.dataset.usage_count = String(model.usage_count);
card.dataset.notes = model.notes || ''; card.dataset.notes = model.notes || '';
card.dataset.base_model = model.base_model || 'Unknown'; card.dataset.base_model = model.base_model || 'Unknown';
card.dataset.favorite = model.favorite ? 'true' : 'false'; card.dataset.favorite = model.favorite ? 'true' : 'false';
const hasUpdateAvailable = Boolean(model.update_available); const hasUpdateAvailable = Boolean(model.update_available);
card.dataset.update_available = hasUpdateAvailable ? 'true' : 'false'; card.dataset.update_available = hasUpdateAvailable ? 'true' : 'false';
card.dataset.skip_metadata_refresh = model.skip_metadata_refresh ? 'true' : 'false';
// To only show usage_count when sorting by usage. // To only show usage_count when sorting by usage.
const pageState = getCurrentPageState(); const pageState = getCurrentPageState();
@@ -482,6 +483,10 @@ export function createModelCard(model, modelType) {
card.classList.add('nsfw-content'); card.classList.add('nsfw-content');
} }
if (model.skip_metadata_refresh) {
card.classList.add('skip-refresh');
}
// Apply selection state if in bulk mode and this card is in the selected set (LoRA only) // Apply selection state if in bulk mode and this card is in the selected set (LoRA only)
if (modelType === MODEL_TYPES.LORA && state.bulkMode && state.selectedLoras.has(model.file_path)) { if (modelType === MODEL_TYPES.LORA && state.bulkMode && state.selectedLoras.has(model.file_path)) {
card.classList.add('selected'); card.classList.add('selected');
@@ -608,6 +613,11 @@ export function createModelCard(model, modelType) {
<i class="fas fa-arrow-up"></i> <i class="fas fa-arrow-up"></i>
</span> </span>
` : ''} ` : ''}
${model.skip_metadata_refresh ? `
<span class="model-skip-refresh-badge" title="${translate('modelCard.badges.skipRefresh', {}, 'Metadata refresh skipped')}">
<i class="fas fa-ban"></i>
</span>
` : ''}
</div> </div>
<div class="card-actions"> <div class="card-actions">
${actionIcons} ${actionIcons}

View File

@@ -40,7 +40,8 @@ export class BulkManager {
moveAll: true, moveAll: true,
autoOrganize: true, autoOrganize: true,
deleteAll: true, deleteAll: true,
setContentRating: true setContentRating: true,
skipMetadataRefresh: true
}, },
[MODEL_TYPES.EMBEDDING]: { [MODEL_TYPES.EMBEDDING]: {
addTags: true, addTags: true,
@@ -51,7 +52,8 @@ export class BulkManager {
moveAll: true, moveAll: true,
autoOrganize: true, autoOrganize: true,
deleteAll: true, deleteAll: true,
setContentRating: false setContentRating: false,
skipMetadataRefresh: true
}, },
[MODEL_TYPES.CHECKPOINT]: { [MODEL_TYPES.CHECKPOINT]: {
addTags: true, addTags: true,
@@ -62,7 +64,8 @@ export class BulkManager {
moveAll: false, moveAll: false,
autoOrganize: true, autoOrganize: true,
deleteAll: true, deleteAll: true,
setContentRating: true setContentRating: true,
skipMetadataRefresh: true
}, },
recipes: { recipes: {
addTags: false, addTags: false,
@@ -73,7 +76,8 @@ export class BulkManager {
moveAll: true, moveAll: true,
autoOrganize: false, autoOrganize: false,
deleteAll: true, deleteAll: true,
setContentRating: false setContentRating: false,
skipMetadataRefresh: false
} }
}; };
@@ -1195,6 +1199,59 @@ export class BulkManager {
return successCount > 0; return successCount > 0;
} }
async setSkipMetadataRefresh(value) {
if (state.selectedModels.size === 0) {
showToast('toast.models.noModelsSelected', {}, 'warning');
return;
}
const totalCount = state.selectedModels.size;
state.loadingManager.showSimpleLoading(
translate('toast.models.skipMetadataRefreshUpdating', { count: totalCount })
);
let cancelled = false;
state.loadingManager.showCancelButton(() => {
cancelled = true;
});
let successCount = 0;
let failureCount = 0;
try {
const apiClient = getModelApiClient();
for (const filePath of state.selectedModels) {
if (cancelled) {
showToast('toast.api.operationCancelled', {}, 'info');
break;
}
try {
await apiClient.saveModelMetadata(filePath, { skip_metadata_refresh: value });
successCount++;
} catch (error) {
failureCount++;
console.error(`Failed to set skip_metadata_refresh for ${filePath}:`, error);
}
}
} finally {
state.loadingManager?.hide?.();
}
if (successCount === totalCount) {
const toastKey = value
? 'toast.models.skipMetadataRefreshSet'
: 'toast.models.skipMetadataRefreshCleared';
showToast(toastKey, { count: successCount }, 'success');
} else if (successCount > 0) {
showToast('toast.models.skipMetadataRefreshPartial', {
success: successCount,
failed: failureCount
}, 'warning');
} else {
showToast('toast.models.skipMetadataRefreshFailed', {}, 'error');
}
}
/** /**
* Initialize bulk base model interface * Initialize bulk base model interface
*/ */

View File

@@ -80,6 +80,12 @@
<div class="context-menu-item" data-action="set-content-rating"> <div class="context-menu-item" data-action="set-content-rating">
<i class="fas fa-exclamation-triangle"></i> <span>{{ t('loras.bulkOperations.setContentRating') }}</span> <i class="fas fa-exclamation-triangle"></i> <span>{{ t('loras.bulkOperations.setContentRating') }}</span>
</div> </div>
<div class="context-menu-item" data-action="skip-metadata-refresh">
<i class="fas fa-ban"></i> <span>{{ t('loras.bulkOperations.skipMetadataRefresh') }}</span>
</div>
<div class="context-menu-item" data-action="resume-metadata-refresh">
<i class="fas fa-redo"></i> <span>{{ t('loras.bulkOperations.resumeMetadataRefresh') }}</span>
</div>
<div class="context-menu-separator"></div> <div class="context-menu-separator"></div>
<div class="context-menu-item" data-action="move-all"> <div class="context-menu-item" data-action="move-all">
<i class="fas fa-folder-open"></i> <span>{{ t('loras.bulkOperations.moveAll') }}</span> <i class="fas fa-folder-open"></i> <span>{{ t('loras.bulkOperations.moveAll') }}</span>