
    uj`                       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ZddlZddl	Z
ddlmZmZ ddlmZ ddlmZmZmZmZmZmZ dZdZh d	Zh d
Zd)dZd*dZd+dZd,dZd-d.dZd/dZd0dZ d/dZ!d1d2dZ"d3dZ#d4dZ$d4dZ%d4dZ&d4dZ'd5dZ(d4dZ)d6dZ*d7dZ+d7dZ,d8dZ-d9dZ.d:d Z/d;d!Z0d/d"Z1d<d#Z2	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d=d$Z3d>d%Z4d?d@d&Z5dAd'Z6g d(Z7y)Ba  Generate a portable local Instagram media report with Apache ECharts.

The canonical parent JSON remains the source of truth.  The generator writes:

* ``<folder>.html`` beside the canonical ``<folder>.json``;
* a portable JSON snapshot and legacy ``index.htm`` inside the media folder;
* a local ``echarts.min.js`` runtime inside the media folder.

No network request is required when the report is opened with ``file://``.
    )annotationsN)Counterdefaultdict)datetime)AnyDictIterableListSequenceTuple   z6.1.0-simple>   .m4v.mov.mp4.webm>   .jpg.png.avif.jpeg.webpc                 f    t        j                         j                         j                  d      S )Nseconds)timespec)r   now
astimezone	isoformat     #D:\_St\WS8771\ws8771_html_report.py	_now_textr        s%    <<>$$&00)0DDr   c                    t        | dd      5 }t        j                  |      }d d d        t        t              st        d      |S # 1 sw Y   &xY w)Nrutf-8)encodingreport_json_root_not_object)openjsonload
isinstancedict
ValueError)pathhandledatas      r   
_read_jsonr/   $   sG    	dC'	*fyy  
+dD!677K	 
+	*s   A

Ac                   t         j                  j                  t         j                  j                  |             }t        j                  |d       t        j                  dd|      \  }}	 t        j                  |ddd	      5 }|j                  |       d d d        t        j                  ||        y # 1 sw Y    xY w# t        $ r' 	 t        j                  |        # t        $ r Y  w xY ww xY w)
NTexist_okz.ig_report_.tmp)prefixsuffixdirwr#   
)r$   newline)osr,   dirnameabspathmakedirstempfilemkstempfdopenwritereplace	ExceptionunlinkOSError)r,   textfolderfd	temp_pathr-   s         r   _atomic_write_textrJ   ,   s    WW__RWW__T23FKK&$$M&fUMB		YYr3$?6LL @


9d# @?  	IIi  	  		sH   0C 	B:C :C?C 	C6C&%C6&	C2/C61C22C6c                H    t        | t        j                  |dd             y )NF   )ensure_asciiindent)rJ   r'   dumps)r,   r.   s     r   _atomic_write_jsonrP   <   s    tTZZ5KLr   c                    t        | xs d      j                         }t        j                  dd|      }|j                  d      }|xs |S )N z[<>:\\/?*|\x00-\x1f]_z ._)strstripresub)valuefallbackrF   s      r   _safe_file_namerZ   @   sE    u{!!#D66)35D::eD8r   c                    t        | xs d      j                         }	 t        j                  j	                  |      }|j                  j                         dvs|j                  sy|S # t
        $ r Y yw xY w)NrR   >   httphttps)	rT   rU   urllibparseurlparserC   schemelowernetloc)rX   rF   parseds      r   _safe_http_urlre   G   sl    u{!!#D&&t, }}$55V]]K	  s   A) )	A54A5c                   t        | xs d      j                         j                  dd      }|sy|j                  d      D cg c](  }|dvst        j
                  j                  |d      * }}dj                  |      S c c}w )NrR   \/>   rR   .z-_.~()[]safe)rT   rU   rB   splitr^   r_   quotejoin)r,   rF   partpartss       r   
_quote_relrq   R   sx    tzr?  "**45DCG::c?l?4VZbkVkV\\:6?El88E? ms   	B#Bc                    t        | xs d      j                         j                  d      j                         }|r|j                         dv ry|S )NrR   @>   noneunknown	instagram)rT   rU   lstriprb   rX   rF   s     r   _clean_ownerry   Z   sF    u{!!#**3/557D4::<#CCKr   c                    	 t        t        t        | xs d      j                  dd                  S # t        t
        f$ r t        |      cY S w xY w)N0,rR   )intfloatrT   rB   	TypeErrorr+   )rX   defaults     r   
_int_valuer   a   sK    5U\c*223;<==z" 7|s   03 AAc                    t        | j                  d            }|r|S |r%dt        j                  j	                  |d       dS dS )Nprofile_urlhttps://www.instagram.com/._rj   rh   rR   )re   getr^   r_   rm   )itemownerexplicits      r   _profile_urlr   h   sJ    dhh}56HSX'(:(:5t(:(L'MQO`^``r   c                b   dD ]"  }t        | j                  |            }|s |c S  t        | j                  d      xs d      j                         }|rZt        | j                  d      xs d      j	                         dv rdnd}d| d	t
        j                  j                  |d
       d	S y)N)post_url	media_urlpage_urlcurrent_url
target_urlhref	shortcoderR   
asset_kind>   mp4videoreelpr   rh   z-_rj   )re   r   rT   rU   rb   r^   r_   rm   )r   keyrX   r   kinds        r   	_post_urlr   o   s    Ytxx}-L Z DHH[)/R0668ITXXl39r:@@BFVVv\_+D66<<3E3EiVZ3E3[2\\]^^r   c                8   dD ]  }t        | j                  |      xs d      j                         }|s2t        j                  d|      }|r|j                  d      c S t        j                  d|      }|svdj                  |j                               c S  y)N)downloaded_atfirst_downloaded_atcollected_atmetadata_updated_atlast_seen_at	upload_atrR   z(\d{4}-\d{2}-\d{2})   z(\d{4})(\d{2})(\d{2})-   날짜없음)rT   r   rU   rV   matchgrouprn   groups)r   r   rawr   s       r   
_item_dater   {   s    {$((3-%2&,,./5;;q>!13788ELLN++ | r   c                    t        | j                  d      xs | j                  d      xs d      j                         S )zUReturn the Instagram publish time only; local save time belongs to the file metadata.r   published_atrR   )rT   r   rU   )r   s    r   _display_timer      s2    txx$F(@FBGMMOOr   c                &   t        | j                  d      xs d      j                         }|r|S t        | j                  d      t              r| j                  d      ni }t        |j                  d      xs d      j                         xs dS )N
sort_fieldrR   sortfield	   미지정)rT   r   rU   r)   r*   )r   rX   r   s      r   _sort_fieldr      st    &,"-335E)$((6*:DA488FrDtxx /K0668GKGr   c           	        | j                  d      }t        |t              sg S g }|j                         D ]  \  }}t        |t              st        |      }t	        |      |d<   t	        |j                  d      xs d      j                         }|dk(  xs t        |j                  d            xsf t        t        |j                  d      d            xr@ t	        |j                  d	      xs |j                  d
      xs d      j                          }t        j                  j                  t	        |j                  d	      xs |j                  d
      xs d      j                               }	|	s|sH|	|d	<   t        |	xr= t        j                  j                  t        j                  j                  ||	                  |d<   t        j                  j                  |	      d   j                         }
|rd|d<   n)|
t        v s|dv rd|d<   n|
t         v s|dv rd|d<   nd|d<   t#        |j                  d      xs$ |j                  d      xs |j                  d            }||d<   t%        ||      |d<   t'        |      |d<   t)        |      |d<   t+        |      |d<   t-        |      |d<   |j/                  |        |j1                  d d       |S )Nitems_keyr   rR   metadatametadata_onlymetadata_collectedr   	file_name	file_path_existsr   _kind>   r   r   r   >   jpgjpegimager   fileowner_usernameig_account_idaccount_name_ownerr   r   _dater   r   c           
     \   t        | j                  d      xs d      t        | j                  d      xs d      t        | j                  d      xs d      t        | j                  d      d      t        | j                  d      xs d      t        | j                  d      xs d      fS )	Nr   rR   r   r   rank_nor   r   r   rT   r   r   rX   s    r   <lambda>z"_normalize_items.<locals>.<lambda>   s    		(#)r*		'"(b)		-(.B/uyy+Q/		+&,"-		+&,"-
r   Tr   reverse)r   r)   r*   r   rT   rb   boolr   rU   r:   r,   basenameexistsrn   splitext
VIDEO_EXTS
IMAGE_EXTSry   r   r   r   r   r   appendr   )docmedia_folder	raw_itemsresultr   r   r   r   is_metadatar   extr   s               r   _normalize_itemsr      s    Ii&	#%FOO%S#t$Cy3xV488L)/R0668j( 
D/1J,K 
DHH%9:A>?  JDHHU`La  MAeiememnyez  MA  A  IB  IH  IH  IJ  EJ 	 GG$$S+)>)]$((;BW)][]%^%d%d%fg	%[ybRWW^^BGGLLW`<a-bcYggy)!,224&DMJ$*:":#DMJ$*B"B#DM"DMTXX&67p488O;TpX\X`X`aoXpqX+D%8^%dO["4(W -d 3_)$/]d= &> KK
   
 Mr   c           	        t        | j                  d      xs d      }t        | j                  d      xs d      j                         }|r| d| S t        | j                  d      xs> t        j                  j                  t        | j                  d      xs d            d         }t        j                  dd|t        j                  	      }| d|xs$ | j                  d
      xs | j                  d       S )Nr   rR   r   |filename_stemr   r   u   _(?:No\d+|No없음)(?:_\d+)?$)flagsr   )	rT   r   rU   r:   r,   r   rV   rW   I)r   r   r   stems       r   _post_identityr      s    "(b)EDHH[)/R0668I)%%txx(aBGG,<,<S+AVA\Z\=],^_`,abD662BBDDIDWAdGdhhv.G$((;2GHIIr   c                    t        t              }| D ]  }|t        |         j                  |       ! g }|j	                         D ]0  \  }}t        |d       }t        d |D        d      }t        d |D        d      }t        d |D        d      }t        d |D        d      }	t        d |D        d      }
t        d	 |D        d      }t        d
 |D        d      }t        d |D        d      xs
 |rd|dnd}g }g }|D ]  }t        |j                  d      t              r|j                  d      ni }|j                  d      xs g D ]&  }t        |      }|s||vs|j                  |       ( |j                  d      xs g D ]&  }t        |      }|s||vs|j                  |       ( t        |j                  d            }t        |j                  d            }|r||vr|j                  |       |s||vs|j                  |        t        d |D              }i d|d|d|d|d|	d|
d|dt        d |D        d      d t        d! |D        d      d"t        d# |D        d$      d%|d&|d't        d( |D        d      d)t        d* |D        d      d+t        d, |D        d      d-t        d. |D        d      d/t        d0 |D        d      t        d1 |D              t        d2 |D              t        d3 |D              t        d4 |D              t        d5 |D              t        d6 |D              ||d7||d8	}|j                  |       3 |j                  d9 d:;       |S )<Nc                t    t        | j                  d      d      t        | j                  d      xs d      fS )Nmedia_indexr   r   rR   )r   r   rT   r   s    r   r   z_group_posts.<locals>.<lambda>   s3    :eii>VXY3Z\_`e`i`iju`v`|z|\}2~r   )r   c              3  |   K   | ]4  }|j                  d       st        |j                  d       xs d       6 yw)r   rR   Nr   rT   .0assets     r   	<genexpr>z_group_posts.<locals>.<genexpr>   s0     `&EIIV^L_c%))H-34&   <#<rR   c              3  |   K   | ]4  }|j                  d       st        |j                  d       xs d       6 yw)r   rR   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>   s3     jSXS\S\]hSi#eii4:;r   c              3  |   K   | ]4  }|j                  d       st        |j                  d       xs d       6 yw)r   rR   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>   s3     iuRWR[R[\gRhUYY{39r:r   c              3  |   K   | ]4  }|j                  d       st        |j                  d       xs d       6 yw)r   rR   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>   s3     rfUX]XaXabpXqC		. 9 ?R@fr   c              3     K   | ]9  }t        |j                  d             st        |j                  d              ; yw)
source_urlNre   r   r   s     r   r   z_group_posts.<locals>.<genexpr>   sA       CPVuZhinirirs  jA  [B>%))L*ABPV
   AAc              3     K   | ]9  }t        |j                  d             st        |j                  d              ; yw)thumbnail_urlNr   r   s     r   r   z_group_posts.<locals>.<genexpr>   sE       LV\U`notoxox  zI  pJ  aKnUYY-GHV\r   c              3  R   K   | ]  }t        |j                  d       d       ! yw)r   r   Nr   r   r   s     r   r   z_group_posts.<locals>.<genexpr>   s!     Ouz%))I"6:   %'r   )r   c              3  |   K   | ]4  }|j                  d       st        |j                  d       xs d       6 yw)
rank_labelrR   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>   s3     m6%UZU^U^_kUl3uyy6<"=6r   No03d   No없음direct_media_urlsr   r   	video_urlr   c              3  n   K   | ]-  }|j                  d       dk7  s|j                  d      s*d / ywr   r   r   r   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>   s0     rfU		'8Jj8X]b]f]fgp]qqf   555identityr   r   r   r   r   datec              3  X   K   | ]"  }t        |j                  d       xs d       $ yw)r   r   NrT   r   r   s     r   r   z_group_posts.<locals>.<genexpr>   s&     WPVuUYYw/A>BPV   (*r   display_timec              3  X   K   | ]"  }t        |j                  d       xs d       $ yw)r   rR   Nr  r   s     r   r   z_group_posts.<locals>.<genexpr>   s&      [TZ5UYY%?%E2!FTZr  r   c              3  |   K   | ]4  }|j                  d       st        |j                  d       xs d       6 yw)r   rR   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>   s5     tQW[`[d[der[sEIIm$<$B CQWr   r   r   r   viewsc              3  R   K   | ]  }t        |j                  d       d       ! yw)r  r   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>   !     P*UYYw%7;r   likesc              3  R   K   | ]  }t        |j                  d       d       ! yw)r  r   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>  r  r   commentsc              3  R   K   | ]  }t        |j                  d       d       ! yw)r  r   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>  s!     VveZ		*(=qAvr   repostsc              3  x   K   | ]2  }t        |j                  d       xs |j                  d      d       4 yw)r  sharesr   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>  s3     kdj[`Juyy';'Ruyy?RTUVdjs   8:duplicate_seen_countc              3  R   K   | ]  }t        |j                  d       d       ! yw)r  r   Nr   r   s     r   r   z_group_posts.<locals>.<genexpr>  s&     (ngm^cEII>T4UWX)Ygmr   c              3  d   K   | ](  }t        t        |j                  d       d             * yw)r   r   Nr   r   r   r   s     r   r   z_group_posts.<locals>.<genexpr>  s+     %ohn_dd:eii@T6UWX+Y&Zhn   .0c              3  d   K   | ](  }t        t        |j                  d       d             * yw)video_requestedr   Nr  r   s     r   r   z_group_posts.<locals>.<genexpr>  +     "ibhY^4
599=N3OQR(S#Tbhr  c              3  d   K   | ](  }t        t        |j                  d       d             * yw)video_downloadedr   Nr  r   s     r   r   z_group_posts.<locals>.<genexpr>  +     #kdj[`DEII>P4QST)U$Vdjr  c              3  d   K   | ](  }t        t        |j                  d       d             * yw)image_requestedr   Nr  r   s     r   r   z_group_posts.<locals>.<genexpr>  r  r  c              3  d   K   | ](  }t        t        |j                  d       d             * yw)image_downloadedr   Nr  r   s     r   r   z_group_posts.<locals>.<genexpr>	  r  r  c              3  D   K   | ]  }|j                  d       dk(    yw)r   r   Nr  r   s     r   r   z_group_posts.<locals>.<genexpr>
  s      Vve7!3z!Avs    )r   r   )	r   r  r  r!  r#  r   r   local_file_countassetsc           	     "   t        | j                  d      xs d      t        | j                  d      xs d      t        | j                  d      xs d      t        | j                  d      d      t        | j                  d      xs d      fS )Nr   rR   r  r   r   r   r   r   )posts    r   r   z_group_posts.<locals>.<lambda>  sr    !'R( &B'&,"-txx	*A.%+,
r   Tr   )r   listr   r   r   sortednextmaxr)   r   r*   re   sumanyallr   )r   groupedr   postsr  r&  r   r   r   r   r   r   r   r   direct_videodirect_imager   directr   urlr   	image_urlr%  r(  s                           r   _group_postsr7     sJ   /:4/@Gt$%,,T2 "$E#MMO&$~`&`bdejjlno	iikmnrfrtvw  CPV  C  EG  H
  LV\  L  NP  QOOYZ[m6moqr  f  NUy{  }D  EH  |I  xJ  [e
"$"$E7A%))L_B`bf7gUYY23moFzz'*0b0$S)3l2 '', 1 zz'*0b0$S)3l2 '', 1 'uyy'=>I&uyy'ABIYl:##I.Yl:##I.   rfrr

U
 
 	

 ;
 *
 ]
 CWPVWaop
 C [TZ [egh
 $tQWt  wB  C
 w
 *
 SPPZ[\
 SPPZ[\
 VvV`ab
  skdjkuvw!
" #C(ngm(nxy$z#
$ #&%ohn%o"o""ibh"ii ##kdj#k k""ibh"ii ##kdj#k k  Vv VV+7,!O 05
8 	Ts ,t 
JJ
   	 Lr   c           
        | j                  d      xs g D ]H  }|j                  d      dk(  s|j                  d      s*t        |j                  d      xs d      c S  | j                  d      xs g D cg c]U  }|j                  d      dk(  st        j                  j	                  t        |j                  d      xs d            d   W }}t        | j                  d	      xs d      }|D ]T  }d
D ]M  }||z   }|st        j                  j                  t        j                  j                  ||            sI|c c S  V yc c}w )Nr&  r   r   r   r   rR   r   r   _media_folder)r   r   r   r   r   )r   rT   r:   r,   r   r   rn   )r(  r   video_stemsrG   r   r   	candidates          r   _local_imager<    s:   (#)r)99W(UYYy-Auyy-344 * X\W_W_`hWiWomoWo  RWoesxs|s|  ~E  tF  JQ  tQ277##C		+(>(D"$EFqIWoK  R/*0b1F>Cs
I"''..fi)HI   ? 
  Rs   8EA Ec           	     H   dt        | j                  d      d      ddt        | j                  d      d      ddt        | j                  d      d      dd	t        | j                  d
      d      dg}t        j                  t	        j
                  |dd      d      S )Nu	   조회수r  r   )namerX   u	   좋아요r  u   댓글r  u   공유/리포스트r  Fr|   :rM   
separatorsTrm   )r   r   htmlescaper'   rO   )r(  payloads     r   _metric_jsonrG  *  s    z$((72CQ'GHz$((72CQ'GHJtxx
/CQ$GH&DHHY<OQR1ST	G ;;tzz'*U]abbr   c                6   g }d}| j                  d      xs g D ]  }|j                  d      dk(  rt        |j                  d      xs d      }|s9|dz  }t        ||z         }|j                  d      dk(  rd	n|j                  d      d
k(  rdnd}|j                  d      sdnd}|j                  dt	        j
                  |d       d| d| t	        j
                  |       d        t        | j                  d      t              r| j                  d      ni }	t        |	j                  d      xs g d      D ]@  \  }
}t        |      }|s|j                  dt	        j
                  |d       d|
 d       B t        |	j                  d
      xs g d      D ]@  \  }
}t        |      }|s|j                  dt	        j
                  |d       d|
 d       B | j                  d      r|j                  d       dj                  |      S )Nr   r&  r   r   r   rR   r   r   MP4r   JPGFILEr   u	    (없음)	<a href="TrC  z" target="_blank">u    파일 z</a>r   startu=   " target="_blank" rel="noopener noreferrer">MP4 직접링크 u=   " target="_blank" rel="noopener noreferrer">JPG 직접링크 r   uF   <span class="metadata-note">파일 미저장 · metadata 수집</span> )r   rT   rq   r   rD  rE  r)   r*   	enumeratere   rn   )r(  media_prefixlinkslocal_indexr   r   r   r   missingr4  indexr5  rk   s                r   _asset_linksrV  4  sB   EK(#)r)99W+		+.4"5	q,23		'*g5uEIIgDVZaDa5gm%*YYy%9+ryT!> ??QRVQWW_`k_lmqmxmx  zA  nB  mC  CG  H  	I * /9BU9VX\.]TXX)*ceF

7 3 9rC
sc"LL9T[[T%B$C  DA  BG  AH  HL  M  N D  

7 3 9rC
sc"LL9T[[T%B$C  DA  BG  AH  HL  M  N D xx ]^88E?r   c                   t        | j                  d      xs d      }t        | j                  d      xs d      }t        | j                  d      xs d      }t        | j                  d            }t        | j                  d            }t        | j                  d            }t        |       }	|	rt	        ||	z         nt        | j                  d	            }
t        t        | j                  d
      xs g       i       }t	        |t        |j                  d      xs d      z         }|xs
 |xs |xs d}|
r`dt        j                  |d       dt        j                  |
d       dt        j                  |       dt        j                  |       d	}n\|j                  d      dk(  r-|j                  d      rdt        j                  |d       d}ndt        j                  |d       d}g }|r@|j                  dt        j                  |d       dt        j                  |       d       |r*|j                  dt        j                  |d       d       |r/||k7  r*|j                  dt        j                  |d       d       t        | |      }|r|j                  |       dj                  |||t        | j                  d       xs d      t        | j                  d!      xs d      g      j                         }t        | j                  d"      d#      }|rd$| d%n| j                  d&      rd'nd(}| j                  d)      rd*nd+g}|j                  | j                  d,      rd-n| j                  d.      rd/nd0       |j                  | j                  d1      rd2n| j                  d3      rd4nd5       d6j                  |      }d7| }dj                  g d8t        j                  |d       d9t        j                  t        | j                  d       xs d:      d       d;t        j                  t        | j                  d!      xs d<      d       d=t        j                  |d       d>| d?t        j                  |       d@t        j                  t        | j                  d       xs d:             d@t        j                  t        | j                  d!      xs d<             d@t        j                  |       dAt        j                  |       dBt        j                  |rdC|z   ndD       d6t        j                  |xs dE       dFdj                  |       dGt        | j                  dH      d#      dIdJt        | j                  dK      d#      dIdLt        | j                  dM      d#      dIdNt        | j                  dO      d#      dIdPt        j                  t        | j                  dQ      xs dR             dSt        | j                  dT      d#       dU| dVt        |        dW      S )XNr   rR   r   r   r   r   r   r   r   r&  r   #z<a class="thumb-link" href="TrC  zE" target="_blank" rel="noopener noreferrer"><img loading="lazy" src="z" alt="rO  z"></a>r   r   r   z0<video controls preload="metadata"><source src="uI   ">이 브라우저에서 동영상을 재생할 수 없습니다.</video>z<a class="preview-empty" href="uT   " target="_blank" rel="noopener noreferrer">섬네일 없음<br>게시물 열기</a>rL  z-" target="_blank" rel="noopener noreferrer">@u    계정</a>uC   " target="_blank" rel="noopener noreferrer">Instagram 게시물</a>uG   " target="_blank" rel="noopener noreferrer">원본 미디어 링크</a>r   r  r  r   u   중복 확인 u   회r   u   metadata 수집u   신규 저장r   u   metadata 완료u   metadata 미확인r  u
   MP4 저장r  u
   MP4 요청u   MP4 미선택r#  u
   JPG 저장r!  u
   JPG 요청u   JPG 미선택u    · zpost-chart-z*
<article class="post-card" data-account="z" data-sort="r   z" data-date="r   z" data-search="z">
  <div class="preview">z?</div>
  <div class="card-body">
    <div class="badges"><span>z</span><span>z+</span></div>
    <div class="save-status">z</div>
    <h3>rs   u   계정 미확인u   shortcode 없음z</h3>
    <div class="links">u,   </div>
    <dl>
      <dt>조회수</dt><dd>r  r|   u"   </dd>
      <dt>좋아요</dt><dd>r  u   </dd>
      <dt>댓글</dt><dd>r  u,   </dd>
      <dt>공유/리포스트</dt><dd>r  u&   </dd>
      <dt>게시 시각</dt><dd>r  r   u&   </dd>
      <dt>로컬 파일</dt><dd>r%  u    개</dd>
    </dl>
    <div id="z!" class="post-chart" data-chart="u:   "><span>차트 준비 중</span></div>
  </div>
</article>)rT   r   re   r<  rq   r+  iterrD  rE  r   rV  rn   rb   r   rG  )r(  rQ  chart_indexr   r   r   r   r   r   local_image	image_srcfirst_assetfirst_local	click_urlpreviewexternalasset_linkssearch_text	duplicateduplicate_textstatus_partsstatus_textchart_ids                          r   _post_card_htmlri  O  so   !'R(EDHH[)/R0ITXXl+9z:Jdhhz23H 67J -!89Kt$K:E
<+56>Z^ZbZbcrZsKtItDHHX.4"5r:K\C0L0RPR,SSTK<J<+<I0Yd1S0T  UZ  [_  [f  [f  gp  x|  [}  Z~  ~E  FJ  FQ  FQ  R\  F]  E^  ^_  `d  `k  `k  lu  `v  _w  w}  ~		!W	,1KDT[[Q\dhEiDj  kt  u3DKK	QU4V3W  Xl  mH)DKK4$H#IIvw{  xC  xC  DI  xJ  wK  KV  W  	X)DKK$E#F  GJ  K  	LjH,)DKK
$$G#H  IP  Q  	Rt\2K$((E9j#dhh|>T>ZXZ:[]`aeaiaijpaqawuw]xyz  A  A  CK488$:;Q?I8A~i[4]a]e]efu]vHY  }LN)-2F)G%MabL1C(D[_[c[cdu[v<  }L  N1C(D[_[c[cdu[v<  }L  N++l+K[M*H  * *.++e4*H)IIVW[WbWbcfgkgogop|g}  hM  BM  dN  VZ  X[  W\ \i jn  ju  ju  vy  z~  zB  zB  CI  zJ  z\  N\  v]  ei  jj  ik kz {  {F  {F  GR  Z^  {_  z``y!  ${{:67	 8E	 FJ[[QTUYU]U]^jUkUzozQ{E|D}	 ~K	 LP  LW  LW  X[  \`  \d  \d  ek  \l  \~  p~  X  L@  KA	 AN	 OS  OZ  OZ  [i  Oj  Nk	k	
 #kk+67
8	 
ES5[/A	BC DH IMT]TsasHtGuv *+, ((91=a@A ((91=a@A %TXXj%91=a@A' (2$((92Eq'I!&LM! "&S.1I1PS-Q!R ST! ",DHH5G,H!!L MN  Z!  9!  :Fd9K8L! M! r   c                   t        d |D              }t        d |D              }t        d |D              }t        t               t        |      t	        d |D              t        |      t        t        |j                                     t        t        |j                                     t        t        |j                                     dS )Nc              3  X   K   | ]"  }t        |j                  d       xs d       $ ywr   u   계정없음Nr  r   r(  s     r   r   z_summary.<locals>.<genexpr>  s$     TedTXXg.@.Aer  c              3  X   K   | ]"  }t        |j                  d       xs d       $ ywr   r   Nr  rm  s     r   r   z_summary.<locals>.<genexpr>  s$     SUTc$((<0?K@Ur  c              3  X   K   | ]"  }t        |j                  d       xs d       $ ywr  r   Nr  rm  s     r   r   z_summary.<locals>.<genexpr>  s$     P%$c$((6*<n=%r  c              3  n   K   | ]-  }|j                  d       dk7  s|j                  d      s*d / ywr   r  )r   r   s     r   r   z_summary.<locals>.<genexpr>  s0     j$((72Cz2QVZV^V^_hVi1r  )versiongenerated_at
post_countasset_countaccount_count
by_accountby_sortby_date)r   REPORT_VERSIONr    lenr-  r*   r*  r   )r   r   r1  rx  ry  rz  s         r   _summaryr}    s    TeTTJSUSSGP%PPG!!%jjjjZ6*"2"2"456w}}/0w}}/0	 	r   c           	         t        | D ch c]!  }t        |xs d      st        |xs d      # c}      }dt        j                  |       ddj	                  d |D              z   S c c}w )NrR   z<option value="">	</option>c              3  ~   K   | ]5  }d t        j                  |d       dt        j                  |       d 7 yw)z<option value="TrC  z">r  N)rD  rE  )r   rX   s     r   r   z"_select_options.<locals>.<genexpr>  s<      Kgm^c/$++e489DKK<N;OyYgms   ;=)r*  rT   rD  rE  rn   )values	all_labelrX   uniques       r   _select_optionsr    sp    6N6%S"=MS"%6NOFt{{956i@277 KgmK D   Os
   A)A)c                    t        j                  | dd      }|j                  dd      j                  dd      j                  dd	      S )
NFr?  rA  z</z<\/u    z\u2028u    z\u2029)r'   rO   rB   rx   s     r   _safe_script_jsonr    s@    ::e%JGD<<f%--h	BJJ8U^__r   c           
     ,   t        |D ch c]7  }t        |j                  d            st        |j                  d            9 c}      }t        |      dk(  r|d   S t        | j                  d            }|r|j	                         dvr|S yc c}w )Nr   r   r   folder_name>      전체r/  mergedrv   rR   )r*  ry   r   r|  rb   )r   r1  r(  ownersfolder_owners        r   _report_account_namer    s    j,W[W_W_`gWhJi\$((7"34jkF
6{aay 67L**,4\\ ks
   BBc                   |D ]  }||d<   	 t        | j                  d      xs d      }| d}	t        | j                  d      t              r| j                  d      nt	        | ||      }
dj                  fdt        |d	      D              }t        d
 |D        d      }t        d |D        d      }t        d |D        d      }t        |
j                  d      xs
 t                     }t        | |      }d}|r'|dt        j                  j                  |d      z   z  }|||
|D cg c]%  }i d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d |j                  d       d!|j                  d!      d"|j                  d"      d#|j                  d#      d$|j                  d$      ( c}d%}dj                  g d&t        j                  |	       d't        j                  |	       d(t        j                  |d)*       d+t        j                  t        |      d)*       d,t!        |
j                  d-      t#        |             d.t!        |
j                  d/      t#        |             d0t!        |
j                  d1      d2       d3t#        |
j                  d4      xs i        d5| d6| d7| d8|xs d9 d:t        j                  |       d;t$         d<t        j                  t&               d=t)        |       d>t        j                  t        |      d)*       d?      S c c}w )@Nr9  r  	Instagramu    통합 Instagram 보관함report_summaryrR   c              3  >   K   | ]  \  }}t        ||        y wN)ri  )r   rU  r(  rQ  s      r   r   z_build_html.<locals>.<genexpr>  s!     lRk;5$OD,>Rks   r   rM  c              3  X   K   | ]"  }t        |j                  d       xs d       $ ywrl  r  rm  s     r   r   z_build_html.<locals>.<genexpr>  s'     &b\aTXs488G+<+N'O\ar  u   전체 계정c              3  X   K   | ]"  }t        |j                  d       xs d       $ ywro  r  rm  s     r   r   z_build_html.<locals>.<genexpr>  s'     #a[`SWC(>(M+$N[`r  u   전체 sort byc              3  X   K   | ]"  }t        |j                  d       xs d       $ ywrq  r  rm  s     r   r   z_build_html.<locals>.<genexpr>  s'     #^X]PTC(8(JN$KX]r  u   전체 날짜rt  zhttp://127.0.0.1:8780/z	?account=r   rj   r   r   r  r   r   r  r  r  r  r   r   r  r  r!  r#  r   )r  rt  summaryr1  z<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>a	  </title>
<style>
:root{--bg:#0f1115;--panel:#171a21;--line:#2b303b;--text:#f2f4f8;--muted:#aab2c0;--accent:#6aa7ff}
*{box-sizing:border-box} body{margin:0;background:var(--bg);color:var(--text);font-family:Arial,"Noto Sans KR",sans-serif} a{color:#8fc0ff;text-decoration:none} a:hover{text-decoration:underline}
header{position:sticky;top:0;z-index:5;background:rgba(15,17,21,.97);border-bottom:1px solid var(--line);padding:16px 22px} h1{margin:0 0 8px;font-size:23px} .sub{color:var(--muted);font-size:13px}
.summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;padding:18px 22px} .metric{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px} .metric strong{display:block;font-size:24px}
.controls{display:grid;grid-template-columns:repeat(4,minmax(150px,1fr));gap:8px;padding:0 22px 16px} select,input{background:#202530;color:var(--text);border:1px solid #394150;border-radius:8px;padding:10px 12px;min-width:0}
.grid-cards{display:grid;grid-template-columns:repeat(auto-fill,320px);justify-content:start;gap:16px;padding:0 22px 30px} .post-card{width:320px;background:var(--panel);border:1px solid var(--line);border-radius:14px;overflow:hidden} .preview{width:320px;aspect-ratio:1/1;background:#08090c;display:flex;align-items:center;justify-content:center;overflow:hidden} .preview img,.preview video{width:100%;height:100%} .preview img{object-fit:cover} .preview video{object-fit:contain} .preview-empty{text-align:center;color:var(--muted)} .card-body{padding:14px} .badges{display:flex;gap:7px;flex-wrap:wrap} .badges span{background:#252b36;border-radius:999px;padding:4px 8px;font-size:12px} h3{font-size:15px;word-break:break-all} .links{display:flex;gap:9px;flex-wrap:wrap;font-size:13px} .metadata-note,.save-status{color:var(--muted);font-size:12px} .save-status{margin-top:8px} dl{display:grid;grid-template-columns:105px 1fr;gap:6px;font-size:13px} dt{color:var(--muted)} dd{margin:0;word-break:break-all} .post-chart{height:210px;margin-top:12px;border-top:1px solid var(--line);padding-top:8px} .post-chart>span{display:block;padding:18px;color:var(--muted);text-align:center}
.empty{padding:24px;color:var(--muted)} footer{padding:18px 22px;color:var(--muted);border-top:1px solid var(--line)} [hidden]{display:none!important}
@media(max-width:760px){.controls{grid-template-columns:1fr 1fr}.grid-cards{grid-template-columns:minmax(0,320px);padding-left:12px;padding-right:12px}.post-card,.preview{width:min(100%,320px)}}
</style>
</head>
<body>
<header><h1>uH   </h1><div class="sub">계정 · sort by · 날짜별 필터 · <a href="TrC  uI   " target="_blank" rel="noopener noreferrer">전체보기</a> · <a href="u   " target="_blank">JSON 열기</a> · 완전 로컬 ECharts</div></header>
<section class="summary">
<div class="metric"><span>게시물</span><strong>ru  uF   </strong></div>
<div class="metric"><span>로컬 파일</span><strong>rv  u?   </strong></div>
<div class="metric"><span>계정</span><strong>rw  r   uF   </strong></div>
<div class="metric"><span>수집 날짜</span><strong>rz  um   </strong></div>
</section>
<section class="controls">
<select id="filter-account" aria-label="계정 필터">u?   </select>
<select id="filter-sort" aria-label="sort by 필터">u>   </select>
<select id="filter-date" aria-label="날짜 필터">u   </select>
<input id="search" type="search" placeholder="계정, shortcode, No순위 검색">
</section>
<main id="post-grid" class="grid-cards">u9   <p class="empty">저장된 게시물이 없습니다.</p>u   </main>
<footer>생성 시각: u    · 오프라인 보고서 vu    · ECharts z;</footer>
<script id="report-data" type="application/json">z</script>
<script src="u	  "></script>
<script>
(()=>{
  const cards=[...document.querySelectorAll('.post-card')];
  const account=document.getElementById('filter-account');
  const sort=document.getElementById('filter-sort');
  const date=document.getElementById('filter-date');
  const search=document.getElementById('search');
  const rendered=new WeakSet();
  function compact(v){v=Number(v||0);if(v>=1e9)return (v/1e9).toFixed(1)+'B';if(v>=1e6)return (v/1e6).toFixed(1)+'M';if(v>=1e3)return (v/1e3).toFixed(1)+'K';return String(v);}
  function draw(el){
    if(rendered.has(el))return; rendered.add(el);
    let metrics=[]; try{metrics=JSON.parse(el.dataset.chart||'[]')}catch(_){}
    if(!window.echarts){el.innerHTML='<span>ECharts 로컬 파일을 불러오지 못했습니다. 수치는 위 표에서 확인할 수 있습니다.</span>';return;}
    el.innerHTML='';
    const chart=window.echarts.init(el,null,{renderer:'canvas'});
    chart.setOption({
      animation:false, grid:{left:84,right:18,top:8,bottom:26}, tooltip:{trigger:'axis',axisPointer:{type:'shadow'},valueFormatter:compact},
      xAxis:{type:'value',axisLabel:{formatter:compact},splitLine:{lineStyle:{color:'#303642'}}},
      yAxis:{type:'category',data:metrics.map(x=>x.name),axisLabel:{color:'#c9d1dc'}},
      series:[{type:'bar',data:metrics.map(x=>Number(x.value||0)),barMaxWidth:22,label:{show:true,position:'right',formatter:p=>compact(p.value)}}]
    });
    el.__chart=chart;
  }
  const observer=('IntersectionObserver' in window)?new IntersectionObserver(entries=>entries.forEach(entry=>{if(entry.isIntersecting){draw(entry.target);observer.unobserve(entry.target);}}),{rootMargin:'240px'}):null;
  document.querySelectorAll('.post-chart').forEach(el=>observer?observer.observe(el):draw(el));
  function apply(){
    const q=(search.value||'').trim().toLowerCase();
    cards.forEach(card=>{
      const visible=(!account.value||card.dataset.account===account.value)&&(!sort.value||card.dataset.sort===sort.value)&&(!date.value||card.dataset.date===date.value)&&(!q||(card.dataset.search||'').includes(q));
      card.hidden=!visible;
      if(visible&&!observer){const el=card.querySelector('.post-chart');if(el)draw(el);}
    });
  }
  [account,sort,date].forEach(el=>el.addEventListener('change',apply)); search.addEventListener('input',apply);
  window.addEventListener('resize',()=>document.querySelectorAll('.post-chart').forEach(el=>{if(el.__chart)el.__chart.resize();}));
  apply();
})();
</script>
</body></html>)rT   r   r)   r*   r}  rn   rP  r  r    r  r^   r_   rm   rD  rE  rq   r   r|  r{  ECHARTS_VERSIONr  )r   r   r1  r   rQ  	json_hrefecharts_srcr(  r  titler  cardsaccount_optionssort_optionsdate_optionsrt  report_accountfull_view_urlreport_datas       `              r   _build_htmlr    s     ,_ cggm,;<Km67E+5cgg>N6OQU+Vcgg&'\dehjoqv\wGGGlR[\aijRkllE%&b\a&bdstO"#a[`#acstL"#^X]#^`opLw{{>2AikBL)#u5N,Mv||'9'9.t'9'TTT"$( '
& %'*TXXk2 ( dhh|4	
 488I. '* '* DHHZ0 488I. DHHZ0 %dhh/C&D "488,=#> #DHH-?$@ "488,=#> #DHH-?$@  $TXX.A%B!$ '
	K4I I  I
 	EI
I$ [[ %I$ !i%I$ jnitit  vC  KO  jP  iQ%I$ QZ%I$ [_  [f  [f  gq  r{  g|  DH  [I  ZJ%I$J3%I( 4>gkk,>WY\]bYc3d2e)I(f7)I* 8B'++mB\^abg^h7i6j+I*k0+I, 1;7;;;WYZ0[/\-I,]7-I. 8;7;;y;Q;WUW7X6Y/I.Z8/I4 9H7H5I4I65I6 7C^7I6D57I8 6BN9I8C)9I> */)m2m(n?I>o?I@ L12AI@ 3OAI@ P^N^AI@ _kAI@ lpkvkv  xG  lH  kIAI@I2AIB 3DK2P1QCIBRCID kk*[1>?EID'@EI I-
s   0D+O3
c                   t         j                  j                  t         j                  j                  t         j                  j	                  t
                    dd      }t         j                  j                  |      r"t         j                  j                  |      dk  rt        d      t         j                  j                  | d      }t         j                  j                  |       xs? t         j                  j                  |      t         j                  j                  |      k7  }|r1|dz   }t        j                  ||       t        j                  ||       |S )Nr&  echarts.min.jsi'  echarts_runtime_not_foundr3   )r:   r,   rn   r;   r<   __file__r   getsizeFileNotFoundErrorshutilcopyfilerB   )r   sourcetargetcopy_requiredtemp_targets        r   _copy_echarts_runtimer  +  s    WW\\"''//"''//(*CDhP`aF77>>&!RWW__V%<u%D ;<<WW\\,(89Fv..d"''//&2IRWW__]cMd2dMvo,


;'Mr   c           
         	 t         j                  j                  t        | xs d            } | rt         j                  j	                  |       sdd| dS t        |       }t        |j                  d      xs? t         j                  j                  t         j                  j                  |             d         }t         j                  j                  t        |xs= t         j                  j                  t         j                  j                  |       |                  }t        j                  |d       t        ||      }t        |      }t        |||      }t         j                  j                  |       d   d	z   }| d
}t         j                  j                  ||      }	t         j                  j                  |d      }
t!        |      }t"        t         j                  j                  |      | d| d| | dt$        d|d   d}t'        |      }||d<   ||d<   t)        | |       t'        |      }t"        |d   ddt         j                  j                  |       dddd|d<   t         j                  j+                  |	      t         j                  j+                  |       k7  rt)        |	|       n| }	t-        ||||| dt         j                  j                  |       | d      }t-        ||||d|d      }t/        ||       t/        |
|       d||
|	|t1        |      t1        |      |t"        d	S # t2        $ r5}ddt        |      t        | xs d      t        |xs d      dcY d}~S d}~ww xY w)zBGenerate same-name parent HTML plus portable legacy report assets.rR   Freport_json_not_found)okerror	json_pathr  r   Tr1   z.html.jsonz	index.htmz
/index.htmrh   z/echarts.min.js!canonical_json_is_source_of_truthrt  )rs  	html_filelegacy_html_fileportable_json_fileecharts_fileecharts_versionsource_policy
updated_atreportr  z../ri   r  )rs  rt  r  canonical_html_fileportable_media_folderr  r  report_snapshot)rQ  r  r  )	r  report_pathlegacy_report_pathsnapshot_json_pathecharts_path
item_countru  r   report_versionreport_generation_failed)r  r  detailr  r   N)r:   r,   r<   rT   r   r/   rZ   r   r   r   rn   r;   r=   r   r7  r}  r  r{  r  r*   rP   normcaser  rJ   r|  rC   )r  r   r   r  r   r1  r  canonical_html_pathsnapshot_namesnapshot_pathr  r  report_infocanonical_docsnapshot_doccanonical_htmllegacy_htmlexcs                     r   generate_account_reportr  8  s5   U
GGOOC	R$89	y 9*APYZZ#%cggm&<&p@P@PQSQXQXQaQabkQl@mno@pqwws<+p277<<XaHbdo;p'qr
L40 l3U#3u- gg..y9!<wF&-u-\=AWW\\,D,\: &))*=>#.-z :%0M=/"B*m?;.@!.1	
 S	"-h*1&'9m4M*%#N3$%()9)9:M)N(O#P%(,@+
&' 77M*bgg.>.>y.II}l;%M$'=*gg&&y1&-7
 "#(
 	.?-{;."4"/(e*e*(,

 
	
  
/#hY_"- 23
 	

s%   AL? K)L? ?	M=*M82M=8M=c                  
 t         j                  j                  t        | xs d            

rt         j                  j	                  
      s
dd
dddg dS t        d 
fdt        j                  
      D        D              }g }d}|D ]  }t         j                  j                  t         j                  j                  |            d   }t         j                  j                  
|      }t        ||      }t        |xs i       }||d<   |j                  |       |j                  d	      s|d
z  } t        |      }||z
  }	|	dk(  
|||	||	dk(  rddS ddS )z?Rebuild every canonical account report under _StDown/instagram.rR   Finstagram_root_not_foundr   )r  r  rootrequested_countsuccess_countfailed_countresultsc              3     K   | ]D  }t         j                  j                  |      r#|j                         j	                  d       r| F yw)r  N)r:   r,   isfilerb   endswith)r   r,   s     r   r   z*rebuild_account_reports.<locals>.<genexpr>  s;      P77>>$DJJL$9$9'$B 	Ps   A
Ac              3  ^   K   | ]$  }t         j                  j                  |       & y wr  )r:   r,   rn   )r   r>  r  s     r   r   z*rebuild_account_reports.<locals>.<genexpr>  s#     P?Ot"'',,tT2?Os   *-r  r  r   report_rebuild_partial_failure)r  r  r  r  r  r  r  )r:   r,   r<   rT   isdirr*  listdirr   r   rn   r  r*   r   r   r|  )instagram_root
json_pathsr  r  r  r  r   r   r  r  r  s             @r   rebuild_account_reportsr    s   77??3~345Drww}}T*&@$cdwx  KL  Y[  \  	\ Przz$?OP J %'GM	gg&&rww'7'7	'BCAFww||D+6(LAfl#'{v::dQM   *oO"]2L!#Tohu  HT  ah  yE  IJ  yJ  su  q  q  Pp  q  qr   )r{  r  r  r  )returnrT   )r,   rT   r  Dict[str, Any])r,   rT   rF   rT   r  None)r,   rT   r.   r  r  r  )rv   )rX   r   rY   rT   r  rT   )rX   r   r  rT   )r,   r   r  rT   )r   )rX   r   r   r}   r  r}   )r   r  r   rT   r  rT   )r   r  r  rT   )r   r  r   rT   r  List[Dict[str, Any]])r   Sequence[Dict[str, Any]]r  r  )r(  r  r  rT   )r(  r  rQ  rT   r  rT   )r(  r  rQ  rT   rZ  r}   r  rT   )r   r  r   r  r1  r  r  r  )r  zIterable[str]r  rT   r  rT   )r   r  r1  r  r  rT   )r   r  r   r  r1  r  r   rT   rQ  rT   r  rT   r  rT   r  rT   )r   rT   r  rT   )rR   )r  rT   r   rT   r  r  )r  rT   r  r  )8__doc__
__future__r   rD  r'   r:   rV   r  r>   urllib.parser^   collectionsr   r   r   typingr   r   r	   r
   r   r   r{  r  r   r   r    r/   rJ   rP   rZ   re   rq   ry   r   r   r   r   r   r   r   r   r7  r<  rG  rV  ri  r}  r  r  r  r  r  r  r  __all__r   r   r   <module>r     sB  	 #   	 	    ,  = = .
8
E Ma	P
H/bJIVc66r `z	zz  z 	z
 z z z 	zz
W
tq2 fr   