@danhachuel/thunderbolt 0.4.90 → 0.4.92
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 +22 -29
- package/package.json +1 -1
- package/scripts/cli.mjs +3 -2
- package/scripts/install.mjs +1 -1
package/app/main.py
CHANGED
|
@@ -4,12 +4,14 @@ import io
|
|
|
4
4
|
|
|
5
5
|
os.environ["PYTHONIOENCODING"] = "utf-8"
|
|
6
6
|
os.environ["PYTHONUTF8"] = "1"
|
|
7
|
+
os.environ["CLICK_NO_WIN_CONSOLE"] = "1"
|
|
7
8
|
|
|
8
9
|
def _force_utf8_stream(stream: object) -> object:
|
|
9
10
|
buffer = getattr(stream, "buffer", None)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
if buffer is not None and not getattr(stream, "_thunderbolt_utf8", False):
|
|
12
|
+
wrapped = io.TextIOWrapper(buffer, encoding="utf-8", errors="replace", line_buffering=True)
|
|
13
|
+
setattr(wrapped, "_thunderbolt_utf8", True)
|
|
14
|
+
return wrapped
|
|
13
15
|
return stream
|
|
14
16
|
|
|
15
17
|
_original_stdout, _original_stderr = sys.stdout, sys.stderr
|
|
@@ -2025,7 +2027,7 @@ def render_tiktok_channels():
|
|
|
2025
2027
|
for channel in channels:
|
|
2026
2028
|
channel_id = str(channel.get("id") or "")
|
|
2027
2029
|
with st.container(border=True):
|
|
2028
|
-
card_cols = st.columns([0.7,
|
|
2030
|
+
card_cols = st.columns([0.7, 3.45, 1.15, 1.15, 1.45, 1.55], gap="small")
|
|
2029
2031
|
with card_cols[0]:
|
|
2030
2032
|
avatar_url = _tiktok_avatar_url(channel)
|
|
2031
2033
|
if avatar_url:
|
|
@@ -2041,9 +2043,6 @@ def render_tiktok_channels():
|
|
|
2041
2043
|
st.metric("Seguidores", format_metric_number(channel.get("subscriber_count")))
|
|
2042
2044
|
with card_cols[3]:
|
|
2043
2045
|
st.metric("Curtidas", format_metric_number(channel.get("likes_count")))
|
|
2044
|
-
with card_cols[4]:
|
|
2045
|
-
current_prompt = str(channel.get("default_prompt_master") or channel.get("prompt_master") or "")
|
|
2046
|
-
selected_prompt = st.selectbox("Prompt Master", prompt_ids, index=prompt_ids.index(current_prompt) if current_prompt in prompt_ids else 0, format_func=lambda item: prompt_labels.get(item, item), key=f"tiktok_import_card_prompt_{channel_id}")
|
|
2047
2046
|
with card_cols[5]:
|
|
2048
2047
|
active = st.toggle("Activo", value=bool(channel.get("active", True)), key=f"tiktok_import_card_active_{channel_id}")
|
|
2049
2048
|
if active != bool(channel.get("active", True)):
|
|
@@ -2055,20 +2054,20 @@ def render_tiktok_channels():
|
|
|
2055
2054
|
if st.button("Apagar card", key=f"tiktok_import_card_delete_{channel_id}"):
|
|
2056
2055
|
st.session_state[f"tiktok_delete_{channel_id}"] = True
|
|
2057
2056
|
st.rerun()
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2057
|
+
summary_cols = st.columns(4, gap="medium")
|
|
2058
|
+
with summary_cols[0]:
|
|
2059
|
+
st.markdown("**Blueprint Padrão**")
|
|
2060
|
+
st.caption(prompt_labels.get(str(channel.get("default_prompt_master") or channel.get("prompt_master") or ""), "Sem Blueprint padrão"))
|
|
2061
|
+
with summary_cols[1]:
|
|
2062
|
+
st.markdown("**Nicho**")
|
|
2063
|
+
st.caption(channel_niche_label(channel))
|
|
2064
|
+
with summary_cols[2]:
|
|
2065
|
+
st.markdown("**Narrador/Voz Padrão**")
|
|
2066
|
+
st.caption(str(channel.get("default_voice") or channel.get("voice") or "Sem voz padrão"))
|
|
2067
|
+
with summary_cols[3]:
|
|
2068
|
+
st.markdown("**Idioma**")
|
|
2069
|
+
st.caption(video_language_label(normalize_video_language(channel.get("language") or "pt")))
|
|
2070
|
+
st.caption(f"Fonte do vídeo: {channel_video_source_value(channel.get('style_wide'))} · Proporção: {channel.get('video_aspect_ratio') or 'Portrait 9:16'}")
|
|
2072
2071
|
if st.session_state.get(f"tiktok_delete_{channel_id}"):
|
|
2073
2072
|
st.warning("Apagar este canal TikTok e os dados locais associados?")
|
|
2074
2073
|
confirm_cols = st.columns(2)
|
|
@@ -2081,13 +2080,6 @@ def render_tiktok_channels():
|
|
|
2081
2080
|
if st.button("Cancelar", key=f"tiktok_cancel_delete_{channel_id}"):
|
|
2082
2081
|
st.session_state.pop(f"tiktok_delete_{channel_id}", None)
|
|
2083
2082
|
st.rerun()
|
|
2084
|
-
if st.session_state.get(f"tiktok_niche_{channel_id}"):
|
|
2085
|
-
with st.form(f"tiktok_niche_form_{channel_id}"):
|
|
2086
|
-
niche_value = st.text_input("Nicho do canal", value=str(channel.get("niche") or ""))
|
|
2087
|
-
if st.form_submit_button("Guardar nicho", type="primary"):
|
|
2088
|
-
update_channel(channel_id, {"niche": niche_value.strip(), "reference_channels": [niche_value.strip()] if niche_value.strip() else []})
|
|
2089
|
-
st.session_state.pop(f"tiktok_niche_{channel_id}", None)
|
|
2090
|
-
st.rerun()
|
|
2091
2083
|
if st.session_state.get(f"tiktok_edit_{channel_id}"):
|
|
2092
2084
|
with st.form(f"tiktok_edit_form_{channel_id}"):
|
|
2093
2085
|
edit_name = st.text_input("Nome do canal", value=str(channel.get("name") or ""))
|
|
@@ -2096,6 +2088,7 @@ def render_tiktok_channels():
|
|
|
2096
2088
|
edit_language = st.selectbox("Idioma do roteiro", VIDEO_LANGUAGE_SELECTION_OPTIONS, index=VIDEO_LANGUAGE_SELECTION_OPTIONS.index(normalize_video_language(channel.get("language") or "pt")) if normalize_video_language(channel.get("language") or "pt") in VIDEO_LANGUAGE_SELECTION_OPTIONS else 0, format_func=video_language_label)
|
|
2097
2089
|
edit_source = st.selectbox("Fonte do vídeo", WIDE_STYLE_OPTIONS, index=WIDE_STYLE_OPTIONS.index(channel_video_source_value(channel.get("style_wide"))) if channel_video_source_value(channel.get("style_wide")) in WIDE_STYLE_OPTIONS else 0)
|
|
2098
2090
|
edit_aspect = st.selectbox("Proporção do vídeo", CHANNEL_ASPECT_RATIO_OPTIONS, index=CHANNEL_ASPECT_RATIO_OPTIONS.index(str(channel.get("video_aspect_ratio") or "Portrait 9:16")) if str(channel.get("video_aspect_ratio") or "Portrait 9:16") in CHANNEL_ASPECT_RATIO_OPTIONS else 1)
|
|
2091
|
+
edit_niche = st.text_input("Nicho", value=str(channel.get("niche") or ""))
|
|
2099
2092
|
edit_voice = st.selectbox("Narrador/Voz Padrão", voice_options, index=voice_options.index(str(channel.get("default_voice") or channel.get("voice") or "")) if str(channel.get("default_voice") or channel.get("voice") or "") in voice_options else 0, format_func=lambda item: item or "Sem voz padrão")
|
|
2100
2093
|
edit_automation = st.toggle("Automação ON", value=bool(channel.get("automation_on", False)), key=f"tiktok_edit_automation_{channel_id}")
|
|
2101
2094
|
edit_time = st.text_input("Horário diário (HH:MM)", value=str(channel.get("automation_time") or "00:00"))
|
|
@@ -2105,7 +2098,7 @@ def render_tiktok_channels():
|
|
|
2105
2098
|
if not valid_hhmm(edit_time):
|
|
2106
2099
|
st.error("O horário diário deve estar no formato HH:MM.")
|
|
2107
2100
|
else:
|
|
2108
|
-
update_channel(channel_id, {"name": edit_name.strip(), "url": edit_url.strip(), "handle": edit_handle.strip(), "language": edit_language, "default_voice": edit_voice, "voice": edit_voice, "style_wide": channel_video_source_storage(edit_source), "video_aspect_ratio": edit_aspect, "automation_on": edit_automation, "automation_time": edit_time.strip(), "description": edit_description.strip(), "platform": "tiktok"})
|
|
2101
|
+
update_channel(channel_id, {"name": edit_name.strip(), "url": edit_url.strip(), "handle": edit_handle.strip(), "language": edit_language, "niche": edit_niche.strip(), "reference_channels": [edit_niche.strip()] if edit_niche.strip() else [], "default_voice": edit_voice, "voice": edit_voice, "style_wide": channel_video_source_storage(edit_source), "video_aspect_ratio": edit_aspect, "automation_on": edit_automation, "automation_time": edit_time.strip(), "description": edit_description.strip(), "platform": "tiktok"})
|
|
2109
2102
|
st.session_state.pop(f"tiktok_edit_{channel_id}", None)
|
|
2110
2103
|
st.rerun()
|
|
2111
2104
|
with st.expander("Últimos 10 vídeos publicados", expanded=False):
|
package/package.json
CHANGED
package/scripts/cli.mjs
CHANGED
|
@@ -26,7 +26,7 @@ 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" };
|
|
29
|
+
const pythonEnvironment = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "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}...`);
|
|
@@ -155,6 +155,7 @@ const runtimeEnv = {
|
|
|
155
155
|
...process.env,
|
|
156
156
|
PYTHONIOENCODING: "utf-8",
|
|
157
157
|
PYTHONUTF8: "1",
|
|
158
|
+
CLICK_NO_WIN_CONSOLE: "1",
|
|
158
159
|
THUNDERBOLT_STORAGE_DIR: storageDir,
|
|
159
160
|
STREAMLIT_BROWSER_GATHER_USAGE_STATS: "false",
|
|
160
161
|
STREAMLIT_SERVER_HEADLESS: "true",
|
|
@@ -257,7 +258,7 @@ function startStreamlit() {
|
|
|
257
258
|
child = spawn(python, ["-m", "streamlit", "run", main, "--server.port", String(backendPort), "--server.address", "127.0.0.1"], {
|
|
258
259
|
cwd: root,
|
|
259
260
|
stdio: "inherit",
|
|
260
|
-
env: { ...runtimeEnv, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", THUNDERBOLT_LAUNCHER_RESTART: "1" },
|
|
261
|
+
env: { ...runtimeEnv, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", CLICK_NO_WIN_CONSOLE: "1", THUNDERBOLT_LAUNCHER_RESTART: "1" },
|
|
261
262
|
windowsHide: false,
|
|
262
263
|
});
|
|
263
264
|
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" };
|
|
45
|
+
const pythonEnvironment = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", CLICK_NO_WIN_CONSOLE: "1" };
|
|
46
46
|
|
|
47
47
|
function legacyRoots() {
|
|
48
48
|
const userProfile = process.env.USERPROFILE || home;
|