Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: update/rescan cli commands #101

Merged
merged 2 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- Arrow keys as secondary navigation keybinds alongside hjkl
- Support for basic control with mouse. Check docs for more info.
- Scrolloff option to keep some context the various lists/tables
- Update/rescan CLI commands to refresh MPD's database

### Changed

Expand Down
4 changes: 3 additions & 1 deletion docs/src/content/docs/reference/cli-command-mode.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Usage: rmpc [OPTIONS] [COMMAND]

Commands:
config Prints the default config. Can be used to bootstrap your config file
update Scan MPD's music directory for updates
rescan Scan MPD's music directory for updates. Also rescans unmodified files
theme Prints the default theme. Can be used to bootstrap your theme file
albumart Saves the current album art to a file. Exit codes: * 0: Success * 1: Error * 2: No album art found * 3: No song playing
debuginfo Prints information about optional runtime dependencies
Expand All @@ -49,7 +51,7 @@ Commands:
enableoutput Enable MPD output
disableoutput Disable MPD output
status Prints various information like the playback status
song Prints information about the current song
song Prints info about the current song. If --path specified, prints information about the song at the given path instead. If --path is specified multiple times, prints an array containing all the songs
mount Mounts supported storage to MPD
unmount Unmounts storage with given name
listmounts List currently mounted storages
Expand Down
21 changes: 20 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
config::{cli::Command, Config},
context::AppContext,
mpd::{
commands::volume::Bound,
commands::{volume::Bound, IdleEvent},
mpd_client::{Filter, MpdClient, Tag},
},
utils::macros::status_error,
Expand All @@ -26,6 +26,25 @@ impl Command {
F: FnMut(WorkRequest, &mut C),
{
match self {
ref cmd @ Command::Update { ref path, wait } | ref cmd @ Command::Rescan { ref path, wait } => {
let crate::mpd::commands::Update { job_id } = if matches!(cmd, Command::Update { .. }) {
client.update(path.as_deref())?
} else {
client.rescan(path.as_deref())?
};

if wait {
loop {
client.idle(Some(IdleEvent::Update))?;
let crate::mpd::commands::Status { updating_db, .. } = client.get_status()?;
match updating_db {
Some(current_id) if current_id > job_id => break,
Some(_id) => continue,
None => break,
}
}
}
}
Command::Play { position: None } => client.play()?,
Command::Play { position: Some(pos) } => client.play_pos(pos)?,
Command::Pause => client.pause()?,
Expand Down
16 changes: 16 additions & 0 deletions src/config/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,22 @@ pub struct Args {
pub enum Command {
/// Prints the default config. Can be used to bootstrap your config file.
Config,
/// Scan MPD's music directory for updates.
Update {
/// If supplied, MPD will update only the provided directory/file. If not specified, everything is updated.
path: Option<String>,
/// Rmpc will wait for the update job to finish before returning.
#[arg(short, long, default_value = "false")]
wait: bool,
},
/// Scan MPD's music directory for updates. Also rescans unmodified files.
Rescan {
/// If supplied, MPD will update only the provided directory/file. If not specified, everything is updated.
path: Option<String>,
/// Rmpc will wait for the update job to finish before returning.
#[arg(short, long, default_value = "false")]
wait: bool,
},
/// Prints the default theme. Can be used to bootstrap your theme file.
Theme,
/// Saves the current album art to a file.
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ fn idle_task(mut idle_client: Client<'_>, sender: std::sync::mpsc::Sender<AppEve
let mut error_count = 0;
let sender = sender;
loop {
let events = match idle_client.idle() {
let events = match idle_client.idle(None) {
Ok(val) => val,
Err(err) => {
if error_count > 5 {
Expand Down
3 changes: 2 additions & 1 deletion src/mpd/commands/idle.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::mpd::errors::MpdError;
use crate::mpd::{FromMpd, LineHandled};

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, strum::Display)]
#[strum(serialize_all = "snake_case")]
pub enum IdleEvent {
Player, // the player has been started, stopped or seeked or tags of the currently playing song have changed (e.g. received from stream)
Mixer, // the volume has been changed
Expand Down
2 changes: 2 additions & 0 deletions src/mpd/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod lsinfo;
pub mod outputs;
pub mod playlist_info;
pub mod status;
pub mod update;
pub mod volume;

pub use self::current_song::Song;
Expand All @@ -20,4 +21,5 @@ pub use self::lsinfo::LsInfo;
pub use self::outputs::Output;
pub use self::status::State;
pub use self::status::Status;
pub use self::update::Update;
pub use self::volume::Volume;
4 changes: 2 additions & 2 deletions src/mpd/commands/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ pub struct Update {
}

impl FromMpd for Update {
fn next_internal(&mut self, _key: &str, value: String) -> Result<LineHandled, MpdError> {
match value.as_str() {
fn next_internal(&mut self, key: &str, value: String) -> Result<LineHandled, MpdError> {
match key {
"updating_db" => self.job_id = value.parse()?,
_ => return Ok(LineHandled::No { value }),
};
Expand Down
33 changes: 29 additions & 4 deletions src/mpd/mpd_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use super::{
client::Client,
commands::{
list::MpdList, list_playlist::FileList, outputs::Outputs, status::OnOffOneshot, volume::Bound, IdleEvent,
ListFiles, LsInfo, Mounts, Playlist, Song, Status, Volume,
ListFiles, LsInfo, Mounts, Playlist, Song, Status, Update, Volume,
},
errors::{ErrorCode, MpdError, MpdFailureResponse},
proto_client::ProtoClient,
Expand Down Expand Up @@ -62,7 +62,9 @@ impl ValueChange {
pub trait MpdClient {
fn version(&mut self) -> Version;
fn commands(&mut self) -> MpdResult<MpdList>;
fn idle(&mut self) -> MpdResult<Vec<IdleEvent>>;
fn update(&mut self, path: Option<&str>) -> MpdResult<Update>;
fn rescan(&mut self, path: Option<&str>) -> MpdResult<Update>;
fn idle(&mut self, subsystem: Option<IdleEvent>) -> MpdResult<Vec<IdleEvent>>;
fn noidle(&mut self) -> MpdResult<()>;
fn get_volume(&mut self) -> MpdResult<Volume>;
fn set_volume(&mut self, volume: Volume) -> MpdResult<()>;
Expand Down Expand Up @@ -134,14 +136,37 @@ impl MpdClient for Client<'_> {
self.version
}

fn update(&mut self, path: Option<&str>) -> MpdResult<Update> {
if let Some(path) = path {
self.send(&format!("update {path}"))
.and_then(ProtoClient::read_response)
} else {
self.send("update").and_then(ProtoClient::read_response)
}
}

fn rescan(&mut self, path: Option<&str>) -> MpdResult<Update> {
if let Some(path) = path {
self.send(&format!("rescan {path}"))
.and_then(ProtoClient::read_response)
} else {
self.send("rescan").and_then(ProtoClient::read_response)
}
}

// Lists commands supported by the MPD server
fn commands(&mut self) -> MpdResult<MpdList> {
self.send("commands").and_then(ProtoClient::read_response)
}

// Queries
fn idle(&mut self) -> MpdResult<Vec<IdleEvent>> {
self.send("idle").and_then(ProtoClient::read_response)
fn idle(&mut self, subsystem: Option<IdleEvent>) -> MpdResult<Vec<IdleEvent>> {
if let Some(subsystem) = subsystem {
self.send(&format!("idle {subsystem}"))
.and_then(ProtoClient::read_response)
} else {
self.send("idle").and_then(ProtoClient::read_response)
}
}

fn noidle(&mut self) -> MpdResult<()> {
Expand Down
14 changes: 11 additions & 3 deletions src/tests/fixtures/mpd_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rstest::fixture;
use crate::mpd::{
commands::{
list::MpdList, list_playlist::FileList, status::OnOffOneshot, volume::Bound, IdleEvent, ListFiles, LsInfo,
Playlist, Song, Status, Volume,
Playlist, Song, Status, Update, Volume,
},
errors::MpdError,
mpd_client::{Filter, MpdClient, QueueMoveTarget, SaveMode, SingleOrRange, Tag, ValueChange},
Expand Down Expand Up @@ -91,8 +91,8 @@ impl MpdClient for TestMpdClient {
todo!("Not yet implemented")
}

fn idle(&mut self) -> MpdResult<Vec<IdleEvent>> {
todo!()
fn idle(&mut self, _subsystem: Option<IdleEvent>) -> MpdResult<Vec<IdleEvent>> {
todo!("Not yet implemented")
}

fn noidle(&mut self) -> MpdResult<()> {
Expand Down Expand Up @@ -478,4 +478,12 @@ impl MpdClient for TestMpdClient {
fn search_add(&mut self, _filter: &[Filter<'_, '_>]) -> MpdResult<()> {
todo!("Not yet implemented")
}

fn update(&mut self, _path: Option<&str>) -> MpdResult<Update> {
todo!("Not yet implemented")
}

fn rescan(&mut self, _path: Option<&str>) -> MpdResult<Update> {
todo!("Not yet implemented")
}
}