mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-28 13:16:41 +00:00
Merge pull request #1808 from awiouy/librespot-104-8.2
librespot: switch from ffmpegx to pulseaudio to stream to Kodi
This commit is contained in:
commit
7f971be88b
@ -1,3 +1,8 @@
|
|||||||
|
104
|
||||||
|
- Update to 910974e
|
||||||
|
- Switch from ffmpegx to pulseaudio to stream to Kodi
|
||||||
|
- Wait for sound.target
|
||||||
|
|
||||||
103
|
103
|
||||||
- Update system_information_string
|
- Update system_information_string
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@
|
|||||||
|
|
||||||
PKG_NAME="librespot"
|
PKG_NAME="librespot"
|
||||||
PKG_VERSION="aa86ebf"
|
PKG_VERSION="aa86ebf"
|
||||||
PKG_REV="103"
|
PKG_REV="104"
|
||||||
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/"
|
||||||
PKG_URL="https://github.com/plietar/$PKG_NAME/archive/$PKG_VERSION.zip"
|
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_SECTION="service"
|
||||||
PKG_SHORTDESC="Librespot: play Spotify through LibreELEC using a Spotify app as a remote"
|
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."
|
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() {
|
make_target() {
|
||||||
cd src
|
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 "$PKG_BUILD/.$TARGET_NAME"/*/release
|
cd "$PKG_BUILD/.$TARGET_NAME"/*/release
|
||||||
$STRIP librespot
|
$STRIP librespot
|
||||||
}
|
}
|
||||||
@ -59,7 +59,6 @@ addon() {
|
|||||||
|
|
||||||
mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID/bin"
|
mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID/bin"
|
||||||
cp "$PKG_BUILD/.$TARGET_NAME"/*/release/librespot \
|
cp "$PKG_BUILD/.$TARGET_NAME"/*/release/librespot \
|
||||||
"$(get_build_dir ffmpegx)/.install_pkg/usr/local/bin/ffmpegx" \
|
|
||||||
"$ADDON_BUILD/$PKG_ADDON_ID/bin"
|
"$ADDON_BUILD/$PKG_ADDON_ID/bin"
|
||||||
|
|
||||||
mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID/lib"
|
mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID/lib"
|
||||||
|
@ -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
|
@ -1,22 +0,0 @@
|
|||||||
From 697926d2058e8650a9dd559d90a8a130950c417a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Sasha Hilton <sashahilton00@users.noreply.github.com>
|
|
||||||
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(),
|
|
@ -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 { .. } => {
|
@ -84,6 +84,9 @@ init_alsa() {
|
|||||||
. /etc/profile
|
. /etc/profile
|
||||||
oe_setup_addon service.librespot
|
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 \
|
--disable-audio-cache \
|
||||||
--name \"Librespot@$HOSTNAME\""
|
--name \"Librespot@$HOSTNAME\""
|
||||||
@ -99,14 +102,18 @@ if [ -n "$ls_p" -a -n "$ls_u" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$ls_O" == "Kodi" ]; then
|
if [ "$ls_O" == "Kodi" ]; then
|
||||||
LIBRESPOT="$LIBRESPOT --backend pipe \
|
if [ -z "$(pactl list short modules | grep sink_name=$LS_SINK)" ]; then
|
||||||
--onstart=\"kodi-send \
|
pactl load-module module-null-sink sink_name="$LS_SINK" > /dev/null
|
||||||
--action=RunAddon(service.librespot)\" \
|
fi
|
||||||
| ffmpegx -hide_banner -loglevel warning \
|
pactl suspend-sink "$LS_SINK" 1
|
||||||
-avioflags direct -fflags nobuffer \
|
if [ -z "$(pactl list short modules | grep source=$LS_SINK.monitor)" ]; then
|
||||||
-re -ac 2 -ar 44100 -channel_layout stereo -f s16le -i pipe: \
|
pactl load-module module-rtp-send source="$LS_SINK.monitor" \
|
||||||
-sdp_file \"$ADDON_HOME/librespot.sdp\" \
|
destination_ip=127.0.0.1 port="$LS_PORT" source_ip=127.0.0.1 > /dev/null
|
||||||
-f rtp rtp://127.0.0.1:5555"
|
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
|
||||||
|
@ -24,21 +24,20 @@ import xbmc
|
|||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
|
||||||
|
PORT = '6666'
|
||||||
|
SINK = 'librespot_sink'
|
||||||
|
|
||||||
ICON = xbmcaddon.Addon().getAddonInfo('icon')
|
|
||||||
ID = xbmcaddon.Addon().getAddonInfo('id')
|
|
||||||
NAME = xbmcaddon.Addon().getAddonInfo('name')
|
NAME = xbmcaddon.Addon().getAddonInfo('name')
|
||||||
PROFILE = xbmcaddon.Addon().getAddonInfo('profile')
|
|
||||||
STRINGS = xbmcaddon.Addon().getLocalizedString
|
STRINGS = xbmcaddon.Addon().getLocalizedString
|
||||||
|
|
||||||
ITEM = os.path.join(PROFILE, 'librespot.sdp')
|
|
||||||
LISTITEM = xbmcgui.ListItem(NAME)
|
|
||||||
LISTITEM.setArt({'thumb': ICON})
|
|
||||||
|
|
||||||
|
|
||||||
def addon():
|
def addon():
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
|
pass
|
||||||
|
elif sys.argv[1] == 'start':
|
||||||
Player().play()
|
Player().play()
|
||||||
|
elif sys.argv[1] == 'stop':
|
||||||
|
Player().stop()
|
||||||
elif sys.argv[1] == 'wizard':
|
elif sys.argv[1] == 'wizard':
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
while True:
|
while True:
|
||||||
@ -54,10 +53,6 @@ def addon():
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def systemctl(command):
|
|
||||||
subprocess.call(['systemctl', command, ID])
|
|
||||||
|
|
||||||
|
|
||||||
class Monitor(xbmc.Monitor):
|
class Monitor(xbmc.Monitor):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -70,37 +65,69 @@ class Monitor(xbmc.Monitor):
|
|||||||
|
|
||||||
class Player(xbmc.Player):
|
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, *args, **kwargs):
|
||||||
super(Player, self).__init__(self)
|
super(Player, self).__init__(self)
|
||||||
|
self.service = Service()
|
||||||
|
self.sink = Sink()
|
||||||
if self.isPlaying():
|
if self.isPlaying():
|
||||||
self.onPlayBackStarted()
|
self.onPlayBackStarted()
|
||||||
|
|
||||||
def onPlayBackEnded(self):
|
def onPlayBackEnded(self):
|
||||||
if not self.islibrespot:
|
xbmc.sleep(1000)
|
||||||
xbmc.sleep(5000)
|
|
||||||
if not self.isPlaying():
|
if not self.isPlaying():
|
||||||
systemctl('start')
|
self.service.restart()
|
||||||
|
|
||||||
def onPlayBackStarted(self):
|
def onPlayBackStarted(self):
|
||||||
if self.getPlayingFile() == ITEM:
|
if self.getPlayingFile() != self.ITEM:
|
||||||
self.islibrespot = True
|
self.sink.suspend()
|
||||||
else:
|
self.service.stop()
|
||||||
self.islibrespot = False
|
|
||||||
systemctl('stop')
|
|
||||||
|
|
||||||
def onPlayBackStopped(self):
|
def onPlayBackStopped(self):
|
||||||
systemctl('restart')
|
self.service.restart()
|
||||||
|
|
||||||
def play(self):
|
def play(self):
|
||||||
if not self.isPlaying():
|
if not self.isPlaying():
|
||||||
super(Player, self).play(ITEM, LISTITEM)
|
self.sink.unsuspend()
|
||||||
|
super(Player, self).play(self.ITEM, self.LISTITEM)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
if self.isPlaying():
|
self.sink.suspend()
|
||||||
if self.getPlayingFile() == ITEM:
|
if self.isPlaying() and self.getPlayingFile() == self.ITEM:
|
||||||
super(Player, self).stop()
|
super(Player, self).stop()
|
||||||
else:
|
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__':
|
if __name__ == '__main__':
|
||||||
Monitor().waitForAbort()
|
Monitor().waitForAbort()
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=librespot
|
Description=librespot
|
||||||
After=network-online.target
|
After=network-online.target sound.target
|
||||||
Requires=network-online.target
|
Requires=network-online.target sound.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
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
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user