
    i:                         d Z ddl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	  ed      Z
e
dz  Ze
dz  Ze
dz  Ze
d	z  Zd
ZdZdZdZdefdZdefdZdefdZdedefdZdedee   ddfdZd Zedk(  r e        yy)u   컨셉 #34 한화손해보험 스타일 — 하이브리드 이미지 생성기
Gemini AI 배경 + HTML 텍스트 오버레이 → 1080×1080px PNG
    N)Path)sync_playwrightzC/home/jay/workspace/output/meta-ads/concept-catalog/34-hanwha-stylezbg.jpgztemplate.htmlz
sample.pngz34-hanwha-style.png)gemini-2.0-flash-preview-image-generationzgemini-2.0-flash-expz0https://generativelanguage.googleapis.com/v1betaa?  Warm inviting lifestyle background, soft morning light through sheer curtains, cozy modern Korean home office or cafe environment, wooden desk with coffee cup, warm golden hour sunlight creating soft bokeh effect, subtle plants and greenery in soft focus background, color palette: warm ivory whites (#FFF8F2), soft amber tones, gentle orange highlights, hints of natural green in background, no people, no faces, no text, no logos, emotional warmth and safety, professionally aspirational yet approachable, 1080x1080 pixels, shallow depth of field, natural photography stylereturnc                  :   ddl } ddl}| j                  j                  d      }|r|S dD ]e  }t	        |      }|j                         s|j                  d      }|j                  d|      }|sF|j                  d      j                         c S  t        d	      )
u4   GEMINI_API_KEY를 .env 또는 .env.keys에서 로드r   NGEMINI_API_KEY)z/home/jay/workspace/.envz/home/jay/workspace/.env.keysutf-8encodingz0(?:export\s+)?GEMINI_API_KEY\s*=\s*"?([^"\n]+)"?   u)   GEMINI_API_KEY를 찾을 수 없습니다)osreenvirongetr   exists	read_textsearchgroupstripRuntimeError)r   r   keyenv_filepcontentms          i/home/jay/workspace/.worktrees/task-2116-dev1/output/meta-ads/concept-catalog/34-hanwha-style/generate.pyget_gemini_api_keyr   (   s    
**..)
*C

Q *N88:kk7k3G		MwWAwwqz''))* B
CC    c                      ddl } | j                  g dddd      }|j                  j                         }|st	        d      |S )u!   gcloud CLI로 access token 획득r   N)gcloudauthzprint-access-tokenT)capture_outputtextchecku   빈 토큰 반환)
subprocessrunstdoutr   r   )r%   resulttokens      r   get_gcloud_tokenr*   ;   sJ    ^^0$d  F MM!E.//Lr   c            	         t        d       t        d       t        d       	 t               } t        dt        |        d       t         d|  }ddi}dd	t        igigd
ddgid}t        d       t        j
                         }t        j                  |||d      }|j                  dv r/t         d|  }t        d       t        j                  |||d      }|j                          t        j
                         |z
  }|j%                         }|j'                  d'g       }|s$t#        d(t%        j(                  |      dd)        |d   j'                  d*i       j'                  dg       }t+        d+ |D        d      }|5|D cg c]  }d	|v s|j'                  d	d,       }}t#        d-|dd.        |d/   j'                  d0d1      }t-        j.                  |d/   d2         }d3|v rd4nd5}t0        j3                  |      }|j5                  |       |j7                         j8                  d6z  }t        d7|j:                   d8|d9d:|d;d<       |S # t        $ ru}t        d| d       	 t               }	t        dt        |	       d       	 ddl}
|
j                  j                  dd       ddl}|j!                         }	t        d       n# t        $ r Y nw xY wdD ]  }t         d| d}d|	 dd}dd	t        igigd
ddgid}t        d |        t        j
                         }t        j                  |||d!      }|j                  d"vr)|j                          t        j
                         |z
  } n)t        d#| d$|j                   d        t#        d%      n # t        $ r}t#        d&|       |d}~ww xY wY d}~d}~ww xY wc c}w )=u%   Gemini API로 배경 이미지 생성   ────────────────────────────────────────────────────────────u&   Step 1: Gemini 배경 이미지 생성u     인증: API Key (길이: )zF/models/gemini-2.0-flash-preview-image-generation:generateContent?key=Content-Typezapplication/jsonpartsr#   responseModalitiesIMAGETEXT)contentsgenerationConfigu3     모델: gemini-2.0-flash-preview-image-generationx   )headersjsontimeout)    z1/models/gemini-2.0-flash-exp:generateContent?key=u'     모델 fallback: gemini-2.0-flash-expu     API Key 실패: u"   . gcloud 토큰으로 재시도...u'     인증: gcloud Bearer token (길이: r   Nz&/home/jay/workspace/tools/ai-image-genu     인증: SA Bearer token)r   zgemini-3.1-flash-image-previewzgemini-3-pro-image-previewz/models/z:generateContentzBearer )Authorizationr.   u     모델 시도:    )r9   i  r:   z  u    실패 (HTTP u   모든 모델 시도 실패u   배경 생성 실패: 
candidatesu   candidates 없음: i,  r   c              3   *   K   | ]  }d |v s|  yw)
inlineDataN ).0r   s     r   	<genexpr>z&generate_background.<locals>.<genexpr>   s     =Q<1+<q=s   	 u'   이미지 데이터 없음. 텍스트:    r?   mimeTypez
image/jpegdatajpegz.jpgz.png   u
     완료:  (.0fz KB, z.1fu   초))printr   lenGEMINI_API_BASEGEMINI_PROMPTtimerequestspoststatus_coderaise_for_status	Exceptionr*   syspathinsertgcloud_authget_service_account_tokenr   r7   r   dumpsnextbase64	b64decodeBG_PATHwith_suffixwrite_bytesstatst_sizename)api_keyurlr6   payloadstartrespurl2elapsedapi_errr)   rU   rX   modele2rF   r=   r/   
image_partr   
text_parts	mime_typeimage_bytesextbg_pathsize_kbs                            r   generate_backgroundru   H   s$   	*	
23	*<F$&+CL>;< !!ghogpq!#56!V]$;#<=>!57H I
 	CE		}}S'Mz)%&&WX_W`aD;===wWcRD))+%T 99;D,+J0D1A$31G0HIJJqMi,00"=E=%=tDJ16FA&A+aeeFB'F
FDZPRQR^DTUVV<(,,ZFI"":l#;F#CDKi'&VC!!#&G$lln$$t+G	Jw||nBwsm5T
JKNw  &F"7)+MNO$	F$&E;CJ<qIJ#KL"#==?13  G B()%8HI'.ug%6$6
 #*V],C+D!E F)=?P(Q )%12		}}S'QTU##?:))+"iikE1G5'0@0@/ACD#B& ##@AA 
  	F!7t<=2E	F A&F` Gsm   C I 		OO
O"O2"N,?KN,	K!N, K!!C
N,+O,	O	5OO		OOrs   c                     t        d       t        d       t        d       d| j                          }d| d}t        j                  |d       t        d	t                t        S )
u,   HTML 텍스트 오버레이 템플릿 생성x   
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─
─u   Step 2: HTML 템플릿 생성r,   file://u  <!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1080">
<style>
  @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;600;700;800;900&display=swap');

  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }

  html, body {
    width: 1080px;
    height: 1080px;
    overflow: hidden;
    background: #FFF8F2;
  }

  .canvas {
    position: relative;
    width: 1080px;
    height: 1080px;
    overflow: hidden;
    font-family: 'Pretendard', 'Noto Sans KR', 'Apple SD Gothic Neo', sans-serif;
  }

  /* 배경 이미지 레이어 */
  .bg-layer {
    position: absolute;
    top: 0; left: 0;
    width: 1080px;
    height: 1080px;
    background-image: url('ur  ');
    background-size: cover;
    background-position: center;
  }

  /* 아이보리 반투명 오버레이 */
  .overlay {
    position: absolute;
    top: 0; left: 0;
    width: 1080px;
    height: 1080px;
    background: rgba(255, 248, 242, 0.82);
  }

  /* 콘텐츠 레이어 */
  .content {
    position: absolute;
    top: 0; left: 0;
    width: 1080px;
    height: 1080px;
    padding: 56px 72px;
    display: flex;
    flex-direction: column;
  }

  /* 상단 헤더 */
  .header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    margin-bottom: 16px;
  }

  .brand-name {
    font-size: 40px;
    font-weight: 400;
    color: #4A4A4A;
    letter-spacing: 0.08em;
  }

  .header-tag {
    font-size: 40px;
    font-weight: 500;
    color: #FF6B35;
    letter-spacing: 0.05em;
  }

  /* 오렌지 상단 구분선 */
  .orange-line {
    width: 300px;
    height: 4px;
    background: #FF6B35;
    border-radius: 2px;
    margin-bottom: 28px;
    flex-shrink: 0;
  }

  /* 메인 헤드라인 */
  .main-headline {
    margin-bottom: 20px;
    flex-shrink: 0;
  }

  .headline-line1 {
    font-size: 96px;
    font-weight: 800;
    color: #2C2C2C;
    letter-spacing: -0.01em;
    line-height: 1.15;
    display: block;
  }

  .headline-line2 {
    font-size: 96px;
    font-weight: 800;
    color: #FF6B35;
    letter-spacing: -0.015em;
    line-height: 1.15;
    display: block;
  }

  /* 공감 서브카피 */
  .empathy-copy {
    font-size: 44px;
    font-weight: 400;
    color: #4A4A4A;
    line-height: 1.45;
    letter-spacing: 0.00em;
    margin-bottom: 24px;
    flex-shrink: 0;
  }

  /* 해결책 카드 */
  .solution-card {
    background: #FFFFFF;
    border-left: 5px solid #FF6B35;
    border-radius: 12px;
    padding: 28px 36px;
    margin-bottom: 24px;
    box-shadow: 0 4px 20px rgba(255, 107, 53, 0.10);
    flex-shrink: 0;
  }

  .solution-main {
    font-size: 60px;
    font-weight: 700;
    color: #FF6B35;
    letter-spacing: -0.005em;
    line-height: 1.3;
    display: block;
    margin-bottom: 10px;
  }

  .solution-sub {
    font-size: 44px;
    font-weight: 400;
    color: #4A4A4A;
    letter-spacing: 0.01em;
    line-height: 1.4;
    display: block;
  }

  /* 혜택 배지 행 */
  .badges {
    display: flex;
    flex-wrap: nowrap;
    gap: 12px;
    margin-bottom: 24px;
    flex-shrink: 0;
  }

  .badge-orange {
    background: rgba(255, 107, 53, 0.12);
    border: 1.5px solid rgba(255, 107, 53, 0.40);
    color: #FF6B35;
    font-size: 40px;
    font-weight: 600;
    padding: 10px 22px;
    border-radius: 24px;
    display: inline-block;
    letter-spacing: 0.03em;
    white-space: nowrap;
  }

  .badge-green {
    background: rgba(43, 168, 96, 0.12);
    border: 1.5px solid rgba(43, 168, 96, 0.40);
    color: #2BA860;
    font-size: 40px;
    font-weight: 600;
    padding: 10px 22px;
    border-radius: 24px;
    display: inline-block;
    letter-spacing: 0.03em;
    white-space: nowrap;
  }

  /* CTA 버튼 */
  .cta-wrap {
    margin-bottom: 20px;
    flex-shrink: 0;
  }

  .cta-btn {
    display: inline-block;
    background: #FF6B35;
    color: #FFFFFF;
    font-size: 44px;
    font-weight: 700;
    padding: 22px 56px;
    border-radius: 12px;
    letter-spacing: 0.04em;
    box-shadow: 0 6px 24px rgba(255, 107, 53, 0.30);
    white-space: nowrap;
  }

  /* 하단 가로선 */
  .bottom-line {
    width: 100%;
    height: 2px;
    background: rgba(255, 107, 53, 0.25);
    margin-bottom: 14px;
    flex-shrink: 0;
  }

  /* 하단 서브텍스트 */
  .footer-text {
    font-size: 40px;
    font-weight: 300;
    color: #6B6B6B;
    letter-spacing: 0.03em;
    text-align: center;
    flex-shrink: 0;
  }
</style>
</head>
<body>
<div class="canvas">
  <!-- 배경 이미지 -->
  <div class="bg-layer"></div>
  <!-- 아이보리 반투명 오버레이 -->
  <div class="overlay"></div>

  <!-- 텍스트 콘텐츠 -->
  <div class="content">

    <!-- 상단 헤더 -->
    <div class="header">
      <span class="brand-name">T.O.P 사업단</span>
      <span class="header-tag">함께 성장하는 팀</span>
    </div>

    <!-- 오렌지 구분선 -->
    <div class="orange-line"></div>

    <!-- 메인 헤드라인 -->
    <div class="main-headline">
      <span class="headline-line1">고객 없어서</span>
      <span class="headline-line2">매일이 전쟁인가요?</span>
    </div>

    <!-- 공감 서브카피 -->
    <div class="empathy-copy">
      지인에게 아쉬운 소리 하고,<br>
      거절받고, 상처받는 영업은 이제 그만.
    </div>

    <!-- 해결책 카드 -->
    <div class="solution-card">
      <span class="solution-main">30여종 무상 DB가 그 전쟁을 끝냅니다</span>
      <span class="solution-sub">설계 매니저 + 서포트 매니저, 당신 곁에</span>
    </div>

    <!-- 혜택 배지 -->
    <div class="badges">
      <span class="badge-orange">1:1 밀착 코칭</span>
      <span class="badge-orange">신입 최대 1,000만원</span>
      <span class="badge-green">무상 DB</span>
    </div>

    <!-- CTA 버튼 -->
    <div class="cta-wrap">
      <span class="cta-btn">T.O.P와 함께 시작하기 →</span>
    </div>

    <!-- 하단 구분선 -->
    <div class="bottom-line"></div>

    <!-- 하단 푸터 -->
    <div class="footer-text">고객도, 성장도 혼자 하지 않습니다 · 인카금융서비스 코스닥 상장</div>

  </div>
</div>
</body>
</html>r	   r
   
     저장: )rK   resolveTEMPLATE_PATH
write_text)rs   bg_urihtmls      r   create_html_templater      sv    	,	
)*	*w()*F#F #8 $GbDH	 TG4	J}o
&'r   template_pathoutput_pathsc                    t        d       t        d       t        d       d| j                          }t               5 }|j                  j	                  dg      }	 |j                  ddd	      }|j                  |d
       |j                  d       |j                  d       |D ]q  }|j                  j                  dd       |j                  t        |      dddddd       |j                         j                  dz  }t        d| d|dd       s 	 |j                          	 ddd       y# |j                          w xY w# 1 sw Y   yxY w)u.   Playwright로 HTML 스크린샷 → PNG 저장rw   u   Step 3: Playwright 캡처r,   rx   z--font-render-hinting=none)argsi8  )widthheight)viewportnetworkidle)
wait_untilz() => document.fonts.readyi  Tparentsexist_okpngr   )xyr   r   )rV   typecliprH   ry   rI   rJ   z KB)N)rK   rz   r   chromiumlaunchnew_pagegotoevaluatewait_for_timeoutparentmkdir
screenshotstrra   rb   close)r   r   template_urlr   browserpageout_pathrt   s           r   capture_imager     sI   	,	
%&	*]22456L		 a**##*F)G#H	##tt-L#MDIIl}I= MM67!!$'( B%%dT%BS]1ST_cosEtu"--/11D8
8*Bwsm4@A	B MMO!   MMO! s$   EC D9E9EEEc                  x   t         j                  dd       t        d       t        d       t        d       t               } t	        |       }t        |t        t        g       t        d       t        d       t        dt                t        dt                t        d	t                t        d       y )
NTr   z<============================================================u<   컨셉 #34 — 한화손해보험 스타일 이미지 생성z=
============================================================u   완료!u     sample.png  → u     final.png   → u     template    → )	
OUTPUT_DIRr   rK   ru   r   r   
SAMPLE_PNG	FINAL_PNGr{   )rs   r   s     r   mainr     s    TD1	(O	
HI	(O "#G )1M -*i!89	/	)	zl
+,	yk
*+	}o
./	(Or   __main__)__doc__r\   r7   rU   rO   pathlibr   rP   playwright.sync_apir   r   r^   r{   r   r   MODEL_IDFALLBACK_MODELrM   rN   r   r   r*   ru   r   listr   r   __name__r@   r   r   <module>r      s      
    / WX

x
_,,&
..	 7'DJ DC D&
# 
XT Xvn$ n4 nb	 T$Z D 60 zF r   