-
Benoît Harrault authoredBenoît Harrault authored
gui.py 12.72 KiB
import requests
import random
import xbmc
import xbmcaddon
import xbmcgui
import json
# Plugin data and configuration
ADDON_ID = 'script.spotify.screensaver'
SETTINGS = xbmcaddon.Addon(id=ADDON_ID)
ADDON_NAME = SETTINGS.getAddonInfo('name')
ADDON_VERSION = SETTINGS.getAddonInfo('version')
KODI_MONITOR = xbmc.Monitor()
# Spotify setting
SPOTIFY_API_BASE_URL = 'https://api.spotify.com/v1/'
SPOTIFY_AUTH_URL = 'https://accounts.spotify.com/api/token'
LIBRESPOT_EVENT_FILE = '/tmp/spotify'
class GUI(xbmcgui.WindowXMLDialog):
def __init__(self, *args, **kwargs):
self.isExiting = False
def log(self, msg, level=xbmc.LOGDEBUG):
if self.force_debug:
level = xbmc.LOGERROR
xbmc.log(ADDON_ID + ' - ' + ADDON_VERSION + ' - ' + msg, level)
def onInit(self):
# load vars
self._get_vars()
# get addon settings
self._get_settings()
# set animation time on skin
xbmcgui.Window(10000).setProperty('picture_animation', self.animation)
# get spotify access token
self._init_spotify_access_token()
# get the images
self._init_images()
# start slideshow
self._start_show()
def _get_vars(self):
# get the screensaver window id
self.winid = xbmcgui.Window(xbmcgui.getCurrentWindowDialogId())
# init the monitor class to catch onscreensaverdeactivated calls
self.Monitor = MyMonitor(action=self._exit)
self.stop = False
self.last_spotify_event = ''
self.current_track_id = ''
def _get_settings(self):
# read addon settings
SETTINGS = xbmcaddon.Addon(id=ADDON_ID)
self.addon_path = (SETTINGS.getAddonInfo('path').decode('utf-8'))
self.setting_location = SETTINGS.getAddonInfo(
'profile').decode('utf-8')
self.slideshow_time = [30, 60, 120, 240][int(
SETTINGS.getSetting("RotateTime"))]
self.random = SETTINGS.getSetting("Randomize") == 'true'
self.animation = 'okay' if SETTINGS.getSetting(
"Animate") == 'true' else 'nope'
self.spotify_client_id = SETTINGS.getSetting(
'SpotifyClientId').decode('utf-8')
self.spotify_client_secret = SETTINGS.getSetting(
'SpotifyClientSecret').decode('utf-8')
self.force_debug = SETTINGS.getSetting('ForceDebug') == 'true'
def _init_spotify_access_token(self):
auth_response = requests.post(SPOTIFY_AUTH_URL, {
'grant_type': 'client_credentials',
'client_id': self.spotify_client_id,
'client_secret': self.spotify_client_secret,
})
# convert the response to JSON
auth_response_data = auth_response.json()
# save the access token
self.spotify_access_token = auth_response_data['access_token']
self.log('spotify access_token: ' + str(self.spotify_access_token))
def _init_images(self):
self.PanelItems = self.getControl(101)
self.PanelItems.reset()
self._parse_spotify_event(self._get_last_spotify_event())
def _start_show(self):
# loop until onScreensaverDeactivated is called
while (not self.Monitor.abortRequested()) and (not self.stop):
xbmc.executebuiltin('SetFocus(101)')
self.next = False
countdown = self.slideshow_time
# display the image for the specified amount of time
while (
(not self.Monitor.abortRequested())
and (not self.stop)
and (not self.next)
and countdown > 0
):
countdown -= 1
xbmc.sleep(1000)
new_last_spotify_event = self._get_last_spotify_event()
if (self.last_spotify_event != new_last_spotify_event):
self._parse_spotify_event(new_last_spotify_event)
self.last_spotify_event = new_last_spotify_event
# break out of the for loop if onScreensaverDeactivated is called
if self.stop or self.Monitor.abortRequested():
break
# next (random) image
seek = str(random.randint(1, 7)) if self.random else '1'
xbmc.executebuiltin("Control.Move(101,%s)" % seek)
def _parse_spotify_event(self, spotify_event):
self.log('spotify event: ' + spotify_event)
event_split = spotify_event.split(':')
event_type = event_split[1]
event_value = event_split[2] if len(event_split) > 2 else ''
if event_type == 'changed' or event_type == 'playing':
if (self.current_track_id != event_value):
self._reload_images(event_value)
self.next = True
elif event_type == 'preloading':
self._preload_images(event_value)
elif (
event_type == 'paused'
or event_type == 'stopped'
or event_type == 'sink'
):
self._remove_images()
self.next = True
elif event_type == 'volume_set':
self._unimplemented_event_type(event_type, event_value)
else:
self.log('unknown event type: ' + event_type, xbmc.LOGERROR)
def _unimplemented_event_type(self, event_type, event_value):
self.log(
'unimplemented event type: ' + str(event_type)
+ ' (value: ' + str(event_value) + ')'
)
def _preload_images(self, track_id):
self.log('preload images for track: ' + track_id)
track_data = self._get_track_data(track_id)
if track_data is None:
self.log('failed to get next track data', xbmc.LOGERROR)
else:
track_name = track_data.get('track_name')
track_artists = track_data.get('track_artists')
track_image_url = track_data.get('track_image_url')
if ((track_name is None)
or (track_artists is None)
or (track_image_url is None)):
self.log('failed to parse next track data', xbmc.LOGERROR)
else:
artists_string = ', '.join(track_artists).replace('"', '\"')
notification = (
'Notification("Next:", "'
+ str(artists_string) + ' - ' + str(track_name)
+ '", 29000, ' + str(track_image_url) + ')'
)
xbmc.executebuiltin(notification)
def _reload_images(self, track_id):
self.log('load images for track: ' + track_id)
self.PanelItems = self.getControl(101)
self.PanelItems.reset()
self.PanelItems.addItems(self._build_items_list(track_id))
self.current_track_id = track_id
def _remove_images(self):
self.log('no played track. remove images')
self.PanelItems = self.getControl(101)
self.PanelItems.reset()
self.current_track_id = ''
def _fix_encoding(self, string):
return str(string.encode('utf-8'))
def _get_track_data(self, track_id, try_count=1):
self.log('track_id: [' + str(track_id) + ']')
self.log('(try: ' + str(try_count) + ')')
track_data = {}
headers = {
'Authorization': 'Bearer {token}'.format(
token=self.spotify_access_token
)
}
try:
response = requests.get(
SPOTIFY_API_BASE_URL + 'tracks/' + track_id,
headers=headers
)
data = response.json()
track_data['track_name'] = self._fix_encoding(data.get('name', ''))
track_data['track_artists'] = []
# album/track image
album = data.get('album')
if album is None:
self.log(
'failed to get album from API: '
+ json.dumps(data), xbmc.LOGERROR)
error_string = ('{"error": {"status": 401, '
+ '"message": "The access token expired"}}')
if json.dumps(data) == error_string:
if try_count < 3:
self.log('trying to refresh access token')
self._init_spotify_access_token()
self.log('retry get image')
return self._get_track_data(track_id, try_count + 1)
else:
self.log('giving up', xbmc.LOGERROR)
else:
self.log('unknown error getting image', xbmc.LOGERROR)
else:
self.log('ok got album from API: ' + json.dumps(album))
# get artists
artists = album.get('artists')
if artists is None:
self.log(
'failed to get artists from API: '
+ json.dumps(album), xbmc.LOGERROR)
else:
self.log('ok got artists from API: ' + json.dumps(artists))
for artist in artists:
artist_name = artist.get('name')
if artist_name is None:
self.log(
'failed to get artist_name from API: '
+ json.dumps(artist), xbmc.LOGERROR)
else:
self.log(
'ok got artist_name from API: '
+ json.dumps(artist_name))
track_data['track_artists'].append(
self._fix_encoding(artist_name))
# get images
images = album.get('images')
if images is None:
self.log(
'failed to get images from API: '
+ json.dumps(album), xbmc.LOGERROR)
else:
self.log('ok got images from API: ' + json.dumps(images))
image = images[0]
if image is None:
self.log(
'failed to get image from API: '
+ json.dumps(images), xbmc.LOGERROR)
else:
self.log('ok got image from API: ' + json.dumps(image))
track_image_url = image.get('url')
if track_image_url is None:
self.log(
'failed to get track_image_url from API: '
+ json.dumps(image), xbmc.LOGERROR)
else:
self.log(
'ok got track_image_url from API: '
+ track_image_url)
track_data['track_image_url'] = track_image_url
except Exception as e:
self.log('failed to get track data: ' + str(e), xbmc.LOGERROR)
self.log('track_name: ' + track_data.get('track_name', ''))
self.log('track_artists: ' + str(track_data.get('track_artists', [])))
self.log('track_image_url: ' + track_data.get('track_image_url', ''))
return track_data
def _build_items_list(self, track_id):
imageLST = []
track_data = self._get_track_data(track_id)
if track_data is None:
self.log('failed to get next track data', xbmc.LOGERROR)
else:
track_name = track_data.get('track_name')
track_artists = track_data.get('track_artists')
track_image_url = track_data.get('track_image_url')
if ((track_name is None)
or (track_artists is None)
or (track_image_url is None)):
self.log('failed to parse next track data', xbmc.LOGERROR)
else:
artists_string = ', '.join(track_artists)
imageLST.append(
xbmcgui.ListItem(
artists_string + ' - ' + track_name,
thumbnailImage=track_image_url
)
)
return imageLST
def onFocus(self, controlId):
pass
def onClick(self, controlId):
pass
def onAction(self, action):
self.isExiting = True
self.close()
def _get_last_spotify_event(self):
last_event = ''
try:
f = open(LIBRESPOT_EVENT_FILE, 'r')
last_event = f.readlines()[-1]
finally:
f.close()
last_event = ''.join(last_event.splitlines())
return last_event
def _exit(self):
self.stop = True
self.close()
class MyMonitor(xbmc.Monitor):
def __init__(self, *args, **kwargs):
self.action = kwargs['action']
def onScreensaverDeactivated(self):
self.action()
def onDPMSActivated(self):
self.action()