mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-14 08:15:02 -05:00
Merge branch 'artwork_onlinesrc1'
This commit is contained in:
commit
bdc630baca
961
src/artwork.c
961
src/artwork.c
File diff suppressed because it is too large
Load Diff
21
src/cache.c
21
src/cache.c
@ -57,7 +57,8 @@ struct cache_arg
|
|||||||
int is_remote;
|
int is_remote;
|
||||||
int msec;
|
int msec;
|
||||||
|
|
||||||
char *path; // artwork path
|
const char *path; // artwork path
|
||||||
|
char *pathcopy; // copy of artwork path (for async operations)
|
||||||
int type; // individual or group artwork
|
int type; // individual or group artwork
|
||||||
int64_t persistentid;
|
int64_t persistentid;
|
||||||
int max_w;
|
int max_w;
|
||||||
@ -903,7 +904,7 @@ cache_daap_listener_cb(short event_mask)
|
|||||||
* after the cached timestamp. All cache entries for the given path are deleted, if the file was
|
* after the cached timestamp. All cache entries for the given path are deleted, if the file was
|
||||||
* modified after the cached timestamp.
|
* modified after the cached timestamp.
|
||||||
*
|
*
|
||||||
* @param cmdarg->path the full path to the artwork file (could be an jpg/png image or a media file with embedded artwork)
|
* @param cmdarg->pathcopy the full path to the artwork file (could be an jpg/png image or a media file with embedded artwork)
|
||||||
* @param cmdarg->mtime modified timestamp of the artwork file
|
* @param cmdarg->mtime modified timestamp of the artwork file
|
||||||
* @return 0 if successful, -1 if an error occurred
|
* @return 0 if successful, -1 if an error occurred
|
||||||
*/
|
*/
|
||||||
@ -919,7 +920,7 @@ cache_artwork_ping_impl(void *arg, int *retval)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
cmdarg = arg;
|
cmdarg = arg;
|
||||||
query = sqlite3_mprintf(Q_TMPL_PING, (int64_t)time(NULL), cmdarg->path, (int64_t)cmdarg->mtime);
|
query = sqlite3_mprintf(Q_TMPL_PING, (int64_t)time(NULL), cmdarg->pathcopy, (int64_t)cmdarg->mtime);
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_CACHE, "Running query '%s'\n", query);
|
DPRINTF(E_DBG, L_CACHE, "Running query '%s'\n", query);
|
||||||
|
|
||||||
@ -934,7 +935,7 @@ cache_artwork_ping_impl(void *arg, int *retval)
|
|||||||
|
|
||||||
if (cmdarg->del > 0)
|
if (cmdarg->del > 0)
|
||||||
{
|
{
|
||||||
query = sqlite3_mprintf(Q_TMPL_DEL, cmdarg->path, (int64_t)cmdarg->mtime);
|
query = sqlite3_mprintf(Q_TMPL_DEL, cmdarg->pathcopy, (int64_t)cmdarg->mtime);
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_CACHE, "Running query '%s'\n", query);
|
DPRINTF(E_DBG, L_CACHE, "Running query '%s'\n", query);
|
||||||
|
|
||||||
@ -948,14 +949,14 @@ cache_artwork_ping_impl(void *arg, int *retval)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(cmdarg->path);
|
free(cmdarg->pathcopy);
|
||||||
|
|
||||||
*retval = 0;
|
*retval = 0;
|
||||||
return COMMAND_END;
|
return COMMAND_END;
|
||||||
|
|
||||||
error_ping:
|
error_ping:
|
||||||
sqlite3_free(errmsg);
|
sqlite3_free(errmsg);
|
||||||
free(cmdarg->path);
|
free(cmdarg->pathcopy);
|
||||||
|
|
||||||
*retval = -1;
|
*retval = -1;
|
||||||
return COMMAND_END;
|
return COMMAND_END;
|
||||||
@ -1415,7 +1416,7 @@ cache_artwork_ping(const char *path, time_t mtime, int del)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdarg->path = strdup(path);
|
cmdarg->pathcopy = strdup(path);
|
||||||
cmdarg->mtime = mtime;
|
cmdarg->mtime = mtime;
|
||||||
cmdarg->del = del;
|
cmdarg->del = del;
|
||||||
|
|
||||||
@ -1429,7 +1430,7 @@ cache_artwork_ping(const char *path, time_t mtime, int del)
|
|||||||
* @return 0 if successful, -1 if an error occurred
|
* @return 0 if successful, -1 if an error occurred
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
cache_artwork_delete_by_path(char *path)
|
cache_artwork_delete_by_path(const char *path)
|
||||||
{
|
{
|
||||||
struct cache_arg cmdarg;
|
struct cache_arg cmdarg;
|
||||||
|
|
||||||
@ -1541,7 +1542,7 @@ cache_artwork_get(int type, int64_t persistentid, int max_w, int max_h, int *cac
|
|||||||
* @return 0 if successful, -1 if an error occurred
|
* @return 0 if successful, -1 if an error occurred
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
cache_artwork_stash(struct evbuffer *evbuf, char *path, int format)
|
cache_artwork_stash(struct evbuffer *evbuf, const char *path, int format)
|
||||||
{
|
{
|
||||||
struct cache_arg cmdarg;
|
struct cache_arg cmdarg;
|
||||||
|
|
||||||
@ -1564,7 +1565,7 @@ cache_artwork_stash(struct evbuffer *evbuf, char *path, int format)
|
|||||||
* @return 0 if successful, -1 if an error occurred
|
* @return 0 if successful, -1 if an error occurred
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
cache_artwork_read(struct evbuffer *evbuf, char *path, int *format)
|
cache_artwork_read(struct evbuffer *evbuf, const char *path, int *format)
|
||||||
{
|
{
|
||||||
struct cache_arg cmdarg;
|
struct cache_arg cmdarg;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -31,7 +31,7 @@ void
|
|||||||
cache_artwork_ping(const char *path, time_t mtime, int del);
|
cache_artwork_ping(const char *path, time_t mtime, int del);
|
||||||
|
|
||||||
int
|
int
|
||||||
cache_artwork_delete_by_path(char *path);
|
cache_artwork_delete_by_path(const char *path);
|
||||||
|
|
||||||
int
|
int
|
||||||
cache_artwork_purge_cruft(time_t ref);
|
cache_artwork_purge_cruft(time_t ref);
|
||||||
@ -43,10 +43,10 @@ int
|
|||||||
cache_artwork_get(int type, int64_t persistentid, int max_w, int max_h, int *cached, int *format, struct evbuffer *evbuf);
|
cache_artwork_get(int type, int64_t persistentid, int max_w, int max_h, int *cached, int *format, struct evbuffer *evbuf);
|
||||||
|
|
||||||
int
|
int
|
||||||
cache_artwork_stash(struct evbuffer *evbuf, char *path, int format);
|
cache_artwork_stash(struct evbuffer *evbuf, const char *path, int format);
|
||||||
|
|
||||||
int
|
int
|
||||||
cache_artwork_read(struct evbuffer *evbuf, char *path, int *format);
|
cache_artwork_read(struct evbuffer *evbuf, const char *path, int *format);
|
||||||
|
|
||||||
/* ---------------------------- Cache API --------------------------- */
|
/* ---------------------------- Cache API --------------------------- */
|
||||||
|
|
||||||
|
@ -92,6 +92,7 @@ static cfg_opt_t sec_library[] =
|
|||||||
CFG_STR("name_radio", "Radio", CFGF_NONE),
|
CFG_STR("name_radio", "Radio", CFGF_NONE),
|
||||||
CFG_STR_LIST("artwork_basenames", "{artwork,cover,Folder}", CFGF_NONE),
|
CFG_STR_LIST("artwork_basenames", "{artwork,cover,Folder}", CFGF_NONE),
|
||||||
CFG_BOOL("artwork_individual", cfg_false, CFGF_NONE),
|
CFG_BOOL("artwork_individual", cfg_false, CFGF_NONE),
|
||||||
|
CFG_STR_LIST("artwork_online_sources", NULL, CFGF_NONE),
|
||||||
CFG_STR_LIST("filetypes_ignore", "{.db,.ini,.db-journal,.pdf,.metadata}", CFGF_NONE),
|
CFG_STR_LIST("filetypes_ignore", "{.db,.ini,.db-journal,.pdf,.metadata}", CFGF_NONE),
|
||||||
CFG_STR_LIST("filepath_ignore", NULL, CFGF_NONE),
|
CFG_STR_LIST("filepath_ignore", NULL, CFGF_NONE),
|
||||||
CFG_BOOL("filescan_disable", cfg_false, CFGF_NONE),
|
CFG_BOOL("filescan_disable", cfg_false, CFGF_NONE),
|
||||||
|
70
src/db.c
70
src/db.c
@ -4353,7 +4353,7 @@ db_admin_setint64(const char *key, int64_t value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
admin_get(const char *key, short type, void *value)
|
admin_get(void *value, const char *key, short type)
|
||||||
{
|
{
|
||||||
#define Q_TMPL "SELECT value FROM admin a WHERE a.key = '%q';"
|
#define Q_TMPL "SELECT value FROM admin a WHERE a.key = '%q';"
|
||||||
char *query;
|
char *query;
|
||||||
@ -4364,13 +4364,7 @@ admin_get(const char *key, short type, void *value)
|
|||||||
char **strval;
|
char **strval;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
query = sqlite3_mprintf(Q_TMPL, key);
|
CHECK_NULL(L_DB, query = sqlite3_mprintf(Q_TMPL, key));
|
||||||
if (!query)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query);
|
DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query);
|
||||||
|
|
||||||
@ -4437,52 +4431,22 @@ admin_get(const char *key, short type, void *value)
|
|||||||
#undef Q_TMPL
|
#undef Q_TMPL
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
int
|
||||||
db_admin_get(const char *key)
|
db_admin_get(char **value, const char *key)
|
||||||
{
|
{
|
||||||
char *value = NULL;
|
return admin_get(value, key, DB_TYPE_STRING);
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = admin_get(key, DB_TYPE_STRING, &value);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_DB, "Could not find key '%s' in admin table\n", key);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
db_admin_getint(const char *key)
|
db_admin_getint(int *intval, const char *key)
|
||||||
{
|
{
|
||||||
int value = 0;
|
return admin_get(intval, key, DB_TYPE_INT);
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = admin_get(key, DB_TYPE_INT, &value);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_DB, "Could not find key '%s' in admin table\n", key);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t
|
int
|
||||||
db_admin_getint64(const char *key)
|
db_admin_getint64(int64_t *int64val, const char *key)
|
||||||
{
|
{
|
||||||
int64_t value = 0;
|
return admin_get(int64val, key, DB_TYPE_INT64);
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = admin_get(key, DB_TYPE_INT64, &value);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_DB, "Could not find key '%s' in admin table\n", key);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -4586,11 +4550,11 @@ db_speaker_clear_all(void)
|
|||||||
static int
|
static int
|
||||||
queue_transaction_begin()
|
queue_transaction_begin()
|
||||||
{
|
{
|
||||||
int queue_version;
|
int queue_version = 0;
|
||||||
|
|
||||||
db_transaction_begin();
|
db_transaction_begin();
|
||||||
|
|
||||||
queue_version = db_admin_getint(DB_ADMIN_QUEUE_VERSION);
|
db_admin_getint(&queue_version, DB_ADMIN_QUEUE_VERSION);
|
||||||
queue_version++;
|
queue_version++;
|
||||||
|
|
||||||
return queue_version;
|
return queue_version;
|
||||||
@ -6938,22 +6902,22 @@ db_check_version(void)
|
|||||||
{
|
{
|
||||||
#define Q_VACUUM "VACUUM;"
|
#define Q_VACUUM "VACUUM;"
|
||||||
char *errmsg;
|
char *errmsg;
|
||||||
int db_ver_major;
|
int db_ver_major = 0;
|
||||||
int db_ver_minor;
|
int db_ver_minor = 0;
|
||||||
int db_ver;
|
int db_ver;
|
||||||
int vacuum;
|
int vacuum;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
vacuum = cfg_getbool(cfg_getsec(cfg, "sqlite"), "vacuum");
|
vacuum = cfg_getbool(cfg_getsec(cfg, "sqlite"), "vacuum");
|
||||||
|
|
||||||
db_ver_major = db_admin_getint(DB_ADMIN_SCHEMA_VERSION_MAJOR);
|
db_admin_getint(&db_ver_major, DB_ADMIN_SCHEMA_VERSION_MAJOR);
|
||||||
if (!db_ver_major)
|
if (!db_ver_major)
|
||||||
db_ver_major = db_admin_getint(DB_ADMIN_SCHEMA_VERSION); // Pre schema v15.1
|
db_admin_getint(&db_ver_major, DB_ADMIN_SCHEMA_VERSION); // Pre schema v15.1
|
||||||
|
|
||||||
if (!db_ver_major)
|
if (!db_ver_major)
|
||||||
return 1; // Will create new database
|
return 1; // Will create new database
|
||||||
|
|
||||||
db_ver_minor = db_admin_getint(DB_ADMIN_SCHEMA_VERSION_MINOR);
|
db_admin_getint(&db_ver_minor, DB_ADMIN_SCHEMA_VERSION_MINOR);
|
||||||
|
|
||||||
db_ver = db_ver_major * 100 + db_ver_minor;
|
db_ver = db_ver_major * 100 + db_ver_minor;
|
||||||
|
|
||||||
|
10
src/db.h
10
src/db.h
@ -771,14 +771,14 @@ db_admin_setint(const char *key, int value);
|
|||||||
int
|
int
|
||||||
db_admin_setint64(const char *key, int64_t value);
|
db_admin_setint64(const char *key, int64_t value);
|
||||||
|
|
||||||
char *
|
int
|
||||||
db_admin_get(const char *key);
|
db_admin_get(char **value, const char *key);
|
||||||
|
|
||||||
int
|
int
|
||||||
db_admin_getint(const char *key);
|
db_admin_getint(int *intval, const char *key);
|
||||||
|
|
||||||
int64_t
|
int
|
||||||
db_admin_getint64(const char *key);
|
db_admin_getint64(int64_t *int64val, const char *key);
|
||||||
|
|
||||||
int
|
int
|
||||||
db_admin_delete(const char *key);
|
db_admin_delete(const char *key);
|
||||||
|
@ -386,10 +386,13 @@ https_client_request_impl(struct http_client_ctx *ctx)
|
|||||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ctx->output_body);
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ctx->output_body);
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, HTTP_CLIENT_TIMEOUT);
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, HTTP_CLIENT_TIMEOUT);
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_request_cb);
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_request_cb);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
|
||||||
|
|
||||||
|
// Artwork and playlist requests might require redirects
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5);
|
||||||
|
|
||||||
/* Make request */
|
/* Make request */
|
||||||
DPRINTF(E_INFO, L_HTTP, "Making request for %s\n", ctx->url);
|
DPRINTF(E_INFO, L_HTTP, "Making request for %s\n", ctx->url);
|
||||||
|
|
||||||
@ -416,7 +419,6 @@ http_client_request(struct http_client_ctx *ctx)
|
|||||||
{
|
{
|
||||||
if (strncmp(ctx->url, "http:", strlen("http:")) == 0)
|
if (strncmp(ctx->url, "http:", strlen("http:")) == 0)
|
||||||
return http_client_request_impl(ctx);
|
return http_client_request_impl(ctx);
|
||||||
|
|
||||||
#ifdef HAVE_LIBCURL
|
#ifdef HAVE_LIBCURL
|
||||||
if (strncmp(ctx->url, "https:", strlen("https:")) == 0)
|
if (strncmp(ctx->url, "https:", strlen("https:")) == 0)
|
||||||
return https_client_request_impl(ctx);
|
return https_client_request_impl(ctx);
|
||||||
@ -658,6 +660,8 @@ metadata_packet_get(struct http_icy_metadata *metadata, AVFormatContext *fmtctx)
|
|||||||
|
|
||||||
metadata->title = strdup(ptr + 3);
|
metadata->title = strdup(ptr + 3);
|
||||||
}
|
}
|
||||||
|
else if (strlen(metadata->title) == 0)
|
||||||
|
metadata->title = NULL;
|
||||||
else
|
else
|
||||||
metadata->title = strdup(metadata->title);
|
metadata->title = strdup(metadata->title);
|
||||||
}
|
}
|
||||||
|
@ -319,7 +319,7 @@ httpd_request_etag_matches(struct evhttp_request *req, const char *etag)
|
|||||||
* @return True if the given timestamp matches the request-header-value "If-Modified-Since", otherwise false
|
* @return True if the given timestamp matches the request-header-value "If-Modified-Since", otherwise false
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
httpd_request_not_modified_since(struct evhttp_request *req, const time_t *mtime)
|
httpd_request_not_modified_since(struct evhttp_request *req, time_t mtime)
|
||||||
{
|
{
|
||||||
struct evkeyvalq *input_headers;
|
struct evkeyvalq *input_headers;
|
||||||
struct evkeyvalq *output_headers;
|
struct evkeyvalq *output_headers;
|
||||||
@ -329,7 +329,7 @@ httpd_request_not_modified_since(struct evhttp_request *req, const time_t *mtime
|
|||||||
input_headers = evhttp_request_get_input_headers(req);
|
input_headers = evhttp_request_get_input_headers(req);
|
||||||
modified_since = evhttp_find_header(input_headers, "If-Modified-Since");
|
modified_since = evhttp_find_header(input_headers, "If-Modified-Since");
|
||||||
|
|
||||||
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S %Z", gmtime(mtime));
|
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S %Z", gmtime(&mtime));
|
||||||
|
|
||||||
// Return not modified, if given timestamp matches "If-Modified-Since" request header
|
// Return not modified, if given timestamp matches "If-Modified-Since" request header
|
||||||
if (modified_since && (strcasecmp(last_modified, modified_since) == 0))
|
if (modified_since && (strcasecmp(last_modified, modified_since) == 0))
|
||||||
@ -455,7 +455,7 @@ serve_file(struct evhttp_request *req, const char *uri)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (httpd_request_not_modified_since(req, &sb.st_mtime))
|
if (httpd_request_not_modified_since(req, sb.st_mtime))
|
||||||
{
|
{
|
||||||
httpd_send_reply(req, HTTP_NOTMODIFIED, NULL, NULL, HTTPD_SEND_NO_GZIP);
|
httpd_send_reply(req, HTTP_NOTMODIFIED, NULL, NULL, HTTPD_SEND_NO_GZIP);
|
||||||
return;
|
return;
|
||||||
|
@ -102,7 +102,7 @@ void
|
|||||||
httpd_stream_file(struct evhttp_request *req, int id);
|
httpd_stream_file(struct evhttp_request *req, int id);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
httpd_request_not_modified_since(struct evhttp_request *req, const time_t *mtime);
|
httpd_request_not_modified_since(struct evhttp_request *req, time_t mtime);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
httpd_request_etag_matches(struct evhttp_request *req, const char *etag);
|
httpd_request_etag_matches(struct evhttp_request *req, const char *etag);
|
||||||
|
@ -67,6 +67,16 @@ static char *default_playlist_directory;
|
|||||||
|
|
||||||
/* -------------------------------- HELPERS --------------------------------- */
|
/* -------------------------------- HELPERS --------------------------------- */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_modified(struct evhttp_request *req, const char *key)
|
||||||
|
{
|
||||||
|
int64_t db_update = 0;
|
||||||
|
|
||||||
|
db_admin_getint64(&db_update, key);
|
||||||
|
|
||||||
|
return (!db_update || !httpd_request_not_modified_since(req, (time_t)db_update));
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
safe_json_add_string(json_object *obj, const char *key, const char *value)
|
safe_json_add_string(json_object *obj, const char *key, const char *value)
|
||||||
{
|
{
|
||||||
@ -1023,10 +1033,19 @@ jsonapi_reply_library(struct httpd_request *hreq)
|
|||||||
DPRINTF(E_LOG, L_WEB, "library: failed to get file count info\n");
|
DPRINTF(E_LOG, L_WEB, "library: failed to get file count info\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
safe_json_add_time_from_string(jreply, "started_at", (s = db_admin_get(DB_ADMIN_START_TIME)), true);
|
ret = db_admin_get(&s, DB_ADMIN_START_TIME);
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
safe_json_add_time_from_string(jreply, "started_at", s, true);
|
||||||
free(s);
|
free(s);
|
||||||
safe_json_add_time_from_string(jreply, "updated_at", (s = db_admin_get(DB_ADMIN_DB_UPDATE)), true);
|
}
|
||||||
|
|
||||||
|
ret = db_admin_get(&s, DB_ADMIN_DB_UPDATE);
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
safe_json_add_time_from_string(jreply, "updated_at", s, true);
|
||||||
free(s);
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
json_object_object_add(jreply, "updating", json_object_new_boolean(library_is_scanning()));
|
json_object_object_add(jreply, "updating", json_object_new_boolean(library_is_scanning()));
|
||||||
|
|
||||||
@ -2037,7 +2056,7 @@ queue_item_to_json(struct db_queue_item *queue_item, char shuffle)
|
|||||||
}
|
}
|
||||||
else if (queue_item->file_id > 0 && queue_item->file_id != DB_MEDIA_FILE_NON_PERSISTENT_ID)
|
else if (queue_item->file_id > 0 && queue_item->file_id != DB_MEDIA_FILE_NON_PERSISTENT_ID)
|
||||||
{
|
{
|
||||||
if (queue_item->data_kind != DATA_KIND_PIPE)
|
if (queue_item->data_kind == DATA_KIND_FILE)
|
||||||
{
|
{
|
||||||
// Queue item does not have a valid artwork url, construct artwork url to
|
// Queue item does not have a valid artwork url, construct artwork url to
|
||||||
// get the image through the httpd_artworkapi (uses the artwork handlers).
|
// get the image through the httpd_artworkapi (uses the artwork handlers).
|
||||||
@ -2047,7 +2066,7 @@ queue_item_to_json(struct db_queue_item *queue_item, char shuffle)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Pipe metadata can change if the queue version changes. Construct artwork url
|
// Pipe and stream metadata can change if the queue version changes. Construct artwork url
|
||||||
// similar to non-pipe items, but append the queue version to the url to force
|
// similar to non-pipe items, but append the queue version to the url to force
|
||||||
// clients to reload image if the queue version changes (additional metadata was found).
|
// clients to reload image if the queue version changes (additional metadata was found).
|
||||||
ret = snprintf(artwork_url, sizeof(artwork_url), "/artwork/item/%d?v=%d", queue_item->file_id, queue_item->queue_version);
|
ret = snprintf(artwork_url, sizeof(artwork_url), "/artwork/item/%d?v=%d", queue_item->file_id, queue_item->queue_version);
|
||||||
@ -2458,7 +2477,7 @@ jsonapi_reply_queue(struct httpd_request *hreq)
|
|||||||
uint32_t item_id;
|
uint32_t item_id;
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
int start_pos, end_pos;
|
int start_pos, end_pos;
|
||||||
int version;
|
int version = 0;
|
||||||
char etag[21];
|
char etag[21];
|
||||||
struct player_status status;
|
struct player_status status;
|
||||||
struct db_queue_item queue_item;
|
struct db_queue_item queue_item;
|
||||||
@ -2467,7 +2486,7 @@ jsonapi_reply_queue(struct httpd_request *hreq)
|
|||||||
json_object *item;
|
json_object *item;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
version = db_admin_getint(DB_ADMIN_QUEUE_VERSION);
|
db_admin_getint(&version, DB_ADMIN_QUEUE_VERSION);
|
||||||
db_queue_get_count(&count);
|
db_queue_get_count(&count);
|
||||||
|
|
||||||
snprintf(etag, sizeof(etag), "%d", version);
|
snprintf(etag, sizeof(etag), "%d", version);
|
||||||
@ -2715,7 +2734,6 @@ jsonapi_reply_player_volume(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_artists(struct httpd_request *hreq)
|
jsonapi_reply_library_artists(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
struct query_params query_params;
|
struct query_params query_params;
|
||||||
const char *param;
|
const char *param;
|
||||||
enum media_kind media_kind;
|
enum media_kind media_kind;
|
||||||
@ -2724,11 +2742,9 @@ jsonapi_reply_library_artists(struct httpd_request *hreq)
|
|||||||
int total;
|
int total;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
if (!is_modified(hreq->req, DB_ADMIN_DB_UPDATE))
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
media_kind = 0;
|
media_kind = 0;
|
||||||
param = evhttp_find_header(hreq->query, "media_kind");
|
param = evhttp_find_header(hreq->query, "media_kind");
|
||||||
if (param)
|
if (param)
|
||||||
@ -2782,16 +2798,13 @@ jsonapi_reply_library_artists(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_artist(struct httpd_request *hreq)
|
jsonapi_reply_library_artist(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
const char *artist_id;
|
const char *artist_id;
|
||||||
json_object *reply;
|
json_object *reply;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
if (!is_modified(hreq->req, DB_ADMIN_DB_UPDATE))
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
artist_id = hreq->uri_parsed->path_parts[3];
|
artist_id = hreq->uri_parsed->path_parts[3];
|
||||||
|
|
||||||
reply = fetch_artist(artist_id);
|
reply = fetch_artist(artist_id);
|
||||||
@ -2817,7 +2830,6 @@ jsonapi_reply_library_artist(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_artist_albums(struct httpd_request *hreq)
|
jsonapi_reply_library_artist_albums(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
struct query_params query_params;
|
struct query_params query_params;
|
||||||
const char *artist_id;
|
const char *artist_id;
|
||||||
json_object *reply;
|
json_object *reply;
|
||||||
@ -2825,11 +2837,9 @@ jsonapi_reply_library_artist_albums(struct httpd_request *hreq)
|
|||||||
int total;
|
int total;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
if (!is_modified(hreq->req, DB_ADMIN_DB_UPDATE))
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
artist_id = hreq->uri_parsed->path_parts[3];
|
artist_id = hreq->uri_parsed->path_parts[3];
|
||||||
|
|
||||||
reply = json_object_new_object();
|
reply = json_object_new_object();
|
||||||
@ -2872,7 +2882,6 @@ jsonapi_reply_library_artist_albums(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_albums(struct httpd_request *hreq)
|
jsonapi_reply_library_albums(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
struct query_params query_params;
|
struct query_params query_params;
|
||||||
const char *param;
|
const char *param;
|
||||||
enum media_kind media_kind;
|
enum media_kind media_kind;
|
||||||
@ -2881,11 +2890,9 @@ jsonapi_reply_library_albums(struct httpd_request *hreq)
|
|||||||
int total;
|
int total;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
if (!is_modified(hreq->req, DB_ADMIN_DB_UPDATE))
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
media_kind = 0;
|
media_kind = 0;
|
||||||
param = evhttp_find_header(hreq->query, "media_kind");
|
param = evhttp_find_header(hreq->query, "media_kind");
|
||||||
if (param)
|
if (param)
|
||||||
@ -2939,16 +2946,13 @@ jsonapi_reply_library_albums(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_album(struct httpd_request *hreq)
|
jsonapi_reply_library_album(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
const char *album_id;
|
const char *album_id;
|
||||||
json_object *reply;
|
json_object *reply;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
if (!is_modified(hreq->req, DB_ADMIN_DB_UPDATE))
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
album_id = hreq->uri_parsed->path_parts[3];
|
album_id = hreq->uri_parsed->path_parts[3];
|
||||||
|
|
||||||
reply = fetch_album(album_id);
|
reply = fetch_album(album_id);
|
||||||
@ -2974,7 +2978,6 @@ jsonapi_reply_library_album(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_album_tracks(struct httpd_request *hreq)
|
jsonapi_reply_library_album_tracks(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
struct query_params query_params;
|
struct query_params query_params;
|
||||||
const char *album_id;
|
const char *album_id;
|
||||||
json_object *reply;
|
json_object *reply;
|
||||||
@ -2982,11 +2985,9 @@ jsonapi_reply_library_album_tracks(struct httpd_request *hreq)
|
|||||||
int total;
|
int total;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_MODIFIED);
|
if (!is_modified(hreq->req, DB_ADMIN_DB_MODIFIED))
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
album_id = hreq->uri_parsed->path_parts[3];
|
album_id = hreq->uri_parsed->path_parts[3];
|
||||||
|
|
||||||
reply = json_object_new_object();
|
reply = json_object_new_object();
|
||||||
@ -3029,18 +3030,15 @@ jsonapi_reply_library_album_tracks(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_tracks_get_byid(struct httpd_request *hreq)
|
jsonapi_reply_library_tracks_get_byid(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
struct query_params query_params;
|
struct query_params query_params;
|
||||||
const char *track_id;
|
const char *track_id;
|
||||||
struct db_media_file_info dbmfi;
|
struct db_media_file_info dbmfi;
|
||||||
json_object *reply = NULL;
|
json_object *reply = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_MODIFIED);
|
if (!is_modified(hreq->req, DB_ADMIN_DB_MODIFIED))
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
track_id = hreq->uri_parsed->path_parts[3];
|
track_id = hreq->uri_parsed->path_parts[3];
|
||||||
|
|
||||||
memset(&query_params, 0, sizeof(struct query_params));
|
memset(&query_params, 0, sizeof(struct query_params));
|
||||||
@ -3133,18 +3131,15 @@ jsonapi_reply_library_tracks_put_byid(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_playlists(struct httpd_request *hreq)
|
jsonapi_reply_library_playlists(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
struct query_params query_params;
|
struct query_params query_params;
|
||||||
json_object *reply;
|
json_object *reply;
|
||||||
json_object *items;
|
json_object *items;
|
||||||
int total;
|
int total;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
if (!is_modified(hreq->req, DB_ADMIN_DB_UPDATE))
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
reply = json_object_new_object();
|
reply = json_object_new_object();
|
||||||
items = json_object_new_array();
|
items = json_object_new_array();
|
||||||
json_object_object_add(reply, "items", items);
|
json_object_object_add(reply, "items", items);
|
||||||
@ -3185,16 +3180,13 @@ jsonapi_reply_library_playlists(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_playlist(struct httpd_request *hreq)
|
jsonapi_reply_library_playlist(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
const char *playlist_id;
|
const char *playlist_id;
|
||||||
json_object *reply;
|
json_object *reply;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
if (!is_modified(hreq->req, DB_ADMIN_DB_UPDATE))
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
playlist_id = hreq->uri_parsed->path_parts[3];
|
playlist_id = hreq->uri_parsed->path_parts[3];
|
||||||
|
|
||||||
reply = fetch_playlist(playlist_id);
|
reply = fetch_playlist(playlist_id);
|
||||||
@ -3220,7 +3212,6 @@ jsonapi_reply_library_playlist(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_playlist_tracks(struct httpd_request *hreq)
|
jsonapi_reply_library_playlist_tracks(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
struct query_params query_params;
|
struct query_params query_params;
|
||||||
json_object *reply;
|
json_object *reply;
|
||||||
json_object *items;
|
json_object *items;
|
||||||
@ -3228,11 +3219,9 @@ jsonapi_reply_library_playlist_tracks(struct httpd_request *hreq)
|
|||||||
int total;
|
int total;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_MODIFIED);
|
if (!is_modified(hreq->req, DB_ADMIN_DB_MODIFIED))
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &playlist_id);
|
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &playlist_id);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
@ -3325,7 +3314,6 @@ jsonapi_reply_queue_save(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_genres(struct httpd_request *hreq)
|
jsonapi_reply_library_genres(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
struct query_params query_params;
|
struct query_params query_params;
|
||||||
const char *param;
|
const char *param;
|
||||||
enum media_kind media_kind;
|
enum media_kind media_kind;
|
||||||
@ -3334,9 +3322,7 @@ jsonapi_reply_library_genres(struct httpd_request *hreq)
|
|||||||
int total;
|
int total;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (!is_modified(hreq->req, DB_ADMIN_DB_UPDATE))
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
media_kind = 0;
|
media_kind = 0;
|
||||||
@ -3394,7 +3380,6 @@ jsonapi_reply_library_genres(struct httpd_request *hreq)
|
|||||||
static int
|
static int
|
||||||
jsonapi_reply_library_count(struct httpd_request *hreq)
|
jsonapi_reply_library_count(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
time_t db_update;
|
|
||||||
const char *param_expression;
|
const char *param_expression;
|
||||||
char *expression;
|
char *expression;
|
||||||
struct smartpl smartpl_expression;
|
struct smartpl smartpl_expression;
|
||||||
@ -3403,9 +3388,7 @@ jsonapi_reply_library_count(struct httpd_request *hreq)
|
|||||||
json_object *jreply;
|
json_object *jreply;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (!is_modified(hreq->req, DB_ADMIN_DB_UPDATE))
|
||||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
|
||||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
|
||||||
return HTTP_NOTMODIFIED;
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
memset(&qp, 0, sizeof(struct query_params));
|
memset(&qp, 0, sizeof(struct query_params));
|
||||||
|
@ -144,12 +144,9 @@ metadata_get_http(struct input_metadata *metadata, struct input_source *source)
|
|||||||
return -1; // TODO Perhaps a problem since this prohibits the player updating metadata
|
return -1; // TODO Perhaps a problem since this prohibits the player updating metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m->artist)
|
|
||||||
swap_pointers(&metadata->artist, &m->artist);
|
swap_pointers(&metadata->artist, &m->artist);
|
||||||
// Note we map title to album, because clients should show stream name as titel
|
// Note we map title to album, because clients should show stream name as titel
|
||||||
if (m->title)
|
|
||||||
swap_pointers(&metadata->album, &m->title);
|
swap_pointers(&metadata->album, &m->title);
|
||||||
if (m->artwork_url)
|
|
||||||
swap_pointers(&metadata->artwork_url, &m->artwork_url);
|
swap_pointers(&metadata->artwork_url, &m->artwork_url);
|
||||||
|
|
||||||
http_icy_metadata_free(m, 0);
|
http_icy_metadata_free(m, 0);
|
||||||
|
@ -422,8 +422,10 @@ lastfm_is_enabled(void)
|
|||||||
int
|
int
|
||||||
lastfm_init(void)
|
lastfm_init(void)
|
||||||
{
|
{
|
||||||
lastfm_session_key = db_admin_get(DB_ADMIN_LASTFM_SESSION_KEY);
|
int ret;
|
||||||
if (!lastfm_session_key)
|
|
||||||
|
ret = db_admin_get(&lastfm_session_key, DB_ADMIN_LASTFM_SESSION_KEY);
|
||||||
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_LASTFM, "No valid LastFM session key\n");
|
DPRINTF(E_DBG, L_LASTFM, "No valid LastFM session key\n");
|
||||||
lastfm_disabled = true;
|
lastfm_disabled = true;
|
||||||
|
@ -121,8 +121,10 @@ vlogger_writer(int severity, int domain, const char *fmt, va_list args)
|
|||||||
|
|
||||||
va_copy(ap, args);
|
va_copy(ap, args);
|
||||||
ret = vsnprintf(content, sizeof(content), fmt, ap);
|
ret = vsnprintf(content, sizeof(content), fmt, ap);
|
||||||
if (ret < 0 || ret >= sizeof(content))
|
if (ret < 0)
|
||||||
strcpy(content, "(LOGGING SKIPPED - invalid content)\n");
|
strcpy(content, "(LOGGING SKIPPED - error printing log message)\n");
|
||||||
|
else if (ret >= sizeof(content))
|
||||||
|
strcpy(content + sizeof(content) - 8, "...\n");
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
ret = repeat_count(content);
|
ret = repeat_count(content);
|
||||||
|
47
src/misc.c
47
src/misc.c
@ -380,6 +380,49 @@ safe_snprintf_cat(char *dst, size_t n, const char *fmt, ...)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
safe_snreplace(char *s, size_t sz, const char *pattern, const char *replacement)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
char *src;
|
||||||
|
char *dst;
|
||||||
|
size_t num;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!pattern || !replacement)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t p_len = strlen(pattern);
|
||||||
|
size_t r_len = strlen(replacement);
|
||||||
|
size_t s_len = strlen(s) + 1; // Incl terminator
|
||||||
|
|
||||||
|
ptr = s;
|
||||||
|
while ((ptr = strstr(ptr, pattern)))
|
||||||
|
{
|
||||||
|
// We will move the part of the string after the pattern from src to dst
|
||||||
|
src = ptr + p_len;
|
||||||
|
dst = ptr + r_len;
|
||||||
|
|
||||||
|
num = s_len - (src - s); // Number of bytes w/terminator we need to move
|
||||||
|
if (dst + num > s + sz)
|
||||||
|
return -1; // Not enough room
|
||||||
|
|
||||||
|
// Shift everything after the pattern to the right, use memmove since
|
||||||
|
// there might be an overlap
|
||||||
|
memmove(dst, src, num);
|
||||||
|
|
||||||
|
// Write replacement, no null terminater
|
||||||
|
memcpy(ptr, replacement, r_len);
|
||||||
|
|
||||||
|
// Advance ptr to avoid infinite looping
|
||||||
|
ptr = dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Key/value functions */
|
/* Key/value functions */
|
||||||
struct keyval *
|
struct keyval *
|
||||||
@ -387,7 +430,7 @@ keyval_alloc(void)
|
|||||||
{
|
{
|
||||||
struct keyval *kv;
|
struct keyval *kv;
|
||||||
|
|
||||||
kv = (struct keyval *)malloc(sizeof(struct keyval));
|
kv = calloc(1, sizeof(struct keyval));
|
||||||
if (!kv)
|
if (!kv)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MISC, "Out of memory for keyval alloc\n");
|
DPRINTF(E_LOG, L_MISC, "Out of memory for keyval alloc\n");
|
||||||
@ -395,8 +438,6 @@ keyval_alloc(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(kv, 0, sizeof(struct keyval));
|
|
||||||
|
|
||||||
return kv;
|
return kv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +87,8 @@ safe_asprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
|||||||
int
|
int
|
||||||
safe_snprintf_cat(char *dst, size_t n, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
safe_snprintf_cat(char *dst, size_t n, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||||
|
|
||||||
|
int
|
||||||
|
safe_snreplace(char *s, size_t sz, const char *pattern, const char *replacement);
|
||||||
|
|
||||||
/* Key/value functions */
|
/* Key/value functions */
|
||||||
struct keyval *
|
struct keyval *
|
||||||
|
@ -32,6 +32,30 @@
|
|||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
|
json_object *
|
||||||
|
jparse_select(json_object *haystack, const char *keys[])
|
||||||
|
{
|
||||||
|
json_object *needle;
|
||||||
|
json_bool found;
|
||||||
|
|
||||||
|
needle = NULL;
|
||||||
|
while (*keys)
|
||||||
|
{
|
||||||
|
found = json_object_object_get_ex(haystack, *keys, &needle);
|
||||||
|
if (!found)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (json_object_get_type(needle) == json_type_array)
|
||||||
|
haystack = json_object_array_get_idx(needle, 0);
|
||||||
|
else
|
||||||
|
haystack = needle;
|
||||||
|
|
||||||
|
keys++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return needle;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
jparse_free(json_object* haystack)
|
jparse_free(json_object* haystack)
|
||||||
{
|
{
|
||||||
|
@ -33,6 +33,13 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
// Convenience macro so that instead of calling jparse with an array of keys
|
||||||
|
// to follow, you can call JPARSE_DRILLDOWN(haystack, "key1", "key2"...)
|
||||||
|
#define JPARSE_SELECT(haystack, ...) jparse_select(haystack, (const char *[]){__VA_ARGS__, NULL})
|
||||||
|
|
||||||
|
json_object *
|
||||||
|
jparse_select(json_object *haystack, const char *keys[]);
|
||||||
|
|
||||||
void
|
void
|
||||||
jparse_free(json_object *haystack);
|
jparse_free(json_object *haystack);
|
||||||
|
|
||||||
|
14
src/mpd.c
14
src/mpd.c
@ -959,7 +959,7 @@ mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg,
|
|||||||
{
|
{
|
||||||
struct player_status status;
|
struct player_status status;
|
||||||
uint32_t queue_length = 0;
|
uint32_t queue_length = 0;
|
||||||
int queue_version;
|
int queue_version = 0;
|
||||||
char *state;
|
char *state;
|
||||||
uint32_t itemid = 0;
|
uint32_t itemid = 0;
|
||||||
struct db_queue_item *queue_item;
|
struct db_queue_item *queue_item;
|
||||||
@ -981,7 +981,7 @@ mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue_version = db_admin_getint(DB_ADMIN_QUEUE_VERSION);
|
db_admin_getint(&queue_version, DB_ADMIN_QUEUE_VERSION);
|
||||||
db_queue_get_count(&queue_length);
|
db_queue_get_count(&queue_length);
|
||||||
|
|
||||||
evbuffer_add_printf(evbuf,
|
evbuffer_add_printf(evbuf,
|
||||||
@ -1062,9 +1062,9 @@ mpd_command_stats(struct evbuffer *evbuf, int argc, char **argv, char **errmsg,
|
|||||||
{
|
{
|
||||||
struct query_params qp;
|
struct query_params qp;
|
||||||
struct filecount_info fci;
|
struct filecount_info fci;
|
||||||
time_t start_time;
|
|
||||||
double uptime;
|
double uptime;
|
||||||
int64_t db_update;
|
int64_t db_start = 0;
|
||||||
|
int64_t db_update = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
memset(&qp, 0, sizeof(struct query_params));
|
memset(&qp, 0, sizeof(struct query_params));
|
||||||
@ -1077,9 +1077,9 @@ mpd_command_stats(struct evbuffer *evbuf, int argc, char **argv, char **errmsg,
|
|||||||
return ACK_ERROR_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
start_time = (time_t) db_admin_getint64(DB_ADMIN_START_TIME);
|
db_admin_getint64(&db_start, DB_ADMIN_START_TIME);
|
||||||
uptime = difftime(time(NULL), start_time);
|
uptime = difftime(time(NULL), (time_t) db_start);
|
||||||
db_update = db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
db_admin_getint64(&db_update, DB_ADMIN_DB_UPDATE);
|
||||||
|
|
||||||
//TODO [mpd] Implement missing stats attributes (playtime)
|
//TODO [mpd] Implement missing stats attributes (playtime)
|
||||||
evbuffer_add_printf(evbuf,
|
evbuffer_add_printf(evbuf,
|
||||||
|
100
src/settings.c
100
src/settings.c
@ -22,8 +22,12 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
|
#include "conffile.h"
|
||||||
|
|
||||||
|
// Forward - setting initializers
|
||||||
|
static bool artwork_spotify_default_getbool(struct settings_option *option);
|
||||||
|
static bool artwork_discogs_default_getbool(struct settings_option *option);
|
||||||
|
static bool artwork_coverartarchive_default_getbool(struct settings_option *option);
|
||||||
|
|
||||||
static struct settings_option webinterface_options[] =
|
static struct settings_option webinterface_options[] =
|
||||||
{
|
{
|
||||||
@ -31,12 +35,68 @@ static struct settings_option webinterface_options[] =
|
|||||||
{ "show_composer_for_genre", SETTINGS_TYPE_STR },
|
{ "show_composer_for_genre", SETTINGS_TYPE_STR },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct settings_option artwork_options[] =
|
||||||
|
{
|
||||||
|
{ "use_artwork_source_spotify", SETTINGS_TYPE_BOOL, NULL, artwork_spotify_default_getbool, NULL },
|
||||||
|
{ "use_artwork_source_discogs", SETTINGS_TYPE_BOOL, NULL, artwork_discogs_default_getbool, NULL },
|
||||||
|
{ "use_artwork_source_coverartarchive", SETTINGS_TYPE_BOOL, NULL, artwork_coverartarchive_default_getbool, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
static struct settings_category categories[] =
|
static struct settings_category categories[] =
|
||||||
{
|
{
|
||||||
{ "webinterface", webinterface_options, ARRAY_SIZE(webinterface_options) },
|
{ "webinterface", webinterface_options, ARRAY_SIZE(webinterface_options) },
|
||||||
|
{ "artwork", artwork_options, ARRAY_SIZE(artwork_options) },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------------------------- DEFAULT SETTERS ------------------------------*/
|
||||||
|
|
||||||
|
static bool
|
||||||
|
artwork_default_getbool(bool no_cfg_default, const char *cfg_name)
|
||||||
|
{
|
||||||
|
cfg_t *lib = cfg_getsec(cfg, "library");
|
||||||
|
const char *name;
|
||||||
|
int n_cfg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
n_cfg = cfg_size(lib, "artwork_online_sources");
|
||||||
|
if (n_cfg == 0)
|
||||||
|
return no_cfg_default;
|
||||||
|
|
||||||
|
for (i = 0; i < n_cfg; i++)
|
||||||
|
{
|
||||||
|
name = cfg_getnstr(lib, "artwork_online_sources", i);
|
||||||
|
if (strcasecmp(name, cfg_name) == 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
artwork_spotify_default_getbool(struct settings_option *option)
|
||||||
|
{
|
||||||
|
// Enabled by default, it will only work for premium users anyway. So Spotify
|
||||||
|
// probably won't mind, and the user probably also won't mind that we share
|
||||||
|
// data with Spotify, since he is already doing it.
|
||||||
|
return artwork_default_getbool(true, "spotify");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
artwork_discogs_default_getbool(struct settings_option *option)
|
||||||
|
{
|
||||||
|
return artwork_default_getbool(false, "discogs");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
artwork_coverartarchive_default_getbool(struct settings_option *option)
|
||||||
|
{
|
||||||
|
return artwork_default_getbool(false, "coverartarchive");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------ IMPLEMENTATION -----------------------------*/
|
||||||
|
|
||||||
int
|
int
|
||||||
settings_categories_count()
|
settings_categories_count()
|
||||||
{
|
{
|
||||||
@ -97,32 +157,62 @@ settings_option_get(struct settings_category *category, const char *name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
settings_option_getint(struct settings_option *option)
|
settings_option_getint(struct settings_option *option)
|
||||||
{
|
{
|
||||||
|
int intval = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!option || option->type != SETTINGS_TYPE_INT)
|
if (!option || option->type != SETTINGS_TYPE_INT)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return db_admin_getint(option->name);
|
ret = db_admin_getint(&intval, option->name);
|
||||||
}
|
if (ret == 0)
|
||||||
|
return intval;
|
||||||
|
|
||||||
|
if (option->default_getint)
|
||||||
|
return option->default_getint(option);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
settings_option_getbool(struct settings_option *option)
|
settings_option_getbool(struct settings_option *option)
|
||||||
{
|
{
|
||||||
|
int intval = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!option || option->type != SETTINGS_TYPE_BOOL)
|
if (!option || option->type != SETTINGS_TYPE_BOOL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return db_admin_getint(option->name) > 0;
|
ret = db_admin_getint(&intval, option->name);
|
||||||
|
if (ret == 0)
|
||||||
|
return (intval != 0);
|
||||||
|
|
||||||
|
if (option->default_getbool)
|
||||||
|
return option->default_getbool(option);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
settings_option_getstr(struct settings_option *option)
|
settings_option_getstr(struct settings_option *option)
|
||||||
{
|
{
|
||||||
|
char *s = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!option || option->type != SETTINGS_TYPE_STR)
|
if (!option || option->type != SETTINGS_TYPE_STR)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return db_admin_get(option->name);
|
ret = db_admin_get(&s, option->name);
|
||||||
|
if (ret == 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
if (option->default_getstr)
|
||||||
|
return option->default_getstr(option);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -15,6 +15,9 @@ enum settings_type {
|
|||||||
struct settings_option {
|
struct settings_option {
|
||||||
const char *name;
|
const char *name;
|
||||||
enum settings_type type;
|
enum settings_type type;
|
||||||
|
int (*default_getint)(struct settings_option *option);
|
||||||
|
bool (*default_getbool)(struct settings_option *option);
|
||||||
|
char *(*default_getstr)(struct settings_option *option);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct settings_category {
|
struct settings_category {
|
||||||
@ -42,10 +45,10 @@ settings_option_get_byindex(struct settings_category *category, int index);
|
|||||||
struct settings_option *
|
struct settings_option *
|
||||||
settings_option_get(struct settings_category *category, const char *name);
|
settings_option_get(struct settings_category *category, const char *name);
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
settings_option_getint(struct settings_option *option);
|
settings_option_getint(struct settings_option *option);
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
settings_option_getbool(struct settings_option *option);
|
settings_option_getbool(struct settings_option *option);
|
||||||
|
|
||||||
@ -61,5 +64,4 @@ settings_option_setbool(struct settings_option *option, bool value);
|
|||||||
int
|
int
|
||||||
settings_option_setstr(struct settings_option *option, const char *value);
|
settings_option_setstr(struct settings_option *option, const char *value);
|
||||||
|
|
||||||
|
|
||||||
#endif /* __SETTINGS_H__ */
|
#endif /* __SETTINGS_H__ */
|
||||||
|
@ -419,8 +419,8 @@ token_refresh(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh_token = db_admin_get(DB_ADMIN_SPOTIFY_REFRESH_TOKEN);
|
ret = db_admin_get(&refresh_token, DB_ADMIN_SPOTIFY_REFRESH_TOKEN);
|
||||||
if (!refresh_token)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "No spotify refresh token found\n");
|
DPRINTF(E_LOG, L_SPOTIFY, "No spotify refresh token found\n");
|
||||||
goto error;
|
goto error;
|
||||||
|
Loading…
Reference in New Issue
Block a user