Hello community, here is the log from the commit of package pianobar for openSUSE:Leap:15.2 checked in at 2020-05-13 13:47:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:15.2/pianobar (Old) and /work/SRC/openSUSE:Leap:15.2/.pianobar.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "pianobar" Wed May 13 13:47:51 2020 rev:12 rq:804898 version:2019.02.14 Changes: -------- --- /work/SRC/openSUSE:Leap:15.2/pianobar/pianobar.changes 2020-01-15 15:43:56.687272182 +0100 +++ /work/SRC/openSUSE:Leap:15.2/.pianobar.new.2738/pianobar.changes 2020-05-13 13:47:57.533388223 +0200 @@ -1,0 +2,25 @@ +Thu Feb 14 12:53:46 UTC 2019 - Luigi Baldoni <[email protected]> + +- Update to version 2019.02.14 + * Fix MP3 playback (affects premium subscribers with audio + quality “high”) + version 2019.01.25 + * Implement audio buffering + +------------------------------------------------------------------- +Sun Jul 8 11:45:36 UTC 2018 - [email protected] + +- Update to version 2018.06.22 + * Happy 10th birthday pianobar! + * Add network timeouts and retries + * Fix cross-thread memory access + * Misc UI and documentation improvements + +- Use ffmpeg4 + +------------------------------------------------------------------- +Fri May 4 14:03:47 UTC 2018 - [email protected] + +- Use ffmpeg3 versions of pkgconfig(libav*) + +------------------------------------------------------------------- Old: ---- pianobar-2017.08.30.tar.gz New: ---- pianobar-2019.02.14.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ pianobar.spec ++++++ --- /var/tmp/diff_new_pack.MjiLLI/_old 2020-05-13 13:47:57.873388955 +0200 +++ /var/tmp/diff_new_pack.MjiLLI/_new 2020-05-13 13:47:57.873388955 +0200 @@ -1,7 +1,7 @@ # # spec file for package pianobar # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # Copyright (c) 2016 Packman team: http://packman.links2linux.org/ # # All modifications and additions to the file contributed by third parties @@ -13,27 +13,27 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # Name: pianobar -Version: 2017.08.30 +Version: 2019.02.14 Release: 0 Summary: Pandora Player License: MIT Group: Productivity/Multimedia/Sound/Utilities -Url: https://6xq.net/pianobar +URL: https://6xq.net/pianobar Source0: https://github.com/PromyLOPh/pianobar/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz BuildRequires: libgcrypt-devel BuildRequires: pkgconfig BuildRequires: pkgconfig(ao) BuildRequires: pkgconfig(gnutls) BuildRequires: pkgconfig(json) -BuildRequires: pkgconfig(libavcodec) -BuildRequires: pkgconfig(libavfilter) -BuildRequires: pkgconfig(libavformat) -BuildRequires: pkgconfig(libavutil) +BuildRequires: pkgconfig(libavcodec) >= 58.7.100 +BuildRequires: pkgconfig(libavfilter) >= 7.0.101 +BuildRequires: pkgconfig(libavformat) >= 58.0.102 +BuildRequires: pkgconfig(libavutil) >= 56.6.100 BuildRequires: pkgconfig(libcurl) %description ++++++ pianobar-2017.08.30.tar.gz -> pianobar-2019.02.14.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/.github/issue_template.md new/pianobar-2019.02.14/.github/issue_template.md --- old/pianobar-2017.08.30/.github/issue_template.md 1970-01-01 01:00:00.000000000 +0100 +++ new/pianobar-2019.02.14/.github/issue_template.md 2019-02-14 09:37:03.000000000 +0100 @@ -0,0 +1,19 @@ +### Subject of the issue +Briefly describe your issue here. + +### Your environment +* version of pianobar +* your Linux distribution and release version +* ffmpeg/libav version and the flags it was compiled with (if you compiled yourself) +* your config file + +### Steps to reproduce +Tell us how to reproduce this issue. + +### Expected behaviour +Tell us what should happen. + +### Actual behaviour +Tell us what happens instead, including a copy of all error messages. + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/ChangeLog new/pianobar-2019.02.14/ChangeLog --- old/pianobar-2017.08.30/ChangeLog 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/ChangeLog 2019-02-14 09:37:03.000000000 +0100 @@ -1,3 +1,18 @@ +Release 2019.02.14 + +- Fix MP3 playback (affects premium subscribers with audio quality “high”) + +Release 2019.01.25 + +- Implement audio buffering + +Release 2018.06.22 + +- Happy 10th birthday pianobar! +- Add network timeouts and retries +- Fix cross-thread memory access +- Misc UI and documentation improvements + Release 2017.08.30 - Support binding to specific network interface, see config option bind_to diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/README.md new/pianobar-2019.02.14/README.md --- old/pianobar-2017.08.30/README.md 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/README.md 2019-02-14 09:37:03.000000000 +0100 @@ -1,7 +1,6 @@ # pianobar -pianobar is a console client for the personalized web radio [Pandora] -([http://www.pandora.com](http://www.pandora.com)). +pianobar is a console client for the personalized web radio [Pandora](https://www.pandora.com). ### Features @@ -14,9 +13,9 @@ ### Source Code -The source code can be downloaded at [github.com](http://github.com/PromyLOPh/pianobar/) -or [6xq.net](http://6xq.net/projects/pianobar/). +The source code can be downloaded at [github.com](https://github.com/PromyLOPh/pianobar) +or [6xq.net](https://6xq.net/pianobar/). ### Download/Installation -There are community provided packages available for most Linux distributions (see your distribution’s package manager), Mac OS X ([MacPorts](http://trac.macports.org/browser/trunk/dports/audio/pianobar/Portfile) or [homebrew](http://brew.sh/)) and *BSD as well as a [native Windows port](https://github.com/thedmd/pianobar-windows). +There are community provided packages available for most Linux distributions (see your distribution’s package manager), macOS ([MacPorts](https://www.macports.org) or [Homebrew](https://brew.sh)) and *BSD as well as a [native Windows port](https://github.com/thedmd/pianobar-windows). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/contrib/config-example new/pianobar-2019.02.14/contrib/config-example --- old/pianobar-2017.08.30/contrib/config-example 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/contrib/config-example 2019-02-14 09:37:03.000000000 +0100 @@ -46,19 +46,19 @@ #event_command = /home/user/.config/pianobar/eventcmd #fifo = /tmp/pianobar #sort = quickmix_10_name_az -#love_icon = [+] -#ban_icon = [-] #volume = 0 #ca_bundle = /etc/ssl/certs/ca-certificates.crt #gain_mul = 1.0 # Format strings #format_nowplaying_song = [32m%t[0m by [34m%a[0m on %l[31m%r[0m%@%s +#format_nowplaying_song = [32m%t[0m by [34m%a[0m on %l%r%@%s +#ban_icon = [32m</3[0m +#love_icon = [31m<3[0m +#tired_icon = [33mzZ[0m #format_nowplaying_station = Station [35m%n[0m -#format_list_song = %i) %a - %t%r +#format_list_song = %i) %a - %t%r (%d)%@%s -# high-quality audio (192k mp3, for Pandora One subscribers only!) -#audio_quality = high #rpc_host = internal-tuner.pandora.com #partner_user = pandora one #partner_password = TVCKIBGS9AO9TSYLNNFUML0743LH82D diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/contrib/eventcmd-examples/multi.sh new/pianobar-2019.02.14/contrib/eventcmd-examples/multi.sh --- old/pianobar-2017.08.30/contrib/eventcmd-examples/multi.sh 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/contrib/eventcmd-examples/multi.sh 2019-02-14 09:37:03.000000000 +0100 @@ -1,7 +1,17 @@ #!/bin/bash # -# Executes all scripts located in ~/.config/pianobar/eventcmd.d/ as if they -# were called by pianobar directly +# Usage +# ===== +# +# Set +# +# event_command = /path/to/multi.sh +# +# in pianobar’s config file. Then create the directory +# ~/.config/pianobar/eventcmd.d/, move your eventcmd scripts there and make +# them executable (chmod +x). They will be run in an unspecified order the same +# way the would have been run if pianobar called them directly (i.e. using +# event_command). STDIN=`mktemp ${TMPDIR:-/tmp}/pianobar.XXXXXX` cat >> $STDIN diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/contrib/pianobar.1 new/pianobar-2019.02.14/contrib/pianobar.1 --- old/pianobar-2017.08.30/contrib/pianobar.1 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/contrib/pianobar.1 2019-02-14 09:37:03.000000000 +0100 @@ -194,7 +194,7 @@ .B act_songinfo. .TP -.B ban_icon = </3 +.B ban_icon = </3 Icon for banned songs. .TP @@ -209,6 +209,10 @@ .B route-nopull. .TP +.B buffer_seconds = 5 +Audio buffer size in seconds. + +.TP .B ca_bundle = /etc/ssl/certs/ca-certificates.crt Path to CA certifiate bundle, containing the root and intermediate certificates required to validate Pandora's SSL certificate. @@ -253,6 +257,17 @@ .B %r Rating icon +.B %d +Song duration + +.B %@ + +The at_icon + +.B %s + +Song’s station, if not the current station. + .TP .B format_msg_none = %s .TQ @@ -285,7 +300,7 @@ Album name .B %r -Rating icon (only love icon) +Rating icon .B %@ .B at_icon @@ -318,12 +333,12 @@ Keep a history of the last n songs (5, by default). You can rate these songs. .TP -.B love_icon = <3 +.B love_icon = <3 Icon for loved songs. .TP -.B max_player_errors = 5 -Amount of song download errors in a row after pianobar stops playback. +.B max_retry = 3 +Max failures for several actions before giving up. .TP .B partner_password = AC7IBG09A3DTSYM4R41UJWL07VLN8JI7 @@ -359,6 +374,14 @@ bottom) and name from z to a. .TP +.B timeout = 30 +Network operation timeout. + +.TP +.B tired_icon = zZ +Icon for temporarily suspended songs. + +.TP .B user = [email protected] Your pandora.com username. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/config.h new/pianobar-2019.02.14/src/config.h --- old/pianobar-2017.08.30/src/config.h 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/config.h 2019-02-14 09:37:03.000000000 +0100 @@ -3,7 +3,7 @@ /* package name */ #define PACKAGE "pianobar" -#define VERSION "2017.08.30" +#define VERSION "2019.02.14" /* glibc feature test macros, define _before_ including other files */ #define _POSIX_C_SOURCE 200809L @@ -12,10 +12,33 @@ * ffmpeg’s micro versions always start at 100, that’s how we can distinguish * ffmpeg and libav */ #include <libavfilter/version.h> +#include <libavformat/version.h> /* does graph_send_command exist (ffmpeg >=2.2) */ -#if LIBAVFILTER_VERSION_MAJOR >= 4 && \ +#if !defined(HAVE_AVFILTER_GRAPH_SEND_COMMAND) && \ + LIBAVFILTER_VERSION_MAJOR >= 4 && \ LIBAVFILTER_VERSION_MICRO >= 100 #define HAVE_AVFILTER_GRAPH_SEND_COMMAND #endif +/* explicit init is optional for ffmpeg>=4.0 */ +#if !defined(HAVE_AVFORMAT_NETWORK_INIT) && \ + LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 5, 100) && \ + LIBAVFORMAT_VERSION_MICRO >= 100 +#define HAVE_AVFORMAT_NETWORK_INIT +#endif + +/* dito */ +#if !defined(HAVE_AVFILTER_REGISTER_ALL) && \ + LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7, 14, 100) && \ + LIBAVFILTER_VERSION_MICRO >= 100 +#define HAVE_AVFILTER_REGISTER_ALL +#endif + +/* dito */ +#if !defined(HAVE_AV_REGISTER_ALL) && \ + LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100) && \ + LIBAVFORMAT_VERSION_MICRO >= 100 +#define HAVE_AV_REGISTER_ALL +#endif + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/libpiano/piano.c new/pianobar-2019.02.14/src/libpiano/piano.c --- old/pianobar-2017.08.30/src/libpiano/piano.c 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/libpiano/piano.c 2019-02-14 09:37:03.000000000 +0100 @@ -219,7 +219,11 @@ */ PianoStation_t *PianoFindStationById (PianoStation_t * const stations, const char * const searchStation) { - assert (searchStation != NULL); + assert (stations != NULL); + + if (searchStation == NULL) { + return NULL; + } PianoStation_t *currStation = stations; PianoListForeachP (currStation) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/libpiano/piano.h new/pianobar-2019.02.14/src/libpiano/piano.h --- old/pianobar-2017.08.30/src/libpiano/piano.h 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/libpiano/piano.h 2019-02-14 09:37:03.000000000 +0100 @@ -65,7 +65,8 @@ typedef enum { PIANO_RATE_NONE = 0, PIANO_RATE_LOVE = 1, - PIANO_RATE_BAN = 2 + PIANO_RATE_BAN = 2, + PIANO_RATE_TIRED = 3, } PianoSongRating_t; /* UNKNOWN should be 0, because memset sets audio format to 0 */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/libpiano/request.c new/pianobar-2019.02.14/src/libpiano/request.c --- old/pianobar-2017.08.30/src/libpiano/request.c 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/libpiano/request.c 2019-02-14 09:37:03.000000000 +0100 @@ -116,6 +116,7 @@ case PIANO_REQUEST_GET_STATIONS: { /* get stations, user must be authenticated */ assert (ph->user.listenerId != NULL); + method = "user.getStationList"; break; } @@ -146,7 +147,8 @@ assert (reqData != NULL); assert (reqData->trackToken != NULL); assert (reqData->stationId != NULL); - assert (reqData->rating != PIANO_RATE_NONE); + assert (reqData->rating != PIANO_RATE_NONE && + reqData->rating != PIANO_RATE_TIRED); json_object_object_add (j, "stationToken", json_object_new_string (reqData->stationId)); @@ -357,6 +359,8 @@ json_object_new_string (reqData->station->id)); json_object_object_add (j, "includeExtendedAttributes", json_object_new_boolean (true)); + json_object_object_add (j, "includeExtraParams", + json_object_new_boolean (true)); method = "station.getStation"; break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/libpiano/response.c new/pianobar-2019.02.14/src/libpiano/response.c --- old/pianobar-2017.08.30/src/libpiano/response.c 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/libpiano/response.c 2019-02-14 09:37:03.000000000 +0100 @@ -436,8 +436,13 @@ break; } + case PIANO_REQUEST_ADD_TIRED_SONG: { + PianoSong_t * const song = req->data; + song->rating = PIANO_RATE_TIRED; + break; + } + case PIANO_REQUEST_ADD_SEED: - case PIANO_REQUEST_ADD_TIRED_SONG: case PIANO_REQUEST_SET_QUICKMIX: case PIANO_REQUEST_BOOKMARK_SONG: case PIANO_REQUEST_BOOKMARK_ARTIST: @@ -634,6 +639,11 @@ feedbackSong->rating = getBoolDefault (s, "isPositive", false) ? PIANO_RATE_LOVE : PIANO_RATE_BAN; + json_object *v; + feedbackSong->length = + json_object_object_get_ex (s, "trackLength", &v) ? + json_object_get_int (v) : 0; + info->feedback = PianoListAppendP (info->feedback, feedbackSong); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/main.c new/pianobar-2019.02.14/src/main.c --- old/pianobar-2017.08.30/src/main.c 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/main.c 2019-02-14 09:37:03.000000000 +0100 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2013 +Copyright (c) 2008-2018 Lars-Dominik Braun <[email protected]> Permission is hereby granted, free of charge, to any person obtaining a copy @@ -252,15 +252,12 @@ strncmp (curSong->audioUrl, httpPrefix, strlen (httpPrefix)) != 0) { BarUiMsg (&app->settings, MSG_ERR, "Invalid song url.\n"); } else { - /* setup player */ - memset (&app->player, 0, sizeof (app->player)); + player_t * const player = &app->player; + BarPlayerReset (player); app->player.url = curSong->audioUrl; app->player.gain = curSong->fileGain; - app->player.settings = &app->settings; app->player.songDuration = curSong->length; - pthread_mutex_init (&app->player.pauseMutex, NULL); - pthread_cond_init (&app->player.pauseCond, NULL); assert (interrupted == &app->doQuit); interrupted = &app->player.interrupted; @@ -291,14 +288,12 @@ /* FIXME: pthread_join blocks everything if network connection * is hung up e.g. */ pthread_join (*playerThread, &threadRet); - pthread_cond_destroy (&app->player.pauseCond); - pthread_mutex_destroy (&app->player.pauseMutex); if (threadRet == (void *) PLAYER_RET_OK) { app->playerErrors = 0; } else if (threadRet == (void *) PLAYER_RET_SOFTFAIL) { ++app->playerErrors; - if (app->playerErrors >= app->settings.maxPlayerErrors) { + if (app->playerErrors >= app->settings.maxRetry) { /* don't continue playback if thread reports too many error */ app->nextStation = NULL; } @@ -306,10 +301,10 @@ app->nextStation = NULL; } - memset (&app->player, 0, sizeof (app->player)); - assert (interrupted == &app->player.interrupted); interrupted = &app->doQuit; + + app->player.mode = PLAYER_DEAD; } /* print song duration @@ -317,19 +312,24 @@ static void BarMainPrintTime (BarApp_t *app) { unsigned int songRemaining; char sign; + player_t * const player = &app->player; + + pthread_mutex_lock (&player->lock); + const unsigned int songDuration = player->songDuration; + const unsigned int songPlayed = player->songPlayed; + pthread_mutex_unlock (&player->lock); - if (app->player.songPlayed <= app->player.songDuration) { - songRemaining = app->player.songDuration - app->player.songPlayed; + if (songPlayed <= songDuration) { + songRemaining = songDuration - songPlayed; sign = '-'; } else { /* longer than expected */ - songRemaining = app->player.songPlayed - app->player.songDuration; + songRemaining = songPlayed - songDuration; sign = '+'; } BarUiMsg (&app->settings, MSG_TIME, "%c%02u:%02u/%02u:%02u\r", sign, songRemaining / 60, songRemaining % 60, - app->player.songDuration / 60, - app->player.songDuration % 60); + songDuration / 60, songDuration % 60); } /* main loop @@ -351,14 +351,12 @@ BarMainGetInitialStation (app); - /* little hack, needed to signal: hey! we need a playlist, but don't - * free anything (there is nothing to be freed yet) */ - memset (&app->player, 0, sizeof (app->player)); + player_t * const player = &app->player; while (!app->doQuit) { /* song finished playing, clean up things/scrobble song */ - if (app->player.mode == PLAYER_FINISHED) { - if (app->player.interrupted != 0) { + if (BarPlayerGetMode (player) == PLAYER_FINISHED) { + if (player->interrupted != 0) { app->doQuit = 1; } BarMainPlayerCleanup (app, &playerThread); @@ -366,7 +364,7 @@ /* check whether player finished playing and start playing new * song */ - if (app->player.mode == PLAYER_DEAD) { + if (BarPlayerGetMode (player) == PLAYER_DEAD) { /* what's next? */ if (app->playlist != NULL) { PianoSong_t *histsong = app->playlist; @@ -389,12 +387,12 @@ BarMainHandleUserInput (app); /* show time */ - if (app->player.mode == PLAYER_PLAYING) { + if (BarPlayerGetMode (player) == PLAYER_PLAYING) { BarMainPrintTime (app); } } - if (app->player.mode != PLAYER_DEAD) { + if (BarPlayerGetMode (player) != PLAYER_DEAD) { pthread_join (playerThread, NULL); } } @@ -433,7 +431,7 @@ gcry_check_version (NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); - BarPlayerInit (); + BarPlayerInit (&app.player, &app.settings); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); @@ -502,7 +500,7 @@ PianoDestroyPlaylist (app.playlist); curl_easy_cleanup (app.http); curl_global_cleanup (); - BarPlayerDestroy (); + BarPlayerDestroy (&app.player); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/player.c new/pianobar-2019.02.14/src/player.c --- old/pianobar-2017.08.30/src/player.c 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/player.c 2019-02-14 09:37:03.000000000 +0100 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2014 +Copyright (c) 2008-2018 Lars-Dominik Braun <[email protected]> Permission is hereby granted, free of charge, to any person obtaining a copy @@ -21,7 +21,16 @@ THE SOFTWARE. */ -/* receive/play audio stream */ +/* receive/play audio stream. + * + * There are two threads involved here: + * BarPlayerThread + * Sets up the stream and fetches the data into a ffmpeg buffersrc + * BarAoPlayThread + * Reads data from the filter chain’s sink and hands it over to libao for + * playback. + * + */ #include "config.h" @@ -36,7 +45,6 @@ #include <libavcodec/avcodec.h> #include <libavutil/avutil.h> #include <libavfilter/avfilter.h> -#include <libavfilter/avfiltergraph.h> #include <libavfilter/buffersink.h> #include <libavfilter/buffersrc.h> #ifdef HAVE_LIBAVFILTER_AVCODEC_H @@ -62,23 +70,59 @@ } /* global initialization - * - * XXX: in theory we can select the filters/formats we want to support, but - * this does not work in practise. */ -void BarPlayerInit () { +void BarPlayerInit (player_t * const p, const BarSettings_t * const settings) { ao_initialize (); av_log_set_level (AV_LOG_FATAL); +#ifdef HAVE_AV_REGISTER_ALL av_register_all (); +#endif +#ifdef HAVE_AVFILTER_REGISTER_ALL avfilter_register_all (); +#endif +#ifdef HAVE_AVFORMAT_NETWORK_INIT avformat_network_init (); +#endif + + pthread_mutex_init (&p->lock, NULL); + pthread_cond_init (&p->cond, NULL); + pthread_mutex_init (&p->aoplayLock, NULL); + pthread_cond_init (&p->aoplayCond, NULL); + BarPlayerReset (p); + p->settings = settings; } -void BarPlayerDestroy () { +void BarPlayerDestroy (player_t * const p) { + pthread_cond_destroy (&p->cond); + pthread_mutex_destroy (&p->lock); + pthread_cond_destroy (&p->aoplayCond); + pthread_mutex_destroy (&p->aoplayLock); + +#ifdef HAVE_AVFORMAT_NETWORK_INIT avformat_network_deinit (); +#endif ao_shutdown (); } +void BarPlayerReset (player_t * const p) { + p->doQuit = false; + p->doPause = false; + p->songDuration = 0; + p->songPlayed = 0; + p->mode = PLAYER_DEAD; + p->fvolume = NULL; + p->fgraph = NULL; + p->fctx = NULL; + p->st = NULL; + p->cctx = NULL; + p->fbufsink = NULL; + p->fabuf = NULL; + p->streamIdx = -1; + p->lastTimestamp = 0; + p->interrupted = 0; + p->aoDev = NULL; +} + /* Update volume filter */ void BarPlayerSetVolume (player_t * const player) { @@ -122,7 +166,9 @@ assert (player != NULL); if (player->interrupted > 1) { /* got a sigint multiple times, quit pianobar (handled by main.c). */ + pthread_mutex_lock (&player->lock); player->doQuit = true; + pthread_mutex_unlock (&player->lock); return 1; } else if (player->interrupted != 0) { /* the request is retried with the same player context */ @@ -145,8 +191,16 @@ player->fctx->interrupt_callback.callback = intCb; player->fctx->interrupt_callback.opaque = player; + /* in microseconds */ + unsigned long int timeout = player->settings->timeout*1000000; + char timeoutStr[16]; + ret = snprintf (timeoutStr, sizeof (timeoutStr), "%lu", timeout); + assert (ret < sizeof (timeoutStr)); + AVDictionary *options = NULL; + av_dict_set (&options, "timeout", timeoutStr, 0); + assert (player->url != NULL); - if ((ret = avformat_open_input (&player->fctx, player->url, NULL, NULL)) < 0) { + if ((ret = avformat_open_input (&player->fctx, player->url, NULL, &options)) < 0) { softfail ("Unable to open audio file"); } @@ -190,9 +244,12 @@ av_seek_frame (player->fctx, player->streamIdx, player->lastTimestamp, 0); } - player->songPlayed = 0; - player->songDuration = av_q2d (player->st->time_base) * + const unsigned int songDuration = av_q2d (player->st->time_base) * (double) player->st->duration; + pthread_mutex_lock (&player->lock); + player->songPlayed = 0; + player->songDuration = songDuration; + pthread_mutex_unlock (&player->lock); return true; } @@ -218,14 +275,14 @@ av_get_sample_fmt_name (player->cctx->sample_fmt), cp->channel_layout); if ((ret = avfilter_graph_create_filter (&player->fabuf, - avfilter_get_by_name ("abuffer"), NULL, strbuf, NULL, + avfilter_get_by_name ("abuffer"), "source", strbuf, NULL, player->fgraph)) < 0) { softfail ("create_filter abuffer"); } /* volume */ if ((ret = avfilter_graph_create_filter (&player->fvolume, - avfilter_get_by_name ("volume"), NULL, "0dB", NULL, + avfilter_get_by_name ("volume"), "volume", "0dB", NULL, player->fgraph)) < 0) { softfail ("create_filter volume"); } @@ -235,14 +292,14 @@ snprintf (strbuf, sizeof (strbuf), "sample_fmts=%s", av_get_sample_fmt_name (avformat)); if ((ret = avfilter_graph_create_filter (&fafmt, - avfilter_get_by_name ("aformat"), NULL, strbuf, NULL, + avfilter_get_by_name ("aformat"), "format", strbuf, NULL, player->fgraph)) < 0) { softfail ("create_filter aformat"); } /* abuffersink */ if ((ret = avfilter_graph_create_filter (&player->fbufsink, - avfilter_get_by_name ("abuffersink"), NULL, NULL, NULL, + avfilter_get_by_name ("abuffersink"), "sink", NULL, NULL, player->fgraph)) < 0) { softfail ("create_filter abuffersink"); } @@ -283,10 +340,34 @@ return true; } +/* Operating on shared variables and must be protected by mutex + */ + +static bool shouldQuit (player_t * const player) { + pthread_mutex_lock (&player->lock); + const bool ret = player->doQuit; + pthread_mutex_unlock (&player->lock); + return ret; +} + +static void changeMode (player_t * const player, unsigned int mode) { + pthread_mutex_lock (&player->lock); + player->mode = mode; + pthread_mutex_unlock (&player->lock); +} + +BarPlayerMode BarPlayerGetMode (player_t * const player) { + pthread_mutex_lock (&player->lock); + const BarPlayerMode ret = player->mode; + pthread_mutex_unlock (&player->lock); + return ret; +} + /* decode and play stream. returns 0 or av error code. */ static int play (player_t * const player) { assert (player != NULL); + const int64_t minBufferHealth = player->settings->bufferSecs; AVPacket pkt; AVCodecContext * const cctx = player->cctx; @@ -294,15 +375,15 @@ pkt.data = NULL; pkt.size = 0; - AVFrame *frame = NULL, *filteredFrame = NULL; + AVFrame *frame = NULL; frame = av_frame_alloc (); assert (frame != NULL); - filteredFrame = av_frame_alloc (); - assert (filteredFrame != NULL); - + pthread_t aoplaythread; + pthread_create (&aoplaythread, NULL, BarAoPlayThread, player); enum { FILL, DRAIN, DONE } drainMode = FILL; int ret = 0; - while (!player->doQuit && drainMode != DONE) { + const double timeBase = av_q2d (player->st->time_base); + while (!shouldQuit (player) && drainMode != DONE) { if (drainMode == FILL) { ret = av_read_frame (player->fctx, &pkt); if (ret == AVERROR_EOF) { @@ -315,6 +396,12 @@ continue; } else if (ret < 0) { /* error, abort */ + /* mark the EOF, so that BarAoPlayThread can quit*/ + pthread_mutex_lock (&player->aoplayLock); + const int rt = av_buffersrc_add_frame (player->fabuf, NULL); + assert (rt == 0); + pthread_cond_broadcast (&player->aoplayCond); + pthread_mutex_unlock (&player->aoplayLock); break; } else { /* fill buffer */ @@ -322,22 +409,17 @@ } } - /* pausing */ - pthread_mutex_lock (&player->pauseMutex); - if (player->doPause) { - av_read_pause (player->fctx); - do { - pthread_cond_wait (&player->pauseCond, &player->pauseMutex); - } while (player->doPause); - av_read_play (player->fctx); - } - pthread_mutex_unlock (&player->pauseMutex); - - while (!player->doQuit) { + while (!shouldQuit (player)) { ret = avcodec_receive_frame (cctx, frame); if (ret == AVERROR_EOF) { /* done draining */ drainMode = DONE; + /* mark the EOF*/ + pthread_mutex_lock (&player->aoplayLock); + const int rt = av_buffersrc_add_frame (player->fabuf, NULL); + assert (rt == 0); + pthread_cond_broadcast (&player->aoplayCond); + pthread_mutex_unlock (&player->aoplayLock); break; } else if (ret != 0) { /* no more output */ @@ -348,33 +430,29 @@ if (frame->pts == (int64_t) AV_NOPTS_VALUE) { frame->pts = 0; } + pthread_mutex_lock (&player->aoplayLock); ret = av_buffersrc_write_frame (player->fabuf, frame); assert (ret >= 0); - - while (true) { - if (av_buffersink_get_frame (player->fbufsink, filteredFrame) < 0) { - /* try again next frame */ - break; + pthread_mutex_unlock (&player->aoplayLock); + + int64_t bufferHealth = 0; + do { + pthread_mutex_lock (&player->aoplayLock); + bufferHealth = timeBase * (double) (frame->pts - player->lastTimestamp); + if (bufferHealth > minBufferHealth) { + /* Buffer get healthy, resume */ + pthread_cond_broadcast (&player->aoplayCond); + /* Buffer is healthy enough, wait */ + pthread_cond_wait (&player->aoplayCond, &player->aoplayLock); } - - const int numChannels = av_get_channel_layout_nb_channels ( - filteredFrame->channel_layout); - const int bps = av_get_bytes_per_sample(filteredFrame->format); - ao_play (player->aoDev, (char *) filteredFrame->data[0], - filteredFrame->nb_samples * numChannels * bps); - - av_frame_unref (filteredFrame); - } + pthread_mutex_unlock (&player->aoplayLock); + } while (bufferHealth > minBufferHealth); } - player->songPlayed = av_q2d (player->st->time_base) * (double) pkt.pts; - player->lastTimestamp = pkt.pts; - av_packet_unref (&pkt); } - - av_frame_free (&filteredFrame); av_frame_free (&frame); + pthread_join (aoplaythread, NULL); return ret; } @@ -410,7 +488,7 @@ retry = false; if (openStream (player)) { if (openFilter (player) && openDevice (player)) { - player->mode = PLAYER_PLAYING; + changeMode (player, PLAYER_PLAYING); BarPlayerSetVolume (player); retry = play (player) == AVERROR_INVALIDDATA && !player->interrupted; @@ -422,12 +500,74 @@ /* stream not found */ pret = PLAYER_RET_SOFTFAIL; } - player->mode = PLAYER_WAITING; + changeMode (player, PLAYER_WAITING); finish (player); } while (retry); - player->mode = PLAYER_FINISHED; + changeMode (player, PLAYER_FINISHED); return (void *) pret; } +void *BarAoPlayThread (void *data) { + assert (data != NULL); + + player_t * const player = data; + + AVFrame *filteredFrame = NULL; + filteredFrame = av_frame_alloc (); + assert (filteredFrame != NULL); + + int ret; + const double timeBase = av_q2d (av_buffersink_get_time_base (player->fbufsink)), + timeBaseSt = av_q2d (player->st->time_base); + while (!shouldQuit(player)) { + pthread_mutex_lock (&player->aoplayLock); + ret = av_buffersink_get_frame (player->fbufsink, filteredFrame); + if (ret == AVERROR_EOF || shouldQuit (player)) { + /* we are done here */ + pthread_mutex_unlock (&player->aoplayLock); + break; + } else if (ret < 0) { + /* wait for more frames */ + pthread_cond_broadcast (&player->aoplayCond); + pthread_cond_wait (&player->aoplayCond, &player->aoplayLock); + pthread_mutex_unlock (&player->aoplayLock); + continue; + } + pthread_mutex_unlock (&player->aoplayLock); + + const int numChannels = av_get_channel_layout_nb_channels ( + filteredFrame->channel_layout); + const int bps = av_get_bytes_per_sample (filteredFrame->format); + ao_play (player->aoDev, (char *) filteredFrame->data[0], + filteredFrame->nb_samples * numChannels * bps); + + const double timestamp = (double) filteredFrame->pts * timeBase; + const unsigned int songPlayed = timestamp; + + pthread_mutex_lock (&player->lock); + player->songPlayed = songPlayed; + /* pausing */ + if (player->doPause) { + do { + pthread_cond_wait (&player->cond, &player->lock); + } while (player->doPause); + } + pthread_mutex_unlock (&player->lock); + + /* lastTimestamp must be the last pts, but expressed in terms of + * st->time_base, not the sink’s time_base. */ + const int64_t lastTimestamp = timestamp/timeBaseSt; + /* notify download thread, we might need more data */ + pthread_mutex_lock (&player->aoplayLock); + player->lastTimestamp = lastTimestamp; + pthread_cond_broadcast (&player->aoplayCond); + pthread_mutex_unlock (&player->aoplayLock); + + av_frame_unref (filteredFrame); + } + av_frame_free (&filteredFrame); + + return (void *) 0; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/player.h new/pianobar-2019.02.14/src/player.h --- old/pianobar-2017.08.30/src/player.h 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/player.h 2019-02-14 09:37:03.000000000 +0100 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2014 +Copyright (c) 2008-2018 Lars-Dominik Braun <[email protected]> Permission is hereby granted, free of charge, to any person obtaining a copy @@ -34,28 +34,34 @@ #include <ao/ao.h> #include <libavformat/avformat.h> #include <libavfilter/avfilter.h> -#include <libavfilter/avfiltergraph.h> #include <piano.h> #include "settings.h" +typedef enum { + /* not running */ + PLAYER_DEAD = 0, + /* running, but not ready to play music yet */ + PLAYER_WAITING, + /* currently playing a song */ + PLAYER_PLAYING, + /* finished playing a song */ + PLAYER_FINISHED, +} BarPlayerMode; + typedef struct { - /* protected by pauseMutex */ - volatile bool doQuit; - volatile bool doPause; - pthread_mutex_t pauseMutex; - pthread_cond_t pauseCond; - - enum { - /* not running */ - PLAYER_DEAD = 0, - /* running, but not ready to play music yet */ - PLAYER_WAITING, - /* currently playing a song */ - PLAYER_PLAYING, - /* finished playing a song */ - PLAYER_FINISHED, - } mode; + /* public attributes protected by mutex */ + pthread_mutex_t lock, aoplayLock; + pthread_cond_t cond, aoplayCond; /* broadcast changes to doPause */ + bool doQuit, doPause; + + /* measured in seconds */ + unsigned int songDuration; + unsigned int songPlayed; + + BarPlayerMode mode; + + /* private attributes _not_ protected by mutex */ /* libav */ AVFilterContext *fvolume; @@ -70,20 +76,19 @@ ao_device *aoDev; - /* settings */ + /* settings (must be set before starting the thread) */ double gain; char *url; const BarSettings_t *settings; - - /* measured in seconds */ - volatile unsigned int songDuration; - volatile unsigned int songPlayed; } player_t; enum {PLAYER_RET_OK = 0, PLAYER_RET_HARDFAIL = 1, PLAYER_RET_SOFTFAIL = 2}; void *BarPlayerThread (void *data); +void *BarAoPlayThread (void *data); void BarPlayerSetVolume (player_t * const player); -void BarPlayerInit (); -void BarPlayerDestroy (); +void BarPlayerInit (player_t * const p, const BarSettings_t * const settings); +void BarPlayerReset (player_t * const p); +void BarPlayerDestroy (player_t * const p); +BarPlayerMode BarPlayerGetMode (player_t * const player); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/settings.c new/pianobar-2019.02.14/src/settings.c --- old/pianobar-2017.08.30/src/settings.c 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/settings.c 2019-02-14 09:37:03.000000000 +0100 @@ -119,6 +119,7 @@ free (settings->eventCmd); free (settings->loveIcon); free (settings->banIcon); + free (settings->tiredIcon); free (settings->atIcon); free (settings->npSongFormat); free (settings->npStationFormat); @@ -160,11 +161,15 @@ settings->autoselect = true; settings->history = 5; settings->volume = 0; + settings->timeout = 30; /* seconds */ settings->gainMul = 1.0; - settings->maxPlayerErrors = 5; + /* should be > 4, otherwise expired audio urls (403) can stop playback */ + settings->maxRetry = 5; + settings->bufferSecs = 5; settings->sortOrder = BAR_SORT_NAME_AZ; settings->loveIcon = strdup (" <3"); settings->banIcon = strdup (" </3"); + settings->tiredIcon = strdup (" zZ"); settings->atIcon = strdup (" @ "); settings->npSongFormat = strdup ("\"%t\" by \"%a\" on \"%l\"%r%@%s"); settings->npStationFormat = strdup ("Station \"%n\" (%i)"); @@ -335,8 +340,12 @@ settings->eventCmd = BarSettingsExpandTilde (val, userhome); } else if (streq ("history", key)) { settings->history = atoi (val); - } else if (streq ("max_player_errors", key)) { - settings->maxPlayerErrors = atoi (val); + } else if (streq ("max_retry", key)) { + settings->maxRetry = atoi (val); + } else if (streq ("timeout", key)) { + settings->timeout = atoi (val); + } else if (streq ("buffer_seconds", key)) { + settings->bufferSecs = atoi (val); } else if (streq ("sort", key)) { size_t i; static const char *mapping[] = {"name_az", @@ -358,6 +367,9 @@ } else if (streq ("ban_icon", key)) { free (settings->banIcon); settings->banIcon = strdup (val); + } else if (streq ("tired_icon", key)) { + free (settings->tiredIcon); + settings->tiredIcon = strdup (val); } else if (streq ("at_icon", key)) { free (settings->atIcon); settings->atIcon = strdup (val); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/settings.h new/pianobar-2019.02.14/src/settings.h --- old/pianobar-2017.08.30/src/settings.h 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/settings.h 2019-02-14 09:37:03.000000000 +0100 @@ -84,7 +84,7 @@ typedef struct { bool autoselect; - unsigned int history, maxPlayerErrors; + unsigned int history, maxRetry, timeout, bufferSecs; int volume; float gainMul; BarStationSorting_t sortOrder; @@ -96,8 +96,7 @@ char *bindTo; char *autostartStation; char *eventCmd; - char *loveIcon; - char *banIcon; + char *loveIcon, *banIcon, *tiredIcon; char *atIcon; char *npSongFormat; char *npStationFormat; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/ui.c new/pianobar-2019.02.14/src/ui.c --- old/pianobar-2017.08.30/src/ui.c 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/ui.c 2019-02-14 09:37:03.000000000 +0100 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2013 +Copyright (c) 2008-2018 Lars-Dominik Braun <[email protected]> Permission is hereby granted, free of charge, to any person obtaining a copy @@ -205,6 +205,7 @@ setAndCheck (CURLOPT_PROGRESSDATA, &lint); setAndCheck (CURLOPT_NOPROGRESS, 0); setAndCheck (CURLOPT_POST, 1); + setAndCheck (CURLOPT_TIMEOUT, settings->timeout); if (settings->caBundle != NULL) { setAndCheck (CURLOPT_CAINFO, settings->caBundle); } @@ -241,7 +242,21 @@ list = curl_slist_append (list, "Content-Type: text/plain"); setAndCheck (CURLOPT_HTTPHEADER, list); - httpret = curl_easy_perform (http); + unsigned int retry = 0; + do { + httpret = curl_easy_perform (http); + ++retry; + if (httpret == CURLE_OPERATION_TIMEDOUT) { + free (buffer.data); + buffer.data = NULL; + buffer.pos = 0; + if (retry >= settings->maxRetry) { + break; + } + } else { + break; + } + } while (true); curl_slist_free_all (list); @@ -486,20 +501,21 @@ } /* let user pick one song - * @param pianobar settings + * @param app * @param song list * @param input fds * @return pointer to selected item in song list or NULL */ -PianoSong_t *BarUiSelectSong (const BarSettings_t *settings, +PianoSong_t *BarUiSelectSong (const BarApp_t * const app, PianoSong_t *startSong, BarReadlineFds_t *input) { + const BarSettings_t * const settings = &app->settings; PianoSong_t *tmpSong = NULL; char buf[100]; memset (buf, 0, sizeof (buf)); do { - BarUiListSongs (settings, startSong, buf); + BarUiListSongs (app, startSong, buf); BarUiMsg (settings, MSG_QUESTION, "Select song: "); if (BarReadlineStr (buf, sizeof (buf), input, BAR_RL_DEFAULT) == 0) { @@ -598,7 +614,7 @@ musicId = strdup (tmpArtist->musicId); } } else if (*selectBuf == 't') { - tmpSong = BarUiSelectSong (&app->settings, searchResult.songs, + tmpSong = BarUiSelectSong (app, searchResult.songs, &app->input); if (tmpSong != NULL) { musicId = strdup (tmpSong->musicId); @@ -606,7 +622,7 @@ } } else if (searchResult.songs != NULL) { /* songs found */ - tmpSong = BarUiSelectSong (&app->settings, searchResult.songs, + tmpSong = BarUiSelectSong (app, searchResult.songs, &app->input); if (tmpSong != NULL) { musicId = strdup (tmpSong->musicId); @@ -714,6 +730,23 @@ BarUiMsg (settings, MSG_PLAYING, "%s", outstr); } +static const char *ratingToIcon (const BarSettings_t * const settings, + const PianoSong_t * const song) { + switch (song->rating) { + case PIANO_RATE_LOVE: + return settings->loveIcon; + + case PIANO_RATE_BAN: + return settings->banIcon; + + case PIANO_RATE_TIRED: + return settings->tiredIcon; + + default: + return ""; + } +} + /* Print song infos (artist, title, album, loved) * @param pianobar settings * @param the song @@ -723,7 +756,7 @@ const PianoSong_t *song, const PianoStation_t *station) { char outstr[512]; const char *vals[] = {song->title, song->artist, song->album, - (song->rating == PIANO_RATE_LOVE) ? settings->loveIcon : "", + ratingToIcon (settings, song), station != NULL ? settings->atIcon : "", station != NULL ? station->name : "", song->detailUrl}; @@ -740,23 +773,44 @@ * @param artist/song filter string * @return # of songs */ -size_t BarUiListSongs (const BarSettings_t *settings, +size_t BarUiListSongs (const BarApp_t * const app, const PianoSong_t *song, const char *filter) { + const BarSettings_t * const settings = &app->settings; size_t i = 0; - char digits[8]; PianoListForeachP (song) { if (filter == NULL || (filter != NULL && (BarStrCaseStr (song->artist, filter) != NULL || BarStrCaseStr (song->title, filter) != NULL))) { - char outstr[512]; + const char * const deleted = "(deleted)", * const empty = ""; + const char *stationName = empty; + + const PianoStation_t * const station = + PianoFindStationById (app->ph.stations, song->stationId); + if (station != NULL && station != app->curStation) { + stationName = station->name; + } else if (station == NULL && song->stationId != NULL) { + stationName = deleted; + } + + char outstr[512], digits[8], duration[8] = "??:??"; const char *vals[] = {digits, song->artist, song->title, - (song->rating == PIANO_RATE_LOVE) ? settings->loveIcon : - ((song->rating == PIANO_RATE_BAN) ? settings->banIcon : "")}; + ratingToIcon (settings, song), + duration, + stationName != empty ? settings->atIcon : "", + stationName, + }; + /* pre-format a few strings */ snprintf (digits, sizeof (digits) / sizeof (*digits), "%2zu", i); + const unsigned int length = song->length; + if (length > 0) { + snprintf (duration, sizeof (duration), "%02u:%02u", + length / 60, length % 60); + } + BarUiCustomFormat (outstr, sizeof (outstr), settings->listSongFormat, - "iatr", vals); + "iatrd@s", vals); BarUiAppendNewline (outstr, sizeof (outstr)); BarUiMsg (settings, MSG_LIST, "%s", outstr); } @@ -775,7 +829,7 @@ */ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type, const PianoStation_t *curStation, const PianoSong_t *curSong, - const player_t * const player, PianoStation_t *stations, + player_t * const player, PianoStation_t *stations, PianoReturn_t pRet, CURLcode wRet) { pid_t chld; int pipeFd[2]; @@ -816,6 +870,11 @@ songStation = PianoFindStationById (stations, curSong->stationId); } + pthread_mutex_lock (&player->lock); + const unsigned int songDuration = player->songDuration; + const unsigned int songPlayed = player->songPlayed; + pthread_mutex_unlock (&player->lock); + fprintf (pipeWriteFd, "artist=%s\n" "title=%s\n" @@ -841,8 +900,8 @@ PianoErrorToStr (pRet), wRet, curl_easy_strerror (wRet), - player->songDuration, - player->songPlayed, + songDuration, + songPlayed, curSong == NULL ? PIANO_RATE_NONE : curSong->rating, curSong == NULL ? "" : curSong->detailUrl ); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/ui.h new/pianobar-2019.02.14/src/ui.h --- old/pianobar-2017.08.30/src/ui.h 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/ui.h 2019-02-14 09:37:03.000000000 +0100 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2011 +Copyright (c) 2008-2018 Lars-Dominik Braun <[email protected]> Permission is hereby granted, free of charge, to any person obtaining a copy @@ -38,16 +38,17 @@ void BarUiMsg (const BarSettings_t *, const BarUiMsg_t, const char *, ...) __attribute__((format(printf, 3, 4))); PianoStation_t *BarUiSelectStation (BarApp_t *, PianoStation_t *, const char *, BarUiSelectStationCallback_t, bool); -PianoSong_t *BarUiSelectSong (const BarSettings_t *, PianoSong_t *, - BarReadlineFds_t *); +PianoSong_t *BarUiSelectSong (const BarApp_t * const app, + PianoSong_t *startSong, BarReadlineFds_t *input); PianoArtist_t *BarUiSelectArtist (BarApp_t *, PianoArtist_t *); char *BarUiSelectMusicId (BarApp_t *, PianoStation_t *, const char *); void BarUiPrintStation (const BarSettings_t *, PianoStation_t *); void BarUiPrintSong (const BarSettings_t *, const PianoSong_t *, const PianoStation_t *); -size_t BarUiListSongs (const BarSettings_t *, const PianoSong_t *, const char *); +size_t BarUiListSongs (const BarApp_t * const app, + const PianoSong_t *song, const char *filter); void BarUiStartEventCmd (const BarSettings_t *, const char *, - const PianoStation_t *, const PianoSong_t *, const player_t *, + const PianoStation_t *, const PianoSong_t *, player_t *, PianoStation_t *, PianoReturn_t, CURLcode); bool BarUiPianoCall (BarApp_t * const, const PianoRequestType_t, void *, PianoReturn_t *, CURLcode *); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2017.08.30/src/ui_act.c new/pianobar-2019.02.14/src/ui_act.c --- old/pianobar-2017.08.30/src/ui_act.c 2017-08-30 16:42:37.000000000 +0200 +++ new/pianobar-2019.02.14/src/ui_act.c 2019-02-14 09:37:03.000000000 +0100 @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2013 +Copyright (c) 2008-2018 Lars-Dominik Braun <[email protected]> Permission is hereby granted, free of charge, to any person obtaining a copy @@ -52,11 +52,14 @@ static inline void BarUiDoSkipSong (player_t * const player) { assert (player != NULL); - pthread_mutex_lock (&player->pauseMutex); + pthread_mutex_lock (&player->lock); player->doQuit = true; player->doPause = false; - pthread_cond_broadcast (&player->pauseCond); - pthread_mutex_unlock (&player->pauseMutex); + pthread_cond_broadcast (&player->cond); + pthread_mutex_unlock (&player->lock); + pthread_mutex_lock (&player->aoplayLock); + pthread_cond_broadcast (&player->aoplayCond); + pthread_mutex_unlock (&player->aoplayLock); } /* transform station if necessary to allow changes like rename, rate, ... @@ -419,28 +422,28 @@ /* play */ BarUiActCallback(BarUiActPlay) { - pthread_mutex_lock (&app->player.pauseMutex); + pthread_mutex_lock (&app->player.lock); app->player.doPause = false; - pthread_cond_broadcast (&app->player.pauseCond); - pthread_mutex_unlock (&app->player.pauseMutex); + pthread_cond_broadcast (&app->player.cond); + pthread_mutex_unlock (&app->player.lock); } /* pause */ BarUiActCallback(BarUiActPause) { - pthread_mutex_lock (&app->player.pauseMutex); + pthread_mutex_lock (&app->player.lock); app->player.doPause = true; - pthread_cond_broadcast (&app->player.pauseCond); - pthread_mutex_unlock (&app->player.pauseMutex); + pthread_cond_broadcast (&app->player.cond); + pthread_mutex_unlock (&app->player.lock); } /* toggle pause */ BarUiActCallback(BarUiActTogglePause) { - pthread_mutex_lock (&app->player.pauseMutex); + pthread_mutex_lock (&app->player.lock); app->player.doPause = !app->player.doPause; - pthread_cond_broadcast (&app->player.pauseCond); - pthread_mutex_unlock (&app->player.pauseMutex); + pthread_cond_broadcast (&app->player.cond); + pthread_mutex_unlock (&app->player.lock); } /* rename current station @@ -505,7 +508,7 @@ BarUiActCallback(BarUiActPrintUpcoming) { PianoSong_t * const nextSong = PianoListNextP (selSong); if (nextSong != NULL) { - BarUiListSongs (&app->settings, nextSong, NULL); + BarUiListSongs (app, nextSong, NULL); } else { BarUiMsg (&app->settings, MSG_INFO, "No songs in queue.\n"); } @@ -561,15 +564,15 @@ if (selStation->isQuickMix) { PianoStation_t *toggleStation; while ((toggleStation = BarUiSelectStation (app, app->ph.stations, - "Toggle quickmix for station: ", + "Toggle QuickMix for station: ", BarUiActQuickmixCallback, false)) != NULL) { toggleStation->useQuickMix = !toggleStation->useQuickMix; } - BarUiMsg (&app->settings, MSG_INFO, "Setting quickmix stations... "); + BarUiMsg (&app->settings, MSG_INFO, "Setting QuickMix stations... "); BarUiActDefaultPianoCall (PIANO_REQUEST_SET_QUICKMIX, NULL); BarUiActDefaultEventcmd ("stationquickmixtoggle"); } else { - BarUiMsg (&app->settings, MSG_ERR, "Not a QuickMix station.\n"); + BarUiMsg (&app->settings, MSG_ERR, "Please select a QuickMix station first.\n"); } } @@ -587,7 +590,7 @@ PianoSong_t *histSong; if (app->songHistory != NULL) { - histSong = BarUiSelectSong (&app->settings, app->songHistory, + histSong = BarUiSelectSong (app, app->songHistory, &app->input); if (histSong != NULL) { BarKeyShortcutId_t action; @@ -842,7 +845,7 @@ BarUiActDefaultEventcmd ("stationdeleteartistseed"); } } else if (selectBuf[0] == 's') { - PianoSong_t *song = BarUiSelectSong (&app->settings, + PianoSong_t *song = BarUiSelectSong (app, reqData.info.songSeeds, &app->input); if (song != NULL) { PianoRequestDataDeleteSeed_t subReqData; @@ -869,7 +872,7 @@ BarUiActDefaultEventcmd ("stationdeletestationseed"); } } else if (selectBuf[0] == 'f') { - PianoSong_t *song = BarUiSelectSong (&app->settings, + PianoSong_t *song = BarUiSelectSong (app, reqData.info.feedback, &app->input); if (song != NULL) { BarUiMsg (&app->settings, MSG_INFO, "Deleting feedback... "); @@ -881,4 +884,3 @@ PianoDestroyStationInfo (&reqData.info); } -
