Merge pull request #1302 from JoeyEamigh/add-group-flag

add flag to display connect device as group
This commit is contained in:
Roderick van Domburg 2024-06-17 23:16:28 +02:00 committed by GitHub
commit cdff6da1f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 49 additions and 1 deletions

View file

@ -4,6 +4,7 @@ use crate::core::config::DeviceType;
pub struct ConnectConfig { pub struct ConnectConfig {
pub name: String, pub name: String,
pub device_type: DeviceType, pub device_type: DeviceType,
pub is_group: bool,
pub initial_volume: Option<u16>, pub initial_volume: Option<u16>,
pub has_volume_ctrl: bool, pub has_volume_ctrl: bool,
} }
@ -13,6 +14,7 @@ impl Default for ConnectConfig {
ConnectConfig { ConnectConfig {
name: "Librespot".to_string(), name: "Librespot".to_string(),
device_type: DeviceType::default(), device_type: DeviceType::default(),
is_group: false,
initial_volume: Some(50), initial_volume: Some(50),
has_volume_ctrl: true, has_volume_ctrl: true,
} }

View file

@ -0,0 +1,22 @@
use futures::StreamExt;
use librespot_core::SessionConfig;
use librespot_discovery::DeviceType;
use sha1::{Digest, Sha1};
#[tokio::main(flavor = "current_thread")]
async fn main() {
let name = "Librespot Group";
let device_id = hex::encode(Sha1::digest(name.as_bytes()));
let mut server =
librespot_discovery::Discovery::builder(device_id, SessionConfig::default().client_id)
.name(name)
.device_type(DeviceType::Speaker)
.is_group(true)
.launch()
.unwrap();
while let Some(x) = server.next().await {
println!("Received {:?}", x);
}
}

View file

@ -84,6 +84,7 @@ impl Builder {
server_config: server::Config { server_config: server::Config {
name: "Librespot".into(), name: "Librespot".into(),
device_type: DeviceType::default(), device_type: DeviceType::default(),
is_group: false,
device_id: device_id.into(), device_id: device_id.into(),
client_id: client_id.into(), client_id: client_id.into(),
}, },
@ -104,6 +105,12 @@ impl Builder {
self self
} }
/// Sets whether the device is a group. This affects the icon in Spotify clients. Default is `false`.
pub fn is_group(mut self, is_group: bool) -> Self {
self.server_config.is_group = is_group;
self
}
/// Set the ip addresses on which it should listen to incoming connections. The default is all interfaces. /// Set the ip addresses on which it should listen to incoming connections. The default is all interfaces.
pub fn zeroconf_ip(mut self, zeroconf_ip: Vec<std::net::IpAddr>) -> Self { pub fn zeroconf_ip(mut self, zeroconf_ip: Vec<std::net::IpAddr>) -> Self {
self.zeroconf_ip = zeroconf_ip; self.zeroconf_ip = zeroconf_ip;

View file

@ -39,6 +39,7 @@ pub struct Config {
pub name: Cow<'static, str>, pub name: Cow<'static, str>,
pub device_type: DeviceType, pub device_type: DeviceType,
pub device_id: String, pub device_id: String,
pub is_group: bool,
pub client_id: String, pub client_id: String,
} }
@ -70,6 +71,12 @@ impl RequestHandler {
if let Some(username) = &self.username { if let Some(username) = &self.username {
active_user = username.to_string(); active_user = username.to_string();
} }
// options based on zeroconf guide, search for `groupStatus` on page
let group_status = if self.config.is_group {
"GROUP"
} else {
"NONE"
};
// See: https://developer.spotify.com/documentation/commercial-hardware/implementation/guides/zeroconf/ // See: https://developer.spotify.com/documentation/commercial-hardware/implementation/guides/zeroconf/
let body = json!({ let body = json!({
@ -87,7 +94,8 @@ impl RequestHandler {
"modelDisplayName": "librespot", "modelDisplayName": "librespot",
"libraryVersion": crate::core::version::SEMVER, "libraryVersion": crate::core::version::SEMVER,
"resolverVersion": "1", "resolverVersion": "1",
"groupStatus": "NONE", // valid values are "GROUP" and "NONE"
"groupStatus": group_status,
// valid value documented & seen in the wild: "accesstoken" // valid value documented & seen in the wild: "accesstoken"
// Using it will cause clients to fail to connect. // Using it will cause clients to fail to connect.
"tokenType": "default", "tokenType": "default",

View file

@ -203,6 +203,7 @@ fn get_setup() -> Setup {
const CACHE_SIZE_LIMIT: &str = "cache-size-limit"; const CACHE_SIZE_LIMIT: &str = "cache-size-limit";
const DEVICE: &str = "device"; const DEVICE: &str = "device";
const DEVICE_TYPE: &str = "device-type"; const DEVICE_TYPE: &str = "device-type";
const DEVICE_IS_GROUP: &str = "group";
const DISABLE_AUDIO_CACHE: &str = "disable-audio-cache"; const DISABLE_AUDIO_CACHE: &str = "disable-audio-cache";
const DISABLE_CREDENTIAL_CACHE: &str = "disable-credential-cache"; const DISABLE_CREDENTIAL_CACHE: &str = "disable-credential-cache";
const DISABLE_DISCOVERY: &str = "disable-discovery"; const DISABLE_DISCOVERY: &str = "disable-discovery";
@ -409,6 +410,10 @@ fn get_setup() -> Setup {
DEVICE_TYPE, DEVICE_TYPE,
"Displayed device type. Defaults to speaker.", "Displayed device type. Defaults to speaker.",
"TYPE", "TYPE",
).optflag(
"",
DEVICE_IS_GROUP,
"Whether the device represents a group. Defaults to false.",
) )
.optopt( .optopt(
TEMP_DIR_SHORT, TEMP_DIR_SHORT,
@ -1312,11 +1317,14 @@ fn get_setup() -> Setup {
}) })
.unwrap_or_default(); .unwrap_or_default();
let is_group = opt_present(DEVICE_IS_GROUP);
let has_volume_ctrl = !matches!(mixer_config.volume_ctrl, VolumeCtrl::Fixed); let has_volume_ctrl = !matches!(mixer_config.volume_ctrl, VolumeCtrl::Fixed);
ConnectConfig { ConnectConfig {
name, name,
device_type, device_type,
is_group,
initial_volume, initial_volume,
has_volume_ctrl, has_volume_ctrl,
} }
@ -1685,6 +1693,7 @@ async fn main() {
match librespot::discovery::Discovery::builder(device_id, client_id) match librespot::discovery::Discovery::builder(device_id, client_id)
.name(setup.connect_config.name.clone()) .name(setup.connect_config.name.clone())
.device_type(setup.connect_config.device_type) .device_type(setup.connect_config.device_type)
.is_group(setup.connect_config.is_group)
.port(setup.zeroconf_port) .port(setup.zeroconf_port)
.zeroconf_ip(setup.zeroconf_ip.clone()) .zeroconf_ip(setup.zeroconf_ip.clone())
.launch() .launch()