1 import urllib
, urllib2
, re
, xbmcplugin
, xbmcgui
, xbmc
, sys
, os
, time
, pprint
, shutil
4 __plugin__
= "Grooveshark"
5 __author__
= "Stephen Denham"
10 __XBMC_Revision__
= ""
13 MODE_SEARCH_ALBUMS
= 2
14 MODE_SEARCH_ARTISTS
= 3
24 MODE_SIMILAR_SONG
= 13
25 MODE_SIMILAR_ARTIST
= 14
35 resDir
= xbmc
.translatePath(os
.path
.join(baseDir
, 'resources'))
36 libDir
= xbmc
.translatePath(os
.path
.join(resDir
, 'lib'))
37 imgDir
= xbmc
.translatePath(os
.path
.join(resDir
, 'img'))
38 thumbDir
= os
.path
.join('special://masterprofile/plugin_data/music', os
.path
.join(os
.path
.basename(os
.getcwd()), 'thumb'))
40 sys
.path
.append (libDir
)
41 from GrooveAPI
import *
42 groovesharkApi
= GrooveAPI()
45 def __init__( self
, *args
, **kwargs
):
46 self
.__dict
__.update( kwargs
)
48 class GrovesharkPlayer(xbmc
.Player
):
49 # Player Class: calls function when song changes or playback ends
50 def __init__(self
, *args
, **kwargs
):
51 xbmc
.Player
.__init
__(self
)
52 self
.function
= kwargs
[ "function" ]
54 def onPlayBackStopped(self
):
55 print "onPlayBackStopped"
57 if (not xbmc
.Player().isPlayingAudio()):
60 def onPlayBackEnded(self
):
61 print "onPlayBackEnded"
63 if (not xbmc
.Player().isPlayingAudio()):
66 def onPlayBackStarted(self
):
67 print "onPlayBackStarted"
72 albumImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'album.png'))
73 artistImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'artist.png'))
74 favoritesImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'favorites.png'))
75 playlistImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'playlist.png'))
76 popularImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'popular.png'))
77 songImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'song.png'))
78 defImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'default.tbn'))
79 fanImg
= xbmc
.translatePath(os
.path
.join(baseDir
, 'fanart.jpg'))
81 songsearchlimit
= xbmcplugin
.getSetting('songsearchlimit')
82 albumsearchlimit
= xbmcplugin
.getSetting('albumsearchlimit')
83 artistsearchlimit
= xbmcplugin
.getSetting('artistsearchlimit')
87 self
._handle
= int(sys
.argv
[1])
91 userid
= self
._get
_login
()
94 groovesharkApi
.setRemoveDuplicates(True)
95 xbmcplugin
.setPluginFanart(int(sys
.argv
[1]), self
.fanImg
)
96 if os
.path
.isdir(thumbDir
) == False:
99 self
._add
_dir
('Search songs', '', MODE_SEARCH_SONGS
, self
.songImg
, 0)
100 self
._add
_dir
('Search albums', '', MODE_SEARCH_ALBUMS
, self
.albumImg
, 0)
101 self
._add
_dir
('Search artists', '', MODE_SEARCH_ARTISTS
, self
.artistImg
, 0)
102 self
._add
_dir
('Popular', '', MODE_POPULAR
, self
.popularImg
, 0)
104 self
._add
_dir
('Favorites', '', MODE_FAVORITES
, self
.favoritesImg
, 0)
105 self
._add
_dir
('Playlists', '', MODE_PLAYLISTS
, self
.playlistImg
, 0)
107 def searchSongs(self
):
108 query
= self
._get
_keyboard
(default
="", heading
="Search songs")
110 songs
= groovesharkApi
.searchSongs(query
, limit
= xbmcplugin
.getSetting('songsearchlimit'))
112 self
._add
_songs
_directory
(songs
)
114 dialog
= xbmcgui
.Dialog()
115 dialog
.ok('Grooveshark', 'No matching songs.')
118 def searchAlbums(self
):
119 query
= self
._get
_keyboard
(default
="", heading
="Search albums")
121 albums
= groovesharkApi
.searchAlbums(query
, limit
= xbmcplugin
.getSetting('albumsearchlimit'))
122 if (len(albums
) > 0):
123 self
._add
_albums
_directory
(albums
)
125 dialog
= xbmcgui
.Dialog()
126 dialog
.ok('Grooveshark', 'No matching albums.')
129 def searchArtists(self
):
130 query
= self
._get
_keyboard
(default
="", heading
="Search artists")
132 artists
= groovesharkApi
.searchArtists(query
, limit
= xbmcplugin
.getSetting('artistsearchlimit'))
133 if (len(artists
) > 0):
134 self
._add
_artists
_directory
(artists
)
136 dialog
= xbmcgui
.Dialog()
137 dialog
.ok('Grooveshark', 'No matching artists.')
141 userid
= self
._get
_login
()
143 favorites
= groovesharkApi
.userGetFavoriteSongs(userid
)
144 if (len(favorites
) > 0):
145 self
._add
_songs
_directory
(favorites
)
147 dialog
= xbmcgui
.Dialog()
148 dialog
.ok('Grooveshark', 'You have no favorites.')
152 popular
= groovesharkApi
.popularGetSongs(limit
= xbmcplugin
.getSetting('songsearchlimit'))
153 if (len(popular
) > 0):
154 self
._add
_songs
_directory
(popular
)
156 dialog
= xbmcgui
.Dialog()
157 dialog
.ok('Grooveshark', 'No popular songs.')
161 userid
= self
._get
_login
()
163 playlists
= groovesharkApi
.userGetPlaylists()
164 if (len(playlists
) > 0):
165 self
._add
_playlists
_directory
(playlists
)
167 dialog
= xbmcgui
.Dialog()
168 dialog
.ok('Grooveshark', 'You have no playlists.')
171 def favorite(self
, songid
):
172 userid
= self
._get
_login
()
174 xbmc
.log("Favorite playSong: " + str(songid
))
175 groovesharkApi
.favoriteSong(songID
= songid
)
177 dialog
= xbmcgui
.Dialog()
178 dialog
.ok('Grooveshark', 'You must be logged in', 'to add favorites.')
180 def unfavorite(self
, songid
):
181 userid
= self
._get
_login
()
183 xbmc
.log("Unfavorite playSong: " + str(songid
))
184 groovesharkApi
.unfavoriteSong(songID
= songid
)
186 dialog
= xbmcgui
.Dialog()
187 dialog
.ok('Grooveshark', 'You must be logged in', 'to remove favorites.')
189 def frown(self
, songid
):
190 userid
= self
._get
_login
()
192 xbmc
.log("Frown playSong: " + str(songid
))
193 if groovesharkApi
.radioFrown(songId
= songid
) != True:
194 xbmc
.log("Unable to frown song " + str(songid
))
196 dialog
= xbmcgui
.Dialog()
197 dialog
.ok('Grooveshark', 'You must be logged in', 'to frown a song.')
199 def similarSong(self
, songid
):
200 userid
= self
._get
_login
()
202 xbmc
.log("Frown playSong: " + str(songid
))
203 if groovesharkApi
.radioSong(songId
= songid
) and groovesharkApi
.radioStartSongs() == True:
206 dialog
= xbmcgui
.Dialog()
207 dialog
.ok('Grooveshark', 'Cannot start radio')
209 dialog
= xbmcgui
.Dialog()
210 dialog
.ok('Grooveshark', 'You must be logged in', 'to update radio song.')
212 def similarArtist(self
, artistId
):
213 userid
= self
._get
_login
()
215 xbmc
.log("Add radio artist of playSong: " + str(artistId
))
216 if groovesharkApi
.radioArtist(artistId
= artistId
) and groovesharkApi
.radioStartArtists() == True:
219 dialog
= xbmcgui
.Dialog()
220 dialog
.ok('Grooveshark', 'Cannot start radio')
222 dialog
= xbmcgui
.Dialog()
223 dialog
.ok('Grooveshark', 'You must be logged in', 'to update radio artists.')
225 def album(self
,albumid
):
226 album
= groovesharkApi
.albumGetSongs(albumId
= albumid
, limit
= xbmcplugin
.getSetting('songsearchlimit'))
227 self
._add
_songs
_directory
(album
)
229 def artist(self
, artistid
):
230 albums
= groovesharkApi
.artistGetAlbums(artistId
= artistid
, limit
= xbmcplugin
.getSetting('albumsearchlimit'))
231 self
._add
_albums
_directory
(albums
)
233 def playlist(self
, playlistid
):
234 userid
= self
._get
_login
()
236 songs
= groovesharkApi
.playlistGetSongs(playlistId
= playlistid
, limit
= xbmcplugin
.getSetting('songsearchlimit'))
237 self
._add
_songs
_directory
(songs
)
239 dialog
= xbmcgui
.Dialog()
240 dialog
.ok('Grooveshark', 'You must be logged in', 'to get playlists.')
242 def playSong(self
, item
):
243 url
= item
.getProperty('url')
244 xbmc
.log("Playing: " + url
)
245 xbmc
.Player( xbmc
.PLAYER_CORE_MPLAYER
).play(url
, item
)
248 item
= self
._get
_song
_item
(groovesharkApi
.radioNextSong()[0])
249 url
= item
.getProperty('url')
250 self
.player
= GrovesharkPlayer(xbmc
.PLAYER_CORE_PAPLAYER
, function
=self
.onPlayBackChanged
)
251 self
.player
.play(url
, item
)
253 def songItem(self
, id, name
, album
, artist
, duration
, thumb
, image
):
254 url
= groovesharkApi
.getStreamURL(id)
255 songImg
= self
._get
_icon
(image
, str(id) + "-image")
259 songThm
= self
._get
_icon
(thumb
, str(id) + "-thumb")
262 item
= xbmcgui
.ListItem(label
= artist
+ " - " + album
+ " - " + name
, path
=url
, thumbnailImage
=songThm
, iconImage
=songImg
)
263 item
.setInfo( type="Music", infoLabels
={ "title": name
, "duration": duration
, "album": album
, "artist": artist
} )
264 item
.setProperty('url', url
)
265 item
.setProperty('thumb', songThm
)
266 item
.setProperty('image', songImg
)
267 item
.setProperty('mimetype', 'audio/mpeg')
270 def _get_keyboard(self
, default
="", heading
="", hidden
=False):
271 kb
= xbmc
.Keyboard(default
, heading
, hidden
)
273 if (kb
.isConfirmed()):
274 return unicode(kb
.getText(), "utf-8")
277 def _get_login(self
):
278 username
= xbmcplugin
.getSetting('username')
279 password
= xbmcplugin
.getSetting('password')
280 if (username
== "" or password
== ""):
281 dialog
= xbmcgui
.Dialog()
282 dialog
.ok('Grooveshark', 'Unable to login.', 'Check username and password in settings.')
285 if groovesharkApi
.loggedInStatus() == 1:
286 groovesharkApi
.logout()
287 userid
= groovesharkApi
.loginExt(username
, password
)
289 xbmc
.log("Logged in")
292 dialog
= xbmcgui
.Dialog()
293 dialog
.ok('Grooveshark', 'Unable to login.', 'Check username and password in settings.')
296 def _get_song_item(self
, song
):
304 return self
.songItem(id, name
, album
, artist
, duration
, thumb
, image
)
307 def _get_icon(self
, url
, id):
308 # Get the channel icon
309 localThumb
= os
.path
.join(xbmc
.translatePath(os
.path
.join(thumbDir
, str(id)))) + '.tbn'
311 if os
.path
.isfile(localThumb
) == False:
312 loc
= urllib
.URLopener()
313 loc
.retrieve(url
, localThumb
)
315 xbmc
.log('URL download failed of ' + url
+ ' to ' + localThumb
)
317 shutil
.copyfile(self
.defImg
, localThumb
)
321 return os
.path
.join(os
.path
.join(thumbDir
, str(id))) + '.tbn'
323 def _add_songs_directory(self
, songs
):
325 xbmc
.log("Found " + str(n
) + " songs...")
329 item
= self
._get
_song
_item
(song
)
330 songurl
= item
.getProperty('url')
331 songthumb
= item
.getProperty('thumb')
332 songimage
= item
.getProperty('image')
335 songduration
= song
[2]
338 songartistid
= song
[7]
339 u
=sys
.argv
[0]+"?url="+urllib
.quote_plus(songurl
) \
340 +"&mode="+str(MODE_SONG
)+"&name="+urllib
.quote_plus(songname
)+"&id=" \
342 +"&album="+urllib
.quote_plus(songalbum
) \
343 +"&artist="+urllib
.quote_plus(songartist
) \
344 +"&duration="+str(songduration
) \
345 +"&thumb="+urllib
.quote_plus(songthumb
) \
346 +"&image="+urllib
.quote_plus(songimage
)
347 fav
=sys
.argv
[0]+"?url="+urllib
.quote_plus(songurl
)+"&mode="+str(MODE_FAVORITE
)+"&name="+urllib
.quote_plus(songname
)+"&id="+str(songid
)
348 unfav
=sys
.argv
[0]+"?url="+urllib
.quote_plus(songurl
)+"&mode="+str(MODE_UNFAVORITE
)+"&name="+urllib
.quote_plus(songname
)+"&id="+str(songid
)
349 similarArtist
=sys
.argv
[0]+"?mode="+str(MODE_SIMILAR_ARTIST
)+"&id="+str(songartistid
)
350 similarSong
=sys
.argv
[0]+"?mode="+str(MODE_SIMILAR_SONG
)+"&id="+str(songid
)
351 #frown=sys.argv[0]+"?mode="+str(MODE_FROWN)+"&id="+str(songid)
353 menuItems
.append(("Grooveshark Favorite", "XBMC.RunPlugin("+fav
+")"))
354 menuItems
.append(("Not Grooveshark Favorite", "XBMC.RunPlugin("+unfav
+")"))
355 menuItems
.append(("Listen to similar artist", "XBMC.RunPlugin("+similarArtist
+")"))
356 menuItems
.append(("Listen to similar song", "XBMC.RunPlugin("+similarSong
+")"))
357 # menuItems.append(("No thanks!", "XBMC.RunPlugin("+frown+")"))
358 item
.addContextMenuItems(menuItems
, replaceItems
=False)
359 xbmcplugin
.addDirectoryItem(handle
=int(sys
.argv
[1]),url
=u
,listitem
=item
,isFolder
=False)
361 xbmcplugin
.setContent(self
._handle
, 'songs')
363 def _add_albums_directory(self
, albums
):
364 xbmc
.log("Found " + str(len(albums
)) + " albums...")
366 while i
< len(albums
):
368 albumArtistName
= album
[0]
371 albumImage
= album
[4]
372 self
._add
_dir
(albumName
+ " - " + albumArtistName
, '', MODE_ALBUM
, albumImage
, albumID
)
374 xbmcplugin
.setContent(self
._handle
, 'albums')
375 xbmcplugin
.addSortMethod(self
._handle
, xbmcplugin
.SORT_METHOD_ALBUM_IGNORE_THE
)
377 def _add_artists_directory(self
, artists
):
378 xbmc
.log("Found " + str(len(artists
)) + " artists...")
380 while i
< len(artists
):
382 artistName
= artist
[0]
384 self
._add
_dir
(artistName
, '', MODE_ARTIST
, self
.artistImg
, artistID
)
386 xbmcplugin
.setContent(self
._handle
, 'artists')
387 xbmcplugin
.addSortMethod(self
._handle
, xbmcplugin
.SORT_METHOD_ARTIST_IGNORE_THE
)
389 def _add_playlists_directory(self
, playlists
):
390 xbmc
.log("Found " + str(len(playlists
)) + " playlists...")
392 while i
< len(playlists
):
393 playlist
= playlists
[i
]
394 playlistName
= playlist
[0]
395 playlistID
= playlist
[1]
396 self
._add
_dir
(playlistName
, '', MODE_PLAYLIST
, self
.playlistImg
, playlistID
)
398 xbmcplugin
.setContent(self
._handle
, 'files')
399 xbmcplugin
.addSortMethod(self
._handle
, xbmcplugin
.SORT_METHOD_PLAYLIST_ORDER
)
401 def _add_dir(self
, name
, url
, mode
, iconimage
, id):
402 u
=sys
.argv
[0]+"?url="+urllib
.quote_plus(url
)+"&mode="+str(mode
)+"&name="+urllib
.quote_plus(name
)+"&id="+str(id)
403 dir=xbmcgui
.ListItem(name
, iconImage
=iconimage
, thumbnailImage
=iconimage
)
404 dir.setInfo( type="Music", infoLabels
={ "title": name
} )
405 # Codes from http://xbmc-scripting.googlecode.com/svn/trunk/Script%20Templates/common/gui/codes.py
407 menuItems
.append(("Select", "XBMC.executebuiltin(Action(7))"))
408 dir.addContextMenuItems(menuItems
, replaceItems
=True)
409 return xbmcplugin
.addDirectoryItem(handle
=int(sys
.argv
[1]),url
=u
,listitem
=dir,isFolder
=True)
411 def onPlayBackChanged(self
, change
):
412 print "onPlayBackChanged " + str(change
)
414 groovesharkApi
.radioEnabled
= False
415 self
.musicplaylist
.clear()
416 if change
== 1 and groovesharkApi
.radioTurnedOn() == True:
418 self
.player
.playNext()
419 if change
== 1 and groovesharkApi
.radioTurnedOn() == False:
420 print "clear playlist"
421 self
.musicplaylist
.clear()
426 paramstring
=sys
.argv
[2]
427 if len(paramstring
)>=2:
429 cleanedparams
=params
.replace('?','')
430 if (params
[len(params
)-1]=='/'):
431 params
=params
[0:len(params
)-2]
432 pairsofparams
=cleanedparams
.split('&')
434 for i
in range(len(pairsofparams
)):
436 splitparams
=pairsofparams
[i
].split('=')
437 if (len(splitparams
))==2:
438 param
[splitparams
[0]]=splitparams
[1]
442 grooveshark
= Groveshark();
450 try: url
=urllib
.unquote_plus(params
["url"])
452 try: name
=urllib
.unquote_plus(params
["name"])
454 try: mode
=int(params
["mode"])
456 try: id=int(params
["id"])
463 grooveshark
.categories()
465 elif mode
==MODE_SEARCH_SONGS
:
466 grooveshark
.searchSongs()
468 elif mode
==MODE_SEARCH_ALBUMS
:
469 grooveshark
.searchAlbums()
471 elif mode
==MODE_SEARCH_ARTISTS
:
472 grooveshark
.searchArtists()
474 elif mode
==MODE_POPULAR
:
475 grooveshark
.popular()
477 elif mode
==MODE_PLAYLISTS
:
478 grooveshark
.playlists()
480 elif mode
==MODE_FAVORITES
:
481 grooveshark
.favorites()
483 elif mode
==MODE_SONG
:
484 try: name
=urllib
.unquote_plus(params
["name"])
486 try: album
=urllib
.unquote_plus(params
["album"])
488 try: artist
=urllib
.unquote_plus(params
["artist"])
490 try: duration
=int(params
["duration"])
492 try: thumb
=urllib
.unquote_plus(params
["thumb"])
494 try: image
=urllib
.unquote_plus(params
["image"])
496 song
= grooveshark
.songItem(id, name
, album
, artist
, duration
, thumb
, image
)
497 grooveshark
.playSong(song
)
499 elif mode
==MODE_ARTIST
:
500 grooveshark
.artist(lastID
)
502 elif mode
==MODE_ALBUM
:
503 grooveshark
.album(lastID
)
505 elif mode
==MODE_PLAYLIST
:
506 grooveshark
.playlist(lastID
)
508 elif mode
==MODE_FAVORITE
:
509 grooveshark
.favorite(lastID
)
511 elif mode
==MODE_UNFAVORITE
:
512 grooveshark
.unfavorite(lastID
)
514 elif mode
==MODE_SIMILAR_ARTIST
:
515 grooveshark
.similarArtist(lastID
)
517 elif mode
==MODE_SIMILAR_SONG
:
518 grooveshark
.similarSong(lastID
)
520 elif mode
==MODE_FROWN
:
521 grooveshark
.frown(lastID
)
523 if (mode
< MODE_SONG
):
524 xbmcplugin
.endOfDirectory(int(sys
.argv
[1]))