
    (<i4                    N   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 ej                  j                  d e ee      j$                               ddl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dZdZdZdZdZ ddZ!ddZ"ddZ#ddZ$e%dk(  r e$        yy)u   Meta 캐러셀 광고 A-1 (Hook 슬라이드) 하이브리드 이미지 생성.

Gemini API로 배경(다이어리+볼펜 오브젝트) 생성 →
Playwright HTML 오버레이로 한글 카피 합성.
출력: 1080x1080 PNG
    )annotationsN)Path)sync_playwright)WORKSPACE_ROOTFONT_DIR*   \   gffffff?g      ?z output/meta-ads/a-group-v6/pilotz_bg_a1_hook.jpgzpilot-A1-hook.pngz_a1_hook_template.htmlu'  Cinematic still photograph for a Meta ad. Scene: A densely written paper planner lying open on a dark desk surface, viewed from slightly above at a low 20-degree angle. The planner pages are packed with handwritten Korean-style schedule entries, strikethroughs, question marks, and circled items — conveying exhausting daily grind. A lone ballpoint pen rests diagonally across the planner. Faint ink smudges and indentations visible on the paper surface. NO people, NO hands, NO faces — objects only. Late-evening atmosphere: fluorescent overhead lights already off, only faint ambient blue-grey light seeping from an unseen monitor. Color palette: deep charcoal, cold blue-grey, muted navy. Zero warmth — no yellows, no oranges in the light. The bottom 35% of the frame is darkest — fading into near-black. Shallow depth of field, pen in focus, planner edges softly blurred. Shot on Sony A7RV, 50mm f/1.8, ISO 3200 — slight film grain visible. Mood: silent exhaustion, invisible labor, numbness. 1:1 square composition 1080x1080. No text, no watermark.zgemini-3.1-flash-image-previewzgemini-3-pro-image-previewz0https://generativelanguage.googleapis.com/v1betaz3https://www.googleapis.com/auth/generative-languagec            	        t         j                         rt        dt                 t         S t        d       t        j                         } t        dt        |        d       t         dt         d}d|  dd	}d
dt        igigdddgid}t        dt         d       t        j                         }t        j                  |||d      }|j                  dv rHt        d|j                   dt                t         dt         d}t        j                  |||d      }|j                          t        j                         |z
  }|j                         }|j!                  dg       }|st#        dt%        |      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!                  d!d"      }t)        j*                  |
d    d#         }d$|v rd%nd&}t         j-                  |      }|j/                  |       t        d'|j0                   d(t        |      d)d*|d+d,       |S c c}w )-uG   Gemini API로 배경 이미지를 생성하고 JPEG로 저장합니다.u,   [배경] 캐시된 배경 이미지 사용: u(   [배경] Gemini API 토큰 획득 중...u   [배경] 토큰 획득 완료 (z chars)z/models/z:generateContentzBearer zapplication/json)AuthorizationzContent-TypepartstextresponseModalitiesIMAGETEXT)contentsgenerationConfigu1   [배경] 이미지 생성 요청 중... (모델: )i,  )headersjsontimeout)i  i  u$   [배경] 모델 접근 불가 (HTTP z). Fallback: 
candidatesu   candidates 없음: Nr   contentc              3  *   K   | ]  }d |v s|  yw)
inlineDataN ).0ps     U/home/jay/workspace/.worktrees/task-2057-dev2/tools/ai-image-gen/gen_pilot_a1_hook.py	<genexpr>z&generate_background.<locals>.<genexpr>m   s     =Q<1+<q=s   	 u'   이미지 데이터 없음. 텍스트:    r   mimeTypez
image/jpegdatajpegz.jpgz.pngu   [배경] 완료:  (,z bytes, z.1fu   초))BG_JPEGexistsprintgcloud_authget_access_tokenlenGEMINI_API_BASEMODEL_ID	BG_PROMPTtimerequestspoststatus_codeFALLBACK_MODEL_IDraise_for_statusr   getRuntimeErrorstrnextbase64	b64decodewith_suffixwrite_bytesname)tokenurlr   payloadstartrespelapsedr#   r   r   
image_partr   textsmimeimage_bytesext	save_paths                    r   generate_backgroundrK   F   s   ~~<WIFG	
45((*E	+CJ<w
?@XhZ/?
@C"5'**G
  34561GV3DEG
 
=hZq
IJIIKE==gGSID :%4T5E5E4FmTeSfgh !*;)<<LM}}S'MiikE!G99;D,+J0T4C0ABCCqMi,00"=E=%=tDJ,1AqVq[vr"AADU2AYKPQQl#''
LAD"":l#;F#CDKdN&C##C(I+&	inn-RK0@/C8GTW=X\
]^ Bs   	JJc                    dt          dt          dt          dt          dt          d|  dt         dt         d	t         d
t         dS )u;   A-1 Hook 슬라이드 HTML 오버레이를 빌드합니다.z<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<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-Medium.otf') format('opentype');
    font-weight: 500;
  }
  @font-face {
    font-family: 'Pretendard';
    src: url('file://u  /Pretendard-Regular.otf') format('opentype');
    font-weight: 400;
  }

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

  body {
    width: 1080px;
    height: 1080px;
    overflow: hidden;
    background: #111820;
  }

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

  /* ─── 배경 이미지 ─── */
  .bg {
    position: absolute;
    inset: 0;
    background: url('file://u  ') center center / cover no-repeat;
    /* 상단으로 갈수록 어두워지는 그라디언트 */
    /* 별도 overlay 레이어로 처리 */
  }

  /* ─── 전체 어둠 오버레이 (분위기) ─── */
  .overlay-dark {
    position: absolute;
    inset: 0;
    background: linear-gradient(
      to bottom,
      rgba(8, 11, 20, 0.80) 0%,
      rgba(8, 11, 20, 0.45) 30%,
      rgba(8, 11, 20, 0.25) 50%,
      rgba(8, 11, 20, 0.55) 68%,
      rgba(4, 6, 14, 0.94) 100%
    );
  }

  /* ─── 상단 숨쉬기 영역 (Top 20%) ─── */
  /* 브랜드 로고 자리 — 현재는 빈 공간 */
  .top-space {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 216px; /* 1080 * 0.20 */
  }

  /* ─── 헤드라인 (Center 45%) ─── */
  /* Top 20% ~ 65% 사이: y=216 ~ y=702, 중심 y=459 */
  /* 약간 위로 당겨 시각적 무게중심을 황금비율 위치에 */
  .headline-wrap {
    position: absolute;
    top: 200px;
    left: 64px;
    right: 64px;
    height: 502px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
  }

  .headline {
    font-size: z?px;
    font-weight: 900;
    color: #F0F0F4;
    line-height: u#  ;
    letter-spacing: -4px;
    word-break: keep-all;
    text-shadow: 0 2px 24px rgba(0,0,0,0.6);
  }

  .headline .accent {
    color: #D4A843;
    text-shadow: 0 2px 20px rgba(212, 168, 67, 0.35);
  }

  /* ─── 하단 서브카피 영역 (Bottom 35%) ─── */
  /* y=702 ~ y=1080 */
  .bottom-zone {
    position: absolute;
    top: 702px;
    left: 0;
    right: 0;
    bottom: 0;
  }

  /* 하단 그라디언트 보강 — 텍스트 가독성 */
  .bottom-gradient {
    position: absolute;
    inset: 0;
    background: linear-gradient(
      to bottom,
      rgba(4, 6, 12, 0.0) 0%,
      rgba(4, 6, 12, 0.85) 40%,
      rgba(4, 6, 12, 0.97) 100%
    );
  }

  .subcopy-wrap {
    position: absolute;
    bottom: 88px;
    left: 64px;
    right: 64px;
  }

  .subcopy {
    font-size: zQpx;
    font-weight: 400;
    color: rgba(210, 212, 222, 0.68);
    line-height: u  ;
    letter-spacing: -1px;
    word-break: keep-all;
  }

  /* 대시 강조 — 목록 단어들 */
  .subcopy .em {
    color: rgba(225, 226, 234, 0.88);
    font-weight: 500;
  }

</style>
</head>
<body>
<div class="canvas">

  <!-- 배경 이미지 -->
  <div class="bg"></div>

  <!-- 전체 어둠 오버레이 -->
  <div class="overlay-dark"></div>

  <!-- 상단 숨쉬기 공간 (브랜드 로고 자리) -->
  <div class="top-space"></div>

  <!-- 헤드라인 (중앙 45%) -->
  <div class="headline-wrap">
    <div class="headline">
      열심히 했다.<br>
      근데 왜 <span class="accent">나만 안 되지?</span>
    </div>
  </div>

  <!-- 하단 서브카피 영역 -->
  <div class="bottom-zone">
    <div class="bottom-gradient"></div>
    <div class="subcopy-wrap">
      <p class="subcopy">
        <span class="em">방문 횟수, 전화 통화, 상담 건수</span>—<br>
        숫자는 쌓이는데 통장은 그대로다.
      </p>
    </div>
  </div>

</div>
</body>
</html>)r   
_SIZE_92PX_LH_1_15
_SIZE_42PX_LH_1_75bg_paths    r   
build_htmlrS      s     Z  
 Z  
 Z  
 Z  
 Z  : %I -&Z |   )R |   .iB B    c                   t        t        | j                                     }t        j	                  |d       t        dt                t        d       t               5 }|j                  j                         }	 |j                  ddd      }|j                  dt        j                          d	
       |j                  d       t        j                  j                  dd       |j                  t        t              d       |j!                          	 ddd       t        j#                         j$                  }t        dt         d|dd       y# |j!                          w xY w# 1 sw Y   TxY w)uN   HTML 오버레이를 Playwright로 캡처하여 최종 PNG를 저장합니다.zutf-8)encodingu&   [오버레이] HTML 템플릿 저장: u*   [오버레이] Playwright 캡처 시작...i8  )widthheight)viewportzfile://networkidle)
wait_untili	  Tparentsexist_okpng)pathtypeNu   [오버레이] 완료: r%   r&   z bytes))rS   r8   resolve	HTML_TEMP
write_textr)   r   chromiumlaunchnew_pagegotowait_for_timeout
OUTPUT_PNGparentmkdir
screenshotclosestatst_size)rR   html_contentr   browserpagesizes         r   capture_overlayru   M  s0   c'//"345L8	29+
>?	
67		 
a**##%	##tt-L#MDII	 1 1 345-IP!!$'##D4#@OOZuO=MMO
 ??$$D	#J<r$q
AB MMO
 
s%   !E0=BEE0E--E00E9c                    t        d       t        d       t        dt                t        d       t        j                  dd       t	               } t        |        t        d       t        dt                t        d       y )Nz<============================================================u/   Meta 캐러셀 A-1 Hook 이미지 생성 시작u   출력: Tr\   z=
============================================================u   완료: )r)   rj   
OUTPUT_DIRrl   rK   ru   rQ   s    r   mainrx   i  sm    	(O	
;<	HZL
!"	(OTD1 "#G G	/	HZL
!"	(OrT   __main__)returnr   )rR   r8   rz   r8   )rR   r   rz   None)rz   r{   )&__doc__
__future__r   r:   sysr0   pathlibr   r1   playwright.sync_apir   
gen_configr   r   r`   insertr8   __file__rk   r*   rO   rM   rN   rP   rw   r'   rj   rc   r/   r.   r4   r-   GEMINI_SCOPErK   rS   ru   rx   __name__r   rT   r   <module>r      s    #  
    / / 3tH~,,- . 

 @@

(
(--
11	? 
0 ,0 DD3vDXC8& zF rT   