From fc1e611a73c6ce5199c38511bf6cc788eccc611f Mon Sep 17 00:00:00 2001 From: awiouy Date: Sat, 15 Jul 2017 07:12:52 +0200 Subject: [PATCH] librespot: switch from ffmpegx to pulseaudio to stream to Kodi --- .../addons/service/librespot/changelog.txt | 5 ++ packages/addons/service/librespot/package.mk | 7 +- .../librespot-02_pulseaudio_device.patch | 19 +++++ ...-02_update_system_information_string.patch | 22 ------ .../librespot-03_libreelec_hooks.patch | 79 +++++++++++++++++++ .../librespot/source/bin/librespot.start | 23 ++++-- .../service/librespot/source/default.py | 79 +++++++++++++------ .../source/system.d/service.librespot.service | 5 +- 8 files changed, 177 insertions(+), 62 deletions(-) create mode 100644 packages/addons/service/librespot/patches/librespot-02_pulseaudio_device.patch delete mode 100644 packages/addons/service/librespot/patches/librespot-02_update_system_information_string.patch create mode 100644 packages/addons/service/librespot/patches/librespot-03_libreelec_hooks.patch diff --git a/packages/addons/service/librespot/changelog.txt b/packages/addons/service/librespot/changelog.txt index 07fb7df09c..7bab165905 100644 --- a/packages/addons/service/librespot/changelog.txt +++ b/packages/addons/service/librespot/changelog.txt @@ -1,3 +1,8 @@ +104 +- Update to 910974e +- Switch from ffmpegx to pulseaudio to stream to Kodi +- Wait for sound.target + 103 - Update system_information_string diff --git a/packages/addons/service/librespot/package.mk b/packages/addons/service/librespot/package.mk index d1498eefc7..39a3a2929a 100644 --- a/packages/addons/service/librespot/package.mk +++ b/packages/addons/service/librespot/package.mk @@ -19,12 +19,12 @@ PKG_NAME="librespot" PKG_VERSION="aa86ebf" -PKG_REV="103" +PKG_REV="104" PKG_ARCH="any" PKG_LICENSE="MIT" PKG_SITE="https://github.com/plietar/$PKG_NAME/" PKG_URL="https://github.com/plietar/$PKG_NAME/archive/$PKG_VERSION.zip" -PKG_DEPENDS_TARGET="toolchain avahi ffmpegx libvorbis pyalsaaudio rust" +PKG_DEPENDS_TARGET="toolchain avahi libvorbis pulseaudio pyalsaaudio rust" PKG_SECTION="service" PKG_SHORTDESC="Librespot: play Spotify through LibreELEC using a Spotify app as a remote" PKG_LONGDESC="Librespot ($PKG_VERSION) plays Spotify through LibreELEC using the open source librespot library using a Spotify app as a remote." @@ -43,7 +43,7 @@ configure_target() { make_target() { cd src - $CARGO_BUILD --no-default-features --features "alsa-backend with-avahi" + $CARGO_BUILD --no-default-features --features "alsa-backend pulseaudio-backend with-avahi" cd "$ROOT/$PKG_BUILD/.$TARGET_NAME"/*/release $STRIP librespot } @@ -59,7 +59,6 @@ addon() { mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID/bin" cp "$PKG_BUILD/.$TARGET_NAME"/*/release/librespot \ - "$(get_build_dir ffmpegx)/.install_pkg/usr/local/bin/ffmpegx" \ "$ADDON_BUILD/$PKG_ADDON_ID/bin" mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID/lib" diff --git a/packages/addons/service/librespot/patches/librespot-02_pulseaudio_device.patch b/packages/addons/service/librespot/patches/librespot-02_pulseaudio_device.patch new file mode 100644 index 0000000000..d56b431e6b --- /dev/null +++ b/packages/addons/service/librespot/patches/librespot-02_pulseaudio_device.patch @@ -0,0 +1,19 @@ +diff --git a/src/audio_backend/pulseaudio.rs b/src/audio_backend/pulseaudio.rs +index 3b9a09b..ce78062 100644 +--- a/src/audio_backend/pulseaudio.rs ++++ b/src/audio_backend/pulseaudio.rs +@@ -23,12 +23,13 @@ impl Open for PulseAudioSink { + + let name = CString::new("librespot").unwrap(); + let description = CString::new("A spoty client library").unwrap(); ++ let sink = CString::new("librespot_sink").unwrap(); + + let s = unsafe { + pa_simple_new(null(), // Use the default server. + name.as_ptr(), // Our application's name. + PA_STREAM_PLAYBACK, +- null(), // Use the default device. ++ sink.as_ptr(), // Our sink. + description.as_ptr(), // Description of our stream. + &ss, // Our sample format. + null(), // Use default channel map diff --git a/packages/addons/service/librespot/patches/librespot-02_update_system_information_string.patch b/packages/addons/service/librespot/patches/librespot-02_update_system_information_string.patch deleted file mode 100644 index b670befc32..0000000000 --- a/packages/addons/service/librespot/patches/librespot-02_update_system_information_string.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 697926d2058e8650a9dd559d90a8a130950c417a Mon Sep 17 00:00:00 2001 -From: Sasha Hilton -Date: Tue, 27 Jun 2017 12:45:34 +0100 -Subject: [PATCH] Update system_information_string - ---- - src/connection/mod.rs | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/connection/mod.rs b/src/connection/mod.rs -index af134fc..cfb5000 100644 ---- a/src/connection/mod.rs -+++ b/src/connection/mod.rs -@@ -41,7 +41,7 @@ pub fn authenticate(transport: Transport, credentials: Credentials, device_id: S - system_info => { - cpu_family: CpuFamily::CPU_UNKNOWN, - os: Os::OS_UNKNOWN, -- system_information_string: "This is not the client you are looking for".to_owned(), -+ system_information_string: "librespot".to_string() + "_" + &(version::short_sha()) + "_" + &(version::short_now()), - device_id: device_id, - }, - version_string: version::version_string(), diff --git a/packages/addons/service/librespot/patches/librespot-03_libreelec_hooks.patch b/packages/addons/service/librespot/patches/librespot-03_libreelec_hooks.patch new file mode 100644 index 0000000000..cdb748e31f --- /dev/null +++ b/packages/addons/service/librespot/patches/librespot-03_libreelec_hooks.patch @@ -0,0 +1,79 @@ +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 @@ + Some(Err(e)) => panic!("Vorbis error {:?}", e), + None => { + self.sink.stop().unwrap(); +- self.run_onstop(); ++ info!("onstop 1"); + + let old_state = mem::replace(&mut self.state, PlayerState::Stopped); + old_state.signal_end_of_track(); +@@ -224,6 +224,8 @@ + debug!("command={:?}", cmd); + match cmd { + PlayerCommand::Load(track_id, play, position, end_of_track) => { ++ self.run_onstart(); ++ + if self.state.is_playing() { + self.sink.stop().unwrap(); + } +@@ -232,7 +234,7 @@ + Some(decoder) => { + if play { + if !self.state.is_playing() { +- self.run_onstart(); ++ info!("onstart 1"); + } + self.sink.start().unwrap(); + +@@ -242,7 +244,7 @@ + }; + } else { + if self.state.is_playing() { +- self.run_onstop(); ++ info!("onstop 2"); + } + + self.state = PlayerState::Paused { +@@ -255,7 +257,7 @@ + None => { + end_of_track.complete(()); + if self.state.is_playing() { +- self.run_onstop(); ++ info!("onstart 3"); + } + } + } +@@ -276,7 +278,7 @@ + if let PlayerState::Paused { .. } = self.state { + self.state.paused_to_playing(); + +- self.run_onstart(); ++ info!("onstart 2"); + self.sink.start().unwrap(); + } else { + warn!("Player::play called from invalid state"); +@@ -288,17 +290,19 @@ + self.state.playing_to_paused(); + + self.sink.stop().unwrap(); +- self.run_onstop(); ++ info!("onstop 4"); + } else { + warn!("Player::pause called from invalid state"); + } + } + + PlayerCommand::Stop => { ++ self.run_onstop(); ++ + match self.state { + PlayerState::Playing { .. } => { + self.sink.stop().unwrap(); +- self.run_onstop(); ++ info!("onstop 5"); + self.state = PlayerState::Stopped; + } + PlayerState::Paused { .. } => { diff --git a/packages/addons/service/librespot/source/bin/librespot.start b/packages/addons/service/librespot/source/bin/librespot.start index aefaa45f47..a56a957441 100755 --- a/packages/addons/service/librespot/source/bin/librespot.start +++ b/packages/addons/service/librespot/source/bin/librespot.start @@ -84,6 +84,9 @@ init_alsa() { . /etc/profile oe_setup_addon service.librespot +LS_PORT="6666" +LS_SINK="librespot_sink" + LIBRESPOT="librespot --cache \"$ADDON_HOME/cache\" --disable-audio-cache \ --name \"Librespot@$HOSTNAME\"" @@ -99,14 +102,18 @@ if [ -n "$ls_p" -a -n "$ls_u" ]; then fi if [ "$ls_O" == "Kodi" ]; then - LIBRESPOT="$LIBRESPOT --backend pipe \ - --onstart=\"kodi-send \ - --action=RunAddon(service.librespot)\" \ - | ffmpegx -hide_banner -loglevel warning \ - -avioflags direct -fflags nobuffer \ - -re -ac 2 -ar 44100 -channel_layout stereo -f s16le -i pipe: \ - -sdp_file \"$ADDON_HOME/librespot.sdp\" \ - -f rtp rtp://127.0.0.1:5555" + 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)\"" else init_alsa if [ -n "$ls_o" ]; then diff --git a/packages/addons/service/librespot/source/default.py b/packages/addons/service/librespot/source/default.py index 149b4a2169..3f1fe00e08 100644 --- a/packages/addons/service/librespot/source/default.py +++ b/packages/addons/service/librespot/source/default.py @@ -24,21 +24,20 @@ import xbmc import xbmcaddon import xbmcgui +PORT = '6666' +SINK = 'librespot_sink' -ICON = xbmcaddon.Addon().getAddonInfo('icon') -ID = xbmcaddon.Addon().getAddonInfo('id') NAME = xbmcaddon.Addon().getAddonInfo('name') -PROFILE = xbmcaddon.Addon().getAddonInfo('profile') STRINGS = xbmcaddon.Addon().getLocalizedString -ITEM = os.path.join(PROFILE, 'librespot.sdp') -LISTITEM = xbmcgui.ListItem(NAME) -LISTITEM.setArt({'thumb': ICON}) - 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: @@ -54,10 +53,6 @@ def addon(): break -def systemctl(command): - subprocess.call(['systemctl', command, ID]) - - class Monitor(xbmc.Monitor): def __init__(self, *args, **kwargs): @@ -70,37 +65,69 @@ class Monitor(xbmc.Monitor): 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): super(Player, self).__init__(self) + self.service = Service() + self.sink = Sink() if self.isPlaying(): self.onPlayBackStarted() def onPlayBackEnded(self): - if not self.islibrespot: - xbmc.sleep(5000) - if not self.isPlaying(): - systemctl('start') + xbmc.sleep(1000) + if not self.isPlaying(): + self.service.restart() def onPlayBackStarted(self): - if self.getPlayingFile() == ITEM: - self.islibrespot = True - else: - self.islibrespot = False - systemctl('stop') + if self.getPlayingFile() != self.ITEM: + self.sink.suspend() + self.service.stop() def onPlayBackStopped(self): - systemctl('restart') + self.service.restart() def play(self): if not self.isPlaying(): - super(Player, self).play(ITEM, LISTITEM) + self.sink.unsuspend() + super(Player, self).play(self.ITEM, self.LISTITEM) def stop(self): - if self.isPlaying(): - if self.getPlayingFile() == ITEM: - super(Player, self).stop() + self.sink.suspend() + if self.isPlaying() and self.getPlayingFile() == self.ITEM: + super(Player, self).stop() else: - systemctl('restart') + self.service.restart() + + +class Service(): + + def __init__(self): + self.id = xbmcaddon.Addon().getAddonInfo('id') + + 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']) + if __name__ == '__main__': Monitor().waitForAbort() 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 3d34f79bc5..ed5a04a55e 100644 --- a/packages/addons/service/librespot/source/system.d/service.librespot.service +++ b/packages/addons/service/librespot/source/system.d/service.librespot.service @@ -1,10 +1,11 @@ [Unit] Description=librespot -After=network-online.target -Requires=network-online.target +After=network-online.target sound.target +Requires=network-online.target sound.target [Service] ExecStart=/bin/sh /storage/.kodi/addons/service.librespot/bin/librespot.start +ExecStopPost=/usr/bin/pactl suspend-sink librespot_sink 1 Restart=on-failure [Install]