新規作成
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
# ComfyUI-PromptRandomChoice
|
||||
|
||||
実行のたびに「プロンプトガチャ」を爆速で回す、超軽量なランダム選択ノードじゃ!
|
||||
|
||||
「背景や天気を適当に変えて、一晩中回しておきたいのう……」
|
||||
そんなおぬしの願いを、実行時間 0.010s(注:作者環境で速いとき)という瞬速の魔法で叶えて進ぜよう!
|
||||
|
||||
背景・時間帯・天気などを別々にランダム化したい時に便利じゃ。
|
||||
フロー内に複数個置いても、それぞれ独立して動くぞ。
|
||||
|
||||
|
||||
## 特徴
|
||||
|
||||
- **実行ごとにランダム選択**
|
||||
- キューを雑に100件積んでも、毎回候補を選び直すぞ。
|
||||
|
||||
- **区切りは `|` または実際の改行**
|
||||
- `|` と改行の両方を区切りとして扱うので、混在していても動くぞ。
|
||||
|
||||
- **候補の前後の `,` と空白を自動で整理**
|
||||
- `town,`
|
||||
- `coffee shop,`
|
||||
- `castle, fortress,`
|
||||
のような書き方でも大丈夫じゃ。
|
||||
|
||||
- **プロンプト結合向けの返り値**
|
||||
- 選ばれた候補の前後に `,` を付けて返すぞ。
|
||||
- 例: `coffee shop` → `,coffee shop,`
|
||||
|
||||
- **現在の選択結果をタイトル表示**
|
||||
- 実行時にノードタイトルが `Choice: coffee shop` のように更新されるぞ。
|
||||
- KSamplerのプレビューと見比べやすいのじゃ。
|
||||
|
||||
- **複数設置に対応**
|
||||
- 背景用、時間帯用、天気用など、複数ノードを置いてそれぞれ別々に使えるぞ。
|
||||
|
||||
## 導入方法
|
||||
|
||||
ComfyUIの `custom_nodes` ディレクトリで、以下のコマンドを打ち込むのじゃ!
|
||||
|
||||
```bash
|
||||
git clone https://github.com/ruminar/ComfyUI-PromptRandomChoice.git
|
||||
```
|
||||
|
||||
## 使い方
|
||||
|
||||
1. `Prompt Random Choice` ノードを置く
|
||||
2. `options_text` に候補を入れる
|
||||
3. `prompt_text` を文字列結合ノードなどでポジティブプロンプトへ足す
|
||||
4. キューを好きなだけ積む
|
||||
|
||||
## 入力例
|
||||
|
||||
### 改行区切り (改行前の`,`は任意です)
|
||||
|
||||
```text
|
||||
town,
|
||||
girl's room,
|
||||
park,
|
||||
lake,
|
||||
flower garden,
|
||||
castle, fortress,
|
||||
forest,
|
||||
grasslands,
|
||||
sea,
|
||||
snowy landscape,
|
||||
mountain,
|
||||
flower field,
|
||||
starry sky,
|
||||
coffee shop,
|
||||
```
|
||||
|
||||
### `|` 区切り
|
||||
|
||||
```text
|
||||
town|
|
||||
girl's room|
|
||||
park|
|
||||
lake|
|
||||
flower garden|
|
||||
castle, fortress|
|
||||
forest|
|
||||
grasslands|
|
||||
sea|
|
||||
snowy landscape|
|
||||
mountain|
|
||||
flower field|
|
||||
starry sky|
|
||||
coffee shop|
|
||||
```
|
||||
|
||||
### 選ばれやすさを調整したい場合
|
||||
|
||||
同じ候補を複数回書けば、そのぶん選ばれやすくなります。
|
||||
|
||||
```text
|
||||
day|day|day|sunset|night
|
||||
```
|
||||
|
||||
昼を多めに出したい、たまに夕方や夜も混ぜたい、という時に便利じゃ。
|
||||
|
||||
## 仕様
|
||||
|
||||
- `|` または実際の改行で分割
|
||||
- `\n` という文字列は区切りとして扱わない
|
||||
- 各候補の前後の空白と `,` を trim
|
||||
- 空候補は無視
|
||||
- 候補が空なら空文字 `""` を返す
|
||||
- 候補があれば `,selected,` を返す
|
||||
|
||||
## 出力
|
||||
|
||||
- `prompt_text`
|
||||
- 例: `,coffee shop,`
|
||||
|
||||
## ライセンス
|
||||
|
||||
GPL-3.0(ComfyUI本体の掟に従っておるぞ!)
|
||||
|
||||
## 宣伝画像
|
||||
@@ -0,0 +1,9 @@
|
||||
from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
WEB_DIRECTORY = "./web"
|
||||
|
||||
__all__ = [
|
||||
"NODE_CLASS_MAPPINGS",
|
||||
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||
"WEB_DIRECTORY",
|
||||
]
|
||||
@@ -0,0 +1,96 @@
|
||||
import random
|
||||
import re
|
||||
import secrets
|
||||
import time
|
||||
|
||||
|
||||
DEFAULT_OPTIONS_TEXT = """town,
|
||||
girl's room,
|
||||
park,
|
||||
lake,
|
||||
flower garden,
|
||||
castle, fortress,
|
||||
forest,
|
||||
grasslands,
|
||||
sea,
|
||||
snowy landscape,
|
||||
mountain,
|
||||
flower field,
|
||||
starry sky,
|
||||
coffee shop,"""
|
||||
|
||||
|
||||
def _split_options(options_text: str):
|
||||
text = str(options_text or "")
|
||||
|
||||
# Split by "|" or actual newlines only.
|
||||
# Do not treat literal "\\n" text as a delimiter.
|
||||
parts = re.split(r"\||\r?\n", text)
|
||||
|
||||
options = []
|
||||
for part in parts:
|
||||
item = str(part).strip(" \t\r\n,")
|
||||
if item:
|
||||
options.append(item)
|
||||
|
||||
return options
|
||||
|
||||
|
||||
class PromptRandomChoice:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"options_text": (
|
||||
"STRING",
|
||||
{
|
||||
"multiline": True,
|
||||
"default": DEFAULT_OPTIONS_TEXT,
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompt_text",)
|
||||
FUNCTION = "pick"
|
||||
CATEGORY = "utils/prompt"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, **kwargs):
|
||||
# Force execution for every queued run so the random choice changes.
|
||||
return time.time_ns()
|
||||
|
||||
def pick(self, options_text):
|
||||
options = _split_options(options_text)
|
||||
|
||||
if not options:
|
||||
return {
|
||||
"ui": {
|
||||
"selected_text": [""],
|
||||
},
|
||||
"result": ("",),
|
||||
}
|
||||
|
||||
# Do not use random.choice here.
|
||||
# Some ComfyUI workflows/extensions may reset Python's global random state
|
||||
# from the generation seed, which can make random.choice repeat the same
|
||||
# index every queued run. secrets.choice uses OS entropy instead.
|
||||
selected = secrets.choice(options)
|
||||
prompt_text = f",{selected},"
|
||||
|
||||
return {
|
||||
"ui": {
|
||||
"selected_text": [selected],
|
||||
},
|
||||
"result": (prompt_text,),
|
||||
}
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"PromptRandomChoice": PromptRandomChoice,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"PromptRandomChoice": "Prompt Random Choice",
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
const EXTENSION_NAME = "ruminar.PromptRandomChoice";
|
||||
const NODE_NAME = "PromptRandomChoice";
|
||||
|
||||
function shorten(text, maxLength = 40) {
|
||||
const value = String(text ?? "");
|
||||
if (value.length <= maxLength) {
|
||||
return value;
|
||||
}
|
||||
return value.slice(0, maxLength - 3) + "...";
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: EXTENSION_NAME,
|
||||
|
||||
beforeRegisterNodeDef(nodeType, nodeData) {
|
||||
if (String(nodeData?.name ?? "") !== NODE_NAME) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalOnExecuted = nodeType.prototype.onExecuted;
|
||||
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
originalOnExecuted?.apply(this, arguments);
|
||||
|
||||
const selected = message?.selected_text?.[0] ?? "";
|
||||
if (selected) {
|
||||
this.title = `Choice: ${shorten(selected)}`;
|
||||
} else {
|
||||
this.title = "Prompt Random Choice";
|
||||
}
|
||||
|
||||
this.setDirtyCanvas?.(true, true);
|
||||
app.graph?.setDirtyCanvas?.(true, true);
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user