ó
    ýö¡jÎ0  ã                   ó<  • S r SSKrSSKr\R                  R	                  \R                  R                  \R                  R                  \R                  R                  \5      5      5      SS5      r\R                  R	                  \R                  R                  \R                  R                  \R                  R                  \5      5      5      SS5      r	 SSK
r
\
R                  rSS\
R                  S	S
.SSS\(       a  S\ 3OS-   SS
.SSSS.SSSS.SSSSS.SSSSS
.S \
R                  (       a  \
R                   S!3OS\
R                  S.S"S#S$S.S%S&S'S.S(S)S*S.S+.
rS,S-S.S/.S0/S1.rS8S2 jrS3 rS9S4 jrS5rS:S6 jrS;S7 jrg! \ a	    \" S5      ef = f)<aL  
The shared logged in browser for Phase 2.

One Chrome, one saved profile on this Mac, kept alive between runs so the daily
read does not need Steven each time. He logs into each site once. The cookies
live in data/browser_profile and stay on the machine.

Nothing here places a bid. It reads screens only. No platform API is used.
é    NÚdataÚbrowser_profileÚbrowser_profile_accountsz¸dealer_config.py is missing. Copy dealer_config.example.py to dealer_config.py and fill in your own Carwow filter id, Motorway search URL and accounts email. See SETUP_FOR_NEW_USER.md.ÚMotorwayz!https://pro.motorway.co.uk/signinzhttps://pro.motorway.co.uk)ÚnameÚ	login_urlÚ	stock_urlÚbase_urlÚCarwowz*https://dealers.carwow.co.uk/dealers/loginz<https://dealers.carwow.co.uk/dealers/listings/filtered/stockz?saved_filter_id=Ú zhttps://dealers.carwow.co.ukzGlass'sz!https://uk.glass.co.uk/auth/loginzhttps://uk.glass.co.uk)r   r   r
   ÚCazanaz2https://trade.percayso-vehicle-intelligence.co.uk/z1https://trade.percayso-vehicle-intelligence.co.ukÚAuction4Carsz0https://www.auction4cars.com/?scrollToLogin=truezhttps://www.auction4cars.comz*https://www.auction4cars.com/live-auctions)r   r   r
   r	   ÚClickdealerzhttps://myclickdealer.co.ukz�https://myclickdealer.co.uk/stock_list.php?stock_type=stock&costs=net&status=ins&search_weeks=0&search_weeks_retail=0&vehicle_grading_id=0&action=View+ReportÚ	DealerKitz/loginzDealer Auctionz4https://portal.autotrader.co.uk/dealerauction-login/zhttps://portal.autotrader.co.ukz
LE Capitalz,https://stocktrak.lecapital.co.uk/auth/loginz!https://stocktrak.lecapital.co.ukÚ	DealerWayzhttps://dealerway.co.uk/loginzhttps://dealerway.co.uk)
ÚmotorwayÚcarwowÚglassÚcazanaÚauction4carsÚclickdealerÚ	dealerkitÚdealerauctionÚ	lecapitalÚ	dealerwayFi   i„  )ÚwidthÚheightú---disable-blink-features=AutomationControlled)ÚheadlessÚviewportÚargsc                 ó  • U=(       d    [         n[        R                  " USS9  [        [        5      nUb  XS'   U(       a*  [        UR                  S/ 5      5      [        U5      -   US'   U R                  R                  " U40 UD6$ )aÑ  Open the persistent context that carries Steven's logins. Only one
process may use the profile at a time, so never run two of these at once.
Used for the login walkthrough. extra_args adds Chrome flags for this launch
only, for example a --window-position so a headed window opens on the right
screen. profile_dir overrides which persistent profile is opened (Mark
2026-08-27, "we have 2 accounts for MW... push the watched cars from BB
to both accounts"): defaults to PROFILE_DIR (the one every other
Motorway read and write in this project already shares), but the
accounts@rightdrive.co.uk login needs its own genuinely separate
profile, see ACCOUNTS_PROFILE_DIR, so the two logins never clash or
overwrite each other.T©Úexist_okr   r!   )	ÚPROFILE_DIRÚosÚmakedirsÚdictÚLAUNCH_ARGSÚlistÚgetÚchromiumÚlaunch_persistent_context)Ú
playwrightr   Ú
extra_argsÚprofile_dirÚoptss        Ú1/Users/stevendouglas/BidBrain/bidbrain/browser.pyÚopen_contextr3   ”   sv   € ð ×,¤€KÜ‡K‚K� dÒ+Ü”Ó€DØÑØ#ˆZÑÞÜ˜DŸH™H V¨RÓ0Ó1´D¸Ó4DÑDˆˆV‰Ø×Ñ×8Ò8¸ÑMÈÑMÐMó    c                 óŠ   • [         R                  R                  [         R                  R                  [        5      U  S35      $ )zsWhere a site's saved session state lives, for sites whose login cookie
does not survive a browser restart (Carwow).z_state.json)r&   ÚpathÚjoinÚdirnamer%   )Úsite_keys    r2   Ú
state_pathr:   ª   s-   € ô �7‰7�<‰<œŸ™Ÿ™¬Ó4¸¸
À+Ð6NÓOÐOr4   c                 ó>  ^^	• [        U5      n[        R                  R                  U5      (       ac  S/nU(       a  U[	        U5      -  nU R
                  R                  X%S9m	T	R                  U[        S   S9nUR                  mUU	4S jnXvl	        U$ [        XUS9$ )aú  Open a context for reading a site. If we saved a session state for this
site (because its login is session only, like Carwow, and DealerKit,
found live 2026-08-24: its own dealerkit_session cookie carries
expires=-1, a real browser session cookie, so it is silently dropped
every time the persistent profile's own Chrome process restarts, which
is every single automated run, a fresh OS process each time; open_context
had been used for every DealerKit write in this project regardless, each
one quietly starting from a logged out state unless a login happened to
still be live in whatever was the last still open persistent profile
window), load that so the run is logged in. Otherwise fall back to the
persistent profile (Motorway). extra_args adds Chrome flags for this
launch only (for example a --window-position so a headed window opens on
the right screen, see daily_run.py's _login_win_args), same as
open_context's own parameter, now honoured on both branches here rather
than only the persistent profile one.r   )r   r!   r    )Ústorage_stater    c                  ó¦   >•  T" U 0 UD6   TR                  5         g ! [         a     g f = f!  TR                  5         f ! [         a     f f = f= f©N)ÚcloseÚ	Exception)r!   ÚkwargsÚ_orig_closeÚbs     €€r2   Ú_close_with_browserÚ0open_reader_context.<locals>._close_with_browserÏ   sU   ø€ ðÙ˜TÐ, VÒ,ðØ—G‘G•IøÜ ó ÙðûðØ—G‘G•IøÜ ó Ùðýs7   ƒ- Œ �
*©*­A¯A ¿AÁ 
AÁ
AÁAÁA)r   r/   )r:   r&   r6   Úexistsr*   r,   ÚlaunchÚnew_contextr)   r?   r3   )
r.   r9   r   r/   Úspr!   ÚctxrD   rB   rC   s
           @@r2   Úopen_reader_contextrK   °   s•   ù€ ô  
�HÓ	€BÜ	‡w�w‡~�~�b×ÑØ?Ð@ˆÞØ”D˜Ó$Ñ$ˆDØ×Ñ×&Ñ&°Ð&ÐDˆØ�m‰m¨"´{À:Ñ7NˆmÐOˆð —i‘iˆö	ð (Œ	Øˆ
Ü˜
À*ÑMÐMr4   )ú.jpgz.jpegz.pngz.webpz.gifz.heicz.tifz.tiffc                 ó$  • [         R                  " SS[        U 5      R                  S5      S   R                  S5      S   5      nU(       d  SU U=(       d    S 3$ U(       a,  UR	                  5       R                  [        5      (       d  X2-  nU$ )	aL  A safe local filename for one downloaded image: the url's own last
path segment before the query string, anything odd replaced, so a
later reader can still tell a service history scan from a V5. `ext` is
added when that segment carries no image extension of its own, which
is every Carwow photo (their ids are opaque). Pure, tested.z[^A-Za-z0-9._-]Ú_Ú?r   Ú/éÿÿÿÿzphoto-rL   )ÚreÚsubÚstrÚsplitÚlowerÚendswithÚ
IMAGE_EXTS)ÚurlÚiÚextr   s       r2   Úmedia_filenamer\   ç   sv   € ô �6Š6Ð$ c¬3¨s«8¯>©>¸#Ó+>¸qÑ+A×+GÑ+GÈÓ+LÈRÑ+PÓQ€DÞØ˜�s˜3Ÿ= &˜/Ð*Ð*Þ
�4—:‘:“<×(Ñ(¬×4Ñ4Ø‰ˆØ€Kr4   c           	      óì  • [         R                  " USS9  / n[        U5       H«  u  pV U R                  R                  R                  USS9nUR                  S:w  a  M<  [         R                  R                  U[        XeU5      5      n[        US5       n	U	R                  UR                  5       5        SSS5        UR                  U5        M­     U$ ! , (       d  f       N%= f! [         a     MÏ  f = f)zñDownload image urls to real files in out_dir, through the page's own
signed in session. Returns the paths written, skipping (never raising
on) any one url that fails: one dead link must never lose every other
photo captured in the same pass.Tr#   i0u  )ÚtimeoutéÈ   ÚwbN)r&   r'   Ú	enumerateÚcontextÚrequestr+   Ústatusr6   r7   r\   ÚopenÚwriteÚbodyÚappendr@   )
ÚpageÚurlsÚout_dirr[   ÚpathsrZ   rY   Úrespr6   Úfs
             r2   Údownload_mediaro   õ   sÇ   € ô
 ‡K‚K� $Ò'Ø€EÜ˜D–/‰ˆð		Ø—<‘<×'Ñ'×+Ñ+¨C¸Ð+Ð?ˆDØ�{‰{˜cÓ!ÙÜ—7‘7—<‘< ¬¸ÀÓ)DÓEˆDÜ�d˜DÔ! QØ—‘˜Ÿ	™	›Ô$÷ "à�L‰L˜Öñ "ð €L÷ "Õ!ûô ó 	Úð	ús/   ©4C%Á6C%Â CÂ5C%Ã
C"	ÃC%Ã%
C3Ã2C3)NNN)TN)r   Nr>   )Ú__doc__r&   rR   r6   r7   r8   ÚabspathÚ__file__r%   ÚACCOUNTS_PROFILE_DIRÚdealer_configÚImportErrorÚRuntimeErrorÚCARWOW_SAVED_FILTER_IDÚMOTORWAY_STOCK_URLÚDEALERKIT_BASE_URLÚSITESr)   r3   r:   rK   rX   r\   ro   © r4   r2   Ú<module>r|      s"  ðñó 
Û 	à�g‰g�l‰lØ‡G�G‡O�O�B—G‘G—O‘O B§G¡G§O¡O°HÓ$=Ó>Ó?ÀÐIZó€ð —w‘w—|‘|Ø‡G�G‡O�O�B—G‘G—O‘O B§G¡G§O¡O°HÓ$=Ó>Ó?ÀÐIcóÐ ðÛð '×=Ñ=Ð ð
 Ø8ð #×5Ñ5Ø0ñð ØAð
 Tæ1ð ,Ð,BÐ+CÑDØ79ñ;ð 3ñð Ø8Ø,ñð ØIØGñð ØGØ2ð
 Bñ	ð ð 3ðAð 2ñð ð ER×Dd×Dd˜×8Ñ8Ð9¸Ñ@ÐjlØ!×4Ñ4ñð !ð LØ5ñð ð DØ7ñð ð 5Ø-ñ	ñe\	€ðD Ø¨#Ñ.Ø<Ð=ñ€ôNò,Pô*Nðh R€
ôõøðg ó Ù
ð	Dóð ðús   ÃF ÆF