simpleai22 commited on
Commit
8ccfc68
·
verified ·
1 Parent(s): aef860a

Rename t2i-prompter.py to verifier

Browse files
Files changed (2) hide show
  1. t2i-prompter.py +0 -269
  2. verifier +154 -0
t2i-prompter.py DELETED
@@ -1,269 +0,0 @@
1
- import json, time, uuid, random, requests, re
2
- from pathlib import Path
3
-
4
- HOST = "http://127.0.0.1:3020"
5
- PROMPT_FILE = "/app/workspace/prompts.txt"
6
- AUTH_URL = "http://49.12.247.141:8001"
7
-
8
- # Optional: fixed seeds (None = random)
9
- FIXED_SEEDS = None
10
-
11
- # Optional overrides
12
- OVERRIDE_STEPS = None
13
- OVERRIDE_WIDTH = None
14
- OVERRIDE_HEIGHT = None
15
- OVERRIDE_GUIDANCE = None
16
-
17
- # Node-IDs from your workflow
18
- NODE_PROMPT = "28"
19
- NODE_NOISE = "25"
20
- NODE_SCHED = "17"
21
- NODE_LATENT = "5"
22
- NODE_GUIDE = "80"
23
- NODE_SAVE = "9"
24
-
25
- # ===== Translations =====
26
- TEXTS = {
27
- "en": {
28
- "intro": """Greta | SimpleAI : Hey! Ready to generate some images? Here’s how it works:
29
- 1️⃣ Don’t forget to fill your prompts.txt file. Separate each prompt with {} and a new line.
30
- 2️⃣ Export your workflow as API (Save (API)) in ComfyUI, save it into the same folder as this script,
31
- and when I ask, type the exact filename (e.g. schedule-prompts-api.json).
32
- 3️⃣ Set the number of times I should loop through the list of prompts.
33
- 4️⃣ Set your resolution. For normal images I recommend 4:5 ➡️ 1080x1350px.
34
- For pictures for reels I recommend 9:16 ➡️ 1080×1920px.
35
- Then you’re ready to go! 🚀
36
- To stop this process press CTRL+Z and then type 'kill -9 %1' in the terminal.
37
- You can follow the process live in ComfyUI. Images will be saved in Outputs.
38
- """,
39
- "understand": "Did you understand? yes/no: ",
40
- "not_understood": "Please read the instructions again carefully. 🙏",
41
- "ask_api": "Please enter the filename of your exported workflow API JSON (e.g. schedule-prompts-api.json): ",
42
- "ask_k": "How many times should I loop through the entire prompt list? [Default {default}]: ",
43
- "ask_width": "Enter image width (px): ",
44
- "ask_height": "Enter image height (px): ",
45
- "invalid": "❌ Invalid input, try again.",
46
- "start": "Great! 🎉 We’ll generate {total} images "
47
- "({n_prompts} prompts × {k} runs) at resolution {w}×{h}px.\n",
48
- "summary": "=== Summary ===",
49
- "done": "✅ All done! Check your Outputs folder."
50
- },
51
- "de": {
52
- "intro": """Greta | SimpleAI : Hey! Bereit ein paar Bilder zu generieren? So funktioniert’s:
53
- 1️⃣ Vergiss nicht deine prompts.txt Datei zu befüllen. Trenne jeden Prompt durch {} und einen Zeilenumbruch.
54
- 2️⃣ Exportiere deinen Workflow in ComfyUI als API (Save (API)), speichere ihn im selben Ordner wie dieses Skript
55
- und gib mir gleich den exakten Dateinamen an (z. B. schedule-prompts-api.json).
56
- 3️⃣ Stelle ein, wie oft ich die Liste der Prompts durchlaufen soll.
57
- 4️⃣ Stelle deine Auflösung ein. Für normale Bilder empfehle ich 4:5 ➡️ 1080x1350px.
58
- Für Bilder für Reels empfehle ich 9:16 ➡️ 1080×1920px.
59
- Dann bist du ready to go! 🚀
60
- Um den Prozess zu stoppen drücke STRG+Z und tippe anschließend 'kill -9 %1' im Terminal ein.
61
- Den Prozess kannst du live in ComfyUI verfolgen. Die Bilder findest du im Output-Ordner.
62
- """,
63
- "understand": "Alles verstanden? ja/nein: ",
64
- "not_understood": "Bitte lies dir die Anweisungen noch einmal genau durch. 🙏",
65
- "ask_api": "Bitte gib den Dateinamen deiner exportierten Workflow-API-JSON ein (z. B. schedule-prompts-api.json): ",
66
- "ask_k": "Wie oft soll ich die gesamte Prompt-Liste durchlaufen? [Standard {default}]: ",
67
- "ask_width": "Gib die Bildbreite (px) ein: ",
68
- "ask_height": "Gib die Bildhöhe (px) ein: ",
69
- "invalid": "❌ Ungültige Eingabe, versuche es erneut.",
70
- "start": "Super! 🎉 Wir generieren {total} Bilder "
71
- "({n_prompts} Prompts × {k} Durchläufe) in Auflösung {w}×{h}px.\n",
72
- "summary": "=== Zusammenfassung ===",
73
- "done": "✅ Fertig! Schau im Output-Ordner nach."
74
- }
75
- }
76
-
77
- # ===== Helpers =====
78
- def load_graph(path: str) -> dict:
79
- obj = json.loads(Path(path).read_text(encoding="utf-8"))
80
- return obj["prompt"] if "prompt" in obj else obj
81
-
82
- def load_prompts(path: str):
83
- text = Path(path).read_text(encoding="utf-8")
84
- parts = re.findall(r"\{(.*?)\}", text, flags=re.DOTALL)
85
- return [p.strip() for p in parts if p.strip()]
86
-
87
- def slug(s, maxlen=40):
88
- s = re.sub(r"[^a-zA-Z0-9]+", "-", s).strip("-").lower()
89
- return (s[:maxlen].rstrip("-") or "img")
90
-
91
- def post_prompt(graph: dict, client_id: str) -> str:
92
- r = requests.post(f"{HOST}/prompt", json={"prompt": graph, "client_id": client_id}, timeout=60)
93
- r.raise_for_status()
94
- return r.json().get("prompt_id")
95
-
96
- def wait_done(prompt_id: str, poll=1.0, timeout=600) -> dict:
97
- t0 = time.time()
98
- while True:
99
- r = requests.get(f"{HOST}/history/{prompt_id}", timeout=30)
100
- if r.ok:
101
- j = r.json()
102
- if j.get(prompt_id, {}).get("outputs"):
103
- return j[prompt_id]
104
- if time.time() - t0 > timeout:
105
- raise TimeoutError(f"Timeout after {timeout}s")
106
- time.sleep(poll)
107
-
108
- def extract_files(history_obj: dict):
109
- files = []
110
- for _, node_out in history_obj.get("outputs", {}).items():
111
- for im in node_out.get("images", []):
112
- if im.get("filename"):
113
- files.append(im["filename"])
114
- return files
115
-
116
- # ===== Dialogs =====
117
- def choose_language():
118
- while True:
119
- lang = input("Greta | SimpleAI : english or german? ").strip().lower()
120
- if lang in ["english", "en"]:
121
- return "en"
122
- elif lang in ["german", "deutsch", "de"]:
123
- return "de"
124
- else:
125
- print("❌ Invalid choice, type 'english' or 'german'.")
126
-
127
- def dialog_intro(TEXT):
128
- print(TEXT["intro"])
129
- while True:
130
- understood = input(TEXT["understand"]).strip().lower()
131
- if understood in ["yes", "y", "ja", "j"]:
132
- break
133
- elif understood in ["no", "n", "nein"]:
134
- print(TEXT["not_understood"])
135
- else:
136
- print(TEXT["invalid"])
137
-
138
- def ask_api_filename(TEXT):
139
- while True:
140
- fname = input(TEXT["ask_api"]).strip()
141
- # baue den Pfad mit workspace-Verzeichnis
142
- full_path = Path("/app/workspace") / fname
143
- if full_path.exists():
144
- return str(full_path)
145
- else:
146
- print("❌ File not found in /app/workspace, please try again.")
147
-
148
- def ask_k(TEXT, default=1):
149
- while True:
150
- raw = input(TEXT["ask_k"].format(default=default)).strip()
151
- if raw == "":
152
- return default
153
- try:
154
- k = int(raw)
155
- if k > 0:
156
- return k
157
- except ValueError:
158
- pass
159
- print(TEXT["invalid"])
160
-
161
- def ask_resolution(TEXT):
162
- while True:
163
- try:
164
- w = int(input(TEXT["ask_width"]).strip())
165
- h = int(input(TEXT["ask_height"]).strip())
166
- if w > 0 and h > 0:
167
- return w, h
168
- except ValueError:
169
- pass
170
- print(TEXT["invalid"])
171
-
172
- def membership_check(email, discord_id):
173
- try:
174
- r = requests.post(
175
- f"{AUTH_URL}/check",
176
- json={"email": email, "discord_id": discord_id}, # <--- richtiges Feld
177
- timeout=10
178
- )
179
-
180
- if r.status_code != 200:
181
- print("❌ Membership inactive:", r.text)
182
- return False
183
-
184
- data = r.json()
185
- if not data.get("active"):
186
- print("❌ Membership inactive:", data)
187
- return False
188
-
189
- return True
190
-
191
- except Exception as e:
192
- print("❌ Could not contact auth server:", e)
193
- return False
194
-
195
- # ===== Main =====
196
- def main():
197
- email = input("Enter your membership email: ").strip()
198
- discord = input("Enter your Discord-ID: ").strip()
199
-
200
- if not membership_check(email, discord):
201
- print("❌ Access denied. Exiting.")
202
- return
203
-
204
- lang = choose_language()
205
- TEXT = TEXTS[lang]
206
-
207
- dialog_intro(TEXT)
208
- api_filename = ask_api_filename(TEXT)
209
-
210
- base = load_graph(api_filename)
211
- prompts = load_prompts(PROMPT_FILE)
212
- if not prompts:
213
- print("❌ No prompts found in prompts.txt")
214
- return
215
-
216
- client_id = str(uuid.uuid4())
217
- k = ask_k(TEXT, default=1)
218
- w, h = ask_resolution(TEXT)
219
-
220
- total = len(prompts) * k
221
- print(TEXT["start"].format(total=total, n_prompts=len(prompts), k=k, w=w, h=h))
222
-
223
- summary = []
224
- run_counter = 0
225
-
226
- for run in range(1, k+1):
227
- print(f"=== Run {run}/{k} ===")
228
- for i, prompt_text in enumerate(prompts, 1):
229
- run_counter += 1
230
- g = json.loads(json.dumps(base))
231
-
232
- # Prompt
233
- g[NODE_PROMPT]["inputs"]["string"] = prompt_text
234
-
235
- # Seed
236
- if FIXED_SEEDS:
237
- seed = int(FIXED_SEEDS[(run_counter-1) % len(FIXED_SEEDS)])
238
- else:
239
- seed = random.randrange(0, 2**31 - 1)
240
- g[NODE_NOISE]["inputs"]["noise_seed"] = seed
241
-
242
- # Overrides
243
- if OVERRIDE_STEPS is not None:
244
- g[NODE_SCHED]["inputs"]["steps"] = int(OVERRIDE_STEPS)
245
- if w and h:
246
- g[NODE_LATENT]["inputs"]["width"] = int(w)
247
- g[NODE_LATENT]["inputs"]["height"] = int(h)
248
- if OVERRIDE_GUIDANCE is not None:
249
- g[NODE_GUIDE]["inputs"]["guidance"] = float(OVERRIDE_GUIDANCE)
250
-
251
- # Filename
252
- prefix = f"r{run:02d}_p{i:02d}_{slug(prompt_text)}"
253
- g[NODE_SAVE]["inputs"]["filename_prefix"] = prefix
254
-
255
- pid = post_prompt(g, client_id)
256
- print(f"[{run}/{k} · {i}/{len(prompts)}] pid={pid} seed={seed} prefix={prefix}")
257
- hist = wait_done(pid)
258
- files = extract_files(hist)
259
- print(" -> Files:", files)
260
- summary.append({"run": run, "prompt": prompt_text, "seed": seed, "files": files})
261
-
262
- print("\n" + TEXT["summary"])
263
- for row in summary:
264
- print(f"- Run {row['run']} | Prompt: {row['prompt']}\n"
265
- f" Seed: {row['seed']}\n Files: {row['files']}\n")
266
- print(TEXT["done"])
267
-
268
- if __name__ == "__main__":
269
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
verifier ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import os
3
+ import sys
4
+ import json
5
+ import time
6
+ import zipfile
7
+ import requests
8
+ from pathlib import Path
9
+
10
+ AUTH_URL = os.environ.get("AUTH_URL", "http://49.12.247.141:8001")
11
+ HF_URL = "https://huggingface.co/simpleai22/simple-workflows/resolve/main/simple_workflows.zip"
12
+
13
+ DEST_DIR = Path("/app/workspace")
14
+ ZIP_PATH = DEST_DIR / "simple_workflows.zip"
15
+
16
+ # Optional: falls ZIP verschlüsselt ist (ZipCrypto)
17
+ ZIP_PASSWORD = os.environ.get("ZIP_PASSWORD", "simpleai22rockt") # bei Bedarf anpassen oder leer lassen
18
+
19
+ # Optional: falls das HF-Repo privat ist
20
+ HF_TOKEN = os.environ.get("HF_TOKEN", "").strip()
21
+
22
+ TIMEOUT = 20
23
+
24
+
25
+ def membership_check(email: str, discord_id: str) -> bool:
26
+ """Verifiziert Membership via Auth-Server."""
27
+ try:
28
+ r = requests.post(
29
+ f"{AUTH_URL}/check",
30
+ json={"email": email, "discord_id": discord_id},
31
+ timeout=TIMEOUT,
32
+ )
33
+ if r.status_code != 200:
34
+ print(f"❌ Auth server returned {r.status_code}: {r.text}")
35
+ return False
36
+
37
+ data = r.json()
38
+ if not data.get("active"):
39
+ print(f"❌ Membership inactive: {data}")
40
+ return False
41
+
42
+ print("✅ Membership verified.")
43
+ return True
44
+ except requests.RequestException as e:
45
+ print(f"❌ Could not contact auth server: {e}")
46
+ return False
47
+ except Exception as e:
48
+ print(f"❌ Unexpected error during verification: {e}")
49
+ return False
50
+
51
+
52
+ def download_zip(url: str, dest_path: Path, token: str = "") -> None:
53
+ """Lädt die ZIP per Streaming-Download."""
54
+ dest_path.parent.mkdir(parents=True, exist_ok=True)
55
+ headers = {}
56
+ if token:
57
+ headers["Authorization"] = f"Bearer {token}"
58
+
59
+ print(f"⬇️ Downloading ZIP to {dest_path} ...")
60
+ with requests.get(url, stream=True, headers=headers, timeout=TIMEOUT) as r:
61
+ r.raise_for_status()
62
+ total = int(r.headers.get("content-length", 0))
63
+ downloaded = 0
64
+ chunk_size = 1024 * 256
65
+ with open(dest_path, "wb") as f:
66
+ for chunk in r.iter_content(chunk_size=chunk_size):
67
+ if chunk:
68
+ f.write(chunk)
69
+ downloaded += len(chunk)
70
+ if total:
71
+ pct = downloaded * 100 // total
72
+ print(f"\r {downloaded}/{total} bytes ({pct}%)", end="")
73
+ print()
74
+
75
+ if not dest_path.exists() or dest_path.stat().st_size == 0:
76
+ raise RuntimeError("Download failed or file is empty")
77
+
78
+ print("✅ Download complete.")
79
+
80
+
81
+ def extract_zip(zip_path: Path, target_dir: Path) -> None:
82
+ """Entpackt ZIP; versucht erst ohne Passwort, dann (falls nötig) mit ZIP_PASSWORD."""
83
+ print(f"📦 Extracting into {target_dir} ...")
84
+ target_dir.mkdir(parents=True, exist_ok=True)
85
+
86
+ def _extract(zf: zipfile.ZipFile, pwd: bytes | None):
87
+ for member in zf.infolist():
88
+ # Sicherheit: keine Pfad-Traversal
89
+ extracted_path = target_dir / member.filename
90
+ if not str(extracted_path.resolve()).startswith(str(target_dir.resolve())):
91
+ raise RuntimeError(f"Blocked suspicious path: {member.filename}")
92
+ zf.extract(member, path=target_dir, pwd=pwd)
93
+
94
+ try:
95
+ with zipfile.ZipFile(zip_path) as zf:
96
+ try:
97
+ # Versuch ohne Passwort
98
+ _extract(zf, None)
99
+ print("✅ Extracted without password.")
100
+ return
101
+ except RuntimeError as e:
102
+ # evtl. verschlüsselt
103
+ if "encrypted" not in str(e).lower():
104
+ raise
105
+ print("ℹ️ ZIP seems encrypted. Trying with password ...")
106
+ zf.close()
107
+
108
+ # erneutes Öffnen und mit Passwort versuchen
109
+ with zipfile.ZipFile(zip_path) as zf:
110
+ pwd = ZIP_PASSWORD.encode("utf-8") if ZIP_PASSWORD else None
111
+ if not pwd:
112
+ raise RuntimeError("ZIP is encrypted but no password provided.")
113
+ _extract(zf, pwd)
114
+ print("✅ Extracted with password.")
115
+
116
+ except zipfile.BadZipFile:
117
+ raise RuntimeError("Invalid or corrupted ZIP file.")
118
+ except Exception as e:
119
+ raise RuntimeError(f"Extraction failed: {e}") from e
120
+
121
+
122
+ def main():
123
+ print("=== Verification required ===")
124
+ email = input("Enter your membership email: ").strip()
125
+ discord = input("Enter your Discord-ID: ").strip()
126
+
127
+ if not email or not discord:
128
+ print("❌ Email and Discord-ID are required.")
129
+ sys.exit(1)
130
+
131
+ if not membership_check(email, discord):
132
+ print("❌ Access denied. Exiting.")
133
+ sys.exit(1)
134
+
135
+ try:
136
+ download_zip(HF_URL, ZIP_PATH, token=HF_TOKEN)
137
+ extract_zip(ZIP_PATH, DEST_DIR)
138
+ except Exception as e:
139
+ print(f"❌ Error: {e}")
140
+ sys.exit(1)
141
+ finally:
142
+ # ZIP aufräumen
143
+ if ZIP_PATH.exists():
144
+ try:
145
+ ZIP_PATH.unlink()
146
+ print("🧹 Removed ZIP file.")
147
+ except Exception as e:
148
+ print(f"⚠️ Could not remove ZIP: {e}")
149
+
150
+ print(f"✅ Done. Files are ready in: {DEST_DIR}")
151
+
152
+
153
+ if __name__ == "__main__":
154
+ main()