Hello community,

here is the log from the commit of package pianobar for openSUSE:Factory 
checked in at 2018-07-09 13:29:42
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/pianobar (Old)
 and      /work/SRC/openSUSE:Factory/.pianobar.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "pianobar"

Mon Jul  9 13:29:42 2018 rev:3 rq:621562 version:2018.06.22

Changes:
--------
--- /work/SRC/openSUSE:Factory/pianobar/pianobar.changes        2018-07-06 
10:47:33.226857645 +0200
+++ /work/SRC/openSUSE:Factory/.pianobar.new/pianobar.changes   2018-07-09 
13:31:22.602486273 +0200
@@ -1,0 +2,11 @@
+Sun Jul  8 11:45:36 UTC 2018 - aloi...@gmx.com
+
+- 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
+
+-------------------------------------------------------------------

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

New:
----
  pianobar-2018.06.22.tar.gz

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

Other differences:
------------------
++++++ pianobar.spec ++++++
--- /var/tmp/diff_new_pack.IuYHJc/_old  2018-07-09 13:31:23.874483712 +0200
+++ /var/tmp/diff_new_pack.IuYHJc/_new  2018-07-09 13:31:23.878483704 +0200
@@ -18,7 +18,7 @@
 
 
 Name:           pianobar
-Version:        2017.08.30
+Version:        2018.06.22
 Release:        0
 Summary:        Pandora Player
 License:        MIT
@@ -30,10 +30,10 @@
 BuildRequires:  pkgconfig(ao)
 BuildRequires:  pkgconfig(gnutls)
 BuildRequires:  pkgconfig(json)
-BuildRequires:  pkgconfig(libavcodec) = 57.107.100
-BuildRequires:  pkgconfig(libavfilter) = 6.107.100
-BuildRequires:  pkgconfig(libavformat) = 57.83.100
-BuildRequires:  pkgconfig(libavutil) = 55.78.100
+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-2018.06.22.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/ChangeLog 
new/pianobar-2018.06.22/ChangeLog
--- old/pianobar-2017.08.30/ChangeLog   2017-08-30 16:42:37.000000000 +0200
+++ new/pianobar-2018.06.22/ChangeLog   2018-06-22 08:57:26.000000000 +0200
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/README.md 
new/pianobar-2018.06.22/README.md
--- old/pianobar-2017.08.30/README.md   2017-08-30 16:42:37.000000000 +0200
+++ new/pianobar-2018.06.22/README.md   2018-06-22 08:57:26.000000000 +0200
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/contrib/config-example 
new/pianobar-2018.06.22/contrib/config-example
--- old/pianobar-2017.08.30/contrib/config-example      2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2018.06.22/contrib/config-example      2018-06-22 
08:57:26.000000000 +0200
@@ -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-2018.06.22/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-2018.06.22/contrib/eventcmd-examples/multi.sh  2018-06-22 
08:57:26.000000000 +0200
@@ -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-2018.06.22/contrib/pianobar.1
--- old/pianobar-2017.08.30/contrib/pianobar.1  2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2018.06.22/contrib/pianobar.1  2018-06-22 08:57:26.000000000 
+0200
@@ -194,7 +194,7 @@
 .B act_songinfo.
 
 .TP
-.B ban_icon = </3
+.B ban_icon =  </3
 Icon for banned songs.
 
 .TP
@@ -253,6 +253,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 +296,7 @@
 Album name
 
 .B %r
-Rating icon (only love icon)
+Rating icon
 
 .B %@
 .B at_icon
@@ -318,12 +329,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 +370,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 = y...@user.name
 Your pandora.com username.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/config.h 
new/pianobar-2018.06.22/src/config.h
--- old/pianobar-2017.08.30/src/config.h        2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2018.06.22/src/config.h        2018-06-22 08:57:26.000000000 
+0200
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/libpiano/piano.c 
new/pianobar-2018.06.22/src/libpiano/piano.c
--- old/pianobar-2017.08.30/src/libpiano/piano.c        2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2018.06.22/src/libpiano/piano.c        2018-06-22 
08:57:26.000000000 +0200
@@ -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-2018.06.22/src/libpiano/piano.h
--- old/pianobar-2017.08.30/src/libpiano/piano.h        2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2018.06.22/src/libpiano/piano.h        2018-06-22 
08:57:26.000000000 +0200
@@ -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-2018.06.22/src/libpiano/request.c
--- old/pianobar-2017.08.30/src/libpiano/request.c      2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2018.06.22/src/libpiano/request.c      2018-06-22 
08:57:26.000000000 +0200
@@ -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-2018.06.22/src/libpiano/response.c
--- old/pianobar-2017.08.30/src/libpiano/response.c     2017-08-30 
16:42:37.000000000 +0200
+++ new/pianobar-2018.06.22/src/libpiano/response.c     2018-06-22 
08:57:26.000000000 +0200
@@ -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-2018.06.22/src/main.c
--- old/pianobar-2017.08.30/src/main.c  2017-08-30 16:42:37.000000000 +0200
+++ new/pianobar-2018.06.22/src/main.c  2018-06-22 08:57:26.000000000 +0200
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2013
+Copyright (c) 2008-2018
        Lars-Dominik Braun <l...@6xq.net>
 
 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-2018.06.22/src/player.c
--- old/pianobar-2017.08.30/src/player.c        2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2018.06.22/src/player.c        2018-06-22 08:57:26.000000000 
+0200
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2014
+Copyright (c) 2008-2018
        Lars-Dominik Braun <l...@6xq.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -36,7 +36,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
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/player.h 
new/pianobar-2018.06.22/src/player.h
--- old/pianobar-2017.08.30/src/player.h        2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2018.06.22/src/player.h        2018-06-22 08:57:26.000000000 
+0200
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2014
+Copyright (c) 2008-2018
        Lars-Dominik Braun <l...@6xq.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -34,28 +34,35 @@
 #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;
+       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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/settings.c 
new/pianobar-2018.06.22/src/settings.c
--- old/pianobar-2017.08.30/src/settings.c      2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2018.06.22/src/settings.c      2018-06-22 08:57:26.000000000 
+0200
@@ -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 (" </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 +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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/settings.h 
new/pianobar-2018.06.22/src/settings.h
--- old/pianobar-2017.08.30/src/settings.h      2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2018.06.22/src/settings.h      2018-06-22 08:57:26.000000000 
+0200
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2017.08.30/src/ui.c 
new/pianobar-2018.06.22/src/ui.c
--- old/pianobar-2017.08.30/src/ui.c    2017-08-30 16:42:37.000000000 +0200
+++ new/pianobar-2018.06.22/src/ui.c    2018-06-22 08:57:26.000000000 +0200
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2013
+Copyright (c) 2008-2018
        Lars-Dominik Braun <l...@6xq.net>
 
 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-2018.06.22/src/ui.h
--- old/pianobar-2017.08.30/src/ui.h    2017-08-30 16:42:37.000000000 +0200
+++ new/pianobar-2018.06.22/src/ui.h    2018-06-22 08:57:26.000000000 +0200
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2011
+Copyright (c) 2008-2018
        Lars-Dominik Braun <l...@6xq.net>
 
 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-2018.06.22/src/ui_act.c
--- old/pianobar-2017.08.30/src/ui_act.c        2017-08-30 16:42:37.000000000 
+0200
+++ new/pianobar-2018.06.22/src/ui_act.c        2018-06-22 08:57:26.000000000 
+0200
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2013
+Copyright (c) 2008-2018
        Lars-Dominik Braun <l...@6xq.net>
 
 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... ");


Reply via email to