@danhachuel/thunderbolt 0.4.84 → 0.4.85
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 +42 -11
- package/package.json +1 -1
package/app/main.py
CHANGED
|
@@ -1924,6 +1924,7 @@ def render_tiktok_channels():
|
|
|
1924
1924
|
channels = _tiktok_channel_records()
|
|
1925
1925
|
import_tab, spreadsheet_tab, manual_tab = render_localized_tabs(["Importar do Tiktok", "Canais em lote Planilha", "Cadastro manual"])
|
|
1926
1926
|
prompt_ids, prompt_labels = _tiktok_prompt_options()
|
|
1927
|
+
_, _, _, voice_options, _ = channel_default_options({})
|
|
1927
1928
|
with import_tab:
|
|
1928
1929
|
st.caption("A pesquisa consulta exclusivamente a página pública do TikTok. Não existe método alternativo nem utilização de API.")
|
|
1929
1930
|
st.divider()
|
|
@@ -1984,9 +1985,15 @@ def render_tiktok_channels():
|
|
|
1984
1985
|
language = st.selectbox("Idioma", list(LANGUAGE_CODES), format_func=language_label, key="tiktok_import_language")
|
|
1985
1986
|
niche = st.text_input("Nicho", key="tiktok_import_niche")
|
|
1986
1987
|
prompt = st.selectbox("Prompt Master padrão", prompt_ids, format_func=lambda item: prompt_labels.get(item, item), key="tiktok_import_prompt")
|
|
1988
|
+
voice = st.selectbox("Narrador/Voz Padrão", voice_options, format_func=lambda item: item or "Sem voz padrão", key="tiktok_import_voice")
|
|
1989
|
+
automation_on = st.toggle("Automação ON", value=False, key="tiktok_import_automation")
|
|
1990
|
+
automation_time = st.text_input("Horário diário (HH:MM)", value="00:00", key="tiktok_import_automation_time")
|
|
1987
1991
|
description = st.text_area("Descrição", value=str(imported.get("bio") or ""), key="tiktok_import_description")
|
|
1988
1992
|
if st.form_submit_button("Cadastrar canal TikTok", type="primary"):
|
|
1989
|
-
|
|
1993
|
+
if not valid_hhmm(automation_time):
|
|
1994
|
+
st.error("O horário diário deve estar no formato HH:MM.")
|
|
1995
|
+
st.stop()
|
|
1996
|
+
channel = create_channel(name.strip(), url.strip(), {"platform": "tiktok", "handle": handle.strip(), "language": language, "niche": niche.strip(), "description": description.strip(), "default_prompt_master": prompt, "prompt_master": prompt, "default_voice": voice, "voice": voice, "style_wide": "portrait", "video_aspect_ratio": "Portrait 9:16", "automation_on": automation_on, "automation_time": automation_time.strip(), "metrics_source": "tiktok_public_page", "subscriber_count": imported.get("subscriber_count"), "video_count": imported.get("video_count")})
|
|
1990
1997
|
avatar_url = _tiktok_avatar_url(imported)
|
|
1991
1998
|
update_channel(channel["id"], {"platform": "tiktok", "default_prompt_master": prompt, "prompt_master": prompt, "avatar_url": avatar_url, "thumbnail_url": avatar_url})
|
|
1992
1999
|
st.session_state.pop("tiktok_channel_import", None)
|
|
@@ -2009,6 +2016,7 @@ def render_tiktok_channels():
|
|
|
2009
2016
|
st.markdown(f"**{channel.get('name') or 'Perfil TikTok'}**")
|
|
2010
2017
|
st.caption(f"{channel.get('handle') or 'sem handle'} · {channel.get('url') or 'sem URL'}")
|
|
2011
2018
|
st.caption(channel.get("description") or "Perfil carregado da página pública do TikTok.")
|
|
2019
|
+
st.caption(f"Nicho: {channel.get('niche') or 'não configurado'}")
|
|
2012
2020
|
with card_cols[2]:
|
|
2013
2021
|
st.metric("Seguidores", format_metric_number(channel.get("subscriber_count")))
|
|
2014
2022
|
with card_cols[3]:
|
|
@@ -2027,11 +2035,20 @@ def render_tiktok_channels():
|
|
|
2027
2035
|
if st.button("Apagar card", key=f"tiktok_import_card_delete_{channel_id}"):
|
|
2028
2036
|
st.session_state[f"tiktok_delete_{channel_id}"] = True
|
|
2029
2037
|
st.rerun()
|
|
2030
|
-
st.caption("
|
|
2038
|
+
st.caption("Estilo wide: Portrait 9:16")
|
|
2039
|
+
st.caption(f"Automação: {'ligada' if automation_on else 'desligada'} · {automation_time}")
|
|
2040
|
+
_, _, _, card_voice_options, card_current_voice = channel_default_options(channel)
|
|
2041
|
+
selected_voice = st.selectbox("Narrador/Voz Padrão", card_voice_options, index=card_voice_options.index(card_current_voice) if card_current_voice in card_voice_options else 0, format_func=lambda item: item or "Sem voz padrão", key=f"tiktok_import_card_voice_{channel_id}")
|
|
2042
|
+
automation_on = st.toggle("Automação ON", value=bool(channel.get("automation_on", False)), key=f"tiktok_import_card_automation_{channel_id}")
|
|
2043
|
+
automation_time = st.text_input("Horário diário (HH:MM)", value=str(channel.get("automation_time") or "00:00"), key=f"tiktok_import_card_time_{channel_id}")
|
|
2031
2044
|
if st.button("Guardar", key=f"tiktok_import_card_save_{channel_id}", use_container_width=True, type="primary"):
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2045
|
+
if not valid_hhmm(automation_time):
|
|
2046
|
+
st.error("O horário diário deve estar no formato HH:MM.")
|
|
2047
|
+
else:
|
|
2048
|
+
update_channel(channel_id, {"platform": "tiktok", "default_prompt_master": selected_prompt, "prompt_master": selected_prompt, "default_voice": selected_voice, "voice": selected_voice, "video_aspect_ratio": "Portrait 9:16", "style_wide": "portrait", "automation_on": automation_on, "automation_time": automation_time.strip()})
|
|
2049
|
+
st.success("Dados do canal TikTok guardados.")
|
|
2050
|
+
st.rerun()
|
|
2051
|
+
st.caption("Estilo wide: Portrait 9:16")
|
|
2035
2052
|
niche_action = st.columns([1.0, 4.0])
|
|
2036
2053
|
with niche_action[0]:
|
|
2037
2054
|
if st.button("Editar nicho", key=f"tiktok_import_card_niche_{channel_id}"):
|
|
@@ -2062,11 +2079,18 @@ def render_tiktok_channels():
|
|
|
2062
2079
|
edit_url = st.text_input("URL pública", value=str(channel.get("url") or ""))
|
|
2063
2080
|
edit_handle = st.text_input("Handle", value=str(channel.get("handle") or ""))
|
|
2064
2081
|
edit_language = st.selectbox("Idioma do canal", list(LANGUAGE_CODES), index=list(LANGUAGE_CODES).index(language_code(channel.get("language") or "pt")), format_func=language_label)
|
|
2082
|
+
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")
|
|
2083
|
+
edit_automation = st.toggle("Automação ON", value=bool(channel.get("automation_on", False)), key=f"tiktok_edit_automation_{channel_id}")
|
|
2084
|
+
edit_time = st.text_input("Horário diário (HH:MM)", value=str(channel.get("automation_time") or "00:00"))
|
|
2085
|
+
st.caption("Estilo wide: Portrait 9:16")
|
|
2065
2086
|
edit_description = st.text_area("Descrição", value=str(channel.get("description") or ""))
|
|
2066
2087
|
if st.form_submit_button("Guardar edição", type="primary"):
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2088
|
+
if not valid_hhmm(edit_time):
|
|
2089
|
+
st.error("O horário diário deve estar no formato HH:MM.")
|
|
2090
|
+
else:
|
|
2091
|
+
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": "portrait", "video_aspect_ratio": "Portrait 9:16", "automation_on": edit_automation, "automation_time": edit_time.strip(), "description": edit_description.strip(), "platform": "tiktok"})
|
|
2092
|
+
st.session_state.pop(f"tiktok_edit_{channel_id}", None)
|
|
2093
|
+
st.rerun()
|
|
2070
2094
|
with st.expander("Últimos 10 vídeos publicados", expanded=False):
|
|
2071
2095
|
recent_videos = channel_videos_for(channel, limit=10)
|
|
2072
2096
|
if not recent_videos:
|
|
@@ -2096,7 +2120,7 @@ def render_tiktok_channels():
|
|
|
2096
2120
|
candidate = {"name": row.get("name", ""), "handle": row.get("handle", ""), "url": row.get("url", "")}
|
|
2097
2121
|
if find_duplicate_channel(candidate, channels):
|
|
2098
2122
|
continue
|
|
2099
|
-
created_channel = create_channel(str(row.get("name") or row.get("handle") or "TikTok"), str(row.get("url") or ""), {"platform": "tiktok", "handle": row.get("handle", ""), "language": language_code(row.get("language") or "pt"), "niche": row.get("niche", ""), "description": row.get("description", ""), "default_prompt_master": row.get("prompt_master", ""), "prompt_master": row.get("prompt_master", ""), "style_wide": "portrait", "video_aspect_ratio": "Portrait 9:16", "automation_time": row.get("automation_time") or "00:00"})
|
|
2123
|
+
created_channel = create_channel(str(row.get("name") or row.get("handle") or "TikTok"), str(row.get("url") or ""), {"platform": "tiktok", "handle": row.get("handle", ""), "language": language_code(row.get("language") or "pt"), "niche": row.get("niche", ""), "description": row.get("description", ""), "default_prompt_master": row.get("prompt_master", ""), "prompt_master": row.get("prompt_master", ""), "default_voice": row.get("voice", ""), "voice": row.get("voice", ""), "style_wide": "portrait", "video_aspect_ratio": "Portrait 9:16", "automation_on": bool(row.get("automation_on", False)), "automation_time": row.get("automation_time") or "00:00"})
|
|
2100
2124
|
update_channel(created_channel["id"], {"platform": "tiktok"})
|
|
2101
2125
|
created += 1
|
|
2102
2126
|
st.success(f"{created} canal(is) TikTok cadastrado(s).")
|
|
@@ -2106,7 +2130,7 @@ def render_tiktok_channels():
|
|
|
2106
2130
|
st.subheader(f"Canais TikTok cadastrados ({len(channels)})")
|
|
2107
2131
|
if channels:
|
|
2108
2132
|
st.dataframe(
|
|
2109
|
-
[{k: c.get(v, "") for k, v in {"URL canal": "url", "Nome canal": "name", "Handle canal": "handle", "Idioma": "language", "Nicho": "niche", "Prompt Master": "default_prompt_master", "Proporção": "video_aspect_ratio", "Activo": "active", "Descrição": "description"}.items()} for c in channels],
|
|
2133
|
+
[{k: c.get(v, "") for k, v in {"URL canal": "url", "Nome canal": "name", "Handle canal": "handle", "Idioma": "language", "Nicho": "niche", "Prompt Master": "default_prompt_master", "Narrador/Voz Padrão": "default_voice", "Estilo wide": "style_wide", "Automação ligada": "automation_on", "Horário diário (HH:MM)": "automation_time", "Proporção": "video_aspect_ratio", "Activo": "active", "Descrição": "description"}.items()} for c in channels],
|
|
2110
2134
|
use_container_width=True,
|
|
2111
2135
|
hide_index=True,
|
|
2112
2136
|
height=360,
|
|
@@ -2121,10 +2145,17 @@ def render_tiktok_channels():
|
|
|
2121
2145
|
language = st.selectbox("Idioma", list(LANGUAGE_CODES), format_func=language_label, key="tiktok_manual_channel_language")
|
|
2122
2146
|
niche = st.text_input("Nicho", key="tiktok_manual_channel_niche")
|
|
2123
2147
|
prompt = st.selectbox("Prompt Master padrão", prompt_ids, format_func=lambda item: prompt_labels.get(item, item), key="tiktok_manual_channel_prompt")
|
|
2148
|
+
voice = st.selectbox("Narrador/Voz Padrão", voice_options, format_func=lambda item: item or "Sem voz padrão", key="tiktok_manual_channel_voice")
|
|
2149
|
+
automation_on = st.toggle("Automação ON", value=False, key="tiktok_manual_channel_automation")
|
|
2150
|
+
automation_time = st.text_input("Horário diário (HH:MM)", value="00:00", key="tiktok_manual_channel_automation_time")
|
|
2151
|
+
st.caption("Estilo wide: Portrait 9:16")
|
|
2124
2152
|
description = st.text_area("Descrição", key="tiktok_manual_channel_description")
|
|
2125
2153
|
if st.form_submit_button("Guardar canal manual", type="primary"):
|
|
2126
2154
|
reference = normalize_tiktok_reference(url or handle)
|
|
2127
|
-
|
|
2155
|
+
if not valid_hhmm(automation_time):
|
|
2156
|
+
st.error("O horário diário deve estar no formato HH:MM.")
|
|
2157
|
+
st.stop()
|
|
2158
|
+
channel = create_channel(name.strip() or reference["username"], reference["url"], {"platform": "tiktok", "handle": reference["handle"], "language": language, "niche": niche.strip(), "description": description.strip(), "default_prompt_master": prompt, "prompt_master": prompt, "default_voice": voice, "voice": voice, "style_wide": "portrait", "video_aspect_ratio": "Portrait 9:16", "automation_on": automation_on, "automation_time": automation_time.strip()})
|
|
2128
2159
|
update_channel(channel["id"], {"platform": "tiktok"})
|
|
2129
2160
|
st.success("Canal TikTok cadastrado.")
|
|
2130
2161
|
st.rerun()
|
package/package.json
CHANGED