changeset 2:594c0f9d7972

errr
author Paper <paper@tflc.us>
date Sat, 04 Apr 2026 16:04:31 -0400
parents a5ee18c79a04
children 18f743c980fa
files src/beefweb.rs src/player.rs
diffstat 2 files changed, 174 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/src/beefweb.rs	Sat Apr 04 12:34:46 2026 -0400
+++ b/src/beefweb.rs	Sat Apr 04 16:04:31 2026 -0400
@@ -18,6 +18,9 @@
  * <https://www.gnu.org/licenses/>.
 */
 
+/* Fuck off, I don't care. */
+#![allow(nonstandard_style)]
+
 #[derive(serde::Serialize, serde::Deserialize, Debug)]
 pub struct PlayerInfo {
 	pub name: String,
@@ -71,15 +74,66 @@
 }
 
 #[derive(serde::Serialize, serde::Deserialize, Debug)]
+pub enum PlaybackState {
+	#[serde(rename = "stopped")]
+	STOPPED,
+	#[serde(rename = "playing")]
+	PLAYING,
+	#[serde(rename = "paused")]
+	PAUSED,
+}
+
+/* fb2k playback order */
+#[derive(Debug)]
+pub enum PlaybackOrder {
+	DEFAULT,
+	REPEAT_PLAYLIST,
+	REPEAT_TRACK,
+	RANDOM,
+	SHUFFLE_TRACKS,
+	SHUFFLE_ALBUMS,
+	SHUFFLE_FOLDERS,
+}
+
+#[derive(serde::Serialize, serde::Deserialize, Debug)]
+#[serde(tag = "type")]
+pub enum Options {
+	#[serde(rename = "enum")]
+	Enumeration {
+		#[serde(rename = "enumNames")]
+		enum_names: Vec<String>,
+		id: String,
+		name: String,
+		value: i64,
+	},
+	#[serde(rename = "bool")]
+	Boolean {
+		id: String,
+		name: String,
+		value: bool,
+	},
+}
+
+#[derive(serde::Serialize, serde::Deserialize, Debug)]
 pub struct Player {
 	pub info: PlayerInfo,
 	#[serde(rename = "activeItem")]
 	pub active_item: ActiveItemInfo,
 	/* XXX actually an enum */
 	#[serde(rename = "playbackState")]
-	pub playback_state: String,
+	pub playback_state: PlaybackState,
 	pub volume: VolumeInfo,
 	pub permissions: ApiPermissions,
+
+	options: Option<Vec<Options>>,
+
+	/* these two fields are deprecated,
+	 * replaced with "options" array */
+	#[serde(skip_serializing_if = "Option::is_none")]
+	playbackMode: Option<i64>,
+
+	#[serde(skip_serializing_if = "Option::is_none")]
+	playbackModes: Option<Vec<String>>,
 }
 
 /* {"player": {struct Player}} */
@@ -158,6 +212,76 @@
 			.player);
 	}
 
+	fn playback_order_lookup(playback_order: &str) -> Option<PlaybackOrder>
+	{
+		return match playback_order {
+			"Default" => return Some(PlaybackOrder::DEFAULT),
+			"Repeat (playlist)" => return Some(PlaybackOrder::REPEAT_PLAYLIST),
+			"Repeat (track)" => return Some(PlaybackOrder::REPEAT_TRACK),
+			"Random" => return Some(PlaybackOrder::RANDOM),
+			"Shuffle (tracks)" => return Some(PlaybackOrder::SHUFFLE_TRACKS),
+			"Shuffle (albums)" => return Some(PlaybackOrder::SHUFFLE_ALBUMS),
+			"Shuffle (folders)" => return Some(PlaybackOrder::SHUFFLE_FOLDERS),
+			_ => None,
+		};
+	}
+
+	fn playback_order_cruft(v: &Vec<String>, s: i64) -> Option<PlaybackOrder>
+	{
+		let x = v.get(s as usize);
+
+		match x {
+			Some(y) => return Self::playback_order_lookup(y),
+			_ => (),
+		};
+
+		return None;
+	}
+
+	pub async fn playback_order(&self) -> Result<PlaybackOrder, anyhow::Error>
+	{
+		let p = self.player().await?;
+
+		match p.options {
+			Some(x) => {
+				for i in x {
+					match i {
+						Options::Enumeration { enum_names: e, id: id, name: _, value: v } => {
+							match id.as_str() {
+								"playbackOrder" => {
+									match Self::playback_order_cruft(&e, v) {
+										Some(z) => return Ok(z),
+										_ => (),
+									};
+								},
+								_ => (),
+							};
+						},
+						_ => (),
+					};
+				}
+			},
+			_ => (),
+		}
+
+		match p.playbackModes {
+			Some(x) => {
+				match p.playbackMode {
+					Some(y) => {
+						match Self::playback_order_cruft(&x, y) {
+							Some(v) => return Ok(v),
+							_ => (),
+						}
+					},
+					_ => (),
+				};
+			}
+			_ => (),
+		}
+
+		return Err(anyhow::anyhow!("Unknown or invalid playback order?"));
+	}
+
 	pub async fn volume(&self) -> Result<VolumeInfo, anyhow::Error>
 	{
 		return Ok(self.player().await?.volume);
@@ -237,14 +361,29 @@
 	{
 		return self.set_player(&SetPlayer {
 			volume: None,
-			relative_volume: Some(position),
+			relative_volume: None,
 			muted: None,
-			position: None,
+			position: Some(position),
 			relative_position: None,
 			options: None,
 		}).await;
 	}
 
+/* TODO -- finish this; need to link between string and enum
+
+	pub async fn set_playback_order(&self, ) -> Result<(), anyhow::Error>
+	{
+		let p = self.set_player(&SetPlayer {
+			volume: None,
+			relative_volume: None,
+			muted: None,
+			position: None,
+			relative_position: None,
+			options: 
+		})
+	}
+*/
+
 	pub async fn playlist_items(&self, playlist_id: &str, offset: i64, count: i64, columns: Vec<&str>) -> Result<PlaylistItems, anyhow::Error>
 	{
 		return Ok(self.client
--- a/src/player.rs	Sat Apr 04 12:34:46 2026 -0400
+++ b/src/player.rs	Sat Apr 04 16:04:31 2026 -0400
@@ -23,7 +23,7 @@
 use zbus::fdo;
 use zbus::Result;
 use std::collections::HashMap;
-use std::io::Write;
+//use std::io::Write;
 use std::cell::RefCell;
 
 /* teehee */
@@ -237,34 +237,35 @@
 			_      => (),
 		};
 
-		return match p.unwrap().playback_state.as_str() {
-			"playing" => Ok(mpris_server::PlaybackStatus::Playing),
-			"stopped" => Ok(mpris_server::PlaybackStatus::Stopped),
-			"paused"  => Ok(mpris_server::PlaybackStatus::Paused),
-			_         => Err(fdo::Error::Failed("deez nuts".to_string())),
+		return match p.unwrap().playback_state {
+			beefweb::PlaybackState::PLAYING => Ok(mpris_server::PlaybackStatus::Playing),
+			beefweb::PlaybackState::STOPPED => Ok(mpris_server::PlaybackStatus::Stopped),
+			beefweb::PlaybackState::PAUSED  => Ok(mpris_server::PlaybackStatus::Paused),
 		};
 	}
 
 	async fn loop_status(&self) -> fdo::Result<mpris_server::LoopStatus>
 	{
+		/* TODO -- we can do this w/ self.bw.playback_order() */
 		return Ok(mpris_server::LoopStatus::None);
 	}
 
 	async fn set_loop_status(&self, loop_status: mpris_server::LoopStatus) -> Result<()>
 	{
+		/* TODO -- implement self.bw.set_playback_order() */
 		return Ok(());
 	}
 
 	async fn shuffle(&self) -> fdo::Result<bool>
 	{
-		/* TODO */
+		/* TODO -- we can do this w/ self.bw.playback_order() */
 		println!("Shuffle");
 		return Ok(false);
 	}
 
 	async fn set_shuffle(&self, shuffle: bool) -> Result<()>
 	{
-		/* TODO */
+		/* TODO -- implement self.bw.set_playback_order() */
 		println!("SetShuffle({shuffle})");
 		return Ok(());
 	}
@@ -315,11 +316,33 @@
 
 	async fn volume(&self) -> fdo::Result<mpris_server::Volume>
 	{
-		return Ok(mpris_server::Volume::default());
+		let vr = self.bw.volume().await;
+
+		match vr {
+			Err(_) => return Err(fdo::Error::Failed("uhoh".to_string())),
+			_      => (),
+		}
+
+		let v = vr.unwrap();
+
+		/* dB -> linear */
+		return Ok(match v.r#type {
+			beefweb::VolumeType::DB => 10.0_f64.powf(v.value / 20.0),
+			beefweb::VolumeType::LINEAR => v.value,
+			beefweb::VolumeType::UPDOWN => /* ??? */ v.value,
+		});
 	}
 
 	async fn set_volume(&self, volume: mpris_server::Volume) -> Result<()>
 	{
+		/* linear -> dB */
+		let v = 20.0 * volume.log10();
+
+		match self.bw.set_volume(v).await {
+			Err(_) => return Err(zbus::Error::Failure("uhoh".to_string())),
+			_      => (),
+		}
+
 		return Ok(());
 	}