+
    ¡zˆj;  ã                   óÞ   € R t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	H
t
 RtRtRt^tRt^tRt^t0 RmtR R	 ltRR
 R lltRR R lltR R ltR R ltR R ltR R ltR R ltR# )aH  
Shared, dependency-free core for the manga downloaders.

Provides:
  - a polite HTTP fetcher (single sequential connection, fixed delays,
    exponential backoff, and a hard stop if the server keeps returning 429),
  - a resumable chapter download engine (page_NNN naming, self-healing
    reconciliation of existing files, download_state.json),
  - a Source adapter interface that site modules implement.

A Source adapter provides:
  name            str   identifier, e.g. "mangaread"
  site            str   origin used for links, e.g. "https://www.mangaread.org"
  output_dir(url) Path  archive directory for a manga URL
  chapter_dirname(ch_id) str  e.g. "chapter_12" or "chapter_71_1"
  get_chapters(manga_url) -> list[(ch_id, ch_url, label)]  in READING order
  get_pages(chapter_url) -> list[str]  page image URLs in reading order
N)ÚPathzjMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/124.0g       @g      è?g      @g       @c                ó(   € V ^8„  d   QhR\         RR/# )é   ÚmsgÚreturnN©Ústr)Úformats   "Ú;/home/nathan/projects/ai/drake_scans_download/manga/core.pyÚ__annotate__r   .   s   € ÷ :ñ :ŒSð :�Tñ :ó    c                 óV   € \        \        P                  ! R 4      V ,           RR7       R# )z[%H:%M:%S] T)ÚflushN)ÚprintÚtimeÚstrftime)r   s   &r
   Úlogr   .   s   € Ü	Œ$�-Š-˜Ó
&¨Õ
,°D×9r   c          	      ód   € V ^8„  d   QhR\         R\         R,          R\        R\        R,          /# )r   ÚurlÚrefererNÚtimeoutr   )r   ÚfloatÚbytes)r	   s   "r
   r   r   2   s1   € ÷ $ñ $Œsð $œS 4�Zð $¼ð $Ì%ÐRVÍ,ñ $r   c                óð  € R\         RR/pV'       d   WR&   \        ^\        ^,           4       Fg  p \        P                  P                  WR7      p\        P                  P                  WRR7      ;_uu_ 4       pVP                  4       uuRRR4       u # 	  \        R\         RV  24       R#   + '       g   i     K“  ; i  \        P                  P                   Ed   pTP                  R8X  dx   T\        8¼  d"   \        R	4       \        P                  ! ^4       \        ^T,          ,          p\        R
TR RT R\         R24       \         P"                  ! T4        Rp?EK>  RTP                  u;8:  d   R8  dU   M MQ\        T,          p\        RTP                   RTR RT R\         R2	4       \         P"                  ! T4        Rp?EK«  \        RTP                   RT  R24        Rp? R# Rp?i\        P                  P$                  \&        \(        3 dM   p\        T,          p\        RT RTR RT R\         R2	4       \         P"                  ! T4        Rp?EKB  Rp?ii ; i)z¿GET a URL politely. Returns bytes, or None after repeated failure.

Backs off on 5xx/network errors; on 429 it backs off exponentially and
aborts the whole run if the rate limiting persists.
z
User-AgentÚAcceptz*/*ÚReferer)Úheaders)r   Ni­  u7   Server keeps returning 429 â€” stopping out of respect.z  429 rate-limited. Waiting z.0fzs (attempt Ú/z)...iô  iX  z  HTTP z. Retrying in z for u    â€” giving up on this URL.z  Network error (z). Retrying in z  Failed after z attempts: )Ú
USER_AGENTÚrangeÚMAX_RETRIESÚurllibÚrequestÚRequestÚurlopenÚreadÚerrorÚ	HTTPErrorÚcodeÚABORT_ON_429_RETRIESr   ÚsysÚexitÚRETRY_BACKOFF_BASEr   ÚsleepÚURLErrorÚTimeoutErrorÚOSError)	r   r   r   r   ÚattemptÚreqÚrespÚeÚwaits	   &&&      r
   Úfetchr6   2   sô  € ð œZ¨°5Ð9€GßØ$�	ÑÜ˜œK¨!�OÖ,ˆð	Ü—.‘.×(Ñ(¨Ð(Ó>ˆCÜ—‘×'Ñ'¨Ð'×=Ô=ÀØ—y‘y“{÷ >Ô=ñ -ô4 ˆ/œ+˜ k°#°Ð7Ô8Ù÷1 >×=Ð=ûä�|‰|×%Ñ%õ 	Ø�v‰v˜Œ}ØÔ2Ô2ÜÐQÔRÜ—H’H˜Q”KÜ)¨Q°'­\Õ:�ÜÐ2°4¸°*¸KÈÀyÐPQÔR]ÐQ^Ð^bÐcÔdÜ—
’
˜4Ô ÞØ�a—f‘fÖ"˜s×"Ü)¨GÕ3�Ü�g˜aŸf™f˜X ^°D¸°:¸[ÈÈ	ÐQRÔS^ÐR_Ð_cÐdÔeÜ—
’
˜4Ô ÞÜ�'˜!Ÿ&™&˜  s eÐ+FÐGÔHÞûÜ—‘×%Ñ%¤|´WÐ=ô 	Ü%¨Õ/ˆDÜÐ# A 3 o°d¸3°Z¸{È7È)ÐSTÔU`ÐTaÐaeÐfÔgÜ�JŠJ�tÔÞûð		úsW   ²ACÁ:B0Â

CÂ0CÂ;CÃCÃ I5Ã$BG<Å,A&G<ÇG<Ç<+I5È(I5È)A I0É0I5c                óX   € V ^8„  d   QhR\         R\         R,          R\         R,          /# )r   r   r   Nr   r   )r	   s   "r
   r   r   Y   s.   € ÷ Pñ P”Cð P¤#¨¥*ð P¼¸d½
ñ Pr   c                 óL   € \        WR 7      pVe   VP                  RRR7      # R# ))r   Nzutf-8Úreplace)Úerrors)r6   Údecode)r   r   Údatas   && r
   Ú
fetch_htmlr=   Y   s)   € Ü�Ô&€DØ59Ò5Eˆ4�;‰;�w yˆ;Ó1ÐOÈ4ÐOr   c                ó0   € V ^8„  d   QhR\         R\        /# )r   Úpathr   ©r   Údict)r	   s   "r
   r   r   ^   s   € ÷ ñ ”Tð œdñ r   c                 ó¢   € V P                  4       '       d&    \        P                  ! V P                  4       4      # / #   \         d    / u # i ; i©N)ÚexistsÚjsonÚloadsÚ	read_textÚ	Exception)r?   s   &r
   Ú
load_staterI   ^   sE   € Ø‡{�{‡}‚}ð	Ü—:’:˜dŸn™nÓ.Ó/Ð/ð €Iøô ô 	ØŠIð	ús   ˜#> ¾AÁAc                ó4   € V ^8„  d   QhR\         R\        RR/# )r   r?   Ústater   Nr@   )r	   s   "r
   r   r   g   s!   € ÷ ñ ”Tð ¤$ð ¨4ñ r   c                 ó˜   € V P                  R 4      pVP                  \        P                  ! V^R7      4       VP	                  V 4       R# )z.tmp)ÚindentN)Úwith_suffixÚ
write_textrE   Údumpsr9   )r?   rK   Útmps   && r
   Ú
save_staterR   g   s5   € Ø
×
Ñ
˜6Ó
"€CØ‡N�N”4—:’:˜e¨AÔ.Ô/Ø‡K�K�Ör   c                ó‚   € V ^8„  d   QhR\         R\        \        ,          R\        R\        R\         R\        R\        /# )r   Údest_dirÚurlsr   rK   Ú
state_fileÚlabelr   )r   Úlistr   rA   Úbool)r	   s   "r
   r   r   m   sN   € ÷ Añ Aœtð A¬4´­9ð A¼sð AÜ ðAÜ.2ðAÜ;>ðAÜCGñAr   c           	     ó  € V P                  RRR7       V P                  4        Uu/ uFK  pVP                  4       '       g   K  VP                  P	                  R4      '       d   K>  VP                  VbKM  	  pp\        4       p. p	\        V^4       F¸  w  r«\        P                  P                  V4      pRV
R \        P                  P                  V4      ^,           2pRpWÜ3 F.  pVP                  V4      pVf   K  \        V4      V9  g   K,  Tp M	  Ve   VP                  \        V4      4       V	P                  WÞV34       Kº  	  ^ pVP                  4        FV  w  pp\        V4      V9  g   K  VP                   P#                  4       \$        9   g   K=  VP'                  4        V^,          pKX  	  V'       d   \)        RV R24       ^ p\        V	^4       EF   w  p
w  rÞpW,          pVe   VV8w  d   VP+                  V4       VP-                  4       '       d"   VP/                  4       P0                  ^ 8”  d   Ke  \2        P4                  ! \6        4       \9        W²R	R
7      pVf   V^,          pK›  VP;                  VP                   R,           4      pVP=                  V4       VP?                  V4       V
\@        ,          ^ 8X  g   Kó  V
\C        V	4      8  g   EK  \2        P4                  ! \D        4       EK#  	  V'       d    \)        V RV R\C        V4       R24       R# RVR\C        V4      R\2        PF                  ! R4      /VPI                  R/ 4      V&   \K        WC4       \)        V R\C        V4       R24       R# u upi )a=  Download one chapter's pages as page_001.ext, page_002.ext, ...

Self-healing: existing files are matched by the URL basename (old naming)
or the final page_NNN name and renamed into place; missing pages are
downloaded; stray image files that belong to neither are removed.
Returns True when the chapter is complete.
T©ÚparentsÚexist_okz.partÚpage_Ú03dNz
  removed z$ stray image files from chapter dir.g      N@)r   r   z: r   u!    pages failed â€” retry next run.Fr   ÚimagesÚfinishedz%Y-%m-%dT%H:%M:%SÚdonez: done (z pages).)&ÚmkdirÚiterdirÚis_fileÚnameÚendswithÚsetÚ	enumerateÚosr?   ÚbasenameÚsplitextÚgetÚidÚaddÚappendÚitemsÚsuffixÚlowerÚIMG_EXTSÚunlinkr   ÚrenamerD   ÚstatÚst_sizer   r-   ÚDELAY_IMAGEr6   rN   Úwrite_bytesr9   ÚPAUSE_EVERY_N_IMAGESÚlenÚ
PAUSE_LONGr   Ú
setdefaultrR   )rT   rU   r   rK   rV   rW   ÚpÚexistingÚassignedÚplanÚir   ÚbaseÚ	dest_nameÚsrcÚ	cand_nameÚcandÚremovedrf   ÚfailedÚdestr<   rQ   s   &&&&&&                 r
   Údownload_chapterrŒ   m   sí  € ð ‡N�N˜4¨$€NÔ/à#+×#3Ñ#3Ô#5ó AÑ#5˜aØ—9‘9—;ô Ø'(§v¡v§¡°w×'?ô �—‘˜’	Ñ#5€Hð Aä‹u€HØ€DÜ˜D !Ö$‰ˆÜ�w‰w×Ñ Ó$ˆØ˜A˜c˜7¤2§7¡7×#3Ñ#3°DÓ#9¸!Õ#<Ð"=Ð>ˆ	ØˆØ#Ó*ˆIØ—<‘< 	Ó*ˆDØÔ¤B t£H°HÖ$<Ø�Ùñ	 +ð
 Š?Ø�L‰Lœ˜C›Ô!Ø�‰�Y SÐ)Ö*ñ %ð €GØ—>‘>Ö#‰ˆˆaÜˆa‹5˜Ö  Q§X¡X§^¡^Ó%5¼Ö%AØ�H‰HŒJØ�q�LŠGñ $÷ Üˆj˜˜	Ð!EÐFÔGà€FÜ$-¨d°A×$6Ñ ˆÑ ˆI˜CØÕ#ˆØŠ?˜s dœ{Ø�J‰J�tÔØ�;‰;�=Š=˜TŸY™Y›[×0Ñ0°1Ô4ÙÜ�
Š
”;ÔÜ�S°4Ô8ˆØŠ<Ø�a�KˆFÙØ×Ñ˜tŸ{™{¨WÕ4Ó5ˆØ�‰˜ÔØ�‰�DÔØÔ#Õ# qÖ(¨Q´°T³¯]Ü�JŠJ”z×"ñ %7÷" Üˆuˆg�R˜�x˜q¤ T£ Ð+LÐMÔNÙð 	ˆwØ”#�d“)Ø”D—M’MÐ"5Ó6ð+€E×Ñ�V˜RÓ  Ñ'ô
 ˆzÔ!Üˆ5ˆ'�œ#˜d›)˜ HÐ-Ô.ÙùòmAs   ¦N	ÁN	Á&N	c                ó(   € V ^8„  d   QhR\         RR/# )r   Ú	manga_urlr   Nr   )r	   s   "r
   r   r   ±   s   € ÷ "Wñ "Wœ3ð "W 4ñ "Wr   c                ó   € V P                  V4      pVP                  RRR7       VR,          p\        V4      p\        RV 24       \        P
                  ! \        4       V P                  V4      pV'       g   \        P                  ! R4       \        R\        V4       R24       \        RVP                  4        24       ^ ;rg\        V^4       F±  w  pw  ršp\        R	V R
V R\        V4       R24       \        P
                  ! \        4       V P                  V
4      pV'       g   \        V R24       V^,          pKs  W P                  V	4      ,          p\        WÜW¤W;4      '       d   V^,          pK¨  V^,          pK³  	  \        RV RV R24       V'       d"   \        R4       \        P                  ! ^4       \        R4       R# )z;Download every chapter of a manga through a Source adapter.Tr[   zdownload_state.jsonzFetching manga page: u8   No chapter links found â€” site layout may have changed.zFound z chapters (in reading order).zOutput dir: z
=== z (r   z) ===u3   : could not determine page list â€” retry next run.z
All chapters processed: z ok, z incomplete.z4Re-run this script to retry the incomplete chapters.zKNext: run generate_html.py on this directory to (re)build the reader pages.N)Ú
output_dirrc   rI   r   r   r-   ÚDELAY_CHAPTER_PAGEÚget_chaptersr*   r+   r|   Úresolveri   Ú	get_pagesÚchapter_dirnamerŒ   )ÚsourcerŽ   ÚoutrV   rK   ÚchaptersÚokÚ
incompleterƒ   Úch_idÚch_urlrW   Úpagesr‹   s   &&            r
   Úrunrž   ±   s�  € à
×
Ñ
˜IÓ
&€CØ‡I�I�d T€IÔ*ØÐ,Õ,€JÜ�zÓ"€EäÐ
 	˜{Ð+Ô,Ü‡J‚JÔ!Ô"Ø×"Ñ" 9Ó-€HßÜ�ŠÐKÔLÜˆ&”�X“�Ð<Ð=Ô>Üˆ,�s—{‘{“}�oÐ&Ô'àÐ€BÜ%.¨x¸Ö%;Ñ!ˆÑ!ˆE˜5Üˆf�U�G˜2˜a˜S ¤# h£- °Ð6Ô7Ü�
Š
Ô%Ô&Ø× Ñ  Ó(ˆßÜ�5�'ÐLÐMÔNØ˜!�OˆJÙØ×+Ñ+¨EÓ2Õ2ˆÜ˜D¨¸
×JÒJØ�!�GŠBà˜!�OŠJñ &<ô Ð
$ R D¨¨j¨\¸ÐFÔGßÜÐBÔCÜ�Š�ŒÜÐUÖVr   c                ó0   € V ^8„  d   QhR\         R\         /# )r   r   r   r   )r	   s   "r
   r   r   Ö   s   € ÷ (ñ (”sð (œsñ (r   c                óf   € \         P                  ! RV 4      pV'       d   VP                  ^4      # R# )z8Trailing non-empty path segment, minus a trailing slash.z([^/]+?)/?$Úmanga)ÚreÚsearchÚgroup)r   Úms   & r
   Úslug_from_urlr¦   Ö   s'   € ä
�	Š	�. #Ó&€Aßˆ1�7‰7�1‹:Ð' Ð'r   >   ú.bmpú.gifú.jpgú.pngú.avifú.jpegú.webp)Ng      >@rC   )Ú__doc__rE   rj   r¢   r*   r   Úurllib.errorr!   Úurllib.requestÚpathlibr   r   r‘   ry   r{   r}   r    r,   r)   rt   r   r6   r=   rI   rR   rŒ   rž   r¦   © r   r
   Ú<module>r³      sŠ   ðñó& Û 	Û 	Û 
Û Û Û Ý ð4ð ð Ð Ø€ØÐ Ø€
Ø€ØÐ ØÐ âF€õ:÷$÷NPõ
õõAõH"W÷J(r   