audio: fix librespot playback and use avahi for mDNS
Fix librespot playback by building the dev branch and applying the changes from librespot PR 1528 (https://github.com/librespot-org/librespot/pull/1528) fixing librespot issue 1527 (https://github.com/librespot-org/librespot/issues/1527). Also make librespot use Avahi, since shairport-sync already uses that.
This commit is contained in:
parent
071eb88afa
commit
df365e67f9
4 changed files with 251 additions and 4 deletions
|
@ -77,4 +77,4 @@ nix build .#proxmox-chaosknoten-nixos-template
|
|||
## License
|
||||
|
||||
This CCCHH nix-infra repository is licensed under the [MIT License](./LICENSE).
|
||||
[`0001_oidc_group_and_role_mapping_custom_pipeline.patch`](patches/0001_oidc_group_and_role_mapping_custom_pipeline.patch) is licensed under the Creative Commons: CC BY-SA 4.0 license.
|
||||
[`librespot_PR1528_conflicts_resolved.patch`](patches/librespot_PR1528_conflicts_resolved.patch) is a modified version of [librespot PR 1528](https://github.com/librespot-org/librespot/pull/1528) and is licensed under the [MIT license](https://github.com/librespot-org/librespot/blob/dev/LICENSE).
|
||||
|
|
24
flake.nix
24
flake.nix
|
@ -40,6 +40,29 @@
|
|||
proxmox-vm = ./config/proxmox-vm;
|
||||
prometheus-exporter = ./config/extra/prometheus-exporter.nix;
|
||||
};
|
||||
overlays = {
|
||||
librespotFixOverlay = final: prev: {
|
||||
librespot = (prev.librespot.override { withAvahi = true; }).overrideAttrs (finalAttrs: prevAttr: rec {
|
||||
# Build dev branch.
|
||||
name = "${prevAttr.pname}-${version}";
|
||||
version = "dev";
|
||||
src = prev.fetchFromGitHub {
|
||||
owner = "librespot-org";
|
||||
repo = "librespot";
|
||||
rev = "dev";
|
||||
sha256 = "sha256-s9JpIbqXiVXMlhEuIuKio+rD1rM3kc7bAT0+8+5s35w=";
|
||||
};
|
||||
cargoDeps = final.rustPlatform.fetchCargoVendor {
|
||||
inherit src;
|
||||
hash = "sha256-Lujz2revTAok9B0hzdl8NVQ5XMRY9ACJzoQHIkIgKMg=";
|
||||
};
|
||||
# Fix librespot failing with "Unable to load audio item: Error { kind: Unavailable, error: StatusCode(500) }".
|
||||
patches = (prevAttr.patches or []) ++ [
|
||||
./patches/librespot_PR1528_conflicts_resolved.patch
|
||||
];
|
||||
});
|
||||
};
|
||||
};
|
||||
nixosConfigurations = {
|
||||
audio-hauptraum-kueche = nixpkgs.lib.nixosSystem {
|
||||
inherit system specialArgs;
|
||||
|
@ -56,6 +79,7 @@
|
|||
self.nixosModules.common
|
||||
self.nixosModules.proxmox-vm
|
||||
./config/hosts/audio-hauptraum-tafel
|
||||
{ nixpkgs.overlays = [ self.overlays.librespotFixOverlay ]; }
|
||||
];
|
||||
};
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@ in
|
|||
enable = true;
|
||||
description = "Spotify Connect Receiver Using librespot";
|
||||
unitConfig = {
|
||||
Requires = [ "network-online.target" "pipewire.service" ];
|
||||
After = [ "network-online.target" "pipewire.service" ];
|
||||
Requires = [ "network-online.target" "pipewire.service" "avahi-daemon.service" ];
|
||||
After = [ "network-online.target" "pipewire.service" "avahi-daemon.service" ];
|
||||
};
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.librespot}/bin/librespot --name '${config.ccchh.services.audio.name}' --device-type speaker --bitrate 320 --enable-volume-normalisation --disable-audio-cache --disable-credential-cache";
|
||||
ExecStart = "${pkgs.librespot}/bin/librespot --name '${config.ccchh.services.audio.name}' --device-type speaker --bitrate 320 --enable-volume-normalisation --disable-audio-cache --disable-credential-cache --zeroconf-backend avahi";
|
||||
User = "librespot";
|
||||
Group = "librespot";
|
||||
};
|
||||
|
|
223
patches/librespot_PR1528_conflicts_resolved.patch
Normal file
223
patches/librespot_PR1528_conflicts_resolved.patch
Normal file
|
@ -0,0 +1,223 @@
|
|||
From c4c968e594edcfce231682db5563f7186da7c6f0 Mon Sep 17 00:00:00 2001
|
||||
From: Timon de Groot <tdegroot96@gmail.com>
|
||||
Date: Thu, 7 Aug 2025 12:22:56 +0200
|
||||
Subject: [PATCH 1/5] spclient: Specify base url for metadata requests
|
||||
|
||||
This fixes #1527
|
||||
---
|
||||
core/src/spclient.rs | 15 +++++++++++++--
|
||||
1 file changed, 13 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/core/src/spclient.rs b/core/src/spclient.rs
|
||||
index 87a6098..56c4287 100644
|
||||
--- a/core/src/spclient.rs
|
||||
+++ b/core/src/spclient.rs
|
||||
@@ -55,6 +55,7 @@ const CONNECTION_ID: HeaderName = HeaderName::from_static("x-spotify-connection-
|
||||
const NO_METRICS_AND_SALT: RequestOptions = RequestOptions {
|
||||
metrics: false,
|
||||
salt: false,
|
||||
+ base_url: None,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -86,6 +87,7 @@ impl Default for RequestStrategy {
|
||||
pub struct RequestOptions {
|
||||
metrics: bool,
|
||||
salt: bool,
|
||||
+ base_url: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for RequestOptions {
|
||||
@@ -93,6 +95,7 @@ impl Default for RequestOptions {
|
||||
Self {
|
||||
metrics: true,
|
||||
salt: true,
|
||||
+ base_url: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,7 +452,10 @@ impl SpClient {
|
||||
|
||||
// Reconnection logic: retrieve the endpoint every iteration, so we can try
|
||||
// another access point when we are experiencing network issues (see below).
|
||||
- let mut url = self.base_url().await?;
|
||||
+ let mut url = match &options.base_url {
|
||||
+ Some(base_url) => base_url.clone(),
|
||||
+ None => self.base_url().await?,
|
||||
+ };
|
||||
url.push_str(endpoint);
|
||||
|
||||
// Add metrics. There is also an optional `partner` key with a value like
|
||||
@@ -566,7 +572,12 @@ impl SpClient {
|
||||
|
||||
pub async fn get_metadata(&self, scope: &str, id: &SpotifyId) -> SpClientResult {
|
||||
let endpoint = format!("/metadata/4/{}/{}", scope, id.to_base16()?);
|
||||
- self.request(&Method::GET, &endpoint, None, None).await
|
||||
+ let options = RequestOptions {
|
||||
+ base_url: Some(String::from("https://spclient.wg.spotify.com")),
|
||||
+ ..Default::default()
|
||||
+ };
|
||||
+ self.request_with_options(&Method::GET, &endpoint, None, None, &options)
|
||||
+ .await
|
||||
}
|
||||
|
||||
pub async fn get_track_metadata(&self, track_id: &SpotifyId) -> SpClientResult {
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
||||
From 2b72f3fbdf6519321feeaaecc1ea6e1bb042074e Mon Sep 17 00:00:00 2001
|
||||
From: Timon de Groot <tdegroot96@gmail.com>
|
||||
Date: Thu, 7 Aug 2025 13:51:55 +0200
|
||||
Subject: [PATCH 2/5] spclient: Change RequestOptions to &str
|
||||
|
||||
This will allocate less strings and makes it possible to have const
|
||||
request option values.
|
||||
|
||||
Also document why the metadata base url workaround is needed.
|
||||
---
|
||||
core/src/spclient.rs | 9 ++++++---
|
||||
1 file changed, 6 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/core/src/spclient.rs b/core/src/spclient.rs
|
||||
index 56c4287..11bcef4 100644
|
||||
--- a/core/src/spclient.rs
|
||||
+++ b/core/src/spclient.rs
|
||||
@@ -87,7 +87,7 @@ impl Default for RequestStrategy {
|
||||
pub struct RequestOptions {
|
||||
metrics: bool,
|
||||
salt: bool,
|
||||
- base_url: Option<String>,
|
||||
+ base_url: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl Default for RequestOptions {
|
||||
@@ -453,7 +453,7 @@ impl SpClient {
|
||||
// Reconnection logic: retrieve the endpoint every iteration, so we can try
|
||||
// another access point when we are experiencing network issues (see below).
|
||||
let mut url = match &options.base_url {
|
||||
- Some(base_url) => base_url.clone(),
|
||||
+ Some(base_url) => base_url.to_owned().to_string(),
|
||||
None => self.base_url().await?,
|
||||
};
|
||||
url.push_str(endpoint);
|
||||
@@ -572,8 +572,11 @@ impl SpClient {
|
||||
|
||||
pub async fn get_metadata(&self, scope: &str, id: &SpotifyId) -> SpClientResult {
|
||||
let endpoint = format!("/metadata/4/{}/{}", scope, id.to_base16()?);
|
||||
+ // For unknown reasons, metadata requests must now be sent through spclient.wg.spotify.com.
|
||||
+ // Otherwise, the API will respond with 500 Internal Server Error responses.
|
||||
+ // Context: https://github.com/librespot-org/librespot/issues/1527
|
||||
let options = RequestOptions {
|
||||
- base_url: Some(String::from("https://spclient.wg.spotify.com")),
|
||||
+ base_url: Some("https://spclient.wg.spotify.com"),
|
||||
..Default::default()
|
||||
};
|
||||
self.request_with_options(&Method::GET, &endpoint, None, None, &options)
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
||||
From 73ed5c50849bb660834cd0d7aaa7110c01397055 Mon Sep 17 00:00:00 2001
|
||||
From: Timon de Groot <tdegroot96@gmail.com>
|
||||
Date: Sat, 9 Aug 2025 09:28:51 +0200
|
||||
Subject: [PATCH 3/5] spclient: Make const request options for get_metadata
|
||||
|
||||
---
|
||||
core/src/spclient.rs | 20 ++++++++++++++------
|
||||
1 file changed, 14 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/core/src/spclient.rs b/core/src/spclient.rs
|
||||
index 11bcef4..cbcf092 100644
|
||||
--- a/core/src/spclient.rs
|
||||
+++ b/core/src/spclient.rs
|
||||
@@ -58,6 +58,12 @@ const NO_METRICS_AND_SALT: RequestOptions = RequestOptions {
|
||||
base_url: None,
|
||||
};
|
||||
|
||||
+const SPCLIENT_FALLBACK_ENDPOINT: RequestOptions = RequestOptions {
|
||||
+ metrics: true,
|
||||
+ salt: true,
|
||||
+ base_url: Some("https://spclient.wg.spotify.com"),
|
||||
+};
|
||||
+
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SpClientError {
|
||||
#[error("missing attribute {0}")]
|
||||
@@ -575,12 +581,14 @@ impl SpClient {
|
||||
// For unknown reasons, metadata requests must now be sent through spclient.wg.spotify.com.
|
||||
// Otherwise, the API will respond with 500 Internal Server Error responses.
|
||||
// Context: https://github.com/librespot-org/librespot/issues/1527
|
||||
- let options = RequestOptions {
|
||||
- base_url: Some("https://spclient.wg.spotify.com"),
|
||||
- ..Default::default()
|
||||
- };
|
||||
- self.request_with_options(&Method::GET, &endpoint, None, None, &options)
|
||||
- .await
|
||||
+ self.request_with_options(
|
||||
+ &Method::GET,
|
||||
+ &endpoint,
|
||||
+ None,
|
||||
+ None,
|
||||
+ &SPCLIENT_FALLBACK_ENDPOINT,
|
||||
+ )
|
||||
+ .await
|
||||
}
|
||||
|
||||
pub async fn get_track_metadata(&self, track_id: &SpotifyId) -> SpClientResult {
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
||||
From 6adca21fdf64bd8026a2d6df04c42dd2b1239358 Mon Sep 17 00:00:00 2001
|
||||
From: Timon de Groot <tdegroot96@gmail.com>
|
||||
Date: Sat, 9 Aug 2025 09:40:20 +0200
|
||||
Subject: [PATCH 4/5] spclient: Simplify base url init
|
||||
|
||||
---
|
||||
core/src/spclient.rs | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/core/src/spclient.rs b/core/src/spclient.rs
|
||||
index cbcf092..272975d 100644
|
||||
--- a/core/src/spclient.rs
|
||||
+++ b/core/src/spclient.rs
|
||||
@@ -458,8 +458,8 @@ impl SpClient {
|
||||
|
||||
// Reconnection logic: retrieve the endpoint every iteration, so we can try
|
||||
// another access point when we are experiencing network issues (see below).
|
||||
- let mut url = match &options.base_url {
|
||||
- Some(base_url) => base_url.to_owned().to_string(),
|
||||
+ let mut url = match options.base_url {
|
||||
+ Some(base_url) => base_url.to_string(),
|
||||
None => self.base_url().await?,
|
||||
};
|
||||
url.push_str(endpoint);
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
||||
From 0b5b1eb6c73a9291057b3856939f416113fdd8bb Mon Sep 17 00:00:00 2001
|
||||
From: Timon de Groot <tdegroot96@gmail.com>
|
||||
Date: Sat, 9 Aug 2025 10:14:02 +0200
|
||||
Subject: [PATCH 5/5] Update CHANGELOG.md
|
||||
|
||||
---
|
||||
CHANGELOG.md | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/CHANGELOG.md b/CHANGELOG.md
|
||||
index 560de2b..b62e9f8 100644
|
||||
--- a/CHANGELOG.md
|
||||
+++ b/CHANGELOG.md
|
||||
@@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- [connect] Correctly apply playing/paused state when transferring playback
|
||||
- [player] Saturate invalid seek positions to track duration
|
||||
- [audio] Fall back to other URLs in case of a failure when downloading from CDN
|
||||
+- [core] Metadata requests failing with 500 Internal Server Error
|
||||
|
||||
### Deprecated
|
||||
|
||||
--
|
||||
2.49.0
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue