@danhachuel/thunderbolt 0.4.94 → 0.4.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/main.py +39 -10
- package/hermes_ui/media_providers.py +12 -0
- package/package.json +1 -1
- package/scripts/cli.mjs +4 -3
- package/scripts/install.mjs +1 -1
package/app/main.py
CHANGED
|
@@ -5,6 +5,7 @@ import io
|
|
|
5
5
|
os.environ["PYTHONIOENCODING"] = "utf-8"
|
|
6
6
|
os.environ["PYTHONUTF8"] = "1"
|
|
7
7
|
os.environ["CLICK_NO_WIN_CONSOLE"] = "1"
|
|
8
|
+
os.environ["PYTHONLEGACYWINDOWSSTDIO"] = "1"
|
|
8
9
|
|
|
9
10
|
def _force_utf8_stream(stream: object) -> object:
|
|
10
11
|
if os.name != "nt":
|
|
@@ -7133,6 +7134,7 @@ def _render_media_provider_card(settings: dict[str, Any], cards: list[dict[str,
|
|
|
7133
7134
|
card_form = nullcontext() if embedded else st.form(f"media_card_form_{card_id}")
|
|
7134
7135
|
with card_form:
|
|
7135
7136
|
key_col, model_col = st.columns(2)
|
|
7137
|
+
refresh_clicked = False
|
|
7136
7138
|
with key_col:
|
|
7137
7139
|
api_key = str(card.get("api_key") or "")
|
|
7138
7140
|
if definition.requires_api_key:
|
|
@@ -7158,9 +7160,15 @@ def _render_media_provider_card(settings: dict[str, Any], cards: list[dict[str,
|
|
|
7158
7160
|
key=f"media_card_{card_id}_model_select",
|
|
7159
7161
|
)
|
|
7160
7162
|
model = "" if selected_model == "__select_model__" else selected_model
|
|
7161
|
-
|
|
7163
|
+
if definition.code == "canva":
|
|
7164
|
+
refresh_clicked = st.form_submit_button("Consultar Modelos", use_container_width=True, key=f"media_card_{card_id}_refresh")
|
|
7165
|
+
if definition.code == "canva":
|
|
7166
|
+
base_url = definition.default_base_url
|
|
7167
|
+
st.text_input("Base URL", value=base_url, disabled=True, key=f"media_card_{card_id}_base_url_display")
|
|
7168
|
+
else:
|
|
7169
|
+
base_url = st.text_input("Base URL", value=str(card.get("base_url") or definition.default_base_url), key=f"media_card_{card_id}_base_url")
|
|
7162
7170
|
extra_values: dict[str, str] = {}
|
|
7163
|
-
if definition.extra_fields:
|
|
7171
|
+
if definition.extra_fields and definition.code != "canva":
|
|
7164
7172
|
extra_cols = st.columns(len(definition.extra_fields))
|
|
7165
7173
|
for extra_col, field_name in zip(extra_cols, definition.extra_fields):
|
|
7166
7174
|
with extra_col:
|
|
@@ -7169,17 +7177,37 @@ def _render_media_provider_card(settings: dict[str, Any], cards: list[dict[str,
|
|
|
7169
7177
|
supports_image = True
|
|
7170
7178
|
supports_video = False
|
|
7171
7179
|
st.caption("Canva Connect é usado exclusivamente para thumbnails no Pool de Imagem. Não é provider de vídeo.")
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
st.
|
|
7175
|
-
|
|
7176
|
-
|
|
7180
|
+
credential_cols = st.columns(3)
|
|
7181
|
+
with credential_cols[0]:
|
|
7182
|
+
client_id = st.text_input("Client Id", value=str(card.get("client_id") or ""), key=f"media_card_{card_id}_client_id")
|
|
7183
|
+
with credential_cols[1]:
|
|
7184
|
+
client_secret = st.text_input("Client Secret", value=str(card.get("client_secret") or ""), type="password", key=f"media_card_{card_id}_client_secret")
|
|
7185
|
+
with credential_cols[2]:
|
|
7186
|
+
redirect_uri = st.text_input("Redirect Uri", value=str(card.get("redirect_uri") or ""), key=f"media_card_{card_id}_redirect_uri")
|
|
7187
|
+
extra_values.update({"client_id": client_id.strip(), "client_secret": client_secret.strip(), "redirect_uri": redirect_uri.strip()})
|
|
7188
|
+
oauth_token = card.get("oauth_token") if isinstance(card.get("oauth_token"), dict) else {}
|
|
7189
|
+
has_credentials = bool(client_id.strip() and client_secret.strip() and redirect_uri.strip())
|
|
7190
|
+
is_authorized = bool(oauth_token.get("access_token"))
|
|
7191
|
+
oauth_state = "Autorizado" if is_authorized else ("Não Autorizado" if has_credentials else "⚠️ Incompleto")
|
|
7192
|
+
st.markdown(f"**Estado OAuth:** `{oauth_state}`")
|
|
7193
|
+
if not has_credentials:
|
|
7194
|
+
st.info("Preencha Client Id, Client Secret e Redirect Uri para iniciar a autorização.")
|
|
7195
|
+
else:
|
|
7196
|
+
if st.button("Iniciar autorização", key=f"media_card_{card_id}_authorize"):
|
|
7177
7197
|
verifier, challenge = create_pkce_pair()
|
|
7178
7198
|
state = create_state()
|
|
7179
7199
|
st.session_state[f"canva_verifier_{card_id}"] = verifier
|
|
7180
7200
|
st.session_state[f"canva_state_{card_id}"] = state
|
|
7181
|
-
st.
|
|
7182
|
-
|
|
7201
|
+
st.session_state[f"canva_authorization_url_{card_id}"] = authorization_url(client_id, redirect_uri, "design:content:read design:content:write", state, challenge)
|
|
7202
|
+
authorization_link = str(st.session_state.get(f"canva_authorization_url_{card_id}") or "")
|
|
7203
|
+
if authorization_link:
|
|
7204
|
+
st.link_button("Abrir autorização Canva", authorization_link, use_container_width=True)
|
|
7205
|
+
with st.expander("Opções de exportação e dimensões", expanded=False):
|
|
7206
|
+
export_quality = st.selectbox("Export Quality", ["High", "Medium", "Low"], index=["high", "medium", "low"].index(str(card.get("export_quality") or "medium").lower()), key=f"media_card_{card_id}_export_quality")
|
|
7207
|
+
export_format = st.selectbox("Export Format", ["PNG", "JPG", "PDF"], index=["png", "jpg", "pdf"].index(str(card.get("export_format") or "png").lower()), key=f"media_card_{card_id}_export_format")
|
|
7208
|
+
dimensions = st.selectbox("Thumbnail Width / Height", ["1280 x 720", "1792 x 1024"], index=1 if str(card.get("thumbnail_width")) == "1792" or str(card.get("thumbnail_height")) == "1024" else 0, key=f"media_card_{card_id}_dimensions")
|
|
7209
|
+
selected_width, selected_height = dimensions.replace(" ", "").split("x", 1)
|
|
7210
|
+
extra_values.update({"export_quality": export_quality.lower(), "export_format": export_format.lower(), "thumbnail_width": selected_width, "thumbnail_height": selected_height})
|
|
7183
7211
|
status_cols = st.columns(4)
|
|
7184
7212
|
with status_cols[0]:
|
|
7185
7213
|
enabled = st.checkbox("Provider activo", value=bool(card.get("enabled", True)), key=f"media_card_{card_id}_enabled")
|
|
@@ -7191,7 +7219,8 @@ def _render_media_provider_card(settings: dict[str, Any], cards: list[dict[str,
|
|
|
7191
7219
|
priority = st.number_input("Prioridade", min_value=0, max_value=999, value=int(card.get("priority", index)), step=1, key=f"media_card_{card_id}_priority")
|
|
7192
7220
|
action_cols = st.columns(4)
|
|
7193
7221
|
with action_cols[0]:
|
|
7194
|
-
|
|
7222
|
+
if definition.code != "canva":
|
|
7223
|
+
refresh_clicked = st.form_submit_button("Consultar Modelos", use_container_width=True, key=f"media_card_{card_id}_refresh")
|
|
7195
7224
|
with action_cols[1]:
|
|
7196
7225
|
test_clicked = st.form_submit_button("Testar Chamada API", use_container_width=True, key=f"media_card_{card_id}_test")
|
|
7197
7226
|
with action_cols[2]:
|
|
@@ -219,6 +219,18 @@ def normalize_media_card(card: Any, index: int = 0) -> dict[str, Any]:
|
|
|
219
219
|
for field in definition.extra_fields:
|
|
220
220
|
result[field] = str(source.get(field) or "").strip()
|
|
221
221
|
if provider == "canva":
|
|
222
|
+
quality = str(source.get("export_quality") or "medium").strip().lower()
|
|
223
|
+
result["export_quality"] = {"regular": "medium", "default": "medium"}.get(quality, quality if quality in {"high", "medium", "low"} else "medium")
|
|
224
|
+
export_format = str(source.get("export_format") or "png").strip().lower()
|
|
225
|
+
result["export_format"] = export_format if export_format in {"png", "jpg", "pdf"} else "png"
|
|
226
|
+
raw_width = str(source.get("thumbnail_width") or "").strip().lower().replace("×", "x").replace(" ", "")
|
|
227
|
+
raw_height = str(source.get("thumbnail_height") or "").strip().lower().replace("×", "x").replace(" ", "")
|
|
228
|
+
combined = raw_width if "x" in raw_width else raw_height
|
|
229
|
+
if "x" in combined:
|
|
230
|
+
left, right = combined.split("x", 1)
|
|
231
|
+
raw_width, raw_height = left, right
|
|
232
|
+
result["thumbnail_width"] = raw_width if raw_width in {"1280", "1792"} else "1280"
|
|
233
|
+
result["thumbnail_height"] = raw_height if raw_height in {"720", "1024"} else ("1024" if result["thumbnail_width"] == "1792" else "720")
|
|
222
234
|
result["oauth_token"] = dict(source.get("oauth_token") or {}) if isinstance(source.get("oauth_token"), Mapping) else {}
|
|
223
235
|
if provider == "nano_banana":
|
|
224
236
|
result["aspect_ratio"] = str(source.get("aspect_ratio") or INTERNAL_IMAGE_ASPECT_RATIO).strip()
|
package/package.json
CHANGED
package/scripts/cli.mjs
CHANGED
|
@@ -26,13 +26,13 @@ const venvPython = platform() === "win32" ? join(venvDir, "Scripts", "python.exe
|
|
|
26
26
|
const python = process.env.THUNDERBOLT_PYTHON || process.env.HERMES_PYTHON || (existsSync(venvPython) ? venvPython : (platform() === "win32" ? "python" : "python3"));
|
|
27
27
|
const main = resolve(root, "app", "main.py");
|
|
28
28
|
const settingsPath = join(thunderboltHome, "storage", "state", "settings.json");
|
|
29
|
-
const pythonEnvironment = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", CLICK_NO_WIN_CONSOLE: "1" };
|
|
29
|
+
const pythonEnvironment = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", PYTHONLEGACYWINDOWSSTDIO: "1", CLICK_NO_WIN_CONSOLE: "1" };
|
|
30
30
|
|
|
31
31
|
function run(command, commandArgs, label = "comando", environment = process.env) {
|
|
32
32
|
console.log(`Thunderbolt: a iniciar ${label}...`);
|
|
33
33
|
const result = spawnSync(command, commandArgs, {
|
|
34
34
|
stdio: "inherit",
|
|
35
|
-
env: { ...pythonEnvironment, ...environment, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" },
|
|
35
|
+
env: { ...pythonEnvironment, ...environment, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", PYTHONLEGACYWINDOWSSTDIO: "1", CLICK_NO_WIN_CONSOLE: "1" },
|
|
36
36
|
cwd: root,
|
|
37
37
|
windowsHide: false,
|
|
38
38
|
});
|
|
@@ -155,6 +155,7 @@ const runtimeEnv = {
|
|
|
155
155
|
...process.env,
|
|
156
156
|
PYTHONIOENCODING: "utf-8",
|
|
157
157
|
PYTHONUTF8: "1",
|
|
158
|
+
PYTHONLEGACYWINDOWSSTDIO: "1",
|
|
158
159
|
CLICK_NO_WIN_CONSOLE: "1",
|
|
159
160
|
THUNDERBOLT_STORAGE_DIR: storageDir,
|
|
160
161
|
STREAMLIT_BROWSER_GATHER_USAGE_STATS: "false",
|
|
@@ -258,7 +259,7 @@ function startStreamlit() {
|
|
|
258
259
|
child = spawn(python, ["-m", "streamlit", "run", main, "--server.port", String(backendPort), "--server.address", "127.0.0.1"], {
|
|
259
260
|
cwd: root,
|
|
260
261
|
stdio: "inherit",
|
|
261
|
-
env: { ...runtimeEnv, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", CLICK_NO_WIN_CONSOLE: "1", THUNDERBOLT_LAUNCHER_RESTART: "1" },
|
|
262
|
+
env: { ...runtimeEnv, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", PYTHONLEGACYWINDOWSSTDIO: "1", CLICK_NO_WIN_CONSOLE: "1", THUNDERBOLT_LAUNCHER_RESTART: "1" },
|
|
262
263
|
windowsHide: false,
|
|
263
264
|
});
|
|
264
265
|
child.on("error", (error) => {
|
package/scripts/install.mjs
CHANGED
|
@@ -42,7 +42,7 @@ const defaultMpt = process.env.MONEYPRINTER_PATH || join(thunderboltHome, "Money
|
|
|
42
42
|
const dependencyStatePath = join(thunderboltHome, "storage", "state", "install-state.json");
|
|
43
43
|
const forceDeps = args.includes("--force-deps");
|
|
44
44
|
const refreshMoneyPrinter = args.includes("--refresh-moneyprinter");
|
|
45
|
-
const pythonEnvironment = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", CLICK_NO_WIN_CONSOLE: "1" };
|
|
45
|
+
const pythonEnvironment = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", PYTHONLEGACYWINDOWSSTDIO: "1", CLICK_NO_WIN_CONSOLE: "1" };
|
|
46
46
|
|
|
47
47
|
function legacyRoots() {
|
|
48
48
|
const userProfile = process.env.USERPROFILE || home;
|