
    ikC                         S r SSKrSSKJr  \R                  " S5      rSqSqS rS rS r	SS jr
SS	 jrSS
 jrS rS rS rS rS rS rSS jrSS jrS rg)u  
Spotify Module for Luke.

Gives Luke control over Bill's Spotify — now playing, recent tracks,
top artists, playback control, and playlist management.

WhatsApp commands:
  "what's playing" / "now playing" → current track
  "recent" / "recently played" → last 5 tracks
  "play [song/artist]" → search and play
  "pause" / "stop" → pause playback
  "skip" / "next" → skip to next track
  "queue [song]" → add to queue
  "top artists" / "top tracks" → listening stats
  "playlist [name]" → play a playlist by name
    N)datetimespotifyc                  8   [         cX   SSKJn   U " 5       q[        SLq [         (       a  [        R                  S5        [        $ [        R                  S5         [        $ [        $ ! [         a)  n[        R                  SU 35        Sq  SnA[        $ SnAff = f)z%Get the Spotify client (lazy-loaded).Nr   )get_spotify_clientzSpotify module: ACTIVEz4Spotify module: INACTIVE (run setup_spotify_auth.py)zSpotify init failed: F)
_availablespotify_authr   _clientloggerinfo	Exceptionerror)r   es     5/Users/bsyrros/clawd/whatsapp-agent/spotify_module.py_get_clientr      s     
	7(*G ,Jz45 N	 RS N7N  	LL045JN	s   5A& A& &
B0BBc                  "    [        5         [        $ )zCheck if Spotify is connected.)r   r        r   is_availabler   .   s    Mr   c                  N   [        5       n U (       d  g U R                  5       nU(       a  UR                  S5      (       d  gUR                  S0 5      nUR                  SS5      nSR                  S UR                  S	/ 5       5       5      nUR                  S
0 5      R                  SS5      nUR                  SS5      nUR                  SS5      nUS-   SUS-  S-  S 3nUS-   SUS-  S-  S 3n	UR                  S0 5      R                  SS5      n
SU SU 3/nU(       a  UR	                  SU 35        UR	                  U SU	 SU
 35        SR                  U5      $ ! [
         a4  n[        R                  SU 35        S[        U5      SS  S3s SnA$ SnAff = f) z Get the currently playing track.9   Spotify not connected. Run setup_spotify_auth.py — Luke
is_playingu#   Nothing playing right now. — LukeitemnameUnknown, c              3   *   #    U  H	  oS    v   M     g7fr   Nr   .0as     r   	<genexpr>now_playing.<locals>.<genexpr>A   s     G/F!fI/F   artistsalbum progress_msr   duration_msi`  :i  02ddevicezUnknown deviceu   NOW PLAYING — Lukezby zAlbum: z / z on 
zNow playing error: zCouldn't get playback info: NP   	    — Luke)	r   current_playbackgetjoinappendr   r
   r   str)spcurrentr   trackr$   r%   r'   r(   progressdurationr+   linesr   s                r   now_playingr:   4   s   	BJE%%'gkk,778{{62&+))Gtxx	2/FGG"%))&"5kk-3hh}a0!U*+1kE.Ad-J3,OP!U*+1kE.Ad-J3,OPXr*..v7GH #g'O

 LL75'*+zXJd6(;<yy E*1#./-c!fSbk])DDEs$   -E& D#E& &
F$0)FF$F$c                    [        5       nU(       d  g UR                  U S9nUR                  S/ 5      nU(       d  gSS/nU H  nUS   nUR                  SS	5      nS
R                  S UR                  S/ 5       5       5      nUR                  SS5      n	 [        R
                  " U	R                  SS5      5      n
U
R                  S5      nUR                  SU SU 35        U(       d  M  UR                  SU 35        M     SR                  U5      $ ! [        [        4 a    Sn N]f = f! [         a4  n[        R                  SU 35        S[        U5      SS  S3s SnA$ SnAff = f)zGet recently played tracks.r   limititemsu%   No recent listening history. — Lukeu   RECENTLY PLAYED — Luker&   r6   r   r   r   c              3   *   #    U  H	  oS    v   M     g7fr   r   r   s     r   r!   "recently_played.<locals>.<genexpr>l        L3Ka&	3Kr#   r$   	played_atZz+00:00z	%-I:%M %p      —     r,   zRecently played error: zCouldn't fetch recent tracks: Nr-   r.   )r   current_user_recently_playedr0   r1   r   fromisoformatreplacestrftime
ValueErrorAttributeErrorr2   r   r
   r   r3   )r=   r4   recentr>   r9   r   r6   r   r$   rB   dttime_strr   s                r   recently_playedrP   [   si   	BJG00u0=

7B':+R0DME99VY/DiiL599Y3KLLGb1I++I,=,=c8,LM;;{3 LL2dV5	23xtH:./   yy /   G.qc23/As}IFFGsM   (D0 AD0 7DD0 /(D0 D-*D0 ,D--D0 0
E.:)E)#E.)E.c                 V   [        5       nU(       d  g UR                  XS9nUR                  S/ 5      nU(       d  gSSSS.R                  X 5      nS	U S
3S/n[        US5       Ho  u  pxUR                  SS5      n	SR	                  UR                  S/ 5      SS 5      n
UR                  SU SU	 35        U
(       d  M[  UR                  SU
 35        Mq     SR	                  U5      $ ! [         a4  n[        R                  SU 35        S[        U5      SS  S3s SnA$ SnAff = f)zh
Get Bill's top artists.
time_range: short_term (4 weeks), medium_term (6 months), long_term (all time)
r   r=   
time_ranger>   '   Not enough listening data yet. — LukeLast 4 WeeksLast 6 MonthsAll Time
short_termmedium_term	long_termzTOP ARTISTS (
   ) — Luker&      r   r   r   genresN   rD   .      r,   zTop artists error: zCouldn't fetch top artists: r-   r.   )
r   current_user_top_artistsr0   	enumerater1   r2   r   r
   r   r3   )rS   r=   r4   resultr>   range_labelr9   iartistr   r^   r   s               r   top_artistsrh      s:   
 
BJE,,5,P

7B'< )*#
 #j
%	 	 !Z8"="5!,IA::fi0DYYvzz(B7;<FLL2aS4&)*vuVH-. - yy E*1#./-c!fSbk])DDEs)   (C* B C* (C* *
D(4)D#D(#D(c           	      *   [        5       nU(       d  g UR                  XS9nUR                  S/ 5      nU(       d  gSSSS.R                  X 5      nS	U S
3S/n[        US5       HY  u  pxUR                  SS5      n	SR	                  S UR                  S/ 5       5       5      n
UR                  SU SU	 SU
 35        M[     SR	                  U5      $ ! [         a4  n[        R                  SU 35        S[        U5      SS  S3s SnA$ SnAff = f)zGet Bill's top tracks.r   rR   r>   rT   rU   rV   rW   rX   zTOP TRACKS (r\   r&   r]   r   r   r   c              3   *   #    U  H	  oS    v   M     g7fr   r   r   s     r   r!   top_tracks.<locals>.<genexpr>   rA   r#   r$   rD   r`   rE   r,   zTop tracks error: zCouldn't fetch top tracks: Nr-   r.   )
r   current_user_top_tracksr0   rc   r1   r2   r   r
   r   r3   )rS   r=   r4   rd   r>   re   r9   rf   r6   r   r$   r   s               r   
top_tracksrm      s%   	BJD++%+O

7B'< )*#
 #j
%	 	  }J7<!%+HA99VY/DiiL599Y3KLLGLL2aS4&gY78 ,
 yy D)!-.,SVCR[MCCDs#   (C BC 
D)DDDc                    [        5       nU(       d  g UR                  5       nUR                  S/ 5       Vs/ s H  o3R                  S5      (       d  M  UPM     nnUR                  S/ 5      nU(       d  U(       d  gU(       a  US   S   OUS   S   nUR                  U SSS	9nUR                  S
0 5      R                  S/ 5      nUR                  S0 5      R                  S/ 5      n	UR                  S0 5      R                  S/ 5      n
U HN  nU R	                  5       US   R	                  5       ;   d  M*  UR                  XkS   S9  SUS    SUS   S    S3s  $    U	(       aN  U	S   nUS   nSR                  S UR                  S/ 5       5       5      nUR                  XlS   /S9  SU SU S3$ U
(       a   U
S   nUR                  XoS   S9  SUS    S3$ SU  S3$ s  snf ! [         aP  n[        U5      nSU;   d  SU;   a   S nAg![        R                  S"U 35        S#[        U5      S S$  S3s S nA$ S nAff = f)%z7Search for a song/artist/playlist and start playing it.r   devices	is_activeuP   No Spotify devices found. Open Spotify on your phone or computer first. — Luker   idzplaylist,track,artist   qtyper=   	playlistsr>   tracksr$   r   uri)	device_idcontext_urizPlaying playlist:  (total    tracks) — Luker   c              3   *   #    U  H	  oS    v   M     g7fr   r   r   s     r   r!   play_search.<locals>.<genexpr>   s     #P7O!fI7Or#   )ry   uris	Playing:  by r.   u    (artist radio) — LukezCouldn't find anything for 'u$   '. Try being more specific. — LukeNO_ACTIVE_DEVICE404Nu@   No active Spotify device. Open Spotify somewhere first. — LukezPlay search error: zPlayback error: r-   )r   ro   r0   searchlowerstart_playbackr1   r   r3   r
   r   )queryr4   ro   dactive
any_devicery   resultsrv   rw   r$   plr6   r   artist_namerg   r   	error_msgs                     r   play_searchr      sU   	BJ,9**,$[[B7N755;M!7N[[B/
je'-F1IdO:a=3F	 ))e*A)K KKR044WbA	Xr*..w;++i,00"= B{{}6
 0 0 22!!Ie9!M+BvJ<r"X,w:O9PPabb 
 1IE=D))#PuyyB7O#PPK	uGtfDY??QZF	e}Mvf~..FGG-eW4XYYE OH  9F	*ey.@U*1#./!#a&"+i889sZ   $G' G"G""G' >B6G' 8%G' AG' 5&G' G' "G' '
I1H<)H<6I<Ic                      [        5       n U (       d  g U R                  5         g! [         a0  nS[        U5      ;   a   SnAgS[        U5      SS  S3s SnA$ SnAff = f)	zPause playback.   Spotify not connected. — Lukeu   Paused. — Luker   Nu   Nothing is playing. — LukezPause error: r-   r.   )r   pause_playbackr   r3   r4   r   s     r   pauser      s[    	B06
! 6Q'1s1vcr{m9556    % 
AAAAAc                      [        5       n U (       d  g U R                  5         g! [         a0  nS[        U5      ;   a   SnAgS[        U5      SS  S3s SnA$ SnAff = f)	zResume playback.r   u   Resumed. — Luker   Nu.   No active device. Open Spotify first. — LukezResume error: r-   r.   )r   r   r   r3   r   s     r   resumer   	  s[    	B07
" 7Q'CAs}I667r   c                      [        5       n U (       d  g U R                  5         SSKnUR                  S5        [	        5       $ ! [
         a  nS[        U5      SS  S3s SnA$ SnAff = f)zSkip to next track.r   r   Ng      ?zSkip error: r-   r.   )r   
next_tracktimesleepr:   r   r3   )r4   r   r   s      r   skipr     s\    	B05


3} 5c!fSbk])445s   .A 
A)A$A)$A)c                    [        5       nU(       d  g UR                  U SSS9nUR                  S0 5      R                  S/ 5      nU(       d  SU  S3$ US	   nUS
   nSR                  S UR                  S/ 5       5       5      nUR	                  US   5        SU SU S3$ ! [
         a  nS[        U5      SS  S3s SnA$ SnAff = f)z+Search for a track and add it to the queue.r   r6   r]   rs   rw   r>   zCouldn't find '   '. — Luker   r   r   c              3   *   #    U  H	  oS    v   M     g7fr   r   r   s     r   r!   add_to_queue.<locals>.<genexpr>7  s     H/G!fI/Gr#   r$   rx   zQueued: r   r.   zQueue error: Nr-   )r   r   r0   r1   add_to_queuer   r3   )r   r4   r   rw   r6   r   r$   r   s           r   r   r   (  s    	B06))e');Xr*..w;$UG;77q	V}))HuyyB/GHH
e%$tG9I66 6s1vcr{m9556s$   ?B# AB# #
C	-C>C	C	c                 l   [        5       nU(       d  g UR                  SS9nUR                  S/ 5       HN  nU R                  5       US   R                  5       ;   d  M*  UR	                  US   S9  SUS    S	US
   S    S3s  $    UR                  U SSS9nUR                  S0 5      R                  S/ 5      nU(       a2  US   nUR	                  US   S9  SUS    SUS   S    S	US
   S    S3$ SU  S3$ ! [         a0  nS[        U5      ;   a   SnAgS[        U5      SS  S3s SnA$ SnAff = f)z.Find and play one of Bill's playlists by name.r   2   r<   r>   r   rx   )rz   zPlaying your playlist: r{   rw   r|   r}   playlistrr   rs   rv   r   r   r   ownerdisplay_namezNo playlist found matching 'r   r   Nu6   No active Spotify device. Open Spotify first. — LukezPlaylist error: r-   r.   )r   current_user_playlistsr0   r   r   r   r   r3   )r   r4   rv   r   r   r>   r   s          r   play_playlistr   ?  sa   	B09--B-7	--,Bzz|r&z//11!!bi!80FBr(|G?T>UUfgg - ))d1)=K,00"=qB"U)4r&zl$r'{>/J.K2bQYl[bNcMdduvv-dV;?? 9Q'K!#a&"+i889s7   AC9 %C9 A-C9 3C9 9
D3D.D.(D3.D3c                 \   [        5       nU(       d  g SSKJn  0 n UR                  SS9nUR	                  S0 5      R	                  S/ 5       H  nUS   X5S	   '   M      UR                  S
SS9nUR	                  S/ 5       H  nUS   X5S	   '   M      UR                  SSS9nUR	                  S/ 5       H  nUS   X5S	   '   M     U(       d  g[        R                  " 5       U" SS9-
  R                  S5      n/ n	UR                  5        H  u  p UR                  U
SSSS9nUR	                  S/ 5       H  nUR	                  SS5      nX:  d  M  UR	                  S0 5      R	                  SS5      nUR	                  SS5      nUR	                  SS5      nU	R                  UUR	                  SS5      UUUUS.5        M     [        U	5      U :  d  M    O   U	(       d  g[        5       n/ nU	 HH  nUS     S!US    3R                  5       nUU;  d  M&  UR                  U5        UR                  U5        MJ     UR!                  S" S#S$9  S%S/nUR                  [        U5       S&[        U5      S':w  a  S(OS S)35        UR                  S5        U H  nUS*   S:X  a  S+OS,nUR                  S-US     S.US    35        UR                  S/U S0US1    S2US3    35        US4   (       a  UR                  S/US4    35        UR                  S5        M     S5R#                  U5      $ ! [
         a     GNf = f! [
         a     GNf = f! [
         a     GNf = f! [
         a     GM]  f = f! [
         a4  n[$        R'                  S6U 35        S7[)        U5      S8S9  S:3s S8nA$ S8nAff = f);uK  
Check for new releases from artists Bill follows or listens to.
Music drops on Fridays — Luke runs this Friday morning.

Checks:
  1. Artists Bill follows
  2. Bill's top artists (last 6 months)
Then finds any albums/singles released in the last 7 days.

Returns formatted text with album links, or empty string if nothing new.
r   r   )	timedeltar   r<   r$   r>   r   rq      rZ   rR      rY   uI   No followed or top artists found yet. Listen more and try again. — Luke   )daysz%Y-%m-%dzalbum,single   CA)
album_typer=   countryrelease_dater&   external_urlsr   r   r%   total_tracks)rg   r   ru   rw   dateurlu2   No new drops from your artists this week. — Lukerg   r)   c                 &    U S   S:X  a  SOSU S   4$ )Nru   r%   r   r]   r   r   )xs    r   <lambda>new_releases.<locals>.<lambda>  s    &	W(<1!QvY"Or   T)keyreverseu   NEW MUSIC FRIDAY — Lukez new releaser]   sz from your artists:ru   ALBUMSINGLErD   rE   rF   r{   rw   u    tracks) • Released r   r   r,   zNew releases error: zCouldn't check new releases: Nr-   r.   )r   r   r   current_user_followed_artistsr0   r   rb   nowrJ   r>   artist_albumsr2   lensetr   addsortr1   r
   r   r3   )r=   r4   r   
artist_idsfollowedrg   top
top_recentcutoff	new_drops	artist_idr   albumsr%   r   	album_urlr   r   seenuniquedropr   r9   
type_labelr   s                            r   new_releasesr   ]  s    
BJcF& 
	77b7AH",,y"599'2F+1&>
$<( G	--B=-QC'''2.+1&>
$<( /	442,4WJ$.."5+1&>
$<( 6
 ^ ,,.9!#44>>zJ	&0&6&6&8"I))- 	 *  $ZZ4E#(99^R#@L#-$)IIor$B$F$FyRT$U	%*YY|W%E
',yy'C!((&1$)IIfb$9$.&2$0#,*  5( 9~&; '9> G uD(^$Ad6l^4::<C$d#	  	OY]^,b1F}LFq8Hb0QQdefRD$(LG$;JLL2d8n-U4<.ABLL4
|2d8n-==STXY_T`SabcE{tDK=12LL  yyi  		  		  		D  H  F+A3/0.s1vcr{m9EEFs   M- AL(  3L9 3M
 M- AM- <MA2MM- M- !0M- DM- (
L62M- 5L66M- 9
MM- MM- 

MM- MM- 
M*%M- )M**M- -
N+7)N& N+&N+c                    [        5       nU(       d  g UR                  U SS9nUR                  S/ 5      nU(       d  gS/n[        US5       H  u  pVUR                  SS5      nS	R	                  S
 UR                  S/ 5       5       5      nUR                  S0 5      n	U	R                  SS5      n
U	R                  S0 5      R                  SS5      nSU SU SU 3nU
(       a	  USU
 S3-  nUR                  U5        U(       d  M  UR                  SU 35        M     SR	                  U5      $ ! [         a"  n[        R                  SU 35         SnAgSnAff = f)z
Get Bill's top tracks from the past 4 weeks with Spotify album links.
Used in the weekly report.

Returns a formatted text block or empty string if unavailable.
r&   rY   rR   r>   zYOUR WEEK IN MUSICr]   r   r   r   c              3   *   #    U  H	  oS    v   M     g7fr   r   r   s     r   r!   'weekly_music_summary.<locals>.<genexpr>  rA   r#   r$   r%   r   r   rD   r`   rE   r{   )ra   r,   zWeekly music summary error: N)	r   rl   r0   rc   r1   r2   r   r
   r   )r=   r4   rd   r>   r9   rf   r6   r   r$   r%   
album_namer   liner   s                 r   weekly_music_summaryr     sT    
B++%L+Q

7B'%&!%+HA99VY/DiiL599Y3KLLGIIgr*E62.J 		/26::9bIIs"TF%y1D"ZL**LLyuYK01 ,  yy 3A378s#   )D0 CD0 (D0 0
E:EEc                  \    [        5       [        [        5       =(       d    [        SL 5      S.$ )z.Get Spotify status for dashboard health check.N)	available
configured)r   boolr   r   r   r   
get_statusr     s'     "^<>?Z4-?@ r   )r   )rZ   r   )r   )__doc__loggingr   	getLoggerr
   r	   r   r   r   r:   rP   rh   rm   r   r   r   r   r   r   r   r   r   r   r   r   <module>r      s   "  			9	% 
$$EN"GJ"EJD@29j675 6.9<sFl'Tr   