use bufstream::BufStream;
use crate::convert::*;
use crate::error::{Error, ParseError, ProtoError, Result};
use crate::message::{Channel, Message};
use crate::mount::{Mount, Neighbor};
use crate::output::Output;
use crate::playlist::Playlist;
use crate::plugin::Plugin;
use crate::proto::*;
use crate::search::{Query, Term, Window};
use crate::song::{Id, Song};
use crate::stats::Stats;
use crate::status::{ReplayGain, Status};
use crate::sticker::Sticker;
use crate::version::Version;
use std::collections::HashMap;
use std::convert::From;
use std::io::{BufRead, Lines, Read, Write};
use std::net::{TcpStream, ToSocketAddrs};
#[derive(Debug)]
pub struct Client<S = TcpStream>
where S: Read + Write
{
socket: BufStream<S>,
pub version: Version,
}
impl Default for Client<TcpStream> {
fn default() -> Client<TcpStream> {
Client::<TcpStream>::connect("127.0.0.1:6600").unwrap()
}
}
impl Client<TcpStream> {
pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<Client<TcpStream>> {
TcpStream::connect(addr).map_err(Error::Io).and_then(Client::new)
}
}
impl<S: Read + Write> Client<S> {
pub fn new(socket: S) -> Result<Client<S>> {
let mut socket = BufStream::new(socket);
let mut banner = String::new();
socket.read_line(&mut banner)?;
if !banner.starts_with("OK MPD ") {
return Err(From::from(ProtoError::BadBanner));
}
let version = banner[7..].trim().parse::<Version>()?;
Ok(Client { socket, version })
}
pub fn status(&mut self) -> Result<Status> {
self.run_command("command_list_begin", ())
.and_then(|_| self.run_command("status", ()))
.and_then(|_| self.run_command("replay_gain_status", ()))
.and_then(|_| self.run_command("command_list_end", ()))
.and_then(|_| self.read_struct())
}
pub fn stats(&mut self) -> Result<Stats> {
self.run_command("stats", ()).and_then(|_| self.read_struct())
}
pub fn clearerror(&mut self) -> Result<()> {
self.run_command("clearerror", ()).and_then(|_| self.expect_ok())
}
pub fn volume(&mut self, volume: i8) -> Result<()> {
self.run_command("setvol", volume).and_then(|_| self.expect_ok())
}
pub fn repeat(&mut self, value: bool) -> Result<()> {
self.run_command("repeat", value as u8).and_then(|_| self.expect_ok())
}
pub fn random(&mut self, value: bool) -> Result<()> {
self.run_command("random", value as u8).and_then(|_| self.expect_ok())
}
pub fn single(&mut self, value: bool) -> Result<()> {
self.run_command("single", value as u8).and_then(|_| self.expect_ok())
}
pub fn consume(&mut self, value: bool) -> Result<()> {
self.run_command("consume", value as u8).and_then(|_| self.expect_ok())
}
pub fn crossfade<T: ToSeconds>(&mut self, value: T) -> Result<()> {
self.run_command("crossfade", value.to_seconds()).and_then(|_| self.expect_ok())
}
pub fn mixrampdb(&mut self, value: f32) -> Result<()> {
self.run_command("mixrampdb", value).and_then(|_| self.expect_ok())
}
pub fn mixrampdelay<T: ToSeconds>(&mut self, value: T) -> Result<()> {
self.run_command("mixrampdelay", value.to_seconds()).and_then(|_| self.expect_ok())
}
pub fn replaygain(&mut self, gain: ReplayGain) -> Result<()> {
self.run_command("replay_gain_mode", gain).and_then(|_| self.expect_ok())
}
pub fn play(&mut self) -> Result<()> {
self.run_command("play", ()).and_then(|_| self.expect_ok())
}
pub fn switch<T: ToQueuePlace>(&mut self, place: T) -> Result<()> {
let command = if T::is_id() { "playid" } else { "play" };
self.run_command(command, place.to_place()).and_then(|_| self.expect_ok())
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::should_implement_trait))]
pub fn next(&mut self) -> Result<()> {
self.run_command("next", ()).and_then(|_| self.expect_ok())
}
pub fn prev(&mut self) -> Result<()> {
self.run_command("previous", ()).and_then(|_| self.expect_ok())
}
pub fn stop(&mut self) -> Result<()> {
self.run_command("stop", ()).and_then(|_| self.expect_ok())
}
pub fn toggle_pause(&mut self) -> Result<()> {
self.run_command("pause", ()).and_then(|_| self.expect_ok())
}
pub fn pause(&mut self, value: bool) -> Result<()> {
self.run_command("pause", value as u8).and_then(|_| self.expect_ok())
}
pub fn seek<T: ToSeconds, P: ToQueuePlace>(&mut self, place: P, pos: T) -> Result<()> {
let command = if P::is_id() { "seekid" } else { "seek" };
self.run_command(command, (place.to_place(), pos.to_seconds())).and_then(|_| self.expect_ok())
}
pub fn rewind<T: ToSeconds>(&mut self, pos: T) -> Result<()> {
self.run_command("seekcur", pos.to_seconds()).and_then(|_| self.expect_ok())
}
pub fn songs<T: ToQueueRangeOrPlace>(&mut self, pos: T) -> Result<Vec<Song>> {
let command = if T::is_id() { "playlistid" } else { "playlistinfo" };
self.run_command(command, pos.to_range()).and_then(|_| self.read_structs("file"))
}
pub fn queue(&mut self) -> Result<Vec<Song>> {
self.run_command("playlistinfo", ()).and_then(|_| self.read_structs("file"))
}
pub fn listall(&mut self) -> Result<Vec<Song>> {
self.run_command("listall", ()).and_then(|_| self.read_structs("file"))
}
pub fn currentsong(&mut self) -> Result<Option<Song>> {
self.run_command("currentsong", ())
.and_then(|_| self.read_struct::<Song>())
.map(|s| if s.place.is_none() { None } else { Some(s) })
}
pub fn playlistid(&mut self, id: Id) -> Result<Option<Song>> {
self.run_command("playlistid", id)
.and_then(|_| self.read_struct::<Song>())
.map(|s| if s.place.is_none() { None } else { Some(s) })
}
pub fn clear(&mut self) -> Result<()> {
self.run_command("clear", ()).and_then(|_| self.expect_ok())
}
pub fn changes(&mut self, version: u32) -> Result<Vec<Song>> {
self.run_command("plchanges", version).and_then(|_| self.read_structs("file"))
}
pub fn push<P: ToSongPath>(&mut self, path: P) -> Result<Id> {
self.run_command("addid", path).and_then(|_| self.read_field("Id")).map(Id)
}
pub fn insert<P: ToSongPath>(&mut self, path: P, pos: usize) -> Result<usize> {
self.run_command("addid", (path, pos)).and_then(|_| self.read_field("Id"))
}
pub fn delete<T: ToQueueRangeOrPlace>(&mut self, pos: T) -> Result<()> {
let command = if T::is_id() { "deleteid" } else { "delete" };
self.run_command(command, pos.to_range()).and_then(|_| self.expect_ok())
}
pub fn shift<T: ToQueueRangeOrPlace>(&mut self, from: T, to: usize) -> Result<()> {
let command = if T::is_id() { "moveid" } else { "move" };
self.run_command(command, (from.to_range(), to)).and_then(|_| self.expect_ok())
}
pub fn swap<T: ToQueuePlace>(&mut self, one: T, two: T) -> Result<()> {
let command = if T::is_id() { "swapid" } else { "swap" };
self.run_command(command, (one.to_place(), two.to_place())).and_then(|_| self.expect_ok())
}
pub fn shuffle<T: ToQueueRange>(&mut self, range: T) -> Result<()> {
self.run_command("shuffle", range.to_range()).and_then(|_| self.expect_ok())
}
pub fn priority<T: ToQueueRangeOrPlace>(&mut self, pos: T, prio: u8) -> Result<()> {
let command = if T::is_id() { "prioid" } else { "prio" };
self.run_command(command, (prio, pos.to_range())).and_then(|_| self.expect_ok())
}
pub fn range<T: ToSongId, R: ToSongRange>(&mut self, song: T, range: R) -> Result<()> {
self.run_command("rangeid", (song.to_song_id(), range.to_range())).and_then(|_| self.expect_ok())
}
pub fn tag<T: ToSongId>(&mut self, song: T, tag: &str, value: &str) -> Result<()> {
self.run_command("addtagid", (song.to_song_id(), tag, value)).and_then(|_| self.expect_ok())
}
pub fn untag<T: ToSongId>(&mut self, song: T, tag: &str) -> Result<()> {
self.run_command("cleartagid", (song.to_song_id(), tag)).and_then(|_| self.expect_ok())
}
pub fn ping(&mut self) -> Result<()> {
self.run_command("ping", ()).and_then(|_| self.expect_ok())
}
pub fn close(&mut self) -> Result<()> {
self.run_command("close", ()).and_then(|_| self.expect_ok())
}
pub fn kill(&mut self) -> Result<()> {
self.run_command("kill", ()).and_then(|_| self.expect_ok())
}
pub fn login(&mut self, password: &str) -> Result<()> {
self.run_command("password", password).and_then(|_| self.expect_ok())
}
pub fn playlists(&mut self) -> Result<Vec<Playlist>> {
self.run_command("listplaylists", ()).and_then(|_| self.read_structs("playlist"))
}
pub fn playlist<N: ToPlaylistName>(&mut self, name: N) -> Result<Vec<Song>> {
self.run_command("listplaylistinfo", name.to_name()).and_then(|_| self.read_structs("file"))
}
pub fn load<T: ToQueueRange, N: ToPlaylistName>(&mut self, name: N, range: T) -> Result<()> {
self.run_command("load", (name.to_name(), range.to_range())).and_then(|_| self.expect_ok())
}
pub fn save<N: ToPlaylistName>(&mut self, name: N) -> Result<()> {
self.run_command("save", name.to_name()).and_then(|_| self.expect_ok())
}
pub fn pl_rename<N: ToPlaylistName>(&mut self, name: N, newname: &str) -> Result<()> {
self.run_command("rename", (name.to_name(), newname)).and_then(|_| self.expect_ok())
}
pub fn pl_clear<N: ToPlaylistName>(&mut self, name: N) -> Result<()> {
self.run_command("playlistclear", name.to_name()).and_then(|_| self.expect_ok())
}
pub fn pl_remove<N: ToPlaylistName>(&mut self, name: N) -> Result<()> {
self.run_command("rm", name.to_name()).and_then(|_| self.expect_ok())
}
pub fn pl_push<N: ToPlaylistName, P: ToSongPath>(&mut self, name: N, path: P) -> Result<()> {
self.run_command("playlistadd", (name.to_name(), path)).and_then(|_| self.expect_ok())
}
pub fn pl_delete<N: ToPlaylistName>(&mut self, name: N, pos: u32) -> Result<()> {
self.run_command("playlistdelete", (name.to_name(), pos)).and_then(|_| self.expect_ok())
}
pub fn pl_shift<N: ToPlaylistName>(&mut self, name: N, from: u32, to: u32) -> Result<()> {
self.run_command("playlistmove", (name.to_name(), from, to)).and_then(|_| self.expect_ok())
}
pub fn rescan(&mut self) -> Result<u32> {
self.run_command("rescan", ()).and_then(|_| self.read_field("updating_db"))
}
pub fn update(&mut self) -> Result<u32> {
self.run_command("update", ()).and_then(|_| self.read_field("updating_db"))
}
pub fn listfiles(&mut self, song_path: &str) -> Result<Vec<(String, String)>> {
self.run_command("listfiles", song_path).and_then(|_| self.read_pairs().collect())
}
pub fn find<W>(&mut self, query: &Query, window: W) -> Result<Vec<Song>>
where W: Into<Window> {
self.find_generic("find", query, window.into())
}
pub fn albumart<P: ToSongPath>(&mut self, path: &P) -> Result<Vec<u8>> {
let mut buf = vec![];
loop {
self.run_command("albumart", (path, &*format!("{}", buf.len())))?;
let (_, size) = self.read_pair()?;
let (_, bytes) = self.read_pair()?;
let mut chunk = self.read_bytes(bytes.parse()?)?;
buf.append(&mut chunk);
let _ = self.read_line()?;
let result = self.read_line()?;
if result != "OK" {
return Err(ProtoError::NotOk)?;
}
if size.parse::<usize>()? == buf.len() {
break;
}
}
Ok(buf)
}
pub fn search<W>(&mut self, query: &Query, window: W) -> Result<Vec<Song>>
where W: Into<Window> {
self.find_generic("search", query, window.into())
}
fn find_generic(&mut self, cmd: &str, query: &Query, window: Window) -> Result<Vec<Song>> {
self.run_command(cmd, (query, window)).and_then(|_| self.read_structs("file"))
}
pub fn list(&mut self, term: &Term, query: &Query) -> Result<Vec<String>> {
self.run_command("list", (term, query)).and_then(|_| self.read_pairs().map(|p| p.map(|p| p.1)).collect())
}
pub fn findadd(&mut self, query: &Query) -> Result<()> {
self.run_command("findadd", query).and_then(|_| self.expect_ok())
}
pub fn lsinfo<P: ToSongPath>(&mut self, path: P) -> Result<Vec<Song>> {
self.run_command("lsinfo", path).and_then(|_| self.read_structs("file"))
}
pub fn readcomments<P: ToSongPath>(&mut self, path: P) -> Result<impl Iterator<Item = Result<(String, String)>> + '_> {
self.run_command("readcomments", path)?;
Ok(self.read_pairs())
}
pub fn outputs(&mut self) -> Result<Vec<Output>> {
self.run_command("outputs", ()).and_then(|_| self.read_structs("outputid"))
}
pub fn output<T: ToOutputId>(&mut self, id: T, state: bool) -> Result<()> {
if state {
self.out_enable(id)
} else {
self.out_disable(id)
}
}
pub fn out_disable<T: ToOutputId>(&mut self, id: T) -> Result<()> {
self.run_command("disableoutput", id.to_output_id()).and_then(|_| self.expect_ok())
}
pub fn out_enable<T: ToOutputId>(&mut self, id: T) -> Result<()> {
self.run_command("enableoutput", id.to_output_id()).and_then(|_| self.expect_ok())
}
pub fn out_toggle<T: ToOutputId>(&mut self, id: T) -> Result<()> {
self.run_command("toggleoutput", id.to_output_id()).and_then(|_| self.expect_ok())
}
pub fn music_directory(&mut self) -> Result<String> {
self.run_command("config", ()).and_then(|_| self.read_field("music_directory"))
}
pub fn commands(&mut self) -> Result<Vec<String>> {
self.run_command("commands", ()).and_then(|_| self.read_list("command"))
}
pub fn notcommands(&mut self) -> Result<Vec<String>> {
self.run_command("notcommands", ()).and_then(|_| self.read_list("command"))
}
pub fn urlhandlers(&mut self) -> Result<Vec<String>> {
self.run_command("urlhandlers", ()).and_then(|_| self.read_list("handler"))
}
pub fn tagtypes(&mut self) -> Result<Vec<String>> {
self.run_command("tagtypes", ()).and_then(|_| self.read_list("tagtype"))
}
pub fn decoders(&mut self) -> Result<Vec<Plugin>> {
self.run_command("decoders", ()).and_then(|_| self.read_struct())
}
pub fn channels(&mut self) -> Result<Vec<Channel>> {
self.run_command("channels", ())
.and_then(|_| self.read_list("channel"))
.map(|v| v.into_iter().map(|b| unsafe { Channel::new_unchecked(b) }).collect())
}
pub fn readmessages(&mut self) -> Result<Vec<Message>> {
self.run_command("readmessages", ()).and_then(|_| self.read_structs("channel"))
}
pub fn sendmessage(&mut self, channel: Channel, message: &str) -> Result<()> {
self.run_command("sendmessage", (channel, message)).and_then(|_| self.expect_ok())
}
pub fn subscribe(&mut self, channel: Channel) -> Result<()> {
self.run_command("subscribe", channel).and_then(|_| self.expect_ok())
}
pub fn unsubscribe(&mut self, channel: Channel) -> Result<()> {
self.run_command("unsubscribe", channel).and_then(|_| self.expect_ok())
}
pub fn mounts(&mut self) -> Result<Vec<Mount>> {
self.run_command("listmounts", ()).and_then(|_| self.read_structs("mount"))
}
pub fn neighbors(&mut self) -> Result<Vec<Neighbor>> {
self.run_command("listneighbors", ()).and_then(|_| self.read_structs("neighbor"))
}
pub fn mount(&mut self, path: &str, uri: &str) -> Result<()> {
self.run_command("mount", (path, uri)).and_then(|_| self.expect_ok())
}
pub fn unmount(&mut self, path: &str) -> Result<()> {
self.run_command("unmount", path).and_then(|_| self.expect_ok())
}
pub fn sticker(&mut self, typ: &str, uri: &str, name: &str) -> Result<String> {
self.run_command("sticker get", (typ, uri, name))
.and_then(|_| self.read_field::<Sticker>("sticker"))
.map(|s| s.value)
}
pub fn set_sticker(&mut self, typ: &str, uri: &str, name: &str, value: &str) -> Result<()> {
self.run_command("sticker set", (typ, uri, name, value)).and_then(|_| self.expect_ok())
}
pub fn delete_sticker(&mut self, typ: &str, uri: &str, name: &str) -> Result<()> {
self.run_command("sticker delete", (typ, uri, name)).and_then(|_| self.expect_ok())
}
pub fn clear_stickers(&mut self, typ: &str, uri: &str) -> Result<()> {
self.run_command("sticker delete", (typ, uri)).and_then(|_| self.expect_ok())
}
pub fn stickers(&mut self, typ: &str, uri: &str) -> Result<Vec<String>> {
self.run_command("sticker list", (typ, uri))
.and_then(|_| self.read_list("sticker"))
.map(|v| v.into_iter().map(|b| b.split_once('=').map(|x| x.1.to_owned()).unwrap()).collect())
}
pub fn stickers_map(&mut self, typ: &str, uri: &str) -> Result<HashMap<String, String>> {
self.run_command("sticker list", (typ, uri)).and_then(|_| self.read_list("sticker")).map(|v| {
v.into_iter()
.map(|b| {
let mut iter = b.splitn(2, '=');
(iter.next().unwrap().to_owned(), iter.next().unwrap().to_owned())
})
.collect()
})
}
pub fn find_sticker(&mut self, typ: &str, uri: &str, name: &str) -> Result<Vec<(String, String)>> {
self.run_command("sticker find", (typ, uri, name)).and_then(|_| {
self.read_pairs()
.split("file")
.map(|rmap| {
rmap.map(|map| {
(
map.iter().find_map(|(k, v)| if k == "file" { Some(v.to_owned()) } else { None }).unwrap(),
map.iter()
.find_map(|(k, v)| if k == "sticker" { Some(v.to_owned()) } else { None })
.and_then(|s| s.split_once('=').map(|x| x.1.to_owned()))
.unwrap(),
)
})
})
.collect()
})
}
pub fn find_sticker_eq(&mut self, typ: &str, uri: &str, name: &str, value: &str) -> Result<Vec<String>> {
self.run_command("sticker find", (typ, uri, name, "=", value)).and_then(|_| self.read_list("file"))
}
}
impl<S: Read + Write> Proto for Client<S> {
type Stream = S;
fn read_bytes(&mut self, bytes: usize) -> Result<Vec<u8>> {
let mut buf = Vec::with_capacity(bytes);
let mut chunk = (&mut self.socket).take(bytes as u64);
chunk.read_to_end(&mut buf)?;
Ok(buf)
}
fn read_line(&mut self) -> Result<String> {
let mut buf = Vec::new();
self.socket.read_until(b'\n', &mut buf)?;
if buf.ends_with(&[b'\n']) {
buf.pop();
}
let str = String::from_utf8(buf)
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "stream did not contain valid UTF-8"))?;
Ok(str)
}
fn read_pairs(&mut self) -> Pairs<Lines<&mut BufStream<S>>> {
Pairs((&mut self.socket).lines())
}
fn read_pair(&mut self) -> Result<(String, String)> {
let line = self.read_line()?;
let mut split = line.split(": ");
let key = split.next().ok_or(ParseError::BadPair)?;
let val = split.next().ok_or(ParseError::BadPair)?;
Ok((key.to_string(), val.to_string()))
}
fn run_command<I>(&mut self, command: &str, arguments: I) -> Result<()>
where I: ToArguments {
self.socket
.write_all(command.as_bytes())
.and_then(|_| arguments.to_arguments(&mut |arg| write!(self.socket, " {}", Quoted(arg))))
.and_then(|_| self.socket.write(&[0x0a]))
.and_then(|_| self.socket.flush())
.map_err(From::from)
}
}