mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 23:55:57 -05:00
Merge pull request #177 from chme/mpdartwork
[mpd] support serving artwork over http
This commit is contained in:
commit
9616738c65
@ -203,6 +203,18 @@ spotify {
|
||||
# starred_album_override = false
|
||||
}
|
||||
|
||||
# MPD configuration (only have effect if MPD enabled - see README/INSTALL)
|
||||
mpd {
|
||||
# TCP port to listen on for MPD client requests.
|
||||
# Default port is 6600, set to 0 to disable MPD support.
|
||||
# port = 6600
|
||||
|
||||
# HTTP port to listen for artwork requests (only supported by some MPD clients
|
||||
# and will need additional configuration in the MPD client to work).
|
||||
# Set to 0 to disable serving artwork over http.
|
||||
# http_port = 0
|
||||
}
|
||||
|
||||
# SQLite configuration (allows to modify the operation of the SQLite databases)
|
||||
# Make sure to read the SQLite documentation for the corresponding PRAGMA statements as
|
||||
# changing them from the defaults may increase the possibility of database corruptions!
|
||||
|
@ -139,6 +139,7 @@ static cfg_opt_t sec_sqlite[] =
|
||||
static cfg_opt_t sec_mpd[] =
|
||||
{
|
||||
CFG_INT("port", 6600, CFGF_NONE),
|
||||
CFG_INT("http_port", 0, CFGF_NONE),
|
||||
CFG_END()
|
||||
};
|
||||
|
||||
|
24
src/db.c
24
src/db.c
@ -2315,6 +2315,30 @@ db_file_id_byurl(char *url)
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
int
|
||||
db_file_id_by_virtualpath_match(char *path)
|
||||
{
|
||||
#define Q_TMPL "SELECT f.id FROM files f WHERE f.virtual_path LIKE '%%%q%%';"
|
||||
char *query;
|
||||
int ret;
|
||||
|
||||
query = sqlite3_mprintf(Q_TMPL, path);
|
||||
if (!query)
|
||||
{
|
||||
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = db_file_id_byquery(query);
|
||||
|
||||
sqlite3_free(query);
|
||||
|
||||
return ret;
|
||||
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
void
|
||||
db_file_stamp_bypath(char *path, time_t *stamp, int *id)
|
||||
{
|
||||
|
3
src/db.h
3
src/db.h
@ -452,6 +452,9 @@ db_file_id_byfile(char *filename);
|
||||
int
|
||||
db_file_id_byurl(char *url);
|
||||
|
||||
int
|
||||
db_file_id_by_virtualpath_match(char *path);
|
||||
|
||||
void
|
||||
db_file_stamp_bypath(char *path, time_t *stamp, int *id);
|
||||
|
||||
|
154
src/mpd.c
154
src/mpd.c
@ -37,6 +37,7 @@
|
||||
# include <event2/event.h>
|
||||
# include <event2/buffer.h>
|
||||
# include <event2/bufferevent.h>
|
||||
#include <event2/http.h>
|
||||
# include <event2/listener.h>
|
||||
|
||||
#if defined(HAVE_SYS_EVENTFD_H) && defined(HAVE_EVENTFD)
|
||||
@ -53,6 +54,7 @@
|
||||
#include "conffile.h"
|
||||
#include "misc.h"
|
||||
#include "listener.h"
|
||||
#include "artwork.h"
|
||||
|
||||
#include "player.h"
|
||||
#include "filescanner.h"
|
||||
@ -67,6 +69,8 @@ static struct event *g_exitev;
|
||||
static int g_cmd_pipe[2];
|
||||
static struct event *g_cmdev;
|
||||
|
||||
static struct evhttp *evhttpd;
|
||||
|
||||
struct mpd_command;
|
||||
|
||||
typedef int (*cmd_func)(struct mpd_command *cmd);
|
||||
@ -4274,6 +4278,118 @@ command_cb(int fd, short what, void *arg)
|
||||
event_add(g_cmdev, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback function that handles http requests for artwork files
|
||||
*
|
||||
* Some MPD clients allow retrieval of local artwork by making http request for artwork
|
||||
* files.
|
||||
*
|
||||
* A request for the artwork of an item with virtual path "file:/path/to/example.mp3" looks
|
||||
* like:
|
||||
* GET http://<host>:<port>/path/to/cover.jpg
|
||||
*
|
||||
* Artwork is found by taking the uri and removing everything after the last '/'. The first
|
||||
* item in the library with a virtual path that matches *path/to* is used to read the artwork
|
||||
* file through the default forked-daapd artwork logic.
|
||||
*/
|
||||
static void
|
||||
artwork_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
struct evbuffer *evbuffer;
|
||||
struct evhttp_uri *decoded;
|
||||
const char *uri;
|
||||
const char *path;
|
||||
char *decoded_path;
|
||||
char *last_slash;
|
||||
int itemid;
|
||||
int format;
|
||||
|
||||
if (evhttp_request_get_command(req) != EVHTTP_REQ_GET)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Unsupported request type for artwork\n");
|
||||
evhttp_send_error(req, HTTP_BADMETHOD, "Method not allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
uri = evhttp_request_get_uri(req);
|
||||
DPRINTF(E_DBG, L_MPD, "Got artwork request with uri '%s'\n", uri);
|
||||
|
||||
decoded = evhttp_uri_parse(uri);
|
||||
if (!decoded)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Bad artwork request with uri '%s'\n", uri);
|
||||
evhttp_send_error(req, HTTP_BADREQUEST, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
path = evhttp_uri_get_path(decoded);
|
||||
if (!path)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Invalid path from artwork request with uri '%s'\n", uri);
|
||||
evhttp_send_error(req, HTTP_BADREQUEST, 0);
|
||||
evhttp_uri_free(decoded);
|
||||
return;
|
||||
}
|
||||
|
||||
decoded_path = evhttp_uridecode(path, 0, NULL);
|
||||
if (!decoded_path)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Error decoding path from artwork request with uri '%s'\n", uri);
|
||||
evhttp_send_error(req, HTTP_BADREQUEST, 0);
|
||||
evhttp_uri_free(decoded);
|
||||
return;
|
||||
}
|
||||
|
||||
last_slash = strrchr(decoded_path, '/');
|
||||
if (last_slash)
|
||||
*last_slash = '\0';
|
||||
|
||||
DPRINTF(E_DBG, L_MPD, "Artwork request for path: %s\n", decoded_path);
|
||||
|
||||
itemid = db_file_id_by_virtualpath_match(decoded_path);
|
||||
if (!itemid)
|
||||
{
|
||||
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");
|
||||
evhttp_uri_free(decoded);
|
||||
free(decoded_path);
|
||||
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;
|
||||
}
|
||||
|
||||
format = artwork_get_item(evbuffer, itemid, 600, 600);
|
||||
if (format < 0)
|
||||
{
|
||||
evhttp_send_error(req, HTTP_NOTFOUND, "Document was not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case ART_FMT_PNG:
|
||||
evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "image/png");
|
||||
break;
|
||||
|
||||
default:
|
||||
evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "image/jpeg");
|
||||
break;
|
||||
}
|
||||
evhttp_send_reply(req, HTTP_OK, "OK", evbuffer);
|
||||
}
|
||||
|
||||
evbuffer_free(evbuffer);
|
||||
evhttp_uri_free(decoded);
|
||||
free(decoded_path);
|
||||
}
|
||||
|
||||
/* Thread: main */
|
||||
int mpd_init(void)
|
||||
@ -4284,6 +4400,8 @@ int mpd_init(void)
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_in6 sin6;
|
||||
unsigned short port;
|
||||
unsigned short http_port;
|
||||
const char *http_addr;
|
||||
int v6enabled;
|
||||
int ret;
|
||||
|
||||
@ -4380,6 +4498,33 @@ int mpd_init(void)
|
||||
}
|
||||
evconnlistener_set_error_cb(listener, mpd_accept_error_cb);
|
||||
|
||||
http_port = cfg_getint(cfg_getsec(cfg, "mpd"), "http_port");
|
||||
if (http_port > 0)
|
||||
{
|
||||
evhttpd = evhttp_new(evbase_mpd);
|
||||
if (!evhttpd)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not create HTTP artwork server\n");
|
||||
|
||||
goto evhttp_fail;
|
||||
}
|
||||
|
||||
evhttp_set_gencb(evhttpd, artwork_cb, NULL);
|
||||
|
||||
if (v6enabled)
|
||||
http_addr = "::";
|
||||
else
|
||||
http_addr = "0.0.0.0";
|
||||
|
||||
ret = evhttp_bind_socket(evhttpd, http_addr, http_port);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_MPD, "Could not bind HTTP artwork server at %s:%d\n", http_addr, port);
|
||||
|
||||
goto bind_fail;
|
||||
}
|
||||
}
|
||||
|
||||
DPRINTF(E_INFO, L_MPD, "mpd thread init\n");
|
||||
|
||||
ret = pthread_create(&tid_mpd, NULL, mpd, NULL);
|
||||
@ -4397,6 +4542,10 @@ int mpd_init(void)
|
||||
|
||||
|
||||
thread_fail:
|
||||
bind_fail:
|
||||
if (http_port > 0)
|
||||
evhttp_free(evhttpd);
|
||||
evhttp_fail:
|
||||
connew_fail:
|
||||
evnew_fail:
|
||||
event_base_free(evbase_mpd);
|
||||
@ -4419,6 +4568,7 @@ void mpd_deinit(void)
|
||||
{
|
||||
struct idle_client *temp;
|
||||
unsigned short port;
|
||||
unsigned short http_port;
|
||||
int ret;
|
||||
|
||||
port = cfg_getint(cfg_getsec(cfg, "mpd"), "port");
|
||||
@ -4446,6 +4596,10 @@ void mpd_deinit(void)
|
||||
free(temp);
|
||||
}
|
||||
|
||||
http_port = cfg_getint(cfg_getsec(cfg, "mpd"), "http_port");
|
||||
if (http_port > 0)
|
||||
evhttp_free(evhttpd);
|
||||
|
||||
// Free event base (should free events too)
|
||||
event_base_free(evbase_mpd);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user