summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml2
-rw-r--r--INSTALL4
-rw-r--r--src/config.h2
-rw-r--r--src/libpiano/response.c27
-rw-r--r--src/player.c25
-rw-r--r--src/ui.c84
-rw-r--r--src/ui_act.c8
7 files changed, 104 insertions, 48 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/INSTALL b/INSTALL
index 2d36705..fa18296 100644
--- a/INSTALL
+++ b/INSTALL
@@ -7,10 +7,10 @@ Dependencies
- gmake
- pthreads
- libao
-- libcurl
+- libcurl>=7.32.0
- gcrypt[1]
- json-c
-- ffmpeg>=3.3 [2]
+- ffmpeg>=5.1 [2]
- UTF-8 console/locale
[1] with blowfish cipher enabled
diff --git a/src/config.h b/src/config.h
index 2ea7ce8..f3d3d3e 100644
--- a/src/config.h
+++ b/src/config.h
@@ -3,7 +3,7 @@
/* package name */
#define PACKAGE "pianobar"
-#define VERSION "2022.04.01"
+#define VERSION "2022.04.01-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..753d490 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);
@@ -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");
}