attempt to show songs for artist if not albums are found
[clinton/xbmc-groove.git] / default.py
1 # Copyright 2011 Stephen Denham
2
3 # This file is part of xbmc-groove.
4 #
5 # xbmc-groove is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # xbmc-groove is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with xbmc-groove. If not, see <http://www.gnu.org/licenses/>.
17
18
19 import urllib, urllib2, sys, os, shutil, re, pickle, time, traceback, xbmcaddon, xbmcplugin, xbmcgui, xbmc
20
21 __addon__ = xbmcaddon.Addon('plugin.audio.groove')
22 __addonname__ = __addon__.getAddonInfo('name')
23 __cwd__ = __addon__.getAddonInfo('path')
24 __author__ = __addon__.getAddonInfo('author')
25 __version__ = __addon__.getAddonInfo('version')
26 __language__ = __addon__.getLocalizedString
27 __debugging__ = __addon__.getSetting('debug')
28
29 # Directory listings must be < MODE_SONG
30 MODE_SEARCH_SONGS = 1
31 MODE_SEARCH_ALBUMS = 2
32 MODE_SEARCH_ARTISTS = 3
33 MODE_SEARCH_ARTISTS_ALBUMS = 4
34 MODE_SEARCH_PLAYLISTS = 5
35 MODE_ARTIST_POPULAR = 6
36 MODE_POPULAR_SONGS = 7
37 MODE_FAVORITES = 8
38 MODE_PLAYLISTS = 9
39 MODE_ALBUM = 10
40 MODE_ARTIST = 11
41 MODE_PLAYLIST = 12
42 MODE_SONG_PAGE = 13
43 MODE_SIMILAR_ARTISTS = 14
44 MODE_ARTIST_POPULAR_FROM_ALBUMS = 15
45 MODE_SONG = 150
46
47 MODE_FAVORITE = 160
48 MODE_UNFAVORITE = 170
49 MODE_MAKE_PLAYLIST = 180
50 MODE_REMOVE_PLAYLIST = 190
51 MODE_RENAME_PLAYLIST = 200
52 MODE_REMOVE_PLAYLIST_SONG = 210
53 MODE_ADD_PLAYLIST_SONG = 220
54
55 ACTION_MOVE_LEFT = 1
56 ACTION_MOVE_UP = 3
57 ACTION_MOVE_DOWN = 4
58 ACTION_PAGE_UP = 5
59 ACTION_PAGE_DOWN = 6
60 ACTION_SELECT_ITEM = 7
61 ACTION_PREVIOUS_MENU = 10
62
63 # Formats for track labels
64 ARTIST_ALBUM_NAME_LABEL = 0
65 NAME_ALBUM_ARTIST_LABEL = 1
66
67 # Stream marking time (seconds)
68 STREAM_MARKING_TIME = 30
69
70 # Timeout
71 STREAM_TIMEOUT = 30
72
73 songMarkTime = 0
74 player = xbmc.Player()
75 playTimer = None
76
77 baseDir = __cwd__
78 resDir = xbmc.translatePath(os.path.join(baseDir, 'resources'))
79 libDir = xbmc.translatePath(os.path.join(resDir, 'lib'))
80 imgDir = xbmc.translatePath(os.path.join(resDir, 'img'))
81 cacheDir = os.path.join(xbmc.translatePath('special://masterprofile/addon_data/'), os.path.basename(baseDir))
82 tempDir = xbmc.translatePath('special://temp')
83 thumbDirName = 'thumb'
84 thumbDir = os.path.join(xbmc.translatePath('special://masterprofile/addon_data/'), os.path.basename(baseDir), thumbDirName)
85
86 baseModeUrl = 'plugin://plugin.audio.groove/'
87 playlistUrl = baseModeUrl + '?mode=' + str(MODE_PLAYLIST)
88 playlistsUrl = baseModeUrl + '?mode=' + str(MODE_PLAYLISTS)
89 favoritesUrl = baseModeUrl + '?mode=' + str(MODE_FAVORITES)
90
91 searchArtistsAlbumsName = __language__(30006)
92
93 thumbDef = os.path.join(imgDir, 'default.tbn')
94 listBackground = os.path.join(imgDir, 'listbackground.png')
95
96 sys.path.append (libDir)
97 from GroovesharkAPI import GrooveAPI
98 from threading import Event, Thread
99
100 if __debugging__ == 'true':
101 __debugging__ = True
102 else:
103 __debugging__ = False
104
105 try:
106 groovesharkApi = GrooveAPI(__debugging__, tempDir)
107 if groovesharkApi.pingService() != True:
108 raise StandardError(__language__(30007))
109 except:
110 print "Exception on initialisation"
111 print '-'*60
112 traceback.print_exc()
113 print '-'*60
114 dialog = xbmcgui.Dialog(__language__(30008),__language__(30009),__language__(30010))
115 dialog.ok(__language__(30008),__language__(30009))
116 sys.exit(-1)
117
118 # Mark song as playing or played
119 def markSong(songid, duration, streamKey, streamServerID):
120 global songMarkTime
121 global playTimer
122 global player
123 if player.isPlayingAudio():
124 tNow = player.getTime()
125 if tNow >= STREAM_MARKING_TIME and songMarkTime == 0:
126 groovesharkApi.markStreamKeyOver30Secs(streamKey, streamServerID)
127 songMarkTime = tNow
128 elif duration > tNow and duration - tNow < 2 and songMarkTime >= STREAM_MARKING_TIME:
129 playTimer.cancel()
130 songMarkTime = 0
131 groovesharkApi.markSongComplete(songid, streamKey, streamServerID)
132 else:
133 playTimer.cancel()
134 songMarkTime = 0
135
136 class _Info:
137 def __init__( self, *args, **kwargs ):
138 self.__dict__.update( kwargs )
139
140 # Window dialog to select a grooveshark playlist
141 class GroovesharkPlaylistSelect(xbmcgui.WindowDialog):
142
143 def __init__(self, items=[]):
144 gap = int(self.getHeight()/100)
145 w = int(self.getWidth()*0.5)
146 h = self.getHeight()-30*gap
147 rw = self.getWidth()
148 rh = self.getHeight()
149 x = rw/2 - w/2
150 y = rh/2 -h/2
151
152 self.imgBg = xbmcgui.ControlImage(x+gap, 5*gap+y, w-2*gap, h-5*gap, listBackground)
153 self.addControl(self.imgBg)
154
155 self.playlistControl = xbmcgui.ControlList(2*gap+x, y+3*gap+30, w-4*gap, h-10*gap, textColor='0xFFFFFFFF', selectedColor='0xFFFF4242')
156 self.playlistControl.setItemHeight(50)
157 self.addControl(self.playlistControl)
158
159 self.lastPos = 0
160 self.isSelecting = False
161 self.selected = -1
162 listitems = []
163 for playlist in items:
164 listitems.append(xbmcgui.ListItem(playlist[0]))
165 listitems.append(xbmcgui.ListItem(__language__(30011)))
166 self.playlistControl.addItems(listitems)
167 self.setFocus(self.playlistControl)
168 self.playlistControl.selectItem(0)
169 item = self.playlistControl.getListItem(self.lastPos)
170 item.select(True)
171
172 # Highlight selected item
173 def setHighlight(self):
174 if self.isSelecting:
175 return
176 else:
177 self.isSelecting = True
178
179 pos = self.playlistControl.getSelectedPosition()
180 if pos >= 0:
181 item = self.playlistControl.getListItem(self.lastPos)
182 item.select(False)
183 item = self.playlistControl.getListItem(pos)
184 item.select(True)
185 self.lastPos = pos
186 self.isSelecting = False
187
188 # Control - select
189 def onControl(self, control):
190 if control == self.playlistControl:
191 self.selected = self.playlistControl.getSelectedPosition()
192 self.close()
193
194 # Action - close or up/down
195 def onAction(self, action):
196 if action == ACTION_PREVIOUS_MENU:
197 self.selected = -1
198 self.close()
199 elif action == ACTION_MOVE_UP or action == ACTION_MOVE_DOWN or action == ACTION_PAGE_UP or action == ACTION_PAGE_DOWN == 6:
200 self.setFocus(self.playlistControl)
201 self.setHighlight()
202
203
204 class PlayTimer(Thread):
205 # interval -- floating point number specifying the number of seconds to wait before executing function
206 # function -- the function (or callable object) to be executed
207
208 # iterations -- integer specifying the number of iterations to perform
209 # args -- list of positional arguments passed to function
210 # kwargs -- dictionary of keyword arguments passed to function
211
212 def __init__(self, interval, function, iterations=0, args=[], kwargs={}):
213 Thread.__init__(self)
214 self.interval = interval
215 self.function = function
216 self.iterations = iterations
217 self.args = args
218 self.kwargs = kwargs
219 self.finished = Event()
220
221 def run(self):
222 count = 0
223 while not self.finished.isSet() and (self.iterations <= 0 or count < self.iterations):
224 self.finished.wait(self.interval)
225 if not self.finished.isSet():
226 self.function(*self.args, **self.kwargs)
227 count += 1
228
229 def cancel(self):
230 self.finished.set()
231
232 def setIterations(self, iterations):
233 self.iterations = iterations
234
235
236 def getTime(self):
237 return self.iterations * self.interval
238
239
240 class Grooveshark:
241
242 albumImg = xbmc.translatePath(os.path.join(imgDir, 'album.png'))
243 artistImg = xbmc.translatePath(os.path.join(imgDir, 'artist.png'))
244 artistsAlbumsImg = xbmc.translatePath(os.path.join(imgDir, 'artistsalbums.png'))
245 favoritesImg = xbmc.translatePath(os.path.join(imgDir, 'favorites.png'))
246 playlistImg = xbmc.translatePath(os.path.join(imgDir, 'playlist.png'))
247 usersplaylistsImg = xbmc.translatePath(os.path.join(imgDir, 'usersplaylists.png'))
248 popularSongsImg = xbmc.translatePath(os.path.join(imgDir, 'popularSongs.png'))
249 popularSongsArtistImg = xbmc.translatePath(os.path.join(imgDir, 'popularSongsArtist.png'))
250 songImg = xbmc.translatePath(os.path.join(imgDir, 'song.png'))
251 defImg = xbmc.translatePath(os.path.join(imgDir, 'default.tbn'))
252 fanImg = xbmc.translatePath(os.path.join(baseDir, 'fanart.jpg'))
253
254 settings = xbmcaddon.Addon(id='plugin.audio.groove')
255 songsearchlimit = int(settings.getSetting('songsearchlimit'))
256 albumsearchlimit = int(settings.getSetting('albumsearchlimit'))
257 artistsearchlimit = int(settings.getSetting('artistsearchlimit'))
258 songspagelimit = int(settings.getSetting('songspagelimit'))
259 username = settings.getSetting('username')
260 password = settings.getSetting('password')
261
262 userid = 0
263
264 def __init__( self ):
265 self._handle = int(sys.argv[1])
266 if os.path.isdir(cacheDir) == False:
267 os.makedirs(cacheDir)
268 if __debugging__ :
269 xbmc.log(__language__(30012) + " " + cacheDir)
270 artDir = xbmc.translatePath(thumbDir)
271 if os.path.isdir(artDir) == False:
272 os.makedirs(artDir)
273 if __debugging__ :
274 xbmc.log(__language__(30012) + " " + artDir)
275
276 # Top-level menu
277 def categories(self):
278
279 self.userid = self._get_login()
280
281 # Setup
282 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
283
284 self._add_dir(__language__(30013), '', MODE_SEARCH_SONGS, self.songImg, 0)
285 self._add_dir(__language__(30014), '', MODE_SEARCH_ALBUMS, self.albumImg, 0)
286 self._add_dir(__language__(30015), '', MODE_SEARCH_ARTISTS, self.artistImg, 0)
287 self._add_dir(searchArtistsAlbumsName, '', MODE_SEARCH_ARTISTS_ALBUMS, self.artistsAlbumsImg, 0)
288 # Not supported by key
289 #self._add_dir("Search for user's playlists...", '', MODE_SEARCH_PLAYLISTS, self.usersplaylistsImg, 0)
290 self._add_dir(__language__(30016), '', MODE_ARTIST_POPULAR, self.popularSongsArtistImg, 0)
291 self._add_dir(__language__(30017), '', MODE_POPULAR_SONGS, self.popularSongsImg, 0)
292 if (self.userid != 0):
293 self._add_dir(__language__(30018), '', MODE_FAVORITES, self.favoritesImg, 0)
294 self._add_dir(__language__(30019), '', MODE_PLAYLISTS, self.playlistImg, 0)
295
296 # Search for songs
297 def searchSongs(self):
298 query = self._get_keyboard(default="", heading=__language__(30020))
299 if (query != ''):
300 songs = groovesharkApi.getSongSearchResults(query, limit = self.songsearchlimit)
301 if (len(songs) > 0):
302 self._add_songs_directory(songs)
303 else:
304 dialog = xbmcgui.Dialog()
305 dialog.ok(__language__(30008), __language__(30021))
306 self.categories()
307 else:
308 self.categories()
309
310 # Search for albums
311 def searchAlbums(self):
312 query = self._get_keyboard(default="", heading=__language__(30022))
313 if (query != ''):
314 albums = groovesharkApi.getAlbumSearchResults(query, limit = self.albumsearchlimit)
315 if (len(albums) > 0):
316 self._add_albums_directory(albums)
317 else:
318 dialog = xbmcgui.Dialog()
319 dialog.ok(__language__(30008), __language__(30023))
320 self.categories()
321 else:
322 self.categories()
323
324 # Search for artists
325 def searchArtists(self):
326 query = self._get_keyboard(default="", heading=__language__(30024))
327 if (query != ''):
328 artists = groovesharkApi.getArtistSearchResults(query, limit = self.artistsearchlimit)
329 if (len(artists) > 0):
330 self._add_artists_directory(artists)
331 else:
332 dialog = xbmcgui.Dialog()
333 dialog.ok(__language__(30008), __language__(30025))
334 self.categories()
335 else:
336 self.categories()
337
338 # Search for playlists
339 def searchPlaylists(self):
340 query = self._get_keyboard(default="", heading=__language__(30026))
341 if (query != ''):
342 playlists = groovesharkApi.getUserPlaylistsByUsername(query)
343 if (len(playlists) > 0):
344 self._add_playlists_directory(playlists)
345 else:
346 dialog = xbmcgui.Dialog()
347 dialog.ok(__language__(30008), __language__(30027))
348 self.categories()
349 else:
350 self.categories()
351
352 # Search for artists albums
353 def searchArtistsAlbums(self, artistName = None):
354 if artistName == None or artistName == searchArtistsAlbumsName:
355 query = self._get_keyboard(default="", heading=__language__(30028))
356 else:
357 query = artistName
358 if (query != ''):
359 artists = groovesharkApi.getArtistSearchResults(query, limit = self.artistsearchlimit)
360 if (len(artists) > 0):
361 # check for artist name match, first result is sometimes not the closest lexical match
362 artist = next ((a for a in artists if a[0].lower() == query.lower()), artists[0])
363 artistID = artist[1]
364 if __debugging__ :
365 xbmc.log("Found " + artist[0] + "...")
366 albums = groovesharkApi.getArtistAlbums(artistID, self.albumsearchlimit)
367 if (len(albums) > 0):
368 self._add_dir(__language__(30016), '', MODE_ARTIST_POPULAR_FROM_ALBUMS, self.popularSongsArtistImg, artistID)
369 self._add_albums_directory(albums, artistID)
370 else:
371 dialog = xbmcgui.Dialog()
372 dialog.ok(__language__(30008), __language__(30029))
373 self.categories()
374 else:
375 dialog = xbmcgui.Dialog()
376 dialog.ok(__language__(30008), __language__(30030))
377 self.categories()
378 else:
379 self.categories()
380
381 # Get my favorites
382 def favorites(self):
383 userid = self._get_login()
384 if (userid != 0):
385 favorites = groovesharkApi.getUserFavoriteSongs()
386 if (len(favorites) > 0):
387 self._add_songs_directory(favorites, isFavorites=True)
388 else:
389 dialog = xbmcgui.Dialog()
390 dialog.ok(__language__(30008), __language__(30031))
391 self.categories()
392
393 # Get popular songs
394 def popularSongs(self):
395 popular = groovesharkApi.getPopularSongsToday(limit = self.songsearchlimit)
396 if (len(popular) > 0):
397 self._add_songs_directory(popular)
398 else:
399 dialog = xbmcgui.Dialog()
400 dialog.ok(__language__(30008), __language__(30032))
401 self.categories()
402
403 # Get my playlists
404 def playlists(self):
405 userid = self._get_login()
406 if (userid != 0):
407 playlists = groovesharkApi.getUserPlaylists()
408 if (len(playlists) > 0):
409 self._add_playlists_directory(playlists)
410 else:
411 dialog = xbmcgui.Dialog()
412 dialog.ok(__language__(30008), __language__(30033))
413 self.categories()
414 else:
415 dialog = xbmcgui.Dialog()
416 dialog.ok(__language__(30008), __language__(30034), __language__(30035))
417
418 # Make songs a favorite
419 def favorite(self, songid):
420 userid = self._get_login()
421 if (userid != 0):
422 if __debugging__ :
423 xbmc.log("Favorite song: " + str(songid))
424 groovesharkApi.addUserFavoriteSong(songID = songid)
425 xbmc.executebuiltin('XBMC.Notification(' + __language__(30008) + ', ' + __language__(30036) + ', 1000, ' + thumbDef + ')')
426 else:
427 dialog = xbmcgui.Dialog()
428 dialog.ok(__language__(30008), __language__(30034), __language__(30037))
429
430 # Remove song from favorites
431 def unfavorite(self, songid, prevMode=0):
432 userid = self._get_login()
433 if (userid != 0):
434 if __debugging__ :
435 xbmc.log("Unfavorite song: " + str(songid) + ', previous mode was ' + str(prevMode))
436 groovesharkApi.removeUserFavoriteSongs(songIDs = songid)
437 xbmc.executebuiltin('XBMC.Notification(' + __language__(30008) + ', ' + __language__(30038) + ', 1000, ' + thumbDef + ')')
438 # Refresh to remove item from directory
439 if (int(prevMode) == MODE_FAVORITES):
440 xbmc.executebuiltin("Container.Refresh(" + favoritesUrl + ")")
441 else:
442 dialog = xbmcgui.Dialog()
443 dialog.ok(__language__(30008), __language__(30034), __language__(30039))
444
445
446 # Show selected album
447 def album(self, albumid):
448 album = groovesharkApi.getAlbumSongs(albumid, limit = self.songsearchlimit)
449 self._add_songs_directory(album, trackLabelFormat=NAME_ALBUM_ARTIST_LABEL)
450
451 # Show selected artist
452 def artist(self, artistid):
453 albums = groovesharkApi.getArtistAlbums(artistid, limit = self.albumsearchlimit)
454 if (len(albums) > 0):
455 self._add_dir(__language__(30016), '', MODE_ARTIST_POPULAR_FROM_ALBUMS, self.popularSongsArtistImg, artistid)
456 self._add_albums_directory(albums, artistid, True)
457 else:
458 # There are likely songs for the artist even when no verified albums are found
459 self.artistPopularSongs(artistid)
460
461 # Show selected playlist
462 def playlist(self, playlistid, playlistname):
463 userid = self._get_login()
464 if (userid != 0):
465 songs = groovesharkApi.getPlaylistSongs(playlistid)
466 self._add_songs_directory(songs, trackLabelFormat=NAME_ALBUM_ARTIST_LABEL, playlistid=playlistid, playlistname=playlistname)
467 else:
468 dialog = xbmcgui.Dialog()
469 dialog.ok(__language__(30008), __language__(30034), __language__(30040))
470
471 # Search for artist show popular songs of the artist
472 def searchArtistPopularSongs(self):
473 query = self._get_keyboard(default="", heading=__language__(30041))
474 if (query != ''):
475 artists = groovesharkApi.getArtistSearchResults(query, limit = self.artistsearchlimit)
476 if (len(artists) > 0):
477 # check for exact artist name match, sometimes a more
478 # popular artist is returned first (e.g. 'Angel Dust'
479 # gets you 'Faith No More' because of their popular
480 # album 'Angel Dust')
481 artist = next ((a for a in artists if a[0].lower() == query.lower()), artists[0])
482 artistID = artist[1]
483 if __debugging__ :
484 xbmc.log("Found " + artist[0] + "...")
485 self.artistPopularSongs (artistID)
486 else:
487 dialog = xbmcgui.Dialog()
488 dialog.ok(__language__(30008), __language__(30043))
489 self.categories()
490 else:
491 self.categories()
492
493 # Show popular songs of the artist
494 def artistPopularSongs(self, artistID):
495 songs = groovesharkApi.getArtistPopularSongs(artistID, limit = self.songsearchlimit)
496 if (len(songs) > 0):
497 self._add_songs_directory(songs, trackLabelFormat=NAME_ALBUM_ARTIST_LABEL)
498 else:
499 dialog = xbmcgui.Dialog()
500 dialog.ok(__language__(30008), __language__(30042))
501 self.categories()
502
503 # Play a song
504 def playSong(self, item):
505 global playTimer
506 global player
507 if item != None:
508 # Get stream as it could have expired
509 item.select(True)
510 url = ''
511 songid = item.getProperty('songid')
512 stream = groovesharkApi.getSubscriberStreamKey(songid)
513 if stream != False:
514 url = stream['url']
515 key = stream['StreamKey']
516 server = stream['StreamServerID']
517 duration = int(self._setDuration(stream['uSecs']))
518 stream = [songid, duration, url, key, server]
519 self._setSongStream(stream)
520 if url != '':
521 item.setPath(url)
522 xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), succeeded=True, listitem=item)
523 if __debugging__ :
524 xbmc.log("Grooveshark playing: " + url)
525 # Wait for play then start timer
526 seconds = 0
527 while seconds < STREAM_TIMEOUT:
528 try:
529 if player.isPlayingAudio() == True:
530 if playTimer != None:
531 playTimer.cancel()
532 songMarkTime = 0
533 playTimer = PlayTimer(1, markSong, self._setDuration(duration), [songid, duration, key, server])
534 playTimer.start()
535 break
536 except: pass
537 time.sleep(1)
538 seconds = seconds + 1
539 else:
540 xbmc.log("No song URL")
541 else:
542 xbmc.log("No song stream")
543 else:
544 xbmc.executebuiltin('XBMC.Notification(' + __language__(30008) + ', ' + __language__(30044) + ', 1000, ' + thumbDef + ')')
545
546 # Make a song directory item
547 def songItem(self, songid, name, album, artist, coverart, trackLabelFormat=ARTIST_ALBUM_NAME_LABEL, tracknumber=1):
548
549 stream = self._getSongStream(songid)
550 if stream != None:
551 duration = stream[1]
552 url = stream[2]
553 key = stream[3]
554 server = stream[4]
555 songImg = self._get_icon(coverart, 'song-' + str(songid) + "-image")
556 if int(trackLabelFormat) == NAME_ALBUM_ARTIST_LABEL:
557 trackLabel = name + " - " + album + " - " + artist
558 else:
559 trackLabel = artist + " - " + album + " - " + name
560 item = xbmcgui.ListItem(label = trackLabel, thumbnailImage=songImg, iconImage=songImg)
561 item.setPath(url)
562 item.setInfo( type="music", infoLabels={ "title": name, "album": album, "artist": artist, "duration": duration, "tracknumber" : tracknumber} )
563 item.setProperty('mimetype', 'audio/mpeg')
564 item.setProperty("IsPlayable", "true")
565 item.setProperty('songid', str(songid))
566 item.setProperty('coverart', songImg)
567 item.setProperty('title', name)
568 item.setProperty('album', album)
569 item.setProperty('artist', artist)
570 item.setProperty('duration', str(duration))
571 item.setProperty('key', str(key))
572 item.setProperty('server', str(server))
573 item.setProperty('fanart_image', self.fanImg)
574 return item
575 else:
576 xbmc.log("No access to song URL")
577 return None
578
579 # Next page of songs
580 def songPage(self, offset, trackLabelFormat, playlistid = 0, playlistname = ''):
581 self._add_songs_directory([], trackLabelFormat, offset, playlistid = playlistid, playlistname = playlistname)
582
583 # Make a playlist from an album
584 def makePlaylist(self, albumid, name):
585 userid = self._get_login()
586 if (userid != 0):
587 re.split(' - ',name,1)
588 nameTokens = re.split(' - ',name,1) # suggested name
589 name = self._get_keyboard(default=nameTokens[0], heading=__language__(30045))
590 if name != '':
591 album = groovesharkApi.getAlbumSongs(albumid, limit = self.songsearchlimit)
592 songids = []
593 for song in album:
594 songids.append(song[1])
595 if groovesharkApi.createPlaylist(name, songids) == 0:
596 dialog = xbmcgui.Dialog()
597 dialog.ok(__language__(30008), __language__(30046), name)
598 else:
599 xbmc.executebuiltin('XBMC.Notification(' + __language__(30008) + ',' + __language__(30047)+ ', 1000, ' + thumbDef + ')')
600 else:
601 dialog = xbmcgui.Dialog()
602 dialog.ok(__language__(30008), __language__(30034), __language__(30048))
603
604 # Rename a playlist
605 def renamePlaylist(self, playlistid, name):
606 userid = self._get_login()
607 if (userid != 0):
608 newname = self._get_keyboard(default=name, heading=__language__(30049))
609 if newname == '':
610 return
611 elif groovesharkApi.playlistRename(playlistid, newname) == 0:
612 dialog = xbmcgui.Dialog()
613 dialog.ok(__language__(30008), __language__(30050), name)
614 else:
615 # Refresh to show new item name
616 xbmc.executebuiltin("Container.Refresh")
617 else:
618 dialog = xbmcgui.Dialog()
619 dialog.ok(__language__(30008), __language__(30034), __language__(30051))
620
621 # Remove a playlist
622 def removePlaylist(self, playlistid, name):
623 dialog = xbmcgui.Dialog()
624 if dialog.yesno(__language__(30008), name, __language__(30052)) == True:
625 userid = self._get_login()
626 if (userid != 0):
627 if groovesharkApi.playlistDelete(playlistid) == 0:
628 dialog = xbmcgui.Dialog()
629 dialog.ok(__language__(30008), __language__(30053), name)
630 else:
631 # Refresh to remove item from directory
632 xbmc.executebuiltin("Container.Refresh(" + playlistsUrl + ")")
633 else:
634 dialog = xbmcgui.Dialog()
635 dialog.ok(__language__(30008), __language__(30034), __language__(30054))
636
637 # Add song to playlist
638 def addPlaylistSong(self, songid):
639 userid = self._get_login()
640 if (userid != 0):
641 playlists = groovesharkApi.getUserPlaylists()
642 if (len(playlists) > 0):
643 ret = 0
644 # Select the playlist
645 playlistSelect = GroovesharkPlaylistSelect(items=playlists)
646 playlistSelect.setFocus(playlistSelect.playlistControl)
647 playlistSelect.doModal()
648 i = playlistSelect.selected
649 del playlistSelect
650 if i > -1:
651 # Add a new playlist
652 if i >= len(playlists):
653 name = self._get_keyboard(default='', heading=__language__(30055))
654 if name != '':
655 songIds = []
656 songIds.append(songid)
657 if groovesharkApi.createPlaylist(name, songIds) == 0:
658 dialog = xbmcgui.Dialog()
659 dialog.ok(__language__(30008), __language__(30056), name)
660 else:
661 xbmc.executebuiltin('XBMC.Notification(' + __language__(30008) + ',' + __language__(30057) + ', 1000, ' + thumbDef + ')')
662 # Existing playlist
663 else:
664 playlist = playlists[i]
665 playlistid = playlist[1]
666 if __debugging__ :
667 xbmc.log("Add song " + str(songid) + " to playlist " + str(playlistid))
668 songIDs=[]
669 songs = groovesharkApi.getPlaylistSongs(playlistid)
670 for song in songs:
671 songIDs.append(song[1])
672 songIDs.append(songid)
673 ret = groovesharkApi.setPlaylistSongs(playlistid, songIDs)
674 if ret == False:
675 dialog = xbmcgui.Dialog()
676 dialog.ok(__language__(30008), __language__(30058))
677 else:
678 xbmc.executebuiltin('XBMC.Notification(' + __language__(30008) + ',' + __language__(30059) + ', 1000, ' + thumbDef + ')')
679 else:
680 dialog = xbmcgui.Dialog()
681 dialog.ok(__language__(30008), __language__(30060))
682 self.categories()
683 else:
684 dialog = xbmcgui.Dialog()
685 dialog.ok(__language__(30008), __language__(30034), __language__(30061))
686
687 # Remove song from playlist
688 def removePlaylistSong(self, playlistid, playlistname, songid):
689 dialog = xbmcgui.Dialog()
690 if dialog.yesno(__language__(30008), __language__(30062), __language__(30063)) == True:
691 userid = self._get_login()
692 if (userid != 0):
693 songs = groovesharkApi.getPlaylistSongs(playlistID)
694 songIDs=[]
695 for song in songs:
696 if (song[1] != songid):
697 songIDs.append(song[1])
698 ret = groovesharkApi.setPlaylistSongs(playlistID, songIDs)
699 if ret == False:
700 dialog = xbmcgui.Dialog()
701 dialog.ok(__language__(30008), __language__(30064), __language__(30065))
702 else:
703 # Refresh to remove item from directory
704 xbmc.executebuiltin('XBMC.Notification(' + __language__(30008) + ',' + __language__(30066)+ ', 1000, ' + thumbDef + ')')
705 xbmc.executebuiltin("Container.Update(" + playlistUrl + "&id="+str(playlistid) + "&name=" + str(playlistname) + ")")
706 else:
707 dialog = xbmcgui.Dialog()
708 dialog.ok(__language__(30008), __language__(30034), __language__(30067))
709
710 # Find similar artists to searched artist
711 def similarArtists(self, artistId):
712 similar = groovesharkApi.getSimilarArtists(artistId, limit = self.artistsearchlimit)
713 if (len(similar) > 0):
714 self._add_artists_directory(similar)
715 else:
716 dialog = xbmcgui.Dialog()
717 dialog.ok(__language__(30008), __language__(30068))
718 self.categories()
719
720 # Get keyboard input
721 def _get_keyboard(self, default="", heading="", hidden=False):
722 kb = xbmc.Keyboard(default, heading, hidden)
723 kb.doModal()
724 if (kb.isConfirmed()):
725 return unicode(kb.getText(), "utf-8")
726 return ''
727
728 # Login to grooveshark
729 def _get_login(self):
730 if (self.username == "" or self.password == ""):
731 dialog = xbmcgui.Dialog()
732 dialog.ok(__language__(30008), __language__(30069), __language__(30070), __language__(30082))
733 return 0
734 else:
735 uid = groovesharkApi.login(self.username, self.password)
736 if (uid != 0):
737 return uid
738 else:
739 dialog = xbmcgui.Dialog()
740 dialog.ok(__language__(30008), __language__(30069), __language__(30070), __language__(30082))
741 return 0
742
743 # File download
744 def _get_icon(self, url, songid):
745 if url != 'None':
746 localThumb = os.path.join(xbmc.translatePath(os.path.join(thumbDir, str(songid)))) + '.tbn'
747 try:
748 if os.path.isfile(localThumb) == False:
749 headers = { 'User-Agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/32.0.1700.102 Chrome/32.0.1700.102 Safari/537.36' }
750 req = urllib2.Request(url, None, headers)
751 loc = urllib2.urlopen(req)
752 output = open(localThumb,'wb')
753 output.write(loc.read())
754 output.close()
755 except:
756 shutil.copy2(thumbDef, localThumb)
757 return os.path.join(os.path.join(thumbDir, str(songid))) + '.tbn'
758 else:
759 return thumbDef
760
761 # Add songs to directory
762 def _add_songs_directory(self, songs, trackLabelFormat=ARTIST_ALBUM_NAME_LABEL, offset=0, playlistid=0, playlistname='', isFavorites=False):
763
764 totalSongs = len(songs)
765 offset = int(offset)
766 start = 0
767 end = totalSongs
768
769 # No pages needed
770 if offset == 0 and totalSongs <= self.songspagelimit:
771 if __debugging__ :
772 xbmc.log("Found " + str(totalSongs) + " songs...")
773 # Pages
774 else:
775 # Cache all next pages songs
776 if offset == 0:
777 self._setSavedSongs(songs)
778 else:
779 songs = self._getSavedSongs()
780 totalSongs = len(songs)
781
782 if totalSongs > 0:
783 start = offset
784 end = min(start + self.songspagelimit,totalSongs)
785
786 n = start
787 items = end - start
788 while n < end:
789 song = songs[n]
790 name = song[0]
791 songid = song[1]
792 album = song[2]
793 artist = song[4]
794 coverart = song[6]
795 item = self.songItem(songid, name, album, artist, coverart, trackLabelFormat, (n+1))
796 if item != None:
797 coverart = item.getProperty('coverart')
798 songname = song[0]
799 songalbum = song[2]
800 songartist = song[4]
801 u=sys.argv[0]+"?mode="+str(MODE_SONG)+"&name="+urllib.quote_plus(songname)+"&id="+str(songid) \
802 +"&album="+urllib.quote_plus(songalbum) \
803 +"&artist="+urllib.quote_plus(songartist) \
804 +"&coverart="+urllib.quote_plus(coverart)
805 fav=sys.argv[0]+"?mode="+str(MODE_FAVORITE)+"&name="+urllib.quote_plus(songname)+"&id="+str(songid)
806 unfav=sys.argv[0]+"?mode="+str(MODE_UNFAVORITE)+"&name="+urllib.quote_plus(songname)+"&id="+str(songid)+"&prevmode="
807 menuItems = []
808 if isFavorites == True:
809 unfav = unfav +str(MODE_FAVORITES)
810 else:
811 menuItems.append((__language__(30071), "XBMC.RunPlugin("+fav+")"))
812 menuItems.append((__language__(30072), "XBMC.RunPlugin("+unfav+")"))
813 if playlistid > 0:
814 rmplaylstsong=sys.argv[0]+"?playlistid="+str(playlistid)+"&id="+str(songid)+"&mode="+str(MODE_REMOVE_PLAYLIST_SONG)+"&name="+str(playlistname)
815 menuItems.append((__language__(30073), "XBMC.RunPlugin("+rmplaylstsong+")"))
816 else:
817 addplaylstsong=sys.argv[0]+"?id="+str(songid)+"&mode="+str(MODE_ADD_PLAYLIST_SONG)
818 menuItems.append((__language__(30074), "XBMC.RunPlugin("+addplaylstsong+")"))
819 item.addContextMenuItems(menuItems, replaceItems=False)
820 xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=item,isFolder=False, totalItems=items)
821 else:
822 end = min(end + 1,totalSongs)
823 if __debugging__ :
824 xbmc.log(song[0] + " does not exist.")
825 n = n + 1
826
827 if totalSongs > end:
828 u=sys.argv[0]+"?mode="+str(MODE_SONG_PAGE)+"&id=playlistid"+"&offset="+str(end)+"&label="+str(trackLabelFormat)+"&name="+str(playlistname)
829 self._add_dir(__language__(30075) + '...', u, MODE_SONG_PAGE, self.songImg, 0, totalSongs - end)
830
831 xbmcplugin.setContent(self._handle, 'songs')
832 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
833
834 # Add albums to directory
835 def _add_albums_directory(self, albums, artistid=0, isverified=False):
836 n = len(albums)
837 itemsExisting = n
838 if __debugging__ :
839 xbmc.log("Found " + str(n) + " albums...")
840 i = 0
841 while i < n:
842 album = albums[i]
843 albumID = album[3]
844 if isverified:
845 albumArtistName = album[0]
846 albumName = album[2]
847 albumImage = self._get_icon(album[4], 'album-' + str(albumID))
848 self._add_dir(albumName + " - " + albumArtistName, '', MODE_ALBUM, albumImage, albumID, itemsExisting)
849 else:
850 itemsExisting = itemsExisting - 1
851 i = i + 1
852 # Not supported by key
853 #if artistid > 0:
854 # self._add_dir('Similar artists...', '', MODE_SIMILAR_ARTISTS, self.artistImg, artistid)
855 xbmcplugin.setContent(self._handle, 'albums')
856 xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_ALBUM_IGNORE_THE)
857 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
858
859 # Add artists to directory
860 def _add_artists_directory(self, artists):
861 n = len(artists)
862 itemsExisting = n
863 if __debugging__ :
864 xbmc.log("Found " + str(n) + " artists...")
865 i = 0
866 while i < n:
867 artist = artists[i]
868 artistID = artist[1]
869 artistName = artist[0]
870 self._add_dir(artistName, '', MODE_ARTIST, self.artistImg, artistID, itemsExisting)
871 i = i + 1
872 xbmcplugin.setContent(self._handle, 'artists')
873 xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_ARTIST_IGNORE_THE)
874 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
875
876 # Add playlists to directory
877 def _add_playlists_directory(self, playlists):
878 n = len(playlists)
879 if __debugging__ :
880 xbmc.log("Found " + str(n) + " playlists...")
881 i = 0
882 while i < n:
883 playlist = playlists[i]
884 playlistName = playlist[0]
885 playlistID = playlist[1]
886 self._add_dir(playlistName, '', MODE_PLAYLIST, self.playlistImg, playlistID, n)
887 i = i + 1
888 xbmcplugin.setContent(self._handle, 'files')
889 xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_LABEL)
890 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
891
892 # Add whatever directory
893 def _add_dir(self, name, url, mode, iconimage, itemId, items=1):
894
895 if url == '':
896 u=sys.argv[0]+"?mode="+str(mode)+"&name="+urllib.quote_plus(name)+"&id="+str(itemId)
897 else:
898 u = url
899 directory=xbmcgui.ListItem(name, iconImage=iconimage, thumbnailImage=iconimage)
900 directory.setInfo( type="Music", infoLabels={ "title": name } )
901 directory.setProperty('fanart_image', self.fanImg)
902
903 # Custom menu items
904 menuItems = []
905 if mode == MODE_ALBUM:
906 mkplaylst=sys.argv[0]+"?mode="+str(MODE_MAKE_PLAYLIST)+"&name="+name+"&id="+str(itemId)
907 menuItems.append((__language__(30076), "XBMC.RunPlugin("+mkplaylst+")"))
908 if mode == MODE_PLAYLIST:
909 rmplaylst=sys.argv[0]+"?mode="+str(MODE_REMOVE_PLAYLIST)+"&name="+urllib.quote_plus(name)+"&id="+str(itemId)
910 menuItems.append((__language__(30077), "XBMC.RunPlugin("+rmplaylst+")"))
911 mvplaylst=sys.argv[0]+"?mode="+str(MODE_RENAME_PLAYLIST)+"&name="+urllib.quote_plus(name)+"&id="+str(itemId)
912 menuItems.append((__language__(30078), "XBMC.RunPlugin("+mvplaylst+")"))
913
914 directory.addContextMenuItems(menuItems, replaceItems=False)
915
916 return xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=directory,isFolder=True, totalItems=items)
917
918 def _getSavedSongs(self):
919 path = os.path.join(cacheDir, 'songs.dmp')
920 try:
921 f = open(path, 'rb')
922 songs = pickle.load(f)
923 f.close()
924 except:
925 songs = []
926 pass
927 return songs
928
929 def _setSavedSongs(self, songs):
930 try:
931 # Create the 'data' directory if it doesn't exist.
932 if not os.path.exists(cacheDir):
933 os.makedirs(cacheDir)
934 path = os.path.join(cacheDir, 'songs.dmp')
935 f = open(path, 'wb')
936 pickle.dump(songs, f, protocol=pickle.HIGHEST_PROTOCOL)
937 f.close()
938 except:
939 xbmc.log("An error occurred saving songs")
940 pass
941
942 # Duration to seconds
943 def _setDuration(self, usecs):
944 if usecs < 60000000:
945 usecs = usecs * 10 # Some durations are 10x to small
946 return int(usecs / 1000000)
947
948 def _getSongStream(self, songid):
949 idSong = int(songid)
950 stream = None
951 streams = {}
952 path = os.path.join(cacheDir, 'streams.dmp')
953 try:
954 f = open(path, 'rb')
955 streams = pickle.load(f)
956 song = streams[songid]
957
958 duration = song[1]
959 url = song[2]
960 key = song[3]
961 server = song[4]
962 stream = [idSong, duration, url, key, server]
963
964 if __debugging__ :
965 xbmc.log("Found " + str(idSong) + " in stream cache")
966
967 f.close()
968 except:
969 pass
970
971 # Not in cache
972 if stream == None:
973 stream = groovesharkApi.getSubscriberStreamKey(songid)
974 if stream != False and stream['url'] != '':
975 duration = self._setDuration(stream['uSecs'])
976 url = stream['url']
977 key = stream['StreamKey']
978 server = stream['StreamServerID']
979 stream = [idSong, duration, url, key, server]
980 self._addSongStream(stream)
981
982 return stream
983
984 def _addSongStream(self, stream):
985 streams = self._getStreams()
986 streams[int(stream[0])] = stream
987 path = os.path.join(cacheDir, 'streams.dmp')
988 try:
989 f = open(path, 'wb')
990 pickle.dump(streams, f, protocol=pickle.HIGHEST_PROTOCOL)
991 f.close()
992 if __debugging__ :
993 xbmc.log("Added " + str(stream[0]) + " to stream cache")
994 except:
995 xbmc.log("An error occurred adding to stream")
996
997 def _setSongStream(self, stream):
998 idStream = int(stream[0])
999 stream[1] = self._setDuration(stream[1])
1000 streams = self._getStreams()
1001 path = os.path.join(cacheDir, 'streams.dmp')
1002
1003 try:
1004 streams[idStream] = stream
1005 f = open(path, 'wb')
1006 pickle.dump(streams, f, protocol=pickle.HIGHEST_PROTOCOL)
1007 f.close()
1008 if __debugging__ :
1009 xbmc.log("Updated " + str(idStream) + " in stream cache")
1010
1011 except:
1012 xbmc.log("An error occurred setting stream")
1013
1014 def _getStreams(self):
1015 path = os.path.join(cacheDir, 'streams.dmp')
1016 try:
1017 f = open(path, 'rb')
1018 streams = pickle.load(f)
1019 f.close()
1020 except:
1021 streams = {}
1022 pass
1023 return streams
1024
1025
1026 # Parse URL parameters
1027 def get_params():
1028 param=[]
1029 paramstring=sys.argv[2]
1030 if __debugging__ :
1031 xbmc.log(paramstring)
1032 if len(paramstring)>=2:
1033 params=sys.argv[2]
1034 cleanedparams=params.replace('?','')
1035 if (params[len(params)-1]=='/'):
1036 params=params[0:len(params)-2]
1037 pairsofparams=cleanedparams.split('&')
1038 param={}
1039 for i in range(len(pairsofparams)):
1040 splitparams={}
1041 splitparams=pairsofparams[i].split('=')
1042 if (len(splitparams))==2:
1043 param[splitparams[0]]=splitparams[1]
1044 return param
1045
1046 # Main
1047 grooveshark = Grooveshark();
1048
1049 params=get_params()
1050 mode=None
1051 try: mode=int(params["mode"])
1052 except: pass
1053 itemId=0
1054 try: itemId=int(params["id"])
1055 except: pass
1056 name = None
1057 try: name=urllib.unquote_plus(params["name"])
1058 except: pass
1059
1060 # Call function for URL
1061 if mode==None:
1062 grooveshark.categories()
1063
1064 elif mode==MODE_SEARCH_SONGS:
1065 grooveshark.searchSongs()
1066
1067 elif mode==MODE_SEARCH_ALBUMS:
1068 grooveshark.searchAlbums()
1069
1070 elif mode==MODE_SEARCH_ARTISTS:
1071 grooveshark.searchArtists()
1072
1073 elif mode==MODE_SEARCH_ARTISTS_ALBUMS:
1074 grooveshark.searchArtistsAlbums(name)
1075
1076 elif mode==MODE_SEARCH_PLAYLISTS:
1077 grooveshark.searchPlaylists()
1078
1079 elif mode==MODE_POPULAR_SONGS:
1080 grooveshark.popularSongs()
1081
1082 elif mode==MODE_ARTIST_POPULAR:
1083 grooveshark.searchArtistPopularSongs()
1084
1085 elif mode==MODE_FAVORITES:
1086 grooveshark.favorites()
1087
1088 elif mode==MODE_PLAYLISTS:
1089 grooveshark.playlists()
1090
1091 elif mode==MODE_SONG_PAGE:
1092 try: offset=urllib.unquote_plus(params["offset"])
1093 except: pass
1094 try: label=urllib.unquote_plus(params["label"])
1095 except: pass
1096 grooveshark.songPage(offset, label, itemId, name)
1097
1098 elif mode==MODE_SONG:
1099 try: album=urllib.unquote_plus(params["album"])
1100 except: pass
1101 try: artist=urllib.unquote_plus(params["artist"])
1102 except: pass
1103 try: coverart=urllib.unquote_plus(params["coverart"])
1104 except: pass
1105 song = grooveshark.songItem(itemId, name, album, artist, coverart)
1106 grooveshark.playSong(song)
1107
1108 elif mode==MODE_ARTIST:
1109 grooveshark.artist(itemId)
1110
1111 elif mode==MODE_ALBUM:
1112 grooveshark.album(itemId)
1113
1114 elif mode==MODE_PLAYLIST:
1115 grooveshark.playlist(itemId, name)
1116
1117 elif mode==MODE_FAVORITE:
1118 grooveshark.favorite(itemId)
1119
1120 elif mode==MODE_UNFAVORITE:
1121 try: prevMode=int(urllib.unquote_plus(params["prevmode"]))
1122 except:
1123 prevMode = 0
1124 grooveshark.unfavorite(itemId, prevMode)
1125
1126 elif mode==MODE_SIMILAR_ARTISTS:
1127 grooveshark.similarArtists(itemId)
1128
1129 elif mode==MODE_MAKE_PLAYLIST:
1130 grooveshark.makePlaylist(itemId, name)
1131
1132 elif mode==MODE_REMOVE_PLAYLIST:
1133 grooveshark.removePlaylist(itemId, name)
1134
1135 elif mode==MODE_RENAME_PLAYLIST:
1136 grooveshark.renamePlaylist(itemId, name)
1137
1138 elif mode==MODE_REMOVE_PLAYLIST_SONG:
1139 try: playlistID=urllib.unquote_plus(params["playlistid"])
1140 except: pass
1141 grooveshark.removePlaylistSong(playlistID, name, itemId)
1142
1143 elif mode==MODE_ADD_PLAYLIST_SONG:
1144 grooveshark.addPlaylistSong(itemId)
1145
1146 elif mode==MODE_ARTIST_POPULAR_FROM_ALBUMS:
1147 grooveshark.artistPopularSongs(itemId)
1148
1149 if mode < MODE_SONG:
1150 xbmcplugin.endOfDirectory(int(sys.argv[1]))