Hello community,

here is the log from the commit of package pianobar for openSUSE:Leap:15.2 
checked in at 2020-05-13 13:47:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Leap:15.2/pianobar (Old)
 and      /work/SRC/openSUSE:Leap:15.2/.pianobar.new.2738 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "pianobar"

Wed May 13 13:47:51 2020 rev:12 rq:804898 version:2019.02.14

Changes:
--------
--- /work/SRC/openSUSE:Leap:15.2/pianobar/pianobar.changes      2020-01-15 
15:43:56.687272182 +0100
+++ /work/SRC/openSUSE:Leap:15.2/.pianobar.new.2738/pianobar.changes    
2020-05-13 13:47:57.533388223 +0200
@@ -1,0 +2,25 @@
+Thu Feb 14 12:53:46 UTC 2019 - Luigi Baldoni <[email protected]>
+
+- Update to version 2019.02.14
+  * Fix MP3 playback (affects premium subscribers with audio
+    quality “high”)
+  version 2019.01.25
+  * Implement audio buffering
+
+-------------------------------------------------------------------
+Sun Jul  8 11:45:36 UTC 2018 - [email protected]
+
+- Update to version 2018.06.22
+  * Happy 10th birthday pianobar!
+  * Add network timeouts and retries
+  * Fix cross-thread memory access
+  * Misc UI and documentation improvements
+
+- Use ffmpeg4
+
+-------------------------------------------------------------------
+Fri May  4 14:03:47 UTC 2018 - [email protected]
+
+- Use ffmpeg3 versions of pkgconfig(libav*)
+
+-------------------------------------------------------------------

Old:
----
  pianobar-2017.08.30.tar.gz

New:
----
  pianobar-2019.02.14.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ pianobar.spec ++++++
--- /var/tmp/diff_new_pack.MjiLLI/_old  2020-05-13 13:47:57.873388955 +0200
+++ /var/tmp/diff_new_pack.MjiLLI/_new  2020-05-13 13:47:57.873388955 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package pianobar
 #
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
 # Copyright (c) 2016 Packman team: http://packman.links2linux.org/
 #
 # All modifications and additions to the file contributed by third parties
@@ -13,27 +13,27 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
 #
 
 
 Name:           pianobar
-Version:        2017.08.30
+Version:        2019.02.14
 Release:        0
 Summary:        Pandora Player
 License:        MIT
 Group:          Productivity/Multimedia/Sound/Utilities
-Url:            https://6xq.net/pianobar
+URL:            https://6xq.net/pianobar
 Source0:        
https://github.com/PromyLOPh/pianobar/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz
 BuildRequires:  libgcrypt-devel
 BuildRequires:  pkgconfig
 BuildRequires:  pkgconfig(ao)
 BuildRequires:  pkgconfig(gnutls)
 BuildRequires:  pkgconfig(json)
-BuildRequires:  pkgconfig(libavcodec)
-BuildRequires:  pkgconfig(libavfilter)
-BuildRequires:  pkgconfig(libavformat)
-BuildRequires:  pkgconfig(libavutil)
+BuildRequires:  pkgconfig(libavcodec) >= 58.7.100
+BuildRequires:  pkgconfig(libavfilter) >= 7.0.101
+BuildRequires:  pkgconfig(libavformat) >= 58.0.102
+BuildRequires:  pkgconfig(libavutil) >= 56.6.100
 BuildRequires:  pkgconfig(libcurl)
 
 %description

++++++ pianobar-2017.08.30.tar.gz -> pianobar-2019.02.14.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/.github/issue_template.md 
new/pianobar-2019.02.14/.github/issue_template.md
--- old/pianobar-2017.08.30/.github/issue_template.md   1970-01-01 
01:00:00.000000000 +0100
+++ new/pianobar-2019.02.14/.github/issue_template.md   2019-02-14 
09:37:03.000000000 +0100
@@ -0,0 +1,19 @@
+### Subject of the issue
+Briefly describe your issue here.
+
+### Your environment
+* version of pianobar
+* your Linux distribution and release version
+* ffmpeg/libav version and the flags it was compiled with (if you compiled 
yourself)
+* your config file
+
+### Steps to reproduce
+Tell us how to reproduce this issue.
+
+### Expected behaviour
+Tell us what should happen.
+
+### Actual behaviour
+Tell us what happens instead, including a copy of all error messages.
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/ChangeLog 
new/pianobar-2019.02.14/ChangeLog
--- old/pianobar-2017.08.30/ChangeLog   2017-08-30 16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/ChangeLog   2019-02-14 09:37:03.000000000 +0100
@@ -1,3 +1,18 @@
+Release 2019.02.14
+
+- Fix MP3 playback (affects premium subscribers with audio quality “high”)
+
+Release 2019.01.25
+
+- Implement audio buffering
+
+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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/README.md 
new/pianobar-2019.02.14/README.md
--- old/pianobar-2017.08.30/README.md   2017-08-30 16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/README.md   2019-02-14 09:37:03.000000000 +0100
@@ -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](https://www.pandora.com).
 
 ### Features
 
@@ -14,9 +13,9 @@
 
 ### Source Code
 
-The source code can be downloaded at 
[github.com](http://github.com/PromyLOPh/pianobar/)
-or [6xq.net](http://6xq.net/projects/pianobar/).
+The source code can be downloaded at 
[github.com](https://github.com/PromyLOPh/pianobar)
+or [6xq.net](https://6xq.net/pianobar/).
 
 ### Download/Installation
 
-There are community provided packages available for most Linux distributions 
(see your distribution’s package manager), Mac OS X 
([MacPorts](http://trac.macports.org/browser/trunk/dports/audio/pianobar/Portfile)
 or [homebrew](http://brew.sh/)) and *BSD as well as a [native Windows 
port](https://github.com/thedmd/pianobar-windows).
+There are community provided packages available for most Linux distributions 
(see your distribution’s package manager), macOS 
([MacPorts](https://www.macports.org) or [Homebrew](https://brew.sh)) and *BSD 
as well as a [native Windows port](https://github.com/thedmd/pianobar-windows).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/contrib/config-example 
new/pianobar-2019.02.14/contrib/config-example
--- old/pianobar-2017.08.30/contrib/config-example      2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/contrib/config-example      2019-02-14 
09:37:03.000000000 +0100
@@ -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 =  </3
+#love_icon =  <3
+#tired_icon =  zZ
 #format_nowplaying_station = Station %n
-#format_list_song = %i) %a - %t%r
+#format_list_song = %i) %a - %t%r (%d)%@%s
 
-# high-quality audio (192k mp3, for Pandora One subscribers only!)
-#audio_quality = high
 #rpc_host = internal-tuner.pandora.com
 #partner_user = pandora one
 #partner_password = TVCKIBGS9AO9TSYLNNFUML0743LH82D
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pianobar-2017.08.30/contrib/eventcmd-examples/multi.sh 
new/pianobar-2019.02.14/contrib/eventcmd-examples/multi.sh
--- old/pianobar-2017.08.30/contrib/eventcmd-examples/multi.sh  2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/contrib/eventcmd-examples/multi.sh  2019-02-14 
09:37:03.000000000 +0100
@@ -1,7 +1,17 @@
 #!/bin/bash
 #
-# Executes all scripts located in ~/.config/pianobar/eventcmd.d/ as if they
-# were called by pianobar directly
+# Usage
+# =====
+# 
+# Set
+# 
+#      event_command = /path/to/multi.sh
+# 
+# in pianobar’s config file. Then create the directory
+# ~/.config/pianobar/eventcmd.d/, move your eventcmd scripts there and make
+# them executable (chmod +x). They will be run in an unspecified order the same
+# way the would have been run if pianobar called them directly (i.e. using
+# event_command).
 
 STDIN=`mktemp ${TMPDIR:-/tmp}/pianobar.XXXXXX`
 cat >> $STDIN
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/contrib/pianobar.1 
new/pianobar-2019.02.14/contrib/pianobar.1
--- old/pianobar-2017.08.30/contrib/pianobar.1  2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2019.02.14/contrib/pianobar.1  2019-02-14 09:37:03.000000000 
+0100
@@ -194,7 +194,7 @@
 .B act_songinfo.
 
 .TP
-.B ban_icon = </3
+.B ban_icon =  </3
 Icon for banned songs.
 
 .TP
@@ -209,6 +209,10 @@
 .B route-nopull.
 
 .TP
+.B buffer_seconds = 5
+Audio buffer size in seconds.
+
+.TP
 .B ca_bundle = /etc/ssl/certs/ca-certificates.crt
 Path to CA certifiate bundle, containing the root and intermediate certificates
 required to validate Pandora's SSL certificate.
@@ -253,6 +257,17 @@
 .B %r
 Rating icon
 
+.B %d
+Song duration
+
+.B %@
+
+The at_icon
+
+.B %s
+
+Song’s station, if not the current station.
+
 .TP
 .B format_msg_none = %s
 .TQ
@@ -285,7 +300,7 @@
 Album name
 
 .B %r
-Rating icon (only love icon)
+Rating icon
 
 .B %@
 .B at_icon
@@ -318,12 +333,12 @@
 Keep a history of the last n songs (5, by default). You can rate these songs.
 
 .TP
-.B love_icon = <3
+.B love_icon =  <3
 Icon for loved songs.
 
 .TP
-.B max_player_errors = 5
-Amount of song download errors in a row after pianobar stops playback.
+.B max_retry = 3
+Max failures for several actions before giving up.
 
 .TP
 .B partner_password = AC7IBG09A3DTSYM4R41UJWL07VLN8JI7
@@ -359,6 +374,14 @@
 bottom) and name from z to a.
 
 .TP
+.B timeout = 30
+Network operation timeout.
+
+.TP
+.B tired_icon =  zZ
+Icon for temporarily suspended songs.
+
+.TP
 .B user = [email protected]
 Your pandora.com username.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/config.h 
new/pianobar-2019.02.14/src/config.h
--- old/pianobar-2017.08.30/src/config.h        2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2019.02.14/src/config.h        2019-02-14 09:37:03.000000000 
+0100
@@ -3,7 +3,7 @@
 /* package name */
 #define PACKAGE "pianobar"
 
-#define VERSION "2017.08.30"
+#define VERSION "2019.02.14"
 
 /* glibc feature test macros, define _before_ including other files */
 #define _POSIX_C_SOURCE 200809L
@@ -12,10 +12,33 @@
  * ffmpeg’s micro versions always start at 100, that’s how we can distinguish
  * ffmpeg and libav */
 #include <libavfilter/version.h>
+#include <libavformat/version.h>
 
 /* does graph_send_command exist (ffmpeg >=2.2) */
-#if LIBAVFILTER_VERSION_MAJOR >= 4 && \
+#if !defined(HAVE_AVFILTER_GRAPH_SEND_COMMAND) && \
+               LIBAVFILTER_VERSION_MAJOR >= 4 && \
                LIBAVFILTER_VERSION_MICRO >= 100
 #define HAVE_AVFILTER_GRAPH_SEND_COMMAND
 #endif
 
+/* explicit init is optional for ffmpeg>=4.0 */
+#if !defined(HAVE_AVFORMAT_NETWORK_INIT) && \
+               LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 5, 100) && \
+               LIBAVFORMAT_VERSION_MICRO >= 100
+#define HAVE_AVFORMAT_NETWORK_INIT
+#endif
+
+/* dito */
+#if !defined(HAVE_AVFILTER_REGISTER_ALL) && \
+               LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7, 14, 100) && \
+               LIBAVFILTER_VERSION_MICRO >= 100
+#define HAVE_AVFILTER_REGISTER_ALL
+#endif
+
+/* dito */
+#if !defined(HAVE_AV_REGISTER_ALL) && \
+               LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100) && \
+               LIBAVFORMAT_VERSION_MICRO >= 100
+#define HAVE_AV_REGISTER_ALL
+#endif
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/libpiano/piano.c 
new/pianobar-2019.02.14/src/libpiano/piano.c
--- old/pianobar-2017.08.30/src/libpiano/piano.c        2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/src/libpiano/piano.c        2019-02-14 
09:37:03.000000000 +0100
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/libpiano/piano.h 
new/pianobar-2019.02.14/src/libpiano/piano.h
--- old/pianobar-2017.08.30/src/libpiano/piano.h        2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/src/libpiano/piano.h        2019-02-14 
09:37:03.000000000 +0100
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/libpiano/request.c 
new/pianobar-2019.02.14/src/libpiano/request.c
--- old/pianobar-2017.08.30/src/libpiano/request.c      2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/src/libpiano/request.c      2019-02-14 
09:37:03.000000000 +0100
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/libpiano/response.c 
new/pianobar-2019.02.14/src/libpiano/response.c
--- old/pianobar-2017.08.30/src/libpiano/response.c     2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/src/libpiano/response.c     2019-02-14 
09:37:03.000000000 +0100
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/main.c 
new/pianobar-2019.02.14/src/main.c
--- old/pianobar-2017.08.30/src/main.c  2017-08-30 16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/src/main.c  2019-02-14 09:37:03.000000000 +0100
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2013
+Copyright (c) 2008-2018
        Lars-Dominik Braun <[email protected]>
 
 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/player.c 
new/pianobar-2019.02.14/src/player.c
--- old/pianobar-2017.08.30/src/player.c        2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2019.02.14/src/player.c        2019-02-14 09:37:03.000000000 
+0100
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2014
+Copyright (c) 2008-2018
        Lars-Dominik Braun <[email protected]>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -21,7 +21,16 @@
 THE SOFTWARE.
 */
 
-/* receive/play audio stream */
+/* receive/play audio stream.
+ *
+ * There are two threads involved here:
+ * BarPlayerThread
+ *             Sets up the stream and fetches the data into a ffmpeg buffersrc
+ * BarAoPlayThread
+ *             Reads data from the filter chain’s sink and hands it over to 
libao for
+ *             playback.
+ * 
+ */
 
 #include "config.h"
 
@@ -36,7 +45,6 @@
 #include <libavcodec/avcodec.h>
 #include <libavutil/avutil.h>
 #include <libavfilter/avfilter.h>
-#include <libavfilter/avfiltergraph.h>
 #include <libavfilter/buffersink.h>
 #include <libavfilter/buffersrc.h>
 #ifdef HAVE_LIBAVFILTER_AVCODEC_H
@@ -62,23 +70,59 @@
 }
 
 /*     global initialization
- *
- *     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);
+#ifdef HAVE_AV_REGISTER_ALL
        av_register_all ();
+#endif
+#ifdef HAVE_AVFILTER_REGISTER_ALL
        avfilter_register_all ();
+#endif
+#ifdef HAVE_AVFORMAT_NETWORK_INIT
        avformat_network_init ();
+#endif
+
+       pthread_mutex_init (&p->lock, NULL);
+       pthread_cond_init (&p->cond, NULL);
+       pthread_mutex_init (&p->aoplayLock, NULL);
+       pthread_cond_init (&p->aoplayCond, NULL);
+       BarPlayerReset (p);
+       p->settings = settings;
 }
 
-void BarPlayerDestroy () {
+void BarPlayerDestroy (player_t * const p) {
+       pthread_cond_destroy (&p->cond);
+       pthread_mutex_destroy (&p->lock);
+       pthread_cond_destroy (&p->aoplayCond);
+       pthread_mutex_destroy (&p->aoplayLock);
+
+#ifdef HAVE_AVFORMAT_NETWORK_INIT
        avformat_network_deinit ();
+#endif
        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 +166,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 +191,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 +244,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;
 }
@@ -218,14 +275,14 @@
                        av_get_sample_fmt_name (player->cctx->sample_fmt),
                        cp->channel_layout);
        if ((ret = avfilter_graph_create_filter (&player->fabuf,
-                       avfilter_get_by_name ("abuffer"), NULL, strbuf, NULL,
+                       avfilter_get_by_name ("abuffer"), "source", strbuf, 
NULL,
                        player->fgraph)) < 0) {
                softfail ("create_filter abuffer");
        }
 
        /* volume */
        if ((ret = avfilter_graph_create_filter (&player->fvolume,
-                       avfilter_get_by_name ("volume"), NULL, "0dB", NULL,
+                       avfilter_get_by_name ("volume"), "volume", "0dB", NULL,
                        player->fgraph)) < 0) {
                softfail ("create_filter volume");
        }
@@ -235,14 +292,14 @@
        snprintf (strbuf, sizeof (strbuf), "sample_fmts=%s",
                        av_get_sample_fmt_name (avformat));
        if ((ret = avfilter_graph_create_filter (&fafmt,
-                                       avfilter_get_by_name ("aformat"), NULL, 
strbuf, NULL,
+                                       avfilter_get_by_name ("aformat"), 
"format", strbuf, NULL,
                                        player->fgraph)) < 0) {
                softfail ("create_filter aformat");
        }
 
        /* abuffersink */
        if ((ret = avfilter_graph_create_filter (&player->fbufsink,
-                       avfilter_get_by_name ("abuffersink"), NULL, NULL, NULL,
+                       avfilter_get_by_name ("abuffersink"), "sink", NULL, 
NULL,
                        player->fgraph)) < 0) {
                softfail ("create_filter abuffersink");
        }
@@ -283,10 +340,34 @@
        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) {
        assert (player != NULL);
+       const int64_t minBufferHealth = player->settings->bufferSecs;
 
        AVPacket pkt;
        AVCodecContext * const cctx = player->cctx;
@@ -294,15 +375,15 @@
        pkt.data = NULL;
        pkt.size = 0;
 
-       AVFrame *frame = NULL, *filteredFrame = NULL;
+       AVFrame *frame = NULL;
        frame = av_frame_alloc ();
        assert (frame != NULL);
-       filteredFrame = av_frame_alloc ();
-       assert (filteredFrame != NULL);
-
+       pthread_t aoplaythread;
+       pthread_create (&aoplaythread, NULL, BarAoPlayThread, player);
        enum { FILL, DRAIN, DONE } drainMode = FILL;
        int ret = 0;
-       while (!player->doQuit && drainMode != DONE) {
+       const double timeBase = av_q2d (player->st->time_base);
+       while (!shouldQuit (player) && drainMode != DONE) {
                if (drainMode == FILL) {
                        ret = av_read_frame (player->fctx, &pkt);
                        if (ret == AVERROR_EOF) {
@@ -315,6 +396,12 @@
                                continue;
                        } else if (ret < 0) {
                                /* error, abort */
+                               /* mark the EOF, so that BarAoPlayThread can 
quit*/
+                               pthread_mutex_lock (&player->aoplayLock);
+                               const int rt = av_buffersrc_add_frame 
(player->fabuf, NULL);
+                               assert (rt == 0);
+                               pthread_cond_broadcast (&player->aoplayCond);
+                               pthread_mutex_unlock (&player->aoplayLock);
                                break;
                        } else {
                                /* fill buffer */
@@ -322,22 +409,17 @@
                        }
                }
 
-               /* pausing */
-               pthread_mutex_lock (&player->pauseMutex);
-               if (player->doPause) {
-                       av_read_pause (player->fctx);
-                       do {
-                               pthread_cond_wait (&player->pauseCond, 
&player->pauseMutex);
-                       } while (player->doPause);
-                       av_read_play (player->fctx);
-               }
-               pthread_mutex_unlock (&player->pauseMutex);
-
-               while (!player->doQuit) {
+               while (!shouldQuit (player)) {
                        ret = avcodec_receive_frame (cctx, frame);
                        if (ret == AVERROR_EOF) {
                                /* done draining */
                                drainMode = DONE;
+                               /* mark the EOF*/
+                               pthread_mutex_lock (&player->aoplayLock);
+                               const int rt = av_buffersrc_add_frame 
(player->fabuf, NULL);
+                               assert (rt == 0);
+                               pthread_cond_broadcast (&player->aoplayCond);
+                               pthread_mutex_unlock (&player->aoplayLock);
                                break;
                        } else if (ret != 0) {
                                /* no more output */
@@ -348,33 +430,29 @@
                        if (frame->pts == (int64_t) AV_NOPTS_VALUE) {
                                frame->pts = 0;
                        }
+                       pthread_mutex_lock (&player->aoplayLock);
                        ret = av_buffersrc_write_frame (player->fabuf, frame);
                        assert (ret >= 0);
-
-                       while (true) {
-                               if (av_buffersink_get_frame (player->fbufsink, 
filteredFrame) < 0) {
-                                       /* try again next frame */
-                                       break;
+                       pthread_mutex_unlock (&player->aoplayLock);
+                       
+                       int64_t bufferHealth = 0;
+                       do {
+                               pthread_mutex_lock (&player->aoplayLock);
+                               bufferHealth = timeBase * (double) (frame->pts 
- player->lastTimestamp);
+                               if (bufferHealth > minBufferHealth) {
+                                       /* Buffer get healthy, resume */
+                                       pthread_cond_broadcast 
(&player->aoplayCond);
+                                       /* Buffer is healthy enough, wait */
+                                       pthread_cond_wait (&player->aoplayCond, 
&player->aoplayLock);
                                }
-
-                               const int numChannels = 
av_get_channel_layout_nb_channels (
-                                               filteredFrame->channel_layout);
-                               const int bps = 
av_get_bytes_per_sample(filteredFrame->format);
-                               ao_play (player->aoDev, (char *) 
filteredFrame->data[0],
-                                               filteredFrame->nb_samples * 
numChannels * bps);
-
-                               av_frame_unref (filteredFrame);
-                       }
+                               pthread_mutex_unlock (&player->aoplayLock);
+                       } while (bufferHealth > minBufferHealth);
                }
 
-               player->songPlayed = av_q2d (player->st->time_base) * (double) 
pkt.pts;
-               player->lastTimestamp = pkt.pts;
-
                av_packet_unref (&pkt);
        }
-
-       av_frame_free (&filteredFrame);
        av_frame_free (&frame);
+       pthread_join (aoplaythread, NULL);
 
        return ret;
 }
@@ -410,7 +488,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,12 +500,74 @@
                        /* 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;
 }
 
+void *BarAoPlayThread (void *data) {
+       assert (data != NULL);
+
+       player_t * const player = data;
+
+       AVFrame *filteredFrame = NULL;
+       filteredFrame = av_frame_alloc ();
+       assert (filteredFrame != NULL);
+
+       int ret;
+       const double timeBase = av_q2d (av_buffersink_get_time_base 
(player->fbufsink)),
+                       timeBaseSt = av_q2d (player->st->time_base);
+       while (!shouldQuit(player)) {
+               pthread_mutex_lock (&player->aoplayLock);
+               ret = av_buffersink_get_frame (player->fbufsink, filteredFrame);
+               if (ret == AVERROR_EOF || shouldQuit (player)) {
+                       /* we are done here */
+                       pthread_mutex_unlock (&player->aoplayLock);
+                       break;
+               } else if (ret < 0) {
+                       /* wait for more frames */
+                       pthread_cond_broadcast (&player->aoplayCond);
+                       pthread_cond_wait (&player->aoplayCond, 
&player->aoplayLock);
+                       pthread_mutex_unlock (&player->aoplayLock);
+                       continue;
+               }
+               pthread_mutex_unlock (&player->aoplayLock);
+
+               const int numChannels = av_get_channel_layout_nb_channels (
+                               filteredFrame->channel_layout);
+               const int bps = av_get_bytes_per_sample (filteredFrame->format);
+               ao_play (player->aoDev, (char *) filteredFrame->data[0],
+                               filteredFrame->nb_samples * numChannels * bps);
+
+               const double timestamp = (double) filteredFrame->pts * timeBase;
+               const unsigned int songPlayed = timestamp;
+
+               pthread_mutex_lock (&player->lock);
+               player->songPlayed = songPlayed;
+               /* pausing */
+               if (player->doPause) {
+                       do {
+                               pthread_cond_wait (&player->cond, 
&player->lock);
+                       } while (player->doPause);
+               }
+               pthread_mutex_unlock (&player->lock);
+
+               /* lastTimestamp must be the last pts, but expressed in terms of
+                * st->time_base, not the sink’s time_base. */
+               const int64_t lastTimestamp = timestamp/timeBaseSt;
+               /* notify download thread, we might need more data */
+               pthread_mutex_lock (&player->aoplayLock);
+               player->lastTimestamp = lastTimestamp;
+               pthread_cond_broadcast (&player->aoplayCond);
+               pthread_mutex_unlock (&player->aoplayLock);
+
+               av_frame_unref (filteredFrame);
+       }
+       av_frame_free (&filteredFrame);
+
+       return (void *) 0;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/player.h 
new/pianobar-2019.02.14/src/player.h
--- old/pianobar-2017.08.30/src/player.h        2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2019.02.14/src/player.h        2019-02-14 09:37:03.000000000 
+0100
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2014
+Copyright (c) 2008-2018
        Lars-Dominik Braun <[email protected]>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -34,28 +34,34 @@
 #include <ao/ao.h>
 #include <libavformat/avformat.h>
 #include <libavfilter/avfilter.h>
-#include <libavfilter/avfiltergraph.h>
 #include <piano.h>
 
 #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, aoplayLock;
+       pthread_cond_t cond, aoplayCond; /* 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 +76,19 @@
 
        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 *BarAoPlayThread (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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/settings.c 
new/pianobar-2019.02.14/src/settings.c
--- old/pianobar-2017.08.30/src/settings.c      2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2019.02.14/src/settings.c      2019-02-14 09:37:03.000000000 
+0100
@@ -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,15 @@
        settings->autoselect = true;
        settings->history = 5;
        settings->volume = 0;
+       settings->timeout = 30; /* seconds */
        settings->gainMul = 1.0;
-       settings->maxPlayerErrors = 5;
+       /* should be > 4, otherwise expired audio urls (403) can stop playback 
*/
+       settings->maxRetry = 5;
+       settings->bufferSecs = 5;
        settings->sortOrder = BAR_SORT_NAME_AZ;
        settings->loveIcon = strdup (" <3");
        settings->banIcon = strdup (" </3");
+       settings->tiredIcon = strdup (" zZ");
        settings->atIcon = strdup (" @ ");
        settings->npSongFormat = strdup ("\"%t\" by \"%a\" on \"%l\"%r%@%s");
        settings->npStationFormat = strdup ("Station \"%n\" (%i)");
@@ -335,8 +340,12 @@
                                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 ("buffer_seconds", key)) {
+                               settings->bufferSecs = atoi (val);
                        } else if (streq ("sort", key)) {
                                size_t i;
                                static const char *mapping[] = {"name_az",
@@ -358,6 +367,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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/settings.h 
new/pianobar-2019.02.14/src/settings.h
--- old/pianobar-2017.08.30/src/settings.h      2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2019.02.14/src/settings.h      2019-02-14 09:37:03.000000000 
+0100
@@ -84,7 +84,7 @@
 
 typedef struct {
        bool autoselect;
-       unsigned int history, maxPlayerErrors;
+       unsigned int history, maxRetry, timeout, bufferSecs;
        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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/ui.c 
new/pianobar-2019.02.14/src/ui.c
--- old/pianobar-2017.08.30/src/ui.c    2017-08-30 16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/src/ui.c    2019-02-14 09:37:03.000000000 +0100
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2013
+Copyright (c) 2008-2018
        Lars-Dominik Braun <[email protected]>
 
 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/ui.h 
new/pianobar-2019.02.14/src/ui.h
--- old/pianobar-2017.08.30/src/ui.h    2017-08-30 16:42:37.000000000 +0200
+++ new/pianobar-2019.02.14/src/ui.h    2019-02-14 09:37:03.000000000 +0100
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2011
+Copyright (c) 2008-2018
        Lars-Dominik Braun <[email protected]>
 
 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 *);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/ui_act.c 
new/pianobar-2019.02.14/src/ui_act.c
--- old/pianobar-2017.08.30/src/ui_act.c        2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2019.02.14/src/ui_act.c        2019-02-14 09:37:03.000000000 
+0100
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2013
+Copyright (c) 2008-2018
        Lars-Dominik Braun <[email protected]>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -52,11 +52,14 @@
 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);
+       pthread_mutex_lock (&player->aoplayLock);
+       pthread_cond_broadcast (&player->aoplayCond);
+       pthread_mutex_unlock (&player->aoplayLock);
 }
 
 /*     transform station if necessary to allow changes like rename, rate, ...
@@ -419,28 +422,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 +508,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");
        }
@@ -561,15 +564,15 @@
        if (selStation->isQuickMix) {
                PianoStation_t *toggleStation;
                while ((toggleStation = BarUiSelectStation (app, 
app->ph.stations,
-                               "Toggle quickmix for station: ",
+                               "Toggle QuickMix for station: ",
                                BarUiActQuickmixCallback, false)) != NULL) {
                        toggleStation->useQuickMix = 
!toggleStation->useQuickMix;
                }
-               BarUiMsg (&app->settings, MSG_INFO, "Setting quickmix 
stations... ");
+               BarUiMsg (&app->settings, MSG_INFO, "Setting QuickMix 
stations... ");
                BarUiActDefaultPianoCall (PIANO_REQUEST_SET_QUICKMIX, NULL);
                BarUiActDefaultEventcmd ("stationquickmixtoggle");
        } else {
-               BarUiMsg (&app->settings, MSG_ERR, "Not a QuickMix station.\n");
+               BarUiMsg (&app->settings, MSG_ERR, "Please select a QuickMix 
station first.\n");
        }
 }
 
@@ -587,7 +590,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 +845,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 +872,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... ");
@@ -881,4 +884,3 @@
 
        PianoDestroyStationInfo (&reqData.info);
 }
-


Reply via email to