Compare commits

...

13 Commits
29.0 ... master

Author SHA1 Message Date
ejurgensen
49171dac1a [artwork] Rename to artwork_get_by_file_id and artwork_get_by_group_id
For consistency with new artwork_get_by_queue_item_id
2025-10-27 22:54:11 +01:00
ejurgensen
09b9b0c7fc [airplay] Support artwork for non-library items, e.g. from Spotify search 2025-10-27 22:54:11 +01:00
ejurgensen
6f45f8b4a5 [dacp] Add support for non-library artwork, e.g. from Spotify searches
Fixes #1936
2025-10-27 22:54:11 +01:00
ejurgensen
921d4446d6 [artwork] Add function to get artwork for non-library queue items
Adds artwork_get_by_queue_item_id() and a source handler for getting artwork
from queue_item->artwork_url.
2025-10-27 22:54:11 +01:00
ejurgensen
bf598153f3
[docs] Document HDMI volume control changes for Raspberry Pi
Added information about HDMI volume control issues on Raspberry Pi.
2025-10-24 14:21:16 +02:00
github-actions[bot]
c1bdac931e [web] Rebuild web interface 2025-10-22 17:56:26 +00:00
Alain Nussbaumer
a1e4982c0b [web] Update to newer versions of libraries 2025-10-22 19:55:32 +02:00
Alain Nussbaumer
352a73044e Merge branch 'master' of github.com:owntone/owntone-server 2025-10-22 19:53:21 +02:00
ejurgensen
5f526c7a7e [dacp] Fix segfault from invalid queries
When parsing a DACP request (pattern: ^/ctrl-int/[[:digit:]]+/playqueue-edit$)
with a command parameter being "move" and an edit-params parameter lacking a
colon, strchr(param, ':') at httpd_dacp.c:2038 will return NULL, and safe_atoi32
is called with its first parameter str being 1. This will bypass the NULL check
at src/misc.c:650 and causes a segmentation fault at the call to strtol at line
657.

Closes #1933
2025-10-08 19:49:01 +02:00
ejurgensen
b7e385ffe0 [httpd] Better logging of evbuffer_read() errors
Fixes #1931
2025-09-30 20:44:01 +02:00
Alain Nussbaumer
2eba24b4ba [web] Update libraries 2025-09-24 10:43:15 +10:00
github-actions[bot]
336200727d [web] Rebuild web interface 2025-09-18 05:55:20 +00:00
Alain Nussbaumer
b523ea4d35 [web] Update to newer versions of libraries for security reasons 2025-09-18 15:54:38 +10:00
14 changed files with 683 additions and 523 deletions

View File

@ -490,3 +490,7 @@ Note however, the equalizer appears to require a `plughw` device which means you
This error will occur for output hardware that do not support concurrent device open and the server plays 2 files of different bitrate (44.1khz and 48khz) back to back.
If you observe the error, you will need to use the `dmix` configuration as mentioned above.
* Volume control on Raspberry Pi with hdmi output doesn't work
Prior to Debian 13, in the /boot/firmware.config file the entry "dtoverlay=vc4-kms-v3d" was commented out. Since 13 this line is active (not commented out). This changes, among other things, the way the HDMI and BCM devices are handled. Commenting out the entry again will fix the volume control for the hdmi output. If you use the RPi headless commenting this out shouldn't have undesirable effects. See https://forums.raspberrypi.com/viewtopic.php?t=49928&start=1825#p2344272.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -113,6 +113,8 @@ struct artwork_ctx {
uint32_t media_kind;
// Input data for group handlers
int64_t persistentid;
// Input data for queue item handlers
struct db_queue_item *queue_item;
// Not to be used by handler - query for item or group
struct query_params qp;
@ -210,6 +212,8 @@ static int source_item_ownpl_get(struct artwork_ctx *ctx);
static int source_item_spotifywebapi_search_get(struct artwork_ctx *ctx);
static int source_item_discogs_get(struct artwork_ctx *ctx);
static int source_item_coverartarchive_get(struct artwork_ctx *ctx);
/* Forward - queue item handlers */
static int source_queue_item_artwork_url_get(struct artwork_ctx *ctx);
/* List of sources that can provide artwork for a group (i.e. usually an album
* identified by a persistentid). The source handlers will be called in the
@ -353,6 +357,24 @@ static struct artwork_source artwork_item_source[] =
}
};
/* List of sources that can provide artwork for a queue item. The source
* handlers will be called in the order of this list. Must be terminated by a
* NULL struct.
*/
static struct artwork_source artwork_queue_item_source[] =
{
{
.name = "artwork url",
.handler = source_queue_item_artwork_url_get,
.cache = NEVER,
},
{
.name = NULL,
.handler = NULL,
.cache = 0,
}
};
/* Forward - parsers of online source responses */
static enum parse_result response_jparse_spotify(char **artwork_url, json_object *response, int max_w, int max_h);
static enum parse_result response_jparse_discogs(char **artwork_url, json_object *response, int max_w, int max_h);
@ -1607,41 +1629,25 @@ source_item_own_get(struct artwork_ctx *ctx)
return artwork_get(ctx->evbuf, path, NULL, false, ctx->data_kind, ctx->req_params);
}
/*
* Downloads the artwork from the location pointed to by queue_item->artwork_url
*/
static int
source_item_artwork_url_get(struct artwork_ctx *ctx)
{
struct db_queue_item *queue_item;
const char *proto_http = "http:";
const char *proto_https = "https:";
bool is_http;
bool is_https;
int ret;
DPRINTF(E_SPAM, L_ART, "Trying artwork url for %s\n", ctx->dbmfi->path);
queue_item = db_queue_fetch_byfileid(ctx->id);
if (!queue_item || !queue_item->artwork_url)
goto notfound;
is_http = (strncmp(queue_item->artwork_url, proto_http, strlen(proto_http)) == 0);
is_https = (strncmp(queue_item->artwork_url, proto_https, strlen(proto_https)) == 0);
if (!is_http && !is_https)
goto notfound;
ret = artwork_get_byurl(ctx->evbuf, queue_item->artwork_url, ctx->req_params);
snprintf(ctx->path, sizeof(ctx->path), "%s", queue_item->artwork_url);
free_queue_item(queue_item, 0);
if (ctx->queue_item)
{
ret = source_queue_item_artwork_url_get(ctx);
}
else
{
ctx->queue_item = db_queue_fetch_byfileid(ctx->id);
ret = source_queue_item_artwork_url_get(ctx);
free_queue_item(ctx->queue_item, 0);
}
return ret;
notfound:
free_queue_item(queue_item, 0);
return ART_E_NONE;
}
/*
@ -1852,11 +1858,46 @@ source_item_ownpl_get(struct artwork_ctx *ctx)
return format;
}
/*
* Downloads artwork from ctx->queue_item->artwork_url
*/
static int
source_queue_item_artwork_url_get(struct artwork_ctx *ctx)
{
const char *proto_http = "http:";
const char *proto_https = "https:";
const char *artwork_url;
bool is_http;
bool is_https;
int ret;
if (!ctx->queue_item || !ctx->queue_item->artwork_url)
goto notfound;
DPRINTF(E_SPAM, L_ART, "Trying artwork url for %s\n", ctx->queue_item->path);
artwork_url = ctx->queue_item->artwork_url;
is_http = (strncmp(artwork_url, proto_http, strlen(proto_http)) == 0);
is_https = (strncmp(artwork_url, proto_https, strlen(proto_https)) == 0);
if (!is_http && !is_https)
goto notfound;
ret = artwork_get_byurl(ctx->evbuf, artwork_url, ctx->req_params);
snprintf(ctx->path, sizeof(ctx->path), "%s", artwork_url);
return ret;
notfound:
return ART_E_NONE;
}
/* --------------------------- SOURCE PROCESSING --------------------------- */
static int
process_items(struct artwork_ctx *ctx, int item_mode)
process_file_items(struct artwork_ctx *ctx, int item_mode)
{
struct db_media_file_info dbmfi;
int i;
@ -2000,16 +2041,55 @@ process_group(struct artwork_ctx *ctx)
}
invalid_group:
return process_items(ctx, 0);
return process_file_items(ctx, 0);
}
static int
process_queue_item(struct artwork_ctx *ctx)
{
const char *source_name;
int i;
int ret;
for (i = 0; artwork_queue_item_source[i].handler; i++)
{
// If just one handler says we should not cache a negative result then we obey that
if ((artwork_queue_item_source[i].cache & ON_FAILURE) == 0)
ctx->cache = NEVER;
source_name = artwork_queue_item_source[i].name;
DPRINTF(E_SPAM, L_ART, "Checking queue item source '%s'\n", source_name);
ret = artwork_queue_item_source[i].handler(ctx);
if (ret > 0)
{
DPRINTF(E_DBG, L_ART, "Artwork for queue item '%s' found in source '%s'\n", ctx->queue_item->title, source_name);
ctx->cache = artwork_queue_item_source[i].cache;
return ret;
}
else if (ret == ART_E_ABORT)
{
DPRINTF(E_DBG, L_ART, "Source '%s' stopped search for artwork for queue item '%s'\n", source_name, ctx->queue_item->title);
ctx->cache = NEVER;
}
else if (ret == ART_E_ERROR)
{
DPRINTF(E_LOG, L_ART, "Source '%s' returned an error for queue item '%s'\n", source_name, ctx->queue_item->title);
ctx->cache = NEVER;
}
}
return -1;
}
/* ------------------------------ ARTWORK API ------------------------------ */
int
artwork_get_item(struct evbuffer *evbuf, int id, int max_w, int max_h, int format)
artwork_get_by_file_id(struct evbuffer *evbuf, int id, int max_w, int max_h, int format)
{
struct artwork_ctx ctx;
struct artwork_ctx ctx = { 0 };
char filter[32];
int ret;
@ -2018,8 +2098,6 @@ artwork_get_item(struct evbuffer *evbuf, int id, int max_w, int max_h, int forma
if (id == DB_MEDIA_FILE_NON_PERSISTENT_ID)
return -1;
memset(&ctx, 0, sizeof(struct artwork_ctx));
ctx.qp.type = Q_ITEMS;
ctx.qp.filter = filter;
ctx.evbuf = evbuf;
@ -2038,7 +2116,7 @@ artwork_get_item(struct evbuffer *evbuf, int id, int max_w, int max_h, int forma
// Note: process_items will set ctx.persistentid for the following process_group()
// - and do nothing else if artwork_individual is not configured by user
ret = process_items(&ctx, 1);
ret = process_file_items(&ctx, 1);
if (ret > 0)
{
if (ctx.cache & ON_SUCCESS)
@ -2068,15 +2146,13 @@ artwork_get_item(struct evbuffer *evbuf, int id, int max_w, int max_h, int forma
}
int
artwork_get_group(struct evbuffer *evbuf, int id, int max_w, int max_h, int format)
artwork_get_by_group_id(struct evbuffer *evbuf, int id, int max_w, int max_h, int format)
{
struct artwork_ctx ctx;
struct artwork_ctx ctx = { 0 };
int ret;
DPRINTF(E_DBG, L_ART, "Artwork request for group %d (max_w=%d, max_h=%d)\n", id, max_w, max_h);
memset(&ctx, 0, sizeof(struct artwork_ctx));
/* Get the persistent id for the given group id */
ret = db_group_persistentid_byid(id, &ctx.persistentid);
if (ret < 0)
@ -2111,6 +2187,48 @@ artwork_get_group(struct evbuffer *evbuf, int id, int max_w, int max_h, int form
return -1;
}
int
artwork_get_by_queue_item_id(struct evbuffer *evbuf, int item_id, int max_w, int max_h, int format)
{
struct artwork_ctx ctx = { 0 };
struct db_queue_item *queue_item;
int ret;
DPRINTF(E_DBG, L_ART, "Artwork request for queue item %d (max_w=%d, max_h=%d)\n", item_id, max_w, max_h);
queue_item = db_queue_fetch_byitemid(item_id);
if (!queue_item)
return -1;
if (queue_item->file_id != DB_MEDIA_FILE_NON_PERSISTENT_ID)
{
ret = artwork_get_by_file_id(evbuf, queue_item->file_id, max_w, max_h, format);
free_queue_item(queue_item, 0);
return ret;
}
ctx.queue_item = queue_item;
ctx.evbuf = evbuf;
ctx.req_params.max_w = max_w;
ctx.req_params.max_h = max_h;
ctx.req_params.format = format;
ctx.cache = ON_FAILURE;
ret = process_queue_item(&ctx);
if (ret > 0)
{
// No caching of queue item artwork implemented as of yet
free_queue_item(queue_item, 0);
return ret;
}
DPRINTF(E_DBG, L_ART, "No artwork found for queue item %d\n", item_id);
free_queue_item(queue_item, 0);
return -1;
}
/* Checks if the file is an artwork file */
bool
artwork_file_is_artwork(const char *filename)

View File

@ -13,7 +13,7 @@
#include <stdbool.h>
/*
* Get the artwork image for an individual item (track)
* Get the artwork image for an individual library item (track)
*
* @out evbuf Event buffer that will contain the (scaled) image
* @in id The mfi item id
@ -23,7 +23,7 @@
* @return ART_FMT_* on success, -1 on error or no artwork found
*/
int
artwork_get_item(struct evbuffer *evbuf, int id, int max_w, int max_h, int format);
artwork_get_by_file_id(struct evbuffer *evbuf, int id, int max_w, int max_h, int format);
/*
* Get the artwork image for a group (an album or an artist)
@ -36,7 +36,21 @@ artwork_get_item(struct evbuffer *evbuf, int id, int max_w, int max_h, int forma
* @return ART_FMT_* on success, -1 on error or no artwork found
*/
int
artwork_get_group(struct evbuffer *evbuf, int id, int max_w, int max_h, int format);
artwork_get_by_group_id(struct evbuffer *evbuf, int id, int max_w, int max_h, int format);
/*
* Get the artwork image for a queue item. If the queue item is in the library,
* this will return the same as artwork_get_by_file_id
*
* @out evbuf Event buffer that will contain the (scaled) image
* @in item_id The queue item id
* @in max_w Requested maximum image width (may not be obeyed)
* @in max_h Requested maximum image height (may not be obeyed)
* @in format Requested format (may not be obeyed), 0 for default
* @return ART_FMT_* on success, -1 on error or no artwork found
*/
int
artwork_get_by_queue_item_id(struct evbuffer *evbuf, int item_id, int max_w, int max_h, int format);
/*
* Checks if the file is an artwork file (based on user config)

View File

@ -920,7 +920,7 @@ stream_chunk_raw_cb(int fd, short event, void *arg)
if (ret == 0)
DPRINTF(E_INFO, L_HTTPD, "Done streaming file id %d\n", st->id);
else
DPRINTF(E_LOG, L_HTTPD, "Streaming error, file id %d\n", st->id);
DPRINTF(E_LOG, L_HTTPD, "Read error, file id %d: %s\n", st->id, strerror(errno));
stream_end(st);
return;

View File

@ -74,20 +74,20 @@ response_process(struct httpd_request *hreq, int format)
static int
artworkapi_reply_nowplaying(struct httpd_request *hreq)
{
struct player_status status;
uint32_t max_w;
uint32_t max_h;
uint32_t id;
int ret;
ret = request_process(hreq, &max_w, &max_h);
if (ret != 0)
return ret;
ret = player_playing_now(&id);
if (ret != 0)
player_get_status(&status);
if (status.status == PLAY_STOPPED)
return HTTP_NOTFOUND;
ret = artwork_get_item(hreq->out_body, id, max_w, max_h, 0);
ret = artwork_get_by_queue_item_id(hreq->out_body, status.item_id, max_w, max_h, 0);
return response_process(hreq, ret);
}
@ -108,7 +108,7 @@ artworkapi_reply_item(struct httpd_request *hreq)
if (ret != 0)
return HTTP_BADREQUEST;
ret = artwork_get_item(hreq->out_body, id, max_w, max_h, 0);
ret = artwork_get_by_file_id(hreq->out_body, id, max_w, max_h, 0);
return response_process(hreq, ret);
}
@ -129,7 +129,7 @@ artworkapi_reply_group(struct httpd_request *hreq)
if (ret != 0)
return HTTP_BADREQUEST;
ret = artwork_get_group(hreq->out_body, id, max_w, max_h, 0);
ret = artwork_get_by_group_id(hreq->out_body, id, max_w, max_h, 0);
return response_process(hreq, ret);
}

View File

@ -1986,9 +1986,9 @@ daap_reply_extra_data(struct httpd_request *hreq)
}
if (strcmp(hreq->path_parts[2], "groups") == 0)
ret = artwork_get_group(hreq->out_body, id, max_w, max_h, 0);
ret = artwork_get_by_group_id(hreq->out_body, id, max_w, max_h, 0);
else if (strcmp(hreq->path_parts[2], "items") == 0)
ret = artwork_get_item(hreq->out_body, id, max_w, max_h, 0);
ret = artwork_get_by_file_id(hreq->out_body, id, max_w, max_h, 0);
len = evbuffer_get_length(hreq->out_body);

View File

@ -164,7 +164,7 @@ dacp_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct db_
* FIXME: Giving the client invalid ids on purpose is hardly ideal, but the
* clients don't seem to use these ids for anything other than rating.
*/
if (queue_item->data_kind == DATA_KIND_HTTP || queue_item->data_kind == DATA_KIND_PIPE)
if (queue_item->data_kind == DATA_KIND_HTTP || queue_item->data_kind == DATA_KIND_PIPE || queue_item->file_id == DB_MEDIA_FILE_NON_PERSISTENT_ID)
{
// Could also use queue_item->queue_version, but it changes a bit too much
// leading to Remote reloading too much
@ -1894,6 +1894,7 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq)
const char *querymodifier;
const char *sort;
const char *param;
const char *ptr;
char modifiedquery[32];
int mode;
int plid;
@ -1955,7 +1956,8 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq)
else
{
// Modify the query: Take the id from the editquery and use it as a queuefilter playlist id
ret = safe_atoi32(strchr(editquery, ':') + 1, &plid);
ptr = strchr(editquery, ':');
ret = ptr ? safe_atoi32(ptr + 1, &plid) : -1;
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Invalid playlist id in request: %s\n", editquery);
@ -2029,38 +2031,44 @@ dacp_reply_playqueueedit_move(struct httpd_request *hreq)
struct player_status status;
int ret;
const char *param;
const char *ptr;
int src;
int dst;
param = httpd_query_value_find(hreq->query, "edit-params");
if (param)
{
ret = safe_atoi32(strchr(param, ':') + 1, &src);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Invalid edit-params move-from value in playqueue-edit request\n");
if (!param)
goto out;
dacp_send_error(hreq, "cacr", "Invalid request");
return -1;
}
ptr = strchr(param, ':');
if (!ptr)
goto error;
ret = safe_atoi32(strchr(param, ',') + 1, &dst);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Invalid edit-params move-to value in playqueue-edit request\n");
ret = safe_atoi32(ptr + 1, &src);
if (ret < 0)
goto error;
dacp_send_error(hreq, "cacr", "Invalid request");
return -1;
}
ptr = strchr(param, ',');
if (!ptr)
goto error;
player_get_status(&status);
db_queue_move_byposrelativetoitem(src, dst, status.item_id, status.shuffle);
}
ret = safe_atoi32(ptr + 1, &dst);
if (ret < 0)
goto error;
player_get_status(&status);
db_queue_move_byposrelativetoitem(src, dst, status.item_id, status.shuffle);
out:
/* 204 No Content is the canonical reply */
httpd_send_reply(hreq, HTTP_NOCONTENT, "No Content", HTTPD_SEND_NO_GZIP);
return 0;
error:
DPRINTF(E_LOG, L_DACP, "Invalid edit-params in playqueue-edit request: '%s'\n", param);
dacp_send_error(hreq, "cacr", "Invalid request");
return -1;
}
static int
@ -2249,11 +2257,11 @@ dacp_reply_playstatusupdate(struct httpd_request *hreq)
static int
dacp_reply_nowplayingartwork(struct httpd_request *hreq)
{
struct player_status status;
char clen[32];
const char *param;
char *ctype;
size_t len;
uint32_t id;
int max_w;
int max_h;
int ret;
@ -2290,11 +2298,11 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq)
goto error;
}
ret = player_playing_now(&id);
if (ret < 0)
player_get_status(&status);
if (status.status == PLAY_STOPPED)
goto no_artwork;
ret = artwork_get_item(hreq->out_body, id, max_w, max_h, 0);
ret = artwork_get_by_queue_item_id(hreq->out_body, status.item_id, max_w, max_h, 0);
len = evbuffer_get_length(hreq->out_body);
switch (ret)
@ -2538,8 +2546,7 @@ dacp_reply_setspeakers(struct httpd_request *hreq)
}
nspk = 1;
ptr = param;
while ((ptr = strchr(ptr + 1, ',')))
for (ptr = param; ptr; ptr = strchr(ptr + 1, ','))
nspk++;
CHECK_NULL(L_DACP, ids = calloc((nspk + 1), sizeof(uint64_t)));

View File

@ -2391,7 +2391,7 @@ mpd_command_albumart(struct mpd_command_output *out, struct mpd_command_input *i
// Ref. docs: "If the song file was recognized, but there is no picture, the
// response is successful, but is otherwise empty"
format = artwork_get_item(artwork, id, ART_DEFAULT_WIDTH, ART_DEFAULT_HEIGHT, 0);
format = artwork_get_by_file_id(artwork, id, ART_DEFAULT_WIDTH, ART_DEFAULT_HEIGHT, 0);
if (format == ART_FMT_PNG)
evbuffer_add_printf(out->evbuf, "type: image/png\n");
else if (format == ART_FMT_JPEG)
@ -4150,7 +4150,7 @@ artwork_cb(struct evhttp_request *req, void *arg)
const char *path;
char *decoded_path;
char *last_slash;
int itemid;
int file_id;
int format;
if (evhttp_request_get_command(req) != EVHTTP_REQ_GET)
@ -4195,8 +4195,8 @@ artwork_cb(struct evhttp_request *req, void *arg)
DPRINTF(E_DBG, L_MPD, "Artwork request for path: %s\n", decoded_path);
itemid = db_file_id_byvirtualpath_match(decoded_path);
if (!itemid)
file_id = db_file_id_byvirtualpath_match(decoded_path);
if (!file_id)
{
DPRINTF(E_WARN, L_MPD, "No item found for path '%s' from request uri '%s'\n", decoded_path, uri);
evhttp_send_error(req, HTTP_NOTFOUND, "Document was not found");
@ -4205,17 +4205,9 @@ artwork_cb(struct evhttp_request *req, void *arg)
return;
}
evbuffer = evbuffer_new();
if (!evbuffer)
{
DPRINTF(E_LOG, L_MPD, "Could not allocate an evbuffer for artwork request\n");
evhttp_send_error(req, HTTP_INTERNAL, "Document was not found");
evhttp_uri_free(decoded);
free(decoded_path);
return;
}
CHECK_NULL(L_MPD, evbuffer = evbuffer_new());
format = artwork_get_item(evbuffer, itemid, ART_DEFAULT_WIDTH, ART_DEFAULT_HEIGHT, 0);
format = artwork_get_by_file_id(evbuffer, file_id, ART_DEFAULT_WIDTH, ART_DEFAULT_HEIGHT, 0);
if (format < 0)
{
evhttp_send_error(req, HTTP_NOTFOUND, "Document was not found");

View File

@ -1632,7 +1632,7 @@ airplay_metadata_prepare(struct output_metadata *metadata)
CHECK_NULL(L_AIRPLAY, rmd->metadata = evbuffer_new());
CHECK_NULL(L_AIRPLAY, tmp = evbuffer_new());
ret = artwork_get_item(rmd->artwork, queue_item->file_id, ART_DEFAULT_WIDTH, ART_DEFAULT_HEIGHT, 0);
ret = artwork_get_by_queue_item_id(rmd->artwork, queue_item->id, ART_DEFAULT_WIDTH, ART_DEFAULT_HEIGHT, 0);
if (ret < 0)
{
DPRINTF(E_INFO, L_AIRPLAY, "Failed to retrieve artwork for file '%s'; no artwork will be sent\n", queue_item->path);

View File

@ -2314,7 +2314,7 @@ raop_metadata_prepare(struct output_metadata *metadata)
CHECK_NULL(L_RAOP, rmd->metadata = evbuffer_new());
CHECK_NULL(L_RAOP, tmp = evbuffer_new());
ret = artwork_get_item(rmd->artwork, queue_item->file_id, ART_DEFAULT_WIDTH, ART_DEFAULT_HEIGHT, 0);
ret = artwork_get_by_queue_item_id(rmd->artwork, queue_item->id, ART_DEFAULT_WIDTH, ART_DEFAULT_HEIGHT, 0);
if (ret < 0)
{
DPRINTF(E_INFO, L_RAOP, "Failed to retrieve artwork for file '%s'; no artwork will be sent\n", queue_item->path);

File diff suppressed because it is too large Load Diff

View File

@ -16,27 +16,27 @@
"@mdi/js": "^7.4.47",
"@spotify/web-api-ts-sdk": "^1.2.0",
"@ts-pro/vue-eternal-loading": "^1.3.1",
"axios": "^1.11.0",
"axios": "^1.12.2",
"bulma": "^1.0.4",
"luxon": "^3.7.2",
"mdi-vue": "^3.0.13",
"pinia": "^3.0.3",
"reconnectingwebsocket": "^1.0.0",
"vue": "^3.5.21",
"vue": "^3.5.22",
"vue-i18n": "^11.1.12",
"vue-router": "^4.5.1",
"vue-router": "^4.6.3",
"vue3-click-away": "^1.2.4",
"vue3-lazyload": "^0.3.8",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@intlify/unplugin-vue-i18n": "^11.0.0",
"@intlify/unplugin-vue-i18n": "^11.0.1",
"@vitejs/plugin-vue": "^6.0.1",
"eslint": "^9.35.0",
"eslint": "^9.38.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-vue": "^10.4.0",
"eslint-plugin-vue": "^10.5.1",
"prettier": "^3.6.2",
"sass": "^1.92.1",
"vite": "^7.1.5"
"sass": "^1.93.2",
"vite": "^7.1.11"
}
}