카페, 식당 템플릿 적용

feature-imageMatch
김성경 2026-07-09 14:19:48 +09:00
parent 76485ebbcc
commit e53084ed17
4 changed files with 45 additions and 19 deletions

View File

@ -225,29 +225,48 @@ autotext_template_h_1 = {
"stroke_color": "#333333",
"stroke_width": "0.2 vmin"
}
DVST0001 = "75161273-0422-4771-adeb-816bd7263fb0"
DVST0002 = "c68cf750-bc40-485a-a2c5-3f9fe301e386"
DVST0003 = "e1fb5b00-1f02-4f63-99fa-7524b433ba47"
DVST0001 = "fe11aeab-ff29-4bc8-9f75-c695c7e243e6"
DVRT0001 = "3c4b74c4-82d7-4742-9b05-4b999fef4cbd"
DVCF0001 = "ebb532ec-e49a-4a7e-b0e2-a8132f1bf1f7"
DHST0001 = "660be601-080a-43ea-bf0f-adcf4596fa98"
DHST0002 = "3f194cc7-464e-4581-9db2-179d42d3e40f"
DHST0003 = "f45df555-2956-4a13-9004-ead047070b3d"
DVST0001T = "fe11aeab-ff29-4bc8-9f75-c695c7e243e6"
HST_LIST = [DHST0001,DHST0002,DHST0003]
VST_LIST = [DVST0001T]
VST_LIST = [DVST0001,DVRT0001,DVCF0001]
# industry → 세로형 템플릿 매핑 (신규 세로형 템플릿 추가 시 여기에만 등록)
VERTICAL_INDUSTRY_TEMPLATE_MAP: dict[str, str] = {
"stay": DVST0001,
"restaurant": DVRT0001,
"cafe": DVCF0001,
}
# 매핑에 없는 업종(salon, clinic 등)의 폴백 템플릿
DEFAULT_VERTICAL_TEMPLATE = DVST0001
SCENE_TRACK = 1
AUDIO_TRACK = 2
SUBTITLE_TRACK = 3
KEYWORD_TRACK = 4
def select_template(orientation:OrientationType):
def select_template(orientation: OrientationType, industry: str | None = None) -> str:
"""orientation과 industry에 따라 Creatomate 템플릿 ID를 선택합니다.
Args:
orientation: 영상 방향 ("horizontal" 또는 "vertical")
industry: 업종 분류 (stay|restaurant|cafe ).
세로형에서만 사용되며, 매핑에 없으면 기본 템플릿으로 폴백합니다.
Returns:
선택된 템플릿 ID
"""
if orientation == "horizontal":
template_id = DHST0001
elif orientation == "vertical":
template_id = random.choice(VST_LIST)
template_id = VERTICAL_INDUSTRY_TEMPLATE_MAP.get(industry, DEFAULT_VERTICAL_TEMPLATE)
else:
raise
logger.info(f"[select_template] orientation={orientation}, template_id={template_id}")
logger.info(f"[select_template] orientation={orientation}, industry={industry}, template_id={template_id}")
return template_id
async def get_shared_client() -> httpx.AsyncClient:
@ -296,21 +315,22 @@ class CreatomateService:
def __init__(
self,
api_key: str | None = None,
orientation: OrientationType = "vertical"
orientation: OrientationType = "vertical",
industry: str | None = None,
):
"""
Args:
api_key: Creatomate API (Bearer token으로 사용)
None일 경우 config에서 자동으로 가져옴
orientation: 영상 방향 ("horizontal" 또는 "vertical", 기본값: "vertical")
target_duration: 목표 영상 길이 ()
None일 경우 orientation에 해당하는 기본값 사용
industry: 업종 분류 (stay|restaurant|cafe ). 세로형 템플릿 선택에 사용되며,
매핑에 없거나 None이면 기본 세로형 템플릿으로 폴백
"""
self.api_key = api_key or apikey_settings.CREATOMATE_API_KEY
self.orientation = orientation
# orientation에 따른 템플릿 설정 가져오기
self.template_id = select_template(orientation)
# orientation·industry에 따른 템플릿 설정 가져오기
self.template_id = select_template(orientation, industry=industry)
self.headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}",

View File

@ -18,8 +18,9 @@ class NvMapPwScraper():
_context = None
_win_width = 1280
_win_height = 720
_max_retry = 3
_timeout = 60 # place id timeout threshold seconds
_max_retry = 3
_timeout = 30 # place id timeout threshold seconds
_retry_delay_ms = 1500 # 검색 실패 후 재시도 전 대기시간 (네이버 요청 밀도 완화)
# instance var
page = None
@ -422,12 +423,14 @@ patchedGetter.toString();''')
# 2차 시도: 정제 주소 + 업체명
refined = self._refine_address(address)
if refined != address:
await self.page.wait_for_timeout(self._retry_delay_ms)
logger.info(f"[REFINE] 주소 정제: '{address}''{refined}'")
result = await self._try_search(refined, title)
if result:
return result
# 3차 시도: 업체명만으로 검색
await self.page.wait_for_timeout(self._retry_delay_ms)
logger.info(f"[RETRY] 업체명만으로 재시도: '{title}'")
result = await self._try_search("", title)
if result:

View File

@ -191,6 +191,7 @@ async def generate_video(
store_address = project.detail_region_info
brand_name = project.store_name
region = project.region
industry = project.industry
# MarketingIntel 조회
marketing_result = await session.execute(
@ -357,7 +358,8 @@ async def generate_video(
f"[generate_video] Stage 2 START - Creatomate API - task_id: {task_id}"
)
creatomate_service = CreatomateService(
orientation=orientation
orientation=orientation,
industry=industry,
)
logger.debug(
f"[generate_video] Using template_id: {creatomate_service.template_id}, (song duration: {song_duration})"

View File

@ -57,10 +57,8 @@ async def generate_creative_assets_background(
for attempt in range(1, max_retries + 1):
try:
creatomate_service = CreatomateService(orientation=orientation)
template = await creatomate_service.get_one_template_data(creatomate_service.template_id)
# ── 프로젝트 / 마케팅 인텔 로드 ──────────────────────────────────
# 템플릿 선택에 project.industry가 필요하므로 서비스 생성보다 먼저 로드
async with BackgroundSessionLocal() as session:
project_result = await session.execute(
select(Project)
@ -74,6 +72,9 @@ async def generate_creative_assets_background(
)
marketing_intelligence = marketing_result.scalar_one_or_none()
creatomate_service = CreatomateService(orientation=orientation, industry=project.industry)
template = await creatomate_service.get_one_template_data(creatomate_service.template_id)
store_address = project.detail_region_info
customer_name = project.store_name
language = project.language or "Korean"