
    `gj=                       d Z ddlmZ ddlZddlmZmZmZmZ ddl	m
Z
 ddlZddlmZmZ  G d de      Z G d	 d
e      Z G d de      Z G d d      ZddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZddZdddZdddZdddZddZy)z8Thin Spotify Web API helper used by Hermes native tools.    )annotationsN)AnyDictIterableOptional)urlparse)	AuthError#resolve_spotify_runtime_credentialsc                      e Zd ZdZy)SpotifyErrorzBase Spotify tool error.N__name__
__module____qualname____doc__     I/root/.hermes/venv/lib/python3.12/site-packages/plugins/spotify/client.pyr   r      s    "r   r   c                      e Zd ZdZy)SpotifyAuthRequiredErrorz>Raised when the user needs to authenticate with Spotify first.Nr   r   r   r   r   r      s    Hr   r   c                  :     e Zd ZdZddd	 	 	 	 	 	 	 d fdZ xZS )SpotifyAPIErrorzStructured Spotify API failure.Nstatus_coderesponse_bodyc               N    t         |   |       || _        || _        d | _        y N)super__init__r   r   path)selfmessager   r   	__class__s       r   r   zSpotifyAPIError.__init__   s)     	!&*	r   )r"   strr   Optional[int]r   Optional[str]returnNone)r   r   r   r   r   __classcell__)r#   s   @r   r   r      s>    ) &*'+

 #	

 %
 

 
r   r   c                     e Zd Zd9dZdddd:dZed;d       Zd<dZddddd		 	 	 	 	 	 	 	 	 	 	 	 	 d=d
Zd>dZ	d?dZ
ddd@dZdddAdZdddAdZdddddd	 	 	 	 	 	 	 	 	 	 	 dBdZdddCdZdddCdZdddCdZdddDdZdddEdZdddFdZdddGdZd?dZdddHdZddddd 	 	 	 	 	 	 	 	 	 	 	 	 	 dId!Zd"dd#dJd$ZdddKd%Zdddd&	 	 	 	 	 	 	 	 	 dLd'Zdd(	 	 	 	 	 	 	 dMd)Zdd*	 	 	 	 	 	 	 dNd+Zddddd,	 	 	 	 	 	 	 	 	 	 	 dOd-ZdddPd.Zd"ddd/dQd0Z d"ddd/dRd1Z!dSd2Z"dSd3Z#d"ddd/dRd4Z$dTd5Z%dUd6Z&d"ddd7	 	 	 	 	 	 	 dVd8Z'y)WSpotifyClientc                2    | j                  d      | _        y )NT)refresh_if_expiring)_resolve_runtime_runtimer!   s    r   r   zSpotifyClient.__init__*   s    --$-Gr   FTforce_refreshr-   c               j    	 t        ||      S # t        $ r}t        t        |            |d }~ww xY w)Nr1   )r
   r	   r   r$   )r!   r2   r-   excs       r   r.   zSpotifyClient._resolve_runtime-   s=    	>6+$7   	>*3s84#=	>s    	2-2c                p    t        | j                  j                  d      xs d      j                  d      S )Nbase_url /)r$   r/   getrstripr0   s    r   r6   zSpotifyClient.base_url6   s,    4==$$Z06B7>>sCCr   c                ,    d| j                   d    ddS )NzBearer access_tokenapplication/json)AuthorizationzContent-Type)r/   r0   s    r   _headerszSpotifyClient._headers:   s#    &t}}^'D&EF.
 	
r   N)params	json_bodyallow_retry_on_401empty_responsec          	     <   | j                    | }t        j                  ||| j                         t	        |      |t	        |      nd d      }|j
                  dk(  r0|r.| j                  dd      | _        | j                  ||||d      S |j
                  dk\  r| j                  |||	       |j
                  d
k(  s|j                  s|xs d|j
                  ddS d|j                  j                  dd      v r|j                         S d|j                  dS )Ng      >@)headersr@   jsontimeout  Tr1   F)r@   rA   rB   i  )methodr       )successr   emptyr=   zcontent-typer7   )rK   text)r6   httpxrequestr?   _strip_noner   r.   r/   _raise_api_errorcontentrE   r9   rF   rM   )	r!   rI   r    r@   rA   rB   rC   urlresponses	            r   rO   zSpotifyClient.request@   s'    v&==MMOv&+4+@Y'd
 3&+= 11Z^1_DM<<##(     3&!!(6!E3&h.>.>!jhFZFZei%jj!1!1!5!5nb!II==?"77r   c          	         |j                   j                         }t        |j                  t	        ||      |||j
                  j                  d            }t        ||j                  |      }||_        |)N)fallbackzRetry-After)r   detailrI   r    retry_afterr   )	rM   strip_friendly_spotify_error_messager   _extract_spotify_error_detailrE   r9   r   r    )r!   rT   rI   r    rW   r"   errors          r   rQ   zSpotifyClient._raise_api_errord   sn    $$&1 ,,0FK ((,,];
  X5I5IY_`
r   c                &    | j                  dd      S )NGETz/me/player/devicesrO   r0   s    r   get_deviceszSpotifyClient.get_devicesq   s    ||E#788r   )playc               2    | j                  dd|g|d      S )NPUT
/me/player)
device_idsra   rA   r_   )r!   	device_idra   s      r   transfer_playbackzSpotifyClient.transfer_playbackt   s&    ||E<$+<
|  	r   )marketc          	     8    | j                  ddd|idddd      S )	Nr^   rd   ri   rJ   TzNo active Spotify playback session was found. Open Spotify on a device and start playback, or transfer playback to an available device.r   rL   r"   r@   rC   r_   r!   ri   s     r   get_playback_statez SpotifyClient.get_playback_statez   s7    ||f%" e	  	
 		
r   c          	     8    | j                  ddd|idddd      S )	Nr^   z/me/player/currently-playingri   rJ   TzSSpotify is not currently playing anything. Start playback in Spotify and try again.rk   rl   r_   rm   s     r   get_currently_playingz#SpotifyClient.get_currently_playing   s4    ||*f%"p	  	
 		
r   )rg   context_uriurisoffsetposition_msc          
     :    | j                  ddd|i||||d      S )Nrc   z/me/player/playrg   )rq   rr   rs   rt   )r@   rA   r_   )r!   rg   rq   rr   rs   rt   s         r   start_playbackzSpotifyClient.start_playback   s9     ||+* *		  

 
	
r   )rg   c               .    | j                  ddd|i      S )Nrc   z/me/player/pauserg   r@   r_   r!   rg   s     r   pause_playbackzSpotifyClient.pause_playback   s    ||E#5{I>V|WWr   c               .    | j                  ddd|i      S )NPOSTz/me/player/nextrg   rx   r_   ry   s     r   	skip_nextzSpotifyClient.skip_next   s    ||F$5{I>V|WWr   c               .    | j                  ddd|i      S )Nr|   z/me/player/previousrg   rx   r_   ry   s     r   skip_previouszSpotifyClient.skip_previous   s    ||F$9;PYBZ|[[r   c               0    | j                  dd||d      S )Nrc   z/me/player/seek)rt   rg   rx   r_   )r!   rt   rg   s      r   seekzSpotifyClient.seek   s%    ||E#4&">
|  	r   c               0    | j                  dd||d      S )Nrc   z/me/player/repeatstaterg   rx   r_   r!   r   rg   s      r   
set_repeatzSpotifyClient.set_repeat   s    ||E#6]f?g|hhr   c          	     p    | j                  ddt        t        |            j                         |d      S )Nrc   z/me/player/shuffler   rx   )rO   r$   boollowerr   s      r   set_shufflezSpotifyClient.set_shuffle   s1    ||E#7#dSXkJZJ`J`Jbqz@{|||r   c               0    | j                  dd||d      S )Nrc   z/me/player/volume)volume_percentrg   rx   r_   )r!   r   rg   s      r   
set_volumezSpotifyClient.set_volume   s&    ||E#6,"@
|  	r   c                &    | j                  dd      S )Nr^   /me/player/queuer_   r0   s    r   	get_queuezSpotifyClient.get_queue   s    ||E#566r   c               0    | j                  dd||d      S )Nr|   r   )urirg   rx   r_   )r!   r   rg   s      r   add_to_queuezSpotifyClient.add_to_queue   s    ||F$6sYb?c|ddr   
   r   )limitrs   ri   include_externalc               V    | j                  dd|dj                  |      ||||d      S )Nr^   z/search,)qtyper   rs   ri   r   rx   rO   join)r!   querysearch_typesr   rs   ri   r   s          r   searchzSpotifyClient.search   s;     ||E9HH\* 06
|  	r      r   rs   c               0    | j                  dd||d      S )Nr^   /me/playlistsr   rx   r_   )r!   r   rs   s      r   get_my_playlistszSpotifyClient.get_my_playlists   s    ||E?UV\;]|^^r   c               4    | j                  dd| d|i      S )Nr^   /playlists/ri   rx   r_   )r!   playlist_idri   s      r   get_playlistzSpotifyClient.get_playlist   s$    ||E[#>RXGY|ZZr   )publiccollaborativedescriptionc          	     4    | j                  dd||||d      S )Nr|   r   namer   r   r   rf   r_   )r!   r   r   r   r   s        r   create_playlistzSpotifyClient.create_playlist   s-     ||FO*&	@
|  	r   )positionc               8    | j                  dd| d||d      S )Nr|   r   /items)rr   r   rf   r_   )r!   r   rr   r   s       r   add_playlist_itemsz SpotifyClient.add_playlist_items   s0     ||Fk+f$E R
|  	r   )snapshot_idc          	     b    | j                  dd| d|D cg c]  }d|i c}|d      S c c}w )NDELETEr   r   r   )itemsr   rf   r_   )r!   r   rr   r   r   s        r   remove_playlist_itemsz#SpotifyClient.remove_playlist_items   sB     ||HK=&G.23sucl3&T
|  	3s   ,r   c          	     :    | j                  dd| ||||d      S )Nrc   r   r   rf   r_   )r!   r   r   r   r   r   s         r   update_playlist_detailsz%SpotifyClient.update_playlist_details  s4     ||E[#>*&	K
|  	r   c               4    | j                  dd| d|i      S )Nr^   /albums/ri   rx   r_   )r!   album_idri   s      r   	get_albumzSpotifyClient.get_album  s#    ||EXhZ#8(FAS|TTr   r   rs   ri   c               :    | j                  dd| d|||d      S )Nr^   r   z/tracksr   rx   r_   )r!   r   r   rs   ri   s        r   get_album_trackszSpotifyClient.get_album_tracks  s1    ||EXhZw#?I
|  	r   c               2    | j                  dd|||d      S )Nr^   z
/me/tracksr   rx   r_   r!   r   rs   ri   s       r   get_saved_trackszSpotifyClient.get_saved_tracks"       ||E<%SYek8l|mmr   c               L    | j                  ddddj                  |      i      S )Nrc   /me/libraryrr   r   rx   r   r!   rr   s     r   save_library_itemsz SpotifyClient.save_library_items%  s$    ||E=&#((4.9Q|RRr   c               L    | j                  ddddj                  |      i      S )Nr^   z/me/library/containsrr   r   rx   r   r   s     r   library_containszSpotifyClient.library_contains(  s&    ||E#96388TX>BZ|[[r   c               2    | j                  dd|||d      S )Nr^   z
/me/albumsr   rx   r_   r   s       r   get_saved_albumszSpotifyClient.get_saved_albums+  r   r   c               z    |D cg c]  }d| 	 }}| j                  ddddj                  |      i      S c c}w )Nzspotify:track:r   r   rr   r   rx   r   )r!   	track_idstrack_idrr   s       r   remove_saved_tracksz!SpotifyClient.remove_saved_tracks.  D    <EF.
+FF||HmVSXXd^<T|UU G   8c               z    |D cg c]  }d| 	 }}| j                  ddddj                  |      i      S c c}w )Nzspotify:album:r   r   rr   r   rx   r   )r!   	album_idsr   rr   s       r   remove_saved_albumsz!SpotifyClient.remove_saved_albums2  r   r   r   afterbeforec               2    | j                  dd|||d      S )Nr^   z/me/player/recently-playedr   rx   r_   )r!   r   r   r   s       r   get_recently_playedz!SpotifyClient.get_recently_played6  s+     ||E#?I
|  	r   )r'   r(   )r2   r   r-   r   r'   Dict[str, Any])r'   r$   )r'   zDict[str, str])rI   r$   r    r$   r@   Optional[Dict[str, Any]]rA   r   rB   r   rC   r   r'   r   )rT   httpx.ResponserI   r$   r    r$   r'   r(   )r'   r   )rg   r$   ra   r   r'   r   )ri   r&   r'   r   )rg   r&   rq   r&   rr   zOptional[list[str]]rs   r   rt   r%   r'   r   )rg   r&   r'   r   )rt   intrg   r&   r'   r   )r   r$   rg   r&   r'   r   )r   r   rg   r&   r'   r   )r   r   rg   r&   r'   r   )r   r$   rg   r&   r'   r   )r   r$   r   	list[str]r   r   rs   r   ri   r&   r   r&   r'   r   )r   r   rs   r   r'   r   )r   r$   ri   r&   r'   r   )
r   r$   r   r   r   r   r   r&   r'   r   )r   r$   rr   r   r   r%   r'   r   )r   r$   rr   r   r   r&   r'   r   )r   r$   r   r&   r   Optional[bool]r   r   r   r&   r'   r   )r   r$   ri   r&   r'   r   )
r   r$   r   r   rs   r   ri   r&   r'   r   )r   r   rs   r   ri   r&   r'   r   )rr   r   r'   r   )r   r   r'   r   )r   r   r'   r   )r   r   r   r%   r   r%   r'   r   )(r   r   r   r   r.   propertyr6   r?   rO   rQ   r`   rh   rn   rp   rv   rz   r}   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r+   r+   )   sc   H 9>[_ > D D
 ,0.2#'37"8"8 "8
 )"8 ,"8 !"8 1"8 
"8H9 AF  =A 

 @D 

 $(%)$(+/%)
 !
 #	

 "
 )
 #
 

* <@ X 7; X ;? \ DH  DH i FJ } MQ 7 DH e  $*.   	
    ( 
& 02 _ IM [ #%)  	
  # 
( #'
 
 	

  
 

" &*
 
 	

 #
 

  #!%(,%)  	
  & # 
  CG U ?APQko  02\` nS\ 02\` nVV # $  	
  
r   r+   c               H   |}	 | j                         }t        |t              rR|j                  d      }t        |t              rt	        |j                  d      xs |      }nt        |t              r|}|j                         S # t
        $ r Y |j                         S w xY w)Nr\   r"   )rF   
isinstancedictr9   r$   	ExceptionrY   )rT   rV   rW   payload	error_objs        r   r[   r[   D  s    F	--/gt$G,I)T*Y]]95?@Is+" <<>  <<>s   A2B 	B! B!c                    |j                         }|j                  d      }| dk(  ry| dk(  r|r	 yd|v sd|v ryy	| d
k(  r|ryy| dk(  rd}|r	|d| dz  }|S |r|S d|  dS )Nrd   rH   zJSpotify authentication failed or expired. Run `hermes auth spotify` again.i  zSpotify rejected this playback request. Playback control usually requires a Spotify Premium account and an active Spotify Connect device.scope
permissionzSpotify rejected the request because the current auth scope is insufficient. Re-run `hermes auth spotify` to refresh permissions.zRSpotify rejected the request. The account may not have permission for this action.i  zTSpotify could not find an active playback device or player session for this request.zSpotify resource not found.i  zSpotify rate limit exceeded.z Retry after z	 seconds.z'Spotify API request failed with status .)r   
startswith)r   rW   rI   r    rX   normalized_detailis_playback_pathr"   s           r   rZ   rZ   S  s     |4c[c8 ''<;L+L Wcci,c0{m9==G4[MCCr   c                d    | si S | j                         D ci c]  \  }}|	|| c}}S c c}}w r   )r   )r   keyvalues      r   rP   rP   {  s1    	)0N:3E<MCJNNNs   
,,c                   | xs dj                         }|st        d      |j                  d      rB|j                  d      }t	        |      dk\  r#|d   }|r||k7  rt        d| d| d	      |d
   S d|v rit        |      }|j                  j                  d      D cg c]  }|s|	 }}t	        |      d
k\  r%|d   |d   }}|r||k7  rt        d| d| d	      |S |S c c}w )Nr7   zSpotify id/uri/url is required.spotify::      Expected a Spotify , got r      zopen.spotify.comr8   r   )rY   r   r   splitlenr   r    )	r   expected_typecleanedparts	item_typeparsedpart
path_partsitem_ids	            r   normalize_spotify_idr    s   {!!#G<==*%c"u:?aIm!;"%8vi[XY#Z[[8OW$'"'-{{'8'8'=FtdF
Fz?a!+A
1wIm!;"%8vi[XY#Z[[NN Gs   !C$)C$c                   | xs dj                         }|st        d      |j                  d      r@|r<|j                  d      }t	        |      dk\  r|d   |k7  rt        d| d|d    d	      |S t        ||      }|rd| d| S |S )
Nr7   zSpotify URI/url/id is required.r   r   r   r   r   r   r   )rY   r   r   r   r   r  )r   r   r   r   r  s        r   normalize_spotify_urir    s    {!!#G<==*%MM#&E5zQ58}#<"%8veTUhZWX#YZZ"7M:G-'33Nr   c                    g }| D ]-  }t        t        |      |      }||vs|j                  |       / |st        d      |S )Nz&At least one Spotify item is required.)r  r$   appendr   )valuesr   rr   r   r   s        r   normalize_spotify_urisr    sO    D #CJ>d?KK CDDKr   c                0    t        j                  | d      S )NF)ensure_ascii)rF   dumps)datas    r   compact_jsonr    s    ::d//r   )rT   r   rV   r$   r'   r$   )r   r   rW   r$   rI   r$   r    r$   rX   r&   r'   r$   )r   r   r'   r   r   )r   r$   r   r&   r'   r$   )r
  zIterable[str]r   r&   r'   r   )r  r   r'   r$   )r   
__future__r   rF   typingr   r   r   r   urllib.parser   rN   hermes_cli.authr	   r
   RuntimeErrorr   r   r   r+   r[   rZ   rP   r  r  r  r  r   r   r   <module>r     s    > "  0 0 ! #< #I| Il  X Xv%D%D %D 	%D
 %D %D 	%DPO, 0r   