+
    H-i@                        ^ RI t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	 ^ RI
HtHtHtHtHt ^ RIt]P"                  ! R]P$                  4      tRRRR	R
RRR/tR R ltR R ltR R ltR R ltR)R R lltR*R R llt] ! R R4      4       tR+R R lltR+R R lltR  R! ltR" R# ltR,R$ R% llt R& R' lt!]"R(8X  d   ]#! ]!! 4       4      hR# )-    N)	dataclass)Path)AnyDictListOptionalTuplezhttps?://([^/]+\.)?hotline\.ua/desktopzoMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36mobile_safarizMozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1googlebot_desktopzHMozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)googlebot_smartphonezMozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)c                    V ^8  d   QhRR/# )   returnN )formats   "debug_hotline.py__annotate__r      s      d     c                      \         P                  P                  RRR7        \         P                  P                  RRR7       R#   \         d     L1i ; i  \         d     R# i ; i)zN
Windows terminals can have a non-UTF8 codepage. Ensure printing won't crash.
utf-8replaceencodingerrorsN)sysstdoutreconfigure	Exceptionstderrr   r   r   _stdout_safer!      sa    

	B

	B    s"   !A !A AAA'&A'c                0    V ^8  d   QhR\         R\         /# )r   sr   str)r   s   "r   r   r   %   s     * *# *# *r   c                 h    \         P                  ! R RV 4      p \        V 4      ^8  d
   V R,          # T # )z[^a-zA-Z0-9._-]+_:N   N)resublen)r#   s   &r   _sanitize_filenamer,   %   s.    
"C+A!fsl1T7))r   c                0    V ^8  d   QhR\         R\         /# r   urlr   r$   )r   s   "r   r   r   *   s      c c r   c                     V P                  4       p V '       g   V # R V 9   d   V # V P                  R^4      ^ ,          p RV 9   d
   V R,           # V R,           # )z	tab=about#?z
&tab=aboutz
?tab=about)stripsplit)r/   s   &r   to_about_urlr5   *   sT    
))+C
c

))C
A
C
cz\!!r   c                0    V ^8  d   QhR\         R\        /# r.   )r%   bool)r   s   "r   r   r   9   s     
 
 
 
r   c                 *  a  Rp\         ;QJ d    V 3R lV 4       F  '       g   K   RM	  RM! V 3R lV 4       4      '       d   R# \        P                  ! RS \        P                  4      '       d   R# \        P                  ! RS 4      '       d   R# R# )/sr/c              3   ,   <"   T F	  qS9   x  K  	  R # 5iNr   ).0xr/   s   & r   	<genexpr>*is_probably_product_url.<locals>.<genexpr><   s     
!S8Ss   TFz/[a-z0-9-]+/[a-z0-9-]+/z/\d+/)r9   z	/compare/z/help/z	/catalog/z
/category/z/user/)anyr)   search
IGNORECASE)r/   bads   f r   is_probably_product_urlrD   9   s^    
NC
s
!S
!sss
!S
!!!	yy+S"--@@	yy3r   c                v    V ^8  d   QhR\         R\         R\         R\        R\        \         \        3,          /# )r   queryapi_keyhlnumr   )r%   intr   r   )r   s   "r   r   r   F   s<       s   VZ[^`c[cVd r   c           
          R RRV  R2RVR\        V4      RV/p\        P                  ! RV^R7      pVP                  4        VP	                  4       # )	enginegoogleqz site:hotline.uarH   rI   rG   zhttps://serpapi.com/search.json)paramstimeout)r%   requestsgetraise_for_statusjson)rF   rG   rH   rI   rO   rs   &&&&  r   serpapi_google_searchrV   F   s\     	(w&'bs3x7F 	6vrRA668Or   c                t    V ^8  d   QhR\         \        \        3,          R\        R\        \        ,          /# )r   	serp_json	max_linksr   )r   r%   r   rJ   r   )r   s   "r   r   r   T   s.      T#s(^  TRUY r   c                   a . oR  V3R llpV P                  R. 4      ;'       g    .  F  pV! VP                  R4      4       K  	  V P                  R. 4      ;'       g    .  F  pV! VP                  R4      4       K  	  V P                  R. 4      ;'       g    .  F  pV! VP                  R4      4       K  	  SRV # )c                :    V ^8  d   QhR\         \        ,          /# )r   u)r   r%   )r   s   "r   r   +extract_hotline_links.<locals>.__annotate__W   s      x} r   c                    < V '       g   R # V P                  4       p \        P                  V 4      '       g   R # V S9  d   SP                  V 4       R # R # r;   )r3   HOTLINE_DOMAIN_RErA   append)r\   linkss   &r   add"extract_hotline_links.<locals>.addW   s?    GGI ''**E>LLO r   organic_resultslinkinline_imagesrelated_resultsN)rR   )rX   rY   rb   itemra   s   &&  @r   extract_hotline_linksri   T   s    E  /4:::DHHV ;or288b8DHHV 9/4:::DHHV ; )r   c                   ,   a  ] tR t^jt o V 3R ltRtV tR# )
FetchDebugc                ~   < V ^8  d   Qh/ S[ ;R&   S[;R&   S[ ;R&   S[;R&   S[;R&   S[S[ ,          ;R&   S[ ;R&   # )r   r/   status_codecontent_type
elapsed_mslength
saved_file	first_200)r%   rJ   r   )r   __classdict__s   "r   r   FetchDebug.__annotate__j   s^     	H    	 
 O  K    N r   r   N)__name__
__module____qualname____firstlineno____annotate_func____static_attributes____classdictcell__)rs   s   @r   rk   rk   j   s      r   rk   c          
          V ^8  d   QhR\         R\         R\        \         ,          R\        \         \        3,          /# )r   r/   uaacceptr   )r%   r   r	   rk   )r   s   "r   r   r   u   s5     ! !3 !C !# !%ZBX !r   c                 j   R VRR/pV'       d   W#R&   \         P                   ! 4       p\        P                  ! W^RR7      p\        \         P                   ! 4       V,
          R,          4      pVP                  P                  RR4      p\        VP                  \        4      '       d   VP                  MRpR	p	\        R
4      p
V
P                  4       '       dl   V
P                  4       '       dV   V
R\        V 4       R\        \         P                   ! 4       4       R2,          p VP                  VRRR7       \        V4      p	\        V VP                  VV\!        V4      V	VR,          P#                  RR4      P#                  RR4      P%                  4       R7      pW3#   \         d    R	p	 Lli ; iz
User-AgentzAccept-Languagezuk,ru;q=0.9,en;q=0.8AcceptT)headersrP   allow_redirectsi  zcontent-type Nuploads	py_debug_r'   z.htmlr   ignorer   :N   N
 )r/   rm   rn   ro   rp   rq   rr   )timerQ   rR   rJ   r   
isinstancetextr%   r   existsis_dirr,   
write_textr   rk   rm   r+   r   r3   )r/   r}   r~   r   t0rU   ro   rn   r   rq   out_dirfndbgs   &&&          r   	fetch_urlr   u   sj   b1G "	BS2tLAdiikB&$./J99==4L,,166"DJ9oG~~GNN,,#5c#:";1S=M<NeTT	MM$MBRJ MM!4yt*$$T3/77cBHHJC 9  	J	s   &F" "F21F2c                    V ^8  d   QhR\         P                  R\        R\        R\        \        ,          R\        \        \
        3,          /# )r   sessr/   r}   r~   r   )rQ   Sessionr%   r   r	   rk   )r   s   "r   r   r      sG     ! !H,, !3 !C !RU !bghkmwhwbx !r   c                 `   R VRR/pV'       d   W4R&   \         P                   ! 4       pV P                  W^RR7      p\        \         P                   ! 4       V,
          R,          4      pVP                  P                  RR4      p\	        VP
                  \        4      '       d   VP
                  MRp	R	p
\        R
4      pVP                  4       '       dl   VP                  4       '       dV   VR\        V4       R\        \         P                   ! 4       4       R2,          p VP                  V	RRR7       \        V4      p
\        VVP                  VV\        V	4      V
V	R,          P!                  RR4      P!                  RR4      P#                  4       R7      pW3#   \         d    R	p
 Lli ; ir   )r   rR   rJ   r   r   r   r%   r   r   r   r,   r   r   rk   rm   r+   r   r3   )r   r/   r}   r~   r   r   rU   ro   rn   r   rq   r   r   r   s   &&&&          r   fetch_url_sessionr      sl   b1G "	Br4HAdiikB&$./J99==4L,,166"DJ9oG~~GNN,,#5c#:";1S=M<NeTT	MM$MBRJ MM!4yt*$$T3/77cBHHJC 9  	J	s   !F F-,F-c                R    V ^8  d   QhR\         R\        \         \        3,          /# r   pager   )r%   r   rJ   )r   s   "r   r   r      s"       S#X r   c                    T ;'       g    R pRRV9   g   RV9   d   ^M^ R\        \        P                  ! RV\        P                  R7      4      R\        \        P                  ! RV\        P                  R7      4      /# )	r   has_similarProductszid="similarProducts"similarProductsspecifications_table_mentionszspecifications-tableflagsspecifications_table_tagsz1<table[^>]+class=[\"'][^\"']*specifications-table)r+   r)   findallrB   )r   r#   s   & r   quick_presence_countsr      s~    

A%=%BFW[\F\qcd'RZZ8OQRZ\ZgZg-h)i#S4hjksu  tA  tA  *B  &C r   c                h    V ^8  d   QhR\         R\        \        \         \         3,          ,          /# r   )r%   r   r	   )r   s   "r   r   r      s+     R R RU38_(= Rr   c                  a T ;'       g    RpRV9  d   . # R R lp. o\         P                  ! RV4      pV'       d   VP                  4       MR4pVR48w  d1   V\        ^ VR,
          4      \	        \        V4      VR,           4       MTpR V3R llpV'       dS   VP                  ^4      p\         P                  ! R	RV4      P                  R
R4      P                  RR4      pV! RV4       V! RV! RV4      4       V! RV! RV4      4       V! RV! RV4      4       V! RV! RV4      4       V! RV! RV4      4       V! RV! RV4      4       V! RV! RV4      4       V! RV! RV4      4       V! RV! RV4      4       V! R V! R!V4      4       V! R"V! R#V4      4       V! R$V! R%V4      4       V! R&V! R'V4      4       V! R(V! R)V4      4       V! R*V4      pV! R+V4      p	T! R,T;'       g    T	4       T! R-T;'       g    T	4       R.p
VP                  V
4      pVR48w  d   WVR/,            p\         P                  ! R0V4      pV'       dc   VP                  ^4      VP                  ^4      VP                  ^4      prR1V9   d
   V! VR14       R2V9   d
   V! VR24       R3V9   d
   V! VR34       \        4       p. pS F  w  ppVP                  4       P                  4       VP                  4       P                  4       3pVV9   d   KK  VP                  V4       VP                  VP                  4       VP                  4       34       K  	  V# )5a/  
Extract a useful subset of specs directly from the SSR Nuxt payload.
This stays "no browser": we parse embedded window.__NUXT__ data.
Heuristic: only return pairs we can confidently derive.
Strategy:
  - anchor on dimensions pattern when present
  - search within a local window to avoid false matches
r   zwindow.__NUXT__c                <    V ^8  d   QhR\         R\         R\         /# )r   patternscoper   r$   )r   s   "r   r   +parse_specs_from_nuxt.<locals>.__annotate__   s!     / /s /3 /3 /r   c                     \         P                  ! W\         P                  R 7      pV'       d    VP                  ^4      P	                  4       # R# )r   r   )r)   rA   rB   groupr3   )r   r   ms   && r   first$parse_specs_from_nuxt.<locals>.first   s4    IIgBMM:%&qwwqz!.B.r   u=   (\d{1,3},\d+\s*[x×х]\s*\d{1,3},\d+\s*[x×х]\s*\d{1,3},\d+)i.  c                0    V ^8  d   QhR\         R\         /# )r   namevaluer$   )r   s   "r   r   r      s     " "# "c "r   c                 t   < T;'       g    R P                  4       pV'       g   R# SP                  W34       R# )r   N)r3   r`   )r   r   outs   &&r   rb   "parse_specs_from_nuxt.<locals>.add   s+    "##%

D=!r   z\s*   ×r=   u   хu#   Розміри корпусу, ммu    Діагональ, дюймівz"(1,\d{2})"u%   Роздільна здатністьz"(\d{3,4}x\d{3,4})"u!   Пило/вологозахистz"(5\s*ATM)"u   Маса, гz"(16,5)"u   Вібромоторu   "(вбудований)"u5   Ємність аккумулятора, мА·годz"(300)"u   Підтримка 4G/5Gu   "(немає)"	Bluetoothz"(5\.3)"u   Форма дисплеюu   "(прямокутна)"u.   Захисне покриття дисплеяu   "(скло)"uM   Час роботи (максимальна функціональність)u   "(до 9 днів)"u#   Довжина ремінця, ммz"(135-215)"u   Типu   "(Фітнес-браслет)"u   Дисплей, типz"(TFT LCD|TFT)"u   "(фіолетовий)"z
"(Purple)"u   Колір корпусуu   Колір ремінцяz25892178,25892177,25892179i   z825892178,25892177,25892179,"([^"]+)","([^"]+)","([^"]+)"u   пластикu   силіконu   до 18 днів)r)   rA   startmaxminr+   r   r*   r   findsetr3   lowerrb   r`   )r   r#   r   
dims_matchcenterwinrb   dimscol_case	col_upperidsposafterr   n1n2n3seendedupedkvkeyr   s   &                     @r   parse_specs_from_nuxtr      s,    	

A!	/ "$C []^_J#-Z2FEKr\!C6E>"SQ%%@
AWXC" " "vvfb$'//c:BB4M148 *E.#,FG/7Ms1ST+U>3-GHk3/0&A3 GH?zSVAWX"E*;S$ABU;,-#U+F%LM8%QT:UVWY^_uwzY{|-u^S/IJ%:C@A %(:C"@A0#6HmS)I#X%:%:;#X%:%:; 'C
&&+C
byc	"IIQSXYQWWQZB3&B()3&B()!S(B*+ 5D%'G1wwy !'')//"34$;	1779-.  Nr   c          	          V ^8  d   QhR\         \        \        \        3,          ,          R\        R\         \        \        \        3,          ,          /# )r   specslimitr   )r   r	   r%   rJ   )r   s   "r   r   r     s<      tE#s(O, S $uSRUXBW r   c                 b   . p\        4       pV  F  w  rEVP                  4       P                  4       VP                  4       P                  4       3pWc9   d   KI  VP                  V4       VP	                  VP                  4       VP                  4       34       \        V4      V8  g   K   V# 	  V# r;   )r   r3   r   rb   r`   r+   )r   r   r   r   r   r   r   s   &&     r   dedup_specsr     s    !#C5Dwwy !'')//"34;

AGGIqwwy)*s8uJ  Jr   c                $    V ^8  d   QhR\         /# )r   r   )rJ   )r   s   "r   r   r   *  s     s sc sr   c                     \        4        \        P                  ! R R7      p V P                  RRRR7       V P                  R\        P
                  ! RR4      R	R
7       V P                  R\        ^
RR7       V P                  R\        ^ RR7       V P                  RRRR7       V P                  RRRR
7       V P                  RR\        \        P                  4       4      RR7       V P                  RRRR7       V P                  RRRR7       V P                  RRRR
7       V P                  R RR!R
7       V P                  4       p\        P                  VP                  \        R,          4      p. pVP                  '       g   VP                  '       g   \        R"\         P"                  R#7       ^# \%        VP&                  VP                  4      p\)        V\+        VP,                  ^4      R$7      p\        R%4       V'       g   \        R&4       \/        V4       F.  w  rV\1        V4      '       d   R'MR(p\        R)V R*V R+V 24       K0  	  VP2                  P5                  4       pV'       g   V'       g   \        R,\         P"                  R#7       ^# VP6                  ^ 8  g   VP6                  \9        V4      8  d>   \        R-VP6                   R.\9        V4      ^,
           2\         P"                  R#7       ^# W1P6                  ,          p\:        P=                  V4      '       g!   \        R/V 2\         P"                  R#7       ^# \?        V4      p	VP@                  '       d   \        R04       \        R1V	 R224       \        PC                  4        F  w  r\E        W4      w  r\G        V4      p\        R)T
 R3VPH                   R4VPJ                  : R5VPL                   R6VPN                   R7VPP                  ;'       g    R8 24       \        R9VR:,           R;VR<,           R=VR>,           24       K  	  ^ # \        R?4       \        R@VP                   24       VPR                  '       d   \        RA4       \        RBV 24       \        RCV	 24       \T        PV                  ! 4       pVPX                  '       d    \Z        P\                  ! \_        VPX                  4      Pa                  RDRE7      4      p\c        V\d        4      '       d   TM.  Fz  p\c        V\f        4      '       g   K  VPh                  Pk                  VP                  RFR4      VP                  RGR4      VP                  RHRI4      VP                  RJRK4      RL7       K|  	  VPR                  '       d   \o        W\        R,          4      p\o        WV4      w  r\        ROVPH                   24       \        RPVPJ                   24       \        RQVPN                   24       \        RRVPL                   24       \        RSVPP                  ;'       g    RT 24       \        RUVPp                   24       VPr                  '       d    . pVPh                   FD  pVPu                  RFVPv                  RGVPx                  RHVPz                  RJVP|                  /4       KF  	  \_        VPr                  4      P                  \Z        P                  ! VRV^RW7      RDRE7       \        RXVPr                   RY\9        V4       RZ24       VPH                  ^8w  g   VPL                  R\8  d   \        R]4       \        V4      p\        V^PR^7      p\        R_4       \        R`\9        V4       24       VRa,           F  w  pp\        RbV RNV 24       K  	  ^ #   \l         d7   p\        RMTPX                   RNT 2\         P"                  R#7        RIp?ELeRIp?ii ; i  \l         d7   p\        R[TPr                   RNT 2\         P"                  R#7        RIp?ELRIp?ii ; i)cz0Debug SerpAPI results and Hotline specs parsing.)descriptionz--queryTzSearch query (product name).)requiredhelpz--serpapi-keySERPAPI_API_KEYr   z%SerpAPI key (or env SERPAPI_API_KEY).)defaultr   z--max-linkszMax hotline links to show.)typer   r   z--pickz1Which link index to fetch from results (0-based).z--no-serpapi
store_truez"Skip SerpAPI step; requires --url.)actionr   z--urlz&Direct hotline product URL (optional).z--uar
   z'User-Agent preset for fetching Hotline.)r   choicesr   z--compare-uaz?Fetch same URL with multiple UA presets to compare server HTML.z--warm-cookieszVFirst request with desktop UA to get cookies, then fetch with --ua using same session.z--cookies-outz8Save received cookies to JSON file (requests cookiejar).z--cookies-inz,Load cookies from JSON file before fetching.zMERROR: SerpAPI key missing. Provide --serpapi-key or set SERPAPI_API_KEY env.)file)rY   z
== SerpAPI hotline.ua links ==z(none)zPRODUCT?zmaybe-not-product[z] r   zC
No URL to fetch. Provide --url or enable SerpAPI with a valid key.z
Pick index out of range: z. Available 0..z
ERROR: URL is not hotline.ua: z+
== Compare User-Agents (same about_url) ==zabout_url: r   z	] status=z type=z len=z ms=z saved=-z         similarProducts=r   z specsTableTags=r   z
 mentions=r   z
== Fetch Hotline about tab ==zua_preset : z7warm_cookies: true (desktop -> target UA, same session)zproduct_url: zabout_url  : r   )r   r   r   domainNpath/)r   r   z%WARNING: failed to load cookies from z: zstatus     : ztype       : zelapsed_ms : zlength     : zsaved_html : z(not saved)zsnippet    : F)ensure_asciiindentzsaved_cookies: z (z	 cookies)z#WARNING: failed to save cookies to i  zc
NOTE: Response looks suspiciously small or non-200; could be anti-bot, redirect, or markup change.)r   z
== Parsed specs (NUXT only) ==znuxt_count : :N<   Nz- )Cr!   argparseArgumentParseradd_argumentosgetenvrJ   sorted
UA_PRESETSkeys
parse_argsrR   r}   
no_serpapiserpapi_keyprintr   r    rV   rF   ri   r   rY   	enumeraterD   r/   r3   pickr+   r_   rA   r5   
compare_uaitemsr   r   rm   rn   rp   ro   rq   warm_cookiesrQ   r   
cookies_inrT   loadsr   	read_textr   listdictcookiesr   r   r   rr   cookies_outr`   r   r   r   r   r   dumpsr   r   )apargsr}   ra   serpir\   flag
target_url	about_urlpreset	preset_uar   r   countsr   datacer'   r   
nuxt_specsr   r   s                           r   mainr  *  s#   N		 	 -_	`BOOI3QOROOORYY7H"-MT{O|OOMR>ZO[OOH38kOlOON<>bOcOOGR.VOWOOFIvjoo>O7P  XAO  BOON<>O  AOO$\  AYO  ZOOOR6pOqOONB5cOd==?D	I!6	7BE???ahkhrhrs$TZZ1A1AB%dc$..!6LM01(Oe$DA!8!;!;:ATDAaS4&!%& % !JX_b_i_ij99q=DIIU3/		{/#e*UV,X_b_i_ij99%
##J//0=CJJOZ(I<=I;b)*!+!1!1!3F!)7ID*40FAfXYs&7vc>N>N=QQVWZWaWaVbbfgjgugufvv}  B  M  M  T  T  QT  ~U  V  W-f5J.K-LL\]cd  ^A  ]B  BL  MS  Ts  Mt  Lu  v  w	 "4
 	
+,	L	
"#GH	M*
&'	M)
%& D	c::d4??3==w=OPD'd33T;!!T**  EE&"%EE'2&5540vs+	 !  < dz)/DE!$26ID	M#//*
+,	M#**+
,-	M#..)
*+	M#**
&'	M#..99M:
;<	M#--
)*	bC\\

FAFFGQWWhRXZ[Z`Z`ab "!!"--djj5YZ.[fm-nOD$4$4#5RCzKL #d!2tu&t,JZr2J	
,-	M#j/*
+,311#Rsm   G  	c9$//9J"QCPWZWaWabb	c*  	b78H8H7IA3OVYV`V`aa	bs2   C^> 9B8` >_?	+_::_?a+`>>a__main__)uk
   )   r;   )r   )$r   rT   r   r)   r   r   dataclassesr   pathlibr   typingr   r   r   r   r	   rQ   compilerB   r_   r   r!   r,   r5   rD   rV   ri   rk   r   r   r   r   r   r  ru   
SystemExitr   r   r   <module>r     s      	 	 
  !  3 3  JJA2==Q    A  _c  Q	
*

,   !H!HRjsl z
TV
 r   