diff --git a/packages/addons/service/librespot/changelog.txt b/packages/addons/service/librespot/changelog.txt index 69ed960755..7204e9a460 100644 --- a/packages/addons/service/librespot/changelog.txt +++ b/packages/addons/service/librespot/changelog.txt @@ -1,3 +1,7 @@ +106 +- Rework code +- Display artist and title on track load + 105 - Update to 910974e diff --git a/packages/addons/service/librespot/package.mk b/packages/addons/service/librespot/package.mk index 6bc4c30336..d330836898 100644 --- a/packages/addons/service/librespot/package.mk +++ b/packages/addons/service/librespot/package.mk @@ -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/" diff --git a/packages/addons/service/librespot/patches/librespot-03_libreelec_hooks.patch b/packages/addons/service/librespot/patches/librespot-03_libreelec_hooks.patch index cdb748e31f..d6d07df4ef 100644 --- a/packages/addons/service/librespot/patches/librespot-03_libreelec_hooks.patch +++ b/packages/addons/service/librespot/patches/librespot-03_libreelec_hooks.patch @@ -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_id).wait().unwrap(); ++ let artist = self.session.metadata().get::(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(); diff --git a/packages/addons/service/librespot/source/addon.py b/packages/addons/service/librespot/source/addon.py index 86a51cca4e..d7a59a1187 100644 --- a/packages/addons/service/librespot/source/addon.py +++ b/packages/addons/service/librespot/source/addon.py @@ -16,8 +16,24 @@ # along with LibreELEC. If not, see . ################################################################################ -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() diff --git a/packages/addons/service/librespot/source/bin/librespot.env b/packages/addons/service/librespot/source/bin/librespot.env new file mode 100644 index 0000000000..3ca5ee662a --- /dev/null +++ b/packages/addons/service/librespot/source/bin/librespot.env @@ -0,0 +1,2 @@ +LS_PORT="6666" +LS_SINK="librespot_sink" diff --git a/packages/addons/service/librespot/source/bin/librespot.onstart b/packages/addons/service/librespot/source/bin/librespot.onstart new file mode 100644 index 0000000000..e1590efbcc --- /dev/null +++ b/packages/addons/service/librespot/source/bin/librespot.onstart @@ -0,0 +1,2 @@ +#!/bin/sh +echo -e "play\n$LS_ARTIST\n$LS_TITLE" > "$LS_FIFO" diff --git a/packages/addons/service/librespot/source/bin/librespot.onstop b/packages/addons/service/librespot/source/bin/librespot.onstop new file mode 100644 index 0000000000..594f6c4929 --- /dev/null +++ b/packages/addons/service/librespot/source/bin/librespot.onstop @@ -0,0 +1,2 @@ +#!/bin/sh +echo -e "stop" > "$LS_FIFO" diff --git a/packages/addons/service/librespot/source/bin/librespot.start b/packages/addons/service/librespot/source/bin/librespot.start index a56a957441..8fb8aaad44 100755 --- a/packages/addons/service/librespot/source/bin/librespot.start +++ b/packages/addons/service/librespot/source/bin/librespot.start @@ -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 diff --git a/packages/addons/service/librespot/source/default.py b/packages/addons/service/librespot/source/default.py index 3f1fe00e08..c5e98fe35d 100644 --- a/packages/addons/service/librespot/source/default.py +++ b/packages/addons/service/librespot/source/default.py @@ -16,10 +16,11 @@ # along with LibreELEC. If not, see . ################################################################################ -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() diff --git a/packages/addons/service/librespot/source/resources/settings.xml b/packages/addons/service/librespot/source/resources/settings.xml index bc502031c0..46264902af 100644 --- a/packages/addons/service/librespot/source/resources/settings.xml +++ b/packages/addons/service/librespot/source/resources/settings.xml @@ -10,7 +10,7 @@ - + diff --git a/packages/addons/service/librespot/source/system.d/service.librespot.service b/packages/addons/service/librespot/source/system.d/service.librespot.service index ed5a04a55e..2688e2006c 100644 --- a/packages/addons/service/librespot/source/system.d/service.librespot.service +++ b/packages/addons/service/librespot/source/system.d/service.librespot.service @@ -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]