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 + + 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 = [32m%t[0m by [34m%a[0m on %l[31m%r[0m%@%s @@ -58,6 +60,7 @@ #tired_icon = [33mzZ[0m #format_nowplaying_station = Station [35m%n[0m #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"},