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