Mercurial > beefweb_mpris
view src/beefweb.rs @ 0:d60ab8a4442f
*: check in
| author | Paper <paper@tflc.us> |
|---|---|
| date | Sat, 04 Apr 2026 12:32:50 -0400 |
| parents | |
| children | a5ee18c79a04 |
line wrap: on
line source
/* * Tiny layer for interfacing with beefweb. Does basically the * bare minimum, and does not expose the whole API. * * Copyright (C) 2026 Paper * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see * <https://www.gnu.org/licenses/>. */ #[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct PlayerInfo { pub name: String, pub title: String, pub version: String, // fuck camel case #[serde(rename = "pluginVersion")] pub plugin_version: String, } #[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct ActiveItemInfo { #[serde(rename = "playlistId")] pub playlist_id: String, #[serde(rename = "playlistIndex")] pub playlist_index: i64, pub index: i64, pub position: f64, pub duration: f64, pub columns: Vec<String>, } #[derive(serde::Serialize, serde::Deserialize, Debug)] pub enum VolumeType { #[serde(rename = "db")] DB, #[serde(rename = "linear")] LINEAR, #[serde(rename = "upDown")] UPDOWN, /* ??? */ } #[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct VolumeInfo { pub r#type: VolumeType, pub min: f64, pub max: f64, pub value: f64, #[serde(rename = "isMuted")] pub muted: bool, } #[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct ApiPermissions { #[serde(rename = "changePlaylists")] pub change_playlists: bool, #[serde(rename = "changeOutput")] pub change_output: bool, #[serde(rename = "changeClientConfig")] pub change_client_config: 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 volume: VolumeInfo, pub permissions: ApiPermissions, } /* {"player": {struct Player}} */ #[derive(serde::Serialize, serde::Deserialize, Debug)] struct PlayerHelper { player: Player, } #[derive(serde::Serialize, serde::Deserialize, Debug)] #[serde(untagged)] enum SetPlayerOption { Integer { id: String, value: i64 }, Boolean { id: String, value: bool }, } #[derive(serde::Serialize, serde::Deserialize, Debug)] #[serde_with::skip_serializing_none] struct SetPlayer { /* dB */ volume: Option<f64>, /* ??? */ #[serde(rename = "relativeVolume")] relative_volume: Option<f64>, #[serde(rename = "isMuted")] muted: Option<bool>, position: Option<f64>, #[serde(rename = "relativePosition")] relative_position: Option<f64>, /* teehee */ options: Option<Vec<SetPlayerOption>>, } #[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct PlaylistItem { pub columns: Vec<String> } #[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct PlaylistItems { pub offset: i64, #[serde(rename = "totalCount")] pub total_count: i64, pub items: Vec<PlaylistItem>, } #[derive(serde::Serialize, serde::Deserialize, Debug)] struct PlaylistItemsHelper { #[serde(rename = "playlistItems")] playlist_items: PlaylistItems, } /* --- NOW, THE ACTUAL BEEFWEB THING */ pub struct Beefweb { client: reqwest::Client, base_url: String, } impl Beefweb { pub fn new(base: &str) -> Beefweb { return Beefweb { client: reqwest::Client::new(), base_url: base.to_string(), }; } pub async fn player(&self) -> Result<Player, anyhow::Error> { return Ok(self.client .get(format!("{}/player", self.base_url)) .send() .await? .json::<PlayerHelper>() .await? .player); } pub async fn volume(&self) -> Result<VolumeInfo, anyhow::Error> { return Ok(self.player().await?.volume); } pub async fn position(&self) -> Result<f64, anyhow::Error> { return Ok(self.player().await?.active_item.position); } /* XXX might be able to use macros for this? idk --paper */ async fn play_pause_stop(&self, x: &str) -> Result<(), anyhow::Error> { self.client .post(format!("{}/player/{}", self.base_url, x)) .send() .await?; return Ok(()); } pub async fn play(&self) -> Result<(), anyhow::Error> { return self.play_pause_stop("play").await; } pub async fn pause(&self) -> Result<(), anyhow::Error> { return self.play_pause_stop("pause").await; } pub async fn stop(&self) -> Result<(), anyhow::Error> { return self.play_pause_stop("stop").await; } pub async fn play_pause(&self) -> Result<(), anyhow::Error> { return self.play_pause_stop("play-pause").await; } pub async fn next(&self) -> Result<(), anyhow::Error> { return self.play_pause_stop("next").await; } pub async fn previous(&self) -> Result<(), anyhow::Error> { return self.play_pause_stop("previous").await; } /* XXX what should this return? */ async fn set_player(&self, p: &SetPlayer) -> Result<(), anyhow::Error> { self.client .post(format!("{}/player", self.base_url)) .json::<SetPlayer>(p) .send() .await?; return Ok(()); } pub async fn set_volume(&self, volume: f64) -> Result<(), anyhow::Error> { return self.set_player(&SetPlayer { volume: Some(volume), relative_volume: None, muted: None, position: None, relative_position: None, options: None, }).await; } pub async fn set_position(&self, position: f64) -> Result<(), anyhow::Error> { return self.set_player(&SetPlayer { volume: None, relative_volume: Some(position), muted: None, position: None, relative_position: None, options: None, }).await; } pub async fn playlist_items(&self, playlist_id: &str, offset: i64, count: i64, columns: Vec<&str>) -> Result<PlaylistItems, anyhow::Error> { return Ok(self.client .get(format!("{}/playlists/{}/items/{}:{}", self.base_url, playlist_id, offset, count)) .query(&[("columns", columns.join(","))]) .send() .await? .json::<PlaylistItemsHelper>() .await? .playlist_items); } pub async fn artwork(&self, playlist_id: &str, index: i64) -> Result<bytes::Bytes, anyhow::Error> { return Ok(self.client .get(format!("{}/artwork/{}/{}", self.base_url, playlist_id, index)) .send() .await? .bytes() .await?); } }
