
    i@                       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 eZdZdZd	Zd
ZdZdZdZdZdZdZedz  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Z#d8dZ$d9dZ%de de de de d e d!e d"e d#e d$e d%e d&e d'e d(e d)e d*Z&d+e de de de d e d!e d,e d-e d.e d/e d0e d1e d2e d3e d4Z'd:d5Z(d6 Z)e*d7k(  r e)        yy);u   Cell-7 서울대보험쌤 × 정당한 보상 배너 생성 (v3.2 최종본)
- meta-feed-1080x1080.png  (Template B: 전체 배경 + 크림 패널)
- google-resp-1200x628.png (Template A: 좌측 텍스트 + 우측 배경)
    )annotationsN)Path)sync_playwright)
CTA_MIN_PXFONT_DIRWORKSPACE_ROOT,   0   6   <   4   8   L   g333333?g      ?ztools/ai-image-genzoutput/banners/cell-7-snu-fairz@output/meta-ads/concept-catalog/45-oneshow-finance-luxury/bg.jpgzbg-cell7-1080.pngzbg-cell7-1200.pngz0https://generativelanguage.googleapis.com/v1betaz)gemini-2.0-flash-preview-image-generationzgemini-2.0-flash-expa?  Warm cozy study room with mahogany bookshelves filled with books, a vintage desk lamp casting warm golden light, leather armchair, reading glasses on an open notebook, warm brown and amber tones, intimate and professional atmosphere, photorealistic interior photography, no people, no text, no logos. Square 1:1 format.aT  Wide panoramic view of a warm professional study with floor-to-ceiling mahogany bookshelves, vintage brass desk lamp glowing warmly, leather-bound books and a coffee cup on the desk, golden hour sunlight filtering through curtains, warm brown and amber atmosphere, photorealistic, no people, no text, no logos. Wide landscape 1.91:1 format.c                     t         j                  j                  dt        t                     dd l} | j                  d      xs dS )Nr   GEMINI_API_KEY'AIzaSyDEy7IcOoTbQsI4nxfPOw3ZFbYqEL_PgFU)syspathinsertstrBASE_DIRgcloud_authget_api_key)r   s    ^/home/jay/workspace/.worktrees/task-2116-dev1/tools/ai-image-gen/gen_cell7_snu_fair_banners.pyr   r   B   s3    HHOOAs8}%""#34a8aa    c                   |j                         r/|j                         j                  dkD  rt        d| d|        yt	               }dd| igigddd	gid
}t
        t        dg}|D ][  }t         d| d| }t        d| d| d       	 t        j                  ||d      }|j                  dk7  r)t        d|j                   d|j                  dd         u|j                         }
|
j                  dg       }|st        d       |d   j                  di       j                  dg       }t        d |D        d      }|s5t        d|D cg c]  }t!        |j#                                c}        t%        j&                  |d   d         }|j)                  |       t        d| d t+        |      d!d"        y y## t        $ r}	t        d|	        Y d}	~	}d}	~	ww xY wc c}w )$u>   Gemini API로 배경 이미지 생성. 성공 시 True 반환.i0u  [u   ] 기존 배경 재사용: TpartstextresponseModalitiesIMAGETEXT)contentsgenerationConfigzgemini-1.5-flashz/models/z:generateContent?key=u   ] Gemini 요청 중 (모델: z)...x   )jsontimeoutu     요청 예외: N   z  HTTP z: 
candidatesu     candidates 없음r   contentc              3  *   K   | ]  }d |v s|  yw)
inlineDataN ).0ps     r   	<genexpr>z-generate_gemini_background.<locals>.<genexpr>i   s     A|q/@1As   	u!     inlineData 없음. parts keys: r,   dataz  [OK]  (,z bytes)F)existsstatst_sizeprintr   GEMINI_MODELGEMINI_FALLBACK_MODELGEMINI_API_BASErequestspost	Exceptionstatus_coder   r&   getnextlistkeysbase64	b64decodewrite_byteslen)promptout_pathlabelapi_keypayloadmodelsmodelurlresper1   r)   r   
image_partr/   	img_bytess                   r   generate_gemini_backgroundrS   H   s	   X]]_44v=%3H:>?mG 01231GV3DEG
 13EFF  !%0EgYO%5eWDAB	==7C@D
 s"GD,,-R		$3/@AByy{XXlB/
')1!!)R044WbAAeA4H
5u6U!tAFFH~6U5VWX$$Z%=f%EF	Y'zC	N1#5W=>9< 3  	%aS)*	" 7Vs   G G-	G*G%%G*z<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=1080">
  <style>
    @font-face { font-family: 'Pretendard'; src: url('file://z}/Pretendard-Black.otf') format('opentype'); font-weight: 900; }
    @font-face { font-family: 'Pretendard'; src: url('file://z/Pretendard-ExtraBold.otf') format('opentype'); font-weight: 800; }
    @font-face { font-family: 'Pretendard'; src: url('file://z|/Pretendard-Bold.otf') format('opentype'); font-weight: 700; }
    @font-face { font-family: 'Pretendard'; src: url('file://z/Pretendard-SemiBold.otf') format('opentype'); font-weight: 600; }
    @font-face { font-family: 'Pretendard'; src: url('file://z~/Pretendard-Medium.otf') format('opentype'); font-weight: 500; }
    @font-face { font-family: 'Pretendard'; src: url('file://aG  /Pretendard-Regular.otf') format('opentype'); font-weight: 400; }

    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    html, body { width: 1080px; height: 1080px; overflow: hidden; font-family: 'Pretendard', sans-serif; word-break: keep-all; }

    .canvas { position: relative; width: 1080px; height: 1080px; overflow: hidden; }
    .bg-image { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; }
    .dark-overlay { position: absolute; inset: 0; background: rgba(62, 39, 35, 0.58); }

    .text-panel {
      position: absolute;
      top: 50%; left: 50%;
      transform: translate(-50%, -50%);
      width: 860px;
      padding: 56px 48px;
      background: rgba(255, 248, 231, 0.82);
      border-radius: 12px;
      backdrop-filter: blur(4px);
      box-shadow: 0 8px 32px rgba(0,0,0,0.2);
      display: flex; flex-direction: column;
      align-items: center;
      gap: 28px; text-align: center;
    }

    .badge {
      display: inline-block;
      background: #C9A84C; color: #1A0E00;
      font-size: z7px; font-weight: 700;
      height: 52px; line-height: zpx;
      padding: 0 24px;
      border-radius: 6px;
      white-space: nowrap;
      letter-spacing: -0.03em;
    }

    .headline {
      font-size: z9px; font-weight: 700;
      color: #3E2723; line-height: zp;
      letter-spacing: -1.5px;
    }
    .headline .gold { color: #C9A84C; }

    .sub-copy {
      font-size: zpx; font-weight: 500;
      color: #A07828;
      white-space: nowrap;
      letter-spacing: -0.02em;
    }

    .sub-copy-2 {
      font-size: a  px; font-weight: 500;
      color: #A07828;
      white-space: nowrap;
      letter-spacing: -0.02em;
    }

    .cta-btn {
      display: inline-block;
      background: linear-gradient(135deg, #C9A84C 0%, #D4B87A 100%);
      color: #1A0E00;
      font-size: z7px; font-weight: 700;
      height: 76px; line-height: u+  px;
      padding: 0 40px;
      min-width: 360px;
      width: auto;
      border-radius: 6px;
      box-shadow: 0 6px 24px rgba(201,168,76,0.45);
      white-space: nowrap;
      letter-spacing: -0.03em;
      text-align: center;
    }
  </style>
</head>
<body>
  <div class="canvas">
    <img class="bg-image" src="bg-cell7-1080.png" alt="background">
    <div class="dark-overlay"></div>
    <div class="text-panel">
      <span class="badge">서울대보험쌤</span>
      <p class="headline">3년째 참았던 수수료,<br>저는 <span class="gold">바꿨어요</span></p>
      <p class="sub-copy">경력직 직전연봉 최대 50% 정착지원금</p>
      <p class="sub-copy-2">검증된 조직</p>
      <div class="cta-btn">수익 구조 직접 확인 →</div>
    </div>
  </div>
</body>
</html>
z<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=1200">
  <style>
    @font-face { font-family: 'Pretendard'; src: url('file://aA  /Pretendard-Regular.otf') format('opentype'); font-weight: 400; }

    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    html, body { width: 1200px; height: 628px; overflow: hidden; font-family: 'Pretendard', sans-serif; word-break: keep-all; }

    .canvas { position: relative; width: 1200px; height: 628px; overflow: hidden; }
    .bg-image { position: absolute; right: 0; top: 0; width: 65%; height: 100%; object-fit: cover; object-position: center right; }

    .gradient-overlay {
      position: absolute; inset: 0;
      background: linear-gradient(
        to right,
        rgba(62,39,35,0.95) 0%,
        rgba(62,39,35,0.95) 40%,
        rgba(62,39,35,0.85) 52%,
        rgba(62,39,35,0.50) 68%,
        rgba(62,39,35,0.15) 82%,
        transparent 100%
      );
    }

    .accent-bar {
      position: absolute;
      top: 80px; left: 0;
      width: 4px; height: 460px;
      background: linear-gradient(to bottom, #C9A84C, #D4B87A);
      border-radius: 2px;
    }

    .text-area {
      position: absolute;
      top: 0; left: 0;
      width: 62%; height: 100%;
      padding: 48px 56px 48px 60px;
      display: flex; flex-direction: column;
      justify-content: center;
      gap: 20px;
    }

    .badge {
      display: inline-block;
      background: #C9A84C; color: #1A0E00;
      font-size: z7px; font-weight: 700;
      height: 48px; line-height: zpx;
      padding: 0 20px;
      border-radius: 6px;
      white-space: nowrap;
      letter-spacing: -0.03em;
      align-self: flex-start;
      margin-bottom: 6px;
    }

    .headline {
      font-size: z9px; font-weight: 700;
      color: #FFF8E7; line-height: zp;
      letter-spacing: -1.2px;
    }
    .headline .gold { color: #C9A84C; }

    .sub-copy {
      font-size: zpx; font-weight: 500;
      color: #C9A84C;
      text-shadow: 0 1px 3px rgba(0,0,0,0.5);
      white-space: nowrap;
      letter-spacing: -0.02em;
    }

    .sub-copy-2 {
      font-size: a"  px; font-weight: 500;
      color: rgba(255,248,231,0.8);
      text-shadow: 0 1px 3px rgba(0,0,0,0.5);
      white-space: nowrap;
    }

    .cta-btn {
      display: inline-block;
      background: linear-gradient(135deg, #C9A84C 0%, #D4B87A 100%);
      color: #1A0E00;
      font-size: z7px; font-weight: 700;
      height: 56px; line-height: ul  px;
      padding: 0 32px;
      min-width: 220px;
      width: auto;
      border-radius: 6px;
      box-shadow: 0 4px 16px rgba(201,168,76,0.35);
      white-space: nowrap;
      letter-spacing: -0.03em;
      align-self: flex-start;
      text-align: center;
      margin-top: 16px;
    }
  </style>
</head>
<body>
  <div class="canvas">
    <img class="bg-image" src="bg-cell7-1200.png" alt="background">
    <div class="gradient-overlay"></div>
    <div class="accent-bar"></div>
    <div class="text-area">
      <span class="badge">서울대보험쌤</span>
      <p class="headline">3년째 참았던 수수료,<br>저는 <span class="gold">바꿨어요</span></p>
      <p class="sub-copy">경력직 직전연봉 최대 50%<br>정착지원금 | 검증된 조직</p>
      <div class="cta-btn">수익 구조 직접 확인 →</div>
    </div>
  </div>
</body>
</html>
c           
        t               5 }|j                  j                         }	 |j                  ||d      }|j	                  d| j                          d       |j                  d       |j                  t        |      ddd||d	
       |j                         j                  dz  }t        d|j                   d| d       |j                          	 ddd       y# |j                          w xY w# 1 sw Y   yxY w)u-   Playwright로 HTML을 PNG로 캡처합니다.)widthheight)viewportzfile://networkidle)
wait_untili	  pngr   )xyrU   rV   )r   typeclip   u     [렌더링 완료] r2    KB)N)r   chromiumlaunchnew_pagegotoresolvewait_for_timeout
screenshotr   r5   r6   r7   nameclose)	html_pathrU   rV   rH   r/   browserpagesize_kbs           r   render_html_to_pngrn   Y  s    		 a**##%	##u-O#PDII	 1 1 345-IP!!$'OO]1uG  
 mmo--5G)(--7)4HIMMO  MMO s#   C;B%C&C;&C88C;;Dc                 d   t        d       t        d       t        d       t        j                  dd       t        d       t        t        t
        d      } | sBt        dt                dd l}|j                  t        t
               t        d	t
                t        t        t        d
      }|sBt        dt                dd l}|j                  t        t               t        d	t                t        d       t        dz  }t        dz  }|j                  t        d       t        d|        |j                  t        d       t        d|        t        d       t        dz  }t        dz  }t        d       t        |dd|       t        d       t        |dd|       t        d       t        d       t
        t        ||||g}|D ]O  }|j                         r.t        d| d|j!                         j"                  dz   d       At        d| d        Q t        d       y )!Nz<============================================================u;   Cell-7 서울대보험쌤 × 정당한 보상 배너 생성T)parentsexist_oku!   
[Step 1] 배경 이미지 생성u   1080x1080 배경u#     Gemini 실패 → 폴백 사용: r   u     폴백 복사 완료: u   1200x628 배경u   
[Step 2] HTML 파일 저장zmeta-feed-1080x1080.htmlzgoogle-resp-1200x628.htmlzutf-8)encodingu
     저장: u   
[Step 3] Playwright 렌더링zmeta-feed-1080x1080.pngzgoogle-resp-1200x628.pngu     1080x1080 렌더링 중...i8  u     1200x628 렌더링 중...i  it  z=
============================================================u   완료!z  OK  z  (r_   r`   z  !!  u
     (없음))r7   
OUTPUT_DIRmkdirrS   PROMPT_1080BG_1080_PATHFALLBACK_BGshutilcopy2PROMPT_1200BG_1200_PATH
write_text	HTML_1080	HTML_1200rn   r4   r5   r6   )	ok_1080rx   ok_1200html_1080_pathhtml_1200_pathout_1080out_1200filesfs	            r   mainr   n  s   	(O	
GH	(OTD1 

./(lDVWG3K=AB[,/(78(lDUVG3K=AB[,/(78 

)*"<<N"==Ni':	J~&
'(i':	J~&
'( 

+,55H66H	
()~tT8<	
'(~tS(; 
/	)<S[\E *88:F1#S!1!1T!9 :$?@F1#Z()	*
 
(Or   __main__)returnr   )rG   r   rH   r   rI   r   r   bool)
rj   r   rU   intrV   r   rH   r   r   None)+__doc__
__future__r   rC   r   timepathlibr   r;   playwright.sync_apir   
gen_configr   r   r   _CTA_PX
_SIZE_44PX
_SIZE_48PX
_SIZE_54PX
_SIZE_60PX_LH_48PX_LH_52PX_LH_56PX_LH_76PX_LH_1_2_LH_1_25r   rs   rw   rv   r{   r:   r8   r9   ru   rz   r   rS   r}   r~   rn   r   __name__r-   r   r   <module>r      sG  
 #  
    / ; ; 




 00>>
aa////D:. 7 . b+`? @Hj I??Gj I??Gj I??Gj I??Gj I??Gj I6  ""* ,  $$,: .    
  ""* ,Sc	J? @Hj I??Gj I??Gj I??Gj I??Gj I??Gj *IT  ""* 
,  $$+9 -    
  ""* ,ww	x*=@ zF r   