
    i=                       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/d0d#Zd/d0d$Zd/d1d(Zd2d+ZdS )3z8Thin 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dS )SpotifyErrorzBase Spotify tool error.N__name__
__module____qualname____doc__     ;/home/ubuntu/.hermes/hermes-agent/plugins/spotify/client.pyr   r      s        """"r   r   c                      e Zd ZdZdS )SpotifyAuthRequiredErrorz>Raised when the user needs to authenticate with Spotify first.Nr   r   r   r   r   r      s        HHH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_bodymessagestrr   Optional[int]r   Optional[str]returnNonec               t    t                                          |           || _        || _        d | _        d S N)super__init__r   r   path)selfr   r   r   	__class__s       r   r%   zSpotifyAPIError.__init__   s8     	!!!&*			r   )r   r   r   r   r   r   r    r!   )r   r   r   r   r%   __classcell__)r(   s   @r   r   r      sU        )) &*'+
 
 
 
 
 
 
 
 
 
 
 
r   r   c                     e Zd ZdkdZddddldZedmd            ZdndZddddddodZdpdZ	dqdZ
dddrd"Zdd#dsd&Zdd#dsd'Zdddddd(dtd/Zdd0dud1Zdd0dud2Zdd0dud3Zdd0dvd5Zdd0dwd7Zdd0dxd8Zdd0dyd:Zdqd;Zdd0dzd=Zd>d?ddd@d{dFZdGd?dHd|dIZdd#d}dKZddddLd~dQZddRddTZddUddWZdddddXddZZdd#dd\ZdGd?dd]dd^Z dGd?dd]dd_Z!dd`Z"ddaZ#dGd?dd]ddbZ$dddZ%ddfZ&dGdddgddjZ'dS )SpotifyClientr    r!   c                <    |                      d          | _        d S )NT)refresh_if_expiring)_resolve_runtime_runtimer'   s    r   r%   zSpotifyClient.__init__*   s    --$-GGr   FTforce_refreshr-   r2   boolr-   Dict[str, Any]c                   	 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     	> 	> 	>*3s8844#=	>s    
?:?r   c                z    t          | j                            d          pd                              d          S )Nbase_url /)r   r/   getrstripr0   s    r   r8   zSpotifyClient.base_url6   s3    4=$$Z006B77>>sCCCr   Dict[str, str]c                (    d| j         d          ddS )NzBearer access_tokenapplication/json)AuthorizationzContent-Type)r/   r0   s    r   _headerszSpotifyClient._headers:   s&    Ft}^'DFF.
 
 	
r   N)params	json_bodyallow_retry_on_401empty_responsemethodr&   rC   Optional[Dict[str, Any]]rD   rE   rF   r   c          	     ,   | j          | }t          j        |||                                 t	          |          |t	          |          nd d          }|j        dk    r8|r6|                     dd          | _        |                     ||||d          S |j        dk    r|                     |||	           |j        d
k    s|j	        s|p
d|j        ddS d|j
                            dd          v r|                                S d|j        dS )Ng      >@)headersrC   jsontimeout  Tr1   F)rC   rD   rE   i  )rG   r&      )successr   emptyr@   zcontent-typer9   )rO   text)r8   httpxrequestrB   _strip_noner   r.   r/   _raise_api_errorcontentrJ   r;   rK   rQ   )	r'   rG   r&   rC   rD   rE   rF   urlresponses	            r   rS   zSpotifyClient.request@   sQ    &&&=MMOOv&&+4+@Y'''d
 
 
 3&&+=& 11Z^1__DM<<##(      3&&!!(6!EEE3&&h.>&!jhFZei%j%jj!1!5!5nb!I!III==??"777r   rX   httpx.Responsec          	         |j                                         }t          |j        t	          ||          |||j                            d                    }t          ||j        |          }||_        |)N)fallbackzRetry-After)r   detailrG   r&   retry_afterr   )	rQ   strip_friendly_spotify_error_messager   _extract_spotify_error_detailrJ   r;   r   r&   )r'   rX   rG   r&   r\   r   errors          r   rU   zSpotifyClient._raise_api_errord   s    $$&&1 ,0FKKK (,,];;
 
 
  X5IY_```
r   c                .    |                      dd          S )NGETz/me/player/devicesrS   r0   s    r   get_deviceszSpotifyClient.get_devicesq   s    ||E#7888r   )play	device_idrf   c               :    |                      dd|g|d          S )NPUT
/me/player)
device_idsrf   rD   rd   )r'   rg   rf   s      r   transfer_playbackzSpotifyClient.transfer_playbackt   s0    ||E<$+<
 <
|   	r   )marketrn   r   c          	     @    |                      ddd|idddd          S )	Nrc   rj   rn   rN   TzNo active Spotify playback session was found. Open Spotify on a device and start playback, or transfer playback to an available device.r   rP   r   rC   rF   rd   r'   rn   s     r   get_playback_statez SpotifyClient.get_playback_statez   sA    ||f%" e 	  	
 	
 		
r   c          	     @    |                      ddd|idddd          S )	Nrc   z/me/player/currently-playingrn   rN   TzSSpotify is not currently playing anything. Start playback in Spotify and try again.rp   rq   rd   rr   s     r   get_currently_playingz#SpotifyClient.get_currently_playing   s>    ||*f%"p 	  	
 	
 		
r   )rg   context_uriurisoffsetposition_msrv   rw   Optional[list[str]]rx   ry   r   c          
     B    |                      ddd|i||||d          S )Nri   z/me/player/playrg   )rv   rw   rx   ry   )rC   rD   rd   )r'   rg   rv   rw   rx   ry   s         r   start_playbackzSpotifyClient.start_playback   sC     ||+* *	 	  

 

 
	
r   )rg   c               6    |                      ddd|i          S )Nri   z/me/player/pauserg   rC   rd   r'   rg   s     r   pause_playbackzSpotifyClient.pause_playback   s     ||E#5{I>V|WWWr   c               6    |                      ddd|i          S )NPOSTz/me/player/nextrg   r~   rd   r   s     r   	skip_nextzSpotifyClient.skip_next   s     ||F$5{I>V|WWWr   c               6    |                      ddd|i          S )Nr   z/me/player/previousrg   r~   rd   r   s     r   skip_previouszSpotifyClient.skip_previous   s!    ||F$9;PYBZ|[[[r   intc               8    |                      dd||d          S )Nri   z/me/player/seek)ry   rg   r~   rd   )r'   ry   rg   s      r   seekzSpotifyClient.seek   s/    ||E#4&">
 >
|   	r   statec               8    |                      dd||d          S )Nri   z/me/player/repeatr   rg   r~   rd   r'   r   rg   s      r   
set_repeatzSpotifyClient.set_repeat   s$    ||E#6]f?g?g|hhhr   c          	         |                      ddt          t          |                                                    |d          S )Nri   z/me/player/shuffler   r~   )rS   r   r3   lowerr   s      r   set_shufflezSpotifyClient.set_shuffle   s?    ||E#7#dSXkkJZJZJ`J`JbJbqz@{@{||||r   volume_percentc               8    |                      dd||d          S )Nri   z/me/player/volume)r   rg   r~   rd   )r'   r   rg   s      r   
set_volumezSpotifyClient.set_volume   s1    ||E#6,"@
 @
|   	r   c                .    |                      dd          S )Nrc   /me/player/queuerd   r0   s    r   	get_queuezSpotifyClient.get_queue   s    ||E#5666r   uric               8    |                      dd||d          S )Nr   r   )r   rg   r~   rd   )r'   r   rg   s      r   add_to_queuezSpotifyClient.add_to_queue   s$    ||F$6sYb?c?c|dddr   
   r   )limitrx   rn   include_externalquerysearch_types	list[str]r   r   c               f    |                      dd|d                    |          ||||d          S )Nrc   z/search,)qtyper   rx   rn   r   r~   rS   join)r'   r   r   r   rx   rn   r   s          r   searchzSpotifyClient.search   sH     ||E9HH\** 06
 6
|   	r      r   rx   c               8    |                      dd||d          S )Nrc   /me/playlistsr   r~   rd   )r'   r   rx   s      r   get_my_playlistszSpotifyClient.get_my_playlists   s#    ||E?UV\;];]|^^^r   playlist_idc               <    |                      dd| d|i          S )Nrc   /playlists/rn   r~   rd   )r'   r   rn   s      r   get_playlistzSpotifyClient.get_playlist   s)    ||E#>#>#>RXGY|ZZZr   )publiccollaborativedescriptionnamer   r   r   c          	     <    |                      dd||||d          S )Nr   r   r   r   r   r   rl   rd   )r'   r   r   r   r   s        r   create_playlistzSpotifyClient.create_playlist   s8     ||FO*&	@
 @
|   	r   )positionr   c               @    |                      dd| d||d          S )Nr   r   /items)rw   r   rl   rd   )r'   r   rw   r   s       r   add_playlist_itemsz SpotifyClient.add_playlist_items   s>     ||F$E+$E$E$E R
 R
|   	r   )snapshot_idr   c               T    |                      dd| dd |D             |d          S )NDELETEr   r   c                    g | ]}d |iS )r   r   ).0r   s     r   
<listcomp>z7SpotifyClient.remove_playlist_items.<locals>.<listcomp>  s    333sucl333r   )itemsr   rl   rd   )r'   r   rw   r   s       r   remove_playlist_itemsz#SpotifyClient.remove_playlist_items   sL     ||H&GK&G&G&G33d333&T
 T
|   	r   r   Optional[bool]c          	     B    |                      dd| ||||d          S )Nri   r   r   rl   rd   )r'   r   r   r   r   r   s         r   update_playlist_detailsz%SpotifyClient.update_playlist_details  sA     ||E#>#>#>*&	K
 K
|   	r   album_idc               <    |                      dd| d|i          S )Nrc   /albums/rn   r~   rd   )r'   r   rn   s      r   	get_albumzSpotifyClient.get_album  s(    ||E#8h#8#8(FAS|TTTr   r   rx   rn   c               B    |                      dd| d|||d          S )Nrc   r   z/tracksr   r~   rd   )r'   r   r   rx   rn   s        r   get_album_trackszSpotifyClient.get_album_tracks  s?    ||E#?h#?#?#?I
 I
|   	r   c               :    |                      dd|||d          S )Nrc   z
/me/tracksr   r~   rd   r'   r   rx   rn   s       r   get_saved_trackszSpotifyClient.get_saved_tracks"  &    ||E<%SYek8l8l|mmmr   c               \    |                      dddd                    |          i          S )Nri   /me/libraryrw   r   r~   r   r'   rw   s     r   save_library_itemsz SpotifyClient.save_library_items%  s)    ||E=&#((4..9Q|RRRr   c               \    |                      dddd                    |          i          S )Nrc   z/me/library/containsrw   r   r~   r   r   s     r   library_containszSpotifyClient.library_contains(  s+    ||E#96388TX>>BZ|[[[r   c               :    |                      dd|||d          S )Nrc   z
/me/albumsr   r~   rd   r   s       r   get_saved_albumszSpotifyClient.get_saved_albums+  r   r   	track_idsc               t    d |D             }|                      dddd                    |          i          S )Nc                    g | ]}d | S )zspotify:track:r   )r   track_ids     r   r   z5SpotifyClient.remove_saved_tracks.<locals>.<listcomp>/  !    FFF+++FFFr   r   r   rw   r   r~   r   )r'   r   rw   s      r   remove_saved_tracksz!SpotifyClient.remove_saved_tracks.  <    FFIFFF||HmVSXXd^^<T|UUUr   	album_idsc               t    d |D             }|                      dddd                    |          i          S )Nc                    g | ]}d | S )zspotify:album:r   )r   r   s     r   r   z5SpotifyClient.remove_saved_albums.<locals>.<listcomp>3  r   r   r   r   rw   r   r~   r   )r'   r   rw   s      r   remove_saved_albumsz!SpotifyClient.remove_saved_albums2  r   r   r   afterbeforer   r   c               :    |                      dd|||d          S )Nrc   z/me/player/recently-playedr   r~   rd   )r'   r   r   r   s       r   get_recently_playedz!SpotifyClient.get_recently_played6  s6     ||E#?I
 I
|   	r   )r    r!   )r2   r3   r-   r3   r    r4   )r    r   )r    r=   )rG   r   r&   r   rC   rH   rD   rH   rE   r3   rF   rH   r    r   )rX   rY   rG   r   r&   r   r    r!   )r    r   )rg   r   rf   r3   r    r   )rn   r   r    r   )rg   r   rv   r   rw   rz   rx   rH   ry   r   r    r   )rg   r   r    r   )ry   r   rg   r   r    r   )r   r   rg   r   r    r   )r   r3   rg   r   r    r   )r   r   rg   r   r    r   )r   r   rg   r   r    r   )r   r   r   r   r   r   rx   r   rn   r   r   r   r    r   )r   r   rx   r   r    r   )r   r   rn   r   r    r   )
r   r   r   r3   r   r3   r   r   r    r   )r   r   rw   r   r   r   r    r   )r   r   rw   r   r   r   r    r   )r   r   r   r   r   r   r   r   r   r   r    r   )r   r   rn   r   r    r   )
r   r   r   r   rx   r   rn   r   r    r   )r   r   rx   r   rn   r   r    r   )rw   r   r    r   )r   r   r    r   )r   r   r    r   )r   r   r   r   r   r   r    r   )(r   r   r   r%   r.   propertyr8   rB   rS   rU   re   rm   rs   ru   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   r+   r+   )   s.       H H H H 9>[_ > > > > > > D D D XD
 
 
 
 ,0.2#'37"8 "8 "8 "8 "8 "8H   9 9 9 9 AF       =A 

 

 

 

 

 

 @D 

 

 

 

 

 

 $(%)$(+/%)
 
 
 
 
 
* <@ X X X X X X 7; X X X X X X ;? \ \ \ \ \ \ DH       DH i i i i i i FJ } } } } } } MQ      7 7 7 7 DH e e e e e e  $*.     & 02 _ _ _ _ _ _ IM [ [ [ [ [ [ #%)     ( #'
 
 
 
 
 
" &*
 
 
 
 
 
  #!%(,%)       CG U U U U U U ?APQko       02\` n n n n n nS S S S\ \ \ \ 02\` n n n n n nV V V VV V V V # $       r   r+   rX   rY   r[   r   r    c               p   |}	 |                                  }t          |t                    rf|                    d          }t          |t                    r%t	          |                    d          p|          }nt          |t                    r|}n# t
          $ r Y nw xY w|                                S )Nra   r   )rK   
isinstancedictr;   r   	Exceptionr^   )rX   r[   r\   payload	error_objs        r   r`   r`   D  s    F	--//gt$$ 	#G,,I)T** #Y]]955?@@Is++ #"   <<>>s   BB 
B! B!r   r   r\   rG   r&   r]   r   c                    |                                 }|                    d          }| dk    rdS | dk    r|r	 dS d|v sd|v rdS d	S | d
k    r|rdS dS | dk    rd}|r	|d| dz  }|S |r|S d|  dS )Nrj   rM   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   r\   rG   r&   r]   normalized_detailis_playback_pathr   s           r   r_   r_   S  s     |44c[[c 	8  '''<;L+L+L W  Wccc 	jii,,c0 	>={====G C[CCCCr   r   rH   r4   c                F    | si S d |                                  D             S )Nc                    i | ]
\  }}|||S r#   r   )r   keyvalues      r   
<dictcomp>z_strip_none.<locals>.<dictcomp>~  s#    NNN:3E<MC<M<M<Mr   )r   )r   s    r   rT   rT   {  s+     	NNNNNNr   r   expected_typec                   | pd                                 }|st          d          |                    d          rV|                    d          }t	          |          dk    r.|d         }|r||k    rt          d| d| d	          |d
         S d|v rvt          |          }d |j                            d          D             }t	          |          d
k    r0|d         |d         }}|r||k    rt          d| d| d	          |S |S )Nr9   zSpotify id/uri/url is required.spotify::      Expected a Spotify , got r      zopen.spotify.comc                    g | ]}||S r   r   )r   parts     r   r   z(normalize_spotify_id.<locals>.<listcomp>  s    FFFtFdFFFr   r:   r   )r^   r   r   splitlenr   r&   )r   r   cleanedparts	item_typeparsed
path_partsitem_ids           r   normalize_spotify_idr    sG   {!!##G ><===*%% c""u::??aI \m!;!;"#Z#Z#Zi#Z#Z#Z[[[8OW$$'""FFv{'8'8'='=FFF
z??a!+A
1wI \m!;!;"#Z#Z#Zi#Z#Z#Z[[[NNr   c                Z   | pd                                 }|st          d          |                    d          rT|rP|                    d          }t	          |          dk    r(|d         |k    rt          d| d|d          d	          |S t          ||          }|rd| d| S |S )
Nr9   zSpotify URI/url/id is required.r   r   r   r  r  r  r   )r^   r   r   r  r  r  )r   r   r	  r
  r  s        r   normalize_spotify_urir    s    {!!##G ><===*%%  	[MM#&&E5zzQ58}#<#<"#Y#Y#YeTUh#Y#Y#YZZZ"7M::G 43-33'333Nr   valuesIterable[str]r   c                    g }| D ]8}t          t          |          |          }||vr|                    |           9|st          d          |S )Nz&At least one Spotify item is required.)r  r   appendr   )r  r   rw   r   r   s        r   normalize_spotify_urisr    sd    D  #CJJ>>d??KK ECDDDKr   datar   c                .    t          j        | d          S )NF)ensure_ascii)rK   dumps)r  s    r   compact_jsonr    s    :d////r   )rX   rY   r[   r   r    r   )r   r   r\   r   rG   r   r&   r   r]   r   r    r   )r   rH   r    r4   r#   )r   r   r   r   r    r   )r  r  r   r   r    r   )r  r   r    r   )r   
__future__r   rK   typingr   r   r   r   urllib.parser   rR   hermes_cli.authr	   r
   RuntimeErrorr   r   r   r+   r`   r_   rT   r  r  r  r  r   r   r   <module>r!     s   > > " " " " " "  0 0 0 0 0 0 0 0 0 0 0 0 ! ! ! ! ! !        # # # # #< # # #I I I I I| I I I    l    X X X X X X X Xv   %D %D %D %DPO O O O    ,         0 0 0 0 0 0r   