summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml2
-rw-r--r--ChangeLog6
-rw-r--r--INSTALL34
-rw-r--r--Makefile4
-rw-r--r--README.rst36
-rw-r--r--contrib/config-example1
-rw-r--r--contrib/pianobar.114
-rw-r--r--src/config.h2
-rw-r--r--src/libpiano/response.c27
-rw-r--r--src/player.c27
-rw-r--r--src/ui.c84
-rw-r--r--src/ui_act.c8
12 files changed, 151 insertions, 94 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 69aeefd..536a7d9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,7 +9,7 @@ on:
jobs:
build:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
diff --git a/ChangeLog b/ChangeLog
index d36525a..55f945b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Release 2024.12.21
+
+- Improved network error retrying
+- Improved compatibility with newer versions of ffmpeg and libcurl
+- Minor documentation fixes
+
Release 2022.04.01
- Not a joke
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index 2d36705..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,34 +0,0 @@
-Install
-=======
-
-Dependencies
-------------
-
-- gmake
-- pthreads
-- libao
-- libcurl
-- gcrypt[1]
-- json-c
-- ffmpeg>=3.3 [2]
-- UTF-8 console/locale
-
-[1] with blowfish cipher enabled
-[2] required: demuxer mov, decoder aac, protocol http and filters volume,
- aformat, aresample
-
-Building
---------
-
-Edit the Makefile and then type
-
- gmake clean && gmake
-
-You can run the client directly from the source directory now
-
- ./pianobar
-
-Or install it by issuing
-
- gmake install
-
diff --git a/Makefile b/Makefile
index 4e8fd0a..8c647fb 100644
--- a/Makefile
+++ b/Makefile
@@ -52,8 +52,8 @@ LIBAV_LDFLAGS:=$(shell $(PKG_CONFIG) --libs libavcodec libavformat libavutil lib
LIBCURL_CFLAGS:=$(shell $(PKG_CONFIG) --cflags libcurl)
LIBCURL_LDFLAGS:=$(shell $(PKG_CONFIG) --libs libcurl)
-LIBGCRYPT_CFLAGS:=
-LIBGCRYPT_LDFLAGS:=-lgcrypt
+LIBGCRYPT_CFLAGS:=$(shell $(PKG_CONFIG) --cflags libgcrypt)
+LIBGCRYPT_LDFLAGS:=$(shell $(PKG_CONFIG) --libs libgcrypt)
LIBJSONC_CFLAGS:=$(shell $(PKG_CONFIG) --cflags json-c 2>/dev/null || $(PKG_CONFIG) --cflags json)
LIBJSONC_LDFLAGS:=$(shell $(PKG_CONFIG) --libs json-c 2>/dev/null || $(PKG_CONFIG) --libs json)
diff --git a/README.rst b/README.rst
index c4d812e..6e3a5db 100644
--- a/README.rst
+++ b/README.rst
@@ -33,10 +33,11 @@ and \*BSD as well as a `native Windows port`_.
.. _homebrew: http://brew.sh/
.. _native Windows Port: https://github.com/thedmd/pianobar-windows
-The current pianobar release is 2022.04.01_ (sha256__, sign__). More recent and
+The current pianobar release is 2024.12.21_ (sha256__, sign__). More recent and
experimental code is available at GitHub_ and the local gitweb_. Older releases
are available here:
+- 2022.04.01_ (sha256__, sign__)
- 2020.11.28_ (sha256__, sign__)
- 2020.04.05_ (sha256__, sign__)
- 2019.02.14_ (sha256__, sign__)
@@ -67,6 +68,9 @@ are available here:
- 2010.10.07_ (sha1__)
- 2010.08.21_ (sha1__)
+.. _2024.12.21: https://6xq.net/pianobar/pianobar-2024.12.21.tar.bz2
+__ https://6xq.net/pianobar/pianobar-2024.12.21.tar.bz2.sha256
+__ https://6xq.net/pianobar/pianobar-2024.12.21.tar.bz2.asc
.. _2022.04.01: https://6xq.net/pianobar/pianobar-2022.04.01.tar.bz2
__ https://6xq.net/pianobar/pianobar-2022.04.01.tar.bz2.sha256
__ https://6xq.net/pianobar/pianobar-2022.04.01.tar.bz2.asc
@@ -157,6 +161,36 @@ __ https://6xq.net/pianobar/pianobar-2010.10.07.tar.bz2.sha1
.. _2010.08.21: https://6xq.net/pianobar/pianobar-2010.08.21.tar.bz2
__ https://6xq.net/pianobar/pianobar-2010.08.21.tar.bz2.sha1
+Install
+-------
+
+You need the following software to build pianobar:
+
+- GNU make
+- pthreads
+- libao
+- libcurl ≥ 7.32.0
+- gcrypt [1]_
+- json-c
+- ffmpeg ≤ 5.1 [2]_
+- UTF-8 console/locale
+
+.. [1] with blowfish cipher enabled
+.. [2] required: demuxer mov, decoder aac, protocol http and filters volume,
+ aformat, aresample
+
+Then type::
+
+ gmake clean && gmake
+
+You can run the client directly from the source directory now::
+
+ ./pianobar
+
+Or install it to ``/usr/local`` by issuing::
+
+ gmake install
+
FAQ
---
diff --git a/contrib/config-example b/contrib/config-example
index 5f5dc2d..060fbcb 100644
--- a/contrib/config-example
+++ b/contrib/config-example
@@ -23,7 +23,6 @@
#act_stationaddbygenre = g
#act_songinfo = i
#act_addshared = j
-#act_songmove = m
#act_songnext = n
#act_songpause = S
#act_songpausetoggle = p
diff --git a/contrib/pianobar.1 b/contrib/pianobar.1
index c5b82aa..887ae4a 100644
--- a/contrib/pianobar.1
+++ b/contrib/pianobar.1
@@ -103,10 +103,6 @@ beginning.
Delete artist/song seeds or feedback.
.TP
-.B act_songmove = m
-Move current song to another station
-
-.TP
.B act_songnext = n
Skip current song.
@@ -449,13 +445,13 @@ can report certain "events" to an external application (see
information like error code and description, was well as song information
related to the current event, is supplied through stdin.
-Currently supported events are: artistbookmark, songban, songbookmark,
-songexplain, songfinish, songlove, songmove, songshelf, songstart,
+Currently supported events are: artistbookmark, settingschange, settingsget,
+songban, songbookmark, songexplain, songfinish, songlove, songshelf, songstart,
stationaddgenre, stationaddmusic, stationaddshared, stationcreate,
stationdelete, stationdeleteartistseed, stationdeletefeedback,
-stationdeletesongseed, stationfetchinfo, stationfetchplaylist,
-stationfetchgenre stationquickmixtoggle, stationrename, userlogin,
-usergetstations
+stationdeletesongseed, stationdeletestationseed, stationfetchgenre,
+stationfetchinfo, stationfetchplaylist, stationgetmodes, stationquickmixtoggle,
+stationrename, stationsetmode, usergetstations, userlogin
An example script can be found in the contrib/ directory of
.B pianobar's
diff --git a/src/config.h b/src/config.h
index f3d3d3e..850c4aa 100644
--- a/src/config.h
+++ b/src/config.h
@@ -3,7 +3,7 @@
/* package name */
#define PACKAGE "pianobar"
-#define VERSION "2022.04.01-dev"
+#define VERSION "2024.12.21-dev"
/* glibc feature test macros, define _before_ including other files */
#define _POSIX_C_SOURCE 200809L
diff --git a/src/libpiano/response.c b/src/libpiano/response.c
index 1e79261..0be8872 100644
--- a/src/libpiano/response.c
+++ b/src/libpiano/response.c
@@ -206,7 +206,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
break;
}
- for (int i = 0; i < json_object_array_length (stations); i++) {
+ for (unsigned int i = 0; i < json_object_array_length (stations); i++) {
PianoStation_t *tmpStation;
json_object *s = json_object_array_get_idx (stations, i);
@@ -229,7 +229,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
if (mix != NULL) {
PianoStation_t *curStation = ph->stations;
PianoListForeachP (curStation) {
- for (int i = 0; i < json_object_array_length (mix); i++) {
+ for (unsigned int i = 0; i < json_object_array_length (mix); i++) {
json_object *id = json_object_array_get_idx (mix, i);
if (strcmp (json_object_get_string (id),
curStation->id) == 0) {
@@ -256,7 +256,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
}
assert (items != NULL);
- for (int i = 0; i < json_object_array_length (items); i++) {
+ for (unsigned int i = 0; i < json_object_array_length (items); i++) {
json_object *s = json_object_array_get_idx (items, i);
PianoSong_t *song;
@@ -377,7 +377,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
/* get artists */
json_object *artists;
if (json_object_object_get_ex (result, "artists", &artists)) {
- for (int i = 0; i < json_object_array_length (artists); i++) {
+ for (unsigned int i = 0; i < json_object_array_length (artists); i++) {
json_object *a = json_object_array_get_idx (artists, i);
PianoArtist_t *artist;
@@ -396,7 +396,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
/* get songs */
json_object *songs;
if (json_object_object_get_ex (result, "songs", &songs)) {
- for (int i = 0; i < json_object_array_length (songs); i++) {
+ for (unsigned int i = 0; i < json_object_array_length (songs); i++) {
json_object *s = json_object_array_get_idx (songs, i);
PianoSong_t *song;
@@ -456,7 +456,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
/* get genre stations */
json_object *categories;
if (json_object_object_get_ex (result, "categories", &categories)) {
- for (int i = 0; i < json_object_array_length (categories); i++) {
+ for (unsigned int i = 0; i < json_object_array_length (categories); i++) {
json_object *c = json_object_array_get_idx (categories, i);
PianoGenreCategory_t *tmpGenreCategory;
@@ -471,7 +471,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
/* get genre subnodes */
json_object *stations;
if (json_object_object_get_ex (c, "stations", &stations)) {
- for (int k = 0;
+ for (unsigned int k = 0;
k < json_object_array_length (stations); k++) {
json_object *s =
json_object_array_get_idx (stations, k);
@@ -520,12 +520,13 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
assert (reqData != NULL);
json_object *explanations;
- if (json_object_object_get_ex (result, "explanations", &explanations)) {
+ if (json_object_object_get_ex (result, "explanations", &explanations) &&
+ json_object_array_length (explanations) > 0) {
reqData->retExplain = malloc (strSize *
sizeof (*reqData->retExplain));
strncpy (reqData->retExplain, "We're playing this track "
"because it features ", strSize);
- for (int i = 0; i < json_object_array_length (explanations); i++) {
+ for (unsigned int i = 0; i < json_object_array_length (explanations); i++) {
json_object *e = json_object_array_get_idx (explanations,
i);
json_object *f;
@@ -573,7 +574,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
/* songs */
json_object *songs;
if (json_object_object_get_ex (music, "songs", &songs)) {
- for (int i = 0; i < json_object_array_length (songs); i++) {
+ for (unsigned int i = 0; i < json_object_array_length (songs); i++) {
json_object *s = json_object_array_get_idx (songs, i);
PianoSong_t *seedSong;
@@ -594,7 +595,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
/* artists */
json_object *artists;
if (json_object_object_get_ex (music, "artists", &artists)) {
- for (int i = 0; i < json_object_array_length (artists); i++) {
+ for (unsigned int i = 0; i < json_object_array_length (artists); i++) {
json_object *a = json_object_array_get_idx (artists, i);
PianoArtist_t *seedArtist;
@@ -622,7 +623,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
continue;
}
assert (json_object_is_type (val, json_type_array));
- for (int i = 0; i < json_object_array_length (val); i++) {
+ for (unsigned int i = 0; i < json_object_array_length (val); i++) {
json_object *s = json_object_array_get_idx (val, i);
PianoSong_t *feedbackSong;
@@ -665,7 +666,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
json_object *availableModes;
if (json_object_object_get_ex (result, "availableModes", &availableModes)) {
- for (int i = 0; i < json_object_array_length (availableModes); i++) {
+ for (unsigned 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));
diff --git a/src/player.c b/src/player.c
index 875f473..bded009 100644
--- a/src/player.c
+++ b/src/player.c
@@ -235,7 +235,7 @@ static bool openStream (player_t * const player) {
softfail ("avcodec_parameters_to_context");
}
- AVCodec * const decoder = avcodec_find_decoder (cp->codec_id);
+ const AVCodec * const decoder = avcodec_find_decoder (cp->codec_id);
if (decoder == NULL) {
softfail ("find_decoder");
}
@@ -282,11 +282,13 @@ static bool openFilter (player_t * const player) {
/* abuffer */
AVRational time_base = player->st->time_base;
+ char channelLayout[128];
+ av_channel_layout_describe(&player->cctx->ch_layout, channelLayout, sizeof(channelLayout));
snprintf (strbuf, sizeof (strbuf),
- "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64,
+ "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s",
time_base.num, time_base.den, cp->sample_rate,
av_get_sample_fmt_name (player->cctx->sample_fmt),
- cp->channel_layout);
+ channelLayout);
if ((ret = avfilter_graph_create_filter (&player->fabuf,
avfilter_get_by_name ("abuffer"), "source", strbuf, NULL,
player->fgraph)) < 0) {
@@ -340,7 +342,7 @@ static bool openDevice (player_t * const player) {
memset (&aoFmt, 0, sizeof (aoFmt));
aoFmt.bits = av_get_bytes_per_sample (avformat) * 8;
assert (aoFmt.bits > 0);
- aoFmt.channels = cp->channels;
+ aoFmt.channels = cp->ch_layout.nb_channels;
aoFmt.rate = getSampleRate (player);
aoFmt.byte_format = AO_FMT_NATIVE;
@@ -431,8 +433,12 @@ static int play (player_t * const player) {
} 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);
+ char error[AV_ERROR_MAX_STRING_SIZE];
+ if (av_strerror(ret, error, sizeof(error)) < 0) {
+ strncpy (error, "(unknown)", sizeof(error)-1);
+ }
+ debugPrint (DEBUG_AUDIO, "av_read_frame failed with code %i (%s), "
+ "sending NULL frame\n", ret, error);
pthread_mutex_lock (&player->aoplayLock);
const int rt = av_buffersrc_add_frame (player->fabuf, NULL);
assert (rt == 0);
@@ -508,7 +514,7 @@ static void finish (player_t * const player) {
player->fgraph = NULL;
}
if (player->cctx != NULL) {
- avcodec_close (player->cctx);
+ avcodec_free_context (&player->cctx);
player->cctx = NULL;
}
if (player->fctx != NULL) {
@@ -533,7 +539,9 @@ void *BarPlayerThread (void *data) {
if (openFilter (player) && openDevice (player)) {
changeMode (player, PLAYER_PLAYING);
BarPlayerSetVolume (player);
- retry = play (player) == AVERROR_INVALIDDATA &&
+ const int ret = play (player);
+ retry = (ret == AVERROR_INVALIDDATA ||
+ ret == -ECONNRESET) &&
!player->interrupted;
} else {
/* filter missing or audio device busy */
@@ -583,8 +591,7 @@ void *BarAoPlayThread (void *data) {
}
pthread_mutex_unlock (&player->aoplayLock);
- const int numChannels = av_get_channel_layout_nb_channels (
- filteredFrame->channel_layout);
+ const int numChannels = filteredFrame->ch_layout.nb_channels;
const int bps = av_get_bytes_per_sample (filteredFrame->format);
ao_play (player->aoDev, (char *) filteredFrame->data[0],
filteredFrame->nb_samples * numChannels * bps);
diff --git a/src/ui.c b/src/ui.c
index 204179c..0c7386a 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -161,8 +161,8 @@ static size_t httpFetchCb (char *ptr, size_t size, size_t nmemb,
/* libcurl progress callback. aborts the current request if user pressed ^C
*/
-int progressCb (void * const data, double dltotal, double dlnow,
- double ultotal, double ulnow) {
+int progressCb (void * const data, curl_off_t dltotal, curl_off_t dlnow,
+ curl_off_t ultotal, curl_off_t ulnow) {
const sig_atomic_t lint = *((sig_atomic_t *) data);
if (lint) {
return 1;
@@ -224,8 +224,8 @@ static CURLcode BarPianoHttpRequest (CURL * const http,
setAndCheck (CURLOPT_POSTFIELDS, req->postData);
setAndCheck (CURLOPT_WRITEFUNCTION, httpFetchCb);
setAndCheck (CURLOPT_WRITEDATA, &buffer);
- setAndCheck (CURLOPT_PROGRESSFUNCTION, progressCb);
- setAndCheck (CURLOPT_PROGRESSDATA, &lint);
+ setAndCheck (CURLOPT_XFERINFOFUNCTION, progressCb);
+ setAndCheck (CURLOPT_XFERINFODATA, &lint);
setAndCheck (CURLOPT_NOPROGRESS, 0);
setAndCheck (CURLOPT_POST, 1);
setAndCheck (CURLOPT_TIMEOUT, settings->timeout);
@@ -844,6 +844,49 @@ size_t BarUiListSongs (const BarApp_t * const app,
return i;
}
+enum {
+ NO_DURATION = 0,
+};
+#define NO_POSTFIX ""
+
+/* Print song information to the eventcmd stream
+ * @param Event command stream.
+ * @param Song information.
+ * @param Printed key name postfix, use NO_POSTFIX to print bare keys.
+ * @param Override song length from song parameter, use NO_DURATION if unavailable.
+ */
+static void BarUiEventcmdPrintSong (FILE * restrict stream,
+ const PianoSong_t * const song, const char * const postfix,
+ const unsigned int songDuration) {
+ assert (song != NULL);
+ assert (stream != NULL);
+ assert (postfix != NULL);
+
+ fprintf (stream,
+ "artist%s=%s\n"
+ "title%s=%s\n"
+ "album%s=%s\n"
+ "coverArt%s=%s\n"
+ "rating%s=%i\n"
+ "detailUrl%s=%s\n"
+ "songDuration%s=%u\n",
+ postfix,
+ song->artist,
+ postfix,
+ song->title,
+ postfix,
+ song->album,
+ postfix,
+ song->coverArt,
+ postfix,
+ song->rating,
+ postfix,
+ song->detailUrl,
+ postfix,
+ songDuration == NO_DURATION ? song->length : songDuration
+ );
+}
+
/* Excute external event handler
* @param settings containing the cmdline
* @param event type
@@ -900,36 +943,37 @@ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type,
pthread_mutex_unlock (&player->lock);
fprintf (pipeWriteFd,
- "artist=%s\n"
- "title=%s\n"
- "album=%s\n"
- "coverArt=%s\n"
"stationName=%s\n"
"songStationName=%s\n"
"pRet=%i\n"
"pRetStr=%s\n"
"wRet=%i\n"
"wRetStr=%s\n"
- "songDuration=%u\n"
- "songPlayed=%u\n"
- "rating=%i\n"
- "detailUrl=%s\n",
- curSong == NULL ? "" : curSong->artist,
- curSong == NULL ? "" : curSong->title,
- curSong == NULL ? "" : curSong->album,
- curSong == NULL ? "" : curSong->coverArt,
+ "songPlayed=%u\n",
curStation == NULL ? "" : curStation->name,
songStation == NULL ? "" : songStation->name,
pRet,
PianoErrorToStr (pRet),
wRet,
curl_easy_strerror (wRet),
- songDuration,
- songPlayed,
- curSong == NULL ? PIANO_RATE_NONE : curSong->rating,
- curSong == NULL ? "" : curSong->detailUrl
+ songPlayed
);
+ if (curSong != NULL) {
+ BarUiEventcmdPrintSong (pipeWriteFd, curSong, NO_POSTFIX, songDuration);
+ }
+
+ const PianoSong_t *nextSong = PianoListNextP (curSong);
+ if (nextSong != NULL) {
+ unsigned int i = 0;
+ PianoListForeachP (nextSong) {
+ char postfix[16];
+ snprintf (postfix, sizeof(postfix)-1, "Next%i", i);
+ BarUiEventcmdPrintSong (pipeWriteFd, nextSong, postfix, NO_DURATION);
+ i++;
+ }
+ }
+
if (stations != NULL) {
/* send station list */
PianoStation_t **sortedStations = NULL;
diff --git a/src/ui_act.c b/src/ui_act.c
index 30bd4e5..fa5c43b 100644
--- a/src/ui_act.c
+++ b/src/ui_act.c
@@ -271,8 +271,12 @@ BarUiActCallback(BarUiActExplain) {
BarUiMsg (&app->settings, MSG_INFO, "Receiving explanation... ");
if (BarUiActDefaultPianoCall (PIANO_REQUEST_EXPLAIN, &reqData)) {
- BarUiMsg (&app->settings, MSG_INFO, "%s\n", reqData.retExplain);
- free (reqData.retExplain);
+ if (reqData.retExplain == NULL) {
+ BarUiMsg (&app->settings, MSG_ERR, "No explanation provided.\n");
+ } else {
+ BarUiMsg (&app->settings, MSG_INFO, "%s\n", reqData.retExplain);
+ free (reqData.retExplain);
+ }
}
BarUiActDefaultEventcmd ("songexplain");
}