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