diff --git a/Makefile b/Makefile index b0124fa..92098b0 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ all: mpd-notification README.html mpd-notification: mpd-notification.c mpd-notification.h config.h version.h $(CC) $(CFLAGS) $(LDFLAGS) -o mpd-notification mpd-notification.c -config.h: +config.h: config.def.h $(CP) config.def.h config.h version.h: $(wildcard .git/HEAD .git/index .git/refs/tags/*) Makefile diff --git a/config.def.h b/config.def.h index 54ffaf8..24502f3 100644 --- a/config.def.h +++ b/config.def.h @@ -24,6 +24,22 @@ #define TEXT_STOP "Stopped playback" #define TEXT_NONE "No action received yet." #define TEXT_UNKNOWN "(unknown)" +/** + * Define the format used when showing elapsed time in the notification. + * The first argument of the format is the elapsed time and the second + * is the total time of the song. You might want to use %s + * to have the total time before the elapsed time in the resulting text. + */ +#define TEXT_TIME_RATIO "%1s / %2s" +/** + * Define the format used when the total time is showned alone + */ +#define TEXT_TOTAL_TIME "%s" +/** + * Define the format used to print a time in the notification. The syntax is + * the same as the one used by the function strftime. + */ +#define TIME_FORMAT "%M:%S" /* this is a regular expression that has to match image filename used * for artwork */ diff --git a/mpd-notification.c b/mpd-notification.c index eed7070..e0c5fb0 100644 --- a/mpd-notification.c +++ b/mpd-notification.c @@ -34,6 +34,10 @@ uint8_t oneline = 0; #ifdef HAVE_LIBAV magic_t magic = NULL; #endif +const char * music_dir = NULL; +unsigned int scale = 0; +int file_workaround = 0; +int show_time = 0; /*** received_signal ***/ void received_signal(int signal) { @@ -54,10 +58,19 @@ void received_signal(int signal) { if (verbose > 0) printf("%s: Received signal %s, showing last notification again.\n", program, strsignal(signal)); + //Mpd is idle because of the main loop of this program. We + //need to stop idle mode to interact with it. The idle mode + //is recovered at the end of this block to let the main loop + //continue as usual. + mpd_send_noidle(conn); + mpd_recv_idle(conn, false); + + update_notification(true); if (notify_notification_show(notification, &error) == FALSE) { g_printerr("%s: Error \"%s\" while trying to show notification again.\n", program, error->message); g_error_free(error); } + mpd_send_idle_mask(conn, MPD_IDLE_PLAYER); break; default: fprintf(stderr, "%s: Reveived signal %s (%d), no idea what to do...\n", program, strsignal(signal), signal); @@ -219,18 +232,166 @@ char * append_string(char * string, const char * format, const char delim, const return string; } -/*** main ***/ -int main(int argc, char ** argv) { - dictionary * ini = NULL; +int update_notification(int show_elapsed_time) +{ + static enum mpd_state last_state = MPD_STATE_UNKNOWN; + const char * title = NULL, * artist = NULL, * album = NULL; + + struct mpd_status* status; + enum mpd_state state = MPD_STATE_UNKNOWN; + struct mpd_song * song = NULL; + const char * song_uri = NULL; + + long elapsed_time, total_time; + size_t TIMES_TEXT_SIZE = 32; + size_t TIMES_RATIO_TEXT_SIZE = 4 * TIMES_TEXT_SIZE; + char elapsed_time_text[TIMES_TEXT_SIZE]; + char total_time_text[TIMES_TEXT_SIZE]; + char time_ratio_text[TIMES_RATIO_TEXT_SIZE]; + char * notifystr = NULL; GdkPixbuf * pixbuf = NULL; + int nonotification = 0; + + mpd_command_list_begin(conn, true); + mpd_send_status(conn); + mpd_send_current_song(conn); + mpd_command_list_end(conn); + + status = mpd_recv_status(conn); + state = mpd_status_get_state(status); + if (state == MPD_STATE_PLAY || state == MPD_STATE_PAUSE) { + + elapsed_time = mpd_status_get_elapsed_time(status); + total_time = mpd_status_get_total_time(status); + + /* There's a bug in libnotify where the server spec version is fetched + * too late, which results in issue with image date. Make sure to + * show a notification without image data (just generic icon) first. */ + if (last_state != MPD_STATE_PLAY) { + notify_notification_update(notification, TEXT_TOPIC, "Starting playback...", ICON_AUDIO_X_GENERIC); + notify_notification_show(notification, NULL); + } + + mpd_response_next(conn); + + song = mpd_recv_song(conn); + + title = mpd_song_get_tag(song, MPD_TAG_TITLE, 0); + + /* ignore if we have no title */ + if (title == NULL) + { + nonotification = 0; + } + else + { +#ifdef HAVE_SYSTEMD + sd_notifyf(0, "READY=1\nSTATUS=%s: %s", state == MPD_STATE_PLAY ? "Playing" : "Paused", title); +#endif + + /* initial allocation and string termination */ + notifystr = strdup(""); + notifystr = append_string(notifystr, TEXT_PLAY_PAUSE_STATE, 0, state == MPD_STATE_PLAY ? "Playing": "Paused"); + notifystr = append_string(notifystr, TEXT_PLAY_PAUSE_TITLE, 0, title); + + if ((artist = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)) != NULL) + notifystr = append_string(notifystr, TEXT_PLAY_PAUSE_ARTIST, oneline ? ' ' : '\n', artist); + + if ((album = mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)) != NULL) + notifystr = append_string(notifystr, TEXT_PLAY_PAUSE_ALBUM, oneline ? ' ' : '\n', album); + + song_uri = mpd_song_get_uri(song); + + if (music_dir != NULL && song_uri != NULL) { + GdkPixbuf * copy; + + pixbuf = retrieve_artwork(music_dir, song_uri); + + if (pixbuf != NULL && scale > 0) { + int x, y; + + x = gdk_pixbuf_get_width(pixbuf); + y = gdk_pixbuf_get_height(pixbuf); + + if ((copy = gdk_pixbuf_scale_simple (pixbuf, + (x > y ? scale : scale * x / y), + (y > x ? scale : scale * y / x), + GDK_INTERP_BILINEAR)) != NULL) { + g_object_unref(pixbuf); + pixbuf = copy; + } + } + + + } + + mpd_song_free(song); + + if (show_time) + { + strftime(total_time_text, TIMES_TEXT_SIZE, TIME_FORMAT, gmtime(&total_time)); + if (show_elapsed_time) + { + strftime(elapsed_time_text, TIMES_TEXT_SIZE, TIME_FORMAT, gmtime(&elapsed_time)); + snprintf(time_ratio_text, TIMES_RATIO_TEXT_SIZE, TEXT_TIME_RATIO, elapsed_time_text, total_time_text); + notifystr = append_string(notifystr, "%s", oneline ? ' ' : '\n', time_ratio_text); + } + else + { + notifystr = append_string(notifystr, TEXT_TOTAL_TIME, oneline ? ' ' : '\n', total_time_text); + } + } + } + + } else if (state == MPD_STATE_STOP) { + notifystr = strdup(TEXT_STOP); +#ifdef HAVE_SYSTEMD + sd_notify(0, "READY=1\nSTATUS=" TEXT_STOP); +#endif + } else + notifystr = strdup(TEXT_UNKNOWN); + + last_state = state; + + if (verbose > 0) + printf("%s: %s\n", program, notifystr); + + /* Some notification daemons do not support handing pixbuf data. Write a PNG + * file and give the path. */ + if (file_workaround > 0 && pixbuf != NULL) { + gdk_pixbuf_save(pixbuf, "/tmp/.mpd-notification-artwork.png", "png", NULL, NULL); + + notify_notification_update(notification, TEXT_TOPIC, notifystr, "/tmp/.mpd-notification-artwork.png"); + } else + notify_notification_update(notification, TEXT_TOPIC, notifystr, ICON_AUDIO_X_GENERIC); + + /* Call this unconditionally! When pixbuf is NULL this clears old image. */ + notify_notification_set_image_from_pixbuf(notification, pixbuf); + + if (notifystr != NULL) { + free(notifystr); + notifystr = NULL; + } + if (pixbuf != NULL) { + g_object_unref(pixbuf); + pixbuf = NULL; + } + + mpd_status_free(status); + mpd_response_finish(conn); + + return !nonotification; +} + +/*** main ***/ +int main(int argc, char ** argv) { + dictionary * ini = NULL; GError * error = NULL; - enum mpd_state state = MPD_STATE_UNKNOWN, last_state = MPD_STATE_UNKNOWN; - const char * mpd_host, * mpd_port_str, * music_dir, * uri = NULL; + const char * mpd_host, * mpd_port_str; unsigned mpd_port = MPD_PORT, mpd_timeout = MPD_TIMEOUT, notification_timeout = NOTIFICATION_TIMEOUT; - struct mpd_song * song = NULL; - unsigned int i, version = 0, help = 0, scale = 0, file_workaround = 0; + unsigned int i, version = 0, help = 0; int rc = EXIT_FAILURE; program = argv[0]; @@ -255,6 +416,7 @@ int main(int argc, char ** argv) { notification_timeout = iniparser_getint(ini, ":timeout", notification_timeout); oneline = iniparser_getboolean(ini, ":oneline", oneline); scale = iniparser_getint(ini, ":scale", scale); + show_time = iniparser_getboolean(ini, ":show-time", show_time); } /* get the verbose status */ @@ -391,96 +553,11 @@ int main(int argc, char ** argv) { #endif while (doexit == 0 && mpd_run_idle_mask(conn, MPD_IDLE_PLAYER)) { - mpd_command_list_begin(conn, true); - mpd_send_status(conn); - mpd_send_current_song(conn); - mpd_command_list_end(conn); - - state = mpd_status_get_state(mpd_recv_status(conn)); - if (state == MPD_STATE_PLAY || state == MPD_STATE_PAUSE) { - /* There's a bug in libnotify where the server spec version is fetched - * too late, which results in issue with image date. Make sure to - * show a notification without image data (just generic icon) first. */ - if (last_state != MPD_STATE_PLAY) { - notify_notification_update(notification, TEXT_TOPIC, "Starting playback...", ICON_AUDIO_X_GENERIC); - notify_notification_show(notification, NULL); - } - - mpd_response_next(conn); - - song = mpd_recv_song(conn); - - title = mpd_song_get_tag(song, MPD_TAG_TITLE, 0); - - /* ignore if we have no title */ - if (title == NULL) - goto nonotification; - -#ifdef HAVE_SYSTEMD - sd_notifyf(0, "READY=1\nSTATUS=%s: %s", state == MPD_STATE_PLAY ? "Playing" : "Paused", title); -#endif - - /* initial allocation and string termination */ - notifystr = strdup(""); - notifystr = append_string(notifystr, TEXT_PLAY_PAUSE_STATE, 0, state == MPD_STATE_PLAY ? "Playing": "Paused"); - notifystr = append_string(notifystr, TEXT_PLAY_PAUSE_TITLE, 0, title); - - if ((artist = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)) != NULL) - notifystr = append_string(notifystr, TEXT_PLAY_PAUSE_ARTIST, oneline ? ' ' : '\n', artist); - - if ((album = mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)) != NULL) - notifystr = append_string(notifystr, TEXT_PLAY_PAUSE_ALBUM, oneline ? ' ' : '\n', album); - - uri = mpd_song_get_uri(song); - - if (music_dir != NULL && uri != NULL) { - GdkPixbuf * copy; - - pixbuf = retrieve_artwork(music_dir, uri); - - if (pixbuf != NULL && scale > 0) { - int x, y; - - x = gdk_pixbuf_get_width(pixbuf); - y = gdk_pixbuf_get_height(pixbuf); - - if ((copy = gdk_pixbuf_scale_simple (pixbuf, - (x > y ? scale : scale * x / y), - (y > x ? scale : scale * y / x), - GDK_INTERP_BILINEAR)) != NULL) { - g_object_unref(pixbuf); - pixbuf = copy; - } - } - - - } - - mpd_song_free(song); - } else if (state == MPD_STATE_STOP) { - notifystr = strdup(TEXT_STOP); -#ifdef HAVE_SYSTEMD - sd_notify(0, "READY=1\nSTATUS=" TEXT_STOP); -#endif - } else - notifystr = strdup(TEXT_UNKNOWN); - - last_state = state; - - if (verbose > 0) - printf("%s: %s\n", program, notifystr); - - /* Some notification daemons do not support handing pixbuf data. Write a PNG - * file and give the path. */ - if (file_workaround > 0 && pixbuf != NULL) { - gdk_pixbuf_save(pixbuf, "/tmp/.mpd-notification-artwork.png", "png", NULL, NULL); - - notify_notification_update(notification, TEXT_TOPIC, notifystr, "/tmp/.mpd-notification-artwork.png"); - } else - notify_notification_update(notification, TEXT_TOPIC, notifystr, ICON_AUDIO_X_GENERIC); - - /* Call this unconditionally! When pixbuf is NULL this clears old image. */ - notify_notification_set_image_from_pixbuf(notification, pixbuf); + printf("Entering while\n"); + if (!update_notification(false)) + { + continue; + } if (notify_notification_show(notification, &error) == FALSE) { g_printerr("%s: Error showing notification: %s\n", program, error->message); @@ -488,18 +565,8 @@ int main(int argc, char ** argv) { goto out10; } - -nonotification: - if (notifystr != NULL) { - free(notifystr); - notifystr = NULL; - } - if (pixbuf != NULL) { - g_object_unref(pixbuf); - pixbuf = NULL; - } - mpd_response_finish(conn); } + printf("While quited\n"); if (verbose > 0) printf("%s: Exiting...\n", program); diff --git a/mpd-notification.h b/mpd-notification.h index 5754c18..6154d54 100644 --- a/mpd-notification.h +++ b/mpd-notification.h @@ -37,7 +37,7 @@ #define PROGNAME "mpd-notification" -#define OPT_FILE_WORKAROUND UCHAR_MAX + 1 +#define OPT_FILE_WORKAROUND UCHAR_MAX + 1 /*** received_signal ***/ void received_signal(int signal); @@ -48,6 +48,20 @@ GdkPixbuf * retrieve_artwork(const char * music_dir, const char * uri); /*** append_string ***/ char * append_string(char * string, const char * format, const char delim, const char * s); +/** + * Update the notification object stored. Adds elapsed time it needed, otherwise + * adds total time. + * + * \param music_dir The directory used by mpd + * \param image_scale The maximum side size of the picture used in the + * notification. The aspect ratio between the two side is kept. + * \param notificaiton_image_workaround If positive, the notification image + * will be passed to the notification system as a path to a png file, + * otherwise it will be passed as a GdkPixbuf. + * \return 1 if the notification as been succesfully updated, 0 otherwise. + */ +int update_notification(int show_elapsed_time); + /*** main ***/ int main(int argc, char ** argv);