Big update for v3 API.
[clinton/xbmc-groove.git] / default.py
CommitLineData
f95afae7 1import urllib, sys, os, shutil, re, pickle, time, tempfile, xbmcaddon, xbmcplugin, xbmcgui, xbmc
8817bb2e 2MODE_SEARCH_SONGS = 1
3MODE_SEARCH_ALBUMS = 2
4MODE_SEARCH_ARTISTS = 3
86f629ea 5MODE_SEARCH_ARTISTS_ALBUMS = 4
6MODE_SEARCH_PLAYLISTS = 5
97289139 7MODE_ARTIST_POPULAR = 6
8MODE_POPULAR_SONGS = 7
9MODE_FAVORITES = 8
10MODE_PLAYLISTS = 9
11MODE_ALBUM = 10
12MODE_ARTIST = 11
13MODE_PLAYLIST = 12
14MODE_SONG_PAGE = 13
052028f1 15MODE_SIMILAR_ARTISTS = 14
16MODE_SONG = 15
17MODE_FAVORITE = 16
18MODE_UNFAVORITE = 17
19MODE_MAKE_PLAYLIST = 18
20MODE_REMOVE_PLAYLIST = 19
21MODE_RENAME_PLAYLIST = 20
22MODE_REMOVE_PLAYLIST_SONG = 21
23MODE_ADD_PLAYLIST_SONG = 22
7ea6f166 24
38df1fa5 25ACTION_MOVE_LEFT = 1
7ea6f166 26ACTION_MOVE_UP = 3
27ACTION_MOVE_DOWN = 4
28ACTION_PAGE_UP = 5
29ACTION_PAGE_DOWN = 6
30ACTION_SELECT_ITEM = 7
31ACTION_PREVIOUS_MENU = 10
8817bb2e 32
86f629ea 33# Formats for track labels
34ARTIST_ALBUM_NAME_LABEL = 0
35NAME_ALBUM_ARTIST_LABEL = 1
36
f95afae7 37# Stream marking time (seconds)
38STREAM_MARKING_TIME = 30
39
40songMarkTime = 0
41player = xbmc.Player()
42playTimer = None
43
6ae708d0 44baseDir = os.getcwd()
45resDir = xbmc.translatePath(os.path.join(baseDir, 'resources'))
8817bb2e 46libDir = xbmc.translatePath(os.path.join(resDir, 'lib'))
47imgDir = xbmc.translatePath(os.path.join(resDir, 'img'))
7ce01be6 48cacheDir = os.path.join(xbmc.translatePath('special://masterprofile/addon_data/'), os.path.basename(os.getcwd()))
49thumbDirName = 'thumb'
50thumbDir = os.path.join('special://masterprofile/addon_data/', os.path.basename(os.getcwd()), thumbDirName)
4be42357 51
52baseModeUrl = 'plugin://plugin.audio.groove/'
e278f474 53playlistUrl = baseModeUrl + '?mode=' + str(MODE_PLAYLIST)
4be42357 54playlistsUrl = baseModeUrl + '?mode=' + str(MODE_PLAYLISTS)
55favoritesUrl = baseModeUrl + '?mode=' + str(MODE_FAVORITES)
56
3d95dfcb 57searchArtistsAlbumsName = "Search for artist's albums..."
58
7ce01be6 59thumbDef = os.path.join(imgDir, 'default.tbn')
052028f1 60listBackground = os.path.join(imgDir, 'listbackground.png')
8817bb2e 61
62sys.path.append (libDir)
7ce01be6 63from GroovesharkAPI import GrooveAPI
f95afae7 64from threading import Event, Thread
7ce01be6 65
a3ad8f73 66try:
67 groovesharkApi = GrooveAPI()
7ce01be6 68 if groovesharkApi.pingService() != True:
69 raise StandardError('No Grooveshark service')
a3ad8f73 70except:
71 dialog = xbmcgui.Dialog()
72 dialog.ok('Grooveshark XBMC', 'Unable to connect with Grooveshark.', 'Please try again later')
73 sys.exit(-1)
74
f95afae7 75# Mark song as playing or played
76def markSong(songid, duration):
77 global songMarkTime
78 global playTimer
79 global player
80 if player.isPlayingAudio():
81 tNow = player.getTime()
82 if tNow >= STREAM_MARKING_TIME and songMarkTime == 0:
83 groovesharkApi.markStreamKeyOver30Secs()
84 songMarkTime = tNow
85 elif duration > tNow and duration - tNow < 2 and songMarkTime >= STREAM_MARKING_TIME:
86 playTimer.cancel()
87 songMarkTime = 0
88 groovesharkApi.markSongComplete(songid)
89 else:
90 playTimer.cancel()
91 songMarkTime = 0
92
8817bb2e 93class _Info:
94 def __init__( self, *args, **kwargs ):
95 self.__dict__.update( kwargs )
e278f474 96
052028f1 97# Window dialog to select a grooveshark playlist
98class GroovesharkPlaylistSelect(xbmcgui.WindowDialog):
99
100 def __init__(self, items=[]):
101 gap = int(self.getHeight()/100)
102 w = int(self.getWidth()*0.5)
103 h = self.getHeight()-30*gap
104 rw = self.getWidth()
105 rh = self.getHeight()
106 x = rw/2 - w/2
107 y = rh/2 -h/2
108
109 self.imgBg = xbmcgui.ControlImage(x+gap, 5*gap+y, w-2*gap, h-5*gap, listBackground)
110 self.addControl(self.imgBg)
111
112 self.playlistControl = xbmcgui.ControlList(2*gap+x, y+3*gap+30, w-4*gap, h-10*gap, textColor='0xFFFFFFFF', selectedColor='0xFFFF4242', itemTextYOffset=0, itemHeight=50, alignmentY = 0)
113 self.addControl(self.playlistControl)
114
115 self.lastPos = 0
116 self.isSelecting = False
117 self.selected = -1
118 listitems = []
119 for playlist in items:
120 listitems.append(xbmcgui.ListItem(playlist[0]))
121 listitems.append(xbmcgui.ListItem('New...'))
122 self.playlistControl.addItems(listitems)
123 self.setFocus(self.playlistControl)
124 self.playlistControl.selectItem(0)
125 item = self.playlistControl.getListItem(self.lastPos)
126 item.select(True)
127
128 # Highlight selected item
129 def setHighlight(self):
130 if self.isSelecting:
131 return
132 else:
133 self.isSelecting = True
134
135 pos = self.playlistControl.getSelectedPosition()
136 if pos >= 0:
137 item = self.playlistControl.getListItem(self.lastPos)
138 item.select(False)
139 item = self.playlistControl.getListItem(pos)
140 item.select(True)
141 self.lastPos = pos
142 self.isSelecting = False
143
144 # Control - select
145 def onControl(self, control):
146 if control == self.playlistControl:
147 self.selected = self.playlistControl.getSelectedPosition()
148 self.close()
149
150 # Action - close or up/down
151 def onAction(self, action):
152 if action == ACTION_PREVIOUS_MENU:
153 self.selected = -1
154 self.close()
155 elif action == ACTION_MOVE_UP or action == ACTION_MOVE_DOWN or action == ACTION_PAGE_UP or action == ACTION_PAGE_DOWN == 6:
156 self.setFocus(self.playlistControl)
157 self.setHighlight()
f95afae7 158
159
160class PlayTimer(Thread):
161 # interval -- floating point number specifying the number of seconds to wait before executing function
162 # function -- the function (or callable object) to be executed
163
164 # iterations -- integer specifying the number of iterations to perform
165 # args -- list of positional arguments passed to function
166 # kwargs -- dictionary of keyword arguments passed to function
167
168 def __init__(self, interval, function, iterations=0, args=[], kwargs={}):
169 Thread.__init__(self)
170 self.interval = interval
171 self.function = function
172 self.iterations = iterations
173 self.args = args
174 self.kwargs = kwargs
175 self.finished = Event()
176
177 def run(self):
178 count = 0
179 while not self.finished.isSet() and (self.iterations <= 0 or count < self.iterations):
180 self.finished.wait(self.interval)
181 if not self.finished.isSet():
182 self.function(*self.args, **self.kwargs)
183 count += 1
184
185 def cancel(self):
186 self.finished.set()
187
188 def setIterations(self, iterations):
189 self.iterations = iterations
190
191
192 def getTime(self):
193 return self.iterations * self.interval
194
195
8817bb2e 196class Groveshark:
973b4c6c 197
7ea6f166 198 albumImg = xbmc.translatePath(os.path.join(imgDir, 'album.png'))
199 artistImg = xbmc.translatePath(os.path.join(imgDir, 'artist.png'))
86f629ea 200 artistsAlbumsImg = xbmc.translatePath(os.path.join(imgDir, 'artistsalbums.png'))
7ea6f166 201 favoritesImg = xbmc.translatePath(os.path.join(imgDir, 'favorites.png'))
202 playlistImg = xbmc.translatePath(os.path.join(imgDir, 'playlist.png'))
86f629ea 203 usersplaylistsImg = xbmc.translatePath(os.path.join(imgDir, 'usersplaylists.png'))
7ea6f166 204 popularSongsImg = xbmc.translatePath(os.path.join(imgDir, 'popularSongs.png'))
97289139 205 popularSongsArtistImg = xbmc.translatePath(os.path.join(imgDir, 'popularSongsArtist.png'))
7ea6f166 206 songImg = xbmc.translatePath(os.path.join(imgDir, 'song.png'))
207 defImg = xbmc.translatePath(os.path.join(imgDir, 'default.tbn'))
2254a6b5 208 fanImg = xbmc.translatePath(os.path.join(baseDir, 'fanart.png'))
8817bb2e 209
2254a6b5 210 settings = xbmcaddon.Addon(id='plugin.audio.groove')
7ce01be6 211 songsearchlimit = int(settings.getSetting('songsearchlimit'))
212 albumsearchlimit = int(settings.getSetting('albumsearchlimit'))
213 artistsearchlimit = int(settings.getSetting('artistsearchlimit'))
97289139 214 songspagelimit = int(settings.getSetting('songspagelimit'))
2254a6b5 215 username = settings.getSetting('username')
216 password = settings.getSetting('password')
38df1fa5 217 userid = 0
4be42357 218
8817bb2e 219 def __init__( self ):
220 self._handle = int(sys.argv[1])
7ce01be6 221 if os.path.isdir(cacheDir) == False:
222 os.makedirs(cacheDir)
223 xbmc.log("Made " + cacheDir)
164e42d8 224 artDir = xbmc.translatePath(thumbDir)
225 if os.path.isdir(artDir) == False:
3a794693 226 os.makedirs(artDir)
227 xbmc.log("Made " + artDir)
052028f1 228
e278f474 229 # Top-level menu
8817bb2e 230 def categories(self):
2254a6b5 231
38df1fa5 232 self.userid = self._get_login()
b738088f 233
234 # Setup
6ae708d0 235 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
6ae708d0 236
86f629ea 237 self._add_dir('Search for songs...', '', MODE_SEARCH_SONGS, self.songImg, 0)
238 self._add_dir('Search for albums...', '', MODE_SEARCH_ALBUMS, self.albumImg, 0)
239 self._add_dir('Search for artists...', '', MODE_SEARCH_ARTISTS, self.artistImg, 0)
3d95dfcb 240 self._add_dir(searchArtistsAlbumsName, '', MODE_SEARCH_ARTISTS_ALBUMS, self.artistsAlbumsImg, 0)
f95afae7 241 # Not supported by key
242 #self._add_dir("Search for user's playlists...", '', MODE_SEARCH_PLAYLISTS, self.usersplaylistsImg, 0)
243 self._add_dir('Popular Grooveshark songs for artist...', '', MODE_ARTIST_POPULAR, self.popularSongsArtistImg, 0)
244 self._add_dir('Popular Grooveshark songs', '', MODE_POPULAR_SONGS, self.popularSongsImg, 0)
38df1fa5 245 if (self.userid != 0):
f95afae7 246 self._add_dir('My Grooveshark favorites', '', MODE_FAVORITES, self.favoritesImg, 0)
247 self._add_dir('My Grooveshark playlists', '', MODE_PLAYLISTS, self.playlistImg, 0)
e278f474 248
249 # Search for songs
8817bb2e 250 def searchSongs(self):
f95afae7 251 query = self._get_keyboard(default="", heading='Search for songs powered by Grooveshark')
7ea6f166 252 if (query != ''):
7ce01be6 253 songs = groovesharkApi.getSongSearchResults(query, limit = self.songsearchlimit)
8817bb2e 254 if (len(songs) > 0):
6ae708d0 255 self._add_songs_directory(songs)
8817bb2e 256 else:
257 dialog = xbmcgui.Dialog()
38df1fa5 258 dialog.ok('Grooveshark XBMC', 'No matching songs.')
8817bb2e 259 self.categories()
7ea6f166 260 else:
261 self.categories()
8817bb2e 262
e278f474 263 # Search for albums
8817bb2e 264 def searchAlbums(self):
f95afae7 265 query = self._get_keyboard(default="", heading='Search for albums powered by Grooveshark')
7ea6f166 266 if (query != ''):
7ce01be6 267 albums = groovesharkApi.getAlbumSearchResults(query, limit = self.albumsearchlimit)
8817bb2e 268 if (len(albums) > 0):
6ae708d0 269 self._add_albums_directory(albums)
8817bb2e 270 else:
271 dialog = xbmcgui.Dialog()
38df1fa5 272 dialog.ok('Grooveshark XBMC', 'No matching albums.')
8817bb2e 273 self.categories()
7ea6f166 274 else:
275 self.categories()
8817bb2e 276
e278f474 277 # Search for artists
8817bb2e 278 def searchArtists(self):
f95afae7 279 query = self._get_keyboard(default="", heading='Search for artists powered by Grooveshark')
7ea6f166 280 if (query != ''):
7ce01be6 281 artists = groovesharkApi.getArtistSearchResults(query, limit = self.artistsearchlimit)
8817bb2e 282 if (len(artists) > 0):
6ae708d0 283 self._add_artists_directory(artists)
8817bb2e 284 else:
285 dialog = xbmcgui.Dialog()
38df1fa5 286 dialog.ok('Grooveshark XBMC', 'No matching artists.')
8817bb2e 287 self.categories()
7ea6f166 288 else:
289 self.categories()
86f629ea 290
291 # Search for playlists
292 def searchPlaylists(self):
97289139 293 query = self._get_keyboard(default="", heading="Username")
86f629ea 294 if (query != ''):
f95afae7 295 playlists = groovesharkApi.getUserPlaylistsByUsername(query)
86f629ea 296 if (len(playlists) > 0):
297 self._add_playlists_directory(playlists)
298 else:
299 dialog = xbmcgui.Dialog()
300 dialog.ok('Grooveshark XBMC', 'No Grooveshark playlists found.')
301 self.categories()
302 else:
303 self.categories()
304
305 # Search for artists albums
3d95dfcb 306 def searchArtistsAlbums(self, artistName = None):
307 if artistName == None or artistName == searchArtistsAlbumsName:
f95afae7 308 query = self._get_keyboard(default="", heading="Search for artist's albums powered by Grooveshark")
99f72740 309 else:
310 query = artistName
86f629ea 311 if (query != ''):
312 artists = groovesharkApi.getArtistSearchResults(query, limit = self.artistsearchlimit)
313 if (len(artists) > 0):
314 artist = artists[0]
315 artistID = artist[1]
316 xbmc.log("Found " + artist[0] + "...")
317 albums = groovesharkApi.getArtistAlbums(artistID, limit = self.albumsearchlimit)
318 if (len(albums) > 0):
052028f1 319 self._add_albums_directory(albums, artistID)
86f629ea 320 else:
321 dialog = xbmcgui.Dialog()
322 dialog.ok('Grooveshark XBMC', 'No matching albums.')
323 self.categories()
324 else:
325 dialog = xbmcgui.Dialog()
326 dialog.ok('Grooveshark XBMC', 'No matching artists.')
327 self.categories()
328 else:
329 self.categories()
330
e278f474 331 # Get my favorites
8817bb2e 332 def favorites(self):
36cc00d7 333 userid = self._get_login()
334 if (userid != 0):
7ce01be6 335 favorites = groovesharkApi.getUserFavoriteSongs()
36cc00d7 336 if (len(favorites) > 0):
052028f1 337 self._add_songs_directory(favorites, isFavorites=True)
36cc00d7 338 else:
339 dialog = xbmcgui.Dialog()
38df1fa5 340 dialog.ok('Grooveshark XBMC', 'You have no favorites.')
36cc00d7 341 self.categories()
8817bb2e 342
e278f474 343 # Get popular songs
36cc00d7 344 def popularSongs(self):
7ce01be6 345 popular = groovesharkApi.getPopularSongsToday(limit = self.songsearchlimit)
8817bb2e 346 if (len(popular) > 0):
6ae708d0 347 self._add_songs_directory(popular)
8817bb2e 348 else:
349 dialog = xbmcgui.Dialog()
38df1fa5 350 dialog.ok('Grooveshark XBMC', 'No popular songs.')
8817bb2e 351 self.categories()
36cc00d7 352
e278f474 353 # Get my playlists
8817bb2e 354 def playlists(self):
355 userid = self._get_login()
356 if (userid != 0):
7ce01be6 357 playlists = groovesharkApi.getUserPlaylists()
8817bb2e 358 if (len(playlists) > 0):
6ae708d0 359 self._add_playlists_directory(playlists)
8817bb2e 360 else:
361 dialog = xbmcgui.Dialog()
38df1fa5 362 dialog.ok('Grooveshark XBMC', 'You have no Grooveshark playlists.')
8817bb2e 363 self.categories()
7ea6f166 364 else:
365 dialog = xbmcgui.Dialog()
38df1fa5 366 dialog.ok('Grooveshark XBMC', 'You must be logged in ', ' to see your Grooveshark playlists.')
7ea6f166 367
e278f474 368 # Make songs a favorite
8817bb2e 369 def favorite(self, songid):
370 userid = self._get_login()
371 if (userid != 0):
406ab447 372 xbmc.log("Favorite song: " + str(songid))
7ce01be6 373 groovesharkApi.addUserFavoriteSong(songID = songid)
38df1fa5 374 xbmc.executebuiltin('XBMC.Notification(Grooveshark XBMC, Added to favorites, 1000, ' + thumbDef + ')')
8817bb2e 375 else:
376 dialog = xbmcgui.Dialog()
38df1fa5 377 dialog.ok('Grooveshark XBMC', 'You must be logged in', 'to add favorites.')
052028f1 378
379 # Remove song from favorites
380 def unfavorite(self, songid, prevMode=0):
f95afae7 381 userid = self._get_login()
052028f1 382 if (userid != 0):
383 xbmc.log("Unfavorite song: " + str(songid) + ', previous mode was ' + str(prevMode))
f95afae7 384 groovesharkApi.removeUserFavoriteSongs(songIDs = songid)
052028f1 385 xbmc.executebuiltin('XBMC.Notification(Grooveshark XBMC, Removed from Grooveshark favorites, 1000, ' + thumbDef + ')')
386 # Refresh to remove item from directory
387 if (int(prevMode) == MODE_FAVORITES):
388 xbmc.executebuiltin("Container.Refresh(" + favoritesUrl + ")")
389 else:
390 dialog = xbmcgui.Dialog()
391 dialog.ok('Grooveshark XBMC', 'You must be logged in', 'to remove Grooveshark favorites.')
392
e278f474 393
394 # Show selected album
36cc00d7 395 def album(self, albumid):
7ce01be6 396 album = groovesharkApi.getAlbumSongs(albumid, limit = self.songsearchlimit)
86f629ea 397 self._add_songs_directory(album, trackLabelFormat=NAME_ALBUM_ARTIST_LABEL)
e278f474 398
399 # Show selected artist
8817bb2e 400 def artist(self, artistid):
7ce01be6 401 albums = groovesharkApi.getArtistAlbums(artistid, limit = self.albumsearchlimit)
052028f1 402 self._add_albums_directory(albums, artistid)
8817bb2e 403
e278f474 404 # Show selected playlist
e6f8730b 405 def playlist(self, playlistid, playlistname):
8817bb2e 406 userid = self._get_login()
407 if (userid != 0):
e6f8730b 408 songs = groovesharkApi.getPlaylistSongs(playlistid)
052028f1 409 self._add_songs_directory(songs, trackLabelFormat=NAME_ALBUM_ARTIST_LABEL, playlistid=playlistid, playlistname=playlistname)
8817bb2e 410 else:
411 dialog = xbmcgui.Dialog()
38df1fa5 412 dialog.ok('Grooveshark XBMC', 'You must be logged in', 'to get Grooveshark playlists.')
8817bb2e 413
97289139 414 # Show popular songs of the artist
415 def artistPopularSongs(self):
416 query = self._get_keyboard(default="", heading="Artist")
417 if (query != ''):
418 artists = groovesharkApi.getArtistSearchResults(query, limit = self.artistsearchlimit)
419 if (len(artists) > 0):
420 artist = artists[0]
421 artistID = artist[1]
422 xbmc.log("Found " + artist[0] + "...")
423 songs = groovesharkApi.getArtistPopularSongs(artistID, limit = self.songsearchlimit)
424 if (len(songs) > 0):
425 self._add_songs_directory(songs, trackLabelFormat=NAME_ALBUM_ARTIST_LABEL)
426 else:
427 dialog = xbmcgui.Dialog()
428 dialog.ok('Grooveshark XBMC', 'No popular songs.')
429 self.categories()
430 else:
431 dialog = xbmcgui.Dialog()
432 dialog.ok('Grooveshark XBMC', 'No matching artists.')
433 self.categories()
434 else:
435 self.categories()
436
e278f474 437 # Play a song
6ae708d0 438 def playSong(self, item):
f95afae7 439 global playTimer
440 global player
441 if item != None:
442 songid = item.getProperty('songid')
443 stream = groovesharkApi.getSubscriberStreamKey(songid)
444 url = stream['url']
445 item.setPath(url)
446 xbmc.log("Grooveshark playing: " + url)
7ce01be6 447 xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), succeeded=True, listitem=item)
f95afae7 448 # Wait for play then start time
449 seconds = 0
450 while seconds < STREAM_MARKING_TIME:
451 try:
452 if player.isPlayingAudio() == True:
453 if playTimer != None:
454 playTimer.cancel()
455 songMarkTime = 0
456 duration = int(item.getProperty('duration'))
457 playTimer = PlayTimer(1, markSong, duration, [songid, duration])
458 playTimer.start()
459 break
460 except: pass
461 time.sleep(1)
462 seconds = seconds + 1
7ce01be6 463 else:
cbb0985e 464 xbmc.executebuiltin('XBMC.Notification(Grooveshark XBMC, Unable to play song, 1000, ' + thumbDef + ')')
f95afae7 465
e278f474 466 # Make a song directory item
86f629ea 467 def songItem(self, songid, name, album, artist, coverart, trackLabelFormat=ARTIST_ALBUM_NAME_LABEL):
7ce01be6 468 songImg = self._get_icon(coverart, 'song-' + str(songid) + "-image")
2cb26bea 469 if int(trackLabelFormat) == NAME_ALBUM_ARTIST_LABEL:
86f629ea 470 trackLabel = name + " - " + album + " - " + artist
471 else:
472 trackLabel = artist + " - " + album + " - " + name
f95afae7 473 duration = self._getSongDuration(songid)
86f629ea 474 item = xbmcgui.ListItem(label = trackLabel, thumbnailImage=songImg, iconImage=songImg)
f95afae7 475 item.setInfo( type="music", infoLabels={ "title": name, "album": album, "artist": artist, "duration": duration} )
2254a6b5 476 item.setProperty('mimetype', 'audio/mpeg')
477 item.setProperty("IsPlayable", "true")
7ce01be6 478 item.setProperty('songid', str(songid))
479 item.setProperty('coverart', songImg)
480 item.setProperty('title', name)
481 item.setProperty('album', album)
482 item.setProperty('artist', artist)
f95afae7 483 item.setProperty('duration', str(duration))
7ce01be6 484
6ae708d0 485 return item
2254a6b5 486
97289139 487 # Next page of songs
052028f1 488 def songPage(self, page, trackLabelFormat, playlistid = 0, playlistname = ''):
489 self._add_songs_directory([], trackLabelFormat, page, playlistid = playlistid, playlistname = playlistname)
490
491 # Make a playlist from an album
492 def makePlaylist(self, albumid, name):
f95afae7 493 userid = self._get_login()
052028f1 494 if (userid != 0):
495 re.split(' - ',name,1)
496 nameTokens = re.split(' - ',name,1) # suggested name
497 name = self._get_keyboard(default=nameTokens[0], heading="Grooveshark playlist name")
498 if name != '':
499 album = groovesharkApi.getAlbumSongs(albumid, limit = self.songsearchlimit)
500 songids = []
501 for song in album:
502 songids.append(song[1])
f95afae7 503 if groovesharkApi.createPlaylist(name, songids) == 0:
052028f1 504 dialog = xbmcgui.Dialog()
505 dialog.ok('Grooveshark XBMC', 'Cannot create Grooveshark playlist ', name)
506 else:
507 xbmc.executebuiltin('XBMC.Notification(Grooveshark XBMC, Grooveshark playlist created, 1000, ' + thumbDef + ')')
508 else:
509 dialog = xbmcgui.Dialog()
510 dialog.ok('Grooveshark XBMC', 'You must be logged in ', ' to create a Grooveshark playlist.')
511
512 # Rename a playlist
513 def renamePlaylist(self, playlistid, name):
f95afae7 514 userid = self._get_login()
052028f1 515 if (userid != 0):
516 newname = self._get_keyboard(default=name, heading="Grooveshark playlist name")
517 if newname == '':
518 return
f95afae7 519 elif groovesharkApi.playlistRename(playlistid, newname) == 0:
052028f1 520 dialog = xbmcgui.Dialog()
521 dialog.ok('Grooveshark XBMC', 'Cannot rename Grooveshark playlist ', name)
522 else:
523 # Refresh to show new item name
524 xbmc.executebuiltin("Container.Refresh")
525 else:
526 dialog = xbmcgui.Dialog()
527 dialog.ok('Grooveshark XBMC', 'You must be logged in ', ' to rename a Grooveshark playlist.')
528
529 # Remove a playlist
530 def removePlaylist(self, playlistid, name):
531 dialog = xbmcgui.Dialog()
532 if dialog.yesno('Grooveshark XBMC', name, 'Delete this Grooveshark playlist?') == True:
f95afae7 533 userid = self._get_login()
052028f1 534 if (userid != 0):
f95afae7 535 if groovesharkApi.playlistDelete(playlistid) == 0:
052028f1 536 dialog = xbmcgui.Dialog()
537 dialog.ok('Grooveshark XBMC', 'Cannot remove Grooveshark playlist ', name)
538 else:
539 # Refresh to remove item from directory
540 xbmc.executebuiltin("Container.Refresh(" + playlistsUrl + ")")
541 else:
542 dialog = xbmcgui.Dialog()
543 dialog.ok('Grooveshark XBMC', 'You must be logged in ', ' to delete a Grooveshark playlist.')
544
545 # Add song to playlist
546 def addPlaylistSong(self, songid):
f95afae7 547 userid = self._get_login()
052028f1 548 if (userid != 0):
549 playlists = groovesharkApi.getUserPlaylists()
550 if (len(playlists) > 0):
551 ret = 0
552 # Select the playlist
553 playlistSelect = GroovesharkPlaylistSelect(items=playlists)
554 playlistSelect.setFocus(playlistSelect.playlistControl)
555 playlistSelect.doModal()
556 i = playlistSelect.selected
557 del playlistSelect
558 if i > -1:
559 # Add a new playlist
560 if i >= len(playlists):
561 name = self._get_keyboard(default='', heading="Grooveshark playlist name")
562 if name != '':
563 songIds = []
564 songIds.append(songid)
f95afae7 565 if groovesharkApi.createPlaylist(name, songIds) == 0:
052028f1 566 dialog = xbmcgui.Dialog()
567 dialog.ok('Grooveshark XBMC', 'Cannot create Grooveshark playlist ', name)
568 else:
569 xbmc.executebuiltin('XBMC.Notification(Grooveshark XBMC, Grooveshark playlist created, 1000, ' + thumbDef + ')')
570 # Existing playlist
571 else:
572 playlist = playlists[i]
573 playlistid = playlist[1]
574 xbmc.log("Add song " + str(songid) + " to playlist " + str(playlistid))
f95afae7 575 songIDs=[]
576 songs = groovesharkApi.getPlaylistSongs(playlistid)
577 for song in songs:
578 songIDs.append(song[1])
579 songIDs.append(songid)
580 ret = groovesharkApi.setPlaylistSongs(playlistid, songIDs)
581 if ret == False:
052028f1 582 dialog = xbmcgui.Dialog()
583 dialog.ok('Grooveshark XBMC', 'Cannot add to playlist ')
584 else:
585 xbmc.executebuiltin('XBMC.Notification(Grooveshark XBMC, Added song to Grooveshark playlist, 1000, ' + thumbDef + ')')
586 else:
587 dialog = xbmcgui.Dialog()
588 dialog.ok('Grooveshark XBMC', 'You have no Grooveshark playlists.')
589 self.categories()
590 else:
591 dialog = xbmcgui.Dialog()
592 dialog.ok('Grooveshark XBMC', 'You must be logged in', 'to add a song to a Grooveshark playlist.')
593
594 # Remove song from playlist
f95afae7 595 def removePlaylistSong(self, playlistid, playlistname, songid):
e6f8730b 596 dialog = xbmcgui.Dialog()
597 if dialog.yesno('Grooveshark XBMC', 'Delete this song from', 'the Grooveshark playlist?') == True:
f95afae7 598 userid = self._get_login()
052028f1 599 if (userid != 0):
f95afae7 600 songs = groovesharkApi.getPlaylistSongs(playlistID)
601 songIDs=[]
602 for song in songs:
603 if (song[1] != songid):
604 songIDs.append(song[1])
605 ret = groovesharkApi.setPlaylistSongs(playlistID, songIDs)
606 if ret == False:
052028f1 607 dialog = xbmcgui.Dialog()
608 dialog.ok('Grooveshark XBMC', 'Failed to remove', 'song from Grooveshark playlist.')
609 else:
610 # Refresh to remove item from directory
611 xbmc.executebuiltin('XBMC.Notification(Grooveshark XBMC, Removed song from Grooveshark playlist, 1000, ' + thumbDef + ')')
e6f8730b 612 xbmc.executebuiltin("Container.Update(" + playlistUrl + "&id="+str(playlistid) + "&name=" + playlistname + ")")
052028f1 613 else:
614 dialog = xbmcgui.Dialog()
615 dialog.ok('Grooveshark XBMC', 'You must be logged in', 'to delete a song from a Grooveshark playlist.')
616
617 # Find similar artists to searched artist
618 def similarArtists(self, artistId):
f95afae7 619 similar = groovesharkApi.getSimilarArtists(artistId, limit = self.artistsearchlimit)
052028f1 620 if (len(similar) > 0):
621 self._add_artists_directory(similar)
622 else:
623 dialog = xbmcgui.Dialog()
624 dialog.ok('Grooveshark XBMC', 'No similar artists.')
625 self.categories()
97289139 626
e278f474 627 # Get keyboard input
8817bb2e 628 def _get_keyboard(self, default="", heading="", hidden=False):
3cfead3c 629 kb = xbmc.Keyboard(default, heading, hidden)
630 kb.doModal()
631 if (kb.isConfirmed()):
632 return unicode(kb.getText(), "utf-8")
633 return ''
8817bb2e 634
e278f474 635 # Login to grooveshark
f95afae7 636 def _get_login(self):
2254a6b5 637 if (self.username == "" or self.password == ""):
8817bb2e 638 dialog = xbmcgui.Dialog()
38df1fa5 639 dialog.ok('Grooveshark XBMC', 'Unable to login.', 'Check username and password in settings.')
8817bb2e 640 return 0
641 else:
38df1fa5 642 if self.userid == 0:
f95afae7 643 uid = groovesharkApi.login(self.username, self.password)
38df1fa5 644 if (uid != 0):
38df1fa5 645 return uid
8817bb2e 646 else:
647 dialog = xbmcgui.Dialog()
38df1fa5 648 dialog.ok('Grooveshark XBMC', 'Unable to login.', 'Check username and password in settings.')
8817bb2e 649 return 0
650
e278f474 651 # Get a song directory item
86f629ea 652 def _get_song_item(self, song, trackLabelFormat):
6ae708d0 653 name = song[0]
7ce01be6 654 songid = song[1]
655 album = song[2]
656 artist = song[4]
657 coverart = song[6]
86f629ea 658 return self.songItem(songid, name, album, artist, coverart, trackLabelFormat)
6ae708d0 659
660 # File download
7ce01be6 661 def _get_icon(self, url, songid):
662 if url != 'None':
663 localThumb = os.path.join(xbmc.translatePath(os.path.join(thumbDir, str(songid)))) + '.tbn'
664 try:
665 if os.path.isfile(localThumb) == False:
666 loc = urllib.URLopener()
667 loc.retrieve(url, localThumb)
668 except:
669 shutil.copy2(thumbDef, localThumb)
670 return os.path.join(os.path.join(thumbDir, str(songid))) + '.tbn'
671 else:
672 return thumbDef
e278f474 673
674 # Add songs to directory
052028f1 675 def _add_songs_directory(self, songs, trackLabelFormat=ARTIST_ALBUM_NAME_LABEL, page=0, playlistid=0, playlistname='', isFavorites=False):
97289139 676
677 totalSongs = len(songs)
678 page = int(page)
679
680 # No pages needed
681 if page == 0 and totalSongs <= self.songspagelimit:
682 xbmc.log("Found " + str(totalSongs) + " songs...")
683 # Pages
684 else:
685 # Cache all songs
686 if page == 0:
687 self._setSavedSongs(songs)
688 else:
689 songs = self._getSavedSongs()
690 totalSongs = len(songs)
691
692 if totalSongs > 0:
693 start = page * self.songspagelimit
694 end = start + self.songspagelimit
695 songs = songs[start:end]
696
052028f1 697 id = 0
97289139 698 for song in songs:
86f629ea 699 item = self._get_song_item(song, trackLabelFormat)
7ce01be6 700 coverart = item.getProperty('coverart')
6ae708d0 701 songname = song[0]
702 songid = song[1]
7ce01be6 703 songalbum = song[2]
704 songartist = song[4]
f95afae7 705
706
e278f474 707 u=sys.argv[0]+"?mode="+str(MODE_SONG)+"&name="+urllib.quote_plus(songname)+"&id="+str(songid) \
6ae708d0 708 +"&album="+urllib.quote_plus(songalbum) \
709 +"&artist="+urllib.quote_plus(songartist) \
7ce01be6 710 +"&coverart="+urllib.quote_plus(coverart)
e278f474 711 fav=sys.argv[0]+"?mode="+str(MODE_FAVORITE)+"&name="+urllib.quote_plus(songname)+"&id="+str(songid)
052028f1 712 unfav=sys.argv[0]+"?mode="+str(MODE_UNFAVORITE)+"&name="+urllib.quote_plus(songname)+"&id="+str(songid)+"&prevmode="
6ae708d0 713 menuItems = []
052028f1 714 if isFavorites == True:
715 unfav = unfav +str(MODE_FAVORITES)
716 else:
717 menuItems.append(("Grooveshark favorite", "XBMC.RunPlugin("+fav+")"))
f95afae7 718 menuItems.append(("Not Grooveshark favorite", "XBMC.RunPlugin("+unfav+")"))
719 if playlistid > 0:
720 rmplaylstsong=sys.argv[0]+"?playlistid="+str(playlistid)+"&id="+str(songid)+"&mode="+str(MODE_REMOVE_PLAYLIST_SONG)+"&name="+playlistname
721 menuItems.append(("Remove from Grooveshark playlist", "XBMC.RunPlugin("+rmplaylstsong+")"))
722 else:
723 addplaylstsong=sys.argv[0]+"?id="+str(songid)+"&mode="+str(MODE_ADD_PLAYLIST_SONG)
724 menuItems.append(("Add to Grooveshark playlist", "XBMC.RunPlugin("+addplaylstsong+")"))
6ae708d0 725 item.addContextMenuItems(menuItems, replaceItems=False)
97289139 726 xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=item,isFolder=False, totalItems=len(songs))
052028f1 727 id = id + 1
97289139 728
729 page = page + 1
730 if totalSongs > page * self.songspagelimit:
052028f1 731 u=sys.argv[0]+"?mode="+str(MODE_SONG_PAGE)+"&id=playlistid"+"&page="+str(page)+"&label="+str(trackLabelFormat)+"&name="+playlistname
97289139 732 self._add_dir('More songs...', u, MODE_SONG_PAGE, self.songImg, 0, totalSongs - (page * self.songspagelimit))
cb06c186 733
8817bb2e 734 xbmcplugin.setContent(self._handle, 'songs')
31731635 735 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
e278f474 736
737 # Add albums to directory
052028f1 738 def _add_albums_directory(self, albums, artistid=0):
31731635 739 n = len(albums)
740 xbmc.log("Found " + str(n) + " albums...")
8817bb2e 741 i = 0
31731635 742 while i < n:
8817bb2e 743 album = albums[i]
744 albumArtistName = album[0]
745 albumName = album[2]
746 albumID = album[3]
2254a6b5 747 albumImage = self._get_icon(album[4], 'album-' + str(albumID))
31731635 748 self._add_dir(albumName + " - " + albumArtistName, '', MODE_ALBUM, albumImage, albumID, n)
8817bb2e 749 i = i + 1
f95afae7 750 # Not supported by key
751 #if artistid > 0:
752 # self._add_dir('Similar artists...', '', MODE_SIMILAR_ARTISTS, self.artistImg, artistid)
8817bb2e 753 xbmcplugin.setContent(self._handle, 'albums')
754 xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_ALBUM_IGNORE_THE)
31731635 755 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
e278f474 756
757 # Add artists to directory
6ae708d0 758 def _add_artists_directory(self, artists):
31731635 759 n = len(artists)
760 xbmc.log("Found " + str(n) + " artists...")
8817bb2e 761 i = 0
31731635 762 while i < n:
8817bb2e 763 artist = artists[i]
764 artistName = artist[0]
765 artistID = artist[1]
31731635 766 self._add_dir(artistName, '', MODE_ARTIST, self.artistImg, artistID, n)
8817bb2e 767 i = i + 1
768 xbmcplugin.setContent(self._handle, 'artists')
769 xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_ARTIST_IGNORE_THE)
31731635 770 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
e278f474 771
772 # Add playlists to directory
6ae708d0 773 def _add_playlists_directory(self, playlists):
31731635 774 n = len(playlists)
775 xbmc.log("Found " + str(n) + " playlists...")
8817bb2e 776 i = 0
31731635 777 while i < n:
8817bb2e 778 playlist = playlists[i]
779 playlistName = playlist[0]
780 playlistID = playlist[1]
86f629ea 781 dir = self._add_dir(playlistName, '', MODE_PLAYLIST, self.playlistImg, playlistID, n)
8817bb2e 782 i = i + 1
783 xbmcplugin.setContent(self._handle, 'files')
86f629ea 784 xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_LABEL)
31731635 785 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
3fcef5ba 786
e278f474 787 # Add whatever directory
31731635 788 def _add_dir(self, name, url, mode, iconimage, id, items=1):
052028f1 789
97289139 790 if url == '':
791 u=sys.argv[0]+"?mode="+str(mode)+"&name="+urllib.quote_plus(name)+"&id="+str(id)
792 else:
793 u = url
8817bb2e 794 dir=xbmcgui.ListItem(name, iconImage=iconimage, thumbnailImage=iconimage)
b738088f 795 dir.setInfo( type="Music", infoLabels={ "title": name } )
052028f1 796
797 # Custom menu items
f95afae7 798 menuItems = []
799 if mode == MODE_ALBUM:
800 mkplaylst=sys.argv[0]+"?mode="+str(MODE_MAKE_PLAYLIST)+"&name="+name+"&id="+str(id)
801 menuItems.append(("Make Grooveshark playlist", "XBMC.RunPlugin("+mkplaylst+")"))
802 # Broken rename/delete are broken in API
803 if mode == MODE_PLAYLIST:
804 rmplaylst=sys.argv[0]+"?mode="+str(MODE_REMOVE_PLAYLIST)+"&name="+urllib.quote_plus(name)+"&id="+str(id)
805 menuItems.append(("Delete Grooveshark playlist", "XBMC.RunPlugin("+rmplaylst+")"))
806 mvplaylst=sys.argv[0]+"?mode="+str(MODE_RENAME_PLAYLIST)+"&name="+urllib.quote_plus(name)+"&id="+str(id)
807 menuItems.append(("Rename Grooveshark playlist", "XBMC.RunPlugin("+mvplaylst+")"))
808 dir.addContextMenuItems(menuItems, replaceItems=False)
052028f1 809
31731635 810 return xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=dir,isFolder=True, totalItems=items)
97289139 811
812 def _getSavedSongs(self):
813 path = os.path.join(cacheDir, 'songs.dmp')
814 try:
815 f = open(path, 'rb')
816 songs = pickle.load(f)
817 f.close()
818 except:
819 songs = []
820 pass
821 return songs
822
823 def _setSavedSongs(self, songs):
824 try:
825 # Create the 'data' directory if it doesn't exist.
826 if not os.path.exists(cacheDir):
827 os.makedirs(cacheDir)
828 path = os.path.join(cacheDir, 'songs.dmp')
829 f = open(path, 'wb')
830 pickle.dump(songs, f, protocol=pickle.HIGHEST_PROTOCOL)
831 f.close()
832 except:
833 xbmc.log("An error occurred saving songs")
834 pass
f95afae7 835
836 def _getSongDuration(self, songid):
837 path = os.path.join(cacheDir, 'duration.dmp')
838 id = int(songid)
839 durations = []
840 duration = -1
841
842 # Try cache first
843 try:
844 f = open(path, 'rb')
845 durations = pickle.load(f)
846 for song in durations:
847 if song[0] == id:
848 duration = song[1]
849 f.close()
850 except:
851 pass
852
853 if duration < 0:
854 stream = groovesharkApi.getSubscriberStreamKey(songid)
855 usecs = stream['uSecs']
856 if usecs < 60000000:
857 usecs = usecs * 10 # Some durations are 10x to small
858 duration = usecs / 1000000
859 song = [id, duration]
860 durations.append(song)
861 self._setSongDuration(durations)
862
863 return duration
864
865 def _setSongDuration(self, durations):
866 try:
867 # Create the 'data' directory if it doesn't exist.
868 if not os.path.exists(cacheDir):
869 os.makedirs(cacheDir)
870 path = os.path.join(cacheDir, 'duration.dmp')
871 f = open(path, 'wb')
872 pickle.dump(durations, f, protocol=pickle.HIGHEST_PROTOCOL)
873 f.close()
874 except:
875 xbmc.log("An error occurred saving durations")
876 pass
877
97289139 878
e278f474 879# Parse URL parameters
8817bb2e 880def get_params():
881 param=[]
882 paramstring=sys.argv[2]
e278f474 883 xbmc.log(paramstring)
8817bb2e 884 if len(paramstring)>=2:
885 params=sys.argv[2]
886 cleanedparams=params.replace('?','')
887 if (params[len(params)-1]=='/'):
888 params=params[0:len(params)-2]
889 pairsofparams=cleanedparams.split('&')
890 param={}
891 for i in range(len(pairsofparams)):
892 splitparams={}
893 splitparams=pairsofparams[i].split('=')
894 if (len(splitparams))==2:
895 param[splitparams[0]]=splitparams[1]
8817bb2e 896 return param
897
e278f474 898# Main
8817bb2e 899grooveshark = Groveshark();
e278f474 900
8817bb2e 901params=get_params()
8817bb2e 902mode=None
8817bb2e 903try: mode=int(params["mode"])
904except: pass
7ce01be6 905id=0
906try: id=int(params["id"])
907except: pass
052028f1 908name = None
909try: name=urllib.unquote_plus(params["name"])
910except: pass
e278f474 911
912# Call function for URL
8817bb2e 913if mode==None:
914 grooveshark.categories()
915
916elif mode==MODE_SEARCH_SONGS:
917 grooveshark.searchSongs()
918
919elif mode==MODE_SEARCH_ALBUMS:
920 grooveshark.searchAlbums()
921
922elif mode==MODE_SEARCH_ARTISTS:
923 grooveshark.searchArtists()
86f629ea 924
925elif mode==MODE_SEARCH_ARTISTS_ALBUMS:
a2e75b14 926 grooveshark.searchArtistsAlbums(name)
86f629ea 927
928elif mode==MODE_SEARCH_PLAYLISTS:
929 grooveshark.searchPlaylists()
8817bb2e 930
36cc00d7 931elif mode==MODE_POPULAR_SONGS:
932 grooveshark.popularSongs()
97289139 933
934elif mode==MODE_ARTIST_POPULAR:
a2e75b14 935 grooveshark.artistPopularSongs()
8817bb2e 936
8817bb2e 937elif mode==MODE_FAVORITES:
938 grooveshark.favorites()
939
e278f474 940elif mode==MODE_PLAYLISTS:
941 grooveshark.playlists()
97289139 942
943elif mode==MODE_SONG_PAGE:
944 try: page=urllib.unquote_plus(params["page"])
945 except: pass
946 try: label=urllib.unquote_plus(params["label"])
947 except: pass
052028f1 948 grooveshark.songPage(page, label, id, name)
e278f474 949
8817bb2e 950elif mode==MODE_SONG:
8817bb2e 951 try: album=urllib.unquote_plus(params["album"])
952 except: pass
953 try: artist=urllib.unquote_plus(params["artist"])
954 except: pass
7ce01be6 955 try: coverart=urllib.unquote_plus(params["coverart"])
8817bb2e 956 except: pass
7ce01be6 957 song = grooveshark.songItem(id, name, album, artist, coverart)
3fcef5ba 958 grooveshark.playSong(song)
8817bb2e 959
960elif mode==MODE_ARTIST:
4be42357 961 grooveshark.artist(id)
8817bb2e 962
963elif mode==MODE_ALBUM:
4be42357 964 grooveshark.album(id)
8817bb2e 965
966elif mode==MODE_PLAYLIST:
e6f8730b 967 grooveshark.playlist(id, name)
8817bb2e 968
969elif mode==MODE_FAVORITE:
4be42357 970 grooveshark.favorite(id)
97289139 971
052028f1 972elif mode==MODE_UNFAVORITE:
973 try: prevMode=int(urllib.unquote_plus(params["prevmode"]))
974 except:
975 prevMode = 0
976 grooveshark.unfavorite(id, prevMode)
977
978elif mode==MODE_SIMILAR_ARTISTS:
979 grooveshark.similarArtists(id)
980
981elif mode==MODE_MAKE_PLAYLIST:
982 grooveshark.makePlaylist(id, name)
983
984elif mode==MODE_REMOVE_PLAYLIST:
985 grooveshark.removePlaylist(id, name)
986
987elif mode==MODE_RENAME_PLAYLIST:
988 grooveshark.renamePlaylist(id, name)
989
990elif mode==MODE_REMOVE_PLAYLIST_SONG:
991 try: playlistID=urllib.unquote_plus(params["playlistid"])
992 except: pass
993 grooveshark.removePlaylistSong(playlistID, name, id)
994
995elif mode==MODE_ADD_PLAYLIST_SONG:
996 grooveshark.addPlaylistSong(id)
997
e278f474 998if mode < MODE_SONG:
8817bb2e 999 xbmcplugin.endOfDirectory(int(sys.argv[1]))