Merge pull request #1745 from awiouy/librespot-streaming-8.2

librespot: enable streaming to Kodi
This commit is contained in:
Christian Hewitt 2017-07-06 10:18:06 +04:00 committed by GitHub
commit 14e2e54611
9 changed files with 262 additions and 228 deletions

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="@PKG_ADDON_ID@"
name="@ADDON_NAME@"
version="@ADDON_VERSION@"
provider-name="@PROVIDER_NAME@">
<requires>
<import addon="os.libreelec.tv" version="@OS_VERSION@"/>
<import addon="xbmc.python" version="2.1.0"/>
@REQUIRES@
</requires>
<extension point="xbmc.service" library="default.py">
<provides>@PKG_ADDON_PROVIDES@</provides>
</extension>
<extension point="xbmc.python.library" library="addon.py">
<provides></provides>
</extension>
<extension point="xbmc.addon.metadata">
<summary>@PKG_SHORTDESC@</summary>
<description>
@PKG_LONGDESC@
</description>
<disclaimer>
@PKG_DISCLAIMER@
</disclaimer>
<platform>all</platform>
<news>
@PKG_ADDON_NEWS@
</news>
<assets>
<icon>resources/icon.png</icon>
<fanart>resources/fanart.png</fanart>
@PKG_ADDON_SCREENSHOT@
</assets>
</extension>
</addon>

View File

@ -1,2 +1,5 @@
101
- Enable streaming to Kodi
100
- Initial addon

View File

@ -18,20 +18,21 @@
################################################################################
PKG_NAME="librespot"
PKG_VERSION="2259188"
PKG_REV="100"
PKG_VERSION="67deb07"
PKG_REV="101"
PKG_ARCH="any"
PKG_LICENSE="prop."
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 libvorbis pyalsaaudio rust"
PKG_DEPENDS_TARGET="toolchain avahi ffmpegx libvorbis pyalsaaudio rust"
PKG_SECTION="service"
PKG_LONGDESC="Librespot ($PKG_VERSION) plays Spotify through LibreELEC using the opensource librespot library 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_AUTORECONF="no"
PKG_IS_ADDON="yes"
PKG_ADDON_NAME="Librespot"
PKG_ADDON_TYPE="xbmc.service"
PKG_ADDON_TYPE="xbmc.service.library"
PKG_MAINTAINER="Anton Voyl (awiouy)"
configure_target() {
@ -43,6 +44,8 @@ configure_target() {
make_target() {
cd src
$CARGO_BUILD --no-default-features --features "alsa-backend with-avahi"
cd "$PKG_BUILD/.$TARGET_NAME"/*/release
$STRIP librespot
}
makeinstall_target() {
@ -50,15 +53,16 @@ makeinstall_target() {
}
addon() {
mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID"
cp "$(get_build_dir pyalsaaudio)/.install_pkg/usr/lib/python2.7/site-packages/alsaaudio.so" \
"$ADDON_BUILD/$PKG_ADDON_ID"
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"
cp "$(get_build_dir avahi)/avahi-compat-libdns_sd/.libs/libdns_sd.so.1" \
"$ADDON_BUILD/$PKG_ADDON_ID/lib"
mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID/wizard"
cp "$(get_build_dir pyalsaaudio)/.install_pkg/usr/lib/python2.7/site-packages/alsaaudio.so" \
"$ADDON_BUILD/$PKG_ADDON_ID/wizard/"
}

View File

@ -1,87 +0,0 @@
From 031cc0a420db9d3ae8dd3543d07ff8503bdc508d Mon Sep 17 00:00:00 2001
From: Michael Herger <michael@herger.net>
Date: Tue, 20 Jun 2017 12:31:55 +0200
Subject: [PATCH] Add --disable-audio-cache startup parameter
Disable caching of downloaded audio files at runtime. Comes in handy when running librespot on a small device with SD card or other small storage.
---
src/audio_file.rs | 24 +++++++++++++-----------
src/main.rs | 2 ++
src/session.rs | 2 ++
3 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/src/audio_file.rs b/src/audio_file.rs
index 369d5ca..d014ba2 100644
--- a/src/audio_file.rs
+++ b/src/audio_file.rs
@@ -151,17 +151,19 @@ impl AudioFileManager {
complete_tx: Some(complete_tx),
};
- let session = self.session();
- self.session().spawn(move |_| {
- complete_rx.map(move |mut file| {
- if let Some(cache) = session.cache() {
- cache.save_file(file_id, &mut file);
- debug!("File {} complete, saving to cache", file_id);
- } else {
- debug!("File {} complete", file_id);
- }
- }).or_else(|oneshot::Canceled| Ok(()))
- });
+ if self.session().config().use_audio_cache {
+ let session = self.session();
+ self.session().spawn(move |_| {
+ complete_rx.map(move |mut file| {
+ if let Some(cache) = session.cache() {
+ cache.save_file(file_id, &mut file);
+ debug!("File {} complete, saving to cache", file_id);
+ } else {
+ debug!("File {} complete", file_id);
+ }
+ }).or_else(|oneshot::Canceled| Ok(()))
+ });
+ }
AudioFileOpen::Streaming(open)
}
diff --git a/src/main.rs b/src/main.rs
index 38c57fd..8a31a44 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -86,6 +86,7 @@ struct Setup {
fn setup(args: &[String]) -> Setup {
let mut opts = getopts::Options::new();
opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
+ .optflag("", "disable-audio-cache", "Disable caching of the audio data.")
.reqopt("n", "name", "Device name", "NAME")
.optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE")
.optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
@@ -152,6 +153,7 @@ fn setup(args: &[String]) -> Setup {
bitrate: bitrate,
onstart: matches.opt_str("onstart"),
onstop: matches.opt_str("onstop"),
+ use_audio_cache: !matches.opt_present("disable-audio-cache"),
};
let device = matches.opt_str("device");
diff --git a/src/session.rs b/src/session.rs
index 86162bd..a5d397e 100644
--- a/src/session.rs
+++ b/src/session.rs
@@ -49,6 +49,7 @@ pub struct Config {
pub bitrate: Bitrate,
pub onstart: Option<String>,
pub onstop: Option<String>,
+ pub use_audio_cache: bool,
}
impl Default for Config {
@@ -60,6 +61,7 @@ impl Default for Config {
bitrate: Bitrate::Bitrate160,
onstart: None,
onstop: None,
+ use_audio_cache: true,
}
}
}

View File

@ -16,24 +16,8 @@
# along with LibreELEC. If not, see <http://www.gnu.org/licenses/>.
################################################################################
import alsaaudio as alsa
import xbmcaddon
import xbmcgui
from default import addon as addon
if __name__ == '__main__':
addon = xbmcaddon.Addon('service.librespot')
dialog = xbmcgui.Dialog()
strings = addon.getLocalizedString
while True:
pcms = alsa.pcms()[1:]
if len(pcms) == 0:
dialog.ok(strings(30211), strings(30212))
break
pcmx = dialog.select(strings(30113), pcms)
if pcmx == -1:
break
pcm = pcms[pcmx]
addon.setSetting('ls_o', pcm)
break
addon()

View File

@ -17,10 +17,6 @@
# along with LibreELEC. If not, see <http://www.gnu.org/licenses/>.
################################################################################
. /etc/os-release
. /etc/profile
oe_setup_addon service.librespot
activate_card() {
if [ -e "/proc/asound/$1" ]; then
return
@ -39,62 +35,83 @@ activate_card() {
esac
}
if [ ! "$(cat /proc/asound/pcm 2> /dev/null)" ]; then
case "$LIBREELEC_ARCH" in
RPi*.arm)
activate_card "ALSA"
init_alsa() {
. /etc/os-release
if [ ! "$(cat /proc/asound/pcm 2> /dev/null)" ]; then
case "$LIBREELEC_ARCH" in
RPi*.arm)
activate_card "ALSA"
;;
*)
echo "Unable to activate an audio interface on $LIBREELEC_ARCH"
exit
;;
esac
fi
case "$ls_o" in
*:CARD=*)
card="${ls_o##*:CARD=}"
card="${card%%,*}"
activate_card "$card"
index="$(readlink /proc/asound/$card)"
index="${index##*card}"
;;
hw:*,*)
echo "The hw:d,s specification is unreliable, use device:CARD=card instead"
index="${ls_o##hw:}"
index="${index%%,*}"
card="card$index"
activate_card "$card"
;;
*)
echo "Unable to activate an audio interface on $LIBREELEC_ARCH"
exit
if [ -n "$ls_o" ]; then
echo "Unknown playback device specification $ls_o"
fi
;;
esac
fi
case "$ls_o" in
*:CARD=*)
card="${ls_o##*:CARD=}"
card="${card%%,*}"
activate_card "$card"
index="$(readlink /proc/asound/$card)"
index="${index##*card}"
;;
hw:*,*)
echo "The hw:d,s specification is unreliable, use device:CARD=card instead"
index="${ls_o##hw:}"
index="${index%%,*}"
card="card$index"
activate_card "$card"
;;
*)
if [ -n "$ls_o" ]; then
echo "Unknown playback device specification $ls_o"
fi
;;
esac
case "$LIBREELEC_ARCH" in
RPi*.arm)
[ "$(readlink /proc/asound/ALSA)" == "card$index" ] && [ "$pcm_3" ] &&
amixer -c "$index" cset name="PCM Playback Route" "$pcm_3"
;;
esac
}
. /etc/profile
oe_setup_addon service.librespot
LIBRESPOT="librespot --cache \"$ADDON_HOME/cache\"
--disable-audio-cache \
--name \"Librespot@$HOSTNAME\""
if [ -n "$ls_b" -a "$ls_b" != "-" ]; then
bitrate="--bitrate $ls_b"
fi
if [ -n "$ls_o" ]; then
device="--device $ls_o"
LIBRESPOT="$LIBRESPOT --bitrate $ls_b"
fi
if [ -n "$ls_p" -a -n "$ls_u" ]; then
discovery="--disable-discovery --password $ls_p --username $ls_u"
LIBRESPOT="$LIBRESPOT --disable-discovery \
--password \"$ls_p\" \
--username \"$ls_u\""
fi
case "$LIBREELEC_ARCH" in
RPi*.arm)
[ "$(readlink /proc/asound/ALSA)" == "card$index" ] && [ "$pcm_3" ] &&
amixer -c "$index" cset name="PCM Playback Route" "$pcm_3"
;;
esac
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"
else
init_alsa
if [ -n "$ls_o" ]; then
LIBRESPOT="$LIBRESPOT --device \"$ls_o\""
fi
fi
librespot $bitrate \
--cache "$ADDON_HOME/cache" \
$device \
--disable-audio-cache \
$discovery \
--name "Librespot@$HOSTNAME"
eval $LIBRESPOT

View File

@ -16,20 +16,91 @@
# along with LibreELEC. If not, see <http://www.gnu.org/licenses/>.
################################################################################
import alsaaudio as alsa
import os
import subprocess
import sys
import xbmc
import xbmcaddon
import xbmcgui
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:
Player().play()
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
def systemctl(command):
subprocess.call(['systemctl', command, ID])
class Monitor(xbmc.Monitor):
def __init__(self, *args, **kwargs):
xbmc.Monitor.__init__(self)
self.id = xbmcaddon.Addon().getAddonInfo('id')
super(Monitor, self).__init__(self)
self.player = Player()
def onSettingsChanged(self):
subprocess.call(['systemctl', 'restart', self.id])
self.player.stop()
class Player(xbmc.Player):
def __init__(self, *args, **kwargs):
super(Player, self).__init__(self)
if self.isPlaying():
self.onPlayBackStarted()
def onPlayBackEnded(self):
if not self.islibrespot:
xbmc.sleep(5000)
if not self.isPlaying():
systemctl('start')
def onPlayBackStarted(self):
if self.getPlayingFile() == ITEM:
self.islibrespot = True
else:
self.islibrespot = False
systemctl('stop')
def onPlayBackStopped(self):
systemctl('restart')
def play(self):
if not self.isPlaying():
super(Player, self).play(ITEM, LISTITEM)
def stop(self):
if self.isPlaying():
if self.getPlayingFile() == ITEM:
super(Player, self).stop()
else:
systemctl('restart')
if __name__ == '__main__':
Monitor().waitForAbort()

View File

@ -4,69 +4,73 @@ msgid ""
msgstr ""
msgctxt "#30100"
msgid "Configuration"
msgid "Librespot"
msgstr ""
msgctxt "#30101"
msgid "ALSA"
msgstr ""
msgctxt "#30102"
msgid "Configuration wizard"
msgstr ""
msgctxt "#30103"
msgid "Playback device"
msgstr ""
msgctxt "#30104"
msgid "Playback route"
msgstr ""
msgctxt "#30105"
msgid "auto detect"
msgstr ""
msgctxt "#30106"
msgid "headphone jack"
msgstr ""
msgctxt "#30107"
msgid "HDMI"
msgstr ""
msgctxt "#30108"
msgid "Spotify"
msgstr ""
msgctxt "#30109"
msgid "Username"
msgstr ""
msgctxt "#30110"
msgid "Password"
msgstr ""
msgctxt "#30111"
msgid "Discovery mode (set username and password to disable)"
msgstr ""
msgctxt "#30112"
msgid "Bit rate"
msgstr ""
msgctxt "#30113"
msgctxt "#30102"
msgid "-"
msgstr ""
msgctxt "#30114"
msgid "90"
msgctxt "#30103"
msgid "96"
msgstr ""
msgctxt "#30115"
msgctxt "#30104"
msgid "160"
msgstr ""
msgctxt "#30116"
msgctxt "#30105"
msgid "320"
msgstr ""
msgctxt "#30106"
msgid "Output"
msgstr ""
msgctxt "#30107"
msgid "Username"
msgstr ""
msgctxt "#30108"
msgid "Password"
msgstr ""
msgctxt "#30109"
msgid "Discovery mode (set username and password to disable)"
msgstr ""
msgctxt "#30110"
msgid "ALSA"
msgstr ""
msgctxt "#30111"
msgid "Configuration wizard"
msgstr ""
msgctxt "#30112"
msgid "Playback device"
msgstr ""
msgctxt "#30113"
msgid "Playback route"
msgstr ""
msgctxt "#30114"
msgid "auto detect"
msgstr ""
msgctxt "#30115"
msgid "headphone jack"
msgstr ""
msgctxt "#30116"
msgid "HDMI"
msgstr ""
msgctxt "#30210"
msgid "Could not find a playback device"
msgstr ""

View File

@ -1,14 +1,17 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings>
<category label="30100" >
<setting label="30101" type="lsep" />
<setting label="30102" type="action" action="RunScript(/storage/.kodi/addons/service.librespot/wizard/wizard.py)" />
<setting label="30103" type="text" id="ls_o" default="" />
<setting label="30104" type="enum" id="pcm_3" lvalues="30105|30106|30107" visible="eq(-1,default:CARD=ALSA)|eq(-1,sysdefault:CARD=ALSA)" />
<setting label="30108" type="lsep" />
<setting label="30109" type="text" id="ls_u" />
<setting label="30110" type="text" id="ls_p" option="hidden" visible="!eq(-1,)" />
<setting label="30111" type="bool" id="ls_d" default="true" enable="false" visible="eq(-1,)|eq(-2,)" />
<setting label="30112" type="labelenum" id="ls_b" lvalues="30113|30114|30115|30116" />
<setting label="30101" type="labelenum" id="ls_b" lvalues="30102|30103|30104|30105" />
<setting label="30106" type="labelenum" id="ls_O" lvalues="ALSA|Kodi" />
<setting label="30107" type="text" id="ls_u" />
<setting label="30108" type="text" id="ls_p" option="hidden" visible="!eq(-1,)" />
<setting label="30109" type="bool" id="ls_d" default="true" enable="false" visible="eq(-1,)|eq(-2,)" />
<setting label="30109" type="bool" id="ls_D" default="false" enable="false" visible="!eq(-2,)+!eq(-3,)" />
</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="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>
</settings>