@danhachuel/thunderbolt 0.5.81 → 0.5.83
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 +41 -23
- package/package.json +1 -1
package/app/main.py
CHANGED
|
@@ -1494,6 +1494,23 @@ def _growth_channel_metrics(channel: dict[str, Any]) -> tuple[list[dict[str, Any
|
|
|
1494
1494
|
def render_growth_youtube():
|
|
1495
1495
|
st.title("Analista Growth Youtube")
|
|
1496
1496
|
st.caption("Auditoria pública baseada nos 3 pilares críticos e nas métricas secundárias do agente Growth.")
|
|
1497
|
+
growth_settings = read_json("settings.json", {})
|
|
1498
|
+
youtube_data_api_ready = bool(str(growth_settings.get("youtube_api_key") or os.getenv("YOUTUBE_API_KEY") or "").strip())
|
|
1499
|
+
growth_accounts = growth_settings.get("youtube_batch_accounts", [])
|
|
1500
|
+
analytics_accounts_ready = False
|
|
1501
|
+
if isinstance(growth_accounts, list):
|
|
1502
|
+
for growth_account in growth_accounts:
|
|
1503
|
+
if isinstance(growth_account, dict) and youtube_batch_account_status(growth_account, STORAGE).ok:
|
|
1504
|
+
analytics_accounts_ready = True
|
|
1505
|
+
break
|
|
1506
|
+
st.markdown("**Estado das APIs de Growth**")
|
|
1507
|
+
api_status_cols = st.columns(2, gap="small")
|
|
1508
|
+
with api_status_cols[0]:
|
|
1509
|
+
st.caption("YouTube Data API v3: Metadados públicos")
|
|
1510
|
+
_api_status_badge("Configured" if youtube_data_api_ready else "Missing configuration", "ready" if youtube_data_api_ready else "missing")
|
|
1511
|
+
with api_status_cols[1]:
|
|
1512
|
+
st.caption("YouTube Analytics API: Métricas internas")
|
|
1513
|
+
_api_status_badge("Configured" if analytics_accounts_ready else "Missing configuration", "ready" if analytics_accounts_ready else "missing")
|
|
1497
1514
|
channels = [item for item in read_json("channels.json", []) if isinstance(item, dict) and item.get("active", True) and is_youtube_channel_record(item)]
|
|
1498
1515
|
if not channels:
|
|
1499
1516
|
st.info("Cadastre pelo menos um canal YouTube em Canais YouTube para iniciar a análise.")
|
|
@@ -5441,7 +5458,11 @@ def render_automation():
|
|
|
5441
5458
|
for channel in channels:
|
|
5442
5459
|
channel_id = channel["id"]
|
|
5443
5460
|
with st.container(border=True):
|
|
5444
|
-
|
|
5461
|
+
blueprint_ids, blueprint_labels, current_blueprint, voice_options, current_voice = channel_default_options(channel)
|
|
5462
|
+
automation_blueprint = current_blueprint
|
|
5463
|
+
automation_voice = current_voice
|
|
5464
|
+
automation_format = str(channel.get("format") or "wide")
|
|
5465
|
+
header_cols = st.columns([0.62, 2.15, 1.55, 1.25, 1.25, 1.45, 1.3], gap="small")
|
|
5445
5466
|
with header_cols[0]:
|
|
5446
5467
|
if channel.get("thumbnail_url"):
|
|
5447
5468
|
st.image(channel["thumbnail_url"], width=48)
|
|
@@ -5451,18 +5472,24 @@ def render_automation():
|
|
|
5451
5472
|
st.write(f"**{channel.get('name', 'Sem nome')}**")
|
|
5452
5473
|
st.caption(channel.get("handle") or channel.get("url") or "sem URL")
|
|
5453
5474
|
with header_cols[2]:
|
|
5454
|
-
|
|
5475
|
+
st.markdown("**Thumbnail Blueprint**")
|
|
5476
|
+
st.caption(str(paired_thumbnail.get("name") or "Generic_Thumbnail_Blueprint"))
|
|
5455
5477
|
with header_cols[3]:
|
|
5456
|
-
schedule_time = st.text_input("Horário (HH:MM)", value=channel.get("automation_time", "00:00"), key=f"automation_time_{channel_id}")
|
|
5457
|
-
blueprint_ids, blueprint_labels, current_blueprint, voice_options, current_voice = channel_default_options(channel)
|
|
5458
|
-
default_cols = st.columns([1.0, 1.0, 1.35, 1.45, 1.55, 1.0], gap="small")
|
|
5459
|
-
with default_cols[0]:
|
|
5460
5478
|
st.markdown("**Idioma do roteiro**")
|
|
5461
5479
|
st.caption(video_language_label(normalize_video_language(channel.get("language") or "pt")))
|
|
5462
|
-
with
|
|
5480
|
+
with header_cols[4]:
|
|
5463
5481
|
st.markdown("**Nicho Padrão**")
|
|
5464
5482
|
st.caption(channel_niche_label(channel))
|
|
5465
|
-
with
|
|
5483
|
+
with header_cols[5]:
|
|
5484
|
+
st.markdown("**Fonte do vídeo**")
|
|
5485
|
+
st.caption(channel_video_source_value(channel.get("style_wide")))
|
|
5486
|
+
st.markdown("**Proporção do vídeo**")
|
|
5487
|
+
st.caption(str(channel.get("video_aspect_ratio") or "Landscape 16:9"))
|
|
5488
|
+
with header_cols[6]:
|
|
5489
|
+
schedule_time = st.text_input("Horário (HH:MM)", value=channel.get("automation_time", "00:00"), key=f"automation_time_{channel_id}")
|
|
5490
|
+
|
|
5491
|
+
control_cols = st.columns([1.8, 1.8, 1.35, 1.2], gap="small")
|
|
5492
|
+
with control_cols[0]:
|
|
5466
5493
|
automation_blueprint = st.selectbox(
|
|
5467
5494
|
"Blueprint Padrão",
|
|
5468
5495
|
blueprint_ids,
|
|
@@ -5470,19 +5497,8 @@ def render_automation():
|
|
|
5470
5497
|
format_func=lambda item: blueprint_labels.get(item, item or "Sem Blueprint padrão"),
|
|
5471
5498
|
key=f"automation_blueprint_{channel_id}",
|
|
5472
5499
|
)
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
st.markdown("**Fonte do vídeo**")
|
|
5476
|
-
st.caption(channel_video_source_value(channel.get("style_wide")))
|
|
5477
|
-
st.markdown("**Proporção do vídeo**")
|
|
5478
|
-
st.caption(str(channel.get("video_aspect_ratio") or "Landscape 16:9"))
|
|
5479
|
-
st.markdown("**Formato**")
|
|
5480
|
-
st.caption(str(channel.get("format") or "wide"))
|
|
5481
|
-
st.markdown("**Thumbnail Blueprint**")
|
|
5482
|
-
st.caption(str(paired_thumbnail.get("name") or "Generic_Thumbnail_Blueprint"))
|
|
5483
|
-
with default_cols[4]:
|
|
5484
|
-
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}")
|
|
5485
|
-
with default_cols[5]:
|
|
5500
|
+
paired_thumbnail = thumbnail_blueprint_for_blueprint(automation_blueprint)
|
|
5501
|
+
with control_cols[1]:
|
|
5486
5502
|
automation_voice = st.selectbox(
|
|
5487
5503
|
"Narrador/Voz Padrão",
|
|
5488
5504
|
voice_options,
|
|
@@ -5490,8 +5506,10 @@ def render_automation():
|
|
|
5490
5506
|
format_func=lambda item: item or "Sem voz padrão",
|
|
5491
5507
|
key=f"automation_voice_{channel_id}",
|
|
5492
5508
|
)
|
|
5493
|
-
with
|
|
5494
|
-
|
|
5509
|
+
with control_cols[2]:
|
|
5510
|
+
enabled = st.toggle("Automação ligada", value=bool(channel.get("automation_on", False)), key=f"automation_on_{channel_id}")
|
|
5511
|
+
with control_cols[3]:
|
|
5512
|
+
if st.button("Guardar", key=f"automation_save_{channel_id}", use_container_width=True, type="primary"):
|
|
5495
5513
|
if not valid_hhmm(schedule_time):
|
|
5496
5514
|
st.error("Use o formato HH:MM, por exemplo 08:30.")
|
|
5497
5515
|
else:
|
package/package.json
CHANGED