@danhachuel/thunderbolt 0.4.46 → 0.4.48
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 +61 -12
- package/package.json +1 -1
package/app/main.py
CHANGED
|
@@ -1858,6 +1858,16 @@ def _tiktok_channel_records() -> list[dict[str, Any]]:
|
|
|
1858
1858
|
return [channel for channel in read_json("channels.json", []) if isinstance(channel, dict) and channel.get("platform") == "tiktok"]
|
|
1859
1859
|
|
|
1860
1860
|
|
|
1861
|
+
def _tiktok_avatar_url(value: dict[str, Any]) -> str:
|
|
1862
|
+
for key in ("avatar_url", "thumbnail_url", "profile_image_url", "avatar", "image_url"):
|
|
1863
|
+
candidate = value.get(key)
|
|
1864
|
+
if isinstance(candidate, dict):
|
|
1865
|
+
candidate = candidate.get("url") or candidate.get("src")
|
|
1866
|
+
if str(candidate or "").strip():
|
|
1867
|
+
return str(candidate).strip()
|
|
1868
|
+
return ""
|
|
1869
|
+
|
|
1870
|
+
|
|
1861
1871
|
def _tiktok_prompt_options() -> tuple[list[str], dict[str, str]]:
|
|
1862
1872
|
files = list_prompt_master_files()
|
|
1863
1873
|
labels = {path.name: get_display_name("prompt_masters", path, path.stem) for path in files}
|
|
@@ -1873,6 +1883,40 @@ def render_tiktok_channels():
|
|
|
1873
1883
|
prompt_ids, prompt_labels = _tiktok_prompt_options()
|
|
1874
1884
|
with import_tab:
|
|
1875
1885
|
st.caption("A pesquisa consulta exclusivamente a página pública do TikTok. Não existe método alternativo nem utilização de API.")
|
|
1886
|
+
st.subheader(f"Canais TikTok cadastrados ({len(channels)})")
|
|
1887
|
+
if channels:
|
|
1888
|
+
for channel in channels:
|
|
1889
|
+
channel_id = str(channel.get("id") or "")
|
|
1890
|
+
with st.container(border=True):
|
|
1891
|
+
card_cols = st.columns([0.7, 2.3, 1.0, 1.0, 1.35, 1.25])
|
|
1892
|
+
with card_cols[0]:
|
|
1893
|
+
avatar_url = _tiktok_avatar_url(channel)
|
|
1894
|
+
if avatar_url:
|
|
1895
|
+
st.image(avatar_url, width=58)
|
|
1896
|
+
else:
|
|
1897
|
+
st.markdown("### TT")
|
|
1898
|
+
with card_cols[1]:
|
|
1899
|
+
st.markdown(f"**{channel.get('name') or 'Perfil TikTok'}**")
|
|
1900
|
+
st.caption(f"{channel.get('handle') or 'sem handle'} · {channel.get('url') or 'sem URL'}")
|
|
1901
|
+
st.caption(channel.get("description") or "Perfil carregado da página pública do TikTok.")
|
|
1902
|
+
with card_cols[2]:
|
|
1903
|
+
st.metric("Seguidores", format_metric_number(channel.get("subscriber_count")))
|
|
1904
|
+
with card_cols[3]:
|
|
1905
|
+
st.metric("Curtidas", format_metric_number(channel.get("likes_count")))
|
|
1906
|
+
with card_cols[4]:
|
|
1907
|
+
current_prompt = str(channel.get("default_prompt_master") or channel.get("prompt_master") or "")
|
|
1908
|
+
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}")
|
|
1909
|
+
with card_cols[5]:
|
|
1910
|
+
st.caption("Formato")
|
|
1911
|
+
st.write("Portrait 9:16")
|
|
1912
|
+
if st.button("Guardar", key=f"tiktok_import_card_save_{channel_id}", use_container_width=True, type="primary"):
|
|
1913
|
+
update_channel(channel_id, {"platform": "tiktok", "default_prompt_master": selected_prompt, "prompt_master": selected_prompt, "video_aspect_ratio": "Portrait 9:16", "style_wide": "portrait"})
|
|
1914
|
+
st.success("Prompt Master do canal guardado.")
|
|
1915
|
+
st.rerun()
|
|
1916
|
+
else:
|
|
1917
|
+
st.info("Ainda não existem canais TikTok cadastrados. Use o campo abaixo para pesquisar e cadastrar um perfil público.")
|
|
1918
|
+
st.divider()
|
|
1919
|
+
st.subheader("Importar novo canal pela página pública")
|
|
1876
1920
|
source = st.text_input("URL pública ou @handle", placeholder="https://www.tiktok.com/@conta", key="tiktok_channel_source")
|
|
1877
1921
|
lookup_cols = st.columns([1, 1])
|
|
1878
1922
|
with lookup_cols[0]:
|
|
@@ -1893,7 +1937,7 @@ def render_tiktok_channels():
|
|
|
1893
1937
|
with st.container(border=True):
|
|
1894
1938
|
profile_cols = st.columns([0.8, 2.2, 1, 1, 1])
|
|
1895
1939
|
with profile_cols[0]:
|
|
1896
|
-
avatar =
|
|
1940
|
+
avatar = _tiktok_avatar_url(imported)
|
|
1897
1941
|
if avatar:
|
|
1898
1942
|
st.image(avatar, width=82)
|
|
1899
1943
|
else:
|
|
@@ -1932,7 +1976,8 @@ def render_tiktok_channels():
|
|
|
1932
1976
|
description = st.text_area("Descrição", value=str(imported.get("bio") or ""), key="tiktok_import_description")
|
|
1933
1977
|
if st.form_submit_button("Cadastrar canal TikTok", type="primary"):
|
|
1934
1978
|
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, "style_wide": "portrait", "video_aspect_ratio": "Portrait 9:16", "metrics_source": "tiktok_public_page", "subscriber_count": imported.get("subscriber_count"), "video_count": imported.get("video_count")})
|
|
1935
|
-
|
|
1979
|
+
avatar_url = _tiktok_avatar_url(imported)
|
|
1980
|
+
update_channel(channel["id"], {"platform": "tiktok", "default_prompt_master": prompt, "prompt_master": prompt, "avatar_url": avatar_url, "thumbnail_url": avatar_url})
|
|
1936
1981
|
st.session_state.pop("tiktok_channel_import", None)
|
|
1937
1982
|
st.success(f"Canal {channel['name']} cadastrado.")
|
|
1938
1983
|
st.rerun()
|
|
@@ -1953,6 +1998,17 @@ def render_tiktok_channels():
|
|
|
1953
1998
|
st.success(f"{created} canal(is) TikTok cadastrado(s).")
|
|
1954
1999
|
st.rerun()
|
|
1955
2000
|
st.caption("A planilha usa os campos de URL, nome, handle, idioma, nicho, Prompt Master, descrição e horário.")
|
|
2001
|
+
st.divider()
|
|
2002
|
+
st.subheader(f"Canais TikTok cadastrados ({len(channels)})")
|
|
2003
|
+
if channels:
|
|
2004
|
+
st.dataframe(
|
|
2005
|
+
[{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],
|
|
2006
|
+
use_container_width=True,
|
|
2007
|
+
hide_index=True,
|
|
2008
|
+
height=360,
|
|
2009
|
+
)
|
|
2010
|
+
else:
|
|
2011
|
+
st.info("Ainda não existem canais TikTok cadastrados.")
|
|
1956
2012
|
with manual_tab:
|
|
1957
2013
|
with st.form("tiktok_channel_manual_form"):
|
|
1958
2014
|
url = st.text_input("URL canal", placeholder="https://www.tiktok.com/@conta", key="tiktok_manual_channel_url")
|
|
@@ -1968,14 +2024,6 @@ def render_tiktok_channels():
|
|
|
1968
2024
|
update_channel(channel["id"], {"platform": "tiktok"})
|
|
1969
2025
|
st.success("Canal TikTok cadastrado.")
|
|
1970
2026
|
st.rerun()
|
|
1971
|
-
st.divider()
|
|
1972
|
-
st.subheader(f"Canais TikTok cadastrados ({len(channels)})")
|
|
1973
|
-
if channels:
|
|
1974
|
-
st.dataframe([{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], use_container_width=True, hide_index=True, height=360)
|
|
1975
|
-
else:
|
|
1976
|
-
st.info("Ainda não existem canais TikTok cadastrados.")
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
2027
|
def render_channels():
|
|
1980
2028
|
st.title("Canais Youtube")
|
|
1981
2029
|
st.caption("Escolha entre importar dados públicos do YouTube ou preencher o canal manualmente.")
|
|
@@ -4607,7 +4655,7 @@ def render_tiktok_automation():
|
|
|
4607
4655
|
with st.container(border=True):
|
|
4608
4656
|
header_cols = st.columns([0.55, 2.35, 1.35, 1.5, 1.35])
|
|
4609
4657
|
with header_cols[0]:
|
|
4610
|
-
profile_image =
|
|
4658
|
+
profile_image = _tiktok_avatar_url(channel)
|
|
4611
4659
|
if profile_image:
|
|
4612
4660
|
st.image(profile_image, width=48)
|
|
4613
4661
|
else:
|
|
@@ -4639,7 +4687,8 @@ def render_tiktok_automation():
|
|
|
4639
4687
|
if not valid_hhmm(schedule_time):
|
|
4640
4688
|
st.error("Use o formato HH:MM, por exemplo 08:30.")
|
|
4641
4689
|
else:
|
|
4642
|
-
|
|
4690
|
+
avatar_url = _tiktok_avatar_url(channel)
|
|
4691
|
+
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})
|
|
4643
4692
|
st.success("Automação TikTok guardada.")
|
|
4644
4693
|
st.rerun()
|
|
4645
4694
|
|
package/package.json
CHANGED