1 Commits
Author SHA1 Message Date
shin132002 6e1cf9b518 v1.5.0: Filtered nodes: add keyword output and filter_mode OFF 2026-09-29 01:24:30 +09:00
8 changed files with 245 additions and 47 deletions
+20
View File
@@ -2,6 +2,26 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [1.5.0] - 2026-09-29
### Added
#### `keyword` output (Filtered Random LoRA Loader / Filtered Random LoRA Loader (LBW))
- New **`keyword`** STRING output with the `keyword_filter` keywords the selected LoRA actually matched, joined with `_`
- AND: all keywords (`aaa bbb` → `aaa_bbb`)
- OR: only the keywords that matched (`aaa bbb` → `aaa` or `bbb`). If the LoRA contains several of them, or `num_loras` is 2 or more, every matched keyword is joined in input order
- Spaces inside a quoted phrase also become `_` (`"aaa bbb" ccc` with AND → `aaa_bbb_ccc`)
- Case is kept as typed; keywords matched in metadata (`search_in_metadata`) are included
- Empty when `filter_mode` is OFF, when `keyword_filter` is empty, when no LoRA matched, or when `num_loras` is 0
- Added as the **last** slot, so existing workflow connections stay valid. LoRA selection and all other outputs are unchanged (same seed, same result as 1.4.0)
- Random LoRA Loader (3 folders) has no keyword filter and is unchanged
#### `filter_mode`: new `OFF` option (Filtered Random LoRA Loader / Filtered Random LoRA Loader (LBW))
- `OFF` turns the keyword filter off while keeping the typed keywords, so it can be switched off for a while without deleting them. Picks the same LoRAs as an empty `keyword_filter`
- Added after `AND` / `OR`; the default stays `AND`. Saved workflows keep their `AND` / `OR` setting
---
## [1.4.0] - 2026-09-24 ## [1.4.0] - 2026-09-24
### Changed ### Changed
+36 -4
View File
@@ -88,9 +88,10 @@ Advanced node with LoRA Block Weight (LBW) support for precise effect control.
### Filtered Nodes (Filtered & LBW) ### Filtered Nodes (Filtered & LBW)
- **Keyword Filtering**: Space-separated keywords with phrase support - **Keyword Filtering**: Space-separated keywords with phrase support
- **AND/OR Modes**: Flexible filtering logic - **AND/OR/OFF Modes**: Flexible filtering logic (OFF: v1.5.0)
- **Metadata Search**: Search in filenames or embedded metadata - **Metadata Search**: Search in filenames or embedded metadata
- **Fast Caching**: Instant metadata access after first load - **Fast Caching**: Instant metadata access after first load
- **Keyword Output**: Outputs the keywords the selected LoRA actually matched (v1.5.0)
### LBW Node Only 🆕 ### LBW Node Only 🆕
@@ -513,10 +514,11 @@ All nodes provide the following outputs:
| `negative_text` | STRING | Full negative prompt text | | `negative_text` | STRING | Full negative prompt text |
| `preview` | IMAGE | Batch of preview images for selected LoRAs (v1.1.0) | | `preview` | IMAGE | Batch of preview images for selected LoRAs (v1.1.0) |
| `lora_text` | STRING | Positive prompt text **with** LoRA syntax (the former `positive_text` content, v1.4.0) | | `lora_text` | STRING | Positive prompt text **with** LoRA syntax (the former `positive_text` content, v1.4.0) |
| `keyword` | STRING | **Filtered nodes only.** The keywords the selected LoRA actually matched (v1.5.0). See [Keyword Output](#keyword-output-v150) |
> ⚠️ **Output changes in v1.4.0** > ⚠️ **Output changes in v1.4.0**
> >
> `positive_text` no longer contains `<lora:...>` tags; the previous content now comes from the new `lora_text` output (last slot). All other outputs keep their positions and types, so existing connections stay valid. > `positive_text` no longer contains `<lora:...>` tags; the previous content now comes from the new `lora_text` output (added at the end). All other outputs keep their positions and types, so existing connections stay valid.
> >
> - `positive_text` fed a text encoder: no rewiring needed. The tags are no longer read as text > - `positive_text` fed a text encoder: no rewiring needed. The tags are no longer read as text
> - `positive_text` was used to record the LoRA syntax (saved prompts, metadata): reconnect that to `lora_text` > - `positive_text` was used to record the LoRA syntax (saved prompts, metadata): reconnect that to `lora_text`
@@ -566,7 +568,7 @@ anime style, alice, blonde hair, 1girl, beautiful
### Key Features ### Key Features
- **Keyword Filtering**: Filter LoRAs by space-separated keywords - **Keyword Filtering**: Filter LoRAs by space-separated keywords
- **AND/OR Modes**: Flexible filtering logic - **AND/OR/OFF Modes**: Flexible filtering logic (OFF: v1.5.0)
- **Phrase Matching**: Use quotes for exact phrases - **Phrase Matching**: Use quotes for exact phrases
- **Metadata Search**: Search in filenames or embedded metadata - **Metadata Search**: Search in filenames or embedded metadata
- **Fast Caching**: Instant metadata access after first load - **Fast Caching**: Instant metadata access after first load
@@ -578,7 +580,7 @@ anime style, alice, blonde hair, 1girl, beautiful
|-----------|-------------|---------| |-----------|-------------|---------|
| `lora_folder_path` | LoRA folder path | (empty) | | `lora_folder_path` | LoRA folder path | (empty) |
| `keyword_filter` | Space-separated keywords or quoted phrases | (empty) | | `keyword_filter` | Space-separated keywords or quoted phrases | (empty) |
| `filter_mode` | `AND` / `OR` | `AND` | | `filter_mode` | `AND` / `OR` / `OFF` | `AND` |
| `search_in_metadata` | Search in JSON/embedded metadata | `false` | | `search_in_metadata` | Search in JSON/embedded metadata | `false` |
| `num_loras` | Number of LoRAs to select | `1` | | `num_loras` | Number of LoRAs to select | `1` |
| `model_strength` | MODEL strength (fixed or range) | `"1.0"` | | `model_strength` | MODEL strength (fixed or range) | `"1.0"` |
@@ -601,6 +603,14 @@ keyword_filter: "anime realistic"
→ Matches files containing "anime" OR "realistic" → Matches files containing "anime" OR "realistic"
``` ```
**OFF mode (v1.5.0):**
```
filter_mode: OFF
keyword_filter: "anime realistic"
→ No filtering; picks from every LoRA in the folder
→ The keywords you typed stay, so you can switch the filter off for a while without deleting them
```
**Phrase matching:** **Phrase matching:**
``` ```
keyword_filter: '"anime style" detailed' keyword_filter: '"anime style" detailed'
@@ -613,6 +623,28 @@ keyword_filter: '"anime style" "detailed eyes" red'
→ Must contain "anime style" AND "detailed eyes" AND "red" → Must contain "anime style" AND "detailed eyes" AND "red"
``` ```
### Keyword Output (v1.5.0)
Outputs the keywords the selected LoRA actually matched, joined with `_`. The LBW node works the same way.
| `keyword_filter` | `filter_mode` | Selected LoRA (filename) | `keyword` output |
|---|---|---|---|
| `aaa bbb` | AND | `aaa_bbb_xxx` | `aaa_bbb` |
| `aaa bbb` | OR | `aaa_xxx` | `aaa` |
| `aaa bbb` | OR | `bbb_xxx` | `bbb` |
| `"aaa bbb"` | AND / OR | `aaa bbb xxx` | `aaa_bbb` |
| `"aaa bbb" ccc` | AND | `aaa bbb ccc` | `aaa_bbb_ccc` |
| (empty) | AND / OR | any | (empty) |
| (anything) | OFF | any | (empty) |
- Spaces inside a phrase also become `_`
- In OR mode, if the selected LoRA contains several of the keywords, or `num_loras` is 2 or more, every matched keyword is joined in input order (e.g. `aaa_bbb`)
- Keywords are output with the case you typed (matching itself ignores case)
- With `search_in_metadata` ON, keywords matched in the metadata are included too
- Empty when `filter_mode` is OFF, when `keyword_filter` is empty, when no LoRA matched, or when `num_loras` is 0
`keyword` was added as the last (ninth) output, so existing workflow connections keep working.
### Metadata Search ### Metadata Search
**Filename search (default):** **Filename search (default):**
+7 -2
View File
@@ -16,7 +16,8 @@ Filtered Random LoRA Loader (LBW) is the Filtered Random LoRA Loader with LoRA B
### **Core Features (shared with Filtered Random LoRA Loader)** ### **Core Features (shared with Filtered Random LoRA Loader)**
- ✅ Random LoRA selection from a single folder - ✅ Random LoRA selection from a single folder
- ✅ Keyword filtering (AND/OR modes) - ✅ Keyword filtering (AND/OR/OFF modes)
- ✅ Output of the matched keywords (`keyword`, v1.5.0)
- ✅ Metadata search (filename or embedded metadata) - ✅ Metadata search (filename or embedded metadata)
- ✅ Preview images (static/animated/video support) - ✅ Preview images (static/animated/video support)
- ✅ Trigger word extraction - ✅ Trigger word extraction
@@ -285,7 +286,7 @@ Detected: SDXL (requires 20)
#### **Keyword Filter** #### **Keyword Filter**
- `keyword_filter`: Space-separated keywords (e.g., `style anime` or `"anime style" red`) - `keyword_filter`: Space-separated keywords (e.g., `style anime` or `"anime style" red`)
- `filter_mode`: AND / OR - `filter_mode`: AND / OR / OFF (OFF turns the filter off but keeps the keywords, v1.5.0)
- `search_in_metadata`: Search in JSON/embedded metadata (slower) - `search_in_metadata`: Search in JSON/embedded metadata (slower)
#### **LBW Settings** #### **LBW Settings**
@@ -316,6 +317,10 @@ When LBW is applied, the `lora_text` output (`positive_text` before v1.4.0) incl
**Note:** The LBW is applied internally to the MODEL. The syntax in `lora_text` is for reference/re-use with other nodes. **Note:** The LBW is applied internally to the MODEL. The syntax in `lora_text` is for reference/re-use with other nodes.
### **Keyword Output (v1.5.0)**
Outputs the keywords the selected LoRA actually matched, joined with `_` (last output). AND mode gives all keywords (`aaa bbb` → `aaa_bbb`); OR mode gives the one that matched (`aaa` or `bbb`). Spaces inside a phrase also become `_` (`"aaa bbb" ccc` → `aaa_bbb_ccc`). Empty when `filter_mode` is OFF. See [Keyword Output in README.md](README.md#keyword-output-v150) for details.
--- ---
## Examples ## Examples
+7 -2
View File
@@ -16,7 +16,8 @@ Filtered Random LoRA Loader (LBW) は、Filtered Random LoRA LoaderにLoRAブロ
### **基本機能(Filtered Random LoRA Loaderと共通)** ### **基本機能(Filtered Random LoRA Loaderと共通)**
- ✅ 1つのフォルダからLoRAをランダム選択 - ✅ 1つのフォルダからLoRAをランダム選択
- ✅ キーワードフィルタ(AND/ORモード) - ✅ キーワードフィルタ(AND/OR/OFFモード)
- ✅ マッチしたキーワードの出力(`keyword`、v1.5.0)
- ✅ メタデータ検索(ファイル名または埋め込みメタデータ) - ✅ メタデータ検索(ファイル名または埋め込みメタデータ)
- ✅ プレビュー画像(静止画・アニメーション画像・動画に対応) - ✅ プレビュー画像(静止画・アニメーション画像・動画に対応)
- ✅ トリガーワードの取得 - ✅ トリガーワードの取得
@@ -285,7 +286,7 @@ SD1.5検出 → 17要素のウェイトを使用
#### **キーワードフィルタ** #### **キーワードフィルタ**
- `keyword_filter`: スペース区切りのキーワード(例: `style anime` や `"anime style" red`) - `keyword_filter`: スペース区切りのキーワード(例: `style anime` や `"anime style" red`)
- `filter_mode`: AND / OR - `filter_mode`: AND / OR / OFF(OFFはキーワードを残したままフィルタを無効にする、v1.5.0)
- `search_in_metadata`: JSONや埋め込みメタデータも検索対象にする(遅くなります) - `search_in_metadata`: JSONや埋め込みメタデータも検索対象にする(遅くなります)
#### **LBW設定** #### **LBW設定**
@@ -316,6 +317,10 @@ LBWを適用した場合、`lora_text`出力(v1.4.0より前は`positive_text`
**注:** LBWはノード内部でMODELに適用済みです。`lora_text`の構文は、記録や他のノードでの再利用のためのものです。 **注:** LBWはノード内部でMODELに適用済みです。`lora_text`の構文は、記録や他のノードでの再利用のためのものです。
### **keyword出力(v1.5.0)**
選ばれたLoRAが実際にマッチしたキーワードを`_`で連結して出力します(出力の末尾)。ANDモードは全キーワード(`aaa bbb` → `aaa_bbb`)、ORモードはマッチしたもの(`aaa`または`bbb`)です。フレーズ内のスペースも`_`になります(`"aaa bbb" ccc` → `aaa_bbb_ccc`)。`filter_mode`がOFFのときは空です。詳細は[README_ja.md の「keyword出力」](README_ja.md#keyword出力v150)を参照してください。
--- ---
## 使用例 ## 使用例
+36 -4
View File
@@ -88,9 +88,10 @@ LoRAブロックウェイト(LBW)対応の高度なノード。精密な効
### Filteredノード(Filtered & LBW) ### Filteredノード(Filtered & LBW)
- **キーワードフィルタリング**: スペース区切りキーワード、フレーズ対応 - **キーワードフィルタリング**: スペース区切りキーワード、フレーズ対応
- **AND/ORモード**: 柔軟なフィルタリングロジック - **AND/OR/OFFモード**: 柔軟なフィルタリングロジック(OFFはv1.5.0)
- **メタデータ検索**: ファイル名または埋め込みメタデータから検索 - **メタデータ検索**: ファイル名または埋め込みメタデータから検索
- **高速キャッシング**: 初回読み込み後は即座にアクセス - **高速キャッシング**: 初回読み込み後は即座にアクセス
- **keyword出力**: 選ばれたLoRAが実際にマッチしたキーワードを出力(v1.5.0)
### LBWノード限定 🆕 ### LBWノード限定 🆕
@@ -513,10 +514,11 @@ JSONメタデータから作例プロンプトを取得。
| `negative_text` | STRING | 完全ネガティブプロンプトテキスト | | `negative_text` | STRING | 完全ネガティブプロンプトテキスト |
| `preview` | IMAGE | 選択されたLoRAのプレビュー画像バッチ(v1.1.0) | | `preview` | IMAGE | 選択されたLoRAのプレビュー画像バッチ(v1.1.0) |
| `lora_text` | STRING | LoRA構文を**含む**ポジティブプロンプトテキスト(旧`positive_text`の内容、v1.4.0) | | `lora_text` | STRING | LoRA構文を**含む**ポジティブプロンプトテキスト(旧`positive_text`の内容、v1.4.0) |
| `keyword` | STRING | **Filteredノードのみ**。選ばれたLoRAが実際にマッチしたキーワード(v1.5.0)。[keyword出力](#keyword出力v150)を参照 |
> ⚠️ **v1.4.0での出力の変更** > ⚠️ **v1.4.0での出力の変更**
> >
> `positive_text`は`<lora:...>`を含まなくなりました。以前の内容は新しい出力`lora_text`(末尾)から出ます。他の出力は位置も型も変わっていないので、既存の接続はそのまま使えます。 > `positive_text`は`<lora:...>`を含まなくなりました。以前の内容は新しい出力`lora_text`(末尾に追加)から出ます。他の出力は位置も型も変わっていないので、既存の接続はそのまま使えます。
> >
> - `positive_text`をテキストエンコーダーに繋いでいた場合:つなぎ直しは不要です。`<lora:...>`が文字として読まれなくなります > - `positive_text`をテキストエンコーダーに繋いでいた場合:つなぎ直しは不要です。`<lora:...>`が文字として読まれなくなります
> - `positive_text`をLoRA構文の記録(プロンプトの保存やメタデータ)に使っていた場合:`lora_text`につなぎ直してください > - `positive_text`をLoRA構文の記録(プロンプトの保存やメタデータ)に使っていた場合:`lora_text`につなぎ直してください
@@ -566,7 +568,7 @@ anime style, alice, blonde hair, 1girl, beautiful
### 主な機能 ### 主な機能
- **キーワードフィルタリング**: スペース区切りキーワードでLoRAを絞り込み - **キーワードフィルタリング**: スペース区切りキーワードでLoRAを絞り込み
- **AND/ORモード**: 柔軟なフィルタリングロジック - **AND/OR/OFFモード**: 柔軟なフィルタリングロジック(OFFはv1.5.0)
- **フレーズマッチング**: 引用符で完全一致フレーズ - **フレーズマッチング**: 引用符で完全一致フレーズ
- **メタデータ検索**: ファイル名または埋め込みメタデータから検索 - **メタデータ検索**: ファイル名または埋め込みメタデータから検索
- **高速キャッシング**: 初回検索後は即座にアクセス - **高速キャッシング**: 初回検索後は即座にアクセス
@@ -578,7 +580,7 @@ anime style, alice, blonde hair, 1girl, beautiful
|-----------|------|-----------| |-----------|------|-----------|
| `lora_folder_path` | LoRAフォルダパス | (空) | | `lora_folder_path` | LoRAフォルダパス | (空) |
| `keyword_filter` | スペース区切りキーワードまたは引用符フレーズ | (空) | | `keyword_filter` | スペース区切りキーワードまたは引用符フレーズ | (空) |
| `filter_mode` | `AND` / `OR` | `AND` | | `filter_mode` | `AND` / `OR` / `OFF` | `AND` |
| `search_in_metadata` | JSON/埋め込みメタデータから検索 | `false` | | `search_in_metadata` | JSON/埋め込みメタデータから検索 | `false` |
| `num_loras` | 選択するLoRA数 | `1` | | `num_loras` | 選択するLoRA数 | `1` |
| `model_strength` | MODEL強度(固定または範囲) | `"1.0"` | | `model_strength` | MODEL強度(固定または範囲) | `"1.0"` |
@@ -601,6 +603,14 @@ keyword_filter: "anime realistic"
→ "anime"または"realistic"を含むファイルにマッチ → "anime"または"realistic"を含むファイルにマッチ
``` ```
**OFFモード(v1.5.0):**
```
filter_mode: OFF
keyword_filter: "anime realistic"
→ フィルタを使わず、フォルダ内の全LoRAから選択
→ 入力したキーワードは残るので、消さずに一時的にフィルタを切れる
```
**フレーズマッチング:** **フレーズマッチング:**
``` ```
keyword_filter: '"anime style" detailed' keyword_filter: '"anime style" detailed'
@@ -613,6 +623,28 @@ keyword_filter: '"anime style" "detailed eyes" red'
→ "anime style"と"detailed eyes"と"red"を含む必要 → "anime style"と"detailed eyes"と"red"を含む必要
``` ```
### keyword出力(v1.5.0)
選ばれたLoRAが実際にマッチしたキーワードを、`_`で連結したテキストで出力します。LBWノードも同じです。
| `keyword_filter` | `filter_mode` | 選ばれたLoRA(ファイル名) | `keyword`出力 |
|---|---|---|---|
| `aaa bbb` | AND | `aaa_bbb_xxx` | `aaa_bbb` |
| `aaa bbb` | OR | `aaa_xxx` | `aaa` |
| `aaa bbb` | OR | `bbb_xxx` | `bbb` |
| `"aaa bbb"` | AND / OR | `aaa bbb xxx` | `aaa_bbb` |
| `"aaa bbb" ccc` | AND | `aaa bbb ccc` | `aaa_bbb_ccc` |
| (空欄) | AND / OR | どれでも | (空) |
| (何でも) | OFF | どれでも | (空) |
- フレーズ内のスペースも`_`になります
- ORモードで、選ばれたLoRAが複数のキーワードを含む場合や、`num_loras`が2以上の場合は、マッチしたキーワードをすべて入力順に連結します(例:`aaa_bbb`)
- 大文字小文字は入力どおりに出力します(マッチの判定自体は区別しません)
- `search_in_metadata`がONの場合は、メタデータ側でマッチしたキーワードも含まれます
- `filter_mode`がOFFのとき、`keyword_filter`が空欄のとき、条件に合うLoRAがなかったとき、`num_loras`が0のときは空文字です
`keyword`は出力の末尾(9番目)に追加したので、既存のワークフローの接続はそのまま使えます。
### メタデータ検索 ### メタデータ検索
**ファイル名検索(デフォルト):** **ファイル名検索(デフォルト):**
+69 -17
View File
@@ -79,7 +79,9 @@ class FilteredRandomLoRALoader:
"multiline": False, "multiline": False,
"placeholder": "Keywords (space-separated, e.g., 'style anime' or \"anime style\" red)" "placeholder": "Keywords (space-separated, e.g., 'style anime' or \"anime style\" red)"
}), }),
"filter_mode": (["AND", "OR"], { # OFF(v1.5.0): キーワードを残したままフィルタだけ無効にする。末尾に追加。
# 保存済みワークフローは値("AND"/"OR")で保存されているので影響なし
"filter_mode": (["AND", "OR", "OFF"], {
"default": "AND" "default": "AND"
}), }),
"search_in_metadata": ("BOOLEAN", { "search_in_metadata": ("BOOLEAN", {
@@ -120,13 +122,16 @@ class FilteredRandomLoRALoader:
} }
} }
RETURN_TYPES = ("MODEL", "CLIP", "STRING", "STRING", "CONDITIONING", "CONDITIONING", "IMAGE", "STRING") RETURN_TYPES = ("MODEL", "CLIP", "STRING", "STRING", "CONDITIONING", "CONDITIONING", "IMAGE", "STRING", "STRING")
# 0〜6番はv1.4.0以前と位置・型とも同じ(保存済みワークフローは出力を番号で接続するため)。 # 0〜6番はv1.4.0以前と位置・型とも同じ(保存済みワークフローは出力を番号で接続するため)。
# v1.4.0での変更: # v1.4.0での変更:
# positive_text (2番) … <lora:...> を含まない。positive (CONDITIONING) にエンコード # positive_text (2番) … <lora:...> を含まない。positive (CONDITIONING) にエンコード
# される文字列そのもの # される文字列そのもの
# lora_text (7番) … 以前の positive_text の内容(<lora:...> 付き)。記録用 # lora_text (7番) … 以前の positive_text の内容(<lora:...> 付き)。記録用
RETURN_NAMES = ("MODEL", "CLIP", "positive_text", "negative_text", "positive", "negative", "preview", "lora_text") # v1.5.0での追加:
# keyword (8番) … 選ばれたLoRAが実際にマッチしたキーワードを "_" で連結したもの
# (_build_keyword_text 参照)。新しいピンは常に末尾に足す
RETURN_NAMES = ("MODEL", "CLIP", "positive_text", "negative_text", "positive", "negative", "preview", "lora_text", "keyword")
FUNCTION = "load_loras" FUNCTION = "load_loras"
CATEGORY = "loaders" CATEGORY = "loaders"
@@ -269,8 +274,14 @@ class FilteredRandomLoRALoader:
# プレビュー画像バッチ生成 # プレビュー画像バッチ生成
preview_batch = self._generate_preview_batch(preview_images) preview_batch = self._generate_preview_batch(preview_images)
# keyword 出力(選ばれたLoRAが実際にマッチしたキーワード)
keyword_text = self._build_keyword_text(selected_loras, keyword_filter, filter_mode, search_in_metadata)
if keyword_text:
print(f"[FilteredRandomLoRALoader] Matched keyword: {keyword_text}")
return self._generate_outputs(model, clip, final_positive, final_negative, return self._generate_outputs(model, clip, final_positive, final_negative,
token_normalization, weight_interpretation, preview_batch) token_normalization, weight_interpretation, preview_batch,
keyword_text)
def _find_lora_files(self, folder_path, include_subfolders): def _find_lora_files(self, folder_path, include_subfolders):
"""フォルダ内のLoRAファイルを検索""" """フォルダ内のLoRAファイルを検索"""
@@ -312,31 +323,69 @@ class FilteredRandomLoRALoader:
# ソートすることでシード指定が環境をまたいで再現するようになる。 # ソートすることでシード指定が環境をまたいで再現するようになる。
return sorted(lora_files) return sorted(lora_files)
def _parse_keywords(self, keyword_filter): def _split_keywords(self, keyword_filter):
""" """
キーワードをパース(スペース区切り、"..."でフレーズ対応) キーワードを入力どおりの大文字小文字で分割(スペース区切り、"..."でフレーズ対応)
例: 例:
"anime style" → ["anime", "style"] anime style → ["anime", "style"]
"anime style" red → ["anime style", "red"] "anime style" red → ["anime style", "red"]
"anime style" "vibrant colors" → ["anime style", "vibrant colors"] "anime style" "vibrant colors" → ["anime style", "vibrant colors"]
""" """
if not keyword_filter.strip(): if not keyword_filter.strip():
return [] return []
import re
# ダブルクォート内のフレーズを抽出 # ダブルクォート内のフレーズを抽出
pattern = r'"([^"]+)"|(\S+)' pattern = r'"([^"]+)"|(\S+)'
matches = re.findall(pattern, keyword_filter) matches = re.findall(pattern, keyword_filter)
# (quoted, unquoted) のタプルから値を取得 # (quoted, unquoted) のタプルから値を取得
keywords = [m[0] if m[0] else m[1] for m in matches] return [m[0] if m[0] else m[1] for m in matches if m[0] or m[1]]
# 小文字化 def _parse_keywords(self, keyword_filter):
keywords = [kw.lower() for kw in keywords if kw] """フィルタ用のキーワード(大文字小文字を区別しないので小文字化)"""
return [kw.lower() for kw in self._split_keywords(keyword_filter)]
return keywords def _build_keyword_text(self, selected_loras, keyword_filter, filter_mode, search_in_metadata):
"""
keyword 出力用の文字列。選ばれたLoRAが実際にマッチしたキーワードを返す
キーワードは入力順・重複なしで "_" 連結。フレーズ内のスペースも "_" にする
("aaa bbb" → aaa_bbb)。大文字小文字は入力のまま。マッチ判定の対象は
_filter_lora_files と同じ(ファイル名、search_in_metadata がONならメタデータも)。
AND: 選ばれたLoRAは全キーワードにマッチしているので全部出る
(aaa bbb → aaa_bbb、"aaa bbb" ccc → aaa_bbb_ccc)
OR : ヒットしたものだけ出る(aaa または bbb)。1つのLoRAが両方を含めば
aaa_bbb、num_loras が2以上なら選ばれた全LoRAのヒットをまとめる
filter_mode が OFF、keyword_filter が空、LoRAが1つも選ばれなかった場合は空文字。
"""
if filter_mode == "OFF":
return ""
keywords = self._split_keywords(keyword_filter)
if not keywords or not selected_loras:
return ""
targets = []
for lora_path in dict.fromkeys(selected_loras):
target = os.path.splitext(os.path.basename(lora_path))[0].lower()
if search_in_metadata:
metadata_keywords = self._get_metadata_keywords(lora_path)
if metadata_keywords:
target = f"{target} {metadata_keywords}"
targets.append(target)
hits, seen = [], set()
for kw in keywords:
key = kw.lower()
if key in seen:
continue
seen.add(key)
if any(key in t for t in targets):
label = re.sub(r"\s+", "_", kw.strip())
if label:
hits.append(label)
return "_".join(hits)
def _unique_by_filename(self, lora_files): def _unique_by_filename(self, lora_files):
""" """
@@ -371,7 +420,7 @@ class FilteredRandomLoRALoader:
def _filter_lora_files(self, lora_files, keyword_filter, filter_mode="OR", search_in_metadata=False): def _filter_lora_files(self, lora_files, keyword_filter, filter_mode="OR", search_in_metadata=False):
"""キーワードでLoRAファイルをフィルタリング""" """キーワードでLoRAファイルをフィルタリング"""
if not keyword_filter.strip(): if filter_mode == "OFF" or not keyword_filter.strip():
return lora_files # フィルタなし = 全ファイル return lora_files # フィルタなし = 全ファイル
# キーワードをパース(スペース区切り、"..."でフレーズ対応) # キーワードをパース(スペース区切り、"..."でフレーズ対応)
@@ -1005,7 +1054,8 @@ class FilteredRandomLoRALoader:
return preview_batch return preview_batch
def _generate_outputs(self, model, clip, final_positive, final_negative, def _generate_outputs(self, model, clip, final_positive, final_negative,
token_normalization, weight_interpretation, preview_batch): token_normalization, weight_interpretation, preview_batch,
keyword_text=""):
"""CONDITIONING生成と出力""" """CONDITIONING生成と出力"""
# LoRA構文を削除してクリーンなプロンプトにする。clean_positive は positive_text # LoRA構文を削除してクリーンなプロンプトにする。clean_positive は positive_text
# 出力にもなるので、エラー時の戻り値でも使えるよう try の外で作る # 出力にもなるので、エラー時の戻り値でも使えるよう try の外で作る
@@ -1027,7 +1077,8 @@ class FilteredRandomLoRALoader:
# positive_text = エンコードした文字列そのもの / lora_text = <lora:...> 付き(旧 positive_text) # positive_text = エンコードした文字列そのもの / lora_text = <lora:...> 付き(旧 positive_text)
return (model, clip, clean_positive, final_negative, return (model, clip, clean_positive, final_negative,
positive_conditioning, negative_conditioning, preview_batch, final_positive) positive_conditioning, negative_conditioning, preview_batch, final_positive,
keyword_text)
except Exception as e: except Exception as e:
print(f"[FilteredRandomLoRALoader] Error generating outputs: {e}") print(f"[FilteredRandomLoRALoader] Error generating outputs: {e}")
# エラー時は黒画像 # エラー時は黒画像
@@ -1036,7 +1087,8 @@ class FilteredRandomLoRALoader:
black_image = torch.zeros((1, 1240, 1240, 3), dtype=torch.float32) black_image = torch.zeros((1, 1240, 1240, 3), dtype=torch.float32)
except: except:
black_image = None black_image = None
return (model, clip, clean_positive, final_negative, None, None, black_image, final_positive) return (model, clip, clean_positive, final_negative, None, None, black_image, final_positive,
keyword_text)
NODE_CLASS_MAPPINGS = { NODE_CLASS_MAPPINGS = {
+69 -17
View File
@@ -98,7 +98,9 @@ class FilteredRandomLoRALoaderLBW:
"multiline": False, "multiline": False,
"placeholder": "Keywords (space-separated, e.g., 'style anime' or \"anime style\" red)" "placeholder": "Keywords (space-separated, e.g., 'style anime' or \"anime style\" red)"
}), }),
"filter_mode": (["AND", "OR"], { # OFF(v1.5.0): キーワードを残したままフィルタだけ無効にする。末尾に追加。
# 保存済みワークフローは値("AND"/"OR")で保存されているので影響なし
"filter_mode": (["AND", "OR", "OFF"], {
"default": "AND" "default": "AND"
}), }),
"search_in_metadata": ("BOOLEAN", { "search_in_metadata": ("BOOLEAN", {
@@ -157,13 +159,16 @@ class FilteredRandomLoRALoaderLBW:
} }
} }
RETURN_TYPES = ("MODEL", "CLIP", "STRING", "STRING", "CONDITIONING", "CONDITIONING", "IMAGE", "STRING") RETURN_TYPES = ("MODEL", "CLIP", "STRING", "STRING", "CONDITIONING", "CONDITIONING", "IMAGE", "STRING", "STRING")
# 0〜6番はv1.4.0以前と位置・型とも同じ(保存済みワークフローは出力を番号で接続するため)。 # 0〜6番はv1.4.0以前と位置・型とも同じ(保存済みワークフローは出力を番号で接続するため)。
# v1.4.0での変更: # v1.4.0での変更:
# positive_text (2番) … <lora:...> を含まない。positive (CONDITIONING) にエンコード # positive_text (2番) … <lora:...> を含まない。positive (CONDITIONING) にエンコード
# される文字列そのもの # される文字列そのもの
# lora_text (7番) … 以前の positive_text の内容(<lora:...> 付き)。記録用 # lora_text (7番) … 以前の positive_text の内容(<lora:...> 付き)。記録用
RETURN_NAMES = ("MODEL", "CLIP", "positive_text", "negative_text", "positive", "negative", "preview", "lora_text") # v1.5.0での追加:
# keyword (8番) … 選ばれたLoRAが実際にマッチしたキーワードを "_" で連結したもの
# (_build_keyword_text 参照)。新しいピンは常に末尾に足す
RETURN_NAMES = ("MODEL", "CLIP", "positive_text", "negative_text", "positive", "negative", "preview", "lora_text", "keyword")
FUNCTION = "load_loras" FUNCTION = "load_loras"
CATEGORY = "loaders" CATEGORY = "loaders"
@@ -315,8 +320,14 @@ class FilteredRandomLoRALoaderLBW:
# プレビュー画像バッチ生成 # プレビュー画像バッチ生成
preview_batch = self._generate_preview_batch(preview_images) preview_batch = self._generate_preview_batch(preview_images)
# keyword 出力(選ばれたLoRAが実際にマッチしたキーワード)
keyword_text = self._build_keyword_text(selected_loras, keyword_filter, filter_mode, search_in_metadata)
if keyword_text:
print(f"[FilteredRandomLoRALoaderLBW] Matched keyword: {keyword_text}")
return self._generate_outputs(model, clip, final_positive, final_negative, return self._generate_outputs(model, clip, final_positive, final_negative,
token_normalization, weight_interpretation, preview_batch) token_normalization, weight_interpretation, preview_batch,
keyword_text)
def _find_lora_files(self, folder_path, include_subfolders): def _find_lora_files(self, folder_path, include_subfolders):
"""フォルダ内のLoRAファイルを検索""" """フォルダ内のLoRAファイルを検索"""
@@ -358,31 +369,69 @@ class FilteredRandomLoRALoaderLBW:
# ソートすることでシード指定が環境をまたいで再現するようになる。 # ソートすることでシード指定が環境をまたいで再現するようになる。
return sorted(lora_files) return sorted(lora_files)
def _parse_keywords(self, keyword_filter): def _split_keywords(self, keyword_filter):
""" """
キーワードをパース(スペース区切り、"..."でフレーズ対応) キーワードを入力どおりの大文字小文字で分割(スペース区切り、"..."でフレーズ対応)
例: 例:
"anime style" → ["anime", "style"] anime style → ["anime", "style"]
"anime style" red → ["anime style", "red"] "anime style" red → ["anime style", "red"]
"anime style" "vibrant colors" → ["anime style", "vibrant colors"] "anime style" "vibrant colors" → ["anime style", "vibrant colors"]
""" """
if not keyword_filter.strip(): if not keyword_filter.strip():
return [] return []
import re
# ダブルクォート内のフレーズを抽出 # ダブルクォート内のフレーズを抽出
pattern = r'"([^"]+)"|(\S+)' pattern = r'"([^"]+)"|(\S+)'
matches = re.findall(pattern, keyword_filter) matches = re.findall(pattern, keyword_filter)
# (quoted, unquoted) のタプルから値を取得 # (quoted, unquoted) のタプルから値を取得
keywords = [m[0] if m[0] else m[1] for m in matches] return [m[0] if m[0] else m[1] for m in matches if m[0] or m[1]]
# 小文字化 def _parse_keywords(self, keyword_filter):
keywords = [kw.lower() for kw in keywords if kw] """フィルタ用のキーワード(大文字小文字を区別しないので小文字化)"""
return [kw.lower() for kw in self._split_keywords(keyword_filter)]
return keywords def _build_keyword_text(self, selected_loras, keyword_filter, filter_mode, search_in_metadata):
"""
keyword 出力用の文字列。選ばれたLoRAが実際にマッチしたキーワードを返す
キーワードは入力順・重複なしで "_" 連結。フレーズ内のスペースも "_" にする
("aaa bbb" → aaa_bbb)。大文字小文字は入力のまま。マッチ判定の対象は
_filter_lora_files と同じ(ファイル名、search_in_metadata がONならメタデータも)。
AND: 選ばれたLoRAは全キーワードにマッチしているので全部出る
(aaa bbb → aaa_bbb、"aaa bbb" ccc → aaa_bbb_ccc)
OR : ヒットしたものだけ出る(aaa または bbb)。1つのLoRAが両方を含めば
aaa_bbb、num_loras が2以上なら選ばれた全LoRAのヒットをまとめる
filter_mode が OFF、keyword_filter が空、LoRAが1つも選ばれなかった場合は空文字。
"""
if filter_mode == "OFF":
return ""
keywords = self._split_keywords(keyword_filter)
if not keywords or not selected_loras:
return ""
targets = []
for lora_path in dict.fromkeys(selected_loras):
target = os.path.splitext(os.path.basename(lora_path))[0].lower()
if search_in_metadata:
metadata_keywords = self._get_metadata_keywords(lora_path)
if metadata_keywords:
target = f"{target} {metadata_keywords}"
targets.append(target)
hits, seen = [], set()
for kw in keywords:
key = kw.lower()
if key in seen:
continue
seen.add(key)
if any(key in t for t in targets):
label = re.sub(r"\s+", "_", kw.strip())
if label:
hits.append(label)
return "_".join(hits)
def _unique_by_filename(self, lora_files): def _unique_by_filename(self, lora_files):
""" """
@@ -417,7 +466,7 @@ class FilteredRandomLoRALoaderLBW:
def _filter_lora_files(self, lora_files, keyword_filter, filter_mode="OR", search_in_metadata=False): def _filter_lora_files(self, lora_files, keyword_filter, filter_mode="OR", search_in_metadata=False):
"""キーワードでLoRAファイルをフィルタリング""" """キーワードでLoRAファイルをフィルタリング"""
if not keyword_filter.strip(): if filter_mode == "OFF" or not keyword_filter.strip():
return lora_files # フィルタなし = 全ファイル return lora_files # フィルタなし = 全ファイル
# キーワードをパース(スペース区切り、"..."でフレーズ対応) # キーワードをパース(スペース区切り、"..."でフレーズ対応)
@@ -1292,7 +1341,8 @@ class FilteredRandomLoRALoaderLBW:
return preview_batch return preview_batch
def _generate_outputs(self, model, clip, final_positive, final_negative, def _generate_outputs(self, model, clip, final_positive, final_negative,
token_normalization, weight_interpretation, preview_batch): token_normalization, weight_interpretation, preview_batch,
keyword_text=""):
"""CONDITIONING生成と出力""" """CONDITIONING生成と出力"""
# LoRA構文を削除してクリーンなプロンプトにする。clean_positive は positive_text # LoRA構文を削除してクリーンなプロンプトにする。clean_positive は positive_text
# 出力にもなるので、エラー時の戻り値でも使えるよう try の外で作る # 出力にもなるので、エラー時の戻り値でも使えるよう try の外で作る
@@ -1314,7 +1364,8 @@ class FilteredRandomLoRALoaderLBW:
# positive_text = エンコードした文字列そのもの / lora_text = <lora:...> 付き(旧 positive_text) # positive_text = エンコードした文字列そのもの / lora_text = <lora:...> 付き(旧 positive_text)
return (model, clip, clean_positive, final_negative, return (model, clip, clean_positive, final_negative,
positive_conditioning, negative_conditioning, preview_batch, final_positive) positive_conditioning, negative_conditioning, preview_batch, final_positive,
keyword_text)
except Exception as e: except Exception as e:
print(f"[FilteredRandomLoRALoaderLBW] Error generating outputs: {e}") print(f"[FilteredRandomLoRALoaderLBW] Error generating outputs: {e}")
# エラー時は黒画像 # エラー時は黒画像
@@ -1323,4 +1374,5 @@ class FilteredRandomLoRALoaderLBW:
black_image = torch.zeros((1, 1240, 1240, 3), dtype=torch.float32) black_image = torch.zeros((1, 1240, 1240, 3), dtype=torch.float32)
except: except:
black_image = None black_image = None
return (model, clip, clean_positive, final_negative, None, None, black_image, final_positive) return (model, clip, clean_positive, final_negative, None, None, black_image, final_positive,
keyword_text)
+1 -1
View File
@@ -1,7 +1,7 @@
[project] [project]
name = "randomloraloader" name = "randomloraloader"
description = "Randomly select and apply LoRAs with keyword filtering, LoRA Block Weight support, preview images, and multiple folder support for ComfyUI" description = "Randomly select and apply LoRAs with keyword filtering, LoRA Block Weight support, preview images, and multiple folder support for ComfyUI"
version = "1.4.0" version = "1.5.0"
license = {file = "LICENSE"} license = {file = "LICENSE"}
classifiers = [ classifiers = [