@danhachuel/thunderbolt 0.6.7 → 0.6.9
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
CHANGED
|
@@ -118,7 +118,7 @@ from integrations.upload_post import UploadPostAdapter, UPLOAD_POST_PLATFORM_OPT
|
|
|
118
118
|
from integrations.bilibili_upload import BilibiliApiAdapter, BILIBILI_DEFAULT_TID, BILIBILI_VIDEO_EXTENSIONS, normalise_bilibili_api_cards
|
|
119
119
|
from integrations.distrokid_upload import DistroKidAdapter, DISTROKID_AUDIO_EXTENSIONS, DISTROKID_COVER_EXTENSIONS, close_distrokid_session
|
|
120
120
|
from integrations.music_uploads import JewelMusicAdapter, PushtunesAdapter, YTMusicApiAdapter, MUSIC_UPLOAD_EXTENSIONS, PUSHTUNES_OPERATIONS, PUSHTUNES_SOURCES, PUSHTUNES_TARGETS, YT_MUSIC_UPLOAD_EXTENSIONS
|
|
121
|
-
from integrations.upload_routing import COMPOSIO_OPERATION_OPTIONS, OFFICIAL_DAILY_LIMIT, official_upload_count, upload_with_default_route
|
|
121
|
+
from integrations.upload_routing import COMPOSIO_OPERATION_OPTIONS, OFFICIAL_DAILY_LIMIT, official_upload_count, resolve_youtube_account, upload_with_default_route
|
|
122
122
|
from integrations.youtube_direct_upload import YouTubeDirectUploader
|
|
123
123
|
from integrations.youtube_direct_credentials import delete_credentials_document, direct_account_status, document_status, ensure_credentials_document, load_credentials_document, merge_credentials_document, parse_credentials_document, save_credentials_document, update_credentials_document_session_info
|
|
124
124
|
from integrations.session_info_health import check_account_session_info_health
|
|
@@ -5597,7 +5597,7 @@ def render_upload_direct():
|
|
|
5597
5597
|
with st.container(border=True):
|
|
5598
5598
|
st.write(f"**{task.get('topic', 'Sem tópico')}** — {task.get('channel_name', 'Canal')}")
|
|
5599
5599
|
st.caption(video_path or "Sem caminho de vídeo registado")
|
|
5600
|
-
account =
|
|
5600
|
+
account = resolve_youtube_account(settings, channel)
|
|
5601
5601
|
credential_status = document_status(STORAGE, account, channel, settings, channels) if account else None
|
|
5602
5602
|
if not account:
|
|
5603
5603
|
st.warning("Este canal não tem uma conta Google associada. Associe a conta no cartão deste canal em Canais cadastrados.")
|
|
@@ -6364,7 +6364,7 @@ def render_upload_conventional():
|
|
|
6364
6364
|
st.caption(video_path or "Sem caminho de vídeo registado")
|
|
6365
6365
|
selected_youtube_channel = upload_targets.get("YouTube") if "YouTube" in destination else None
|
|
6366
6366
|
channel = selected_youtube_channel or channel_map.get(str(task.get("channel_id")), {})
|
|
6367
|
-
account =
|
|
6367
|
+
account = resolve_youtube_account(settings, channel)
|
|
6368
6368
|
if "YouTube" in destination:
|
|
6369
6369
|
detected_language = normalize_video_language(task.get("language") or channel.get("language") or "pt")
|
|
6370
6370
|
language_state_key = f"yt_language_{task['id']}"
|
|
@@ -23,7 +23,7 @@ from typing import Any
|
|
|
23
23
|
|
|
24
24
|
from integrations.moneyprinter_config import sync_moneyprinter_config
|
|
25
25
|
from integrations.session_info_health import check_all_accounts_session_info_health, emit_session_info_health_alerts
|
|
26
|
-
from integrations.upload_routing import upload_with_default_route
|
|
26
|
+
from integrations.upload_routing import resolve_youtube_account, upload_with_default_route
|
|
27
27
|
from hermes_ui.creative_generation import CreativeGenerationError, generate_creative_package, generate_title_and_keywords, generate_thumbnail_prompt, generate_topic_for_channel
|
|
28
28
|
from hermes_ui.script_documents import save_script_document
|
|
29
29
|
from hermes_ui.script_generation import generate_script_document
|
|
@@ -693,7 +693,18 @@ def _material_video_attempts(task: dict[str, Any], settings: dict[str, Any]) ->
|
|
|
693
693
|
fallback = [item for item in attempts if item[0] != route]
|
|
694
694
|
ordered = preferred + fallback
|
|
695
695
|
if ordered:
|
|
696
|
-
|
|
696
|
+
# Uma execução do helper já recebe a lista de chaves do provider
|
|
697
|
+
# configurado; não repetir o mesmo provider por cada cartão/key.
|
|
698
|
+
# Repetições anteriores reinstalavam dependências e produziam cascatas
|
|
699
|
+
# como Pexels → Pexels → Pixabay sem acrescentar uma estratégia real.
|
|
700
|
+
unique: list[tuple[str, str]] = []
|
|
701
|
+
seen: set[str] = set()
|
|
702
|
+
for provider, api_key in ordered:
|
|
703
|
+
if provider in seen:
|
|
704
|
+
continue
|
|
705
|
+
seen.add(provider)
|
|
706
|
+
unique.append((provider, api_key))
|
|
707
|
+
return unique
|
|
697
708
|
# Legacy settings keep the complete key list in one config field. Let
|
|
698
709
|
# MoneyPrinterTurbo retain its own list handling when no individual cards
|
|
699
710
|
# are available, preserving backwards compatibility for older installs.
|
|
@@ -1566,7 +1577,7 @@ def _run_task(task: dict[str, Any]) -> dict[str, Any]:
|
|
|
1566
1577
|
settings,
|
|
1567
1578
|
storage_root=STORAGE,
|
|
1568
1579
|
channel=channel,
|
|
1569
|
-
account=
|
|
1580
|
+
account=resolve_youtube_account(settings, channel),
|
|
1570
1581
|
video_path=str(video_path),
|
|
1571
1582
|
title=title,
|
|
1572
1583
|
description=str(script.get("summary") or "") + "\n\n" + str(script.get("content") or "")[:5000],
|
|
@@ -45,11 +45,41 @@ def _attempt_record(name: str, result: IntegrationResult, *, skipped: bool = Fal
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
def resolve_youtube_account(settings: dict[str, Any], channel: dict[str, Any] | None) -> dict[str, Any] | None:
|
|
49
|
+
"""Resolve the configured Google account without relying only on the channel ID.
|
|
50
|
+
|
|
51
|
+
Older channel records may retain the account e-mail while the account ID is
|
|
52
|
+
missing or stale. Prefer an exact ID match, then use a case-insensitive
|
|
53
|
+
e-mail match, and finally accept a sole configured account only when the
|
|
54
|
+
channel has no contradictory account reference.
|
|
55
|
+
"""
|
|
56
|
+
if not isinstance(channel, dict):
|
|
57
|
+
return None
|
|
58
|
+
accounts = [item for item in settings.get("youtube_batch_accounts", []) if isinstance(item, dict) and item.get("id")]
|
|
59
|
+
if not accounts:
|
|
60
|
+
return None
|
|
61
|
+
account_id = str(channel.get("google_account_id") or "").strip()
|
|
62
|
+
if account_id:
|
|
63
|
+
match = next((item for item in accounts if str(item.get("id") or "").strip() == account_id), None)
|
|
64
|
+
if match is not None:
|
|
65
|
+
return match
|
|
66
|
+
account_email = str(channel.get("google_account_email") or "").strip().casefold()
|
|
67
|
+
if account_email:
|
|
68
|
+
match = next((item for item in accounts if str(item.get("email") or "").strip().casefold() == account_email), None)
|
|
69
|
+
if match is not None:
|
|
70
|
+
return match
|
|
71
|
+
if not account_id and not account_email and len(accounts) == 1:
|
|
72
|
+
return accounts[0]
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
48
76
|
def _quota_key(channel: dict[str, Any], account: dict[str, Any] | None) -> str:
|
|
49
77
|
if account and account.get("id"):
|
|
50
78
|
return f"account:{account['id']}"
|
|
51
79
|
if channel.get("google_account_id"):
|
|
52
80
|
return f"account:{channel['google_account_id']}"
|
|
81
|
+
if channel.get("google_account_email"):
|
|
82
|
+
return f"account-email:{str(channel['google_account_email']).strip().casefold()}"
|
|
53
83
|
return f"channel:{channel.get('id', 'unknown')}"
|
|
54
84
|
|
|
55
85
|
|
|
@@ -107,6 +137,7 @@ def upload_with_default_route(
|
|
|
107
137
|
composio_publisher: Callable[..., IntegrationResult] | None = None,
|
|
108
138
|
) -> IntegrationResult:
|
|
109
139
|
attempts: list[dict[str, Any]] = []
|
|
140
|
+
account = account or resolve_youtube_account(settings, channel)
|
|
110
141
|
composio_enabled = bool(settings.get("composio_enabled", False)) and bool(settings.get("composio_auto_upload", True))
|
|
111
142
|
composio_ready = composio_enabled and bool(settings.get("composio_api_key")) and bool(settings.get("composio_tool_slug"))
|
|
112
143
|
if composio_enabled and not composio_ready:
|
package/package.json
CHANGED
package/seed/skills/mpt_agent.py
CHANGED
|
@@ -697,7 +697,7 @@ def ensure_moviepy(root: Path, uv: str) -> None:
|
|
|
697
697
|
MOVIEPY_REQUIREMENT,
|
|
698
698
|
"python",
|
|
699
699
|
"-c",
|
|
700
|
-
"import
|
|
700
|
+
"import importlib.metadata; print(f\"moviepy {importlib.metadata.version('moviepy')}\")",
|
|
701
701
|
],
|
|
702
702
|
cwd=root,
|
|
703
703
|
)
|