
    (<iE                    \   d Z ddlmZ ddlZddlZddlZddlmZ ddlZddl	m
Z
 ddlmZmZmZ dZeZdZd	Zd
ZdZdZeZdZ ee      j2                  Zedz  Zedz  dz  Zedz  Zedz  ZdZdZ dZ!dZ"dZ#d dZ$d!dZ%d"d#dZ&d$dZ'd$dZ(d%dZ)d Z*e+dk(  r ejX                   e*              yy)&u  
컨셉 #26 Hyundai-Style 프로덕션 배너 생성
- c26-1200x628.png  (landscape, Meta Ads / Google Ads)
- c26-1200x1200.png (square, Meta Ads / Google Ads)

타겟: 보험 FA 리쿠르팅 (T.O.P 사업단)
디자인: 다크 메탈릭, 현대차 프리미엄 미니멀리즘
    )annotationsN)Path)sync_playwright)
CTA_MIN_PXHEAD_SUB_RATIOWORKSPACE_ROOT   ,   6   <   g333333?g      ?g?z3output/meta-ads/production-banners/26-hyundai-styleoutputz	v4-hybridzbg_c26_landscape.jpgzbg_c26_square.jpgz0https://generativelanguage.googleapis.com/v1betazgemini-3-pro-image-previewzgemini-3.1-flash-image-previewa  Dark metallic industrial interior, brushed steel surfaces, dramatic angular lighting from upper left, cool blue-gray tones with faint silver streaks, deep charcoal atmosphere, cinematic wide shot, no people, no text, no logos, photorealistic, 16:9 landscape format. Left third slightly darker and more shadowed for text overlay area. Right two-thirds shows more metallic texture and light dynamics.aa  Dark metallic industrial interior, brushed steel surfaces, dramatic angular lighting streaks from upper left, cool blue-gray tones, subtle silver light reflections, deep charcoal black center with gradual metallic glow toward edges, no people, no text, no logos, photorealistic, cinematic, square 1:1 format. Ultra-premium automotive showroom aesthetic.c                     t         j                  j                  dt        t                     dd l} | j                         S )Nr   )syspathinsertstrBASE_DIRgcloud_authget_access_token)r   s    ^/home/jay/workspace/.worktrees/task-2057-dev2/tools/ai-image-gen/gen_c26_production_banners.pyget_auth_tokenr   B   s*    HHOOAs8}%''))    c                    t          d| d}d|  dd}t        j                  |||d      }|j                  dk(  r|S |r)t          d| d	| }t        j                  |d
di|d      S |S )Nz/models/z:generateContentzBearer zapplication/json)AuthorizationContent-Typei,  )headersjsontimeout   z:generateContent?key=r   )GEMINI_API_BASErequestspoststatus_code)tokenapi_keymodelpayloadurlr   respurl_ks           r   _call_geminir+   H   s    XeW,<
=C")% 1CUVG==gGSID3"#8E72GyQ}}U^=O,PW^hkllKr   c           	     \   |j                         r8|j                         j                  dkD  r|st        d|j                          yt        d|j                   d       t               }t        j                  j                  dt        t                     dd l}|j                  d      }dd	| igigd
ddgid}t        t        fD ]-  }	 t        ||||      }|j                   dk7  rt        d| d|j                    d       ?|j#                         }	|	j%                  dg       }
|
st        d| d       s|
d   j%                  di       j%                  dg       }t'        d |D        d       }|t        d| d       t)        j*                  |d   d         }|j,                  j/                  dd       |j1                  |       t        d|j                   dt3        |      dz   d        y t        d       y # t4        $ r}t        d| d|        Y d }~]d }~ww xY w)!NiP  u$     [배경] 기존 캐시 재사용: Tu%     [배경] Gemini API 호출 중... ()r   GEMINI_API_KEYpartstextresponseModalitiesIMAGETEXT)contentsgenerationConfigr   u     [배경] z HTTP u   , 다음 모델 시도...
candidatesu$    candidates 없음, 다음 시도...contentc              3  *   K   | ]  }d |v s|  yw)
inlineDataN ).0ps     r   	<genexpr>z&generate_background.<locals>.<genexpr>q   s     EQ<13DqEs   	u-    이미지 데이터 없음, 다음 시도...r9   dataparentsexist_oku     [배경] 완료:  (    KB)u	    오류: u>     [배경] 모든 모델 실패 — CSS 단색 fallback 사용F)existsstatst_sizeprintnamer   r   r   r   r   r   r   get_api_keyMODEL_PRIMARYMODEL_FALLBACKr+   r#   r   getnextbase64	b64decodeparentmkdirwrite_byteslen	Exception)promptoutput_pathforcer$   r   r%   r'   r&   r)   r>   r6   r/   
image_partimage_byteses                  r   generate_backgroundr\   T   s9    0 0 2 : :V CE4[5E5E4FGH	1+2B2B1C1
EFEHHOOAs8}%%%&67G  01231GV3DEG
  0 5	5ww?D3&E7&1A1A0BB[\]99;D,3JE7*NOPqM%%i488"EEE%EtLJ!E7*WXY **:l+CF+KLK$$TD$A##K0'(8(8'9C<Ld<R;SSWXY)50 

JK	  	5Kwis344	5s,   9H3H<A	HA1H	H+H&&H+c                    | r%| j                         rd| j                          d}nd}d| dt         dt         dt         dt
         d	t         d
t         dt         dt         dS )Nbackground-image: url('file://z>'); background-size: cover; background-position: center right;zKbackground: linear-gradient(135deg, #0D0F12 0%, #1A1E24 40%, #232B33 100%);a  <!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<style>
  @font-face {
    font-family: 'Pretendard';
    src: local('Pretendard');
  }
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body {
    width: 1200px;
    height: 628px;
    overflow: hidden;
    font-family: 'Pretendard', 'Noto Sans KR', -apple-system, sans-serif;
  }
  .container {
    width: 1200px;
    height: 628px;
    position: relative;
    u~  
  }
  /* 왼쪽에서 오른쪽으로 그라디언트 오버레이 — 텍스트 영역 가독성 확보 */
  .overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(
      to right,
      rgba(10, 12, 16, 0.92) 0%,
      rgba(10, 12, 16, 0.85) 30%,
      rgba(10, 12, 16, 0.60) 55%,
      rgba(10, 12, 16, 0.10) 100%
    );
  }
  /* 텍스트 영역: 왼쪽 58% */
  .text-area {
    position: absolute;
    left: 0;
    top: 0;
    width: 696px;
    height: 628px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 0 72px;
    gap: 0;
  }
  .brand-badge {
    font-size: zpx;
    font-weight: 700;
    color: #4A90D9;
    letter-spacing: 1px;
    word-break: keep-all;
    margin-bottom: 24px;
    line-height: z#;
  }
  .headline {
    font-size: ?px;
    font-weight: 800;
    color: #FFFFFF;
    line-height: z;
    letter-spacing: -1.5px;
    word-break: keep-all;
    margin-bottom: 20px;
  }
  .headline .accent {
    color: #4A90D9;
  }
  .sub-copy {
    font-size: ?px;
    font-weight: 500;
    color: #B0BAC6;
    line-height: a  ;
    letter-spacing: -0.5px;
    word-break: keep-all;
    margin-bottom: 40px;
  }
  .cta-btn {
    display: inline-flex;
    align-items: center;
    background: #4A90D9;
    color: #FFFFFF;
    font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
    font-size: uH  px;
    font-weight: 700;
    padding: 18px 48px;
    border-radius: 50px;
    letter-spacing: -0.3px;
    word-break: keep-all;
    white-space: nowrap;
    align-self: flex-start;
  }
  /* 오른쪽 메탈릭 강조선 */
  .accent-line {
    position: absolute;
    right: 504px;
    top: 60px;
    bottom: 60px;
    width: 2px;
    background: linear-gradient(to bottom, transparent 0%, #4A90D9 30%, #C0C0C0 60%, transparent 100%);
    opacity: 0.5;
  }
  /* 하단 브랜드 서브라인 */
  .brand-sub {
    position: absolute;
    bottom: 28px;
    left: 72px;
    font-size: u  px;
    font-weight: 400;
    color: rgba(176, 186, 198, 0.6);
    letter-spacing: 0.5px;
    word-break: keep-all;
  }
</style>
</head>
<body>
<div class="container">
  <div class="overlay"></div>
  <div class="accent-line"></div>
  <div class="text-area">
    <div class="brand-badge">T.O.P 사업단</div>
    <div class="headline">열심히는 하는데,<br><span class="accent">월급</span>은 제자리걸음?</div>
    <div class="sub-copy">이제 변속할 시간입니다</div>
    <div class="cta-btn">무료 상담 신청 →</div>
  </div>
  <div class="brand-sub">인카금융서비스 코스닥 상장</div>
</div>
</body>
</html>)	rE   resolve_CTA_PX_LH_1_2
_SIZE_54PX_LH_1_25
_SIZE_44PX	_LH_RATIO
_SIZE_28PXbg_pathbg_csss     r   build_html_landscaperl      s    7>>#1'//2C1D  EC  D^( H 8 y    |   	 |    y 0 | W@ @r   c                    | r%| j                         rd| j                          d}nd}d| dt         dt         dt         dt
         d	t         d
t         dt         dt         dS )Nr^   z8'); background-size: cover; background-position: center;zVbackground: radial-gradient(ellipse at center, #1A1E24 0%, #0D0F12 60%, #080A0D 100%);a  <!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<style>
  @font-face {
    font-family: 'Pretendard';
    src: local('Pretendard');
  }
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body {
    width: 1200px;
    height: 1200px;
    overflow: hidden;
    font-family: 'Pretendard', 'Noto Sans KR', -apple-system, sans-serif;
  }
  .container {
    width: 1200px;
    height: 1200px;
    position: relative;
    u{  
  }
  /* 다크 오버레이 — 0.58 opacity */
  .dark-overlay {
    position: absolute;
    inset: 0;
    background: rgba(8, 10, 13, 0.58);
  }
  /* 중앙 반투명 패널 */
  .central-panel {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 960px;
    background: rgba(15, 18, 24, 0.82);
    border: 1px solid rgba(74, 144, 217, 0.25);
    border-radius: 16px;
    padding: 72px 80px 80px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0;
    backdrop-filter: blur(2px);
  }
  /* 상단 브랜드 배지 */
  .brand-badge {
    font-size: zpx;
    font-weight: 700;
    color: #4A90D9;
    letter-spacing: 2px;
    word-break: keep-all;
    margin-bottom: 8px;
    text-align: center;
    line-height: u   ;
  }
  /* 배지 하단 구분선 */
  .badge-divider {
    width: 48px;
    height: 2px;
    background: linear-gradient(to right, transparent, #4A90D9, transparent);
    margin-bottom: 40px;
  }
  .headline {
    font-size: r_   u\  ;
    letter-spacing: -2px;
    word-break: keep-all;
    text-align: center;
    margin-bottom: 28px;
  }
  .headline .accent {
    color: #4A90D9;
  }
  /* 헤드라인 하단 구분선 */
  .headline-divider {
    width: 80px;
    height: 2px;
    background: #C0C0C0;
    opacity: 0.4;
    margin-bottom: 28px;
  }
  .sub-copy {
    font-size: r`   a$  ;
    letter-spacing: -0.5px;
    word-break: keep-all;
    text-align: center;
    margin-bottom: 52px;
  }
  .cta-btn {
    display: inline-flex;
    align-items: center;
    background: #4A90D9;
    color: #FFFFFF;
    font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
    font-size: u<  px;
    font-weight: 700;
    padding: 22px 64px;
    border-radius: 50px;
    letter-spacing: -0.3px;
    word-break: keep-all;
    white-space: nowrap;
  }
  /* 하단 브랜드 서브라인 */
  .brand-sub {
    position: absolute;
    bottom: 40px;
    left: 50%;
    transform: translateX(-50%);
    font-size: u  px;
    font-weight: 400;
    color: rgba(176, 186, 198, 0.5);
    letter-spacing: 0.5px;
    word-break: keep-all;
    white-space: nowrap;
  }
  /* 모서리 메탈릭 액센트 라인 */
  .corner-tl {
    position: absolute;
    top: 40px;
    left: 40px;
    width: 60px;
    height: 2px;
    background: linear-gradient(to right, #4A90D9, transparent);
  }
  .corner-tl::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 2px;
    height: 60px;
    background: linear-gradient(to bottom, #4A90D9, transparent);
  }
  .corner-br {
    position: absolute;
    bottom: 40px;
    right: 40px;
    width: 60px;
    height: 2px;
    background: linear-gradient(to left, #4A90D9, transparent);
  }
  .corner-br::after {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    width: 2px;
    height: 60px;
    background: linear-gradient(to top, #4A90D9, transparent);
  }
</style>
</head>
<body>
<div class="container">
  <div class="dark-overlay"></div>
  <div class="corner-tl"></div>
  <div class="corner-br"></div>
  <div class="central-panel">
    <div class="brand-badge">T.O.P 사업단</div>
    <div class="badge-divider"></div>
    <div class="headline">열심히는 하는데,<br><span class="accent">월급</span>은 제자리걸음?</div>
    <div class="headline-divider"></div>
    <div class="sub-copy">이제 변속할 시간입니다</div>
    <div class="cta-btn">무료 상담 신청 →</div>
  </div>
  <div class="brand-sub">인카금융서비스 코스닥 상장</div>
</div>
</body>
</html>)	rE   ra   rb   rc   
_SIZE_60PXre   rf   _LH_1_35rh   ri   s     r   build_html_squarerp     s    7>>#1'//2C1DD|}i( H 6 y   
 |   $ |    y  | <en nr   c                    |j                   d|j                   dz  }|j                  | d       	 t               5 }|j                  j                         }	 |j                  ||d      }|j                  d|j                          d	       |j                  d
       |j                   j                  dd       |j                  t        |      d       |j                         j                  dz  }t        d|j                    d|dd       	 |j#                          d d d        |j%                         r|j'                          yy# |j#                          w xY w# 1 sw Y   nxY wnE# t(        $ r9}	t        d|	        Y d }	~	|j%                         r|j'                          yyd }	~	ww xY w	 |j%                         r|j'                          y y # |j%                         r|j'                          w w xY w)N_temp_z.htmlutf-8encoding)widthheight)viewportzfile://networkidle)
wait_untili	  Tr?   png)r   typerC   u     [캡처] 완료: rB   z.0frD   u     [캡처] 오류: F)rQ   stem
write_textr   chromiumlaunchnew_pagegotora   wait_for_timeoutrR   
screenshotr   rF   rG   rH   rI   closerE   unlinkrU   )
html_contentrW   rv   rw   	html_tempr<   browserpagesize_kbr[   s
             r   capture_html_to_pngr     s   ""vk.>.>-?u%EEI8 	 !jj'')G
 ''5F1S'T		GI$5$5$7#89m	T%%d+""(((ES%5EB%**,44t;+K,<,<+=R}DQR	 "   	  	  	   #A3'( 		 "  9 s_   
E2 E%B=EE%&E2 E""E%%E.*E2 1G 2	F4;F/	G /F44G #G=c                 R   t         j                  dd       t        j                  dd       i } t        d       t        d       t        d       t	        j                         }t        t        t              }t        |rt        nd       }t         dz  }|j                  |d       t        d	|        t         d
z  }t        ||dd      }||t	        j                         |z
  d| d<   t        d       t        d       t        d       t	        j                         }t        t        t              }t        |rt        nd       }t         dz  }	|	j                  |d       t        d	|	        t         dz  }
t        ||
dd      }||
t	        j                         |z
  d| d<   t        d       t        d       t        d       d}| j                         D ]4  \  }}|d   rdnd}t        d| d| d|d   dd|d           |d   r3d}6 t        d|rd nd!        |rd"S d#S )$NTr?   z=
============================================================u,   [1/2] c26-1200x628 (Landscape) 생성 시작z<============================================================zc26-1200x628.htmlrs   rt   u     [HTML] 저장: zc26-1200x628.pngi  it  )okr   elapsed	landscapeu*   [2/2] c26-1200x1200 (Square) 생성 시작zc26-1200x1200.htmlzc26-1200x1200.pngsquareu   생성 결과r   u   성공u   실패z  z: rB   r   z.1fu	   초) → r   Fu	   
최종: u   전체 성공u   일부 실패r      )
OUTPUT_DIRrR   TMP_DIRrH   timer\   BG_PROMPT_LANDSCAPEBG_PATH_LANDSCAPErl   r~   r   BG_PROMPT_SQUAREBG_PATH_SQUARErp   items)resultst0bg_ok_lshtml_lshtml_path_lspng_path_lsok_lsbg_ok_sqhtml_sqhtml_path_sqpng_path_sqok_sqall_okkeyrstatuss                   r   mainr     s   TD1MM$M.G 
-	
89	&M	B"#68IJH"#4dKG  33LGg6	l^
,-11KdC@E"'WYIYZGK 
-	
67	&M	B"#3^DH(EG  44LGg6	l^
,-22KdDAE$kdiikTVFVWGH 
-	/	&MF--/ QtW(3%r&AiL#5YqykJKwF	 
J&oF
GH1Ar   __main__)returnr   )
r$   r   r%   z
str | Noner&   r   r'   dictr   zrequests.Response)F)rV   r   rW   r   rX   boolr   r   )rj   zPath | Noner   r   )
r   r   rW   r   rv   intrw   r   r   r   )-__doc__
__future__r   rO   r   r   pathlibr   r!   playwright.sync_apir   
gen_configr   r   r   rh   rb   rf   rd   rn   rc   re   rg   ro   __file__rQ   r   r   r   r   r   r    rK   rL   r   r   r   r+   r\   rl   rp   r   r   __name__exitr:   r   r   <module>r      s   #  
    / A A 





	 >  SS

X

+44 11D-1G 3 *	*^FVtr66r zCHHTV r   