don't check isVerified flag when adding albums to directory
[clinton/xbmc-groove.git] / default.py
... / ...
CommitLineData
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
19import 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
30MODE_SEARCH_SONGS = 1
31MODE_SEARCH_ALBUMS = 2
32MODE_SEARCH_ARTISTS = 3
33MODE_SEARCH_ARTISTS_ALBUMS = 4
34MODE_SEARCH_PLAYLISTS = 5
35MODE_ARTIST_POPULAR = 6
36MODE_POPULAR_SONGS = 7
37MODE_FAVORITES = 8
38MODE_PLAYLISTS = 9
39MODE_ALBUM = 10
40MODE_ARTIST = 11
41MODE_PLAYLIST = 12
42MODE_SONG_PAGE = 13
43MODE_SIMILAR_ARTISTS = 14
44MODE_ARTIST_POPULAR_FROM_ALBUMS = 15
45MODE_SONG = 150
46
47MODE_FAVORITE = 160
48MODE_UNFAVORITE = 170
49MODE_MAKE_PLAYLIST = 180
50MODE_REMOVE_PLAYLIST = 190
51MODE_RENAME_PLAYLIST = 200
52MODE_REMOVE_PLAYLIST_SONG = 210
53MODE_ADD_PLAYLIST_SONG = 220
54
55ACTION_MOVE_LEFT = 1
56ACTION_MOVE_UP = 3
57ACTION_MOVE_DOWN = 4
58ACTION_PAGE_UP = 5
59ACTION_PAGE_DOWN = 6
60ACTION_SELECT_ITEM = 7
61ACTION_PREVIOUS_MENU = 10
62
63# Formats for track labels
64ARTIST_ALBUM_NAME_LABEL = 0
65NAME_ALBUM_ARTIST_LABEL = 1
66
67# Stream marking time (seconds)
68STREAM_MARKING_TIME = 30
69
70# Timeout
71STREAM_TIMEOUT = 30
72
73songMarkTime = 0
74player = xbmc.Player()
75playTimer = None
76
77baseDir = __cwd__
78resDir = xbmc.translatePath(os.path.join(baseDir, 'resources'))
79libDir = xbmc.translatePath(os.path.join(resDir, 'lib'))
80imgDir = xbmc.translatePath(os.path.join(resDir, 'img'))
81cacheDir = os.path.join(xbmc.translatePath('special://masterprofile/addon_data/'), os.path.basename(baseDir))
82tempDir = xbmc.translatePath('special://temp')
83thumbDirName = 'thumb'
84thumbDir = os.path.join(xbmc.translatePath('special://masterprofile/addon_data/'), os.path.basename(baseDir), thumbDirName)
85
86baseModeUrl = 'plugin://plugin.audio.groove/'
87playlistUrl = baseModeUrl + '?mode=' + str(MODE_PLAYLIST)
88playlistsUrl = baseModeUrl + '?mode=' + str(MODE_PLAYLISTS)
89favoritesUrl = baseModeUrl + '?mode=' + str(MODE_FAVORITES)
90
91searchArtistsAlbumsName = __language__(30006)
92
93thumbDef = os.path.join(imgDir, 'default.tbn')
94listBackground = os.path.join(imgDir, 'listbackground.png')
95
96sys.path.append (libDir)
97from GroovesharkAPI import GrooveAPI
98from threading import Event, Thread
99
100if __debugging__ == 'true':
101 __debugging__ = True
102else:
103 __debugging__ = False
104
105try:
106 groovesharkApi = GrooveAPI(__debugging__, tempDir)
107 if groovesharkApi.pingService() != True:
108 raise StandardError(__language__(30007))
109except:
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
119def 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
136class _Info:
137 def __init__( self, *args, **kwargs ):
138 self.__dict__.update( kwargs )
139
140# Window dialog to select a grooveshark playlist
141class 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
204class 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
240class 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
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 i = i + 1
850 # Not supported by key
851 #if artistid > 0:
852 # self._add_dir('Similar artists...', '', MODE_SIMILAR_ARTISTS, self.artistImg, artistid)
853 xbmcplugin.setContent(self._handle, 'albums')
854 xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_ALBUM_IGNORE_THE)
855 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
856
857 # Add artists to directory
858 def _add_artists_directory(self, artists):
859 n = len(artists)
860 itemsExisting = n
861 if __debugging__ :
862 xbmc.log("Found " + str(n) + " artists...")
863 i = 0
864 while i < n:
865 artist = artists[i]
866 artistID = artist[1]
867 artistName = artist[0]
868 self._add_dir(artistName, '', MODE_ARTIST, self.artistImg, artistID, itemsExisting)
869 i = i + 1
870 xbmcplugin.setContent(self._handle, 'artists')
871 xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_ARTIST_IGNORE_THE)
872 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
873
874 # Add playlists to directory
875 def _add_playlists_directory(self, playlists):
876 n = len(playlists)
877 if __debugging__ :
878 xbmc.log("Found " + str(n) + " playlists...")
879 i = 0
880 while i < n:
881 playlist = playlists[i]
882 playlistName = playlist[0]
883 playlistID = playlist[1]
884 self._add_dir(playlistName, '', MODE_PLAYLIST, self.playlistImg, playlistID, n)
885 i = i + 1
886 xbmcplugin.setContent(self._handle, 'files')
887 xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_LABEL)
888 xbmcplugin.setPluginFanart(int(sys.argv[1]), self.fanImg)
889
890 # Add whatever directory
891 def _add_dir(self, name, url, mode, iconimage, itemId, items=1):
892
893 if url == '':
894 u=sys.argv[0]+"?mode="+str(mode)+"&name="+urllib.quote_plus(name)+"&id="+str(itemId)
895 else:
896 u = url
897 directory=xbmcgui.ListItem(name, iconImage=iconimage, thumbnailImage=iconimage)
898 directory.setInfo( type="Music", infoLabels={ "title": name } )
899 directory.setProperty('fanart_image', self.fanImg)
900
901 # Custom menu items
902 menuItems = []
903 if mode == MODE_ALBUM:
904 mkplaylst=sys.argv[0]+"?mode="+str(MODE_MAKE_PLAYLIST)+"&name="+name+"&id="+str(itemId)
905 menuItems.append((__language__(30076), "XBMC.RunPlugin("+mkplaylst+")"))
906 if mode == MODE_PLAYLIST:
907 rmplaylst=sys.argv[0]+"?mode="+str(MODE_REMOVE_PLAYLIST)+"&name="+urllib.quote_plus(name)+"&id="+str(itemId)
908 menuItems.append((__language__(30077), "XBMC.RunPlugin("+rmplaylst+")"))
909 mvplaylst=sys.argv[0]+"?mode="+str(MODE_RENAME_PLAYLIST)+"&name="+urllib.quote_plus(name)+"&id="+str(itemId)
910 menuItems.append((__language__(30078), "XBMC.RunPlugin("+mvplaylst+")"))
911
912 directory.addContextMenuItems(menuItems, replaceItems=False)
913
914 return xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=directory,isFolder=True, totalItems=items)
915
916 def _getSavedSongs(self):
917 path = os.path.join(cacheDir, 'songs.dmp')
918 try:
919 f = open(path, 'rb')
920 songs = pickle.load(f)
921 f.close()
922 except:
923 songs = []
924 pass
925 return songs
926
927 def _setSavedSongs(self, songs):
928 try:
929 # Create the 'data' directory if it doesn't exist.
930 if not os.path.exists(cacheDir):
931 os.makedirs(cacheDir)
932 path = os.path.join(cacheDir, 'songs.dmp')
933 f = open(path, 'wb')
934 pickle.dump(songs, f, protocol=pickle.HIGHEST_PROTOCOL)
935 f.close()
936 except:
937 xbmc.log("An error occurred saving songs")
938 pass
939
940 # Duration to seconds
941 def _setDuration(self, usecs):
942 if usecs < 60000000:
943 usecs = usecs * 10 # Some durations are 10x to small
944 return int(usecs / 1000000)
945
946 def _getSongStream(self, songid):
947 idSong = int(songid)
948 stream = None
949 streams = {}
950 path = os.path.join(cacheDir, 'streams.dmp')
951 try:
952 f = open(path, 'rb')
953 streams = pickle.load(f)
954 song = streams[songid]
955
956 duration = song[1]
957 url = song[2]
958 key = song[3]
959 server = song[4]
960 stream = [idSong, duration, url, key, server]
961
962 if __debugging__ :
963 xbmc.log("Found " + str(idSong) + " in stream cache")
964
965 f.close()
966 except:
967 pass
968
969 # Not in cache
970 if stream == None:
971 stream = groovesharkApi.getSubscriberStreamKey(songid)
972 if stream != False and stream['url'] != '':
973 duration = self._setDuration(stream['uSecs'])
974 url = stream['url']
975 key = stream['StreamKey']
976 server = stream['StreamServerID']
977 stream = [idSong, duration, url, key, server]
978 self._addSongStream(stream)
979
980 return stream
981
982 def _addSongStream(self, stream):
983 streams = self._getStreams()
984 streams[int(stream[0])] = stream
985 path = os.path.join(cacheDir, 'streams.dmp')
986 try:
987 f = open(path, 'wb')
988 pickle.dump(streams, f, protocol=pickle.HIGHEST_PROTOCOL)
989 f.close()
990 if __debugging__ :
991 xbmc.log("Added " + str(stream[0]) + " to stream cache")
992 except:
993 xbmc.log("An error occurred adding to stream")
994
995 def _setSongStream(self, stream):
996 idStream = int(stream[0])
997 stream[1] = self._setDuration(stream[1])
998 streams = self._getStreams()
999 path = os.path.join(cacheDir, 'streams.dmp')
1000
1001 try:
1002 streams[idStream] = stream
1003 f = open(path, 'wb')
1004 pickle.dump(streams, f, protocol=pickle.HIGHEST_PROTOCOL)
1005 f.close()
1006 if __debugging__ :
1007 xbmc.log("Updated " + str(idStream) + " in stream cache")
1008
1009 except:
1010 xbmc.log("An error occurred setting stream")
1011
1012 def _getStreams(self):
1013 path = os.path.join(cacheDir, 'streams.dmp')
1014 try:
1015 f = open(path, 'rb')
1016 streams = pickle.load(f)
1017 f.close()
1018 except:
1019 streams = {}
1020 pass
1021 return streams
1022
1023
1024# Parse URL parameters
1025def get_params():
1026 param=[]
1027 paramstring=sys.argv[2]
1028 if __debugging__ :
1029 xbmc.log(paramstring)
1030 if len(paramstring)>=2:
1031 params=sys.argv[2]
1032 cleanedparams=params.replace('?','')
1033 if (params[len(params)-1]=='/'):
1034 params=params[0:len(params)-2]
1035 pairsofparams=cleanedparams.split('&')
1036 param={}
1037 for i in range(len(pairsofparams)):
1038 splitparams={}
1039 splitparams=pairsofparams[i].split('=')
1040 if (len(splitparams))==2:
1041 param[splitparams[0]]=splitparams[1]
1042 return param
1043
1044# Main
1045grooveshark = Grooveshark();
1046
1047params=get_params()
1048mode=None
1049try: mode=int(params["mode"])
1050except: pass
1051itemId=0
1052try: itemId=int(params["id"])
1053except: pass
1054name = None
1055try: name=urllib.unquote_plus(params["name"])
1056except: pass
1057
1058# Call function for URL
1059if mode==None:
1060 grooveshark.categories()
1061
1062elif mode==MODE_SEARCH_SONGS:
1063 grooveshark.searchSongs()
1064
1065elif mode==MODE_SEARCH_ALBUMS:
1066 grooveshark.searchAlbums()
1067
1068elif mode==MODE_SEARCH_ARTISTS:
1069 grooveshark.searchArtists()
1070
1071elif mode==MODE_SEARCH_ARTISTS_ALBUMS:
1072 grooveshark.searchArtistsAlbums(name)
1073
1074elif mode==MODE_SEARCH_PLAYLISTS:
1075 grooveshark.searchPlaylists()
1076
1077elif mode==MODE_POPULAR_SONGS:
1078 grooveshark.popularSongs()
1079
1080elif mode==MODE_ARTIST_POPULAR:
1081 grooveshark.searchArtistPopularSongs()
1082
1083elif mode==MODE_FAVORITES:
1084 grooveshark.favorites()
1085
1086elif mode==MODE_PLAYLISTS:
1087 grooveshark.playlists()
1088
1089elif mode==MODE_SONG_PAGE:
1090 try: offset=urllib.unquote_plus(params["offset"])
1091 except: pass
1092 try: label=urllib.unquote_plus(params["label"])
1093 except: pass
1094 grooveshark.songPage(offset, label, itemId, name)
1095
1096elif mode==MODE_SONG:
1097 try: album=urllib.unquote_plus(params["album"])
1098 except: pass
1099 try: artist=urllib.unquote_plus(params["artist"])
1100 except: pass
1101 try: coverart=urllib.unquote_plus(params["coverart"])
1102 except: pass
1103 song = grooveshark.songItem(itemId, name, album, artist, coverart)
1104 grooveshark.playSong(song)
1105
1106elif mode==MODE_ARTIST:
1107 grooveshark.artist(itemId)
1108
1109elif mode==MODE_ALBUM:
1110 grooveshark.album(itemId)
1111
1112elif mode==MODE_PLAYLIST:
1113 grooveshark.playlist(itemId, name)
1114
1115elif mode==MODE_FAVORITE:
1116 grooveshark.favorite(itemId)
1117
1118elif mode==MODE_UNFAVORITE:
1119 try: prevMode=int(urllib.unquote_plus(params["prevmode"]))
1120 except:
1121 prevMode = 0
1122 grooveshark.unfavorite(itemId, prevMode)
1123
1124elif mode==MODE_SIMILAR_ARTISTS:
1125 grooveshark.similarArtists(itemId)
1126
1127elif mode==MODE_MAKE_PLAYLIST:
1128 grooveshark.makePlaylist(itemId, name)
1129
1130elif mode==MODE_REMOVE_PLAYLIST:
1131 grooveshark.removePlaylist(itemId, name)
1132
1133elif mode==MODE_RENAME_PLAYLIST:
1134 grooveshark.renamePlaylist(itemId, name)
1135
1136elif mode==MODE_REMOVE_PLAYLIST_SONG:
1137 try: playlistID=urllib.unquote_plus(params["playlistid"])
1138 except: pass
1139 grooveshark.removePlaylistSong(playlistID, name, itemId)
1140
1141elif mode==MODE_ADD_PLAYLIST_SONG:
1142 grooveshark.addPlaylistSong(itemId)
1143
1144elif mode==MODE_ARTIST_POPULAR_FROM_ALBUMS:
1145 grooveshark.artistPopularSongs(itemId)
1146
1147if mode < MODE_SONG:
1148 xbmcplugin.endOfDirectory(int(sys.argv[1]))