@danhachuel/thunderbolt 0.4.89 → 0.4.91
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 +25 -44
- package/package.json +1 -1
package/app/main.py
CHANGED
|
@@ -1311,7 +1311,6 @@ def render_channel_edit_form(channel: dict, youtube_account_ids: list[str], yout
|
|
|
1311
1311
|
edited_language = st.selectbox("Idioma do roteiro", language_options, index=language_options.index(normalize_video_language(channel.get("language") or "pt")) if normalize_video_language(channel.get("language") or "pt") in language_options else 0, format_func=video_language_label)
|
|
1312
1312
|
edited_style = 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)
|
|
1313
1313
|
edited_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 "Landscape 16:9")) if str(channel.get("video_aspect_ratio") or "Landscape 16:9") in CHANNEL_ASPECT_RATIO_OPTIONS else 0)
|
|
1314
|
-
edited_format = st.selectbox("Formato", CHANNEL_FORMAT_OPTIONS, index=CHANNEL_FORMAT_OPTIONS.index(str(channel.get("format") or "wide")) if str(channel.get("format") or "wide") in CHANNEL_FORMAT_OPTIONS else 0)
|
|
1315
1314
|
edited_niche = st.text_input("Nicho", value=str(channel.get("niche") or channel_niche_label(channel) if channel_niche_label(channel) != "SEM NICHO CONFIGURADO" else "") )
|
|
1316
1315
|
with edit_cols[1]:
|
|
1317
1316
|
edited_blueprint = st.selectbox("Blueprint Padrão", blueprint_ids, index=blueprint_ids.index(current_blueprint) if current_blueprint in blueprint_ids else 0, format_func=lambda item: blueprint_labels.get(item, item or "Sem Blueprint padrão"))
|
|
@@ -1333,7 +1332,7 @@ def render_channel_edit_form(channel: dict, youtube_account_ids: list[str], yout
|
|
|
1333
1332
|
else:
|
|
1334
1333
|
update_channel(channel_id, {
|
|
1335
1334
|
"name": edited_name.strip(), "url": edited_url.strip(), "handle": edited_handle.strip(), "language": edited_language,
|
|
1336
|
-
"style_wide": channel_video_source_storage(edited_style), "video_aspect_ratio": edited_aspect,
|
|
1335
|
+
"style_wide": channel_video_source_storage(edited_style), "video_aspect_ratio": edited_aspect,
|
|
1337
1336
|
"niche": edited_niche.strip(), "reference_channels": [item.strip() for item in re.split(r"[,|]", edited_niche) if item.strip()],
|
|
1338
1337
|
"default_blueprint_id": edited_blueprint.strip(), "blueprint_id": edited_blueprint.strip(),
|
|
1339
1338
|
"default_voice": edited_voice.strip(), "voice": edited_voice.strip(),
|
|
@@ -2004,7 +2003,6 @@ def render_tiktok_channels():
|
|
|
2004
2003
|
language = st.selectbox("Idioma do roteiro", VIDEO_LANGUAGE_SELECTION_OPTIONS, format_func=video_language_label, key="tiktok_import_language")
|
|
2005
2004
|
source_value = st.selectbox("Fonte do vídeo", WIDE_STYLE_OPTIONS, key="tiktok_import_source")
|
|
2006
2005
|
aspect_value = st.selectbox("Proporção do vídeo", CHANNEL_ASPECT_RATIO_OPTIONS, index=1, key="tiktok_import_aspect")
|
|
2007
|
-
format_value = st.selectbox("Formato", CHANNEL_FORMAT_OPTIONS, index=1, key="tiktok_import_format")
|
|
2008
2006
|
niche = st.text_input("Nicho", key="tiktok_import_niche")
|
|
2009
2007
|
prompt = st.selectbox("Prompt Master padrão", prompt_ids, format_func=lambda item: prompt_labels.get(item, item), key="tiktok_import_prompt")
|
|
2010
2008
|
voice = st.selectbox("Narrador/Voz Padrão", voice_options, format_func=lambda item: item or "Sem voz padrão", key="tiktok_import_voice")
|
|
@@ -2015,7 +2013,7 @@ def render_tiktok_channels():
|
|
|
2015
2013
|
if not valid_hhmm(automation_time):
|
|
2016
2014
|
st.error("O horário diário deve estar no formato HH:MM.")
|
|
2017
2015
|
st.stop()
|
|
2018
|
-
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": channel_video_source_storage(source_value), "video_aspect_ratio": aspect_value, "format":
|
|
2016
|
+
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": channel_video_source_storage(source_value), "video_aspect_ratio": aspect_value, "format": "Shorts", "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")})
|
|
2019
2017
|
avatar_url = _tiktok_avatar_url(imported)
|
|
2020
2018
|
update_channel(channel["id"], {"platform": "tiktok", "default_prompt_master": prompt, "prompt_master": prompt, "avatar_url": avatar_url, "thumbnail_url": avatar_url})
|
|
2021
2019
|
st.session_state.pop("tiktok_channel_import", None)
|
|
@@ -2027,7 +2025,7 @@ def render_tiktok_channels():
|
|
|
2027
2025
|
for channel in channels:
|
|
2028
2026
|
channel_id = str(channel.get("id") or "")
|
|
2029
2027
|
with st.container(border=True):
|
|
2030
|
-
card_cols = st.columns([0.7,
|
|
2028
|
+
card_cols = st.columns([0.7, 3.45, 1.15, 1.15, 1.45, 1.55], gap="small")
|
|
2031
2029
|
with card_cols[0]:
|
|
2032
2030
|
avatar_url = _tiktok_avatar_url(channel)
|
|
2033
2031
|
if avatar_url:
|
|
@@ -2043,9 +2041,6 @@ def render_tiktok_channels():
|
|
|
2043
2041
|
st.metric("Seguidores", format_metric_number(channel.get("subscriber_count")))
|
|
2044
2042
|
with card_cols[3]:
|
|
2045
2043
|
st.metric("Curtidas", format_metric_number(channel.get("likes_count")))
|
|
2046
|
-
with card_cols[4]:
|
|
2047
|
-
current_prompt = str(channel.get("default_prompt_master") or channel.get("prompt_master") or "")
|
|
2048
|
-
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}")
|
|
2049
2044
|
with card_cols[5]:
|
|
2050
2045
|
active = st.toggle("Activo", value=bool(channel.get("active", True)), key=f"tiktok_import_card_active_{channel_id}")
|
|
2051
2046
|
if active != bool(channel.get("active", True)):
|
|
@@ -2057,27 +2052,20 @@ def render_tiktok_channels():
|
|
|
2057
2052
|
if st.button("Apagar card", key=f"tiktok_import_card_delete_{channel_id}"):
|
|
2058
2053
|
st.session_state[f"tiktok_delete_{channel_id}"] = True
|
|
2059
2054
|
st.rerun()
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
st.rerun()
|
|
2075
|
-
st.caption(f"Fonte do vídeo: {channel_video_source_value(channel.get('style_wide'))}")
|
|
2076
|
-
niche_action = st.columns([1.0, 4.0])
|
|
2077
|
-
with niche_action[0]:
|
|
2078
|
-
if st.button("Editar nicho", key=f"tiktok_import_card_niche_{channel_id}"):
|
|
2079
|
-
st.session_state[f"tiktok_niche_{channel_id}"] = not st.session_state.get(f"tiktok_niche_{channel_id}", False)
|
|
2080
|
-
st.rerun()
|
|
2055
|
+
summary_cols = st.columns(4, gap="medium")
|
|
2056
|
+
with summary_cols[0]:
|
|
2057
|
+
st.markdown("**Blueprint Padrão**")
|
|
2058
|
+
st.caption(prompt_labels.get(str(channel.get("default_prompt_master") or channel.get("prompt_master") or ""), "Sem Blueprint padrão"))
|
|
2059
|
+
with summary_cols[1]:
|
|
2060
|
+
st.markdown("**Nicho**")
|
|
2061
|
+
st.caption(channel_niche_label(channel))
|
|
2062
|
+
with summary_cols[2]:
|
|
2063
|
+
st.markdown("**Narrador/Voz Padrão**")
|
|
2064
|
+
st.caption(str(channel.get("default_voice") or channel.get("voice") or "Sem voz padrão"))
|
|
2065
|
+
with summary_cols[3]:
|
|
2066
|
+
st.markdown("**Idioma**")
|
|
2067
|
+
st.caption(video_language_label(normalize_video_language(channel.get("language") or "pt")))
|
|
2068
|
+
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'}")
|
|
2081
2069
|
if st.session_state.get(f"tiktok_delete_{channel_id}"):
|
|
2082
2070
|
st.warning("Apagar este canal TikTok e os dados locais associados?")
|
|
2083
2071
|
confirm_cols = st.columns(2)
|
|
@@ -2090,13 +2078,6 @@ def render_tiktok_channels():
|
|
|
2090
2078
|
if st.button("Cancelar", key=f"tiktok_cancel_delete_{channel_id}"):
|
|
2091
2079
|
st.session_state.pop(f"tiktok_delete_{channel_id}", None)
|
|
2092
2080
|
st.rerun()
|
|
2093
|
-
if st.session_state.get(f"tiktok_niche_{channel_id}"):
|
|
2094
|
-
with st.form(f"tiktok_niche_form_{channel_id}"):
|
|
2095
|
-
niche_value = st.text_input("Nicho do canal", value=str(channel.get("niche") or ""))
|
|
2096
|
-
if st.form_submit_button("Guardar nicho", type="primary"):
|
|
2097
|
-
update_channel(channel_id, {"niche": niche_value.strip(), "reference_channels": [niche_value.strip()] if niche_value.strip() else []})
|
|
2098
|
-
st.session_state.pop(f"tiktok_niche_{channel_id}", None)
|
|
2099
|
-
st.rerun()
|
|
2100
2081
|
if st.session_state.get(f"tiktok_edit_{channel_id}"):
|
|
2101
2082
|
with st.form(f"tiktok_edit_form_{channel_id}"):
|
|
2102
2083
|
edit_name = st.text_input("Nome do canal", value=str(channel.get("name") or ""))
|
|
@@ -2105,7 +2086,7 @@ def render_tiktok_channels():
|
|
|
2105
2086
|
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)
|
|
2106
2087
|
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)
|
|
2107
2088
|
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)
|
|
2108
|
-
|
|
2089
|
+
edit_niche = st.text_input("Nicho", value=str(channel.get("niche") or ""))
|
|
2109
2090
|
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")
|
|
2110
2091
|
edit_automation = st.toggle("Automação ON", value=bool(channel.get("automation_on", False)), key=f"tiktok_edit_automation_{channel_id}")
|
|
2111
2092
|
edit_time = st.text_input("Horário diário (HH:MM)", value=str(channel.get("automation_time") or "00:00"))
|
|
@@ -2115,7 +2096,7 @@ def render_tiktok_channels():
|
|
|
2115
2096
|
if not valid_hhmm(edit_time):
|
|
2116
2097
|
st.error("O horário diário deve estar no formato HH:MM.")
|
|
2117
2098
|
else:
|
|
2118
|
-
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, "
|
|
2099
|
+
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"})
|
|
2119
2100
|
st.session_state.pop(f"tiktok_edit_{channel_id}", None)
|
|
2120
2101
|
st.rerun()
|
|
2121
2102
|
with st.expander("Últimos 10 vídeos publicados", expanded=False):
|
|
@@ -2172,7 +2153,6 @@ def render_tiktok_channels():
|
|
|
2172
2153
|
language = st.selectbox("Idioma do roteiro", VIDEO_LANGUAGE_SELECTION_OPTIONS, format_func=video_language_label, key="tiktok_manual_channel_language")
|
|
2173
2154
|
source_value = st.selectbox("Fonte do vídeo", WIDE_STYLE_OPTIONS, key="tiktok_manual_channel_source")
|
|
2174
2155
|
aspect_value = st.selectbox("Proporção do vídeo", CHANNEL_ASPECT_RATIO_OPTIONS, index=1, key="tiktok_manual_channel_aspect")
|
|
2175
|
-
format_value = st.selectbox("Formato", CHANNEL_FORMAT_OPTIONS, index=1, key="tiktok_manual_channel_format")
|
|
2176
2156
|
niche = st.text_input("Nicho", key="tiktok_manual_channel_niche")
|
|
2177
2157
|
prompt = st.selectbox("Prompt Master padrão", prompt_ids, format_func=lambda item: prompt_labels.get(item, item), key="tiktok_manual_channel_prompt")
|
|
2178
2158
|
voice = st.selectbox("Narrador/Voz Padrão", voice_options, format_func=lambda item: item or "Sem voz padrão", key="tiktok_manual_channel_voice")
|
|
@@ -2264,7 +2244,6 @@ def render_channels():
|
|
|
2264
2244
|
language = st.selectbox("Idioma do roteiro", VIDEO_LANGUAGE_SELECTION_OPTIONS, index=VIDEO_LANGUAGE_SELECTION_OPTIONS.index(normalize_video_language(imported.get("language") or "pt")) if normalize_video_language(imported.get("language") or "pt") in VIDEO_LANGUAGE_SELECTION_OPTIONS else 0, format_func=video_language_label, key="yt_import_language")
|
|
2265
2245
|
style = st.selectbox("Fonte do vídeo", WIDE_STYLE_OPTIONS, index=0, key="yt_import_style")
|
|
2266
2246
|
video_aspect_ratio = st.selectbox("Proporção do vídeo", CHANNEL_ASPECT_RATIO_OPTIONS, key="yt_import_aspect_ratio")
|
|
2267
|
-
video_format = st.selectbox("Formato", CHANNEL_FORMAT_OPTIONS, key="yt_import_format")
|
|
2268
2247
|
blueprint = st.selectbox("Blueprint Padrão", blueprint_ids, index=blueprint_ids.index(imported_blueprint) if imported_blueprint in blueprint_ids else 0, format_func=lambda item: blueprint_labels.get(item, item or "Sem Blueprint padrão"), key="yt_import_blueprint")
|
|
2269
2248
|
voice_options = voice_catalog(imported.get("default_voice") or imported.get("voice", ""))
|
|
2270
2249
|
current_voice = imported.get("default_voice") or imported.get("voice", "")
|
|
@@ -2294,7 +2273,7 @@ def render_channels():
|
|
|
2294
2273
|
"niche": niche.strip(),
|
|
2295
2274
|
"reference_channels": [item.strip() for item in re.split(r"[,|]", niche) if item.strip()],
|
|
2296
2275
|
"language": language,
|
|
2297
|
-
"style_wide": channel_video_source_storage(style), "video_aspect_ratio": video_aspect_ratio, "format":
|
|
2276
|
+
"style_wide": channel_video_source_storage(style), "video_aspect_ratio": video_aspect_ratio, "format": "wide",
|
|
2298
2277
|
"blueprint_id": blueprint.strip(),
|
|
2299
2278
|
"default_blueprint_id": blueprint.strip(),
|
|
2300
2279
|
"default_voice": voice.strip(),
|
|
@@ -5015,15 +4994,14 @@ def render_tiktok_automation():
|
|
|
5015
4994
|
with default_cols[3]:
|
|
5016
4995
|
prompt = st.selectbox("Prompt Master Padrão", prompt_ids, index=prompt_ids.index(channel.get("default_prompt_master", "")) if channel.get("default_prompt_master", "") in prompt_ids else 0, format_func=lambda item: prompt_labels.get(item, item), key=f"tiktok_automation_prompt_{channel_id}")
|
|
5017
4996
|
with default_cols[4]:
|
|
5018
|
-
st.
|
|
5019
|
-
st.caption("TikTok / Shorts")
|
|
4997
|
+
automation_format = st.selectbox("Formato", CHANNEL_FORMAT_OPTIONS, index=CHANNEL_FORMAT_OPTIONS.index(str(channel.get("format") or "Shorts")) if str(channel.get("format") or "Shorts") in CHANNEL_FORMAT_OPTIONS else 1, key=f"tiktok_automation_format_{channel_id}")
|
|
5020
4998
|
with default_cols[5]:
|
|
5021
4999
|
if st.button("Guardar", key=f"tiktok_automation_save_{channel_id}", use_container_width=True, type="primary"):
|
|
5022
5000
|
if not valid_hhmm(schedule_time):
|
|
5023
5001
|
st.error("Use o formato HH:MM, por exemplo 08:30.")
|
|
5024
5002
|
else:
|
|
5025
5003
|
avatar_url = _tiktok_avatar_url(channel)
|
|
5026
|
-
update_channel(channel_id, {"automation_on": bool(enabled), "automation_time": schedule_time.strip(), "default_prompt_master": prompt, "prompt_master": prompt, "platform": "tiktok", "video_aspect_ratio": "Portrait 9:16", "style_wide": "portrait", "avatar_url": avatar_url, "thumbnail_url": avatar_url})
|
|
5004
|
+
update_channel(channel_id, {"automation_on": bool(enabled), "automation_time": schedule_time.strip(), "default_prompt_master": prompt, "prompt_master": prompt, "platform": "tiktok", "format": automation_format, "video_aspect_ratio": "Portrait 9:16", "style_wide": "portrait", "avatar_url": avatar_url, "thumbnail_url": avatar_url})
|
|
5027
5005
|
st.success("Automação TikTok guardada.")
|
|
5028
5006
|
st.rerun()
|
|
5029
5007
|
st.divider()
|
|
@@ -5118,6 +5096,8 @@ def render_automation():
|
|
|
5118
5096
|
st.markdown("**Thumbnail Blueprint**")
|
|
5119
5097
|
st.caption(str(paired_thumbnail.get("name") or "Generic_Thumbnail_Blueprint"))
|
|
5120
5098
|
with default_cols[4]:
|
|
5099
|
+
automation_format = st.selectbox("Formato", CHANNEL_FORMAT_OPTIONS, index=CHANNEL_FORMAT_OPTIONS.index(str(channel.get("format") or "wide")) if str(channel.get("format") or "wide") in CHANNEL_FORMAT_OPTIONS else 0, key=f"automation_format_{channel_id}")
|
|
5100
|
+
with default_cols[5]:
|
|
5121
5101
|
automation_voice = st.selectbox(
|
|
5122
5102
|
"Narrador/Voz Padrão",
|
|
5123
5103
|
voice_options,
|
|
@@ -5133,6 +5113,7 @@ def render_automation():
|
|
|
5133
5113
|
update_channel(channel_id, {
|
|
5134
5114
|
"automation_on": bool(enabled),
|
|
5135
5115
|
"automation_time": schedule_time.strip(),
|
|
5116
|
+
"format": automation_format,
|
|
5136
5117
|
})
|
|
5137
5118
|
set_channel_defaults(channel_id, automation_blueprint, automation_voice)
|
|
5138
5119
|
paired_id = str(paired_thumbnail.get("id") or "Generic_Thumbnail_Blueprint")
|
package/package.json
CHANGED