/******************************************************************************!
* \file Player.cpp
* \author Sebastien Beaugrand
* \sa http://beaugrand.chez.com/
* \copyright CeCILL 2.1 Free Software license
******************************************************************************/
#include <iostream>
#include "Player.h"
#include "log.h"
/******************************************************************************!
* \fn ~Player
******************************************************************************/
Player::~Player()
{
DEBUG("");
this->quit();
}
/******************************************************************************!
* \fn isError
******************************************************************************/
int
Player::isError(const char* func)
{
enum mpd_error err = mpd_connection_get_error(mConn);
if (err != MPD_ERROR_SUCCESS) {
if (err == MPD_ERROR_CLOSED ||
err == MPD_ERROR_SYSTEM) {
DEBUG("MPD_ERROR_CLOSED || MPD_ERROR_SYSTEM");
return err;
}
ERROR(func << " " <<
err << " " << mpd_connection_get_error_message(mConn));
mpd_connection_clear_error(mConn);
return -1;
}
return 0;
}
/******************************************************************************!
* \fn init
******************************************************************************/
int
Player::init()
{
if (mConn != NULL) {
mpd_connection_free(mConn);
}
mConn = mpd_connection_new("/run/mpd.sock", 0, 0);
if (mConn == NULL) {
ERROR("mConn == NULL");
return 1;
}
if (this->isError(__FUNCTION__)) {
mpd_connection_free(mConn);
mConn = NULL;
return 2;
}
if (! mpd_connection_set_keepalive(mConn, true)) {
ERROR("set_keepalive");
return 3;
}
if (! mpd_send_command(mConn, "config", NULL)) {
return 4;
}
struct mpd_pair* pair = mpd_recv_pair_named(mConn, "music_directory");
if (pair != NULL) {
this->musicDirectory = pair->value;
mpd_return_pair(mConn, pair);
}
mpd_response_finish(mConn) || mpd_connection_clear_error(mConn);
return 0;
}
/******************************************************************************!
* \fn getMPDStatus
******************************************************************************/
struct mpd_status*
Player::getMPDStatus()
{
int res;
struct mpd_status* status = mpd_run_status(mConn);
res = this->isError(__FUNCTION__);
if (res == MPD_ERROR_CLOSED ||
res == MPD_ERROR_SYSTEM) {
if (status != NULL) {
mpd_status_free(status);
}
DEBUG("reconnect");
this->init();
status = mpd_run_status(mConn);
if (this->isError(__FUNCTION__)) {
}
}
return status;
}
/******************************************************************************!
* \fn getStatus
******************************************************************************/
unsigned int
Player::getStatus()
{
enum mpd_state res;
struct mpd_status* status = this->getMPDStatus();
if (status == NULL) {
return STATE_UNKNOWN;
}
res = mpd_status_get_state(status);
mpd_status_free(status);
/* */ if (res == MPD_STATE_PLAY) {
return STATE_PLAY;
} else if (res == MPD_STATE_PAUSE) {
return STATE_PAUSE;
}
return STATE_UNKNOWN;
}
/******************************************************************************!
* \fn getPlaytime
******************************************************************************/
unsigned int
Player::getPlaytime()
{
unsigned int res;
struct mpd_status* status = this->getMPDStatus();
if (status == NULL) {
return 0;
}
res = mpd_status_get_elapsed_ms(status);
mpd_status_free(status);
return res;
}
/******************************************************************************!
* \fn titleList
******************************************************************************/
Json::Value
Player::titleList()
{
Json::Value r;
unsigned int playtime;
int pos;
struct mpd_song* song = NULL;
int count = 0;
int length;
const char* tag;
struct mpd_status* status = this->getMPDStatus();
if (status == NULL) {
return r;
}
pos = mpd_status_get_song_pos(status);
DEBUG("pos " << pos);
r["pos"] = pos;
length = mpd_status_get_queue_length(status);
DEBUG("length " << length);
mpd_status_free(status);
r["song"] = {};
for (count = 0; count < length; ++count) {
Json::Value s;
song = mpd_run_get_queue_song_pos(mConn, count);
if (this->isError(__FUNCTION__)) {
if (song != NULL) {
mpd_song_free(song);
}
return r;
}
if (song == NULL) {
return r;
}
DEBUG("id " << mpd_song_get_id(song));
tag = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0);
if (tag != NULL) {
s["artist"] = tag;
}
tag = mpd_song_get_tag(song, MPD_TAG_TITLE, 0);
if (tag != NULL) {
s["title"] = tag;
}
unsigned int duration = mpd_song_get_duration_ms(song);
duration /= 1000;
s["duration"] = duration;
if (count == pos) {
playtime = this->getPlaytime();
if (playtime != 0) {
playtime /= 1000;
s["playtime"] = playtime;
}
}
mpd_song_free(song);
r["song"].append(s);
}
return r;
}
/******************************************************************************!
* \fn stop
******************************************************************************/
void
Player::stop()
{
bool res;
struct mpd_status* status = this->getMPDStatus();
if (status == NULL) {
return;
}
mpd_status_free(status);
res = mpd_run_stop(mConn);
if (this->isError(__FUNCTION__)) {
}
if (! res) {
ERROR("mpd_run_stop");
}
}
/******************************************************************************!
* \fn start
******************************************************************************/
void
Player::start()
{
DEBUG("");
bool res;
struct mpd_status* status = this->getMPDStatus();
if (status == NULL) {
DEBUG("status == NULL");
return;
}
mpd_status_free(status);
res = mpd_run_play(mConn);
if (this->isError(__FUNCTION__)) {
}
if (! res) {
ERROR("mpd_run_play");
}
}
/******************************************************************************!
* \fn startId
******************************************************************************/
void
Player::startId(int pos)
{
bool res;
struct mpd_status* status = this->getMPDStatus();
if (status == NULL) {
return;
}
mpd_status_free(status);
res = mpd_run_play_pos(mConn, pos);
if (this->isError(__FUNCTION__)) {
return;
}
if (! res) {
ERROR("mpd_run_play_pos");
return;
}
this->start();
}
/******************************************************************************!
* \fn startRel
******************************************************************************/
void
Player::startRel(int pos)
{
bool res;
struct mpd_status* status = this->getMPDStatus();
if (status == NULL) {
return;
}
mpd_status_free(status);
if (pos > 0) {
res = mpd_run_next(mConn);
} else {
res = mpd_run_previous(mConn);
}
if (this->isError(__FUNCTION__)) {
return;
}
if (! res) {
ERROR("mpd_run_ next previous");
return;
}
this->start();
}
/******************************************************************************!
* \fn pause
******************************************************************************/
void
Player::pause()
{
bool res;
if (this->getStatus() == STATE_PAUSE) {
res = mpd_run_play(mConn);
} else {
res = mpd_run_pause(mConn, true);
}
if (this->isError(__FUNCTION__)) {
}
if (! res) {
ERROR("mpd_run_ play pause");
}
}
/******************************************************************************!
* \fn resume
******************************************************************************/
void
Player::resume(int milliseconds)
{
bool res;
unsigned int status;
// Status
status = this->getStatus();
if (status != STATE_PLAY) {
this->start();
sleep(2);
}
// Seek
res = mpd_run_seek_current(mConn, milliseconds / 1000.0, false);
if (this->isError(__FUNCTION__)) {
}
if (! res) {
ERROR("mpd_run_seek_current");
}
}
/******************************************************************************!
* \fn m3u
******************************************************************************/
void
Player::m3u(std::string_view album)
{
bool res;
this->stop();
res = mpd_run_clear(mConn);
if (this->isError(__FUNCTION__)) {
}
if (! res) {
ERROR("mpd_run_clear");
}
std::string m3u = this->musicDirectory + '/' + album.data();
DEBUG(m3u);
res = mpd_run_load(mConn, m3u.c_str());
if (this->isError(__FUNCTION__)) {
ERROR(m3u);
}
if (! res) {
ERROR("mpd_run_load");
}
this->start();
}
/******************************************************************************!
* \fn currentTitle
******************************************************************************/
Json::Value
Player::currentTitle()
{
Json::Value r;
struct mpd_status* status = this->getMPDStatus();
if (status == NULL) {
return r;
}
int pos = mpd_status_get_song_pos(status);
if (pos < 0) {
return r;
}
r["pos"] = pos;
r["length"] = mpd_status_get_queue_length(status);
if (mpd_status_get_state(status) == MPD_STATE_PAUSE) {
r["pause"] = true;
} else {
r["pause"] = false;
}
mpd_status_free(status);
struct mpd_song* song = mpd_run_get_queue_song_pos(mConn, pos);
if (this->isError(__FUNCTION__)) {
if (song != NULL) {
mpd_song_free(song);
}
return r;
}
if (song == NULL) {
ERROR("mpd_run_get_queue_song_pos");
return r;
}
std::string path;
if (const char* tag = mpd_song_get_tag(song, MPD_TAG_TITLE, 0);
tag != NULL) {
r["title"] = tag;
} else if (const char* tag2 = mpd_song_get_tag(song, MPD_TAG_NAME, 0);
tag2 != NULL) {
std::string name = tag2;
auto pos1 = name.find(" - ") + 3;
r["title"] = name.substr(pos1);
}
if (const char* tag = mpd_song_get_tag(song, MPD_TAG_ALBUM, 0);
tag != NULL) {
r["album"] = tag;
} else {
path = mpd_song_get_uri(song);
auto pos1 = path.rfind('/');
auto pos2 = path.rfind(" - ", pos1 - 1) + 3;
r["album"] = path.substr(pos2, pos1 - pos2);
}
if (const char* tag = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0);
tag != NULL) {
r["artist"] = tag;
} else {
if (path.empty()) {
path = mpd_song_get_uri(song);
}
auto pos1 = path.rfind('/');
auto pos3 = path.rfind('/', pos1 - 1) + 1;
auto pos2 = path.find(" - ", pos3);
r["artist"] = path.substr(pos3, pos2 - pos3);
}
if (const char* tag = mpd_song_get_tag(song, MPD_TAG_DATE, 0);
tag != NULL) {
r["date"] = tag;
} else {
if (path.empty()) {
path = mpd_song_get_uri(song);
}
auto pos1 = path.rfind('/');
auto pos4 = path.rfind('/', pos1 - 1) + 1;
auto pos3 = path.find(" - ", pos4) + 3;
auto pos2 = path.find(" - ", pos3);
r["date"] = path.substr(pos3, pos2 - pos3);
}
mpd_song_free(song);
return r;
}
/******************************************************************************!
* \fn quit
******************************************************************************/
void
Player::quit()
{
if (mConn == NULL) {
return;
}
struct mpd_status* status = this->getMPDStatus();
if (status == NULL) {
mConn = NULL;
return;
}
mpd_status_free(status);
mpd_connection_free(mConn);
mConn = NULL;
}