Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package pianobar for openSUSE:Factory 
checked in at 2021-04-14 10:11:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/pianobar (Old)
 and      /work/SRC/openSUSE:Factory/.pianobar.new.2401 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "pianobar"

Wed Apr 14 10:11:12 2021 rev:5 rq:884944 version:2020.11.28

Changes:
--------
--- /work/SRC/openSUSE:Factory/pianobar/pianobar.changes        2019-02-15 
10:02:36.247621842 +0100
+++ /work/SRC/openSUSE:Factory/.pianobar.new.2401/pianobar.changes      
2021-04-14 10:11:44.141570344 +0200
@@ -1,0 +2,12 @@
+Tue Apr 13 12:37:17 UTC 2021 - Luigi Baldoni <aloi...@gmx.com>
+
+- Update to version 2020.11.28
+  * Support changing station modes
+  * Allow choosing time format
+  * Add optional debug output
+  version 2020.04.05:
+  * Support writing audio to named pipe (audio_pipe)
+  * Add resampling (sample_rate)
+  * Fix station list not showing more than 95 stations
+
+-------------------------------------------------------------------

Old:
----
  pianobar-2019.02.14.tar.gz

New:
----
  pianobar-2020.11.28.tar.gz

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

Other differences:
------------------
++++++ pianobar.spec ++++++
--- /var/tmp/diff_new_pack.RPnNjB/_old  2021-04-14 10:11:44.693571275 +0200
+++ /var/tmp/diff_new_pack.RPnNjB/_new  2021-04-14 10:11:44.697571282 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package pianobar
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2021 SUSE LLC
 # Copyright (c) 2016 Packman team: http://packman.links2linux.org/
 #
 # All modifications and additions to the file contributed by third parties
@@ -18,7 +18,7 @@
 
 
 Name:           pianobar
-Version:        2019.02.14
+Version:        2020.11.28
 Release:        0
 Summary:        Pandora Player
 License:        MIT

++++++ pianobar-2019.02.14.tar.gz -> pianobar-2020.11.28.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/.github/workflows/build.yml 
new/pianobar-2020.11.28/.github/workflows/build.yml
--- old/pianobar-2019.02.14/.github/workflows/build.yml 1970-01-01 
01:00:00.000000000 +0100
+++ new/pianobar-2020.11.28/.github/workflows/build.yml 2020-11-28 
11:32:58.000000000 +0100
@@ -0,0 +1,20 @@
+name: build
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-20.04
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: deps
+      run: sudo apt install libao-dev libavcodec-dev libavfilter-dev 
libavformat-dev libavutil-dev libcurl4-gnutls-dev libgcrypt20-dev libjson-c-dev 
libpth-dev pkg-config build-essential
+    - name: make
+      run: make
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/ChangeLog 
new/pianobar-2020.11.28/ChangeLog
--- old/pianobar-2019.02.14/ChangeLog   2019-02-14 09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/ChangeLog   2020-11-28 11:32:58.000000000 +0100
@@ -1,3 +1,15 @@
+Release 2020.11.28
+
+- Support changing station modes
+- Allow choosing time format
+- Add optional debug output
+
+Release 2020.04.05
+
+- Support writing audio to named pipe (audio_pipe)
+- Add resampling (sample_rate)
+- Fix station list not showing more than 95 stations
+
 Release 2019.02.14
 
 - Fix MP3 playback (affects premium subscribers with audio quality ???high???)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/INSTALL 
new/pianobar-2020.11.28/INSTALL
--- old/pianobar-2019.02.14/INSTALL     2019-02-14 09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/INSTALL     2020-11-28 11:32:58.000000000 +0100
@@ -10,7 +10,7 @@
 - libcurl
 - gcrypt[1]
 - json-c
-- libav>=12/ffmpeg>=3.1 [2]
+- ffmpeg>=3.3 [2]
 - UTF-8 console/locale
 
 [1] with blowfish cipher enabled
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/Makefile 
new/pianobar-2020.11.28/Makefile
--- old/pianobar-2019.02.14/Makefile    2019-02-14 09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/Makefile    2020-11-28 11:32:58.000000000 +0100
@@ -24,6 +24,7 @@
 PIANOBAR_DIR:=src
 PIANOBAR_SRC:=\
                ${PIANOBAR_DIR}/main.c \
+               ${PIANOBAR_DIR}/debug.c \
                ${PIANOBAR_DIR}/player.c \
                ${PIANOBAR_DIR}/settings.c \
                ${PIANOBAR_DIR}/terminal.c \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/README.md 
new/pianobar-2020.11.28/README.md
--- old/pianobar-2019.02.14/README.md   2019-02-14 09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/README.md   2020-11-28 11:32:58.000000000 +0100
@@ -1,5 +1,7 @@
 # pianobar
 
+![build](https://github.com/PromyLOPh/pianobar/workflows/build/badge.svg)
+
 pianobar is a console client for the personalized web radio 
[Pandora](https://www.pandora.com).
 
 ### Features
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/contrib/config-example 
new/pianobar-2020.11.28/contrib/config-example
--- old/pianobar-2019.02.14/contrib/config-example      2019-02-14 
09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/contrib/config-example      2020-11-28 
11:32:58.000000000 +0100
@@ -49,6 +49,8 @@
 #volume = 0
 #ca_bundle = /etc/ssl/certs/ca-certificates.crt
 #gain_mul = 1.0
+#sample_rate = 44100
+#audio_pipe = /tmp/mypipe
 
 # Format strings
 #format_nowplaying_song = %t by %a on %l%r%@%s
@@ -58,6 +60,7 @@
 #tired_icon =  zZ
 #format_nowplaying_station = Station %n
 #format_list_song = %i) %a - %t%r (%d)%@%s
+#format_time = %e/%t
 
 #rpc_host = internal-tuner.pandora.com
 #partner_user = pandora one
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/contrib/pianobar.1 
new/pianobar-2020.11.28/contrib/pianobar.1
--- old/pianobar-2019.02.14/contrib/pianobar.1  2019-02-14 09:37:03.000000000 
+0100
+++ new/pianobar-2020.11.28/contrib/pianobar.1  2020-11-28 11:32:58.000000000 
+0100
@@ -180,6 +180,12 @@
 Select audio quality.
 
 .TP
+.B audio_pipe = /path/to/fifo
+Stream decoded, raw audio samples to a pipe instead of the default audio 
device. Use
+.B sample_rate
+to enforce a fixed sample rate.
+
+.TP
 .B autoselect = {1,0}
 Auto-select last remaining item of filtered list. Currently enabled for station
 selection only.
@@ -323,6 +329,22 @@
 Station id
 
 .TP
+.B format_time = %s%r/%t
+Time format.
+
+.B %e
+Elapsed time
+
+.B %r
+Remaining time
+
+.B %s
+Sign
+
+.B %t
+Total time
+
+.TP
 .B gain_mul = 1.0
 Pandora sends a ReplayGain value with every song. This sets a multiplier so 
that the gain adjustment can be
 reduced. 0.0 means no gain adjustment, 1.0 means full gain adjustment, values 
inbetween reduce the magnitude
@@ -368,6 +390,10 @@
 .B rpc_tls_port = 443
 
 .TP
+.B sample_rate = 0
+Force fixed output sample rate. The default, 0, uses the stream???s sample 
rate.
+
+.TP
 .B sort = {name_az, name_za, quickmix_01_name_az, quickmix_01_name_za, 
quickmix_10_name_az, quickmix_10_name_za}
 Sort station list by name or type (is quickmix) and name. name_az for example
 sorts by name from a to z, quickmix_01_name_za by type (quickmix at the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/config.h 
new/pianobar-2020.11.28/src/config.h
--- old/pianobar-2019.02.14/src/config.h        2019-02-14 09:37:03.000000000 
+0100
+++ new/pianobar-2020.11.28/src/config.h        2020-11-28 11:32:58.000000000 
+0100
@@ -3,7 +3,7 @@
 /* package name */
 #define PACKAGE "pianobar"
 
-#define VERSION "2019.02.14"
+#define VERSION "2020.11.28"
 
 /* glibc feature test macros, define _before_ including other files */
 #define _POSIX_C_SOURCE 200809L
@@ -42,3 +42,10 @@
 #define HAVE_AV_REGISTER_ALL
 #endif
 
+#ifndef NDEBUG
+#define HAVE_DEBUGLOG
+#define debug(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define debug(...)
+#endif
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/debug.c 
new/pianobar-2020.11.28/src/debug.c
--- old/pianobar-2019.02.14/src/debug.c 1970-01-01 01:00:00.000000000 +0100
+++ new/pianobar-2020.11.28/src/debug.c 2020-11-28 11:32:58.000000000 +0100
@@ -0,0 +1,29 @@
+/*
+Copyright (c) 2008-2018
+       Lars-Dominik Braun <l...@6xq.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "debug.h"
+
+#ifdef HAVE_DEBUGLOG
+unsigned int debug = 0;
+#endif
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/debug.h 
new/pianobar-2020.11.28/src/debug.h
--- old/pianobar-2019.02.14/src/debug.h 1970-01-01 01:00:00.000000000 +0100
+++ new/pianobar-2020.11.28/src/debug.h 2020-11-28 11:32:58.000000000 +0100
@@ -0,0 +1,64 @@
+/*
+Copyright (c) 2008-2018
+       Lars-Dominik Braun <l...@6xq.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#pragma once
+
+#include "config.h"
+#include <stdbool.h>
+
+#ifdef HAVE_DEBUGLOG
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+/* bitfield */
+typedef enum {
+       DEBUG_NETWORK = 1,
+       DEBUG_AUDIO = 2,
+       DEBUG_UI = 4,
+} debugKind;
+
+extern unsigned int debug;
+
+inline static bool debugEnable () {
+       const char * const debugStr = getenv("PIANOBAR_DEBUG");
+       if (debugStr != NULL) {
+               debug = atoi (debugStr);
+       }
+       return debug;
+}
+
+__attribute__((format(printf, 2, 3)))
+inline static void debugPrint(debugKind kind, const char * const format, ...) {
+       if (debug & kind) {
+               va_list fmtargs;
+               va_start (fmtargs, format);
+               vfprintf (stderr, format, fmtargs);
+               va_end (fmtargs);
+       }
+}
+#else
+inline static bool debugEnable () {}
+#define debugPrint(...)
+#endif
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/libpiano/piano.c 
new/pianobar-2020.11.28/src/libpiano/piano.c
--- old/pianobar-2019.02.14/src/libpiano/piano.c        2019-02-14 
09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/src/libpiano/piano.c        2020-11-28 
11:32:58.000000000 +0100
@@ -183,6 +183,18 @@
        memset (partner, 0, sizeof (*partner));
 }
 
+void PianoDestroyStationMode (PianoStationMode_t * const modes) {
+       PianoStationMode_t *curMode = modes;
+
+       while (curMode != NULL) {
+               free (curMode->name);
+               free (curMode->description);
+               PianoStationMode_t * const lastMode = curMode;
+               curMode = (PianoStationMode_t *) curMode->head.next;
+               free (lastMode);
+       }
+}
+
 /*     frees the whole piano handle structure
  *     @param piano handle
  *     @return nothing
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/libpiano/piano.h 
new/pianobar-2020.11.28/src/libpiano/piano.h
--- old/pianobar-2019.02.14/src/libpiano/piano.h        2019-02-14 
09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/src/libpiano/piano.h        2020-11-28 
11:32:58.000000000 +0100
@@ -155,6 +155,13 @@
        bool explicitContentFilter;
 } PianoSettings_t;
 
+typedef struct {
+       PianoListHead_t head;
+       char *name, *description;
+       bool isAlgorithmic, isTakeover, active;
+       int id;
+} PianoStationMode_t;
+
 typedef enum {
        /* 0 is reserved: memset (x, 0, sizeof (x)) */
        PIANO_REQUEST_LOGIN = 1,
@@ -179,6 +186,8 @@
        PIANO_REQUEST_DELETE_SEED = 22,
        PIANO_REQUEST_GET_SETTINGS = 23,
        PIANO_REQUEST_CHANGE_SETTINGS = 24,
+       PIANO_REQUEST_GET_STATION_MODES = 25,
+       PIANO_REQUEST_SET_STATION_MODE = 26,
 } PianoRequestType_t;
 
 typedef struct PianoRequest {
@@ -266,6 +275,16 @@
        PianoTristate_t explicitContentFilter;
 } PianoRequestDataChangeSettings_t;
 
+typedef struct {
+       PianoStation_t *station;
+       PianoStationMode_t *retModes;
+} PianoRequestDataGetStationModes_t;
+
+typedef struct {
+       PianoStation_t *station;
+       unsigned int id;
+} PianoRequestDataSetStationMode_t;
+
 /* pandora error code offset */
 #define PIANO_RET_OFFSET 1024
 typedef enum {
@@ -355,6 +374,7 @@
 void PianoDestroyPlaylist (PianoSong_t *);
 void PianoDestroySearchResult (PianoSearchResult_t *);
 void PianoDestroyStationInfo (PianoStationInfo_t *);
+void PianoDestroyStationMode (PianoStationMode_t * const);
 
 /* pandora rpc */
 PianoReturn_t PianoRequest (PianoHandle_t *, PianoRequest_t *,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/libpiano/request.c 
new/pianobar-2020.11.28/src/libpiano/request.c
--- old/pianobar-2019.02.14/src/libpiano/request.c      2019-02-14 
09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/src/libpiano/request.c      2020-11-28 
11:32:58.000000000 +0100
@@ -117,6 +117,9 @@
                        /* get stations, user must be authenticated */
                        assert (ph->user.listenerId != NULL);
 
+                       json_object_object_add (j, "returnAllStations",
+                                       json_object_new_boolean (true));
+
                        method = "user.getStationList";
                        break;
                }
@@ -366,6 +369,36 @@
                        break;
                }
 
+               case PIANO_REQUEST_GET_STATION_MODES: {
+                       PianoRequestDataGetStationModes_t *reqData = req->data;
+                       assert (reqData != NULL);
+                       PianoStation_t * const station = reqData->station;
+                       assert (station != NULL);
+
+                       json_object_object_add (j, "stationId",
+                                       json_object_new_string (station->id));
+
+                       method = "interactiveradio.v1.getAvailableModesSimple";
+                       req->secure = true;
+                       break;
+               }
+
+               case PIANO_REQUEST_SET_STATION_MODE: {
+                       PianoRequestDataSetStationMode_t *reqData = req->data;
+                       assert (reqData != NULL);
+                       PianoStation_t * const station = reqData->station;
+                       assert (station != NULL);
+
+                       json_object_object_add (j, "stationId",
+                                       json_object_new_string (station->id));
+                       json_object_object_add (j, "modeId",
+                                       json_object_new_int (reqData->id));
+
+                       method = "interactiveradio.v1.setAndGetAvailableModes";
+                       req->secure = true;
+                       break;
+               }
+
                case PIANO_REQUEST_DELETE_FEEDBACK: {
                        PianoSong_t *song = req->data;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/libpiano/response.c 
new/pianobar-2020.11.28/src/libpiano/response.c
--- old/pianobar-2019.02.14/src/libpiano/response.c     2019-02-14 
09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/src/libpiano/response.c     2020-11-28 
11:32:58.000000000 +0100
@@ -77,7 +77,7 @@
                --len;
        }
 
-       /* append until source exhaused or destination full */
+       /* append until source exhausted or destination full */
        while (*src != '\0' && len > 1) {
                *dest = *src;
                ++dest;
@@ -651,6 +651,66 @@
                        }
                        break;
                }
+
+               case PIANO_REQUEST_GET_STATION_MODES: {
+                       PianoRequestDataGetStationModes_t *reqData = req->data;
+                       assert (reqData != NULL);
+
+                       int active = -1;
+
+                       json_object *activeMode;
+                       if (json_object_object_get_ex (result, "currentModeId", 
&activeMode)) {
+                               active = json_object_get_int (activeMode);
+                       }
+
+                       json_object *availableModes;
+                       if (json_object_object_get_ex (result, 
"availableModes", &availableModes)) {
+                               for (int i = 0; i < json_object_array_length 
(availableModes); i++) {
+                                       json_object *val = 
json_object_array_get_idx (availableModes, i);
+
+                                       assert (json_object_is_type (val, 
json_type_object));
+
+                                       PianoStationMode_t *mode;
+                                       if ((mode = calloc (1, sizeof (*mode))) 
== NULL) {
+                                               return PIANO_RET_OUT_OF_MEMORY;
+                                       }
+
+                                       json_object *modeId;
+                                       if (json_object_object_get_ex (val, 
"modeId", &modeId)) {
+                                               mode->id = json_object_get_int 
(modeId);
+                                               mode->name = PianoJsonStrdup 
(val, "modeName");
+                                               mode->description = 
PianoJsonStrdup (val, "modeDescription");
+                                               mode->isAlgorithmic = 
getBoolDefault (val, "isAlgorithmicMode",
+                                                               false);
+                                               mode->isTakeover = 
getBoolDefault (val, "isTakeoverMode",
+                                                               false);
+                                               mode->active = active == 
mode->id;
+                                       }
+
+                                       reqData->retModes = PianoListAppendP 
(reqData->retModes,
+                                                       mode);
+                               }
+                       }
+                       break;
+               }
+
+               case PIANO_REQUEST_SET_STATION_MODE: {
+                       PianoRequestDataSetStationMode_t *reqData = req->data;
+                       assert (reqData != NULL);
+
+                       int active = -1;
+
+                       json_object *activeMode;
+                       if (json_object_object_get_ex (result, "currentModeId", 
&activeMode)) {
+                               active = json_object_get_int (activeMode);
+                       }
+
+                       if (active != reqData->id) {
+                               /* this did not work */
+                               return PIANO_RET_ERR;
+                       }
+                       break;
+               }
        }
 
 cleanup:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/main.c 
new/pianobar-2020.11.28/src/main.c
--- old/pianobar-2019.02.14/src/main.c  2019-02-14 09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/src/main.c  2020-11-28 11:32:58.000000000 +0100
@@ -51,6 +51,7 @@
 #include <piano.h>
 
 #include "main.h"
+#include "debug.h"
 #include "terminal.h"
 #include "ui.h"
 #include "ui_dispatch.h"
@@ -311,7 +312,7 @@
  */
 static void BarMainPrintTime (BarApp_t *app) {
        unsigned int songRemaining;
-       char sign;
+       char sign[2] = {0, 0};
        player_t * const player = &app->player;
 
        pthread_mutex_lock (&player->lock);
@@ -321,15 +322,26 @@
 
        if (songPlayed <= songDuration) {
                songRemaining = songDuration - songPlayed;
-               sign = '-';
+               sign[0] = '-';
        } else {
                /* longer than expected */
                songRemaining = songPlayed - songDuration;
-               sign = '+';
+               sign[0] = '+';
        }
-       BarUiMsg (&app->settings, MSG_TIME, "%c%02u:%02u/%02u:%02u\r",
-                       sign, songRemaining / 60, songRemaining % 60,
-                       songDuration / 60, songDuration % 60);
+
+       char outstr[512], totalFormatted[16], remainingFormatted[16],
+                       elapsedFormatted[16];
+       const char *vals[] = {totalFormatted, remainingFormatted,
+                       elapsedFormatted, sign};
+       snprintf (totalFormatted, sizeof (totalFormatted), "%02u:%02u",
+                       songDuration/60, songDuration%60);
+       snprintf (remainingFormatted, sizeof (remainingFormatted), "%02u:%02u",
+                       songRemaining/60, songRemaining%60);
+       snprintf (elapsedFormatted, sizeof (elapsedFormatted), "%02u:%02u",
+                       songPlayed/60, songPlayed%60);
+       BarUiCustomFormat (outstr, sizeof (outstr), app->settings.timeFormat,
+                       "tres", vals);
+       BarUiMsg (&app->settings, MSG_TIME, "%s\r", outstr);
 }
 
 /*     main loop
@@ -401,6 +413,7 @@
 
 static void intHandler (int signal) {
        if (interrupted != NULL) {
+               debugPrint(DEBUG_UI, "Received ^C\n");
                *interrupted += 1;
        }
 }
@@ -417,6 +430,8 @@
 int main (int argc, char **argv) {
        static BarApp_t app;
 
+       debugEnable();
+
        memset (&app, 0, sizeof (app));
 
        /* save terminal attributes, before disabling echoing */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/player.c 
new/pianobar-2020.11.28/src/player.c
--- old/pianobar-2019.02.14/src/player.c        2019-02-14 09:37:03.000000000 
+0100
+++ new/pianobar-2020.11.28/src/player.c        2020-11-28 11:32:58.000000000 
+0100
@@ -38,9 +38,12 @@
 #include <string.h>
 #include <math.h>
 #include <stdint.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <assert.h>
+#include <inttypes.h>
 #include <arpa/inet.h>
+#include <sys/stat.h>
 
 #include <libavcodec/avcodec.h>
 #include <libavutil/avutil.h>
@@ -56,6 +59,7 @@
 #include <libavutil/frame.h>
 
 #include "player.h"
+#include "debug.h"
 #include "ui.h"
 #include "ui_types.h"
 
@@ -254,6 +258,15 @@
        return true;
 }
 
+/*     Get output sample rate. Default to stream sample rate
+ */
+static int getSampleRate (const player_t * const player) {
+       AVCodecParameters const * const cp = player->st->codecpar;
+       return player->settings->sampleRate == 0 ?
+                       cp->sample_rate :
+                       player->settings->sampleRate;
+}
+
 /*     setup filter chain
  */
 static bool openFilter (player_t * const player) {
@@ -289,8 +302,8 @@
 
        /* aformat: convert float samples into something more usable */
        AVFilterContext *fafmt = NULL;
-       snprintf (strbuf, sizeof (strbuf), "sample_fmts=%s",
-                       av_get_sample_fmt_name (avformat));
+       snprintf (strbuf, sizeof (strbuf), "sample_fmts=%s:sample_rates=%d",
+                       av_get_sample_fmt_name (avformat), getSampleRate 
(player));
        if ((ret = avfilter_graph_create_filter (&fafmt,
                                        avfilter_get_by_name ("aformat"), 
"format", strbuf, NULL,
                                        player->fgraph)) < 0) {
@@ -328,13 +341,33 @@
        aoFmt.bits = av_get_bytes_per_sample (avformat) * 8;
        assert (aoFmt.bits > 0);
        aoFmt.channels = cp->channels;
-       aoFmt.rate = cp->sample_rate;
+       aoFmt.rate = getSampleRate (player);
        aoFmt.byte_format = AO_FMT_NATIVE;
 
-       int driver = ao_default_driver_id ();
-       if ((player->aoDev = ao_open_live (driver, &aoFmt, NULL)) == NULL) {
-               BarUiMsg (player->settings, MSG_ERR, "Cannot open audio 
device.\n");
-               return false;
+       int driver = -1;
+       if (player->settings->audioPipe) {
+               // using audio pipe
+               struct stat st;
+               if (stat (player->settings->audioPipe, &st)) {
+                       BarUiMsg (player->settings, MSG_ERR, "Cannot stat audio 
pipe file.\n");
+                       return false;
+               }
+               if (!S_ISFIFO (st.st_mode)) {
+                       BarUiMsg (player->settings, MSG_ERR, "File is not a 
pipe, error.\n");
+                       return false;
+               }
+               driver = ao_driver_id ("raw");
+               if ((player->aoDev = ao_open_file(driver, 
player->settings->audioPipe, 1, &aoFmt, NULL)) == NULL) {
+                       BarUiMsg (player->settings, MSG_ERR, "Cannot open audio 
pipe file.\n");
+                       return false;
+               }
+       } else {
+               // use driver from libao configuration
+               driver = ao_default_driver_id ();
+               if ((player->aoDev = ao_open_live (driver, &aoFmt, NULL)) == 
NULL) {
+                       BarUiMsg (player->settings, MSG_ERR, "Cannot open audio 
device.\n");
+                       return false;
+               }
        }
 
        return true;
@@ -390,6 +423,7 @@
                                /* enter drain mode */
                                drainMode = DRAIN;
                                avcodec_send_packet (cctx, NULL);
+                               debugPrint (DEBUG_AUDIO, "decoder entering 
drain mode after EOF\n");
                        } else if (pkt.stream_index != player->streamIdx) {
                                /* unused packet */
                                av_packet_unref (&pkt);
@@ -397,6 +431,8 @@
                        } else if (ret < 0) {
                                /* error, abort */
                                /* mark the EOF, so that BarAoPlayThread can 
quit*/
+                               debugPrint (DEBUG_AUDIO, "av_read_frame failed 
with code %i, sending "
+                                               "NULL frame\n", ret);
                                pthread_mutex_lock (&player->aoplayLock);
                                const int rt = av_buffersrc_add_frame 
(player->fabuf, NULL);
                                assert (rt == 0);
@@ -415,6 +451,7 @@
                                /* done draining */
                                drainMode = DONE;
                                /* mark the EOF*/
+                               debugPrint (DEBUG_AUDIO, "receive_frame got 
EOF, sending NULL frame\n");
                                pthread_mutex_lock (&player->aoplayLock);
                                const int rt = av_buffersrc_add_frame 
(player->fabuf, NULL);
                                assert (rt == 0);
@@ -440,10 +477,14 @@
                                pthread_mutex_lock (&player->aoplayLock);
                                bufferHealth = timeBase * (double) (frame->pts 
- player->lastTimestamp);
                                if (bufferHealth > minBufferHealth) {
+                                       debugPrint (DEBUG_AUDIO, "decoding 
buffer filled health %"PRIi64" minHealth %"PRIi64"\n",
+                                                       bufferHealth, 
minBufferHealth);
                                        /* Buffer get healthy, resume */
                                        pthread_cond_broadcast 
(&player->aoplayCond);
                                        /* Buffer is healthy enough, wait */
                                        pthread_cond_wait (&player->aoplayCond, 
&player->aoplayLock);
+                                       debugPrint (DEBUG_AUDIO, "ao play 
signalled it needs more data health %"PRIi64" minHealth %"PRIi64"\n",
+                                                       bufferHealth, 
minBufferHealth);
                                }
                                pthread_mutex_unlock (&player->aoplayLock);
                        } while (bufferHealth > minBufferHealth);
@@ -452,6 +493,7 @@
                av_packet_unref (&pkt);
        }
        av_frame_free (&frame);
+       debugPrint (DEBUG_AUDIO, "decoder is done, waiting for ao player\n");
        pthread_join (aoplaythread, NULL);
 
        return ret;
@@ -527,9 +569,12 @@
                if (ret == AVERROR_EOF || shouldQuit (player)) {
                        /* we are done here */
                        pthread_mutex_unlock (&player->aoplayLock);
+                       debugPrint (DEBUG_AUDIO, "ao player got EOF, 
exiting\n");
                        break;
                } else if (ret < 0) {
                        /* wait for more frames */
+                       debugPrint (DEBUG_AUDIO, "ao player is waiting for more 
frames after code %i (%s)\n",
+                                       ret, av_err2str (ret));
                        pthread_cond_broadcast (&player->aoplayCond);
                        pthread_cond_wait (&player->aoplayCond, 
&player->aoplayLock);
                        pthread_mutex_unlock (&player->aoplayLock);
@@ -551,8 +596,10 @@
                /* pausing */
                if (player->doPause) {
                        do {
+                               debugPrint (DEBUG_AUDIO, "ao player is 
paused\n");
                                pthread_cond_wait (&player->cond, 
&player->lock);
                        } while (player->doPause);
+                       debugPrint (DEBUG_AUDIO, "ao player continues\n");
                }
                pthread_mutex_unlock (&player->lock);
 
@@ -568,6 +615,7 @@
                av_frame_unref (filteredFrame);
        }
        av_frame_free (&filteredFrame);
+       debugPrint (DEBUG_AUDIO, "ao player is done\n");
 
        return (void *) 0;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/settings.c 
new/pianobar-2020.11.28/src/settings.c
--- old/pianobar-2019.02.14/src/settings.c      2019-02-14 09:37:03.000000000 
+0100
+++ new/pianobar-2020.11.28/src/settings.c      2020-11-28 11:32:58.000000000 
+0100
@@ -124,7 +124,9 @@
        free (settings->npSongFormat);
        free (settings->npStationFormat);
        free (settings->listSongFormat);
+       free (settings->timeFormat);
        free (settings->fifo);
+       free (settings->audioPipe);
        free (settings->rpcHost);
        free (settings->rpcTlsPort);
        free (settings->partnerUser);
@@ -174,6 +176,7 @@
        settings->npSongFormat = strdup ("\"%t\" by \"%a\" on \"%l\"%r%@%s");
        settings->npStationFormat = strdup ("Station \"%n\" (%i)");
        settings->listSongFormat = strdup ("%i) %a - %t%r");
+       settings->timeFormat = strdup ("%s%r/%t");
        settings->rpcHost = strdup (PIANO_RPC_HOST);
        settings->rpcTlsPort = strdup ("443");
        settings->partnerUser = strdup ("android");
@@ -182,7 +185,9 @@
        settings->inkey = strdup ("R=U!LH$O2B#");
        settings->outkey = strdup ("6#26FRL$ZWD");
        settings->fifo = BarGetXdgConfigDir (PACKAGE "/ctl");
+       settings->audioPipe = NULL;
        assert (settings->fifo != NULL);
+       settings->sampleRate = 0; /* default to stream sample rate */
 
        settings->msgFormat[MSG_NONE].prefix = NULL;
        settings->msgFormat[MSG_NONE].postfix = NULL;
@@ -386,11 +391,19 @@
                        } else if (streq ("format_list_song", key)) {
                                free (settings->listSongFormat);
                                settings->listSongFormat = strdup (val);
+                       } else if (streq ("format_time", key)) {
+                               free (settings->timeFormat);
+                               settings->timeFormat = strdup (val);
                        } else if (streq ("fifo", key)) {
                                free (settings->fifo);
                                settings->fifo = BarSettingsExpandTilde (val, 
userhome);
+                       } else if (streq ("audio_pipe", key)) {
+                               free (settings->audioPipe);
+                               settings->audioPipe = BarSettingsExpandTilde 
(val, userhome);
                        } else if (streq ("autoselect", key)) {
                                settings->autoselect = atoi (val);
+                       } else if (streq ("sample_rate", key)) {
+                               settings->sampleRate = atoi (val);
                        } else if (strncmp (formatMsgPrefix, key,
                                        strlen (formatMsgPrefix)) == 0) {
                                static const char *mapping[] = {"none", "info", 
"nowplaying",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/settings.h 
new/pianobar-2020.11.28/src/settings.h
--- old/pianobar-2019.02.14/src/settings.h      2019-02-14 09:37:03.000000000 
+0100
+++ new/pianobar-2020.11.28/src/settings.h      2020-11-28 11:32:58.000000000 
+0100
@@ -100,10 +100,12 @@
        char *atIcon;
        char *npSongFormat;
        char *npStationFormat;
-       char *listSongFormat;
+       char *listSongFormat, *timeFormat;
        char *fifo;
        char *rpcHost, *rpcTlsPort, *partnerUser, *partnerPassword, *device, 
*inkey, *outkey, *caBundle;
+       char *audioPipe;
        char keys[BAR_KS_COUNT];
+       int sampleRate;
        BarMsgFormatStr_t msgFormat[MSG_COUNT];
 } BarSettings_t;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/ui.c 
new/pianobar-2020.11.28/src/ui.c
--- old/pianobar-2019.02.14/src/ui.c    2019-02-14 09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/src/ui.c    2020-11-28 11:32:58.000000000 +0100
@@ -40,6 +40,7 @@
 #include <sys/wait.h>
 
 #include "ui.h"
+#include "debug.h"
 #include "ui_readline.h"
 
 typedef int (*BarSortFunc_t) (const void *, const void *);
@@ -189,6 +190,7 @@
                req->secure ? settings->rpcTlsPort : "80",
                req->urlPath);
        assert (ret >= 0 && ret <= (int) sizeof (url));
+       debugPrint (DEBUG_NETWORK, "??? %s\n", url);
 
        /* save the previous interrupt destination */
        prevint = interrupted;
@@ -261,6 +263,7 @@
        curl_slist_free_all (list);
 
        req->responseData = buffer.data;
+       debugPrint (DEBUG_NETWORK, "??? %s\n", req->responseData);
 
        interrupted = prevint;
 
@@ -649,7 +652,7 @@
  *     @param format characters
  *     @param replacement for each given format character
  */
-static void BarUiCustomFormat (char *dest, size_t destSize, const char *format,
+void BarUiCustomFormat (char *dest, size_t destSize, const char *format,
                const char *formatChars, const char **formatVals) {
        bool haveFormatChar = false;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/ui.h 
new/pianobar-2020.11.28/src/ui.h
--- old/pianobar-2019.02.14/src/ui.h    2019-02-14 09:37:03.000000000 +0100
+++ new/pianobar-2020.11.28/src/ui.h    2020-11-28 11:32:58.000000000 +0100
@@ -53,4 +53,6 @@
 bool BarUiPianoCall (BarApp_t * const, const PianoRequestType_t,
                void *, PianoReturn_t *, CURLcode *);
 void BarUiHistoryPrepend (BarApp_t *app, PianoSong_t *song);
+void BarUiCustomFormat (char *dest, size_t destSize, const char *format,
+               const char *formatChars, const char **formatVals);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/ui_act.c 
new/pianobar-2020.11.28/src/ui_act.c
--- old/pianobar-2019.02.14/src/ui_act.c        2019-02-14 09:37:03.000000000 
+0100
+++ new/pianobar-2020.11.28/src/ui_act.c        2020-11-28 11:32:58.000000000 
+0100
@@ -224,6 +224,15 @@
        }
 }
 
+static void drainPlaylist (BarApp_t * const app) {
+       BarUiDoSkipSong (&app->player);
+       if (app->playlist != NULL) {
+               /* drain playlist */
+               PianoDestroyPlaylist (PianoListNextP (app->playlist));
+               app->playlist->head.next = NULL;
+       }
+}
+
 /*     delete current station
  */
 BarUiActCallback(BarUiActDeleteStation) {
@@ -238,13 +247,7 @@
                BarUiMsg (&app->settings, MSG_INFO, "Deleting station... ");
                if (BarUiActDefaultPianoCall (PIANO_REQUEST_DELETE_STATION,
                                selStation) && selStation == app->curStation) {
-                       BarUiDoSkipSong (&app->player);
-                       if (app->playlist != NULL) {
-                               /* drain playlist */
-                               PianoDestroyPlaylist (PianoListNextP 
(app->playlist));
-                               app->playlist->head.next = NULL;
-                               selSong = NULL;
-                       }
+                       drainPlaylist (app);
                        app->nextStation = NULL;
                        /* XXX: usually we shoudn???t touch cur*, but 
DELETE_STATION destroys
                         * station struct */
@@ -478,12 +481,7 @@
                        "Select station: ", NULL, app->settings.autoselect);
        if (newStation != NULL) {
                app->nextStation = newStation;
-               BarUiDoSkipSong (&app->player);
-               if (app->playlist != NULL) {
-                       /* drain playlist */
-                       PianoDestroyPlaylist (PianoListNextP (app->playlist));
-                       app->playlist->head.next = NULL;
-               }
+               drainPlaylist (app);
        }
 }
 
@@ -773,7 +771,7 @@
        CURLcode wRet;
        PianoRequestDataGetStationInfo_t reqData;
        char selectBuf[2], allowedActions[6], *allowedPos = allowedActions;
-       char question[64];
+       char question[128];
 
        memset (&reqData, 0, sizeof (reqData));
        reqData.station = selStation;
@@ -816,18 +814,17 @@
                strcat (question, "[f]eedback");
                *allowedPos++ = 'f';
        }
+       /* station mode is always available */
+       if (allowedPos != allowedActions) {
+               strcat (question, "? ");
+       }
+       strcat (question, "Manage [m]ode? ");
+       *allowedPos++ = 'm';
+
        *allowedPos = '\0';
-       strcat (question, "? ");
 
        assert (strlen (question) < sizeof (question) / sizeof (*question));
 
-       /* nothing to see? */
-       if (allowedPos == allowedActions) {
-               BarUiMsg (&app->settings, MSG_ERR, "No seeds or feedback 
available yet.\n");
-               PianoDestroyStationInfo (&reqData.info);
-               return;
-       }
-
        BarUiMsg (&app->settings, MSG_QUESTION, "%s", question);
        if (BarReadline (selectBuf, sizeof (selectBuf), allowedActions, 
&app->input,
                                        BAR_RL_FULLRETURN, -1)) {
@@ -879,6 +876,47 @@
                                BarUiActDefaultPianoCall 
(PIANO_REQUEST_DELETE_FEEDBACK, song);
                                BarUiActDefaultEventcmd 
("stationdeletefeedback");
                        }
+               } else if (selectBuf[0] == 'm') {
+                       PianoRequestDataGetStationModes_t subReqData =
+                                       { .station = selStation };
+                       BarUiMsg (&app->settings, MSG_INFO, "Fetching modes... 
");
+                       BarUiActDefaultPianoCall 
(PIANO_REQUEST_GET_STATION_MODES,
+                                       &subReqData);
+                       BarUiActDefaultEventcmd ("stationgetmodes");
+
+                       const PianoStationMode_t *curMode = subReqData.retModes;
+                       unsigned int i = 0;
+                       PianoListForeachP (curMode) {
+                               BarUiMsg (&app->settings, MSG_LIST, "%2i) %s: 
%s%s\n", i,
+                                               curMode->name, 
curMode->description,
+                                               curMode->active ? " (active)" : 
"");
+                               i++;
+                       }
+
+                       BarUiMsg (&app->settings, MSG_QUESTION, "Pick a new 
mode: ");
+                       int selected;
+                       while (true) {
+                               if (BarReadlineInt (&selected, &app->input) == 
0) {
+                                       break;
+                               }
+
+                               const PianoStationMode_t * const selMode =
+                                               PianoListGetP 
(subReqData.retModes, selected);
+                               if (selMode != NULL) {
+                                       PianoRequestDataSetStationMode_t 
subReqDataSet =
+                                                       {.station = selStation, 
.id = selected};
+                                       BarUiMsg (&app->settings, MSG_INFO,
+                                                       "Selecting mode 
\"%s\"... ", selMode->name);
+                                       if (BarUiActDefaultPianoCall (
+                                                       
PIANO_REQUEST_SET_STATION_MODE, &subReqDataSet)) {
+                                               drainPlaylist (app);
+                                       }
+                                       BarUiActDefaultEventcmd 
("stationsetmode");
+                                       break;
+                               }
+                       }
+
+                       PianoDestroyStationMode (subReqData.retModes);
                }
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pianobar-2019.02.14/src/ui_dispatch.h 
new/pianobar-2020.11.28/src/ui_dispatch.h
--- old/pianobar-2019.02.14/src/ui_dispatch.h   2019-02-14 09:37:03.000000000 
+0100
+++ new/pianobar-2020.11.28/src/ui_dispatch.h   2020-11-28 11:32:58.000000000 
+0100
@@ -90,7 +90,7 @@
                                "act_voldown"},
                {')', BAR_DC_GLOBAL, BarUiActVolUp, "increase volume",
                                "act_volup"},
-               {'=', BAR_DC_STATION, BarUiActManageStation, "delete 
seeds/feedback",
+               {'=', BAR_DC_STATION, BarUiActManageStation, "manage station 
seeds/feedback/mode",
                                "act_managestation"},
                {' ', BAR_DC_GLOBAL | BAR_DC_STATION, BarUiActTogglePause, NULL,
                                "act_songpausetoggle2"},

Reply via email to