
    (<i%                        d Z ddlm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	m
Z
 ddlmZmZmZmZ  ej                   e      Z G d de      Zdd	Zdd
ZddZddZedk(  r e        yy)u  
blog-writer 출력물 → TistoryPublisher 입력 연결 파이프라인.

CLI:
  python3 scripts/blog/publish_pipeline.py --file <html_path> --private
  python3 scripts/blog/publish_pipeline.py --file post.html --blog myblog --category 123 --tags AI,Python

HTML 파일 구조 (자동 파싱 지원):
  <title>글 제목</title>  또는  <h1>글 제목</h1>
  <meta name="keywords" content="태그1, 태그2">
  <body> ... HTML 본문 ... </body>
    )annotationsN)
HTMLParser)Path)LoginRequiredErrorPublishErrorSessionExpiredErrorTistoryPublisherc                  <     e Zd ZdZd fdZddZddZd	dZ xZS )
_HtmlMetaParseruG   HTML 파일에서 제목·태그·본문을 추출하는 경량 파서.c                h    t         |           d| _        d| _        d| _        d| _        d| _        y )N F)super__init__titlekeywords	_in_title_in_h1_h1_captured)self	__class__s    N/home/jay/workspace/.worktrees/task-2057-dev2/scripts/blog/publish_pipeline.pyr   z_HtmlMetaParser.__init__(   s2    
$!"'    c                   |dk(  rd| _         y |dk(  r| j                  sd| _        y |dk(  r[|D ci c]  \  }}||
 }}}|j                  d      xs d}|j	                         dk(  r|j                  d      xs d}|| _        y y y c c}}w )	Nr   Th1metanamer   r   content)r   r   r   getlowerr   )r   tagattrskv	attr_dictname_valcontent_vals           r   handle_starttagz_HtmlMetaParser.handle_starttag0   s    '>!DND[!2!2DKF]AF/GA1/GI/G }}V,2H~~:-'mmI6<" + . /Gs   Bc                ^    |dk(  rd| _         y |dk(  r| j                  rd| _        d| _        y y )Nr   Fr   T)r   r   r   )r   r    s     r   handle_endtagz_HtmlMetaParser.handle_endtag<   s2    '>"DND[{{$(!DK r   c                    | j                   r"| j                  s|j                         | _        y | j                  r1| j                  s$| j                  xs |j                         | _        y y y )N)r   r   stripr   r   )r   datas     r   handle_dataz_HtmlMetaParser.handle_dataD   sH    >>$**DJ[[!2!23tzz|DJ "3[r   returnNone)r    strr!   zlist[tuple[str, str | None]]r/   r0   )r    r1   r/   r0   )r,   r1   r/   r0   )	__name__
__module____qualname____doc__r   r'   r)   r-   __classcell__)r   s   @r   r   r   %   s    Q(
, 4r   r   c                   | j                         st        d|        	 | j                  d      }t               }	 |j                  |       |j                  |j                  |dS # t        $ r7 	 | j                  d      }n # t        $ r}t        d|       |d}~ww xY wY uw xY w# t        $ r}t        d|       |d}~ww xY w)	u:  
    HTML 파일을 읽어 제목·태그·본문을 추출한다.

    Args:
        html_path: HTML 파일 경로.

    Returns:
        dict: {"title": str, "keywords": str, "body": str (전체 HTML)}

    Raises:
        FileNotFoundError: 파일이 없을 때.
        ValueError: HTML 파싱 실패 시.
    u(   HTML 파일을 찾을 수 없습니다: zutf-8)encodingzeuc-kru5   HTML 파일 인코딩을 인식할 수 없습니다: Nu   HTML 파싱 실패: )r   r   body)
existsFileNotFoundError	read_textUnicodeDecodeError	Exception
ValueErrorr   feedr   r   )	html_pathraw_htmlexcparsers       r   parse_html_filerE   K   s     "J9+ VWWe&&&8 F@H
 OO   e	e **H*=H 	eTUXTYZ[add	e e  @/u56C?@sL   A( B+ (	B(2BB(	B"BB""B('B(+	C4CCc                    t        j                  ddt         j                  t              } | j	                  dddd       | j	                  d	d
dd       | j	                  ddd d       | j	                  dddd       | j	                  ddd d       | j	                  ddd d       | j	                  dd
dd       | j	                  ddd
dd        | S )!Npublish_pipelineu;   HTML 파일을 티스토리에 발행하는 파이프라인)progdescriptionformatter_classepilogz--fileT	HTML_PATHu%   발행할 HTML 파일 경로 (필수))requiredmetavarhelpz	--private
store_trueu;   비공개 발행 (기본값, PoC에서는 항상 비공개))actiondefaultrO   z--blog	BLOG_NAMEuO   티스토리 블로그 서브도메인 (기본: TISTORY_BLOG_NAME 환경변수))rN   rR   rO   z
--categoryCATEGORY_ID0u+   카테고리 ID (기본: 0 = 분류 없음)z--tagsz	TAG1,TAG2uM   쉼표 구분 태그 목록 (미지정 시 HTML <meta keywords>에서 추출)z--titleTITLEu@   글 제목 (미지정 시 HTML <title> 또는 <h1>에서 추출)z--loginFu<   강제 대화형 로그인 실행 (세션 갱신 시 사용)z	--verbosez-vu   상세 로그 출력)argparseArgumentParserRawDescriptionHelpFormatterr5   add_argument)rD   s    r   build_arg_parserr[   w   s6   $$Q <<	F 4	   J	   ^	   :	   \	   O	   K	   #   Mr   c                f	  K   t        | j                        }	 t        |      }| j                  xs |d   }|st	        dt
        j                         y| j                  rH| j                  j                  d      D cg c]#  }|j                         s|j                         % }}nH|d	   rA|d	   j                  d      D cg c]#  }|j                         s|j                         % }}ng }d
}t	        d       t	        d|        t	        d|        t	        d| j                          t	        d|        t	        d|        	 t        | j                        }|4 d{    | j                  r.t	        d       |j!                          d{    t	        d       	 |j#                          d{   }	|	sqt	        dt
        j                         t	        dt
        j                         t	        d| j                   dt
        j                         	 ddd      d{    y	 |j%                  ||d   | j                  ||       d{   }
ddd      d{    t	        d        t	        d!
        t	        d"|        y## t        $ r(}t	        d| t
        j                         Y d}~yd}~wt        $ r(}t	        d| t
        j                         Y d}~yd}~ww xY wc c}w c c}w # t        $ r(}t	        d| t
        j                         Y d}~yd}~ww xY w7 7 7 # t        $ r}}t	        d| t
        j                         t	        dt
        j                         t	        d| j                   dt
        j                         Y d}~ddd      d{  7   yd}~ww xY w7 7 s# t&        $ r9}t	        d| t
        j                         Y d}~ddd      d{  7   yd}~wt(        $ r9}t	        d| t
        j                         Y d}~ddd      d{  7   yd}~wt*        $ r9}t	        d| t
        j                         Y d}~ddd      d{  7   yd}~ww xY w7 +# 1 d{  7  sw Y   <xY ww)$u   
    파이프라인 실행 함수.

    Args:
        args: argparse.Namespace (CLI 인자).

    Returns:
        종료 코드 (0: 성공, 1: 실패).
    z[ERROR] )fileN   u   [ERROR] HTML 파싱 실패: r   u   [ERROR] 제목을 결정할 수 없습니다. --title 옵션을 사용하거나 HTML 파일에 <title> 또는 <h1> 태그를 추가하세요.,r   privateu   [INFO] 발행 준비u     파일:     u     제목:     u     카테고리: u     태그:     u     공개여부: )	blog_nameu   [ERROR] 설정 오류: u0   [INFO] 대화형 로그인을 시작합니다...u9   [INFO] 로그인 완료. 세션이 저장되었습니다.u   [ERROR] 세션 파일 없음: uG   [HINT] --login 옵션으로 먼저 로그인 세션을 생성하세요:z2  python3 scripts/blog/publish_pipeline.py --file z --loginu(   [ERROR] 세션이 만료되었습니다.u2   [HINT] --login 옵션으로 재로그인하세요:r9   )r   r   category_idtags
visibilityu   [ERROR] 세션 만료: u   [ERROR] 로그인 필요: u   [ERROR] 발행 실패: u   
[SUCCESS] 글 발행 완료!z
  URL:    u     상태:   r   )r   r]   rE   r;   printsysstderrr?   r   rc   splitr+   categoryr	   blogloginlogin_interactive_check_session_validpublish_postr   r   r   )argsrA   parsedrC   r   trc   rd   	publishersession_validpublished_urls              r   run_pipelineru      s     TYYI + .vgEL	

  yy#'99??3#7Ea1779	EE	
	#)*#5#;#;C#@NaAGGI	NNJ	 "	N9+
&'	N5'
"#	T]]O
,-	N4&
!"	ZL
)*$tyy9	
  & &::DE--///MN	"+"@"@"BBM <3::NFSZZXFtyykQYZadakakl'& & &,	"+"8"8v MM% #9 # M/& &R 
*,	J}o
&'	L
%&m  SZZ0 ,SE2D" FN   'u-CJJ?& 0 C  	23%8szzJ[beblblmFtyykQYZadakakl& & &	&. # 	+C51

CA& & &B " 	.se43::FG& & &H  	+C51

CM& & &H	I& & & &s  R1J AR1>K=K=&R1LL,A"R1L %R1+L;,R1/+RL>R+M>M?MA"R%R10O1R17&OOO"R1-R.,R1	K:#KR1K:K50R15K::R1	L8L3.R13L88R1>RM	O
A"O/R3R1>O?R1O

RR1O	RP:R>R1	P
R1RQ:R>R1	Q
R1RR:R>R1	R
R1RRR1R.!R$"R.)R1c                 &   t               } | j                         }|j                  rt        j                  nt        j
                  }t        j                  |dd       t        j                  t        |            }t        j                  |       y)u   CLI 진입점.z1%(asctime)s [%(levelname)s] %(name)s: %(message)sz%Y-%m-%d %H:%M:%S)levelformatdatefmtN)r[   
parse_argsverboseloggingDEBUGINFObasicConfigasynciorunru   rf   exit)
arg_parserro   	log_level	exit_codes       r   mainr     se    !#J  "D!%7<<IB# L./IHHYr   __main__)rA   r   r/   zdict[str, str])r/   zargparse.ArgumentParser)ro   zargparse.Namespacer/   intr.   )r5   
__future__r   rW   r   r|   rf   html.parserr   pathlibr   scripts.blog.tistory_publisherr   r   r   r	   	getLoggerr2   loggerr   rE   r[   ru   r    r   r   <module>r      sx    #    
 "   
		8	$#4j #4L$X8@eP  zF r   