+
    KiI  c                   s   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	t^ RI
Ht ^ RIHt ^ RIHt ^ RIHtHtHtHt ^ RIt^ RIHt ^ RIHt ^ RIHtHt ^ R	IHt ^ R
IHtH t  ^ RI!H"t" ^ RI#H$t$ ^ RI%H&t& ]PN                  PQ                  ]PR                  R4      t*]PN                  PQ                  ]PR                  R4      t+R t,R R lt-R R lt.R R lt/R R lt0R R lt1R t2]! 4       t3]! 4       t4R t5R t6R t7R t8R  t9R! R" lt:R# R$ lt;R% R& lt<R@R' R( llt=R) R* lt>R+ R, lt?R- R. lt@R/ R0 ltAR@R1 R2 lltBR3 tCRAR4 ltDRAR5 ltERBR6 ltFR@R7 ltGR8 R9 ltHR: R; ltIR< R= ltJR> tKR? tLR# )Cz.Functions to interact with the Lutris REST APIN)OrderedDict)datetime)gettext)AnyDictOptionalTuple)settings)NotificationSource)httpsystem)get_gpus_info)	HTTPErrorRequest)LINUX_SYSTEM)logger)time_agoz
auth-tokenz	user.jsonc                sT    \         P                  ! V RV P                  R4       R4      # )zRConvert a date string originating from the API and convert it to a datetime objectNZ.z%Y-%m-%dT%H:%M:%S)timeZstrptimeZfind)Zdate_string   &//usr/lib/python3.14/site-packages/lutris/api.pyget_time_from_api_dater      s'    ==%<{'7'7'<=?RSS    c                $    V ^8  d   QhR\         /#    return)Zfloatformat   "r   __annotate__r   $   s     < <5 <r   c                  s\    \         P                  P                  \        P                  4      # N)ospathZgetmtimer   RUNTIME_VERSIONS_PATH r   r   get_runtime_versions_dater#   $   s    77H::;;r   c                r   r   strr   r   r   r   r   (   s      C r   c                  sb     \        \        4       4      #   \         d    \        R 4      u # i ; i)Znever)r   r#   FileNotFoundError_r"   r   r   "get_runtime_versions_date_time_agor(   (   s/    1344 zs    ..c                r   r   )Zboolr   r   r   r   r   /   s      d r   c                 s:    \        4       p V R,           p\        P                  ! R\        P                  ! V 4      P                  R4      \        P                  ! V4      P                  R4      4       V\        P                  ! 4       8  #   \         d     R# i ; i)z(Check if the runtime needs to be updatedz$Modified at %s, will update after %sz%cTi`T  )r#   r   debugr   ZfromtimestampZstrftimer   r&   )Zmodified_atZshould_update_at     r   check_stale_runtime_versionsr+   /   s    
/1&42"";/88>""#34==dC	

  $))+-- s   BB BBc                F    V ^8  d   QhR\         \        \        3,          /# r   r   r%   r   r   r   r   r   r   >   s      4S> r   c                 s   \        4       p V P                  4        Uu. uF$  pRP                  VR,          VR,          .4      NK&  	  pp\        P                  R,           RP                  V4      ,           p\
        P                  ! VRR/R7      p VP                  4        \        \        P                  RRR7      ;_uu_ 4       p\        P                  ! TP                  T^R7       R
R
R
4       TP                  # u upi   \
        P                   d$   p\        P                  ! R	T4       / u R
p?# R
p?ii ; i  + '       g   i     TP                  # ; i)zQueries runtime + runners + current client versions and stores the result
in a file; the mdate of this file is used to decide when it is stale and should
be replaced.Z ZPCI_IDZPCI_SUBSYS_IDz/api/runtimes/versions?pci_ids=Z,Content-Typeapplication/jsonheadersz#Unable to get runtimes from API: %sNwutf-8ZmodeencodingZindent)r   Zvaluesjoinr   SITE_URLr	   r   getr   r   erroropenr!   jsondump)Z	gpus_infoZgpuZpci_idsurlresponseexruntime_files          r   download_runtime_versionsrB   >   s    IJSJZJZJ\]J\3sxxXO(<=>J\G]


?
?#((7BS
SC||C.:L)MNH 
h,,3	I	I\		(--a8 
J== ^ >> :B?	 
J	I==s/   *C=D $D=D:D5/D:5D:=E	c                r,   r   r-   r   r   r   r   r   Q   s     ' 'd38n 'r   c                 s   \         P                  ! \        P                  4      '       g   / # \	        \        P                  RRR7      ;_uu_ 4       p \
        P                  ! V 4      uuRRR4       #   + '       g   i     R# ; i)z^Load runtime versions from the json file that is created at startup
if it is missing or stale.rr3   r4   N)r
   path_existsr   r!   r;   r<   load)rA       r   get_runtime_versionsrG   Q   sS     h<<==		h,,3	I	I\yy& 
J	I	I	Is   A22B	c                 s`   \         P                  ! \        4      '       g   R# \        \        RRR7      ;_uu_ 4       p V P	                  4       pRRR4        XP                  R4      w  r#RTRT/#   + '       g   i     L*; i  \         d     \        P                  ! R\        4        R# i ; i)	zRead the API token from diskNrC   r3   r5   :z!Unable to read Lutris token in %stokenusername)	r
   rD   API_KEY_FILE_PATHr;   Zreadsplit
ValueErrorr   r:   )
token_fileZ
api_stringrK   rJ          r   read_api_keyrQ   Z   s    /00	w	7	7:__&
 
8$**3/ UJ11 
8	7  8:KLs   A0B 0B 	&B-,B-c                sv   \         P                  R,           pRV RV/p \        P                  ! W#^
R7      pVP	                  4        VP                  4       pRV9   d   VR,          p\        \        RRR7      ;_uu_ 4       pVP                  V : R	V: 24       R
R
R
4       \        4       pV'       g   \        P                  ! R4       M;\        \        RRR7      ;_uu_ 4       p\
        P                  ! W^R7       R
R
R
4       \        P                  4        V# R
#   + '       g   i     L; i  + '       g   i     L;; i  \        P                   \        P"                  \        P$                  \        P&                  \        P(                  3 d#   p	\        P*                  ! RY)4        R
p	?	R# R
p	?	ii ; i)zConnect to the Lutris APIz/api/accounts/tokenrK   password)r>   dataZtimeoutrJ   r2   r3   rH   rI   NzUnable to fetch user infor6   z$Unable to connect to server (%s): %sF)r   r8   requestspostZraise_for_statusr<   r;   rL   Zwritefetch_user_infor   warningUSER_INFO_FILE_PATHr=   LUTRIS_ACCOUNT_CONNECTEDfireZRequestExceptionZConnectionErrorr   ZTooManyRedirectsZTimeoutr:   )
rK   rR   Z	login_urlcredentialsr?   Z	json_dictrJ   rO   Zaccount_infor@   s
   &&        r   connectr\   l   sC   !!$99IxX>K==Y"M!!#MMO	ig&E'w??:  He!<= @*,L:;-sWEEIIlqA F$))+L  ?? FE
 	!!  !!  	;YKsJ   A"E DAE %D1>E D.	)E 1E	<E AF8F33F8c                 s    \         \        3 F7  p \        P                  ! V 4      '       g   K!  \        P
                  ! V 4       K9  	  \        P                  4        R# )z-Removes the API token, disconnecting the userN)rL   rX   r
   rD   r   ZremoveLUTRIS_ACCOUNT_DISCONNECTEDrZ   )Z	file_pathrF   r   
disconnectr^      s=    ')<=	i((IIi  >  $$&r   c                 s    \        4       p V '       g   R# \        P                  R,           p\        P                  ! VRRV R,          ,           /R7      pVP                  4       pVP                  # )z+Retrieves the user info to cache it locallyNz/api/users/meZAuthorizationzToken rJ   r0   )rQ   r   r8   r	   r   r9   r<   )r[   r>   requestr?   rP   r   rV   rV      sT    .K


o
-Cll3([QXEY:Y(Z[G{{}H==r   c                  s    \         P                  P                  \        4      '       g   / # \	        \        R R7      ;_uu_ 4       p \
        P                  ! V 4      pRRR4       V#   + '       g   i     X# ; i)r3   rH   N)r   r    ZexistsrX   r;   r<   rE   )Zuser_info_fileZ	user_infor*   r   read_user_infor`      sS    77>>-..		!G	4	4IIn-	 
5 
5	4s   A&&A7	c                s   \         P                  ! R4       \        P                  R,           V ,           p\        P                  P	                  R4      ^,          p\
        P                  ! VR4      pV^ ,          w  rErgp\        RV/4      p	\        P                  ! 4       p
Wn
        V
P                  WR7      pVP                  4       # )z4Return the available runners for a given runner namezRetrieving runnersz/api/runners/z//i  ZHostr0   )r   r)   r   r8   rM   socketZgetaddrinfor    rT   ZSessionr1   r9   r<   )runner_nameZapi_urlZhostZanswersZ_familyZ_typeZ_protoZ
_canonnameZ	_sockaddrr1   Zsessionr?      &           r   get_runnersrd      s    
LL%&/1K?G""4(+D  s+G6=aj3WV64.)G GO{{7{4H==?r   c                0    V ^8  d   QhR\         R\        /# )r   rb   r   )r%   listr   r   r   r   r      s     - -# -$ -r   c                 sr    \        R P                  \        P                  V 4      4      pVP	                  4       P
                  pV'       g   \        P                  ! R4       T'       g   . # TP	                  R4      ;'       g    . #   \         d$   p\        P                  ! RT4       Rp Rp?LNRp?ii ; i)z{}/api/runners/{}z Failed to get runner informationz$Unable to get runner information: %sNversions)	r   r   r   r8   r9   r<   r   r:   r   )rb   r_   Zrunner_infor@      &   r   download_runner_versionsri      s    -44X5F5FTUkkm((LL;< 	??:&,,",  ;R@s   A!B B6B11B6c                sR    V ^8  d   QhR\         \        \        3,          R\        /# )r   version_infor   )r   r%   r   r   r   r   r      s"     6 6S#X 63 6r   c                 sp    V P                  R 4      ;'       g    RpV P                  R4      p\        W4      # )version architecture)r9   format_version_architecture)rj   rk   arch   &  r   format_runner_versionrq      s5    y)//RGN+D&w55r   c                0    V ^8  d   QhR\         R\         /# r   version_namer   r$   r   r   r   r   r      s     S S S Sr   c                sF    \        V 4      w  rV'       d   \        W4      # R# )znEnsures the version name has an architecture on the end ofit;
adds the system's architecture if it is missing.rl   )parse_version_architecturern   )rt   base_versionro   rp   r   normalize_version_architecturerw      s%     4LAL>J&|:RPRRr   c                sR    V ^8  d   QhR\         R\        \         ,          R\         /# )r   rv   ro   r   )r%   r   r   r   r   r   r      s&      c # RU r   c                s~    V '       g   R# V P                  R4      '       d   V # V'       d   RP                  W4      # V # )zHAssembles a version with architecture from the version and architecture.rl   z	GE-Protonz{}-{})
startswithr   )rv   ro      &&r   rn   rn      s9    
 {++~~l11r   c                sh    V ^8  d   QhR\         R\        \         \        \         ,          3,          /# rs   )r%   r   r   r   r   r   r   r      s(     + +S +U3;M5N +r   c                s    V '       dF   V P                  R4      '       g   V P                  R4      '       d   V P                  R^4      w  rW3# V \        P                  3# )zSplits a version that ends with an architecture into the plain version and
architecture, as a tuple. If the version has no architecture, this provides
the system's architecture instead.z-i386z-x86_64Z-)ZendswithZrsplitr   ro   )rt   rk   ro   rp   r   ru   ru      sU       ))\-B-B9-M-M(//Q7MG= ****r   c          	          V ^8  d   QhR\         R\        \         ,          R\        \        \         \         3,          ,          /# r   rb   rk   r   r%   r   r   r   r   r   r   r      s7      s Xc] xX\]`be]eXfOg r   c                 s   \        T;'       g    R 4      w  r\        4       pV'       d    VR,          V ,          pV Uu. uF$  qUR,          \        P                  R39   g   K"  VNK&  	  ppV'       d!   V Uu. uF  qUR,          V8X  g   K  VNK  	  pp\	        V4      ^8X  d
   V^ ,          #  R# R# u upi u upi   \
         d     R# i ; i)rl   Zrunnersrm   Zallrk   N)ru   rG   r   ro   lenZKeyError)rb   rk   Z_archZruntime_versionsZrunner_versionsrC   s   &&    r   get_runner_version_from_cacher~      s     02>NG+-	.y9+FO*9m/Q~=NS_SdSdfkRl=lqq/Om.="Yo9QXAX11o"Y?#q(&q)) )  n"Y  		s:   B; B1#B1)B; 7B6B6B; 1
B; ;C
	C
c                sh    V ^8  d   QhR\         R\        \        ,          R\        \        ,          /# )r   rg   rk   ro   )rf   r   r%   r   r   r   r   r      s/     R R4 R(3- RxX[} Rr   c              #  s
  a aa"   RV 3R llpS'       d   V! VV3R l4      x  MV! V3R l4      x  V! V3R l4      x  V! V3R l4      x  \         P                  '       d   V! R 4      x  V! V3R lR	R
7      x  R# 5i)z|A generator yielding possible version infos, or None for those that are available;
we pick the first non-None value yielded.Nc                 s   < S Uu. uF  q ! V4      '       g   K  VNK  	  ppV'       d"   V'       g   \        V4      ^8X  d
   V^ ,          # R# u upi )   N)r}   )Z	predicateaccept_ambiguousvZ
candidatesrg   s   &&  r   select_infoZ1iter_get_from_api_candidates.<locals>.select_info  sB    !):AYq\aa
:+s:!/Ca=  ;s
   AAc                 sD   < V R ,          S8H  ;'       d    V R,          S8H  # )rm   rk   r"   )r   ro   rk   s   &r   <lambda>.iter_get_from_api_candidates.<locals>.<lambda>  s$    An$5$=$Y$Y!I,RYBY$Yr   c                 >   < V R ,          S8H  ;'       d
    V R,          # rm   defaultr"   r   ro      &r   r   r     s    An$5$=$N$N!I,$Nr   c                 r   r   r"   r   r   r   r   r     s    . 1T 9 J Ja	l Jr   c                    < V R ,          S8H  # rm   r"   r   r   r   r   r         . 1T 9r   c                 s    V R ,          # )r   r"   )r   r   r   r   r     s    AiLr   c                 r   r   r"   r   r   r   r   r     r   r   T)r   )NF)r   Z	is_64_bit)rg   rk   ro   r   s   fff r   iter_get_from_api_candidatesr      sj      YZZNOO J
KK
9
:: 011
9D
QQs   A B&Bc          	      rz   r{   r|   r   r   r   r   r     s7     	 	S 	8C= 	XVZ[^`c[cVdMe 	r   c                 s    \        T;'       g    R 4      w  r\        V 4      p\        W1V4       FI  pV'       g   K  V'       g   Vu # WP                  R4      8X  g   K0  W$P                  R4      8X  g   KG  Vu # 	  R# )rl   rk   rm   N)ru   ri   r   r9   )rb   rk   ro   rg   Z	candidates   &&   r   get_runner_version_from_apir     sf    .w}}"=MG'4H1(TJ	9  --	22t}}^?\7\   K r   c          	      rz   r{   r|   r   r   r   r   r   (  s=     t t tx} tX`aefiknfnaoXp tr   c                s>    \        W4      ;'       g    \        W4      # )aN  Get the appropriate version for a runner

Params:
    version: Optional version to lookup, will return this one if found

Returns:
    Dict containing version, architecture and url for the runner, an empty dict
    if the data can't be retrieved. If a pseudo-version is accepted, may be
    a dict containing only the version itself.
)r~   r   )rb   rk   ry   r   get_default_runner_version_infor   (  s     )>ssB]^iBssr   c                 sP   \         P                  ! V R R/R7      p VP                  VR7       TP                  ^8w  d$   \        P
                  ! RTP                  4       R# TP                  #   \         P                   d#   p\        P
                  ! RT4        Rp?R# Rp?ii ; i)r.   r/   r0   )rS    Unable to get games from API: %sNzAPI call failed: %s)r	   r   rU   r   r   r:   Zstatus_coder<   )r>   payloadr?   r@      &&  r   get_http_post_responser   6  s    ||C.:L)MNH7# s"*H,@,@A== >> 7<s   A. .B%B  B%c                s    \         P                  R,           p\        V4      ^8  d   VRP                  V4      ,          pV '       g   . # \        P
                  ! RV RV/4      P                  R4      p\        W#4      # )zRead a single page of games from the API and return the response

Args:
    game_ids (list): list of game slugs
    page (str): Page of results to get
z
/api/games?page={}Zgamespager3   r   r8   Zintr   r<   ZdumpsZencoder   )
game_slugsr   r>   r   r   r   get_game_api_pager   C  si     

l
*C
4y1}z  &&	jj':vt<=DDWMG!#//r   c                s   \         P                  RV ,          ,           p\        V4      ^8  d   VRP                  V4      ,          pV'       g   . # \        P
                  ! RV/4      P                  R4      p\        W44      # )zDGet matching Lutris games from a list of appids from a given servicez/api/games/service/%sr   appidsr3   r   )servicer   r   r>   r   s   &&&  r   get_game_service_api_pager   S  si    


5?
?C
4y1}z  &&	jj(F+,33G<G!#//r   c                s   V'       d   \        W 4      pM\        V 4      pV'       g   . # VP                  R. 4      pVP                  R4      '       d   \        P                  ! RVR,          4      pV'       d   VP                  ^4      pM!\        P                  ! RVR,          4        V# V'       d   \        W VR7      pM\        WR7      pV'       g   \        P                  ! RV4        V# WCP                  R4      ,          pK  V# )zBReturn all games from the Lutris API matching the given game slugsresultsZnextz
page=(\d+)zNo page found in %s)r   z"Unable to get response for page %s)	r   r   r9   researchZgroupr   r:   rW   )r   r   r   Zresponse_datar   Z
page_matchZ	next_pages   &&&    r   get_api_gamesr   ^  s    1'F)*5		2.G


F
#
#YY}mF.CD
"((+ILL.f0EF N 5gPYZM-jIMNN?KN 	$$Y//Nr   c                s   V '       g   \        R4      hV'       d   \        P                  W3,          pM\        P                  V ,          p\        P
                  ! RV4       \        P                  ! V4      pVP                  4        VP                  pVf   \        RV,          4      hV'       d   V.pM	VR,          pV Uu. uF  p\        V4      NK  	  up# u upi )z Get installers for a single gamez/No game_slug provided. Can't query an installerzFetching installer %szCouldn't get installer at %sr   )rN   r   ZINSTALLER_REVISION_URLZINSTALLER_URLr   r)   r	   r   r9   r<   ZRuntimeErrornormalize_installer)	game_slugrevisionZinstaller_urlr_   r?   Z
installersZis   &&     r   get_game_installersr   z  s    JKK 779:OO ..:
LL(-8ll=)GKKM||H9MIJJ Z
i(
,67Jq"J777s   Cc                re   )r   slugr   )r%   dictr   r   r   r   r     s      3 4 r   c                 s   \         P                  R V ,          ,           p\        P                  ! V4      p VP	                  4       pVP                  #   \        P
                   d$   p\        P                  ! RY4       / u Rp?# Rp?ii ; i)z/api/games/%szUnable to load %s: %sN)	r   r8   r	   r   r9   r   r   r)   r<   )r   r>   r_   r?   r@   s   &    r   get_game_detailsr     sk    


o4
4Cll3G;;= == >> ,d7	s   A B
'B?B
B
c                rr   )r   	installerr   r   r   r   r   r   r     s      4 4 r   c                s   a  V 3R lpV! R4       V! R4       V! R4       V! R4       V! R4       V! R4       V! R4       S P                  V4       S # )	zAdjusts an installer dict so it is in the correct form, with values
of the expected types. ``additionnal`` kwargs will be added to the result as is.c                 sD   < V S9   d   \        SV ,          4      SV &   R # R # r   r$   )Zkeyr   r   r   must_be_strZ(normalize_installer.<locals>.must_be_str  s"    ) 30IcN r   Znamerk   r   r   r   ZdlcidZrunner)Zupdate)r   Zadditionnalr   s   f, r   r   r     sV    1 	[!r   c                r   r   r   r   r   r   r   r     s      4 r   c                 s   V '       g   / # V P                  4       P                  4       R ,          p R\        P                  P	                  RV RR/4      ,          p\
        P                  ! \        P                  V,           RR/R7      p VP                  4        VP                  #   \
        P                   d$   p\        P                  ! RT4       / u R	p?# R	p?ii ; i)
:Ni   Nz/api/games?%sr   zwith-installersTr.   r/   r0   r   N)ZlowerZstripurllibparse	urlencoder	   r   r   r8   r9   r   r   r:   r<   )queryr>   r?   r@   rh   r   search_gamesr     s    	KKM!$'E
FLL22HeEVX\3]^
^C||H--3nN`=abH == >> 7<	s   
B& &C;CCCc                s   RpRp \         P                  P                  V RR7      pTP                  R8w  d   \        RT ,          4      hTP                  pT'       g   \        RT ,          4      hTP                  R4      '       d
   TR,          pTP                  R4       Uu. uF"  p\         P                  P                  T4      NK$  	  pp\        T4      ^8X  d   T^ ,          pT^,          pT^,          pMO\        T4      ^8X  d   T^ ,          pT^,          pM,\        T4      ^8X  d   T^ ,          pM\        RT ,          4      hR	T9   d   TP                  R	^R
7      w  rMRRrRpTP                  '       dD   \        \         P                  P                  TP                  4      4      pTP!                  R4      pRTRTRTRT	RT
RT/#   \         d0   p\        P
                  ! RT 4       \        RT ,          4      ThRp?ii ; iu upi )zP
Parses `lutris:` urls, extracting any info necessary to install or run a game.
Nlutris)schemezUnable to parse url %szInvalid lutris url %szlutris::i   NN/rI   )Zmaxsplitrl   r   r   actionr   appidlaunch_config_name)r   r   ZurlparseZ	Exceptionr   rW   rN   r   r    rx   rM   Zunquoter}   r   r   Z	parse_qslr9   )r>   r   r   Z
parsed_urlr@   Zurl_pathpartZ	url_partsr   r   r   r   r   s   &            r   parse_installer_urlr     s    F@\\**3x*@
 H$03677H03677 9%%B<8@s8KL8K%%d+8KIL
9~1aL	&q\	Y1	1aL		Y1	aL	03677 i"q9RHV\\++J,<,<=>99Z(YH&70 I  @/50367R?@ Ms   !G #(H
H*HHc                s&   V P                  R4      pV P                  R4      pV P                  R4      pV P                  R4      pV P                  R4      pV P                  R4      p. pV'       d   VP                  V4       MV'       g   \        R4      hV'       d   VP                  V4       MVP                  VR,           V,           4       V'       d   VP                  V4       V Uu. uF-  p\        P                  P                  \        V4      R	R
7      NK/  	  ppRP                  V4      p	V'       d,   \        P                  P                  R\        V4      /4      p
MR	p
\        P                  P                  RR	V	R	V
R34      pV# u upi )zV
Generates 'lutris:' urls, given the same dictionary that
parse_intaller_url returns.
r   r   r   r   r   r   zQA 'lutris:' URL can contain a launch configuration name only if it has an action.rI   rl   )Zsafer   r   N)
r9   ZappendrN   r   r   Zquoter%   r7   r   Z
urlunparse)Zinstaller_infor   r   r   r   r   r   Zpartsr   r    r   r>   rc   r   format_installer_urlr     sG    "";/I!!*-H)F  +Gw'E'++,@AEVlmmYWs]U*+'(@EFV\\D	3EF88E?D&&
CM'BC
,,
!
!8Rr5$"G
HCJ Gs   ,3Fr   )r   )Nr   N)MZ__doc__r<   r   r   ra   r   Zurllib.errorr   Zurllib.parseZurllib.requestZcollectionsr    r   r   r'   Ztypingr   r   r   r   rT   r   r   Zlutris.gui.widgetsr   Zlutris.utilr	   r
   Zlutris.util.graphics.gpur   Zlutris.util.httpr   r   Zlutris.util.linuxr   Zlutris.util.logr   Zlutris.util.stringsr   r    r7   Z	CACHE_DIRrL   rX   r   r#   r(   r+   rB   rG   rQ   rY   r]   r\   r^   rV   r`   rd   ri   rq   rw   rn   ru   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   r"   r   r   <module>r      s5   4  	 	      #    - -   1 $ 2 / * " (GGLL!3!3\B ggll8#5#5{C T
<&'2 ./ 02 @'-6S"+$R:	t
0 0880&3l$r   