@danhachuel/thunderbolt 0.7.2 → 0.7.4

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 +23 -74
  2. package/package.json +1 -1
package/app/main.py CHANGED
@@ -2668,12 +2668,15 @@ def render_channels():
2668
2668
 
2669
2669
  with import_tab:
2670
2670
  st.caption("A pesquisa pública funciona sem API Key. A Data API é opcional e fica disponível numa opção separada para métricas oficiais.")
2671
- source = st.text_input("URL, handle ou ID do canal", placeholder="https://youtube.com/@seucanal", key="youtube_channel_source")
2672
- lookup_cols = st.columns(2)
2671
+ lookup_cols = st.columns([2.2, 1.0], gap="medium")
2673
2672
  with lookup_cols[0]:
2674
- lookup_mode = st.radio("Método de consulta", ["Página pública — sem API Key", "YouTube Data API — API Key opcional"], horizontal=True, key="youtube_channel_lookup_mode")
2673
+ source = st.text_input("URL, handle ou ID do canal", placeholder="https://youtube.com/@seucanal", key="youtube_channel_source")
2675
2674
  with lookup_cols[1]:
2676
- st.caption("Os canais cadastrados são apresentados apenas em lista.")
2675
+ lookup_mode = st.selectbox(
2676
+ "Método de consulta",
2677
+ ["Página pública — sem API Key", "YouTube Data API — API Key opcional"],
2678
+ key="youtube_channel_lookup_mode",
2679
+ )
2677
2680
  col1, col2 = st.columns([1, 1])
2678
2681
  with col1:
2679
2682
  if st.button("Buscar no YouTube", type="primary", width="stretch", key="youtube_channel_lookup"):
@@ -2911,76 +2914,6 @@ def render_channels():
2911
2914
  st.session_state.pop("channel_spreadsheet_rows", None)
2912
2915
  st.rerun()
2913
2916
 
2914
- st.divider()
2915
- registered_channels = [channel for channel in read_json("channels.json", []) if is_youtube_channel_record(channel)]
2916
- st.subheader(f"Canais Youtube cadastrados ({len(registered_channels)})")
2917
- youtube_channel_search = st.text_input(
2918
- "Pesquisar nomes dos canais YouTube",
2919
- key="youtube_registered_channels_search",
2920
- placeholder="Digite o nome do canal",
2921
- )
2922
- visible_registered_channels = [
2923
- channel for channel in registered_channels
2924
- if not youtube_channel_search.strip()
2925
- or youtube_channel_search.strip().casefold() in str(channel.get("name") or "").casefold()
2926
- ]
2927
- if not registered_channels:
2928
- st.info("Nenhum canal cadastrado.")
2929
- elif not visible_registered_channels:
2930
- st.info("Nenhum canal YouTube corresponde à pesquisa.")
2931
- else:
2932
- wide_style_labels = {
2933
- "pexels": "Pexels/Pixabay",
2934
- "full_ia": "Full IA",
2935
- "music": "Apenas Música",
2936
- }
2937
- registered_rows = [
2938
- {
2939
- "URL canal": str(channel.get("url") or ""),
2940
- "Nome canal": str(channel.get("name") or ""),
2941
- "Handle canal": str(channel.get("handle") or ""),
2942
- "Narrador/ voz padrão": str(channel.get("default_voice") or channel.get("voice") or ""),
2943
- "Idioma": language_label(channel.get("language") or "pt"),
2944
- "Nicho": channel_niche_label(channel),
2945
- "Blueprint Padrão": channel_blueprint_summary(channel)["name"],
2946
- "Estilo Wide": wide_style_labels.get(str(channel.get("style_wide") or "").strip().lower(), str(channel.get("style_wide") or "")),
2947
- "Activo": "Sim" if channel.get("active", True) else "Não",
2948
- "Descrição": str(channel.get("description") or ""),
2949
- "Conta Google do Documento deste Canal": str(channel.get("google_account_email") or youtube_account_labels.get(str(channel.get("google_account_id") or ""), "")),
2950
- "Automação Ligada": "Sim" if channel.get("automation_on", False) else "Não",
2951
- "Horário diário (HH:MM)": str(channel.get("automation_time") or "00:00"),
2952
- "DELEGATED_SESSION_ID": str(channel.get("delegated_session_id") or ""),
2953
- "Duração Padrão Vídeos (Min)": channel.get("default_video_duration_minutes") if channel.get("default_video_duration_minutes") is not None else None,
2954
- "Origem": str(channel.get("import_source") or channel.get("metrics_source") or "manual"),
2955
- }
2956
- for channel in registered_channels
2957
- ]
2958
- st.dataframe(
2959
- registered_rows,
2960
- width="stretch",
2961
- hide_index=True,
2962
- height=420,
2963
- column_config={
2964
- "URL canal": st.column_config.LinkColumn("URL canal", width=260),
2965
- "Nome canal": st.column_config.TextColumn("Nome canal", width=180),
2966
- "Handle canal": st.column_config.TextColumn("Handle canal", width=150),
2967
- "Narrador/ voz padrão": st.column_config.TextColumn("Narrador/ voz padrão", width=220),
2968
- "Idioma": st.column_config.TextColumn("Idioma", width=150),
2969
- "Nicho": st.column_config.TextColumn("Nicho", width=180),
2970
- "Blueprint Padrão": st.column_config.TextColumn("Blueprint Padrão", width=240),
2971
- "Estilo Wide": st.column_config.TextColumn("Estilo Wide", width=150),
2972
- "Activo": st.column_config.TextColumn("Activo", width=80),
2973
- "Descrição": st.column_config.TextColumn("Descrição", width=420),
2974
- "Conta Google do Documento deste Canal": st.column_config.TextColumn("Conta Google do Documento deste Canal", width=300),
2975
- "Automação Ligada": st.column_config.TextColumn("Automação Ligada", width=150),
2976
- "Horário diário (HH:MM)": st.column_config.TextColumn("Horário diário (HH:MM)", width=180),
2977
- "DELEGATED_SESSION_ID": st.column_config.TextColumn("DELEGATED_SESSION_ID", width=260),
2978
- "Duração Padrão Vídeos (Min)": st.column_config.NumberColumn("Duração Padrão Vídeos (Min)", width=220),
2979
- "Origem": st.column_config.TextColumn("Origem", width=150),
2980
- },
2981
- )
2982
- st.caption("Tabela de canais cadastrados. Use a barra inferior para navegar horizontalmente e a barra lateral para percorrer os registos.")
2983
-
2984
2917
  with batch_tab:
2985
2918
  st.caption("Esta subaba usa a conta Google/YouTube seleccionada para listar os canais que ela gere. Não lê a caixa Gmail e não usa e-mails como pesquisa pública.")
2986
2919
  accounts = [account for account in settings.get("youtube_batch_accounts", []) if isinstance(account, dict) and account.get("id")]
@@ -3130,7 +3063,23 @@ def render_channels():
3130
3063
  st.rerun()
3131
3064
 
3132
3065
  channels = [channel for channel in read_json("channels.json", []) if is_youtube_channel_record(channel)]
3066
+ st.divider()
3067
+ st.subheader(f"Canais Youtube cadastrados ({len(channels)})")
3068
+ youtube_channel_search = st.text_input(
3069
+ "Pesquisar nomes dos canais YouTube",
3070
+ key="youtube_registered_channels_search",
3071
+ placeholder="Digite o nome do canal",
3072
+ )
3073
+ visible_registered_channels = [
3074
+ channel for channel in channels
3075
+ if not youtube_channel_search.strip()
3076
+ or youtube_channel_search.strip().casefold() in str(channel.get("name") or "").casefold()
3077
+ ]
3133
3078
  if not channels:
3079
+ st.info("Nenhum canal YouTube cadastrado.")
3080
+ return
3081
+ if not visible_registered_channels:
3082
+ st.info("Nenhum canal YouTube corresponde à pesquisa.")
3134
3083
  return
3135
3084
  for channel in visible_registered_channels:
3136
3085
  channel_id = str(channel["id"])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.7.02",
3
+ "version": "0.7.04",
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",