@danhachuel/thunderbolt 0.2.56 → 0.2.59
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 +88 -3
- package/package.json +1 -1
package/app/main.py
CHANGED
|
@@ -2223,6 +2223,68 @@ def render_upload_postiz():
|
|
|
2223
2223
|
(st.success if result.ok else st.error)(result.message)
|
|
2224
2224
|
|
|
2225
2225
|
|
|
2226
|
+
UPLOAD_DESTINATION_TARGET_KEYS = {
|
|
2227
|
+
"TikTok": "tiktok_profiles",
|
|
2228
|
+
"Instagram": "instagram_profiles",
|
|
2229
|
+
"Facebook Pages": "facebook_pages",
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
|
|
2233
|
+
def upload_target_label(target: Any) -> str:
|
|
2234
|
+
if isinstance(target, dict):
|
|
2235
|
+
name = str(target.get("name") or target.get("label") or target.get("title") or target.get("username") or target.get("id") or "Sem nome")
|
|
2236
|
+
handle = str(target.get("handle") or target.get("username") or target.get("url") or "")
|
|
2237
|
+
return f"{name} — {handle}" if handle and handle not in name else name
|
|
2238
|
+
return str(target)
|
|
2239
|
+
|
|
2240
|
+
|
|
2241
|
+
def upload_target_reference(target: Any) -> dict[str, str] | str | None:
|
|
2242
|
+
if target is None:
|
|
2243
|
+
return None
|
|
2244
|
+
if isinstance(target, dict):
|
|
2245
|
+
public_fields = ("id", "name", "label", "handle", "username", "url")
|
|
2246
|
+
return {field: str(target[field]) for field in public_fields if target.get(field)}
|
|
2247
|
+
return str(target)
|
|
2248
|
+
|
|
2249
|
+
|
|
2250
|
+
def upload_targets_for_destination(destination: str, channels: list[dict[str, Any]], settings: dict[str, Any]) -> list[Any]:
|
|
2251
|
+
if destination == "YouTube":
|
|
2252
|
+
return [channel for channel in channels if isinstance(channel, dict) and channel.get("id") and channel.get("active", True)]
|
|
2253
|
+
setting_key = UPLOAD_DESTINATION_TARGET_KEYS.get(destination)
|
|
2254
|
+
if not setting_key:
|
|
2255
|
+
return []
|
|
2256
|
+
configured_targets = settings.get(setting_key, [])
|
|
2257
|
+
if not isinstance(configured_targets, list):
|
|
2258
|
+
return []
|
|
2259
|
+
targets: list[Any] = []
|
|
2260
|
+
for target in configured_targets:
|
|
2261
|
+
if isinstance(target, dict) and target.get("id"):
|
|
2262
|
+
targets.append(target)
|
|
2263
|
+
elif isinstance(target, str) and target.strip():
|
|
2264
|
+
targets.append(target.strip())
|
|
2265
|
+
return targets
|
|
2266
|
+
|
|
2267
|
+
|
|
2268
|
+
def render_upload_destination_target(destination: str, channels: list[dict[str, Any]], settings: dict[str, Any]) -> Any | None:
|
|
2269
|
+
options = upload_targets_for_destination(destination, channels, settings)
|
|
2270
|
+
destination_key = re.sub(r"[^a-z0-9]+", "_", destination.lower()).strip("_")
|
|
2271
|
+
select_label = "Canal" if destination == "YouTube" else "Perfil / página"
|
|
2272
|
+
empty_label = "Nenhum canal YouTube cadastrado" if destination == "YouTube" else f"Nenhum {destination} configurado"
|
|
2273
|
+
if not options:
|
|
2274
|
+
st.selectbox(select_label, [empty_label], disabled=True, key=f"upload_target_{destination_key}")
|
|
2275
|
+
if destination == "YouTube":
|
|
2276
|
+
st.caption("Cadastre ou liste pelo menos um canal YouTube antes de escolher o destino de envio.")
|
|
2277
|
+
else:
|
|
2278
|
+
st.caption(f"A lista de {destination} será ligada numa etapa própria de credenciais/API.")
|
|
2279
|
+
return None
|
|
2280
|
+
return st.selectbox(
|
|
2281
|
+
select_label,
|
|
2282
|
+
options,
|
|
2283
|
+
format_func=upload_target_label,
|
|
2284
|
+
key=f"upload_target_{destination_key}",
|
|
2285
|
+
)
|
|
2286
|
+
|
|
2287
|
+
|
|
2226
2288
|
def render_upload_conventional():
|
|
2227
2289
|
st.title("Upload")
|
|
2228
2290
|
settings = read_json("settings.json", {})
|
|
@@ -2233,6 +2295,12 @@ def render_upload_conventional():
|
|
|
2233
2295
|
postiz = PostizAdapter(settings)
|
|
2234
2296
|
tasks = [t for t in read_json("tasks.json", []) if t.get("state") == "done" or t.get("artifacts", {}).get("video")]
|
|
2235
2297
|
destination = st.multiselect("Destinos", ["YouTube", "TikTok", "Instagram", "Facebook Pages"], default=["YouTube"], key="upload_destinations", placeholder="Seleccione os destinos")
|
|
2298
|
+
upload_targets: dict[str, Any | None] = {}
|
|
2299
|
+
if destination:
|
|
2300
|
+
st.markdown("**Onde enviar**")
|
|
2301
|
+
for target_destination in destination:
|
|
2302
|
+
with st.container(border=True):
|
|
2303
|
+
upload_targets[target_destination] = render_upload_destination_target(target_destination, channels, settings)
|
|
2236
2304
|
|
|
2237
2305
|
if "Instagram" in destination:
|
|
2238
2306
|
st.info("Instagram está disponível no front end. A publicação real será ligada numa etapa de credenciais/API própria.")
|
|
@@ -2277,7 +2345,8 @@ def render_upload_conventional():
|
|
|
2277
2345
|
thumbnail_path = artifacts.get("thumbnail") or artifacts.get("cover", "")
|
|
2278
2346
|
captions_path = artifacts.get("captions") or artifacts.get("subtitle", "")
|
|
2279
2347
|
st.caption(video_path or "Sem caminho de vídeo registado")
|
|
2280
|
-
|
|
2348
|
+
selected_youtube_channel = upload_targets.get("YouTube") if "YouTube" in destination else None
|
|
2349
|
+
channel = selected_youtube_channel or channel_map.get(str(task.get("channel_id")), {})
|
|
2281
2350
|
account = direct_accounts.get(str(channel.get("google_account_id", "")))
|
|
2282
2351
|
if "YouTube" in destination:
|
|
2283
2352
|
title = st.text_input("Título", value=task.get("title") or task.get("topic", "Vídeo Thunderbolt"), key=f"yt_title_{task['id']}")
|
|
@@ -2292,7 +2361,9 @@ def render_upload_conventional():
|
|
|
2292
2361
|
language = st.text_input("Idioma", value="pt-BR", key=f"yt_language_{task['id']}")
|
|
2293
2362
|
quota_count = official_upload_count(channel, account)
|
|
2294
2363
|
st.caption(f"API Oficial hoje: {quota_count}/{OFFICIAL_DAILY_LIMIT} envios nesta conta Gmail.")
|
|
2295
|
-
if
|
|
2364
|
+
if not selected_youtube_channel:
|
|
2365
|
+
st.caption("Seleccione primeiro um canal YouTube no selector acima para activar este envio.")
|
|
2366
|
+
if st.button("Enviar pelo fluxo recomendado", type="primary", key=f"upload_youtube_{task['id']}", disabled=not selected_youtube_channel, help="Escolha o canal YouTube no selector acima." if not selected_youtube_channel else None):
|
|
2296
2367
|
tags = [tag.strip() for tag in tags_raw.split(",") if tag.strip()]
|
|
2297
2368
|
result = upload_with_default_route(
|
|
2298
2369
|
settings,
|
|
@@ -2312,6 +2383,7 @@ def render_upload_conventional():
|
|
|
2312
2383
|
record = {
|
|
2313
2384
|
"task_id": task.get("id"),
|
|
2314
2385
|
"destination": "YouTube",
|
|
2386
|
+
"target": upload_target_reference(channel),
|
|
2315
2387
|
"status": "published" if result.ok else "failed",
|
|
2316
2388
|
"message": result.message,
|
|
2317
2389
|
"data": result.data,
|
|
@@ -2324,8 +2396,21 @@ def render_upload_conventional():
|
|
|
2324
2396
|
if result.data.get("attempts"):
|
|
2325
2397
|
with st.expander("Detalhes dos mecanismos de upload"):
|
|
2326
2398
|
st.json(result.data["attempts"])
|
|
2327
|
-
if "TikTok" in destination
|
|
2399
|
+
tiktok_target = upload_targets.get("TikTok") if "TikTok" in destination else None
|
|
2400
|
+
if "TikTok" in destination and st.button("Enviar para TikTok", key=f"upload_tiktok_{task['id']}", disabled=not tiktok_target, help="Escolha o perfil TikTok no selector acima." if not tiktok_target else None):
|
|
2328
2401
|
result = TikTokAdapter(settings).upload_video(video_path, task.get("title") or task.get("topic", ""))
|
|
2402
|
+
record = {
|
|
2403
|
+
"task_id": task.get("id"),
|
|
2404
|
+
"destination": "TikTok",
|
|
2405
|
+
"target": upload_target_reference(tiktok_target),
|
|
2406
|
+
"status": "published" if result.ok else "failed",
|
|
2407
|
+
"message": result.message,
|
|
2408
|
+
"data": result.data,
|
|
2409
|
+
"created_at": now(),
|
|
2410
|
+
}
|
|
2411
|
+
uploads = read_json("uploads.json", [])
|
|
2412
|
+
uploads.append(record)
|
|
2413
|
+
write_json("uploads.json", uploads)
|
|
2329
2414
|
(st.success if result.ok else st.warning)(result.message)
|
|
2330
2415
|
if "Instagram" in destination:
|
|
2331
2416
|
st.button("Preparar Instagram", key=f"upload_instagram_{task['id']}", disabled=True, help="UI preparada; publicação Instagram ainda não está activa.")
|
package/package.json
CHANGED