polybar-dwm/src/modules/mpd.cpp

355 lines
12 KiB
C++
Raw Normal View History

2017-01-13 12:02:51 +00:00
#include <csignal>
2016-11-02 19:22:45 +00:00
#include "drawtypes/iconset.hpp"
#include "drawtypes/label.hpp"
#include "drawtypes/progressbar.hpp"
2017-01-13 12:02:51 +00:00
#include "modules/mpd.hpp"
2016-12-09 08:02:47 +00:00
#include "utils/factory.hpp"
2016-11-20 22:04:31 +00:00
#include "modules/meta/base.inl"
2016-11-19 05:22:44 +00:00
POLYBAR_NS
2016-11-02 19:22:45 +00:00
namespace modules {
2016-11-20 22:04:31 +00:00
template class module<mpd_module>;
mpd_module::mpd_module(const bar_settings& bar, string name_) : event_module<mpd_module>(bar, move(name_)) {
2016-12-23 14:54:06 +00:00
m_host = m_conf.get(name(), "host", m_host);
m_port = m_conf.get(name(), "port", m_port);
m_pass = m_conf.get(name(), "password", m_pass);
m_synctime = m_conf.get(name(), "interval", m_synctime);
2016-11-02 19:22:45 +00:00
// Add formats and elements {{{
m_formatter->add(FORMAT_ONLINE, TAG_LABEL_SONG,
2016-11-25 07:42:31 +00:00
{TAG_BAR_PROGRESS, TAG_TOGGLE, TAG_TOGGLE_STOP, TAG_LABEL_SONG, TAG_LABEL_TIME, TAG_ICON_RANDOM,
TAG_ICON_REPEAT, TAG_ICON_REPEAT_ONE, TAG_ICON_PREV, TAG_ICON_STOP, TAG_ICON_PLAY, TAG_ICON_PAUSE,
TAG_ICON_NEXT, TAG_ICON_SEEKB, TAG_ICON_SEEKF});
2016-11-02 19:22:45 +00:00
m_formatter->add(FORMAT_OFFLINE, "", {TAG_LABEL_OFFLINE});
m_icons = factory_util::shared<iconset>();
2016-11-02 19:22:45 +00:00
2016-11-25 12:55:15 +00:00
if (m_formatter->has(TAG_ICON_PLAY) || m_formatter->has(TAG_TOGGLE) || m_formatter->has(TAG_TOGGLE_STOP)) {
2016-11-02 19:22:45 +00:00
m_icons->add("play", load_icon(m_conf, name(), TAG_ICON_PLAY));
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_ICON_PAUSE) || m_formatter->has(TAG_TOGGLE)) {
2016-11-02 19:22:45 +00:00
m_icons->add("pause", load_icon(m_conf, name(), TAG_ICON_PAUSE));
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_ICON_STOP) || m_formatter->has(TAG_TOGGLE_STOP)) {
2016-11-02 19:22:45 +00:00
m_icons->add("stop", load_icon(m_conf, name(), TAG_ICON_STOP));
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_ICON_PREV)) {
2016-11-02 19:22:45 +00:00
m_icons->add("prev", load_icon(m_conf, name(), TAG_ICON_PREV));
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_ICON_NEXT)) {
2016-11-02 19:22:45 +00:00
m_icons->add("next", load_icon(m_conf, name(), TAG_ICON_NEXT));
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_ICON_SEEKB)) {
2016-11-02 19:22:45 +00:00
m_icons->add("seekb", load_icon(m_conf, name(), TAG_ICON_SEEKB));
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_ICON_SEEKF)) {
2016-11-02 19:22:45 +00:00
m_icons->add("seekf", load_icon(m_conf, name(), TAG_ICON_SEEKF));
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_ICON_RANDOM)) {
2016-11-02 19:22:45 +00:00
m_icons->add("random", load_icon(m_conf, name(), TAG_ICON_RANDOM));
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_ICON_REPEAT)) {
2016-11-02 19:22:45 +00:00
m_icons->add("repeat", load_icon(m_conf, name(), TAG_ICON_REPEAT));
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_ICON_REPEAT_ONE)) {
2016-11-02 19:22:45 +00:00
m_icons->add("repeat_one", load_icon(m_conf, name(), TAG_ICON_REPEAT_ONE));
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
2016-11-25 12:55:15 +00:00
if (m_formatter->has(TAG_LABEL_SONG)) {
2016-11-02 19:22:45 +00:00
m_label_song = load_optional_label(m_conf, name(), TAG_LABEL_SONG, "%artist% - %title%");
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_LABEL_TIME)) {
2016-11-02 19:22:45 +00:00
m_label_time = load_optional_label(m_conf, name(), TAG_LABEL_TIME, "%elapsed% / %total%");
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
if (m_formatter->has(TAG_ICON_RANDOM) || m_formatter->has(TAG_ICON_REPEAT) ||
m_formatter->has(TAG_ICON_REPEAT_ONE)) {
m_toggle_on_color = m_conf.get(name(), "toggle-on-foreground", ""s);
m_toggle_off_color = m_conf.get(name(), "toggle-off-foreground", ""s);
2016-11-02 19:22:45 +00:00
}
2016-11-25 12:55:15 +00:00
if (m_formatter->has(TAG_LABEL_OFFLINE, FORMAT_OFFLINE)) {
2016-11-02 19:22:45 +00:00
m_label_offline = load_label(m_conf, name(), TAG_LABEL_OFFLINE);
2016-11-25 12:55:15 +00:00
}
if (m_formatter->has(TAG_BAR_PROGRESS)) {
2016-11-02 19:22:45 +00:00
m_bar_progress = load_progressbar(m_bar, m_conf, name(), TAG_BAR_PROGRESS);
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
// }}}
m_lastsync = chrono::system_clock::now();
try {
2016-12-09 08:02:47 +00:00
m_mpd = factory_util::unique<mpdconnection>(m_log, m_host, m_port, m_pass);
2016-11-02 19:22:45 +00:00
m_mpd->connect();
m_status = m_mpd->get_status();
} catch (const mpd_exception& err) {
m_log.err("%s: %s", name(), err.what());
m_mpd.reset();
}
}
void mpd_module::teardown() {
m_mpd.reset();
}
inline bool mpd_module::connected() const {
return m_mpd && m_mpd->connected();
}
void mpd_module::idle() {
2016-11-25 12:55:15 +00:00
if (connected()) {
2017-01-13 12:02:51 +00:00
m_quick_attempts = 0;
2016-11-02 19:22:45 +00:00
sleep(80ms);
2016-11-25 12:55:15 +00:00
} else {
2017-01-13 12:02:51 +00:00
sleep(m_quick_attempts++ < 5 ? 0.5s : 2s);
2016-11-02 19:22:45 +00:00
}
}
bool mpd_module::has_event() {
bool def = false;
if (!connected() && m_statebroadcasted == mpd::connection_state::CONNECTED) {
def = true;
} else if (connected() && m_statebroadcasted == mpd::connection_state::DISCONNECTED) {
def = true;
}
try {
2016-11-25 12:55:15 +00:00
if (!m_mpd) {
2016-12-09 08:02:47 +00:00
m_mpd = factory_util::unique<mpdconnection>(m_log, m_host, m_port, m_pass);
2016-11-25 12:55:15 +00:00
}
if (!connected()) {
2016-11-02 19:22:45 +00:00
m_mpd->connect();
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
} catch (const mpd_exception& err) {
m_log.trace("%s: %s", name(), err.what());
m_mpd.reset();
return def;
}
2016-11-25 12:55:15 +00:00
if (!connected()) {
2016-11-02 19:22:45 +00:00
return def;
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
2016-11-25 12:55:15 +00:00
if (!m_status) {
2016-11-02 19:22:45 +00:00
m_status = m_mpd->get_status_safe();
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
try {
m_mpd->idle();
int idle_flags = 0;
if ((idle_flags = m_mpd->noidle()) != 0) {
m_status->update(idle_flags, m_mpd.get());
return true;
}
if (m_status->match_state(mpdstate::PLAYING)) {
m_status->update_timer();
}
} catch (const mpd_exception& err) {
2017-01-13 12:02:51 +00:00
m_log.err("%s: %s", name(), err.what());
2016-11-02 19:22:45 +00:00
m_mpd.reset();
return def;
}
if ((m_label_time || m_bar_progress) && m_status->match_state(mpdstate::PLAYING)) {
auto now = chrono::system_clock::now();
auto diff = now - m_lastsync;
if (chrono::duration_cast<chrono::milliseconds>(diff).count() > m_synctime * 1000) {
m_lastsync = now;
return true;
}
}
return def;
}
bool mpd_module::update() {
2016-11-25 12:55:15 +00:00
if (connected()) {
2016-11-02 19:22:45 +00:00
m_statebroadcasted = mpd::connection_state::CONNECTED;
2016-11-25 12:55:15 +00:00
} else if (!connected() && m_statebroadcasted != mpd::connection_state::DISCONNECTED) {
2016-11-02 19:22:45 +00:00
m_statebroadcasted = mpd::connection_state::DISCONNECTED;
2016-11-25 12:55:15 +00:00
} else if (!connected()) {
2016-11-02 19:22:45 +00:00
return false;
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
if (!m_status) {
if (connected() && (m_status = m_mpd->get_status_safe())) {
return false;
}
}
string artist;
string album;
string title;
2016-12-04 18:33:04 +00:00
string date;
2016-11-02 19:22:45 +00:00
string elapsed_str;
string total_str;
try {
if (m_status) {
elapsed_str = m_status->get_formatted_elapsed();
total_str = m_status->get_formatted_total();
}
if (m_mpd) {
auto song = m_mpd->get_song();
if (song && song.get()) {
artist = song->get_artist();
album = song->get_album();
title = song->get_title();
2016-12-04 18:33:04 +00:00
date = song->get_date();
2016-11-02 19:22:45 +00:00
}
}
} catch (const mpd_exception& err) {
2017-01-13 12:02:51 +00:00
m_log.err("%s: %s", name(), err.what());
2016-11-02 19:22:45 +00:00
m_mpd.reset();
}
if (m_label_song) {
m_label_song->reset_tokens();
m_label_song->replace_token("%artist%", !artist.empty() ? artist : "untitled artist");
m_label_song->replace_token("%album%", !album.empty() ? album : "untitled album");
m_label_song->replace_token("%title%", !title.empty() ? title : "untitled track");
2016-12-04 18:33:04 +00:00
m_label_song->replace_token("%date%", !date.empty() ? date : "unknown date");
2016-11-02 19:22:45 +00:00
}
if (m_label_time) {
m_label_time->reset_tokens();
m_label_time->replace_token("%elapsed%", elapsed_str);
m_label_time->replace_token("%total%", total_str);
}
2016-11-25 12:55:15 +00:00
if (m_icons->has("random")) {
m_icons->get("random")->m_foreground = m_status && m_status->random() ? m_toggle_on_color : m_toggle_off_color;
2016-11-25 12:55:15 +00:00
}
if (m_icons->has("repeat")) {
m_icons->get("repeat")->m_foreground = m_status && m_status->repeat() ? m_toggle_on_color : m_toggle_off_color;
2016-11-25 12:55:15 +00:00
}
if (m_icons->has("repeat_one")) {
2016-11-02 19:22:45 +00:00
m_icons->get("repeat_one")->m_foreground =
m_status && m_status->single() ? m_toggle_on_color : m_toggle_off_color;
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
return true;
}
string mpd_module::get_format() const {
return connected() ? FORMAT_ONLINE : FORMAT_OFFLINE;
}
string mpd_module::get_output() {
if (m_status && m_status->get_queuelen() == 0) {
m_log.info("%s: Hiding module since queue is empty", name());
return "";
} else {
return event_module::get_output();
}
}
2016-11-25 12:55:15 +00:00
bool mpd_module::build(builder* builder, const string& tag) const {
bool is_playing = m_status && m_status->match_state(mpdstate::PLAYING);
bool is_paused = m_status && m_status->match_state(mpdstate::PAUSED);
bool is_stopped = m_status && m_status->match_state(mpdstate::STOPPED);
2016-11-02 19:22:45 +00:00
2016-11-25 12:55:15 +00:00
if (tag == TAG_LABEL_SONG && !is_stopped) {
2016-11-02 19:22:45 +00:00
builder->node(m_label_song);
2016-11-25 12:55:15 +00:00
} else if (tag == TAG_LABEL_TIME && !is_stopped) {
2016-11-02 19:22:45 +00:00
builder->node(m_label_time);
2016-11-25 12:55:15 +00:00
} else if (tag == TAG_BAR_PROGRESS && !is_stopped) {
builder->node(m_bar_progress->output(!m_status ? 0 : m_status->get_elapsed_percentage()));
2016-11-25 12:55:15 +00:00
} else if (tag == TAG_LABEL_OFFLINE) {
2016-11-02 19:22:45 +00:00
builder->node(m_label_offline);
2016-11-25 12:55:15 +00:00
} else if (tag == TAG_ICON_RANDOM) {
builder->cmd(mousebtn::LEFT, EVENT_RANDOM, m_icons->get("random"));
2016-11-25 12:55:15 +00:00
} else if (tag == TAG_ICON_REPEAT) {
builder->cmd(mousebtn::LEFT, EVENT_REPEAT, m_icons->get("repeat"));
2016-11-25 12:55:15 +00:00
} else if (tag == TAG_ICON_REPEAT_ONE) {
builder->cmd(mousebtn::LEFT, EVENT_REPEAT_ONE, m_icons->get("repeat_one"));
2016-11-25 12:55:15 +00:00
} else if (tag == TAG_ICON_PREV) {
builder->cmd(mousebtn::LEFT, EVENT_PREV, m_icons->get("prev"));
2016-11-25 12:55:15 +00:00
} else if ((tag == TAG_ICON_STOP || tag == TAG_TOGGLE_STOP) && (is_playing || is_paused)) {
builder->cmd(mousebtn::LEFT, EVENT_STOP, m_icons->get("stop"));
2016-11-25 12:55:15 +00:00
} else if ((tag == TAG_ICON_PAUSE || tag == TAG_TOGGLE) && is_playing) {
builder->cmd(mousebtn::LEFT, EVENT_PAUSE, m_icons->get("pause"));
2016-11-25 12:55:15 +00:00
} else if ((tag == TAG_ICON_PLAY || tag == TAG_TOGGLE || tag == TAG_TOGGLE_STOP) && !is_playing) {
builder->cmd(mousebtn::LEFT, EVENT_PLAY, m_icons->get("play"));
2016-11-25 12:55:15 +00:00
} else if (tag == TAG_ICON_NEXT) {
builder->cmd(mousebtn::LEFT, EVENT_NEXT, m_icons->get("next"));
2016-11-25 12:55:15 +00:00
} else if (tag == TAG_ICON_SEEKB) {
builder->cmd(mousebtn::LEFT, EVENT_SEEK + "-5"s, m_icons->get("seekb"));
2016-11-25 12:55:15 +00:00
} else if (tag == TAG_ICON_SEEKF) {
builder->cmd(mousebtn::LEFT, EVENT_SEEK + "+5"s, m_icons->get("seekf"));
2016-11-25 12:55:15 +00:00
} else {
2016-11-02 19:22:45 +00:00
return false;
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
return true;
}
2016-12-23 19:43:52 +00:00
bool mpd_module::input(string&& cmd) {
2016-11-25 12:55:15 +00:00
if (cmd.compare(0, 3, "mpd") != 0) {
2016-11-02 19:22:45 +00:00
return false;
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
try {
2016-12-09 08:02:47 +00:00
auto mpd = factory_util::unique<mpdconnection>(m_log, m_host, m_port, m_pass);
2016-11-02 19:22:45 +00:00
mpd->connect();
auto status = mpd->get_status();
bool is_playing = status->match_state(mpdstate::PLAYING);
bool is_paused = status->match_state(mpdstate::PAUSED);
bool is_stopped = status->match_state(mpdstate::STOPPED);
if (cmd == EVENT_PLAY && !is_playing) {
2016-11-02 19:22:45 +00:00
mpd->play();
} else if (cmd == EVENT_PAUSE && !is_paused) {
mpd->pause(true);
} else if (cmd == EVENT_STOP && !is_stopped) {
2016-11-02 19:22:45 +00:00
mpd->stop();
} else if (cmd == EVENT_PREV && !is_stopped) {
2016-11-02 19:22:45 +00:00
mpd->prev();
} else if (cmd == EVENT_NEXT && !is_stopped) {
2016-11-02 19:22:45 +00:00
mpd->next();
2016-11-25 12:55:15 +00:00
} else if (cmd == EVENT_REPEAT_ONE) {
2016-11-02 19:22:45 +00:00
mpd->set_single(!status->single());
2016-11-25 12:55:15 +00:00
} else if (cmd == EVENT_REPEAT) {
2016-11-02 19:22:45 +00:00
mpd->set_repeat(!status->repeat());
2016-11-25 12:55:15 +00:00
} else if (cmd == EVENT_RANDOM) {
2016-11-02 19:22:45 +00:00
mpd->set_random(!status->random());
2016-11-25 12:55:15 +00:00
} else if (cmd.compare(0, strlen(EVENT_SEEK), EVENT_SEEK) == 0) {
2016-11-02 19:22:45 +00:00
auto s = cmd.substr(strlen(EVENT_SEEK));
int percentage = 0;
2016-11-25 12:55:15 +00:00
if (s.empty()) {
2016-11-02 19:22:45 +00:00
return false;
2017-01-13 12:02:51 +00:00
} else if (s[0] == '+') {
2016-11-02 19:22:45 +00:00
percentage = status->get_elapsed_percentage() + std::atoi(s.substr(1).c_str());
} else if (s[0] == '-') {
percentage = status->get_elapsed_percentage() - std::atoi(s.substr(1).c_str());
} else {
percentage = std::atoi(s.c_str());
}
mpd->seek(status->get_songid(), status->get_seek_position(percentage));
2016-11-25 12:55:15 +00:00
} else {
2016-11-02 19:22:45 +00:00
return false;
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
} catch (const mpd_exception& err) {
m_log.err("%s: %s", name(), err.what());
m_mpd.reset();
}
return true;
}
}
2016-11-19 05:22:44 +00:00
POLYBAR_NS_END