|
|
@@ -6,7 +6,7 @@ from datetime import datetime
|
|
|
from typing import Any
|
|
|
from zoneinfo import ZoneInfo
|
|
|
|
|
|
-from supply_infra.aigc.client import AigcClient
|
|
|
+from supply_infra.aigc.client import AigcClient, MAX_VIDEOS_PER_CRAWLER_PLAN
|
|
|
from supply_infra.aigc.plan_map import (
|
|
|
AigcPlanPair,
|
|
|
assignment_summary,
|
|
|
@@ -24,7 +24,14 @@ logger = logging.getLogger(__name__)
|
|
|
|
|
|
# 仅发布主推荐与人工备选,不包含 rejected / pending_evaluation。
|
|
|
_PUBLISHABLE_BUCKETS = ("primary", "backup")
|
|
|
-_MAX_VIDEOS_PER_CRAWLER_PLAN = 100
|
|
|
+
|
|
|
+
|
|
|
+@dataclass(frozen=True)
|
|
|
+class PublishCandidate:
|
|
|
+ """脱离 ORM Session 后仍可安全使用的发布候选快照。"""
|
|
|
+
|
|
|
+ id: int
|
|
|
+ aweme_id: str
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
@@ -71,11 +78,11 @@ def _resolve_biz_dt(biz_dt: str | None) -> str | None:
|
|
|
|
|
|
|
|
|
def _group_candidates_by_plan(
|
|
|
- candidates: list[VideoDiscoveryCandidate],
|
|
|
+ candidates: list[PublishCandidate],
|
|
|
plan_pairs: list[AigcPlanPair],
|
|
|
-) -> list[tuple[AigcPlanPair, list[VideoDiscoveryCandidate]]]:
|
|
|
+) -> list[tuple[AigcPlanPair, list[PublishCandidate]]]:
|
|
|
buckets = distribute_evenly(candidates, len(plan_pairs))
|
|
|
- grouped: list[tuple[AigcPlanPair, list[VideoDiscoveryCandidate]]] = []
|
|
|
+ grouped: list[tuple[AigcPlanPair, list[PublishCandidate]]] = []
|
|
|
for plan_pair, bucket in zip(plan_pairs, buckets, strict=False):
|
|
|
if bucket:
|
|
|
grouped.append((plan_pair, bucket))
|
|
|
@@ -93,7 +100,7 @@ def publish_videos_from_discovery(
|
|
|
"""
|
|
|
从 video_discovery_candidate 读取视频,按轮询均匀分配到各 AIGC 计划对。
|
|
|
|
|
|
- 仅处理 decision_bucket 为 primary / backup 的候选,不区分品类。
|
|
|
+ 仅处理 decision_bucket 为 primary / backup 且 aweme_id 非空的候选,不区分品类。
|
|
|
"""
|
|
|
resolved_biz_dt = _resolve_biz_dt(biz_dt)
|
|
|
plan_pairs = list_unique_plan_pairs()
|
|
|
@@ -102,12 +109,16 @@ def publish_videos_from_discovery(
|
|
|
|
|
|
with get_session() as session:
|
|
|
repo = VideoDiscoveryRepository(session)
|
|
|
- candidates = repo.list_publishable_candidates(
|
|
|
+ orm_candidates = repo.list_publishable_candidates(
|
|
|
run_id=run_id,
|
|
|
biz_dt=resolved_biz_dt,
|
|
|
skip_published=skip_published,
|
|
|
limit=limit,
|
|
|
)
|
|
|
+ candidates = [
|
|
|
+ PublishCandidate(id=int(item.id), aweme_id=str(item.aweme_id))
|
|
|
+ for item in orm_candidates
|
|
|
+ ]
|
|
|
|
|
|
if not candidates:
|
|
|
return {
|
|
|
@@ -133,7 +144,7 @@ def publish_videos_from_discovery(
|
|
|
batch_results: list[PublishBatchResult] = []
|
|
|
|
|
|
for plan_pair, plan_candidates in grouped:
|
|
|
- candidate_batches = chunk_list(plan_candidates, _MAX_VIDEOS_PER_CRAWLER_PLAN)
|
|
|
+ candidate_batches = chunk_list(plan_candidates, MAX_VIDEOS_PER_CRAWLER_PLAN)
|
|
|
for batch_index, candidate_batch in enumerate(candidate_batches, start=1):
|
|
|
aweme_batch = [str(item.aweme_id) for item in candidate_batch]
|
|
|
id_batch = [int(item.id) for item in candidate_batch]
|
|
|
@@ -173,7 +184,7 @@ def publish_videos_from_discovery(
|
|
|
if not batch.bind_success:
|
|
|
batch.bind_error = str(bind_result.get("error") or "绑定生成计划失败")
|
|
|
|
|
|
- if not dry_run and crawler_plan_id:
|
|
|
+ if not dry_run and crawler_plan_id and batch.bind_success:
|
|
|
with get_session() as session:
|
|
|
updated = VideoDiscoveryRepository(session).mark_candidates_aigc_plans(
|
|
|
id_batch,
|