1 # Copyright 2011 Stephen Denham
3 # This file is part of xbmc-groove.
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.
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.
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/>.
19 import urllib
, urllib2
, sys
, os
, shutil
, re
, pickle
, time
, traceback
, xbmcaddon
, xbmcplugin
, xbmcgui
, xbmc
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')
29 # Directory listings must be < MODE_SONG
31 MODE_SEARCH_ALBUMS
= 2
32 MODE_SEARCH_ARTISTS
= 3
33 MODE_SEARCH_ARTISTS_ALBUMS
= 4
34 MODE_SEARCH_PLAYLISTS
= 5
35 MODE_ARTIST_POPULAR
= 6
36 MODE_POPULAR_SONGS
= 7
43 MODE_SIMILAR_ARTISTS
= 14
44 MODE_ARTIST_POPULAR_FROM_ALBUMS
= 15
49 MODE_MAKE_PLAYLIST
= 180
50 MODE_REMOVE_PLAYLIST
= 190
51 MODE_RENAME_PLAYLIST
= 200
52 MODE_REMOVE_PLAYLIST_SONG
= 210
53 MODE_ADD_PLAYLIST_SONG
= 220
60 ACTION_SELECT_ITEM
= 7
61 ACTION_PREVIOUS_MENU
= 10
63 # Formats for track labels
64 ARTIST_ALBUM_NAME_LABEL
= 0
65 NAME_ALBUM_ARTIST_LABEL
= 1
67 # Stream marking time (seconds)
68 STREAM_MARKING_TIME
= 30
74 player
= xbmc
.Player()
78 resDir
= xbmc
.translatePath(os
.path
.join(baseDir
, 'resources'))
79 libDir
= xbmc
.translatePath(os
.path
.join(resDir
, 'lib'))
80 imgDir
= xbmc
.translatePath(os
.path
.join(resDir
, 'img'))
81 cacheDir
= os
.path
.join(xbmc
.translatePath('special://masterprofile/addon_data/'), os
.path
.basename(baseDir
))
82 tempDir
= xbmc
.translatePath('special://temp')
83 thumbDirName
= 'thumb'
84 thumbDir
= os
.path
.join(xbmc
.translatePath('special://masterprofile/addon_data/'), os
.path
.basename(baseDir
), thumbDirName
)
86 baseModeUrl
= 'plugin://plugin.audio.groove/'
87 playlistUrl
= baseModeUrl
+ '?mode=' + str(MODE_PLAYLIST
)
88 playlistsUrl
= baseModeUrl
+ '?mode=' + str(MODE_PLAYLISTS
)
89 favoritesUrl
= baseModeUrl
+ '?mode=' + str(MODE_FAVORITES
)
91 searchArtistsAlbumsName
= __language__(30006)
93 thumbDef
= os
.path
.join(imgDir
, 'default.tbn')
94 listBackground
= os
.path
.join(imgDir
, 'listbackground.png')
96 sys
.path
.append (libDir
)
97 from GroovesharkAPI
import GrooveAPI
98 from threading
import Event
, Thread
100 if __debugging__
== 'true':
103 __debugging__
= False
106 groovesharkApi
= GrooveAPI(__debugging__
, tempDir
)
107 if groovesharkApi
.pingService() != True:
108 raise StandardError(__language__(30007))
110 print "Exception on initialisation"
112 traceback
.print_exc()
114 dialog
= xbmcgui
.Dialog(__language__(30008),__language__(30009),__language__(30010))
115 dialog
.ok(__language__(30008),__language__(30009))
118 # Mark song as playing or played
119 def markSong(songid
, duration
, streamKey
, streamServerID
):
123 if player
.isPlayingAudio():
124 tNow
= player
.getTime()
125 if tNow
>= STREAM_MARKING_TIME
and songMarkTime
== 0:
126 groovesharkApi
.markStreamKeyOver30Secs(streamKey
, streamServerID
)
128 elif duration
> tNow
and duration
- tNow
< 2 and songMarkTime
>= STREAM_MARKING_TIME
:
131 groovesharkApi
.markSongComplete(songid
, streamKey
, streamServerID
)
137 def __init__( self
, *args
, **kwargs
):
138 self
.__dict
__.update( kwargs
)
140 # Window dialog to select a grooveshark playlist
141 class GroovesharkPlaylistSelect(xbmcgui
.WindowDialog
):
143 def __init__(self
, items
=[]):
144 gap
= int(self
.getHeight()/100)
145 w
= int(self
.getWidth()*0.5)
146 h
= self
.getHeight()-30*gap
148 rh
= self
.getHeight()
152 self
.imgBg
= xbmcgui
.ControlImage(x
+gap
, 5*gap
+y
, w
-2*gap
, h
-5*gap
, listBackground
)
153 self
.addControl(self
.imgBg
)
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
)
160 self
.isSelecting
= False
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
)
172 # Highlight selected item
173 def setHighlight(self
):
177 self
.isSelecting
= True
179 pos
= self
.playlistControl
.getSelectedPosition()
181 item
= self
.playlistControl
.getListItem(self
.lastPos
)
183 item
= self
.playlistControl
.getListItem(pos
)
186 self
.isSelecting
= False
189 def onControl(self
, control
):
190 if control
== self
.playlistControl
:
191 self
.selected
= self
.playlistControl
.getSelectedPosition()
194 # Action - close or up/down
195 def onAction(self
, action
):
196 if action
== ACTION_PREVIOUS_MENU
:
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
)
204 class PlayTimer(Thread
):
205 # interval -- floating point number specifying the number of seconds to wait before executing function
206 # function -- the function (or callable object) to be executed
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
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
219 self
.finished
= Event()
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
)
232 def setIterations(self
, iterations
):
233 self
.iterations
= iterations
237 return self
.iterations
* self
.interval
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'))
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')
264 def __init__( self
):
265 self
._handle
= int(sys
.argv
[1])
266 if os
.path
.isdir(cacheDir
) == False:
267 os
.makedirs(cacheDir
)
269 xbmc
.log(__language__(30012) + " " + cacheDir
)
270 artDir
= xbmc
.translatePath(thumbDir
)
271 if os
.path
.isdir(artDir
) == False:
274 xbmc
.log(__language__(30012) + " " + artDir
)
277 def categories(self
):
279 self
.userid
= self
._get
_login
()
282 xbmcplugin
.setPluginFanart(int(sys
.argv
[1]), self
.fanImg
)
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)
297 def searchSongs(self
):
298 query
= self
._get
_keyboard
(default
="", heading
=__language__(30020))
300 songs
= groovesharkApi
.getSongSearchResults(query
, limit
= self
.songsearchlimit
)
302 self
._add
_songs
_directory
(songs
)
304 dialog
= xbmcgui
.Dialog()
305 dialog
.ok(__language__(30008), __language__(30021))
311 def searchAlbums(self
):
312 query
= self
._get
_keyboard
(default
="", heading
=__language__(30022))
314 albums
= groovesharkApi
.getAlbumSearchResults(query
, limit
= self
.albumsearchlimit
)
315 if (len(albums
) > 0):
316 self
._add
_albums
_directory
(albums
)
318 dialog
= xbmcgui
.Dialog()
319 dialog
.ok(__language__(30008), __language__(30023))
325 def searchArtists(self
):
326 query
= self
._get
_keyboard
(default
="", heading
=__language__(30024))
328 artists
= groovesharkApi
.getArtistSearchResults(query
, limit
= self
.artistsearchlimit
)
329 if (len(artists
) > 0):
330 self
._add
_artists
_directory
(artists
)
332 dialog
= xbmcgui
.Dialog()
333 dialog
.ok(__language__(30008), __language__(30025))
338 # Search for playlists
339 def searchPlaylists(self
):
340 query
= self
._get
_keyboard
(default
="", heading
=__language__(30026))
342 playlists
= groovesharkApi
.getUserPlaylistsByUsername(query
)
343 if (len(playlists
) > 0):
344 self
._add
_playlists
_directory
(playlists
)
346 dialog
= xbmcgui
.Dialog()
347 dialog
.ok(__language__(30008), __language__(30027))
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))
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])
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
)
371 dialog
= xbmcgui
.Dialog()
372 dialog
.ok(__language__(30008), __language__(30029))
375 dialog
= xbmcgui
.Dialog()
376 dialog
.ok(__language__(30008), __language__(30030))
383 userid
= self
._get
_login
()
385 favorites
= groovesharkApi
.getUserFavoriteSongs()
386 if (len(favorites
) > 0):
387 self
._add
_songs
_directory
(favorites
, isFavorites
=True)
389 dialog
= xbmcgui
.Dialog()
390 dialog
.ok(__language__(30008), __language__(30031))
394 def popularSongs(self
):
395 popular
= groovesharkApi
.getPopularSongsToday(limit
= self
.songsearchlimit
)
396 if (len(popular
) > 0):
397 self
._add
_songs
_directory
(popular
)
399 dialog
= xbmcgui
.Dialog()
400 dialog
.ok(__language__(30008), __language__(30032))
405 userid
= self
._get
_login
()
407 playlists
= groovesharkApi
.getUserPlaylists()
408 if (len(playlists
) > 0):
409 self
._add
_playlists
_directory
(playlists
)
411 dialog
= xbmcgui
.Dialog()
412 dialog
.ok(__language__(30008), __language__(30033))
415 dialog
= xbmcgui
.Dialog()
416 dialog
.ok(__language__(30008), __language__(30034), __language__(30035))
418 # Make songs a favorite
419 def favorite(self
, songid
):
420 userid
= self
._get
_login
()
423 xbmc
.log("Favorite song: " + str(songid
))
424 groovesharkApi
.addUserFavoriteSong(songID
= songid
)
425 xbmc
.executebuiltin('XBMC.Notification(' + __language__(30008) + ', ' + __language__(30036) + ', 1000, ' + thumbDef
+ ')')
427 dialog
= xbmcgui
.Dialog()
428 dialog
.ok(__language__(30008), __language__(30034), __language__(30037))
430 # Remove song from favorites
431 def unfavorite(self
, songid
, prevMode
=0):
432 userid
= self
._get
_login
()
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
+ ")")
442 dialog
= xbmcgui
.Dialog()
443 dialog
.ok(__language__(30008), __language__(30034), __language__(30039))
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
)
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)
458 # There are likely songs for the artist even when no verified albums are found
459 self
.artistPopularSongs(artistid
)
461 # Show selected playlist
462 def playlist(self
, playlistid
, playlistname
):
463 userid
= self
._get
_login
()
465 songs
= groovesharkApi
.getPlaylistSongs(playlistid
)
466 self
._add
_songs
_directory
(songs
, trackLabelFormat
=NAME_ALBUM_ARTIST_LABEL
, playlistid
=playlistid
, playlistname
=playlistname
)
468 dialog
= xbmcgui
.Dialog()
469 dialog
.ok(__language__(30008), __language__(30034), __language__(30040))
471 # Search for artist show popular songs of the artist
472 def searchArtistPopularSongs(self
):
473 query
= self
._get
_keyboard
(default
="", heading
=__language__(30041))
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])
484 xbmc
.log("Found " + artist
[0] + "...")
485 self
.artistPopularSongs (artistID
)
487 dialog
= xbmcgui
.Dialog()
488 dialog
.ok(__language__(30008), __language__(30043))
493 # Show popular songs of the artist
494 def artistPopularSongs(self
, artistID
):
495 songs
= groovesharkApi
.getArtistPopularSongs(artistID
, limit
= self
.songsearchlimit
)
497 self
._add
_songs
_directory
(songs
, trackLabelFormat
=NAME_ALBUM_ARTIST_LABEL
)
499 dialog
= xbmcgui
.Dialog()
500 dialog
.ok(__language__(30008), __language__(30042))
504 def playSong(self
, item
):
508 # Get stream as it could have expired
511 songid
= item
.getProperty('songid')
512 stream
= groovesharkApi
.getSubscriberStreamKey(songid
)
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
)
522 xbmcplugin
.setResolvedUrl(handle
=int(sys
.argv
[1]), succeeded
=True, listitem
=item
)
524 xbmc
.log("Grooveshark playing: " + url
)
525 # Wait for play then start timer
527 while seconds
< STREAM_TIMEOUT
:
529 if player
.isPlayingAudio() == True:
530 if playTimer
!= None:
533 playTimer
= PlayTimer(1, markSong
, self
._setDuration
(duration
), [songid
, duration
, key
, server
])
538 seconds
= seconds
+ 1
540 xbmc
.log("No song URL")
542 xbmc
.log("No song stream")
544 xbmc
.executebuiltin('XBMC.Notification(' + __language__(30008) + ', ' + __language__(30044) + ', 1000, ' + thumbDef
+ ')')
546 # Make a song directory item
547 def songItem(self
, songid
, name
, album
, artist
, coverart
, trackLabelFormat
=ARTIST_ALBUM_NAME_LABEL
, tracknumber
=1):
549 stream
= self
._getSongStream
(songid
)
555 songImg
= self
._get
_icon
(coverart
, 'song-' + str(songid
) + "-image")
556 if int(trackLabelFormat
) == NAME_ALBUM_ARTIST_LABEL
:
557 trackLabel
= name
+ " - " + album
+ " - " + artist
559 trackLabel
= artist
+ " - " + album
+ " - " + name
560 item
= xbmcgui
.ListItem(label
= trackLabel
, thumbnailImage
=songImg
, iconImage
=songImg
)
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
)
576 xbmc
.log("No access to song URL")
580 def songPage(self
, offset
, trackLabelFormat
, playlistid
= 0, playlistname
= ''):
581 self
._add
_songs
_directory
([], trackLabelFormat
, offset
, playlistid
= playlistid
, playlistname
= playlistname
)
583 # Make a playlist from an album
584 def makePlaylist(self
, albumid
, name
):
585 userid
= self
._get
_login
()
587 re
.split(' - ',name
,1)
588 nameTokens
= re
.split(' - ',name
,1) # suggested name
589 name
= self
._get
_keyboard
(default
=nameTokens
[0], heading
=__language__(30045))
591 album
= groovesharkApi
.getAlbumSongs(albumid
, limit
= self
.songsearchlimit
)
594 songids
.append(song
[1])
595 if groovesharkApi
.createPlaylist(name
, songids
) == 0:
596 dialog
= xbmcgui
.Dialog()
597 dialog
.ok(__language__(30008), __language__(30046), name
)
599 xbmc
.executebuiltin('XBMC.Notification(' + __language__(30008) + ',' + __language__(30047)+ ', 1000, ' + thumbDef
+ ')')
601 dialog
= xbmcgui
.Dialog()
602 dialog
.ok(__language__(30008), __language__(30034), __language__(30048))
605 def renamePlaylist(self
, playlistid
, name
):
606 userid
= self
._get
_login
()
608 newname
= self
._get
_keyboard
(default
=name
, heading
=__language__(30049))
611 elif groovesharkApi
.playlistRename(playlistid
, newname
) == 0:
612 dialog
= xbmcgui
.Dialog()
613 dialog
.ok(__language__(30008), __language__(30050), name
)
615 # Refresh to show new item name
616 xbmc
.executebuiltin("Container.Refresh")
618 dialog
= xbmcgui
.Dialog()
619 dialog
.ok(__language__(30008), __language__(30034), __language__(30051))
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
()
627 if groovesharkApi
.playlistDelete(playlistid
) == 0:
628 dialog
= xbmcgui
.Dialog()
629 dialog
.ok(__language__(30008), __language__(30053), name
)
631 # Refresh to remove item from directory
632 xbmc
.executebuiltin("Container.Refresh(" + playlistsUrl
+ ")")
634 dialog
= xbmcgui
.Dialog()
635 dialog
.ok(__language__(30008), __language__(30034), __language__(30054))
637 # Add song to playlist
638 def addPlaylistSong(self
, songid
):
639 userid
= self
._get
_login
()
641 playlists
= groovesharkApi
.getUserPlaylists()
642 if (len(playlists
) > 0):
644 # Select the playlist
645 playlistSelect
= GroovesharkPlaylistSelect(items
=playlists
)
646 playlistSelect
.setFocus(playlistSelect
.playlistControl
)
647 playlistSelect
.doModal()
648 i
= playlistSelect
.selected
652 if i
>= len(playlists
):
653 name
= self
._get
_keyboard
(default
='', heading
=__language__(30055))
656 songIds
.append(songid
)
657 if groovesharkApi
.createPlaylist(name
, songIds
) == 0:
658 dialog
= xbmcgui
.Dialog()
659 dialog
.ok(__language__(30008), __language__(30056), name
)
661 xbmc
.executebuiltin('XBMC.Notification(' + __language__(30008) + ',' + __language__(30057) + ', 1000, ' + thumbDef
+ ')')
664 playlist
= playlists
[i
]
665 playlistid
= playlist
[1]
667 xbmc
.log("Add song " + str(songid
) + " to playlist " + str(playlistid
))
669 songs
= groovesharkApi
.getPlaylistSongs(playlistid
)
671 songIDs
.append(song
[1])
672 songIDs
.append(songid
)
673 ret
= groovesharkApi
.setPlaylistSongs(playlistid
, songIDs
)
675 dialog
= xbmcgui
.Dialog()
676 dialog
.ok(__language__(30008), __language__(30058))
678 xbmc
.executebuiltin('XBMC.Notification(' + __language__(30008) + ',' + __language__(30059) + ', 1000, ' + thumbDef
+ ')')
680 dialog
= xbmcgui
.Dialog()
681 dialog
.ok(__language__(30008), __language__(30060))
684 dialog
= xbmcgui
.Dialog()
685 dialog
.ok(__language__(30008), __language__(30034), __language__(30061))
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
()
693 songs
= groovesharkApi
.getPlaylistSongs(playlistID
)
696 if (song
[1] != songid
):
697 songIDs
.append(song
[1])
698 ret
= groovesharkApi
.setPlaylistSongs(playlistID
, songIDs
)
700 dialog
= xbmcgui
.Dialog()
701 dialog
.ok(__language__(30008), __language__(30064), __language__(30065))
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
) + ")")
707 dialog
= xbmcgui
.Dialog()
708 dialog
.ok(__language__(30008), __language__(30034), __language__(30067))
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
)
716 dialog
= xbmcgui
.Dialog()
717 dialog
.ok(__language__(30008), __language__(30068))
721 def _get_keyboard(self
, default
="", heading
="", hidden
=False):
722 kb
= xbmc
.Keyboard(default
, heading
, hidden
)
724 if (kb
.isConfirmed()):
725 return unicode(kb
.getText(), "utf-8")
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))
735 uid
= groovesharkApi
.login(self
.username
, self
.password
)
739 dialog
= xbmcgui
.Dialog()
740 dialog
.ok(__language__(30008), __language__(30069), __language__(30070), __language__(30082))
744 def _get_icon(self
, url
, songid
):
746 localThumb
= os
.path
.join(xbmc
.translatePath(os
.path
.join(thumbDir
, str(songid
)))) + '.tbn'
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())
756 shutil
.copy2(thumbDef
, localThumb
)
757 return os
.path
.join(os
.path
.join(thumbDir
, str(songid
))) + '.tbn'
761 # Add songs to directory
762 def _add_songs_directory(self
, songs
, trackLabelFormat
=ARTIST_ALBUM_NAME_LABEL
, offset
=0, playlistid
=0, playlistname
='', isFavorites
=False):
764 totalSongs
= len(songs
)
770 if offset
== 0 and totalSongs
<= self
.songspagelimit
:
772 xbmc
.log("Found " + str(totalSongs
) + " songs...")
775 # Cache all next pages songs
777 self
._setSavedSongs
(songs
)
779 songs
= self
._getSavedSongs
()
780 totalSongs
= len(songs
)
784 end
= min(start
+ self
.songspagelimit
,totalSongs
)
795 item
= self
.songItem(songid
, name
, album
, artist
, coverart
, trackLabelFormat
, (n
+1))
797 coverart
= item
.getProperty('coverart')
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="
808 if isFavorites
== True:
809 unfav
= unfav
+str(MODE_FAVORITES
)
811 menuItems
.append((__language__(30071), "XBMC.RunPlugin("+fav
+")"))
812 menuItems
.append((__language__(30072), "XBMC.RunPlugin("+unfav
+")"))
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
+")"))
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
)
822 end
= min(end
+ 1,totalSongs
)
824 xbmc
.log(song
[0] + " does not exist.")
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
)
831 xbmcplugin
.setContent(self
._handle
, 'songs')
832 xbmcplugin
.setPluginFanart(int(sys
.argv
[1]), self
.fanImg
)
834 # Add albums to directory
835 def _add_albums_directory(self
, albums
, artistid
=0, isverified
=False):
839 xbmc
.log("Found " + str(n
) + " albums...")
845 albumArtistName
= album
[0]
847 albumImage
= self
._get
_icon
(album
[4], 'album-' + str(albumID
))
848 self
._add
_dir
(albumName
+ " - " + albumArtistName
, '', MODE_ALBUM
, albumImage
, albumID
, itemsExisting
)
850 # Not supported by key
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
)
857 # Add artists to directory
858 def _add_artists_directory(self
, artists
):
862 xbmc
.log("Found " + str(n
) + " artists...")
867 artistName
= artist
[0]
868 self
._add
_dir
(artistName
, '', MODE_ARTIST
, self
.artistImg
, artistID
, itemsExisting
)
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
)
874 # Add playlists to directory
875 def _add_playlists_directory(self
, playlists
):
878 xbmc
.log("Found " + str(n
) + " playlists...")
881 playlist
= playlists
[i
]
882 playlistName
= playlist
[0]
883 playlistID
= playlist
[1]
884 self
._add
_dir
(playlistName
, '', MODE_PLAYLIST
, self
.playlistImg
, playlistID
, n
)
886 xbmcplugin
.setContent(self
._handle
, 'files')
887 xbmcplugin
.addSortMethod(self
._handle
, xbmcplugin
.SORT_METHOD_LABEL
)
888 xbmcplugin
.setPluginFanart(int(sys
.argv
[1]), self
.fanImg
)
890 # Add whatever directory
891 def _add_dir(self
, name
, url
, mode
, iconimage
, itemId
, items
=1):
894 u
=sys
.argv
[0]+"?mode="+str(mode
)+"&name="+urllib
.quote_plus(name
)+"&id="+str(itemId
)
897 directory
=xbmcgui
.ListItem(name
, iconImage
=iconimage
, thumbnailImage
=iconimage
)
898 directory
.setInfo( type="Music", infoLabels
={ "title": name
} )
899 directory
.setProperty('fanart_image', self
.fanImg
)
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
+")"))
912 directory
.addContextMenuItems(menuItems
, replaceItems
=False)
914 return xbmcplugin
.addDirectoryItem(handle
=int(sys
.argv
[1]),url
=u
,listitem
=directory
,isFolder
=True, totalItems
=items
)
916 def _getSavedSongs(self
):
917 path
= os
.path
.join(cacheDir
, 'songs.dmp')
920 songs
= pickle
.load(f
)
927 def _setSavedSongs(self
, songs
):
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')
934 pickle
.dump(songs
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
937 xbmc
.log("An error occurred saving songs")
940 # Duration to seconds
941 def _setDuration(self
, usecs
):
943 usecs
= usecs
* 10 # Some durations are 10x to small
944 return int(usecs
/ 1000000)
946 def _getSongStream(self
, songid
):
950 path
= os
.path
.join(cacheDir
, 'streams.dmp')
953 streams
= pickle
.load(f
)
954 song
= streams
[songid
]
960 stream
= [idSong
, duration
, url
, key
, server
]
963 xbmc
.log("Found " + str(idSong
) + " in stream cache")
971 stream
= groovesharkApi
.getSubscriberStreamKey(songid
)
972 if stream
!= False and stream
['url'] != '':
973 duration
= self
._setDuration
(stream
['uSecs'])
975 key
= stream
['StreamKey']
976 server
= stream
['StreamServerID']
977 stream
= [idSong
, duration
, url
, key
, server
]
978 self
._addSongStream
(stream
)
982 def _addSongStream(self
, stream
):
983 streams
= self
._getStreams
()
984 streams
[int(stream
[0])] = stream
985 path
= os
.path
.join(cacheDir
, 'streams.dmp')
988 pickle
.dump(streams
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
991 xbmc
.log("Added " + str(stream
[0]) + " to stream cache")
993 xbmc
.log("An error occurred adding to stream")
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')
1002 streams
[idStream
] = stream
1003 f
= open(path
, 'wb')
1004 pickle
.dump(streams
, f
, protocol
=pickle
.HIGHEST_PROTOCOL
)
1007 xbmc
.log("Updated " + str(idStream
) + " in stream cache")
1010 xbmc
.log("An error occurred setting stream")
1012 def _getStreams(self
):
1013 path
= os
.path
.join(cacheDir
, 'streams.dmp')
1015 f
= open(path
, 'rb')
1016 streams
= pickle
.load(f
)
1024 # Parse URL parameters
1027 paramstring
=sys
.argv
[2]
1029 xbmc
.log(paramstring
)
1030 if len(paramstring
)>=2:
1032 cleanedparams
=params
.replace('?','')
1033 if (params
[len(params
)-1]=='/'):
1034 params
=params
[0:len(params
)-2]
1035 pairsofparams
=cleanedparams
.split('&')
1037 for i
in range(len(pairsofparams
)):
1039 splitparams
=pairsofparams
[i
].split('=')
1040 if (len(splitparams
))==2:
1041 param
[splitparams
[0]]=splitparams
[1]
1045 grooveshark
= Grooveshark();
1049 try: mode
=int(params
["mode"])
1052 try: itemId
=int(params
["id"])
1055 try: name
=urllib
.unquote_plus(params
["name"])
1058 # Call function for URL
1060 grooveshark
.categories()
1062 elif mode
==MODE_SEARCH_SONGS
:
1063 grooveshark
.searchSongs()
1065 elif mode
==MODE_SEARCH_ALBUMS
:
1066 grooveshark
.searchAlbums()
1068 elif mode
==MODE_SEARCH_ARTISTS
:
1069 grooveshark
.searchArtists()
1071 elif mode
==MODE_SEARCH_ARTISTS_ALBUMS
:
1072 grooveshark
.searchArtistsAlbums(name
)
1074 elif mode
==MODE_SEARCH_PLAYLISTS
:
1075 grooveshark
.searchPlaylists()
1077 elif mode
==MODE_POPULAR_SONGS
:
1078 grooveshark
.popularSongs()
1080 elif mode
==MODE_ARTIST_POPULAR
:
1081 grooveshark
.searchArtistPopularSongs()
1083 elif mode
==MODE_FAVORITES
:
1084 grooveshark
.favorites()
1086 elif mode
==MODE_PLAYLISTS
:
1087 grooveshark
.playlists()
1089 elif mode
==MODE_SONG_PAGE
:
1090 try: offset
=urllib
.unquote_plus(params
["offset"])
1092 try: label
=urllib
.unquote_plus(params
["label"])
1094 grooveshark
.songPage(offset
, label
, itemId
, name
)
1096 elif mode
==MODE_SONG
:
1097 try: album
=urllib
.unquote_plus(params
["album"])
1099 try: artist
=urllib
.unquote_plus(params
["artist"])
1101 try: coverart
=urllib
.unquote_plus(params
["coverart"])
1103 song
= grooveshark
.songItem(itemId
, name
, album
, artist
, coverart
)
1104 grooveshark
.playSong(song
)
1106 elif mode
==MODE_ARTIST
:
1107 grooveshark
.artist(itemId
)
1109 elif mode
==MODE_ALBUM
:
1110 grooveshark
.album(itemId
)
1112 elif mode
==MODE_PLAYLIST
:
1113 grooveshark
.playlist(itemId
, name
)
1115 elif mode
==MODE_FAVORITE
:
1116 grooveshark
.favorite(itemId
)
1118 elif mode
==MODE_UNFAVORITE
:
1119 try: prevMode
=int(urllib
.unquote_plus(params
["prevmode"]))
1122 grooveshark
.unfavorite(itemId
, prevMode
)
1124 elif mode
==MODE_SIMILAR_ARTISTS
:
1125 grooveshark
.similarArtists(itemId
)
1127 elif mode
==MODE_MAKE_PLAYLIST
:
1128 grooveshark
.makePlaylist(itemId
, name
)
1130 elif mode
==MODE_REMOVE_PLAYLIST
:
1131 grooveshark
.removePlaylist(itemId
, name
)
1133 elif mode
==MODE_RENAME_PLAYLIST
:
1134 grooveshark
.renamePlaylist(itemId
, name
)
1136 elif mode
==MODE_REMOVE_PLAYLIST_SONG
:
1137 try: playlistID
=urllib
.unquote_plus(params
["playlistid"])
1139 grooveshark
.removePlaylistSong(playlistID
, name
, itemId
)
1141 elif mode
==MODE_ADD_PLAYLIST_SONG
:
1142 grooveshark
.addPlaylistSong(itemId
)
1144 elif mode
==MODE_ARTIST_POPULAR_FROM_ALBUMS
:
1145 grooveshark
.artistPopularSongs(itemId
)
1147 if mode
< MODE_SONG
:
1148 xbmcplugin
.endOfDirectory(int(sys
.argv
[1]))