diff -Nru pianobar-2017.08.30/ChangeLog pianobar-2018.06.22/ChangeLog --- pianobar-2017.08.30/ChangeLog 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/ChangeLog 2018-06-28 10:04:49.000000000 +0000 @@ -1,3 +1,10 @@ +Release 2018.06.22 + +- Happy 10th birthday pianobar! +- Add network timeouts and retries +- Fix cross-thread memory access +- Misc UI and documentation improvements + Release 2017.08.30 - Support binding to specific network interface, see config option bind_to diff -Nru pianobar-2017.08.30/contrib/config-example pianobar-2018.06.22/contrib/config-example --- pianobar-2017.08.30/contrib/config-example 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/contrib/config-example 2018-06-28 10:04:49.000000000 +0000 @@ -46,19 +46,19 @@ #event_command = /home/user/.config/pianobar/eventcmd #fifo = /tmp/pianobar #sort = quickmix_10_name_az -#love_icon = [+] -#ban_icon = [-] #volume = 0 #ca_bundle = /etc/ssl/certs/ca-certificates.crt #gain_mul = 1.0 # Format strings #format_nowplaying_song = %t by %a on %l%r%@%s +#format_nowplaying_song = %t by %a on %l%r%@%s +#ban_icon = > $STDIN diff -Nru pianobar-2017.08.30/contrib/pianobar.1 pianobar-2018.06.22/contrib/pianobar.1 --- pianobar-2017.08.30/contrib/pianobar.1 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/contrib/pianobar.1 2018-06-28 10:04:49.000000000 +0000 @@ -194,7 +194,7 @@ .B act_songinfo. .TP -.B ban_icon = Sun, 29 Jul 2018 23:33:01 -0400 + +pianobar (2017.08.30-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * debian/patches: Fix build against ffmpeg 4.0. (Closes: #888385) + + -- Sebastian Ramacher Wed, 11 Jul 2018 21:19:06 +0200 + +pianobar (2017.08.30-1) unstable; urgency=medium * New upstream release. * Update Standards-Version to 4.1.0. diff -Nru pianobar-2017.08.30/debian/compat pianobar-2018.06.22/debian/compat --- pianobar-2017.08.30/debian/compat 2017-08-30 15:54:29.000000000 +0000 +++ pianobar-2018.06.22/debian/compat 2018-07-30 03:33:01.000000000 +0000 @@ -1 +1 @@ -9 +11 diff -Nru pianobar-2017.08.30/debian/control pianobar-2018.06.22/debian/control --- pianobar-2017.08.30/debian/control 2017-08-30 15:54:29.000000000 +0000 +++ pianobar-2018.06.22/debian/control 2018-07-30 03:33:01.000000000 +0000 @@ -6,7 +6,7 @@ Romain Beauxis , Unit 193 Build-Depends: - debhelper (>= 9), + debhelper (>= 11), libao-dev, libavcodec-dev, libavfilter-dev, @@ -17,9 +17,9 @@ libjson-c-dev, libpth-dev, pkg-config -Standards-Version: 4.1.0 -Vcs-Bzr: https://anonscm.debian.org/bzr/collab-maint/pianobar -Vcs-Browser: https://anonscm.debian.org/loggerhead/collab-maint/pianobar +Standards-Version: 4.2.0 +Vcs-Git: https://salsa.debian.org/debian/pianobar.git +Vcs-Browser: https://salsa.debian.org/debian/pianobar Homepage: https://6xq.net/pianobar/ Package: pianobar diff -Nru pianobar-2017.08.30/debian/rules pianobar-2018.06.22/debian/rules --- pianobar-2017.08.30/debian/rules 2017-08-30 15:54:29.000000000 +0000 +++ pianobar-2018.06.22/debian/rules 2018-07-30 03:33:01.000000000 +0000 @@ -1,8 +1,8 @@ #!/usr/bin/make -f #export DH_VERBOSE=1 -DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) -export DEB_BUILD_MAINT_OPTIONS=hardening=+all,-pie +include /usr/share/dpkg/architecture.mk +export DEB_BUILD_MAINT_OPTIONS=hardening=+all export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed export V=1 @@ -15,11 +15,11 @@ override_dh_installchangelogs: dh_installchangelogs ChangeLog -override_dh_install: - dh_install --list-missing - override_dh_auto_install: dh_auto_install -- DYNLINK=1 PREFIX=/usr LIBDIR=/usr/lib/$(DEB_HOST_MULTIARCH)/ +override_dh_missing: + dh_missing --list-missing + %: dh $@ diff -Nru pianobar-2017.08.30/README.md pianobar-2018.06.22/README.md --- pianobar-2017.08.30/README.md 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/README.md 2018-06-28 10:04:49.000000000 +0000 @@ -1,7 +1,6 @@ # pianobar -pianobar is a console client for the personalized web radio [Pandora] -([http://www.pandora.com](http://www.pandora.com)). +pianobar is a console client for the personalized web radio [Pandora](http://www.pandora.com). ### Features diff -Nru pianobar-2017.08.30/src/config.h pianobar-2018.06.22/src/config.h --- pianobar-2017.08.30/src/config.h 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/config.h 2018-06-28 10:04:49.000000000 +0000 @@ -3,7 +3,7 @@ /* package name */ #define PACKAGE "pianobar" -#define VERSION "2017.08.30" +#define VERSION "2018.06.22" /* glibc feature test macros, define _before_ including other files */ #define _POSIX_C_SOURCE 200809L diff -Nru pianobar-2017.08.30/src/libpiano/piano.c pianobar-2018.06.22/src/libpiano/piano.c --- pianobar-2017.08.30/src/libpiano/piano.c 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/libpiano/piano.c 2018-06-28 10:04:49.000000000 +0000 @@ -219,7 +219,11 @@ */ PianoStation_t *PianoFindStationById (PianoStation_t * const stations, const char * const searchStation) { - assert (searchStation != NULL); + assert (stations != NULL); + + if (searchStation == NULL) { + return NULL; + } PianoStation_t *currStation = stations; PianoListForeachP (currStation) { diff -Nru pianobar-2017.08.30/src/libpiano/piano.h pianobar-2018.06.22/src/libpiano/piano.h --- pianobar-2017.08.30/src/libpiano/piano.h 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/libpiano/piano.h 2018-06-28 10:04:49.000000000 +0000 @@ -65,7 +65,8 @@ typedef enum { PIANO_RATE_NONE = 0, PIANO_RATE_LOVE = 1, - PIANO_RATE_BAN = 2 + PIANO_RATE_BAN = 2, + PIANO_RATE_TIRED = 3, } PianoSongRating_t; /* UNKNOWN should be 0, because memset sets audio format to 0 */ diff -Nru pianobar-2017.08.30/src/libpiano/request.c pianobar-2018.06.22/src/libpiano/request.c --- pianobar-2017.08.30/src/libpiano/request.c 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/libpiano/request.c 2018-06-28 10:04:49.000000000 +0000 @@ -116,6 +116,7 @@ case PIANO_REQUEST_GET_STATIONS: { /* get stations, user must be authenticated */ assert (ph->user.listenerId != NULL); + method = "user.getStationList"; break; } @@ -146,7 +147,8 @@ assert (reqData != NULL); assert (reqData->trackToken != NULL); assert (reqData->stationId != NULL); - assert (reqData->rating != PIANO_RATE_NONE); + assert (reqData->rating != PIANO_RATE_NONE && + reqData->rating != PIANO_RATE_TIRED); json_object_object_add (j, "stationToken", json_object_new_string (reqData->stationId)); @@ -357,6 +359,8 @@ json_object_new_string (reqData->station->id)); json_object_object_add (j, "includeExtendedAttributes", json_object_new_boolean (true)); + json_object_object_add (j, "includeExtraParams", + json_object_new_boolean (true)); method = "station.getStation"; break; diff -Nru pianobar-2017.08.30/src/libpiano/response.c pianobar-2018.06.22/src/libpiano/response.c --- pianobar-2017.08.30/src/libpiano/response.c 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/libpiano/response.c 2018-06-28 10:04:49.000000000 +0000 @@ -436,8 +436,13 @@ break; } + case PIANO_REQUEST_ADD_TIRED_SONG: { + PianoSong_t * const song = req->data; + song->rating = PIANO_RATE_TIRED; + break; + } + case PIANO_REQUEST_ADD_SEED: - case PIANO_REQUEST_ADD_TIRED_SONG: case PIANO_REQUEST_SET_QUICKMIX: case PIANO_REQUEST_BOOKMARK_SONG: case PIANO_REQUEST_BOOKMARK_ARTIST: @@ -634,6 +639,11 @@ feedbackSong->rating = getBoolDefault (s, "isPositive", false) ? PIANO_RATE_LOVE : PIANO_RATE_BAN; + json_object *v; + feedbackSong->length = + json_object_object_get_ex (s, "trackLength", &v) ? + json_object_get_int (v) : 0; + info->feedback = PianoListAppendP (info->feedback, feedbackSong); } diff -Nru pianobar-2017.08.30/src/main.c pianobar-2018.06.22/src/main.c --- pianobar-2017.08.30/src/main.c 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/main.c 2018-06-28 10:04:49.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2013 +Copyright (c) 2008-2018 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy @@ -252,15 +252,12 @@ strncmp (curSong->audioUrl, httpPrefix, strlen (httpPrefix)) != 0) { BarUiMsg (&app->settings, MSG_ERR, "Invalid song url.\n"); } else { - /* setup player */ - memset (&app->player, 0, sizeof (app->player)); + player_t * const player = &app->player; + BarPlayerReset (player); app->player.url = curSong->audioUrl; app->player.gain = curSong->fileGain; - app->player.settings = &app->settings; app->player.songDuration = curSong->length; - pthread_mutex_init (&app->player.pauseMutex, NULL); - pthread_cond_init (&app->player.pauseCond, NULL); assert (interrupted == &app->doQuit); interrupted = &app->player.interrupted; @@ -291,14 +288,12 @@ /* FIXME: pthread_join blocks everything if network connection * is hung up e.g. */ pthread_join (*playerThread, &threadRet); - pthread_cond_destroy (&app->player.pauseCond); - pthread_mutex_destroy (&app->player.pauseMutex); if (threadRet == (void *) PLAYER_RET_OK) { app->playerErrors = 0; } else if (threadRet == (void *) PLAYER_RET_SOFTFAIL) { ++app->playerErrors; - if (app->playerErrors >= app->settings.maxPlayerErrors) { + if (app->playerErrors >= app->settings.maxRetry) { /* don't continue playback if thread reports too many error */ app->nextStation = NULL; } @@ -306,10 +301,10 @@ app->nextStation = NULL; } - memset (&app->player, 0, sizeof (app->player)); - assert (interrupted == &app->player.interrupted); interrupted = &app->doQuit; + + app->player.mode = PLAYER_DEAD; } /* print song duration @@ -317,19 +312,24 @@ static void BarMainPrintTime (BarApp_t *app) { unsigned int songRemaining; char sign; + player_t * const player = &app->player; + + pthread_mutex_lock (&player->lock); + const unsigned int songDuration = player->songDuration; + const unsigned int songPlayed = player->songPlayed; + pthread_mutex_unlock (&player->lock); - if (app->player.songPlayed <= app->player.songDuration) { - songRemaining = app->player.songDuration - app->player.songPlayed; + if (songPlayed <= songDuration) { + songRemaining = songDuration - songPlayed; sign = '-'; } else { /* longer than expected */ - songRemaining = app->player.songPlayed - app->player.songDuration; + songRemaining = songPlayed - songDuration; sign = '+'; } BarUiMsg (&app->settings, MSG_TIME, "%c%02u:%02u/%02u:%02u\r", sign, songRemaining / 60, songRemaining % 60, - app->player.songDuration / 60, - app->player.songDuration % 60); + songDuration / 60, songDuration % 60); } /* main loop @@ -351,14 +351,12 @@ BarMainGetInitialStation (app); - /* little hack, needed to signal: hey! we need a playlist, but don't - * free anything (there is nothing to be freed yet) */ - memset (&app->player, 0, sizeof (app->player)); + player_t * const player = &app->player; while (!app->doQuit) { /* song finished playing, clean up things/scrobble song */ - if (app->player.mode == PLAYER_FINISHED) { - if (app->player.interrupted != 0) { + if (BarPlayerGetMode (player) == PLAYER_FINISHED) { + if (player->interrupted != 0) { app->doQuit = 1; } BarMainPlayerCleanup (app, &playerThread); @@ -366,7 +364,7 @@ /* check whether player finished playing and start playing new * song */ - if (app->player.mode == PLAYER_DEAD) { + if (BarPlayerGetMode (player) == PLAYER_DEAD) { /* what's next? */ if (app->playlist != NULL) { PianoSong_t *histsong = app->playlist; @@ -389,12 +387,12 @@ BarMainHandleUserInput (app); /* show time */ - if (app->player.mode == PLAYER_PLAYING) { + if (BarPlayerGetMode (player) == PLAYER_PLAYING) { BarMainPrintTime (app); } } - if (app->player.mode != PLAYER_DEAD) { + if (BarPlayerGetMode (player) != PLAYER_DEAD) { pthread_join (playerThread, NULL); } } @@ -433,7 +431,7 @@ gcry_check_version (NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); - BarPlayerInit (); + BarPlayerInit (&app.player, &app.settings); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); @@ -502,7 +500,7 @@ PianoDestroyPlaylist (app.playlist); curl_easy_cleanup (app.http); curl_global_cleanup (); - BarPlayerDestroy (); + BarPlayerDestroy (&app.player); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ diff -Nru pianobar-2017.08.30/src/player.c pianobar-2018.06.22/src/player.c --- pianobar-2017.08.30/src/player.c 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/player.c 2018-06-28 10:04:49.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2014 +Copyright (c) 2008-2018 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #ifdef HAVE_LIBAVFILTER_AVCODEC_H @@ -66,19 +65,46 @@ * XXX: in theory we can select the filters/formats we want to support, but * this does not work in practise. */ -void BarPlayerInit () { +void BarPlayerInit (player_t * const p, const BarSettings_t * const settings) { ao_initialize (); av_log_set_level (AV_LOG_FATAL); av_register_all (); avfilter_register_all (); avformat_network_init (); + + pthread_mutex_init (&p->lock, NULL); + pthread_cond_init (&p->cond, NULL); + BarPlayerReset (p); + p->settings = settings; } -void BarPlayerDestroy () { +void BarPlayerDestroy (player_t * const p) { + pthread_cond_destroy (&p->cond); + pthread_mutex_destroy (&p->lock); + avformat_network_deinit (); ao_shutdown (); } +void BarPlayerReset (player_t * const p) { + p->doQuit = false; + p->doPause = false; + p->songDuration = 0; + p->songPlayed = 0; + p->mode = PLAYER_DEAD; + p->fvolume = NULL; + p->fgraph = NULL; + p->fctx = NULL; + p->st = NULL; + p->cctx = NULL; + p->fbufsink = NULL; + p->fabuf = NULL; + p->streamIdx = -1; + p->lastTimestamp = 0; + p->interrupted = 0; + p->aoDev = NULL; +} + /* Update volume filter */ void BarPlayerSetVolume (player_t * const player) { @@ -122,7 +148,9 @@ assert (player != NULL); if (player->interrupted > 1) { /* got a sigint multiple times, quit pianobar (handled by main.c). */ + pthread_mutex_lock (&player->lock); player->doQuit = true; + pthread_mutex_unlock (&player->lock); return 1; } else if (player->interrupted != 0) { /* the request is retried with the same player context */ @@ -145,8 +173,16 @@ player->fctx->interrupt_callback.callback = intCb; player->fctx->interrupt_callback.opaque = player; + /* in microseconds */ + unsigned long int timeout = player->settings->timeout*1000000; + char timeoutStr[16]; + ret = snprintf (timeoutStr, sizeof (timeoutStr), "%lu", timeout); + assert (ret < sizeof (timeoutStr)); + AVDictionary *options = NULL; + av_dict_set (&options, "timeout", timeoutStr, 0); + assert (player->url != NULL); - if ((ret = avformat_open_input (&player->fctx, player->url, NULL, NULL)) < 0) { + if ((ret = avformat_open_input (&player->fctx, player->url, NULL, &options)) < 0) { softfail ("Unable to open audio file"); } @@ -190,9 +226,12 @@ av_seek_frame (player->fctx, player->streamIdx, player->lastTimestamp, 0); } - player->songPlayed = 0; - player->songDuration = av_q2d (player->st->time_base) * + const unsigned int songDuration = av_q2d (player->st->time_base) * (double) player->st->duration; + pthread_mutex_lock (&player->lock); + player->songPlayed = 0; + player->songDuration = songDuration; + pthread_mutex_unlock (&player->lock); return true; } @@ -283,6 +322,29 @@ return true; } +/* Operating on shared variables and must be protected by mutex + */ + +static bool shouldQuit (player_t * const player) { + pthread_mutex_lock (&player->lock); + const bool ret = player->doQuit; + pthread_mutex_unlock (&player->lock); + return ret; +} + +static void changeMode (player_t * const player, unsigned int mode) { + pthread_mutex_lock (&player->lock); + player->mode = mode; + pthread_mutex_unlock (&player->lock); +} + +BarPlayerMode BarPlayerGetMode (player_t * const player) { + pthread_mutex_lock (&player->lock); + const BarPlayerMode ret = player->mode; + pthread_mutex_unlock (&player->lock); + return ret; +} + /* decode and play stream. returns 0 or av error code. */ static int play (player_t * const player) { @@ -302,7 +364,7 @@ enum { FILL, DRAIN, DONE } drainMode = FILL; int ret = 0; - while (!player->doQuit && drainMode != DONE) { + while (!shouldQuit (player) && drainMode != DONE) { if (drainMode == FILL) { ret = av_read_frame (player->fctx, &pkt); if (ret == AVERROR_EOF) { @@ -323,17 +385,17 @@ } /* pausing */ - pthread_mutex_lock (&player->pauseMutex); + pthread_mutex_lock (&player->lock); if (player->doPause) { av_read_pause (player->fctx); do { - pthread_cond_wait (&player->pauseCond, &player->pauseMutex); + pthread_cond_wait (&player->cond, &player->lock); } while (player->doPause); av_read_play (player->fctx); } - pthread_mutex_unlock (&player->pauseMutex); + pthread_mutex_unlock (&player->lock); - while (!player->doQuit) { + while (!shouldQuit (player)) { ret = avcodec_receive_frame (cctx, frame); if (ret == AVERROR_EOF) { /* done draining */ @@ -367,7 +429,10 @@ } } - player->songPlayed = av_q2d (player->st->time_base) * (double) pkt.pts; + const unsigned int songPlayed = av_q2d (player->st->time_base) * (double) pkt.pts; + pthread_mutex_lock (&player->lock); + player->songPlayed = songPlayed; + pthread_mutex_unlock (&player->lock); player->lastTimestamp = pkt.pts; av_packet_unref (&pkt); @@ -410,7 +475,7 @@ retry = false; if (openStream (player)) { if (openFilter (player) && openDevice (player)) { - player->mode = PLAYER_PLAYING; + changeMode (player, PLAYER_PLAYING); BarPlayerSetVolume (player); retry = play (player) == AVERROR_INVALIDDATA && !player->interrupted; @@ -422,11 +487,11 @@ /* stream not found */ pret = PLAYER_RET_SOFTFAIL; } - player->mode = PLAYER_WAITING; + changeMode (player, PLAYER_WAITING); finish (player); } while (retry); - player->mode = PLAYER_FINISHED; + changeMode (player, PLAYER_FINISHED); return (void *) pret; } diff -Nru pianobar-2017.08.30/src/player.h pianobar-2018.06.22/src/player.h --- pianobar-2017.08.30/src/player.h 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/player.h 2018-06-28 10:04:49.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2014 +Copyright (c) 2008-2018 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy @@ -34,28 +34,35 @@ #include #include #include -#include #include #include "settings.h" +typedef enum { + /* not running */ + PLAYER_DEAD = 0, + /* running, but not ready to play music yet */ + PLAYER_WAITING, + /* currently playing a song */ + PLAYER_PLAYING, + /* finished playing a song */ + PLAYER_FINISHED, +} BarPlayerMode; + typedef struct { - /* protected by pauseMutex */ - volatile bool doQuit; - volatile bool doPause; - pthread_mutex_t pauseMutex; - pthread_cond_t pauseCond; - - enum { - /* not running */ - PLAYER_DEAD = 0, - /* running, but not ready to play music yet */ - PLAYER_WAITING, - /* currently playing a song */ - PLAYER_PLAYING, - /* finished playing a song */ - PLAYER_FINISHED, - } mode; + /* public attributes protected by mutex */ + pthread_mutex_t lock; + pthread_cond_t cond; /* broadcast changes to doPause */ + + bool doQuit, doPause; + + /* measured in seconds */ + unsigned int songDuration; + unsigned int songPlayed; + + BarPlayerMode mode; + + /* private attributes _not_ protected by mutex */ /* libav */ AVFilterContext *fvolume; @@ -70,20 +77,18 @@ ao_device *aoDev; - /* settings */ + /* settings (must be set before starting the thread) */ double gain; char *url; const BarSettings_t *settings; - - /* measured in seconds */ - volatile unsigned int songDuration; - volatile unsigned int songPlayed; } player_t; enum {PLAYER_RET_OK = 0, PLAYER_RET_HARDFAIL = 1, PLAYER_RET_SOFTFAIL = 2}; void *BarPlayerThread (void *data); void BarPlayerSetVolume (player_t * const player); -void BarPlayerInit (); -void BarPlayerDestroy (); +void BarPlayerInit (player_t * const p, const BarSettings_t * const settings); +void BarPlayerReset (player_t * const p); +void BarPlayerDestroy (player_t * const p); +BarPlayerMode BarPlayerGetMode (player_t * const player); diff -Nru pianobar-2017.08.30/src/settings.c pianobar-2018.06.22/src/settings.c --- pianobar-2017.08.30/src/settings.c 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/settings.c 2018-06-28 10:04:49.000000000 +0000 @@ -119,6 +119,7 @@ free (settings->eventCmd); free (settings->loveIcon); free (settings->banIcon); + free (settings->tiredIcon); free (settings->atIcon); free (settings->npSongFormat); free (settings->npStationFormat); @@ -160,11 +161,13 @@ settings->autoselect = true; settings->history = 5; settings->volume = 0; + settings->timeout = 30; /* seconds */ settings->gainMul = 1.0; - settings->maxPlayerErrors = 5; + settings->maxRetry = 3; settings->sortOrder = BAR_SORT_NAME_AZ; settings->loveIcon = strdup (" <3"); settings->banIcon = strdup (" tiredIcon = strdup (" zZ"); settings->atIcon = strdup (" @ "); settings->npSongFormat = strdup ("\"%t\" by \"%a\" on \"%l\"%r%@%s"); settings->npStationFormat = strdup ("Station \"%n\" (%i)"); @@ -335,8 +338,10 @@ settings->eventCmd = BarSettingsExpandTilde (val, userhome); } else if (streq ("history", key)) { settings->history = atoi (val); - } else if (streq ("max_player_errors", key)) { - settings->maxPlayerErrors = atoi (val); + } else if (streq ("max_retry", key)) { + settings->maxRetry = atoi (val); + } else if (streq ("timeout", key)) { + settings->timeout = atoi (val); } else if (streq ("sort", key)) { size_t i; static const char *mapping[] = {"name_az", @@ -358,6 +363,9 @@ } else if (streq ("ban_icon", key)) { free (settings->banIcon); settings->banIcon = strdup (val); + } else if (streq ("tired_icon", key)) { + free (settings->tiredIcon); + settings->tiredIcon = strdup (val); } else if (streq ("at_icon", key)) { free (settings->atIcon); settings->atIcon = strdup (val); diff -Nru pianobar-2017.08.30/src/settings.h pianobar-2018.06.22/src/settings.h --- pianobar-2017.08.30/src/settings.h 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/settings.h 2018-06-28 10:04:49.000000000 +0000 @@ -84,7 +84,7 @@ typedef struct { bool autoselect; - unsigned int history, maxPlayerErrors; + unsigned int history, maxRetry, timeout; int volume; float gainMul; BarStationSorting_t sortOrder; @@ -96,8 +96,7 @@ char *bindTo; char *autostartStation; char *eventCmd; - char *loveIcon; - char *banIcon; + char *loveIcon, *banIcon, *tiredIcon; char *atIcon; char *npSongFormat; char *npStationFormat; diff -Nru pianobar-2017.08.30/src/ui_act.c pianobar-2018.06.22/src/ui_act.c --- pianobar-2017.08.30/src/ui_act.c 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/ui_act.c 2018-06-28 10:04:49.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2013 +Copyright (c) 2008-2018 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy @@ -52,11 +52,11 @@ static inline void BarUiDoSkipSong (player_t * const player) { assert (player != NULL); - pthread_mutex_lock (&player->pauseMutex); + pthread_mutex_lock (&player->lock); player->doQuit = true; player->doPause = false; - pthread_cond_broadcast (&player->pauseCond); - pthread_mutex_unlock (&player->pauseMutex); + pthread_cond_broadcast (&player->cond); + pthread_mutex_unlock (&player->lock); } /* transform station if necessary to allow changes like rename, rate, ... @@ -419,28 +419,28 @@ /* play */ BarUiActCallback(BarUiActPlay) { - pthread_mutex_lock (&app->player.pauseMutex); + pthread_mutex_lock (&app->player.lock); app->player.doPause = false; - pthread_cond_broadcast (&app->player.pauseCond); - pthread_mutex_unlock (&app->player.pauseMutex); + pthread_cond_broadcast (&app->player.cond); + pthread_mutex_unlock (&app->player.lock); } /* pause */ BarUiActCallback(BarUiActPause) { - pthread_mutex_lock (&app->player.pauseMutex); + pthread_mutex_lock (&app->player.lock); app->player.doPause = true; - pthread_cond_broadcast (&app->player.pauseCond); - pthread_mutex_unlock (&app->player.pauseMutex); + pthread_cond_broadcast (&app->player.cond); + pthread_mutex_unlock (&app->player.lock); } /* toggle pause */ BarUiActCallback(BarUiActTogglePause) { - pthread_mutex_lock (&app->player.pauseMutex); + pthread_mutex_lock (&app->player.lock); app->player.doPause = !app->player.doPause; - pthread_cond_broadcast (&app->player.pauseCond); - pthread_mutex_unlock (&app->player.pauseMutex); + pthread_cond_broadcast (&app->player.cond); + pthread_mutex_unlock (&app->player.lock); } /* rename current station @@ -505,7 +505,7 @@ BarUiActCallback(BarUiActPrintUpcoming) { PianoSong_t * const nextSong = PianoListNextP (selSong); if (nextSong != NULL) { - BarUiListSongs (&app->settings, nextSong, NULL); + BarUiListSongs (app, nextSong, NULL); } else { BarUiMsg (&app->settings, MSG_INFO, "No songs in queue.\n"); } @@ -587,7 +587,7 @@ PianoSong_t *histSong; if (app->songHistory != NULL) { - histSong = BarUiSelectSong (&app->settings, app->songHistory, + histSong = BarUiSelectSong (app, app->songHistory, &app->input); if (histSong != NULL) { BarKeyShortcutId_t action; @@ -842,7 +842,7 @@ BarUiActDefaultEventcmd ("stationdeleteartistseed"); } } else if (selectBuf[0] == 's') { - PianoSong_t *song = BarUiSelectSong (&app->settings, + PianoSong_t *song = BarUiSelectSong (app, reqData.info.songSeeds, &app->input); if (song != NULL) { PianoRequestDataDeleteSeed_t subReqData; @@ -869,7 +869,7 @@ BarUiActDefaultEventcmd ("stationdeletestationseed"); } } else if (selectBuf[0] == 'f') { - PianoSong_t *song = BarUiSelectSong (&app->settings, + PianoSong_t *song = BarUiSelectSong (app, reqData.info.feedback, &app->input); if (song != NULL) { BarUiMsg (&app->settings, MSG_INFO, "Deleting feedback... "); diff -Nru pianobar-2017.08.30/src/ui.c pianobar-2018.06.22/src/ui.c --- pianobar-2017.08.30/src/ui.c 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/ui.c 2018-06-28 10:04:49.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2013 +Copyright (c) 2008-2018 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy @@ -205,6 +205,7 @@ setAndCheck (CURLOPT_PROGRESSDATA, &lint); setAndCheck (CURLOPT_NOPROGRESS, 0); setAndCheck (CURLOPT_POST, 1); + setAndCheck (CURLOPT_TIMEOUT, settings->timeout); if (settings->caBundle != NULL) { setAndCheck (CURLOPT_CAINFO, settings->caBundle); } @@ -241,7 +242,21 @@ list = curl_slist_append (list, "Content-Type: text/plain"); setAndCheck (CURLOPT_HTTPHEADER, list); - httpret = curl_easy_perform (http); + unsigned int retry = 0; + do { + httpret = curl_easy_perform (http); + ++retry; + if (httpret == CURLE_OPERATION_TIMEDOUT) { + free (buffer.data); + buffer.data = NULL; + buffer.pos = 0; + if (retry > settings->maxRetry) { + break; + } + } else { + break; + } + } while (true); curl_slist_free_all (list); @@ -486,20 +501,21 @@ } /* let user pick one song - * @param pianobar settings + * @param app * @param song list * @param input fds * @return pointer to selected item in song list or NULL */ -PianoSong_t *BarUiSelectSong (const BarSettings_t *settings, +PianoSong_t *BarUiSelectSong (const BarApp_t * const app, PianoSong_t *startSong, BarReadlineFds_t *input) { + const BarSettings_t * const settings = &app->settings; PianoSong_t *tmpSong = NULL; char buf[100]; memset (buf, 0, sizeof (buf)); do { - BarUiListSongs (settings, startSong, buf); + BarUiListSongs (app, startSong, buf); BarUiMsg (settings, MSG_QUESTION, "Select song: "); if (BarReadlineStr (buf, sizeof (buf), input, BAR_RL_DEFAULT) == 0) { @@ -598,7 +614,7 @@ musicId = strdup (tmpArtist->musicId); } } else if (*selectBuf == 't') { - tmpSong = BarUiSelectSong (&app->settings, searchResult.songs, + tmpSong = BarUiSelectSong (app, searchResult.songs, &app->input); if (tmpSong != NULL) { musicId = strdup (tmpSong->musicId); @@ -606,7 +622,7 @@ } } else if (searchResult.songs != NULL) { /* songs found */ - tmpSong = BarUiSelectSong (&app->settings, searchResult.songs, + tmpSong = BarUiSelectSong (app, searchResult.songs, &app->input); if (tmpSong != NULL) { musicId = strdup (tmpSong->musicId); @@ -714,6 +730,23 @@ BarUiMsg (settings, MSG_PLAYING, "%s", outstr); } +static const char *ratingToIcon (const BarSettings_t * const settings, + const PianoSong_t * const song) { + switch (song->rating) { + case PIANO_RATE_LOVE: + return settings->loveIcon; + + case PIANO_RATE_BAN: + return settings->banIcon; + + case PIANO_RATE_TIRED: + return settings->tiredIcon; + + default: + return ""; + } +} + /* Print song infos (artist, title, album, loved) * @param pianobar settings * @param the song @@ -723,7 +756,7 @@ const PianoSong_t *song, const PianoStation_t *station) { char outstr[512]; const char *vals[] = {song->title, song->artist, song->album, - (song->rating == PIANO_RATE_LOVE) ? settings->loveIcon : "", + ratingToIcon (settings, song), station != NULL ? settings->atIcon : "", station != NULL ? station->name : "", song->detailUrl}; @@ -740,23 +773,44 @@ * @param artist/song filter string * @return # of songs */ -size_t BarUiListSongs (const BarSettings_t *settings, +size_t BarUiListSongs (const BarApp_t * const app, const PianoSong_t *song, const char *filter) { + const BarSettings_t * const settings = &app->settings; size_t i = 0; - char digits[8]; PianoListForeachP (song) { if (filter == NULL || (filter != NULL && (BarStrCaseStr (song->artist, filter) != NULL || BarStrCaseStr (song->title, filter) != NULL))) { - char outstr[512]; + const char * const deleted = "(deleted)", * const empty = ""; + const char *stationName = empty; + + const PianoStation_t * const station = + PianoFindStationById (app->ph.stations, song->stationId); + if (station != NULL && station != app->curStation) { + stationName = station->name; + } else if (station == NULL && song->stationId != NULL) { + stationName = deleted; + } + + char outstr[512], digits[8], duration[8] = "??:??"; const char *vals[] = {digits, song->artist, song->title, - (song->rating == PIANO_RATE_LOVE) ? settings->loveIcon : - ((song->rating == PIANO_RATE_BAN) ? settings->banIcon : "")}; + ratingToIcon (settings, song), + duration, + stationName != empty ? settings->atIcon : "", + stationName, + }; + /* pre-format a few strings */ snprintf (digits, sizeof (digits) / sizeof (*digits), "%2zu", i); + const unsigned int length = song->length; + if (length > 0) { + snprintf (duration, sizeof (duration), "%02u:%02u", + length / 60, length % 60); + } + BarUiCustomFormat (outstr, sizeof (outstr), settings->listSongFormat, - "iatr", vals); + "iatrd@s", vals); BarUiAppendNewline (outstr, sizeof (outstr)); BarUiMsg (settings, MSG_LIST, "%s", outstr); } @@ -775,7 +829,7 @@ */ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type, const PianoStation_t *curStation, const PianoSong_t *curSong, - const player_t * const player, PianoStation_t *stations, + player_t * const player, PianoStation_t *stations, PianoReturn_t pRet, CURLcode wRet) { pid_t chld; int pipeFd[2]; @@ -816,6 +870,11 @@ songStation = PianoFindStationById (stations, curSong->stationId); } + pthread_mutex_lock (&player->lock); + const unsigned int songDuration = player->songDuration; + const unsigned int songPlayed = player->songPlayed; + pthread_mutex_unlock (&player->lock); + fprintf (pipeWriteFd, "artist=%s\n" "title=%s\n" @@ -841,8 +900,8 @@ PianoErrorToStr (pRet), wRet, curl_easy_strerror (wRet), - player->songDuration, - player->songPlayed, + songDuration, + songPlayed, curSong == NULL ? PIANO_RATE_NONE : curSong->rating, curSong == NULL ? "" : curSong->detailUrl ); diff -Nru pianobar-2017.08.30/src/ui.h pianobar-2018.06.22/src/ui.h --- pianobar-2017.08.30/src/ui.h 2017-08-30 14:42:37.000000000 +0000 +++ pianobar-2018.06.22/src/ui.h 2018-06-28 10:04:49.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2011 +Copyright (c) 2008-2018 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy @@ -38,16 +38,17 @@ void BarUiMsg (const BarSettings_t *, const BarUiMsg_t, const char *, ...) __attribute__((format(printf, 3, 4))); PianoStation_t *BarUiSelectStation (BarApp_t *, PianoStation_t *, const char *, BarUiSelectStationCallback_t, bool); -PianoSong_t *BarUiSelectSong (const BarSettings_t *, PianoSong_t *, - BarReadlineFds_t *); +PianoSong_t *BarUiSelectSong (const BarApp_t * const app, + PianoSong_t *startSong, BarReadlineFds_t *input); PianoArtist_t *BarUiSelectArtist (BarApp_t *, PianoArtist_t *); char *BarUiSelectMusicId (BarApp_t *, PianoStation_t *, const char *); void BarUiPrintStation (const BarSettings_t *, PianoStation_t *); void BarUiPrintSong (const BarSettings_t *, const PianoSong_t *, const PianoStation_t *); -size_t BarUiListSongs (const BarSettings_t *, const PianoSong_t *, const char *); +size_t BarUiListSongs (const BarApp_t * const app, + const PianoSong_t *song, const char *filter); void BarUiStartEventCmd (const BarSettings_t *, const char *, - const PianoStation_t *, const PianoSong_t *, const player_t *, + const PianoStation_t *, const PianoSong_t *, player_t *, PianoStation_t *, PianoReturn_t, CURLcode); bool BarUiPianoCall (BarApp_t * const, const PianoRequestType_t, void *, PianoReturn_t *, CURLcode *);