Merge pull request #2953 from awiouy/ls-next

librespot: update to a4e0f58
This commit is contained in:
CvH 2018-09-05 21:58:22 +02:00 committed by GitHub
commit a3c73c3370
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 376 additions and 214 deletions

View File

@ -2,7 +2,7 @@
# Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv) # Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv)
PKG_NAME="rust" PKG_NAME="rust"
PKG_VERSION="1.26.0" PKG_VERSION="1.28.0"
PKG_ARCH="any" PKG_ARCH="any"
PKG_LICENSE="MIT" PKG_LICENSE="MIT"
PKG_SITE="https://www.rust-lang.org" PKG_SITE="https://www.rust-lang.org"

View File

@ -1,3 +1,13 @@
112
- Update to a4e0f58
- Rework Python
- Correct codec in Kodi mode
- Fix setting change
- Fix wizard
- Fix zapping issue
- Display album, artist, icon and title
- Wait for librespot.onevent to finish
111 111
- Update to 431be9e - Update to 431be9e
- Fix delay with Kodi playback option - Fix delay with Kodi playback option

View File

@ -3,9 +3,9 @@
# Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv) # Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv)
PKG_NAME="librespot" PKG_NAME="librespot"
PKG_VERSION="431be9e" PKG_VERSION="a4e0f582a8c705b05c8abba58d9e9c1c06ad532d"
PKG_SHA256="2e336c5415b6ee6f669e673282daccdd770b15d35dd6d71b39b17dc2aa3424c0" PKG_SHA256="63ed879d7185f16963316b0c3149a40875260f5403b2c55c6cdb470e91b7741d"
PKG_REV="111" PKG_REV="112"
PKG_ARCH="any" PKG_ARCH="any"
PKG_LICENSE="MIT" PKG_LICENSE="MIT"
PKG_SITE="https://github.com/librespot-org/librespot/" PKG_SITE="https://github.com/librespot-org/librespot/"

View File

@ -1,30 +1,23 @@
commit 55439529ae313eac5d946aa751387fa747cc6bc4
Author: awiouy <awiouy@gmail.com>
Date: Wed Jun 13 17:39:54 2018 +0200
libreelec: kodi hooks
diff --git a/playback/src/player.rs b/playback/src/player.rs diff --git a/playback/src/player.rs b/playback/src/player.rs
index dd99423..365c108 100644 index ab1a8ab..0aa0630 100644
--- a/playback/src/player.rs --- a/playback/src/player.rs
+++ b/playback/src/player.rs +++ b/playback/src/player.rs
@@ -17,7 +17,7 @@ use core::spotify_id::SpotifyId; @@ -49,15 +49,18 @@ enum PlayerCommand {
use audio::{AudioDecrypt, AudioFile};
use audio::{VorbisDecoder, VorbisPacket};
use audio_backend::Sink;
-use metadata::{FileFormat, Metadata, Track};
+use metadata::{FileFormat, Metadata, Track, Artist};
use mixer::AudioFilter;
pub struct Player {
@@ -49,15 +49,22 @@ enum PlayerCommand {
pub enum PlayerEvent { pub enum PlayerEvent {
Started { Started {
track_id: SpotifyId, track_id: SpotifyId,
+ track: Track,
+ artist: Artist,
+ new_state: String, + new_state: String,
}, },
Changed { Changed {
old_track_id: SpotifyId, old_track_id: SpotifyId,
new_track_id: SpotifyId, new_track_id: SpotifyId,
+ track: Track,
+ artist: Artist,
+ new_state: String, + new_state: String,
}, },
@ -34,17 +27,7 @@ index dd99423..365c108 100644
}, },
} }
@@ -404,6 +411,9 @@ impl PlayerInternal { @@ -413,11 +416,18 @@ impl PlayerInternal {
match self.load_track(track_id, position as i64) {
Some((decoder, normalisation_factor)) => {
+ let track = Track::get(&self.session, track_id).wait().unwrap();
+ let artist = Artist::get(&self.session, track.artists[0]).wait().unwrap();
+
if play {
match self.state {
PlayerState::Playing {
@@ -413,11 +423,20 @@ impl PlayerInternal {
| PlayerState::EndOfTrack { | PlayerState::EndOfTrack {
track_id: old_track_id, track_id: old_track_id,
.. ..
@ -58,19 +41,17 @@ index dd99423..365c108 100644
+ self.send_event(PlayerEvent::Changed { + self.send_event(PlayerEvent::Changed {
+ old_track_id: old_track_id, + old_track_id: old_track_id,
+ new_track_id: track_id, + new_track_id: track_id,
+ track: track,
+ artist: artist,
+ new_state: new_state, + new_state: new_state,
+ }); + });
+ }, + },
+ _ => { + _ => {
+ let new_state = "play".to_string(); + let new_state = "play".to_string();
+ self.send_event(PlayerEvent::Started { track_id, track, artist, new_state }); + self.send_event(PlayerEvent::Started { track_id, new_state });
+ }, + },
} }
self.start_sink(); self.start_sink();
@@ -443,13 +462,20 @@ impl PlayerInternal { @@ -443,13 +453,18 @@ impl PlayerInternal {
| PlayerState::EndOfTrack { | PlayerState::EndOfTrack {
track_id: old_track_id, track_id: old_track_id,
.. ..
@ -83,8 +64,6 @@ index dd99423..365c108 100644
+ self.send_event(PlayerEvent::Changed { + self.send_event(PlayerEvent::Changed {
+ old_track_id: old_track_id, + old_track_id: old_track_id,
+ new_track_id: track_id, + new_track_id: track_id,
+ track: track,
+ artist: artist,
+ new_state: new_state, + new_state: new_state,
+ }) + })
+ }, + },
@ -96,19 +75,17 @@ index dd99423..365c108 100644
} }
} }
@@ -474,7 +500,10 @@ impl PlayerInternal { @@ -474,7 +489,8 @@ impl PlayerInternal {
if let PlayerState::Paused { track_id, .. } = self.state { if let PlayerState::Paused { track_id, .. } = self.state {
self.state.paused_to_playing(); self.state.paused_to_playing();
- self.send_event(PlayerEvent::Started { track_id }); - self.send_event(PlayerEvent::Started { track_id });
+ let track = Track::get(&self.session, track_id).wait().unwrap();
+ let artist = Artist::get(&self.session, track.artists[0]).wait().unwrap();
+ let new_state = "play".to_string(); + let new_state = "play".to_string();
+ self.send_event(PlayerEvent::Started { track_id, track, artist, new_state }); + self.send_event(PlayerEvent::Started { track_id, new_state });
self.start_sink(); self.start_sink();
} else { } else {
warn!("Player::play called from invalid state"); warn!("Player::play called from invalid state");
@@ -486,7 +515,8 @@ impl PlayerInternal { @@ -486,7 +502,8 @@ impl PlayerInternal {
self.state.playing_to_paused(); self.state.playing_to_paused();
self.stop_sink_if_running(); self.stop_sink_if_running();
@ -118,7 +95,7 @@ index dd99423..365c108 100644
} else { } else {
warn!("Player::pause called from invalid state"); warn!("Player::pause called from invalid state");
} }
@@ -497,7 +527,8 @@ impl PlayerInternal { @@ -497,7 +514,8 @@ impl PlayerInternal {
| PlayerState::Paused { track_id, .. } | PlayerState::Paused { track_id, .. }
| PlayerState::EndOfTrack { track_id } => { | PlayerState::EndOfTrack { track_id } => {
self.stop_sink_if_running(); self.stop_sink_if_running();
@ -129,36 +106,34 @@ index dd99423..365c108 100644
} }
PlayerState::Stopped => { PlayerState::Stopped => {
diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs
index b6a653d..f746c8f 100644 index b6a653d..7549e00 100644
--- a/src/player_event_handler.rs --- a/src/player_event_handler.rs
+++ b/src/player_event_handler.rs +++ b/src/player_event_handler.rs
@@ -18,18 +18,28 @@ pub fn run_program_on_events(event: PlayerEvent, onevent: &str) { @@ -18,18 +18,22 @@ pub fn run_program_on_events(event: PlayerEvent, onevent: &str) {
PlayerEvent::Changed { PlayerEvent::Changed {
old_track_id, old_track_id,
new_track_id, new_track_id,
+ track,
+ artist,
+ new_state, + new_state,
} => { } => {
env_vars.insert("PLAYER_EVENT", "change".to_string()); env_vars.insert("PLAYER_EVENT", "change".to_string());
env_vars.insert("OLD_TRACK_ID", old_track_id.to_base16()); - env_vars.insert("OLD_TRACK_ID", old_track_id.to_base16());
env_vars.insert("TRACK_ID", new_track_id.to_base16()); - env_vars.insert("TRACK_ID", new_track_id.to_base16());
+ env_vars.insert("TITLE", track.name.to_string()); + env_vars.insert("OLD_TRACK_ID", old_track_id.to_base62());
+ env_vars.insert("ARTIST", artist.name.to_string()); + env_vars.insert("TRACK_ID", new_track_id.to_base62());
+ env_vars.insert("STATE", new_state.to_string()); + env_vars.insert("STATE", new_state.to_string());
} }
- PlayerEvent::Started { track_id } => { - PlayerEvent::Started { track_id } => {
+ PlayerEvent::Started { track_id, track, artist, new_state } => { + PlayerEvent::Started { track_id, new_state } => {
env_vars.insert("PLAYER_EVENT", "start".to_string()); env_vars.insert("PLAYER_EVENT", "start".to_string());
env_vars.insert("TRACK_ID", track_id.to_base16()); - env_vars.insert("TRACK_ID", track_id.to_base16());
+ env_vars.insert("TITLE", track.name.to_string()); + env_vars.insert("TRACK_ID", track_id.to_base62());
+ env_vars.insert("ARTIST", artist.name.to_string());
+ env_vars.insert("STATE", new_state.to_string()); + env_vars.insert("STATE", new_state.to_string());
} }
- PlayerEvent::Stopped { track_id } => { - PlayerEvent::Stopped { track_id } => {
+ PlayerEvent::Stopped { track_id, new_state } => { + PlayerEvent::Stopped { track_id, new_state } => {
env_vars.insert("PLAYER_EVENT", "stop".to_string()); env_vars.insert("PLAYER_EVENT", "stop".to_string());
env_vars.insert("TRACK_ID", track_id.to_base16()); - env_vars.insert("TRACK_ID", track_id.to_base16());
+ env_vars.insert("TRACK_ID", track_id.to_base62());
+ env_vars.insert("STATE", new_state.to_string()); + env_vars.insert("STATE", new_state.to_string());
} }
} }

View File

@ -0,0 +1,23 @@
From abcd8697b46924f8a18d733fc9d2bf884e901a46 Mon Sep 17 00:00:00 2001
From: leszekb <leszek@control-by.net>
Date: Mon, 14 May 2018 22:42:34 +0200
Subject: [PATCH] Update player_event_handler.rs
---
src/player_event_handler.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs
index b6a653d..6064bc0 100644
--- a/src/player_event_handler.rs
+++ b/src/player_event_handler.rs
@@ -8,7 +8,7 @@ fn run_program(program: &str, env_vars: HashMap<&str, String>) {
Command::new(&v.remove(0))
.args(&v)
.envs(env_vars.iter())
- .spawn()
+ .status()
.expect("program failed to start");
}

View File

@ -1,24 +1,22 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv) # Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv)
import alsaaudio as alsa import alsaaudio
import xbmcaddon import xbmcaddon
import xbmcgui import xbmcgui
dialog = xbmcgui.Dialog() dialog = xbmcgui.Dialog()
strings = xbmcaddon.Addon().getLocalizedString strings = xbmcaddon.Addon().getLocalizedString
while True: while True:
pcms = alsa.pcms()[1:] pcms = alsaaudio.pcms()[1:]
if len(pcms) == 0: if len(pcms) == 0:
dialog.ok(xbmcaddon.Addon().getAddonInfo('name'), strings(30210)) dialog.ok(xbmcaddon.Addon().getAddonInfo('name'), strings(30210))
break break
pcmx = dialog.select(strings(30115), pcms) pcmx = dialog.select(strings(30115), pcms)
if pcmx == -1: if pcmx == -1:
break break
pcm = pcms[pcmx] pcm = pcms[pcmx]
xbmcaddon.Addon().setSetting('ls_o', pcm) xbmcaddon.Addon().setSetting('ls_o', pcm)
break break
del dialog del dialog

View File

@ -1,2 +1,2 @@
#!/bin/sh #!/bin/sh
echo -e "$STATE\n$ARTIST\n$TITLE" > "$LS_FIFO" echo -e "$STATE\n$TRACK_ID" > "$LS_FIFO"

View File

@ -3,6 +3,9 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv) # Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv)
f="/storage/.kodi/userdata/addon_data/service.librespot/settings.xml"
[ -f "$f" ] && sed -i 's/ls_O/ls_m/g' "$f"
activate_card() { activate_card() {
if [ -e "/proc/asound/$1" ]; then if [ -e "/proc/asound/$1" ]; then
return return
@ -50,7 +53,7 @@ init_alsa() {
index="${index%%,*}" index="${index%%,*}"
card="card$index" card="card$index"
activate_card "$card" activate_card "$card"
;; ;;
*) *)
if [ -n "$ls_o" ]; then if [ -n "$ls_o" ]; then
echo "Unknown playback device specification $ls_o" echo "Unknown playback device specification $ls_o"
@ -85,7 +88,7 @@ if [ -n "$ls_p" -a -n "$ls_u" ]; then
--username \"$ls_u\"" --username \"$ls_u\""
fi fi
if [ "$ls_O" == "Kodi" ]; then if [ "$ls_m" == "Kodi" ]; then
LIBRESPOT="$LIBRESPOT --backend pulseaudio --device-type TV" LIBRESPOT="$LIBRESPOT --backend pulseaudio --device-type TV"
else else
init_alsa init_alsa

View File

@ -2,125 +2,12 @@
# Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv) # Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv)
import os import os
import stat
import subprocess
import sys import sys
import threading
import xbmc
import xbmcaddon
import xbmcgui
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'resources', 'lib'))
PORT = '6666' from ls_monitor import Monitor as Monitor
SINK = 'librespot_sink'
def suspendSink(bit):
subprocess.call(['pactl', 'suspend-sink', SINK, bit])
def systemctl(command):
subprocess.call(['systemctl', command, xbmcaddon.Addon().getAddonInfo('id')])
class Controller(threading.Thread):
FIFO = '/var/run/librespot'
def __init__(self, player):
super(Controller, self).__init__()
self.player = player
def run(self):
try:
os.unlink(self.FIFO)
except OSError:
pass
os.mkfifo(self.FIFO)
while os.path.exists(self.FIFO) and stat.S_ISFIFO(os.stat(self.FIFO).st_mode):
with open(self.FIFO, 'r') as fifo:
command = fifo.read().splitlines()
if len(command) == 0:
break
elif command[0] == 'play' and len(command) == 3:
dialog = xbmcgui.Dialog()
dialog.notification(command[1],
command[2],
icon=xbmcaddon.Addon().getAddonInfo('icon'),
sound=False)
del dialog
self.player.play()
elif command[0] == 'stop':
self.player.stop()
elif command[0] == 'pause':
self.player.pause()
try:
os.unlink(self.FIFO)
except OSError:
pass
def stop(self):
with open(self.FIFO, 'w') as fifo:
fifo.close()
class Player(xbmc.Player):
ITEM = 'rtp://127.0.0.1:{port}'.format(port=PORT)
def __init__(self):
super(Player, self).__init__(self)
if self.isPlaying():
self.onPlayBackStarted()
def onPlayBackEnded(self):
suspendSink('1')
xbmc.sleep(1000)
if not self.isPlaying():
systemctl('restart')
def onPlayBackStarted(self):
if self.getPlayingFile() != self.ITEM:
suspendSink('1')
systemctl('stop')
def onPlayBackStopped(self):
systemctl('restart')
def play(self):
if not self.isPlaying() and xbmcaddon.Addon().getSetting('ls_O') == 'Kodi':
suspendSink('0')
listitem = xbmcgui.ListItem(xbmcaddon.Addon().getAddonInfo('name'))
listitem.addStreamInfo('audio',{'codec': 'mp3'})
listitem.setArt({'thumb': xbmcaddon.Addon().getAddonInfo('icon')})
super(Player, self).play(self.ITEM, listitem)
del listitem
xbmcgui.Window(12006).show()
def pause(self):
if self.isPlaying() and self.getPlayingFile() == self.ITEM:
super(Player, self).pause()
def stop(self):
suspendSink('1')
if self.isPlaying() and self.getPlayingFile() == self.ITEM:
super(Player, self).stop()
else:
systemctl('restart')
class Monitor(xbmc.Monitor):
def __init__(self, player):
super(Monitor, self).__init__(self)
self.player = player
def onSettingsChanged(self):
self.player.stop()
if __name__ == '__main__': if __name__ == '__main__':
player = Player() Monitor().waitForAbort()
controller = Controller(player)
controller.start()
Monitor(player).waitForAbort()
controller.stop()
controller.join()

View File

@ -28,7 +28,7 @@ msgid "320"
msgstr "" msgstr ""
msgctxt "#30107" msgctxt "#30107"
msgid "Output" msgid "Output mode"
msgstr "" msgstr ""
msgctxt "#30108" msgctxt "#30108"
@ -48,7 +48,7 @@ msgid "Password"
msgstr "" msgstr ""
msgctxt "#30112" msgctxt "#30112"
msgid "Discovery mode (set username and password to disable)" msgid "Discovery mode"
msgstr "" msgstr ""
msgctxt "#30113" msgctxt "#30113"

View File

@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv)
import subprocess
import xbmcaddon
from ls_log import log as log
_SERVICE = xbmcaddon.Addon().getAddonInfo('id')
_SINK = 'librespot_sink'
def _pactl(bit):
log('pactl {}'.format(bit))
subprocess.call(['pactl', 'suspend-sink', _SINK, bit])
def _systemctl(command):
log('systemctl {}'.format(command))
subprocess.call(['systemctl', command, _SERVICE])
_pactl('1')
class Librespot():
def __init__(self):
self.state = True
def restart(self):
log('restarting librespot')
_systemctl('restart')
self.state = True
def stop(self):
if self.state:
log('stopping librespot')
_systemctl('stop')
self.state = False
def unsuspend(self):
_pactl('0')

View File

@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2018-present Team LibreELEC (https://libreelec.tv)
import xbmc
import xbmcaddon
_MESSAGE = xbmcaddon.Addon().getAddonInfo('name') + ': {}'
def log(message):
xbmc.log(_MESSAGE.format(message), xbmc.LOGNOTICE)

View File

@ -0,0 +1,22 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv)
import xbmc
from ls_log import log as log
from ls_player import Player as Player
class Monitor(xbmc.Monitor):
def __init__(self):
log('monitor started')
self.player = Player()
def onSettingsChanged(self):
self.player.onSettingsChanged()
def waitForAbort(self):
super(Monitor, self).waitForAbort()
self.player.onAbortRequested()
log('monitor stopped')

View File

@ -0,0 +1,134 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv)
import os
import stat
import threading
import xbmc
import xbmcaddon
import xbmcgui
from ls_librespot import Librespot as Librespot
from ls_log import log as log
from ls_spotify import Spotify as Spotify
_CODEC = 'pcm_s16le'
_FIFO = '/var/run/librespot'
_STREAM = 'rtp://127.0.0.1:6666'
_DEFAULT_ICON = xbmcaddon.Addon().getAddonInfo('icon')
_DEFAULT_TITLE = xbmcaddon.Addon().getAddonInfo('name')
class Player(threading.Thread, xbmc.Player):
def __init__(self):
super(Player, self).__init__()
self.updateSettings()
self.dialog = xbmcgui.Dialog()
self.librespot = Librespot()
self.listitem = xbmcgui.ListItem()
self.listitem.addStreamInfo('audio', {'codec': _CODEC})
self.listitem.setPath(_STREAM)
self.spotify = Spotify()
self.start()
if self.isPlaying():
self.onPlayBackStarted()
def onAbortRequested(self):
log('abort requested')
with open(_FIFO, 'w') as fifo:
fifo.close()
self.join()
def onPlayBackEnded(self):
log('a playback ended')
self.librespot.restart()
def onPlayBackStarted(self):
log('a playback started')
if self.getPlayingFile() != _STREAM:
self.librespot.stop()
def onPlayBackStopped(self):
log('a playback stopped')
self.librespot.restart()
def onSettingsChanged(self):
log('settings changed')
self.stop()
self.updateSettings()
def pause(self):
if self.isPlaying() and self.getPlayingFile() == _STREAM:
log('pausing librespot playback')
super(Player, self).pause()
def play(self, track_id):
track = self.spotify.getTrack(track_id)
if track['thumb'] == '':
track['thumb'] = _DEFAULT_ICON
if track['title'] == '':
track['title'] = _DEFAULT_TITLE
if self.kodi:
self.listitem.setArt({'thumb': track['thumb']})
self.listitem.setInfo(
'music',
{
'album': track['album'],
'artist': track['artist'],
'title': track['title']
}
)
if self.isPlaying() and self.getPlayingFile() == _STREAM:
log('updating librespot playback')
self.updateInfoTag(self.listitem)
else:
self.librespot.unsuspend()
log('starting librespot playback')
super(Player, self).play(_STREAM, self.listitem)
else:
self.dialog.notification(
track['title'],
track['artist'],
icon=track['thumb'],
sound=False)
def run(self):
log('named pipe started')
try:
os.unlink(_FIFO)
except OSError:
pass
os.mkfifo(_FIFO)
while (os.path.exists(_FIFO) and
stat.S_ISFIFO(os.stat(_FIFO).st_mode)):
with open(_FIFO, 'r') as fifo:
command = fifo.read().splitlines()
log('named pipe received {}'.format(str(command)))
if len(command) == 0:
break
elif command[0] == 'play':
self.play(command[1])
elif command[0] == 'stop':
self.stop()
elif command[0] == 'pause':
self.pause()
try:
os.unlink(_FIFO)
except OSError:
pass
log('named pipe stopped')
def stop(self):
if self.isPlaying():
if self.getPlayingFile() == _STREAM:
log('stopping librespot playback')
super(Player, self).stop()
else:
self.librespot.stop()
else:
self.librespot.restart()
def updateSettings(self):
self.kodi = (xbmcaddon.Addon().getSetting('ls_m') == 'Kodi')

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2018-present Team LibreELEC (https://libreelec.tv)
import base64
import json
import time
import urllib
import urllib2
from ls_log import log as log
_CLIENT_ID = '169df5532dee47a59913f8528e83ae71'
_CLIENT_SECRET = '1f3d8b507bbe4f68beb3a4472e8ad411'
def _get(default, tree, *indices):
try:
for index in indices:
tree = tree[index]
except LookupError:
tree = default
return tree
class Spotify():
def __init__(self):
self.headers = None
self.expiration = time.time()
self.request = [
'https://accounts.spotify.com/api/token',
urllib.urlencode({'grant_type': 'client_credentials'}),
{'Authorization': 'Basic {}'.format(base64.b64encode(
'{}:{}'.format(_CLIENT_ID, _CLIENT_SECRET)))}
]
def getTrack(self, track_id):
try:
if time.time() > self.expiration:
log('token expired')
token = json.loads(urllib2.urlopen(
urllib2.Request(*self.request)).read())
log('token {} expires in {} seconds'.format(
token['access_token'], token['expires_in']))
self.expiration = time.time() + float(token['expires_in']) - 60
self.headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer {}'.format(token['access_token'])
}
track = json.loads(urllib2.urlopen(urllib2.Request(
'https://api.spotify.com/v1/tracks/{}'.format(track_id), None,
self.headers)).read())
except Exception as e:
log('failed to get track {} from Spotify: {}'.format(e))
track = dict()
return {
'album': _get('', track, 'album', 'name'),
'artist': _get('', track, 'artists', 0, 'name'),
'duration': _get(0, track, 'duration_ms') / 1000,
'thumb': _get('', track, 'album', 'images', 0, 'url'),
'title': _get('', track, 'name')
}

View File

@ -1,17 +1,11 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings> <settings>
<category label="30100" > <setting id="ls_m" label="30107" type="labelenum" lvalues="30108|30109" />
<setting label="30102" type="labelenum" id="ls_b" lvalues="30103|30104|30105|30106" /> <setting label="30114" type="action" subsetting="true" visible="eq(-1,0)" action="RunAddon(service.librespot)" />
<setting label="30107" type="labelenum" id="ls_O" lvalues="30108|30109" /> <setting id="ls_o" label="30115" type="text" subsetting="true" visible="eq(-2,0)" default="" />
<setting label="30110" type="text" id="ls_u" /> <setting id="pcm_3" label="30116" type="enum" subsetting="true" visible="eq(-3,0)" lvalues="30117|30118|30119" enable="eq(-1,default:CARD=ALSA)|eq(-1,sysdefault:CARD=ALSA)" />
<setting label="30111" type="text" id="ls_p" option="hidden" visible="!eq(-1,)" /> <setting id="ls_b" label="30102" type="labelenum" lvalues="30103|30104|30105|30106" />
<setting label="30112" type="bool" id="ls_d" default="true" enable="false" visible="eq(-1,)|eq(-2,)" /> <setting id="ls_a" label="30112" type="bool" default="false" />
<setting label="30112" type="bool" id="ls_D" default="false" enable="false" visible="!eq(-2,)+!eq(-3,)" /> <setting id="ls_u" label="30110" type="text" subsetting="true" visible="eq(-1,true)" default="" />
</category> <setting id="ls_p" label="30111" type="text" subsetting="true" visible="eq(-2,true)" default="" />
<category label="30113" >
<setting label="30107" type="labelenum" id="ls_O" lvalues="30108|30109" visible="false" />
<setting label="30114" type="action" action="RunAddon(service.librespot)" enable="eq(-1,0)" />
<setting label="30115" type="text" id="ls_o" default="" enable="eq(-2,0)" />
<setting label="30116" type="enum" id="pcm_3" lvalues="30117|30118|30119" enable="eq(-3,0)" visible="eq(-1,default:CARD=ALSA)|eq(-1,sysdefault:CARD=ALSA)" />
</category>
</settings> </settings>

View File

@ -1,10 +1,9 @@
<settings> <settings version="2">
<setting id="ls_D" value="false" /> <setting id="ls_a" default="true">false</setting>
<setting id="ls_O" value="ALSA" /> <setting id="ls_b">320</setting>
<setting id="ls_b" value="320" /> <setting id="ls_m">ALSA</setting>
<setting id="ls_d" value="true" /> <setting id="ls_o" default="true"></setting>
<setting id="ls_o" value="" /> <setting id="ls_p" default="true"></setting>
<setting id="ls_p" value="" /> <setting id="ls_u" default="true"></setting>
<setting id="ls_u" value="" /> <setting id="pcm_3" default="true">0</setting>
<setting id="pcm_3" value="0" />
</settings> </settings>