From 64fa72b0547217afe553718f7e2ac7abe6f55495 Mon Sep 17 00:00:00 2001 From: michux Date: Thu, 24 Jan 2008 12:39:55 +0000 Subject: git-svn-id: https://ssl.bulix.org/svn/lcd4linux/trunk@848 3ae390bd-cb1e-0410-b409-cd5a39f66f1f --- plugin_mpd.c | 714 +++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 402 insertions(+), 312 deletions(-) (limited to 'plugin_mpd.c') diff --git a/plugin_mpd.c b/plugin_mpd.c index 5a950c8..0daa1d1 100644 --- a/plugin_mpd.c +++ b/plugin_mpd.c @@ -1,11 +1,11 @@ /* $Id$ * $URL$ * - * mpd informations + * mpd informations v0.7 * * Copyright (C) 2006 Stefan Kuhne * Copyright (C) 2007 Robert Buchholz - * Copyright (C) 2007 Michael Vogt + * Copyright (C) 2008 Michael Vogt * Copyright (C) 2006 The LCD4Linux Team * * This file is part of LCD4Linux. @@ -32,8 +32,44 @@ * int plugin_init_sample (void) * adds various functions * + * changelog v0.5 (20.11.2007): + * changed: mpd::artist(), mpd::title(), mpd::album() + * init code, call only if a function is used. + * removed: "old style" functions, duplicate code + * fixed: strdup() mem leaks + * added: getstate, function which return player status: play/pause/stop + * getVolume, funtcion which returns the mpd volume + * getFilename, return current filename + * + * + * changelog v0.6 (05.12.2007): + * changed: -connection handling + * -verbose "NO TAG" messages + * -init code + * added: -added password support (MPD_PASSWORD env variable) + * -mpd::getSongsInDb - how many songs are in the mpd db? + * -mpd::getMpdUptime - uptime of mpd + * -mpd::getMpdPlayTime - playtime of mpd + * -mpd::getMpdDbPlayTime - playtime of all songs in mpd db + * -basic error handler.. + * -mpd configuration in lcd4linux.conf + * -formatTime* - use those functions to format time values + * removed: -reprand method + * + * + * changelog v0.7 (14.12.2007): + * changed: -connection handling improved, do not disconnect/reconnect for each query + * -> uses less ressources + * */ +/* + +TODO: + -what happens if the db is updating? int mpd_status_db_is_updating() 0/1 + -port configuration to lcd4linux.cfg (like mysql) + +*/ #include "config.h" #include @@ -43,424 +79,478 @@ #include "debug.h" #include "plugin.h" +#include "cfg.h" +/* struct timeval */ +#include + +/* you need libmpd to compile this plugin! http://sarine.nl/libmpd */ #include #ifdef WITH_DMALLOC #include #endif -/* Struct Pointer */ - -struct Pointer { - mpd_Connection *conn; - mpd_Status *status; - mpd_InfoEntity *entity; -}; - - - -static struct Pointer connect() +/* current song */ +#define TAGLEN 512 +#define MAX_PATH 1024 +static char *artist[TAGLEN]; +static char *album[TAGLEN]; +static char *title[TAGLEN]; +static char *filename[MAX_PATH]; +static long l_totalTimeSec; +static long l_elapsedTimeSec; +static int l_bitRate; +static int l_repeatEnabled; +static int l_randomEnabled; +static int l_state; +static int l_volume; +static int l_songsInDb; +static long l_mpdUptime; +static long l_mpdPlaytime; +static long l_mpdDbPlaytime; +static long l_mpdPlaylistLength; +static int l_currentSongPos; + +/* connection information */ +static char host[255]; +static char pw[255]; +static int iport; +static int waittime; +struct timeval timestamp; + +static MpdObj *mi = NULL; +static char Section[] = "Plugin:MPD"; + + +static void error_callback( __attribute__ ((unused)) MpdObj * mi, int errorid, char *msg, __attribute__ ((unused)) + void *userdata) { - char *host = "localhost"; - char *port = "6600"; - int iport; - char *test; - - struct Pointer mpd = { - .conn = NULL, - .status = NULL, - .entity = NULL - }; - - if ((test = getenv("MPD_HOST"))) { - host = test; - } - - if ((test = getenv("MPD_PORT"))) { - port = test; - } - - iport = strtol(port, &test, 10); - - if ((iport < 0) || (*test != '\0')) { - fprintf(stderr, "[MPD] MPD_PORT \"%s\" is not a positive integer\n", port); - exit(EXIT_FAILURE); - } - - mpd.conn = mpd_newConnection(host, iport, 10); - - mpd_sendCommandListOkBegin(mpd.conn); - mpd_sendStatusCommand(mpd.conn); - mpd_sendCurrentSongCommand(mpd.conn); - mpd_sendCommandListEnd(mpd.conn); - - if ((mpd.status = mpd_getStatus(mpd.conn)) == NULL) { - fprintf(stderr, "[MPD] error when getting status: %s\n", mpd.conn->errorStr); - mpd_closeConnection(mpd.conn); - mpd.conn = NULL; - } else if (mpd.status->error) { - printf("[MPD] status error when connecting: %s\n", mpd.status->error); - } else if (mpd.conn->error) { - fprintf(stderr, "[MPD] error when connecting: %s\n", mpd.conn->errorStr); - mpd_freeStatus(mpd.status); - mpd_closeConnection(mpd.conn); - mpd.conn = NULL; - } - - return mpd; + debug("[MPD] caught error %i: '%s'", errorid, msg); } -static void disconnect(struct Pointer mpd) +static int configure_mpd(void) { - if (mpd.conn->error) { - fprintf(stderr, "[MPD] error when disconnecting: %s\n", mpd.conn->errorStr); - mpd_freeStatus(mpd.status); - mpd_closeConnection(mpd.conn); - return; - } + static int configured = 0; - mpd_finishCommand(mpd.conn); - if (mpd.conn->error) { - fprintf(stderr, "[MPD] error when disconnecting: %s\n", mpd.conn->errorStr); - } + char *s; - mpd_freeStatus(mpd.status); - mpd_closeConnection(mpd.conn); -} + if (configured != 0) + return configured; + /* read server */ + s = cfg_get(Section, "server", "localhost"); + if (*s == '\0') { + info("[MPD] empty '%s.server' entry from %s, assuming 'localhost'", Section, cfg_source()); + strcpy(host, "localhost"); + } else + strcpy(host, s); + free(s); -static void artist(RESULT * result, RESULT * query) -{ - char *value = NULL; - struct Pointer mpd = connect(); - if (mpd.conn == NULL) { - SetResult(&result, R_STRING, " "); - return; + /* read port */ + if (cfg_number(Section, "port", 6600, 1, 65536, &iport) < 1) { + info("[MPD] no '%s.port' entry from %s using MPD's default", Section, cfg_source()); } - mpd_nextListOkCommand(mpd.conn); - - while ((mpd.entity = mpd_getNextInfoEntity(mpd.conn))) { - mpd_Song *song = mpd.entity->info.song; + /* read minUpdateTime in ms */ + if (cfg_number(Section, "minUpdateTime", 500, 1, 10000, &waittime) < 1) { + info("[MPD] no '%s.minUpdateTime' entry from %s using MPD's default", Section, cfg_source()); + } - if (mpd.entity->type != MPD_INFO_ENTITY_TYPE_SONG) { - mpd_freeInfoEntity(mpd.entity); - continue; - } - if (!value && song->artist) { - /* we found our first song */ - value = strdup(song->artist); - /* add comment */ - if (query) { - char *myarg; - myarg = strdup(R2S(query)); - value = strcat(value, myarg); - free(myarg); - } - } - mpd_freeInfoEntity(mpd.entity); + /* read password */ + s = cfg_get(Section, "password", ""); + if (*s == '\0') { + info("[MPD] empty '%s.password' entry in %s, assuming none", Section, cfg_source()); + memset(pw, 0, sizeof(pw)); + } else { + strcpy(pw, s); + free(s); } - disconnect(mpd); + debug("[MPD] connection detail: [%s:%d]", host, iport); - /* store result, value must not be NULL */ - SetResult(&result, R_STRING, value ? value : " "); + mi = mpd_new(host, iport, pw); + mpd_signal_connect_error(mi, (ErrorCallback) error_callback, NULL); + mpd_set_connection_timeout(mi, 5); - free(value); + configured = 1; + return configured; } -static void title(RESULT * result) +static int mpd_update() { - char *value = NULL; - struct Pointer mpd = connect(); - if (mpd.conn == NULL) { - SetResult(&result, R_STRING, " "); - return; + int ret = -1; + mpd_Song *song; + static char *notagArt = "No artist tag"; + static char *notagTit = "No title tag"; + static char *notagAlb = "No album tag"; + static char *nofilename = "No Filename"; + struct timeval now; + int len; + + //check if configured + if (configure_mpd() < 0) { + return -1; } - mpd_nextListOkCommand(mpd.conn); - - while ((mpd.entity = mpd_getNextInfoEntity(mpd.conn))) { - mpd_Song *song = mpd.entity->info.song; - - if (mpd.entity->type != MPD_INFO_ENTITY_TYPE_SONG) { - mpd_freeInfoEntity(mpd.entity); - continue; - } + /* reread every 1000 msec only */ + gettimeofday(&now, NULL); + int timedelta = (now.tv_sec - timestamp.tv_sec) * 1000 + (now.tv_usec - timestamp.tv_usec) / 1000; + if (timedelta < waittime) { + return 1; + } - if (!value && song->title) { - value = strdup(song->title); + //debug("[MPD] time delta: %i", timedelta); + + //send password if enabled + if (pw[0] != 0) + mpd_send_password(mi); + + //check if connected + if (mpd_check_connected(mi) != 1) { + debug("[MPD] not connected, try to reconnect..."); + mpd_connect(mi); + + if (mpd_check_connected(mi) != 1) { + debug("[MPD] connection failed, give up..."); + return -1; } - mpd_freeInfoEntity(mpd.entity); + debug("[MPD] connection ok..."); } - disconnect(mpd); - - /* store result, value must not be NULL */ - SetResult(&result, R_STRING, value ? value : " "); + ret = 0; + + mpd_status_update(mi); + + l_elapsedTimeSec = mpd_status_get_elapsed_song_time(mi); + l_bitRate = mpd_status_get_bitrate(mi); + l_totalTimeSec = mpd_status_get_total_song_time(mi); + l_repeatEnabled = mpd_player_get_repeat(mi); + l_randomEnabled = mpd_player_get_random(mi); + l_state = mpd_player_get_state(mi); + l_volume = mpd_status_get_volume(mi); + l_songsInDb = mpd_stats_get_total_songs(mi); + l_mpdUptime = mpd_stats_get_uptime(mi); + l_mpdPlaytime = mpd_stats_get_playtime(mi); + l_mpdDbPlaytime = mpd_stats_get_db_playtime(mi); + l_mpdPlaylistLength = mpd_playlist_get_playlist_length(mi); + l_currentSongPos = mpd_player_get_current_song_pos(mi); + + /* dummy checks */ + if (l_volume < 0 || l_volume > 100) + l_volume = 0; + + song = mpd_playlist_get_current_song(mi); + if (song) { + + /* copy song information to local variables */ + memset(album, 0, TAGLEN); + if (song->album != 0) { + len = strlen(song->album); + if (len > TAGLEN) + len = TAGLEN; + memcpy(album, song->album, len); + + } else + memcpy(album, notagAlb, strlen(notagAlb)); + + memset(artist, 0, TAGLEN); + if (song->artist != 0) { + len = strlen(song->artist); + if (len > TAGLEN) + len = TAGLEN; + memcpy(artist, song->artist, len); + } else + memcpy(artist, notagArt, strlen(notagArt)); + + memset(title, 0, TAGLEN); + if (song->title != 0) { + len = strlen(song->title); + if (len > TAGLEN) + len = TAGLEN; + memcpy(title, song->title, len); + } else + memcpy(title, notagTit, strlen(notagTit)); + + memset(filename, 0, MAX_PATH); + if (song->file != 0) { + len = strlen(song->file); + if (len > MAX_PATH) + len = MAX_PATH; + memcpy(filename, song->file, len); + } else + memcpy(filename, nofilename, strlen(nofilename)); + } + +// mpd_disconnect(mi); /* you need to disconnect here */ + gettimeofday(×tamp, NULL); - free(value); + return ret; } -static void album(RESULT * result) +static void elapsedTimeSec(RESULT * result) { - char *value = NULL; - struct Pointer mpd = connect(); - if (mpd.conn == NULL) { - SetResult(&result, R_STRING, " "); - return; - } - - mpd_nextListOkCommand(mpd.conn); - - while ((mpd.entity = mpd_getNextInfoEntity(mpd.conn))) { - mpd_Song *song = mpd.entity->info.song; + double d; + mpd_update(); + d = (double)l_elapsedTimeSec; + SetResult(&result, R_NUMBER, &d); +} - if (mpd.entity->type != MPD_INFO_ENTITY_TYPE_SONG) { - mpd_freeInfoEntity(mpd.entity); - continue; - } - if (!value && song->album) { - value = strdup(song->album); - } - mpd_freeInfoEntity(mpd.entity); - } +static void totalTimeSec(RESULT * result) +{ + double d; + mpd_update(); + d = (double)l_totalTimeSec; + SetResult(&result, R_NUMBER, &d); +} - disconnect(mpd); +static void bitRate(RESULT * result) +{ + double d; + mpd_update(); + d = (double)l_bitRate; + SetResult(&result, R_NUMBER, &d); +} - /* store result, value must not be NULL */ - SetResult(&result, R_STRING, value ? value : " "); - free(value); +static void getRepeatInt(RESULT * result) +{ + double d; + mpd_update(); + d = (double)l_repeatEnabled; + SetResult(&result, R_NUMBER, &d); } -#define _mpd_dummy 000 -#define _mpd_status_get_elapsed_song_time 001 -#define _mpd_status_get_bitrate 002 -#define _mpd_status_get_total_song_time 003 -#define _mpd_player_get_repeat 004 -#define _mpd_player_get_random 005 -void error_callback( __attribute__ ((unused)) MpdObj * mi, int errorid, char *msg, __attribute__ ((unused)) - void *userdata) +static void getRandomInt(RESULT * result) { - printf("[MPD] caught error %i: '%s'\n", errorid, msg); + double d; + mpd_update(); + d = (double)l_randomEnabled; + SetResult(&result, R_NUMBER, &d); } -static int mpd_get(int function) +static void getArtist(RESULT * result) { - int ret = -1; - MpdObj *mi = NULL; - - char *host = "localhost"; - char *port = "6600"; - int iport; - char *test; - - if ((test = getenv("MPD_HOST"))) { - host = test; + int cSong = mpd_update(); + if (cSong != 0) { /* inform user this information is cached... */ +// debug("[MPD] use cached artist information..."); } + SetResult(&result, R_STRING, artist); +} - if ((test = getenv("MPD_PORT"))) { - port = test; +static void getTitle(RESULT * result) +{ + int cSong = mpd_update(); + if (cSong != 0) { /* inform user this information is cached... */ +// debug("[MPD] use cached title information..."); } + SetResult(&result, R_STRING, title); +} - iport = strtol(port, &test, 10); - - if ((iport < 0) || (*test != '\0')) { - fprintf(stderr, "[MPD] MPD_PORT \"%s\" is not a positive integer\n", port); - exit(EXIT_FAILURE); +static void getAlbum(RESULT * result) +{ + int cSong = mpd_update(); + if (cSong != 0) { /* inform user this information is cached... */ +// debug("[MPD] use cached album information..."); } + SetResult(&result, R_STRING, album); +} - mi = mpd_new(host, iport, NULL); - mpd_signal_connect_error(mi, (ErrorCallback) error_callback, NULL); - mpd_set_connection_timeout(mi, 5); - - if (!mpd_connect(mi)) { - switch (function) { - case _mpd_dummy: - ret = 1; - break; - case _mpd_status_get_elapsed_song_time: - ret = mpd_status_get_elapsed_song_time(mi); - break; - case _mpd_status_get_bitrate: - ret = mpd_status_get_bitrate(mi); - break; - case _mpd_status_get_total_song_time: - ret = mpd_status_get_total_song_time(mi); - break; - case _mpd_player_get_repeat: - ret = mpd_player_get_repeat(mi); - break; - case _mpd_player_get_random: - ret = mpd_player_get_random(mi); - break; - } - - mpd_disconnect(mi); - mpd_free(mi); +static void getFilename(RESULT * result) +{ + int cSong = mpd_update(); + if (cSong != 0) { /* inform user this information is cached... */ +// debug("[MPD] use cached filename information..."); } - return ret; + SetResult(&result, R_STRING, filename); } -static void elapsedTime(RESULT * result) +/* + return player state: + 0=unknown + 1=play + 2=pause + 3=stop +*/ +static void getStateInt(RESULT * result) { - char myTime[6] = " "; - - const int playTime = mpd_get(_mpd_status_get_elapsed_song_time); - - if ((playTime >= 0) && (playTime < 6000)) { - const int minutes = (int) (playTime / 60); - const int seconds = (int) (playTime % 60); - sprintf(myTime, "%02d:%02d", minutes, seconds); - } else if (playTime >= 6000) { - strcpy(myTime, "LONG"); + double ret; + + mpd_update(); + + switch (l_state) { + case MPD_PLAYER_PLAY: + ret = 1; + break; + case MPD_PLAYER_PAUSE: + ret = 2; + break; + case MPD_PLAYER_STOP: + ret = 3; + break; + default: + ret = 0; + break; } - /* store result */ - SetResult(&result, R_STRING, myTime); + SetResult(&result, R_NUMBER, &ret); } -static void elapsedTimeSec(RESULT * result) -{ - const int playTime = mpd_get(_mpd_status_get_elapsed_song_time); - double d = 0.0; - - if (playTime != -1) - d = playTime; - /* store result */ +static void getVolume(RESULT * result) +{ + double d; + mpd_update(); + d = (double)l_volume; + /* return 0..100 or < 0 when failed */ SetResult(&result, R_NUMBER, &d); } -static void totalTime(RESULT * result) +/* return the # of songs in thr mpd db .. */ +static void getSongsInDb(RESULT * result) { - char myTime[6] = "ERROR"; - - const int totTime = mpd_get(_mpd_status_get_total_song_time); - if ((totTime >= 0) && (totTime < 6000)) { - const int minutes = (int) (totTime / 60); - const int seconds = (int) (totTime % 60); - sprintf(myTime, "%02d:%02d", minutes, seconds); - } else if (totTime >= 6000) { - strcpy(myTime, "LONG"); - } - - /* store result */ - SetResult(&result, R_STRING, myTime); + double d; + mpd_update(); + d = (double)l_songsInDb; + /* return 0..100 or < 0 when failed */ + SetResult(&result, R_NUMBER, &d); } -static void totalTimeSec(RESULT * result) +static void getMpdUptime(RESULT * result) { - const int totTime = mpd_get(_mpd_status_get_total_song_time); - double d = 0.0; - - if (totTime != -1) - d = totTime; - - /* store result */ + double d; + mpd_update(); + d = (double)l_mpdUptime; SetResult(&result, R_NUMBER, &d); } -static void bitRate(RESULT * result) +static void getMpdPlayTime(RESULT * result) { - char rateStr[4]; - - const int rate = mpd_get(_mpd_status_get_bitrate); - - if ((rate >= 0) && (rate < 1000)) { - sprintf(rateStr, "%03d", rate); - } - - /* store result */ - SetResult(&result, R_STRING, rateStr); + double d; + mpd_update(); + d = (double)l_mpdPlaytime; + SetResult(&result, R_NUMBER, &d); } -static void getRepeat(RESULT * result) +static void getMpdDbPlayTime(RESULT * result) { - char *value = " "; + double d; + mpd_update(); + d = (double)l_mpdDbPlaytime; + SetResult(&result, R_NUMBER, &d); +} - const int rep = mpd_get(_mpd_player_get_repeat); +static void getMpdPlaylistLength(RESULT * result) +{ + double d; + mpd_update(); + d = (double)l_mpdPlaylistLength; + SetResult(&result, R_NUMBER, &d); +} - if (rep != -1) { - if (rep) - value = "REP"; - /* else value = strdup(" "); */ - } - /* store result */ - SetResult(&result, R_STRING, value); +static void getCurrentSongPos(RESULT * result) +{ + double d; + mpd_update(); + d = (double)l_currentSongPos; + SetResult(&result, R_NUMBER, &d); } -static void getRandom(RESULT * result) + +static void formatTimeMMSS(RESULT * result, RESULT * param) { - char *value = " "; + long sec; + char myTime[6] = " "; - const int ran = mpd_get(_mpd_player_get_random); + sec = R2N(param); + + if ((sec >= 0) && (sec < 6000)) { + const int minutes = (int) (sec / 60); + const int seconds = (int) (sec % 60); + sprintf(myTime, "%02d:%02d", minutes, seconds); + } else if (sec >= 6000) { + strcpy(myTime, "LONG"); + } else + strcpy(myTime, "ERROR"); - if (ran != -1) { - if (ran) - value = strdup("RND"); - /* else value = strdup(" "); */ - } /* store result */ - SetResult(&result, R_STRING, value); + SetResult(&result, R_STRING, myTime); } -static void getRepRand(RESULT * result) +static void formatTimeDDHHMM(RESULT * result, RESULT * param) { - char str[9] = " "; + long sec; + char myTime[16] = " "; - const int ran = mpd_get(_mpd_player_get_random); - const int rep = mpd_get(_mpd_player_get_repeat); + sec = R2N(param); - if (ran != -1 && rep != -1) { + if (sec >= 0) { + int days = sec / 86400; + int hours = (sec % 86400) / 3600; + int minutes = (sec % 3600) / 60; + sprintf(myTime, "%dd%02dh%02dm", days, hours, minutes); /* 3d 12:33h */ + } else + strcpy(myTime, "ERROR"); - if (rep) - sprintf(str, "REP/"); - else - sprintf(str, "---/"); - if (ran) - sprintf(str, "%sRND", str); - else - sprintf(str, "%s---", str); - } /* store result */ - SetResult(&result, R_STRING, str); + SetResult(&result, R_STRING, myTime); } + + int plugin_init_mpd(void) { - /* Check for File */ - if (mpd_get(_mpd_dummy) != 1) { - error("[MPD] Error: Cannot connect to MPD! Is MPD started?"); - return -1; - } - - AddFunction("mpd::artist", 1, artist); - AddFunction("mpd::title", 0, title); - AddFunction("mpd::album", 0, album); - AddFunction("mpd::totalTime", 0, totalTime); + int check; + debug("[MPD] v0.7, check lcd4linux configuration file..."); + + check = configure_mpd(); + if (check) + debug("[MPD] done"); + else + debug("[MPD] error!"); + + memset(album, 0, TAGLEN); + memset(artist, 0, TAGLEN); + memset(title, 0, TAGLEN); + memset(filename, 0, MAX_PATH); + + gettimeofday(×tamp, NULL); + + AddFunction("mpd::artist", 0, getArtist); + AddFunction("mpd::title", 0, getTitle); + AddFunction("mpd::album", 0, getAlbum); + AddFunction("mpd::file", 0, getFilename); AddFunction("mpd::totalTimeSec", 0, totalTimeSec); - AddFunction("mpd::elapsedTime", 0, elapsedTime); AddFunction("mpd::elapsedTimeSec", 0, elapsedTimeSec); AddFunction("mpd::bitRate", 0, bitRate); - AddFunction("mpd::getRepeat", 0, getRepeat); - AddFunction("mpd::getRandom", 0, getRandom); - AddFunction("mpd::getRepRand", 0, getRepRand); + AddFunction("mpd::getRepeatInt", 0, getRepeatInt); + AddFunction("mpd::getRandomInt", 0, getRandomInt); + AddFunction("mpd::getStateInt", 0, getStateInt); + AddFunction("mpd::getVolume", 0, getVolume); + AddFunction("mpd::getSongsInDb", 0, getSongsInDb); + AddFunction("mpd::getMpdUptime", 0, getMpdUptime); + AddFunction("mpd::getMpdPlayTime", 0, getMpdPlayTime); + AddFunction("mpd::getMpdDbPlayTime", 0, getMpdDbPlayTime); + AddFunction("mpd::getMpdPlaylistLength", 0, getMpdPlaylistLength); + AddFunction("mpd::getMpdPlaylistGetCurrentId", 0, getCurrentSongPos); + + AddFunction("mpd::formatTimeMMSS", 1, formatTimeMMSS); + AddFunction("mpd::formatTimeDDHHMM", 1, formatTimeDDHHMM); + return 0; } void plugin_exit_mpd(void) { - /* empty */ + debug("[MPD] disconnect from mpd"); + mpd_free(mi); } -- cgit v1.2.3