librespot: ux improvements

This commit is contained in:
awiouy 2017-07-22 14:50:45 +02:00
parent 24c448405a
commit c9dd60302d
11 changed files with 146 additions and 100 deletions

View File

@ -1,3 +1,7 @@
106
- Rework code
- Display artist and title on track load
105
- Update to 910974e

View File

@ -19,7 +19,7 @@
PKG_NAME="librespot"
PKG_VERSION="910974e"
PKG_REV="105"
PKG_REV="106"
PKG_ARCH="any"
PKG_LICENSE="MIT"
PKG_SITE="https://github.com/plietar/$PKG_NAME/"

View File

@ -1,7 +1,24 @@
diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
--- librespot/src/player.rs 2017-07-09 20:01:31.000000000 +0200
+++ librespot-hooks/src/player.rs 2017-07-17 09:13:26.300852918 +0200
@@ -212,7 +212,7 @@
+++ librespot-hooks/src/player.rs 2017-07-22 13:46:06.741727001 +0200
@@ -2,6 +2,7 @@
use futures::{future, Future};
use std::borrow::Cow;
use std::io::{Read, Seek};
+use std::env;
use std::mem;
use std::sync::mpsc::{RecvError, TryRecvError};
use std::thread;
@@ -11,7 +12,7 @@
use audio_backend::Sink;
use audio_decrypt::AudioDecrypt;
use audio_file::AudioFile;
-use metadata::{FileFormat, Track};
+use metadata::{Artist, FileFormat, Track};
use session::{Bitrate, Session};
use mixer::AudioFilter;
use util::{self, SpotifyId, Subfile};
@@ -212,7 +213,7 @@
Some(Err(e)) => panic!("Vorbis error {:?}", e),
None => {
self.sink.stop().unwrap();
@ -10,16 +27,20 @@ diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
let old_state = mem::replace(&mut self.state, PlayerState::Stopped);
old_state.signal_end_of_track();
@@ -224,6 +224,8 @@
@@ -224,6 +225,12 @@
debug!("command={:?}", cmd);
match cmd {
PlayerCommand::Load(track_id, play, position, end_of_track) => {
+ let track = self.session.metadata().get::<Track>(track_id).wait().unwrap();
+ let artist = self.session.metadata().get::<Artist>(track.artists[0]).wait().unwrap();
+ env::set_var("LS_ARTIST", artist.name);
+ env::set_var("LS_TITLE", track.name);
+ self.run_onstart();
+
if self.state.is_playing() {
self.sink.stop().unwrap();
}
@@ -232,7 +234,7 @@
@@ -232,7 +239,7 @@
Some(decoder) => {
if play {
if !self.state.is_playing() {
@ -28,7 +49,7 @@ diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
}
self.sink.start().unwrap();
@@ -242,7 +244,7 @@
@@ -242,7 +249,7 @@
};
} else {
if self.state.is_playing() {
@ -37,7 +58,7 @@ diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
}
self.state = PlayerState::Paused {
@@ -255,7 +257,7 @@
@@ -255,7 +262,7 @@
None => {
end_of_track.complete(());
if self.state.is_playing() {
@ -46,7 +67,7 @@ diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
}
}
}
@@ -276,7 +278,7 @@
@@ -276,7 +283,7 @@
if let PlayerState::Paused { .. } = self.state {
self.state.paused_to_playing();
@ -55,7 +76,7 @@ diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
self.sink.start().unwrap();
} else {
warn!("Player::play called from invalid state");
@@ -288,17 +290,19 @@
@@ -288,17 +295,19 @@
self.state.playing_to_paused();
self.sink.stop().unwrap();

View File

@ -16,8 +16,24 @@
# along with LibreELEC. If not, see <http://www.gnu.org/licenses/>.
################################################################################
from default import addon as addon
import alsaaudio as alsa
import xbmcaddon
import xbmcgui
dialog = xbmcgui.Dialog()
strings = xbmcaddon.Addon().getLocalizedString
while True:
pcms = alsa.pcms()[1:]
if len(pcms) == 0:
dialog.ok(xbmcaddon.Addon().getAddonInfo('name'), strings(30210))
break
pcmx = dialog.select(strings(30112), pcms)
if pcmx == -1:
break
pcm = pcms[pcmx]
xbmcaddon.Addon().setSetting('ls_o', pcm)
break
del dialog
if __name__ == '__main__':
addon()

View File

@ -0,0 +1,2 @@
LS_PORT="6666"
LS_SINK="librespot_sink"

View File

@ -0,0 +1,2 @@
#!/bin/sh
echo -e "play\n$LS_ARTIST\n$LS_TITLE" > "$LS_FIFO"

View File

@ -0,0 +1,2 @@
#!/bin/sh
echo -e "stop" > "$LS_FIFO"

View File

@ -84,12 +84,11 @@ init_alsa() {
. /etc/profile
oe_setup_addon service.librespot
LS_PORT="6666"
LS_SINK="librespot_sink"
LIBRESPOT="librespot --cache \"$ADDON_HOME/cache\"
LIBRESPOT="librespot --cache \"$ADDON_HOME/cache\" \
--disable-audio-cache \
--name \"Librespot@$HOSTNAME\""
--name \"Librespot@$HOSTNAME\" \
--onstart librespot.onstart \
--onstop librespot.onstop"
if [ -n "$ls_b" -a "$ls_b" != "-" ]; then
LIBRESPOT="$LIBRESPOT --bitrate $ls_b"
@ -102,18 +101,7 @@ if [ -n "$ls_p" -a -n "$ls_u" ]; then
fi
if [ "$ls_O" == "Kodi" ]; then
if [ -z "$(pactl list short modules | grep sink_name=$LS_SINK)" ]; then
pactl load-module module-null-sink sink_name="$LS_SINK" > /dev/null
fi
pactl suspend-sink "$LS_SINK" 1
if [ -z "$(pactl list short modules | grep source=$LS_SINK.monitor)" ]; then
pactl load-module module-rtp-send source="$LS_SINK.monitor" \
destination_ip=127.0.0.1 port="$LS_PORT" source_ip=127.0.0.1 > /dev/null
fi
LIBRESPOT="$LIBRESPOT \
--backend pulseaudio \
--onstart=\"kodi-send --action=RunAddon(service.librespot,start)\" \
--onstop=\"kodi-send --action=RunAddon(service.librespot,stop)\""
LIBRESPOT="$LIBRESPOT --backend pulseaudio"
else
init_alsa
if [ -n "$ls_o" ]; then
@ -121,4 +109,15 @@ else
fi
fi
if [ -z "$(pactl list short modules | grep sink_name=$LS_SINK)" ]; then
pactl load-module module-null-sink sink_name="$LS_SINK" > /dev/null
fi
pactl suspend-sink "$LS_SINK" 1
if [ -z "$(pactl list short modules | grep source=$LS_SINK.monitor)" ]; then
pactl load-module module-rtp-send source="$LS_SINK.monitor" \
destination_ip=127.0.0.1 port="$LS_PORT" source_ip=127.0.0.1 > /dev/null
fi
export LS_FIFO="$ADDON_DIR/rc"
eval $LIBRESPOT

View File

@ -16,10 +16,11 @@
# along with LibreELEC. If not, see <http://www.gnu.org/licenses/>.
################################################################################
import alsaaudio as alsa
import os
import stat
import subprocess
import sys
import threading
import xbmc
import xbmcaddon
import xbmcgui
@ -27,107 +28,105 @@ import xbmcgui
PORT = '6666'
SINK = 'librespot_sink'
NAME = xbmcaddon.Addon().getAddonInfo('name')
STRINGS = xbmcaddon.Addon().getLocalizedString
def suspendSink(bit):
subprocess.call(['pactl', 'suspend-sink', SINK, bit])
def systemctl(command):
subprocess.call(['systemctl', command, xbmcaddon.Addon().getAddonInfo('id')])
def addon():
if len(sys.argv) == 1:
pass
elif sys.argv[1] == 'start':
Player().play()
elif sys.argv[1] == 'stop':
Player().stop()
elif sys.argv[1] == 'wizard':
dialog = xbmcgui.Dialog()
while True:
pcms = alsa.pcms()[1:]
if len(pcms) == 0:
dialog.ok(NAME, STRINGS(30210))
break
pcmx = dialog.select(STRINGS(30112), pcms)
if pcmx == -1:
break
pcm = pcms[pcmx]
xbmcaddon.Addon().setSetting('ls_o', pcm)
break
class Controller(threading.Thread):
FIFO = os.path.join(xbmcaddon.Addon().getAddonInfo('path'), 'rc')
class Monitor(xbmc.Monitor):
def __init__(self, player):
super(Controller, self).__init__()
self.player = player
def __init__(self, *args, **kwargs):
super(Monitor, self).__init__(self)
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()
def onSettingsChanged(self):
self.player.stop()
def stop(self):
try:
os.unlink(self.FIFO)
except OSError:
pass
class Player(xbmc.Player):
ITEM = 'rtp://127.0.0.1:{port}'.format(port=PORT)
LISTITEM = xbmcgui.ListItem(NAME)
LISTITEM.setArt({'thumb': xbmcaddon.Addon().getAddonInfo('icon')})
def __init__(self, *args, **kwargs):
def __init__(self):
super(Player, self).__init__(self)
self.service = Service()
self.sink = Sink()
self.window = xbmcgui.Window(12006)
if self.isPlaying():
self.onPlayBackStarted()
def onPlayBackEnded(self):
suspendSink('1')
xbmc.sleep(1000)
if not self.isPlaying():
self.service.restart()
systemctl('restart')
def onPlayBackStarted(self):
if self.getPlayingFile() != self.ITEM:
self.sink.suspend()
self.service.stop()
suspendSink('1')
systemctl('stop')
def onPlayBackStopped(self):
self.service.restart()
systemctl('restart')
def play(self):
if not self.isPlaying():
self.sink.unsuspend()
super(Player, self).play(self.ITEM, self.LISTITEM)
if not self.isPlaying() and xbmcaddon.Addon().getSetting('ls_O') == 'Kodi':
suspendSink('0')
listitem = xbmcgui.ListItem(xbmcaddon.Addon().getAddonInfo('name'))
listitem.setArt({'thumb': xbmcaddon.Addon().getAddonInfo('icon')})
super(Player, self).play(self.ITEM, listitem)
del listitem
self.window.show()
def stop(self):
self.sink.suspend()
suspendSink('1')
if self.isPlaying() and self.getPlayingFile() == self.ITEM:
super(Player, self).stop()
else:
self.service.restart()
systemctl('restart')
class Service():
class Monitor(xbmc.Monitor):
def __init__(self):
self.id = xbmcaddon.Addon().getAddonInfo('id')
def __init__(self, player):
super(Monitor, self).__init__(self)
self.player = player
def restart(self):
self.systemctl('restart')
def start(self):
self.systemctl('start')
def stop(self):
self.systemctl('stop')
def systemctl(self, command):
subprocess.call(['systemctl', command, self.id])
class Sink():
def suspend(self):
subprocess.call(['pactl', 'suspend-sink', SINK, '1'])
def unsuspend(self):
subprocess.call(['pactl', 'suspend-sink', SINK, '0'])
def onSettingsChanged(self):
self.player.stop()
if __name__ == '__main__':
Monitor().waitForAbort()
player = Player()
controller = Controller(player)
controller.start()
Monitor(player).waitForAbort()
controller.stop()

View File

@ -10,7 +10,7 @@
</category>
<category label="30110" >
<setting label="30106" type="labelenum" id="ls_O" lvalues="ALSA|Kodi" visible="false" />
<setting label="30111" type="action" action="RunAddon(service.librespot,wizard)" enable="eq(-1,0)" />
<setting label="30111" type="action" action="RunAddon(service.librespot)" enable="eq(-1,0)" />
<setting label="30112" type="text" id="ls_o" default="" enable="eq(-2,0)" />
<setting label="30113" type="enum" id="pcm_3" lvalues="30114|30115|30116" enable="eq(-3,0)" visible="eq(-1,default:CARD=ALSA)|eq(-1,sysdefault:CARD=ALSA)" />
</category>

View File

@ -1,11 +1,12 @@
[Unit]
Description=librespot
After=network-online.target sound.target
Requires=network-online.target sound.target
After=kodi.service network-online.target sound.target
Requires=kodi.service network-online.target sound.target
[Service]
EnvironmentFile=/storage/.kodi/addons/service.librespot/bin/librespot.env
ExecStart=/bin/sh /storage/.kodi/addons/service.librespot/bin/librespot.start
ExecStopPost=/usr/bin/pactl suspend-sink librespot_sink 1
ExecStopPost=/usr/bin/pactl suspend-sink "$LS_SINK" 1
Restart=on-failure
[Install]