mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-24 11:16:51 +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
|
||||
- Update to 910974e
|
||||
|
||||
|
@ -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/"
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
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
|
||||
|
@ -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()
|
||||
|
@ -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>
|
||||
|
@ -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]
|
||||
|
Loading…
x
Reference in New Issue
Block a user