From c12b4787ca1d99d5bcb2172a625c31c0ac306daa Mon Sep 17 00:00:00 2001 From: zack Date: Sun, 27 Jul 2025 15:55:35 -0400 Subject: [PATCH] changes --- src/actions.rs | 73 +++++++++++++++++ src/main.rs | 207 ++++++------------------------------------------- src/structs.rs | 161 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+), 183 deletions(-) create mode 100644 src/actions.rs create mode 100644 src/structs.rs diff --git a/src/actions.rs b/src/actions.rs new file mode 100644 index 0000000..6c435ae --- /dev/null +++ b/src/actions.rs @@ -0,0 +1,73 @@ +use serde::Serialize; + +use crate::structs::{IsPlayingResponse, NowPlayingResponse}; + +pub fn get_now_playing() -> Result { + const URL: &str = "http://localhost:10767/api/v1/playback/now-playing"; + + // This call blocks until the request is complete. + let response = reqwest::blocking::get(URL)?; + + let data: NowPlayingResponse = response.json()?; + + Ok(data) +} + +pub fn toggle_playpause() -> Result { + const URL: &str = "http://localhost:10767/api/v1/playback/playpause"; + + let client = reqwest::blocking::Client::new(); + + // This call blocks until the request is complete. + let response = client.post(URL).send()?; + + let data = response.json()?; + + Ok(data) +} + +pub fn skip() -> Result { + const URL: &str = "http://localhost:10767/api/v1/playback/next"; + + let client = reqwest::blocking::Client::new(); + + // This call blocks until the request is complete. + let response = client.post(URL).send()?; + + let data = response.json()?; + + Ok(data) +} + +pub fn like() -> Result { + const URL: &str = "http://localhost:10767/api/v1/playback/set-rating"; + + let client = reqwest::blocking::Client::new(); + + #[derive(Serialize)] + struct RatingBody { + rating: i32, + } + + // This call blocks until the request is complete. + let response = client + .post(URL) + .header("Content-Type", "application/json") + .body(serde_json::to_string(&RatingBody { rating: 1 }).unwrap()) + .send()?; + + let data = response.json()?; + + Ok(data) +} + +pub fn get_is_playing() -> Result { + const URL: &str = "http://localhost:10767/api/v1/playback/is-playing"; + + // This call blocks until the request is complete. + let response = reqwest::blocking::get(URL)?; + + let data: IsPlayingResponse = response.json()?; + + Ok(data) +} diff --git a/src/main.rs b/src/main.rs index 583a49b..2664687 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,168 +6,15 @@ use std::{ use clap::{Parser, Subcommand}; use crossbeam_channel::unbounded; use rust_socketio::{ClientBuilder, Payload}; -use serde::{Deserialize, Serialize}; use tracing::{error, info}; -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct PlaybackUpdate { - pub data: Data, - #[serde(rename = "type")] - pub type_field: String, -} +use crate::{ + actions::{get_is_playing, get_now_playing, like, skip, toggle_playpause}, + structs::{PlaybackUpdate, StatusUpdate}, +}; -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct Data { - #[serde(rename = "currentPlaybackDuration")] - pub current_playback_duration: Option, - #[serde(rename = "currentPlaybackTime")] - pub current_playback_time: Option, - #[serde(rename = "currentPlaybackTimeRemaining")] - pub current_playback_time_remaining: Option, - #[serde(rename = "isPlaying")] - pub is_playing: Option, - #[serde(rename = "albumName")] - pub album_name: Option, - #[serde(rename = "artistName")] - pub artist_name: Option, - pub artwork: Option, - #[serde(rename = "audioLocale")] - pub audio_locale: Option, - #[serde(rename = "audioTraits")] - pub audio_traits: Option>, - #[serde(rename = "composerName")] - pub composer_name: Option, - #[serde(rename = "discNumber")] - pub disc_number: Option, - #[serde(rename = "durationInMillis")] - pub duration_in_millis: Option, - #[serde(rename = "genreNames")] - #[serde(default)] - pub genre_names: Vec, - #[serde(rename = "hasLyrics")] - pub has_lyrics: Option, - #[serde(rename = "hasTimeSyncedLyrics")] - pub has_time_synced_lyrics: Option, - #[serde(rename = "isAppleDigitalMaster")] - pub is_apple_digital_master: Option, - #[serde(rename = "isMasteredForItunes")] - pub is_mastered_for_itunes: Option, - #[serde(rename = "isVocalAttenuationAllowed")] - pub is_vocal_attenuation_allowed: Option, - pub isrc: Option, - pub name: Option, - #[serde(rename = "playParams")] - pub play_params: Option, - #[serde(default)] - pub previews: Vec, - #[serde(rename = "releaseDate")] - pub release_date: Option, - #[serde(rename = "remainingTime")] - pub remaining_time: Option, - #[serde(rename = "trackNumber")] - pub track_number: Option, - pub url: Option, - pub attributes: Option, - pub state: Option, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct Artwork { - pub height: i64, - pub url: String, - pub width: i64, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct PlayParams { - pub id: String, - pub kind: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct Preview { - pub url: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct Attributes { - #[serde(rename = "albumName")] - pub album_name: String, - #[serde(rename = "artistName")] - pub artist_name: String, - pub artwork: Artwork2, - #[serde(rename = "audioLocale")] - pub audio_locale: String, - #[serde(rename = "audioTraits")] - pub audio_traits: Vec, - #[serde(rename = "composerName")] - pub composer_name: String, - #[serde(rename = "currentPlaybackTime")] - pub current_playback_time: f64, - #[serde(rename = "discNumber")] - pub disc_number: i64, - #[serde(rename = "durationInMillis")] - pub duration_in_millis: i64, - #[serde(rename = "genreNames")] - pub genre_names: Vec, - #[serde(rename = "hasLyrics")] - pub has_lyrics: bool, - #[serde(rename = "hasTimeSyncedLyrics")] - pub has_time_synced_lyrics: bool, - #[serde(rename = "isAppleDigitalMaster")] - pub is_apple_digital_master: bool, - #[serde(rename = "isMasteredForItunes")] - pub is_mastered_for_itunes: bool, - #[serde(rename = "isVocalAttenuationAllowed")] - pub is_vocal_attenuation_allowed: bool, - pub isrc: String, - pub name: String, - #[serde(rename = "playParams")] - pub play_params: PlayParams2, - pub previews: Vec, - #[serde(rename = "releaseDate")] - pub release_date: String, - #[serde(rename = "remainingTime")] - pub remaining_time: f64, - #[serde(rename = "trackNumber")] - pub track_number: i64, - pub url: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct Artwork2 { - pub height: i64, - pub url: String, - pub width: i64, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct PlayParams2 { - pub id: String, - pub kind: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct Preview2 { - pub url: String, -} - -#[derive(Debug, Deserialize)] -struct NowPlayingResponse { - info: Data, -} - -#[derive(Debug, Deserialize)] -struct IsPlayingResponse { - is_playing: bool, -} - -#[derive(Clone, Serialize, Deserialize)] -struct StatusUpdate { - text: String, - tooltip: String, - class: Vec, -} +mod actions; +mod structs; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] @@ -178,6 +25,13 @@ struct Cli { #[derive(Subcommand, Clone, Debug)] enum Commands { + /// Like the current song. + Like, + /// Skip the current song. + Skip, + /// Toggle play/pause. + PlayPause, + /// Monitor song changes from Cider Monitor, } @@ -261,35 +115,22 @@ fn setup_ctrlc_handler() -> crossbeam_channel::Receiver<()> { receiver } -fn get_now_playing() -> Result { - const URL: &str = "http://localhost:10767/api/v1/playback/now-playing"; - - // This call blocks until the request is complete. - let response = reqwest::blocking::get(URL)?; - - let data: NowPlayingResponse = response.json()?; - - Ok(data) -} - -fn get_is_playing() -> Result { - const URL: &str = "http://localhost:10767/api/v1/playback/is-playing"; - - // This call blocks until the request is complete. - let response = reqwest::blocking::get(URL)?; - - let data: IsPlayingResponse = response.json()?; - - Ok(data) -} - fn main() -> color_eyre::Result<()> { - color_eyre::install()?; - tracing_subscriber::fmt().pretty().init(); + // color_eyre::install()?; + // tracing_subscriber::fmt().pretty().init(); let cli = Cli::parse(); match &cli.command { + Some(Commands::Like) => { + like()?; + } + Some(Commands::Skip) => { + skip()?; + } + Some(Commands::PlayPause) => { + toggle_playpause()?; + } Some(Commands::Monitor) => { let ctrlc_receiver = setup_ctrlc_handler(); diff --git a/src/structs.rs b/src/structs.rs new file mode 100644 index 0000000..4c0bcb8 --- /dev/null +++ b/src/structs.rs @@ -0,0 +1,161 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PlaybackUpdate { + pub data: Data, + #[serde(rename = "type")] + pub type_field: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Data { + #[serde(rename = "currentPlaybackDuration")] + pub current_playback_duration: Option, + #[serde(rename = "currentPlaybackTime")] + pub current_playback_time: Option, + #[serde(rename = "currentPlaybackTimeRemaining")] + pub current_playback_time_remaining: Option, + #[serde(rename = "isPlaying")] + pub is_playing: Option, + #[serde(rename = "albumName")] + pub album_name: Option, + #[serde(rename = "artistName")] + pub artist_name: Option, + pub artwork: Option, + #[serde(rename = "audioLocale")] + pub audio_locale: Option, + #[serde(rename = "audioTraits")] + pub audio_traits: Option>, + #[serde(rename = "composerName")] + pub composer_name: Option, + #[serde(rename = "discNumber")] + pub disc_number: Option, + #[serde(rename = "durationInMillis")] + pub duration_in_millis: Option, + #[serde(rename = "genreNames")] + #[serde(default)] + pub genre_names: Vec, + #[serde(rename = "hasLyrics")] + pub has_lyrics: Option, + #[serde(rename = "hasTimeSyncedLyrics")] + pub has_time_synced_lyrics: Option, + #[serde(rename = "isAppleDigitalMaster")] + pub is_apple_digital_master: Option, + #[serde(rename = "isMasteredForItunes")] + pub is_mastered_for_itunes: Option, + #[serde(rename = "isVocalAttenuationAllowed")] + pub is_vocal_attenuation_allowed: Option, + pub isrc: Option, + pub name: Option, + #[serde(rename = "playParams")] + pub play_params: Option, + #[serde(default)] + pub previews: Vec, + #[serde(rename = "releaseDate")] + pub release_date: Option, + #[serde(rename = "remainingTime")] + pub remaining_time: Option, + #[serde(rename = "trackNumber")] + pub track_number: Option, + pub url: Option, + pub attributes: Option, + pub state: Option, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Artwork { + pub height: i64, + pub url: String, + pub width: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PlayParams { + pub id: String, + pub kind: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Preview { + pub url: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Attributes { + #[serde(rename = "albumName")] + pub album_name: String, + #[serde(rename = "artistName")] + pub artist_name: String, + pub artwork: Artwork2, + #[serde(rename = "audioLocale")] + pub audio_locale: String, + #[serde(rename = "audioTraits")] + pub audio_traits: Vec, + #[serde(rename = "composerName")] + pub composer_name: String, + #[serde(rename = "currentPlaybackTime")] + pub current_playback_time: f64, + #[serde(rename = "discNumber")] + pub disc_number: i64, + #[serde(rename = "durationInMillis")] + pub duration_in_millis: i64, + #[serde(rename = "genreNames")] + pub genre_names: Vec, + #[serde(rename = "hasLyrics")] + pub has_lyrics: bool, + #[serde(rename = "hasTimeSyncedLyrics")] + pub has_time_synced_lyrics: bool, + #[serde(rename = "isAppleDigitalMaster")] + pub is_apple_digital_master: bool, + #[serde(rename = "isMasteredForItunes")] + pub is_mastered_for_itunes: bool, + #[serde(rename = "isVocalAttenuationAllowed")] + pub is_vocal_attenuation_allowed: bool, + pub isrc: String, + pub name: String, + #[serde(rename = "playParams")] + pub play_params: PlayParams2, + pub previews: Vec, + #[serde(rename = "releaseDate")] + pub release_date: String, + #[serde(rename = "remainingTime")] + pub remaining_time: f64, + #[serde(rename = "trackNumber")] + pub track_number: i64, + pub url: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Artwork2 { + pub height: i64, + pub url: String, + pub width: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PlayParams2 { + pub id: String, + pub kind: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Preview2 { + pub url: String, +} + +#[derive(Debug, Deserialize)] +pub struct NowPlayingResponse { + pub info: Data, +} + +#[derive(Debug, Deserialize)] +pub struct IsPlayingResponse { + pub is_playing: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct StatusUpdate { + pub text: String, + pub tooltip: String, + pub class: Vec, +}