mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-28 05:06:43 +00:00
librespot: ux improvements
This commit is contained in:
parent
24c448405a
commit
c9dd60302d
@ -1,3 +1,7 @@
|
|||||||
|
106
|
||||||
|
- Rework code
|
||||||
|
- Display artist and title on track load
|
||||||
|
|
||||||
105
|
105
|
||||||
- Update to 910974e
|
- Update to 910974e
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
PKG_NAME="librespot"
|
PKG_NAME="librespot"
|
||||||
PKG_VERSION="910974e"
|
PKG_VERSION="910974e"
|
||||||
PKG_REV="105"
|
PKG_REV="106"
|
||||||
PKG_ARCH="any"
|
PKG_ARCH="any"
|
||||||
PKG_LICENSE="MIT"
|
PKG_LICENSE="MIT"
|
||||||
PKG_SITE="https://github.com/plietar/$PKG_NAME/"
|
PKG_SITE="https://github.com/plietar/$PKG_NAME/"
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
|
diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
|
||||||
--- librespot/src/player.rs 2017-07-09 20:01:31.000000000 +0200
|
--- 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
|
+++ librespot-hooks/src/player.rs 2017-07-22 13:46:06.741727001 +0200
|
||||||
@@ -212,7 +212,7 @@
|
@@ -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),
|
Some(Err(e)) => panic!("Vorbis error {:?}", e),
|
||||||
None => {
|
None => {
|
||||||
self.sink.stop().unwrap();
|
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);
|
let old_state = mem::replace(&mut self.state, PlayerState::Stopped);
|
||||||
old_state.signal_end_of_track();
|
old_state.signal_end_of_track();
|
||||||
@@ -224,6 +224,8 @@
|
@@ -224,6 +225,12 @@
|
||||||
debug!("command={:?}", cmd);
|
debug!("command={:?}", cmd);
|
||||||
match cmd {
|
match cmd {
|
||||||
PlayerCommand::Load(track_id, play, position, end_of_track) => {
|
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();
|
+ self.run_onstart();
|
||||||
+
|
+
|
||||||
if self.state.is_playing() {
|
if self.state.is_playing() {
|
||||||
self.sink.stop().unwrap();
|
self.sink.stop().unwrap();
|
||||||
}
|
}
|
||||||
@@ -232,7 +234,7 @@
|
@@ -232,7 +239,7 @@
|
||||||
Some(decoder) => {
|
Some(decoder) => {
|
||||||
if play {
|
if play {
|
||||||
if !self.state.is_playing() {
|
if !self.state.is_playing() {
|
||||||
@ -28,7 +49,7 @@ diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
|
|||||||
}
|
}
|
||||||
self.sink.start().unwrap();
|
self.sink.start().unwrap();
|
||||||
|
|
||||||
@@ -242,7 +244,7 @@
|
@@ -242,7 +249,7 @@
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if self.state.is_playing() {
|
if self.state.is_playing() {
|
||||||
@ -37,7 +58,7 @@ diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.state = PlayerState::Paused {
|
self.state = PlayerState::Paused {
|
||||||
@@ -255,7 +257,7 @@
|
@@ -255,7 +262,7 @@
|
||||||
None => {
|
None => {
|
||||||
end_of_track.complete(());
|
end_of_track.complete(());
|
||||||
if self.state.is_playing() {
|
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 {
|
if let PlayerState::Paused { .. } = self.state {
|
||||||
self.state.paused_to_playing();
|
self.state.paused_to_playing();
|
||||||
|
|
||||||
@ -55,7 +76,7 @@ diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
|
|||||||
self.sink.start().unwrap();
|
self.sink.start().unwrap();
|
||||||
} else {
|
} else {
|
||||||
warn!("Player::play called from invalid state");
|
warn!("Player::play called from invalid state");
|
||||||
@@ -288,17 +290,19 @@
|
@@ -288,17 +295,19 @@
|
||||||
self.state.playing_to_paused();
|
self.state.playing_to_paused();
|
||||||
|
|
||||||
self.sink.stop().unwrap();
|
self.sink.stop().unwrap();
|
||||||
|
@ -16,8 +16,24 @@
|
|||||||
# along with LibreELEC. If not, see <http://www.gnu.org/licenses/>.
|
# 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()
|
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
LS_PORT="6666"
|
||||||
|
LS_SINK="librespot_sink"
|
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo -e "play\n$LS_ARTIST\n$LS_TITLE" > "$LS_FIFO"
|
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo -e "stop" > "$LS_FIFO"
|
@ -84,12 +84,11 @@ init_alsa() {
|
|||||||
. /etc/profile
|
. /etc/profile
|
||||||
oe_setup_addon service.librespot
|
oe_setup_addon service.librespot
|
||||||
|
|
||||||
LS_PORT="6666"
|
LIBRESPOT="librespot --cache \"$ADDON_HOME/cache\" \
|
||||||
LS_SINK="librespot_sink"
|
|
||||||
|
|
||||||
LIBRESPOT="librespot --cache \"$ADDON_HOME/cache\"
|
|
||||||
--disable-audio-cache \
|
--disable-audio-cache \
|
||||||
--name \"Librespot@$HOSTNAME\""
|
--name \"Librespot@$HOSTNAME\" \
|
||||||
|
--onstart librespot.onstart \
|
||||||
|
--onstop librespot.onstop"
|
||||||
|
|
||||||
if [ -n "$ls_b" -a "$ls_b" != "-" ]; then
|
if [ -n "$ls_b" -a "$ls_b" != "-" ]; then
|
||||||
LIBRESPOT="$LIBRESPOT --bitrate $ls_b"
|
LIBRESPOT="$LIBRESPOT --bitrate $ls_b"
|
||||||
@ -102,18 +101,7 @@ if [ -n "$ls_p" -a -n "$ls_u" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$ls_O" == "Kodi" ]; then
|
if [ "$ls_O" == "Kodi" ]; then
|
||||||
if [ -z "$(pactl list short modules | grep sink_name=$LS_SINK)" ]; then
|
LIBRESPOT="$LIBRESPOT --backend pulseaudio"
|
||||||
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)\""
|
|
||||||
else
|
else
|
||||||
init_alsa
|
init_alsa
|
||||||
if [ -n "$ls_o" ]; then
|
if [ -n "$ls_o" ]; then
|
||||||
@ -121,4 +109,15 @@ else
|
|||||||
fi
|
fi
|
||||||
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
|
eval $LIBRESPOT
|
||||||
|
@ -16,10 +16,11 @@
|
|||||||
# along with LibreELEC. If not, see <http://www.gnu.org/licenses/>.
|
# along with LibreELEC. If not, see <http://www.gnu.org/licenses/>.
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
import alsaaudio as alsa
|
|
||||||
import os
|
import os
|
||||||
|
import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
@ -27,107 +28,105 @@ import xbmcgui
|
|||||||
PORT = '6666'
|
PORT = '6666'
|
||||||
SINK = 'librespot_sink'
|
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():
|
class Controller(threading.Thread):
|
||||||
if len(sys.argv) == 1:
|
|
||||||
|
FIFO = os.path.join(xbmcaddon.Addon().getAddonInfo('path'), 'rc')
|
||||||
|
|
||||||
|
def __init__(self, player):
|
||||||
|
super(Controller, self).__init__()
|
||||||
|
self.player = player
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
os.unlink(self.FIFO)
|
||||||
|
except OSError:
|
||||||
pass
|
pass
|
||||||
elif sys.argv[1] == 'start':
|
os.mkfifo(self.FIFO)
|
||||||
Player().play()
|
while os.path.exists(self.FIFO) and stat.S_ISFIFO(os.stat(self.FIFO).st_mode):
|
||||||
elif sys.argv[1] == 'stop':
|
with open(self.FIFO, 'r') as fifo:
|
||||||
Player().stop()
|
command = fifo.read().splitlines()
|
||||||
elif sys.argv[1] == 'wizard':
|
if len(command) == 0:
|
||||||
|
break
|
||||||
|
elif command[0] == 'play' and len(command) == 3:
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
while True:
|
dialog.notification(command[1],
|
||||||
pcms = alsa.pcms()[1:]
|
command[2],
|
||||||
if len(pcms) == 0:
|
icon=xbmcaddon.Addon().getAddonInfo('icon'),
|
||||||
dialog.ok(NAME, STRINGS(30210))
|
sound=False)
|
||||||
break
|
del dialog
|
||||||
pcmx = dialog.select(STRINGS(30112), pcms)
|
self.player.play()
|
||||||
if pcmx == -1:
|
elif command[0] == 'stop':
|
||||||
break
|
|
||||||
pcm = pcms[pcmx]
|
|
||||||
xbmcaddon.Addon().setSetting('ls_o', pcm)
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
class Monitor(xbmc.Monitor):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(Monitor, self).__init__(self)
|
|
||||||
self.player = Player()
|
|
||||||
|
|
||||||
def onSettingsChanged(self):
|
|
||||||
self.player.stop()
|
self.player.stop()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
try:
|
||||||
|
os.unlink(self.FIFO)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Player(xbmc.Player):
|
class Player(xbmc.Player):
|
||||||
|
|
||||||
ITEM = 'rtp://127.0.0.1:{port}'.format(port=PORT)
|
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)
|
super(Player, self).__init__(self)
|
||||||
self.service = Service()
|
self.window = xbmcgui.Window(12006)
|
||||||
self.sink = Sink()
|
|
||||||
if self.isPlaying():
|
if self.isPlaying():
|
||||||
self.onPlayBackStarted()
|
self.onPlayBackStarted()
|
||||||
|
|
||||||
def onPlayBackEnded(self):
|
def onPlayBackEnded(self):
|
||||||
|
suspendSink('1')
|
||||||
xbmc.sleep(1000)
|
xbmc.sleep(1000)
|
||||||
if not self.isPlaying():
|
if not self.isPlaying():
|
||||||
self.service.restart()
|
systemctl('restart')
|
||||||
|
|
||||||
def onPlayBackStarted(self):
|
def onPlayBackStarted(self):
|
||||||
if self.getPlayingFile() != self.ITEM:
|
if self.getPlayingFile() != self.ITEM:
|
||||||
self.sink.suspend()
|
suspendSink('1')
|
||||||
self.service.stop()
|
systemctl('stop')
|
||||||
|
|
||||||
def onPlayBackStopped(self):
|
def onPlayBackStopped(self):
|
||||||
self.service.restart()
|
systemctl('restart')
|
||||||
|
|
||||||
def play(self):
|
def play(self):
|
||||||
if not self.isPlaying():
|
if not self.isPlaying() and xbmcaddon.Addon().getSetting('ls_O') == 'Kodi':
|
||||||
self.sink.unsuspend()
|
suspendSink('0')
|
||||||
super(Player, self).play(self.ITEM, self.LISTITEM)
|
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):
|
def stop(self):
|
||||||
self.sink.suspend()
|
suspendSink('1')
|
||||||
if self.isPlaying() and self.getPlayingFile() == self.ITEM:
|
if self.isPlaying() and self.getPlayingFile() == self.ITEM:
|
||||||
super(Player, self).stop()
|
super(Player, self).stop()
|
||||||
else:
|
else:
|
||||||
self.service.restart()
|
systemctl('restart')
|
||||||
|
|
||||||
|
|
||||||
class Service():
|
class Monitor(xbmc.Monitor):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, player):
|
||||||
self.id = xbmcaddon.Addon().getAddonInfo('id')
|
super(Monitor, self).__init__(self)
|
||||||
|
self.player = player
|
||||||
|
|
||||||
def restart(self):
|
def onSettingsChanged(self):
|
||||||
self.systemctl('restart')
|
self.player.stop()
|
||||||
|
|
||||||
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'])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
Monitor().waitForAbort()
|
player = Player()
|
||||||
|
controller = Controller(player)
|
||||||
|
controller.start()
|
||||||
|
Monitor(player).waitForAbort()
|
||||||
|
controller.stop()
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</category>
|
</category>
|
||||||
<category label="30110" >
|
<category label="30110" >
|
||||||
<setting label="30106" type="labelenum" id="ls_O" lvalues="ALSA|Kodi" visible="false" />
|
<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="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)" />
|
<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>
|
</category>
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=librespot
|
Description=librespot
|
||||||
After=network-online.target sound.target
|
After=kodi.service network-online.target sound.target
|
||||||
Requires=network-online.target sound.target
|
Requires=kodi.service network-online.target sound.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
EnvironmentFile=/storage/.kodi/addons/service.librespot/bin/librespot.env
|
||||||
ExecStart=/bin/sh /storage/.kodi/addons/service.librespot/bin/librespot.start
|
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
|
Restart=on-failure
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user