1 import urllib
, urllib2
, re
, xbmcplugin
, xbmcgui
, xbmc
, sys
, os
, time
, pprint
, shutil
, xbmcaddon
5 MODE_SEARCH_ARTISTS
= 3
15 MODE_SIMILAR_SONG
= 13
16 MODE_SIMILAR_ARTIST
= 14
22 resDir
= xbmc
.translatePath(os
.path
.join(baseDir
, 'resources'))
23 libDir
= xbmc
.translatePath(os
.path
.join(resDir
, 'lib'))
24 imgDir
= xbmc
.translatePath(os
.path
.join(resDir
, 'img'))
25 thumbDir
= os
.path
.join('special://masterprofile/addon_data/', os
.path
.join(os
.path
.basename(os
.getcwd()), 'thumb'))
26 favoritesCache
= xbmc
.translatePath(os
.path
.join('special://masterprofile/addon_data/', os
.path
.join(os
.path
.basename(os
.getcwd()), 'favorites.dmp')))
28 sys
.path
.append (libDir
)
29 from GrooveAPI
import *
30 groovesharkApi
= GrooveAPI()
33 def __init__( self
, *args
, **kwargs
):
34 self
.__dict
__.update( kwargs
)
38 albumImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'album.png'))
39 artistImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'artist.png'))
40 favoritesImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'favorites.png'))
41 playlistImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'playlist.png'))
42 popularImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'popular.png'))
43 songImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'song.png'))
44 defImg
= xbmc
.translatePath(os
.path
.join(imgDir
, 'default.tbn'))
45 fanImg
= xbmc
.translatePath(os
.path
.join(baseDir
, 'fanart.png'))
47 settings
= xbmcaddon
.Addon(id='plugin.audio.groove')
48 songsearchlimit
= settings
.getSetting('songsearchlimit')
49 albumsearchlimit
= settings
.getSetting('albumsearchlimit')
50 artistsearchlimit
= settings
.getSetting('artistsearchlimit')
51 username
= settings
.getSetting('username')
52 password
= settings
.getSetting('password')
55 self
._handle
= int(sys
.argv
[1])
59 xbmc
.log(self
.username
+ ", limits: " + str(self
.songsearchlimit
) + ", " + str(self
.albumsearchlimit
) + ", " + str(self
.artistsearchlimit
))
61 userid
= self
._get
_login
()
64 groovesharkApi
.setRemoveDuplicates(True)
65 xbmcplugin
.setPluginFanart(int(sys
.argv
[1]), self
.fanImg
)
66 if os
.path
.isdir(thumbDir
) == False:
69 self
._add
_dir
('Search songs', '', MODE_SEARCH_SONGS
, self
.songImg
, 0)
70 self
._add
_dir
('Search albums', '', MODE_SEARCH_ALBUMS
, self
.albumImg
, 0)
71 self
._add
_dir
('Search artists', '', MODE_SEARCH_ARTISTS
, self
.artistImg
, 0)
72 self
._add
_dir
('Popular', '', MODE_POPULAR
, self
.popularImg
, 0)
74 self
._add
_dir
('Favorites', '', MODE_FAVORITES
, self
.favoritesImg
, 0)
75 self
._add
_dir
('Playlists', '', MODE_PLAYLISTS
, self
.playlistImg
, 0)
77 def searchSongs(self
):
78 query
= self
._get
_keyboard
(default
="", heading
="Search songs")
80 songs
= groovesharkApi
.searchSongs(query
, limit
= self
.songsearchlimit
)
82 self
._add
_songs
_directory
(songs
)
84 dialog
= xbmcgui
.Dialog()
85 dialog
.ok('Grooveshark', 'No matching songs.')
88 def searchAlbums(self
):
89 query
= self
._get
_keyboard
(default
="", heading
="Search albums")
91 albums
= groovesharkApi
.searchAlbums(query
, limit
= self
.albumsearchlimit
)
93 self
._add
_albums
_directory
(albums
)
95 dialog
= xbmcgui
.Dialog()
96 dialog
.ok('Grooveshark', 'No matching albums.')
99 def searchArtists(self
):
100 query
= self
._get
_keyboard
(default
="", heading
="Search artists")
102 artists
= groovesharkApi
.searchArtists(query
, limit
= self
.artistsearchlimit
)
103 if (len(artists
) > 0):
104 self
._add
_artists
_directory
(artists
)
106 dialog
= xbmcgui
.Dialog()
107 dialog
.ok('Grooveshark', 'No matching artists.')
111 favorites
= self
._get
_favorites
()
112 if (len(favorites
) > 0):
113 self
._add
_songs
_directory
(favorites
)
115 dialog
= xbmcgui
.Dialog()
116 dialog
.ok('Grooveshark', 'You have no favorites.')
120 popular
= groovesharkApi
.popularGetSongs(limit
= self
.songsearchlimit
)
121 if (len(popular
) > 0):
122 self
._add
_songs
_directory
(popular
)
124 dialog
= xbmcgui
.Dialog()
125 dialog
.ok('Grooveshark', 'No popular songs.')
129 userid
= self
._get
_login
()
131 playlists
= groovesharkApi
.userGetPlaylists()
132 if (len(playlists
) > 0):
133 self
._add
_playlists
_directory
(playlists
)
135 dialog
= xbmcgui
.Dialog()
136 dialog
.ok('Grooveshark', 'You have no playlists.')
139 def favorite(self
, songid
):
140 userid
= self
._get
_login
()
142 xbmc
.log("Favorite playSong: " + str(songid
))
143 groovesharkApi
.favoriteSong(songID
= songid
)
144 os
.remove(favoritesCache
)
146 dialog
= xbmcgui
.Dialog()
147 dialog
.ok('Grooveshark', 'You must be logged in', 'to add favorites.')
149 def unfavorite(self
, songid
):
150 userid
= self
._get
_login
()
152 xbmc
.log("Unfavorite playSong: " + str(songid
))
153 groovesharkApi
.unfavoriteSong(songID
= songid
)
154 os
.remove(favoritesCache
)
156 dialog
= xbmcgui
.Dialog()
157 dialog
.ok('Grooveshark', 'You must be logged in', 'to remove favorites.')
159 def frown(self
, songid
):
160 userid
= self
._get
_login
()
162 xbmc
.log("Frown playSong: " + str(songid
))
163 if groovesharkApi
.radioFrown(songId
= songid
) != True:
164 xbmc
.log("Unable to frown song " + str(songid
))
166 dialog
= xbmcgui
.Dialog()
167 dialog
.ok('Grooveshark', 'You must be logged in', 'to frown a song.')
169 def similarSong(self
, songid
):
170 userid
= self
._get
_login
()
172 xbmc
.log("Add song: " + str(songid
))
173 if groovesharkApi
.radioSong(songId
= songid
) != False and groovesharkApi
.radioStartSongs() == True:
176 dialog
= xbmcgui
.Dialog()
177 dialog
.ok('Grooveshark', 'Cannot start radio')
179 dialog
= xbmcgui
.Dialog()
180 dialog
.ok('Grooveshark', 'You must be logged in', 'to update radio song.')
182 def similarArtist(self
, artistId
):
183 userid
= self
._get
_login
()
185 xbmc
.log("Add radio artist: " + str(artistId
))
186 if groovesharkApi
.radioArtist(artistId
= artistId
) != False and groovesharkApi
.radioStartArtists() == True:
189 dialog
= xbmcgui
.Dialog()
190 dialog
.ok('Grooveshark', 'Cannot start radio')
192 dialog
= xbmcgui
.Dialog()
193 dialog
.ok('Grooveshark', 'You must be logged in', 'to update radio artists.')
195 def album(self
,albumid
):
196 album
= groovesharkApi
.albumGetSongs(albumId
= albumid
, limit
= self
.songsearchlimit
)
197 self
._add
_songs
_directory
(album
)
199 def artist(self
, artistid
):
200 albums
= groovesharkApi
.artistGetAlbums(artistId
= artistid
, limit
= self
.albumsearchlimit
)
201 self
._add
_albums
_directory
(albums
)
203 def playlist(self
, playlistid
):
204 userid
= self
._get
_login
()
206 songs
= groovesharkApi
.playlistGetSongs(playlistId
= playlistid
, limit
= self
.songsearchlimit
)
207 self
._add
_songs
_directory
(songs
)
209 dialog
= xbmcgui
.Dialog()
210 dialog
.ok('Grooveshark', 'You must be logged in', 'to get playlists.')
212 def playSong(self
, item
):
213 url
= item
.getProperty('url')
214 xbmc
.log("Playing: " + url
)
215 xbmcplugin
.setResolvedUrl(handle
=int(sys
.argv
[1]), succeeded
=True, listitem
=item
)
218 item
= self
._get
_song
_item
(groovesharkApi
.radioNextSong()[0])
221 def songItem(self
, id, name
, album
, artist
, duration
, thumb
, image
):
222 url
= groovesharkApi
.getStreamURL(id)
223 # Only try to get the image
225 songImg
= self
._get
_icon
(image
, 'song-' + str(id) + "-image")
228 songThm
= self
._get
_icon
(thumb
, 'song-' + str(id) + "-thumb")
230 item
= xbmcgui
.ListItem(label
= artist
+ " - " + album
+ " - " + name
, path
=url
, thumbnailImage
=songThm
, iconImage
=songImg
)
231 item
.setInfo( type="music", infoLabels
={ "title": name
, "duration": duration
, "album": album
, "artist": artist
} )
232 item
.setProperty('mimetype', 'audio/mpeg')
233 item
.setProperty("IsPlayable", "true")
234 item
.setProperty('url', url
)
235 item
.setProperty('thumb', songThm
)
236 item
.setProperty('image', songImg
)
240 def _get_favorites(self
):
242 # if the cache does not exist or is older than x hours then reload
243 if (os
.path
.isfile(favoritesCache
) == False) or (time
.time() - os
.path
.getmtime(favoritesCache
) > 3*60*60):
244 xbmc
.log("Refresh favorites cache")
245 userid
= self
._get
_login
()
247 favorites
= groovesharkApi
.userGetFavoriteSongs(userid
)
248 f
= open(favoritesCache
, 'wb')
249 pickle
.dump(favorites
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
251 xbmc
.log("Refreshed favorites cache")
252 # if not old then read from cache
253 elif os
.path
.isfile(favoritesCache
):
254 xbmc
.log("Existing favorites cache")
255 f
= open(favoritesCache
, 'rb')
256 favorites
= pickle
.load(f
)
261 def _get_keyboard(self
, default
="", heading
="", hidden
=False):
262 kb
= xbmc
.Keyboard(default
, heading
, hidden
)
264 if (kb
.isConfirmed()):
265 return unicode(kb
.getText(), "utf-8")
268 def _get_login(self
):
269 if (self
.username
== "" or self
.password
== ""):
270 dialog
= xbmcgui
.Dialog()
271 dialog
.ok('Grooveshark', 'Unable to login.', 'Check username and password in settings.')
274 if groovesharkApi
.loggedInStatus() == 1:
275 groovesharkApi
.logout()
276 userid
= groovesharkApi
.loginExt(self
.username
, self
.password
)
278 xbmc
.log("Logged in")
281 dialog
= xbmcgui
.Dialog()
282 dialog
.ok('Grooveshark', 'Unable to login.', 'Check username and password in settings.')
285 def _get_song_item(self
, song
):
293 return self
.songItem(id, name
, album
, artist
, duration
, thumb
, image
)
296 def _get_icon(self
, url
, id):
297 localThumb
= os
.path
.join(xbmc
.translatePath(os
.path
.join(thumbDir
, str(id)))) + '.tbn'
299 if os
.path
.isfile(localThumb
) == False:
300 xbmc
.log('Downloading ' + url
+ ' to ' + localThumb
)
301 loc
= urllib
.URLopener()
302 loc
.retrieve(url
, localThumb
)
304 xbmc
.log('URL download failed of ' + url
+ ' to ' + localThumb
)
307 return os
.path
.join(os
.path
.join(thumbDir
, str(id))) + '.tbn'
309 def _add_songs_directory(self
, songs
):
311 xbmc
.log("Found " + str(n
) + " songs...")
315 item
= self
._get
_song
_item
(song
)
316 songurl
= item
.getProperty('url')
317 songthumb
= item
.getProperty('thumb')
318 songimage
= item
.getProperty('image')
321 songduration
= song
[2]
324 #songartistid = song[7]
325 u
=sys
.argv
[0]+"?url="+urllib
.quote_plus(songurl
) \
326 +"&mode="+str(MODE_SONG
)+"&name="+urllib
.quote_plus(songname
)+"&id=" \
328 +"&album="+urllib
.quote_plus(songalbum
) \
329 +"&artist="+urllib
.quote_plus(songartist
) \
330 +"&duration="+str(songduration
) \
331 +"&thumb="+urllib
.quote_plus(songthumb
) \
332 +"&image="+urllib
.quote_plus(songimage
)
333 fav
=sys
.argv
[0]+"?url="+urllib
.quote_plus(songurl
)+"&mode="+str(MODE_FAVORITE
)+"&name="+urllib
.quote_plus(songname
)+"&id="+str(songid
)
334 unfav
=sys
.argv
[0]+"?url="+urllib
.quote_plus(songurl
)+"&mode="+str(MODE_UNFAVORITE
)+"&name="+urllib
.quote_plus(songname
)+"&id="+str(songid
)
335 #similarArtist=sys.argv[0]+"?mode="+str(MODE_SIMILAR_ARTIST)+"&id="+str(songartistid)
336 #similarSong=sys.argv[0]+"?mode="+str(MODE_SIMILAR_SONG)+"&id="+str(songid)
337 #frown=sys.argv[0]+"?mode="+str(MODE_FROWN)+"&id="+str(songid)
339 menuItems
.append(("Grooveshark Favorite", "XBMC.RunPlugin("+fav
+")"))
340 menuItems
.append(("Not Grooveshark Favorite", "XBMC.RunPlugin("+unfav
+")"))
341 #menuItems.append(("Listen to similar artist", "XBMC.RunPlugin("+similarArtist+")"))
342 #menuItems.append(("Listen to similar song", "XBMC.RunPlugin("+similarSong+")"))
343 #menuItems.append(("No thanks!", "XBMC.RunPlugin("+frown+")"))
344 item
.addContextMenuItems(menuItems
, replaceItems
=False)
345 xbmcplugin
.addDirectoryItem(handle
=int(sys
.argv
[1]),url
=u
,listitem
=item
,isFolder
=False,totalItems
=n
)
347 xbmcplugin
.setContent(self
._handle
, 'songs')
348 xbmcplugin
.setPluginFanart(int(sys
.argv
[1]), self
.fanImg
)
350 def _add_albums_directory(self
, albums
):
352 xbmc
.log("Found " + str(n
) + " albums...")
356 albumArtistName
= album
[0]
359 albumImage
= self
._get
_icon
(album
[4], 'album-' + str(albumID
))
360 self
._add
_dir
(albumName
+ " - " + albumArtistName
, '', MODE_ALBUM
, albumImage
, albumID
, n
)
362 xbmcplugin
.setContent(self
._handle
, 'albums')
363 xbmcplugin
.addSortMethod(self
._handle
, xbmcplugin
.SORT_METHOD_ALBUM_IGNORE_THE
)
364 xbmcplugin
.setPluginFanart(int(sys
.argv
[1]), self
.fanImg
)
366 def _add_artists_directory(self
, artists
):
368 xbmc
.log("Found " + str(n
) + " artists...")
372 artistName
= artist
[0]
374 self
._add
_dir
(artistName
, '', MODE_ARTIST
, self
.artistImg
, artistID
, n
)
376 xbmcplugin
.setContent(self
._handle
, 'artists')
377 xbmcplugin
.addSortMethod(self
._handle
, xbmcplugin
.SORT_METHOD_ARTIST_IGNORE_THE
)
378 xbmcplugin
.setPluginFanart(int(sys
.argv
[1]), self
.fanImg
)
380 def _add_playlists_directory(self
, playlists
):
382 xbmc
.log("Found " + str(n
) + " playlists...")
385 playlist
= playlists
[i
]
386 playlistName
= playlist
[0]
387 playlistID
= playlist
[1]
388 self
._add
_dir
(playlistName
, '', MODE_PLAYLIST
, self
.playlistImg
, playlistID
, n
)
390 xbmcplugin
.setContent(self
._handle
, 'files')
391 xbmcplugin
.addSortMethod(self
._handle
, xbmcplugin
.SORT_METHOD_PLAYLIST_ORDER
)
392 xbmcplugin
.setPluginFanart(int(sys
.argv
[1]), self
.fanImg
)
394 def _add_dir(self
, name
, url
, mode
, iconimage
, id, items
=1):
395 u
=sys
.argv
[0]+"?url="+urllib
.quote_plus(url
)+"&mode="+str(mode
)+"&name="+urllib
.quote_plus(name
)+"&id="+str(id)
396 dir=xbmcgui
.ListItem(name
, iconImage
=iconimage
, thumbnailImage
=iconimage
)
397 dir.setInfo( type="Music", infoLabels
={ "title": name
} )
398 # Codes from http://xbmc-scripting.googlecode.com/svn/trunk/Script%20Templates/common/gui/codes.py
400 menuItems
.append(("Select", "XBMC.executebuiltin(Action(7))"))
401 dir.addContextMenuItems(menuItems
, replaceItems
=True)
402 return xbmcplugin
.addDirectoryItem(handle
=int(sys
.argv
[1]),url
=u
,listitem
=dir,isFolder
=True, totalItems
=items
)
407 paramstring
=sys
.argv
[2]
408 if len(paramstring
)>=2:
410 cleanedparams
=params
.replace('?','')
411 if (params
[len(params
)-1]=='/'):
412 params
=params
[0:len(params
)-2]
413 pairsofparams
=cleanedparams
.split('&')
415 for i
in range(len(pairsofparams
)):
417 splitparams
=pairsofparams
[i
].split('=')
418 if (len(splitparams
))==2:
419 param
[splitparams
[0]]=splitparams
[1]
423 grooveshark
= Groveshark();
431 try: url
=urllib
.unquote_plus(params
["url"])
433 try: name
=urllib
.unquote_plus(params
["name"])
435 try: mode
=int(params
["mode"])
437 try: id=int(params
["id"])
444 grooveshark
.categories()
446 elif mode
==MODE_SEARCH_SONGS
:
447 grooveshark
.searchSongs()
449 elif mode
==MODE_SEARCH_ALBUMS
:
450 grooveshark
.searchAlbums()
452 elif mode
==MODE_SEARCH_ARTISTS
:
453 grooveshark
.searchArtists()
455 elif mode
==MODE_POPULAR
:
456 grooveshark
.popular()
458 elif mode
==MODE_PLAYLISTS
:
459 grooveshark
.playlists()
461 elif mode
==MODE_FAVORITES
:
462 grooveshark
.favorites()
464 elif mode
==MODE_SONG
:
465 try: name
=urllib
.unquote_plus(params
["name"])
467 try: album
=urllib
.unquote_plus(params
["album"])
469 try: artist
=urllib
.unquote_plus(params
["artist"])
471 try: duration
=int(params
["duration"])
473 try: thumb
=urllib
.unquote_plus(params
["thumb"])
475 try: image
=urllib
.unquote_plus(params
["image"])
477 song
= grooveshark
.songItem(id, name
, album
, artist
, duration
, thumb
, image
)
478 grooveshark
.playSong(song
)
480 elif mode
==MODE_ARTIST
:
481 grooveshark
.artist(lastID
)
483 elif mode
==MODE_ALBUM
:
484 grooveshark
.album(lastID
)
486 elif mode
==MODE_PLAYLIST
:
487 grooveshark
.playlist(lastID
)
489 elif mode
==MODE_FAVORITE
:
490 grooveshark
.favorite(lastID
)
492 elif mode
==MODE_UNFAVORITE
:
493 grooveshark
.unfavorite(lastID
)
495 elif mode
==MODE_SIMILAR_ARTIST
:
496 grooveshark
.similarArtist(lastID
)
498 elif mode
==MODE_SIMILAR_SONG
:
499 grooveshark
.similarSong(lastID
)
501 elif mode
==MODE_FROWN
:
502 grooveshark
.frown(lastID
)
504 if (mode
< MODE_SONG
):
505 xbmcplugin
.endOfDirectory(int(sys
.argv
[1]))