@danhachuel/thunderbolt 0.4.54 → 0.4.55

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.
Files changed (2) hide show
  1. package/app/main.py +67 -66
  2. package/package.json +1 -1
package/app/main.py CHANGED
@@ -1883,6 +1883,73 @@ def render_tiktok_channels():
1883
1883
  prompt_ids, prompt_labels = _tiktok_prompt_options()
1884
1884
  with import_tab:
1885
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.divider()
1887
+ st.subheader("Importar novo canal pela página pública")
1888
+ source = st.text_input("URL pública ou @handle", placeholder="https://www.tiktok.com/@conta", key="tiktok_channel_source")
1889
+ lookup_cols = st.columns([1, 1])
1890
+ with lookup_cols[0]:
1891
+ if st.button("Buscar no Tiktok", type="primary", use_container_width=True, key="tiktok_channel_lookup"):
1892
+ result = fetch_public_tiktok_profile(source)
1893
+ st.session_state["tiktok_channel_import"] = {"ok": result.ok, "message": result.message, "data": result.data}
1894
+ with lookup_cols[1]:
1895
+ if st.button("Limpar importação", use_container_width=True, key="tiktok_channel_clear"):
1896
+ for key in ("tiktok_channel_import", "tiktok_import_language", "tiktok_import_niche", "tiktok_import_prompt", "tiktok_import_description"):
1897
+ st.session_state.pop(key, None)
1898
+ st.rerun()
1899
+ imported_state = st.session_state.get("tiktok_channel_import", {})
1900
+ if imported_state.get("message"):
1901
+ (st.success if imported_state.get("ok") else st.warning)(imported_state["message"])
1902
+ imported = imported_state.get("data") if imported_state.get("ok") else {}
1903
+ if imported:
1904
+ st.caption("Dados encontrados na página pública. Reveja os cartões e edite os campos antes de guardar.")
1905
+ with st.container(border=True):
1906
+ profile_cols = st.columns([0.8, 2.2, 1, 1, 1])
1907
+ with profile_cols[0]:
1908
+ avatar = _tiktok_avatar_url(imported)
1909
+ if avatar:
1910
+ st.image(avatar, width=82)
1911
+ else:
1912
+ st.markdown("### TT")
1913
+ with profile_cols[1]:
1914
+ st.markdown(f"### {imported.get('name') or imported.get('username') or 'Perfil TikTok'}")
1915
+ st.write(imported.get("handle") or f"@{imported.get('username', '')}")
1916
+ st.caption(imported.get("public_url") or imported.get("url") or "Página pública")
1917
+ with profile_cols[2]:
1918
+ st.metric("Seguidores", format_metric_number(imported.get("subscriber_count")))
1919
+ with profile_cols[3]:
1920
+ st.metric("Curtidas", format_metric_number(imported.get("likes_count")))
1921
+ with profile_cols[4]:
1922
+ st.metric("Vídeos", format_metric_number(imported.get("video_count")))
1923
+ if imported.get("bio"):
1924
+ st.caption(f"Descrição pública: {imported['bio']}")
1925
+ with st.container(border=True):
1926
+ st.markdown("**Dados do canal a cadastrar**")
1927
+ data_cols = st.columns(3)
1928
+ with data_cols[0]:
1929
+ st.write(f"**Nome:** {imported.get('name') or imported.get('username') or '—'}")
1930
+ st.write(f"**Handle:** {imported.get('handle') or '—'}")
1931
+ with data_cols[1]:
1932
+ st.write(f"**URL:** {imported.get('public_url') or imported.get('url') or '—'}")
1933
+ st.write(f"**Origem:** {imported.get('metrics_source') or 'tiktok_public_page'}")
1934
+ with data_cols[2]:
1935
+ st.write("**Formato:** Portrait 9:16")
1936
+ st.write("**Consulta:** Página pública")
1937
+ with st.form("tiktok_channel_import_form"):
1938
+ name = st.text_input("Nome canal", value=str(imported.get("name") or imported.get("username") or ""))
1939
+ url = st.text_input("URL canal", value=str(imported.get("public_url") or imported.get("url") or ""))
1940
+ handle = st.text_input("Handle canal", value=str(imported.get("handle") or ""))
1941
+ language = st.selectbox("Idioma", list(LANGUAGE_CODES), format_func=language_label, key="tiktok_import_language")
1942
+ niche = st.text_input("Nicho", key="tiktok_import_niche")
1943
+ prompt = st.selectbox("Prompt Master padrão", prompt_ids, format_func=lambda item: prompt_labels.get(item, item), key="tiktok_import_prompt")
1944
+ description = st.text_area("Descrição", value=str(imported.get("bio") or ""), key="tiktok_import_description")
1945
+ if st.form_submit_button("Cadastrar canal TikTok", type="primary"):
1946
+ 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")})
1947
+ avatar_url = _tiktok_avatar_url(imported)
1948
+ update_channel(channel["id"], {"platform": "tiktok", "default_prompt_master": prompt, "prompt_master": prompt, "avatar_url": avatar_url, "thumbnail_url": avatar_url})
1949
+ st.session_state.pop("tiktok_channel_import", None)
1950
+ st.success(f"Canal {channel['name']} cadastrado.")
1951
+ st.rerun()
1952
+ st.divider()
1886
1953
  st.subheader(f"Canais TikTok cadastrados ({len(channels)})")
1887
1954
  if channels:
1888
1955
  for channel in channels:
@@ -1975,72 +2042,6 @@ def render_tiktok_channels():
1975
2042
  st.caption(str(recent_video.get("status") or "publicado").title())
1976
2043
  else:
1977
2044
  st.info("Ainda não existem canais TikTok cadastrados. Use o campo abaixo para pesquisar e cadastrar um perfil público.")
1978
- st.divider()
1979
- st.subheader("Importar novo canal pela página pública")
1980
- source = st.text_input("URL pública ou @handle", placeholder="https://www.tiktok.com/@conta", key="tiktok_channel_source")
1981
- lookup_cols = st.columns([1, 1])
1982
- with lookup_cols[0]:
1983
- if st.button("Buscar no Tiktok", type="primary", use_container_width=True, key="tiktok_channel_lookup"):
1984
- result = fetch_public_tiktok_profile(source)
1985
- st.session_state["tiktok_channel_import"] = {"ok": result.ok, "message": result.message, "data": result.data}
1986
- with lookup_cols[1]:
1987
- if st.button("Limpar importação", use_container_width=True, key="tiktok_channel_clear"):
1988
- for key in ("tiktok_channel_import", "tiktok_import_language", "tiktok_import_niche", "tiktok_import_prompt", "tiktok_import_description"):
1989
- st.session_state.pop(key, None)
1990
- st.rerun()
1991
- imported_state = st.session_state.get("tiktok_channel_import", {})
1992
- if imported_state.get("message"):
1993
- (st.success if imported_state.get("ok") else st.warning)(imported_state["message"])
1994
- imported = imported_state.get("data") if imported_state.get("ok") else {}
1995
- if imported:
1996
- st.caption("Dados encontrados na página pública. Reveja os cartões e edite os campos antes de guardar.")
1997
- with st.container(border=True):
1998
- profile_cols = st.columns([0.8, 2.2, 1, 1, 1])
1999
- with profile_cols[0]:
2000
- avatar = _tiktok_avatar_url(imported)
2001
- if avatar:
2002
- st.image(avatar, width=82)
2003
- else:
2004
- st.markdown("### TT")
2005
- with profile_cols[1]:
2006
- st.markdown(f"### {imported.get('name') or imported.get('username') or 'Perfil TikTok'}")
2007
- st.write(imported.get("handle") or f"@{imported.get('username', '')}")
2008
- st.caption(imported.get("public_url") or imported.get("url") or "Página pública")
2009
- with profile_cols[2]:
2010
- st.metric("Seguidores", format_metric_number(imported.get("subscriber_count")))
2011
- with profile_cols[3]:
2012
- st.metric("Curtidas", format_metric_number(imported.get("likes_count")))
2013
- with profile_cols[4]:
2014
- st.metric("Vídeos", format_metric_number(imported.get("video_count")))
2015
- if imported.get("bio"):
2016
- st.caption(f"Descrição pública: {imported['bio']}")
2017
- with st.container(border=True):
2018
- st.markdown("**Dados do canal a cadastrar**")
2019
- data_cols = st.columns(3)
2020
- with data_cols[0]:
2021
- st.write(f"**Nome:** {imported.get('name') or imported.get('username') or '—'}")
2022
- st.write(f"**Handle:** {imported.get('handle') or '—'}")
2023
- with data_cols[1]:
2024
- st.write(f"**URL:** {imported.get('public_url') or imported.get('url') or '—'}")
2025
- st.write(f"**Origem:** {imported.get('metrics_source') or 'tiktok_public_page'}")
2026
- with data_cols[2]:
2027
- st.write("**Formato:** Portrait 9:16")
2028
- st.write("**Consulta:** Página pública")
2029
- with st.form("tiktok_channel_import_form"):
2030
- name = st.text_input("Nome canal", value=str(imported.get("name") or imported.get("username") or ""))
2031
- url = st.text_input("URL canal", value=str(imported.get("public_url") or imported.get("url") or ""))
2032
- handle = st.text_input("Handle canal", value=str(imported.get("handle") or ""))
2033
- language = st.selectbox("Idioma", list(LANGUAGE_CODES), format_func=language_label, key="tiktok_import_language")
2034
- niche = st.text_input("Nicho", key="tiktok_import_niche")
2035
- prompt = st.selectbox("Prompt Master padrão", prompt_ids, format_func=lambda item: prompt_labels.get(item, item), key="tiktok_import_prompt")
2036
- description = st.text_area("Descrição", value=str(imported.get("bio") or ""), key="tiktok_import_description")
2037
- if st.form_submit_button("Cadastrar canal TikTok", type="primary"):
2038
- 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")})
2039
- avatar_url = _tiktok_avatar_url(imported)
2040
- update_channel(channel["id"], {"platform": "tiktok", "default_prompt_master": prompt, "prompt_master": prompt, "avatar_url": avatar_url, "thumbnail_url": avatar_url})
2041
- st.session_state.pop("tiktok_channel_import", None)
2042
- st.success(f"Canal {channel['name']} cadastrado.")
2043
- st.rerun()
2044
2045
  with spreadsheet_tab:
2045
2046
  uploaded = st.file_uploader("Upload da planilha de canais TikTok", type=["xlsx", "xls"], key="tiktok_channel_spreadsheet")
2046
2047
  if uploaded and st.button("Ler e cadastrar planilha TikTok", type="primary", key="tiktok_read_sheet"):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.4.54",
3
+ "version": "0.4.55",
4
4
  "description": "Thunderbolt — interface local para operação de canais faceless e motor MoneyPrinterTurbo",
5
5
  "license": "MIT",
6
6
  "main": "scripts/cli.mjs",