Skip to content

Commit

Permalink
Feat: Implement WebradioDB cache #1071
Browse files Browse the repository at this point in the history
  • Loading branch information
jcorporation committed Jun 24, 2024
1 parent b4abf20 commit 200dfe4
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 52 deletions.
10 changes: 8 additions & 2 deletions htdocs/js/apidoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2275,7 +2275,13 @@ const APImethods = {
}
},
"MYMPD_API_WEBRADIODB_UPDATE": {
"desc": "Updates the full WebradioDB.",
"params": {}
"desc": "Updates the WebradioDB.",
"params": {
"force": {
"type": APItypes.bool,
"example": false,
"desc": "true = forces an update"
}
}
}
};
2 changes: 1 addition & 1 deletion src/compile_time.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ extern struct t_mympd_queue *mympd_api_queue;
#define JSONRPC_UINT_MIN 0
#define JSONRPC_UINT_MAX UINT_MAX
#define JSONRPC_STR_MAX 3000
#define JSONRPC_KEY_MAX 50
#define JSONRPC_KEY_MAX 500
#define JSONRPC_ARRAY_MAX 1000
#define JSONRPC_TIME_MIN 0 // Do 1. Jan 01:00:00 CET 1970
#define JSONRPC_TIME_MAX 253402297169 // Fr 31. Dez 23:59:29 CET 9999
Expand Down
1 change: 1 addition & 0 deletions src/lib/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ bool is_mympd_only_api_method(enum mympd_cmd_ids cmd_id) {
case MYMPD_API_SETTINGS_GET:
case MYMPD_API_CACHE_DISK_CLEAR:
case MYMPD_API_CACHE_DISK_CROP:
case MYMPD_API_WEBRADIODB_UPDATE:
return true;
default:
return false;
Expand Down
170 changes: 161 additions & 9 deletions src/lib/webradio.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
#include "compile_time.h"
#include "src/lib/webradio.h"

#include "dist/mpack/mpack.h"
#include "src/lib/filehandler.h"
#include "src/lib/log.h"
#include "src/lib/mem.h"
#include "src/lib/mpack.h"
#include "src/lib/sds_extras.h"

/**
Expand Down Expand Up @@ -73,11 +77,91 @@ void webradio_free(rax *webradios) {
* @return true on success, else false
*/
bool webradio_save_to_disk(struct t_config *config, rax *webradios, const char *filename) {
(void)config;
(void)webradios;
(void)filename;
//TODO: implement
return true;
if (webradios == NULL) {
MYMPD_LOG_DEBUG(NULL, "Webradio is NULL not saving anything");
return true;
}
MYMPD_LOG_INFO(NULL, "Saving webradios to disc (%s)", filename);
mpack_writer_t writer;
sds tmp_file = sdscatfmt(sdsempty(), "%S/%s/%s.XXXXXX", config->workdir, DIR_WORK_TAGS, filename);
FILE *fp = open_tmp_file(tmp_file);
if (fp == NULL) {
FREE_SDS(tmp_file);
return false;
}
// init mpack
mpack_writer_init_stdfile(&writer, fp, true);
mpack_writer_set_error_handler(&writer, log_mpack_write_error);
mpack_start_array(&writer, (uint32_t)webradios->numele);
raxIterator iter;
raxStart(&iter, webradios);
raxSeek(&iter, "^", NULL, 0);
struct t_list_node *current;
while (raxNext(&iter)) {
mpack_build_map(&writer);
struct t_webradio_data *data = (struct t_webradio_data *)iter.data;
mpack_write_cstr(&writer, "Key");
mpack_write_str(&writer, (char *)iter.key, (uint32_t)iter.key_len);
mpack_write_kv(&writer, "Name", data->name);
mpack_write_kv(&writer, "Image", data->image);
mpack_write_kv(&writer, "Homepage", data->homepage);
mpack_write_kv(&writer, "Country", data->country);
mpack_write_kv(&writer, "State", data->state);
mpack_write_kv(&writer, "Description", data->description);
mpack_write_cstr(&writer, "Genres");
mpack_build_array(&writer);
current = data->genres.head;
while (current != NULL) {
mpack_write_cstr(&writer, current->key);
current = current->next;
}
mpack_complete_array(&writer);
mpack_write_cstr(&writer, "Languages");
mpack_build_array(&writer);
current = data->languages.head;
while (current != NULL) {
mpack_write_cstr(&writer, current->key);
current = current->next;
}
mpack_complete_array(&writer);
mpack_write_cstr(&writer, "Streams");
mpack_build_array(&writer);
current = data->uris.head;
while (current != NULL) {
mpack_build_map(&writer);
mpack_write_kv(&writer, "Uri", current->key);
mpack_write_kv(&writer, "Codec", current->value_p);
mpack_write_kv(&writer, "Bitrate", current->value_i);
mpack_complete_map(&writer);
current = current->next;
}
mpack_complete_array(&writer);
mpack_complete_map(&writer);
}
raxStop(&iter);
mpack_finish_array(&writer);
// finish writing
bool rc = mpack_writer_destroy(&writer) != mpack_ok
? false
: true;
if (rc == false) {
rm_file(tmp_file);
MYMPD_LOG_ERROR("default", "An error occurred encoding the data");
FREE_SDS(tmp_file);
return false;
}
// rename tmp file
sds filepath = sdscatlen(sdsempty(), tmp_file, sdslen(tmp_file) - 7);
errno = 0;
if (rename(tmp_file, filepath) == -1) {
MYMPD_LOG_ERROR(NULL, "Rename file from \"%s\" to \"%s\" failed", tmp_file, filepath);
MYMPD_LOG_ERRNO(NULL, errno);
rm_file(tmp_file);
rc = false;
}
FREE_SDS(filepath);
FREE_SDS(tmp_file);
return rc;
}

/**
Expand All @@ -87,8 +171,76 @@ bool webradio_save_to_disk(struct t_config *config, rax *webradios, const char *
* @return newly allocated rax with webradios
*/
rax *webradio_read_from_disk(struct t_config *config, const char *filename) {
(void)config;
(void)filename;
//TODO: implement
return NULL;
sds filepath = sdscatfmt(sdsempty(), "%S/%s/%s", config->workdir, DIR_WORK_TAGS, filename);
if (testfile_read(filepath) == false) {
FREE_SDS(filepath);
return false;
}
rax *webradios = raxNew();

mpack_tree_t tree;
mpack_tree_init_filename(&tree, filepath, 0);
mpack_tree_set_error_handler(&tree, log_mpack_node_error);
FREE_SDS(filepath);
mpack_tree_parse(&tree);
mpack_node_t root = mpack_tree_root(&tree);
size_t len = mpack_node_array_length(root);
sds key = sdsempty();
sds uri = sdsempty();
sds codec = sdsempty();
for (size_t i = 0; i < len; i++) {
mpack_node_t entry = mpack_node_array_at(root, i);
struct t_webradio_data *data = webradio_data_new();
key = mpackstr_sdscat(key, entry, "Key");
data->name = mpackstr_sds(entry, "Name");
data->image = mpackstr_sds(entry, "Image");
data->homepage = mpackstr_sds(entry, "Homepage");
data->country = mpackstr_sds(entry, "Country");
data->state = mpackstr_sds(entry, "State");
data->description = mpackstr_sds(entry, "Description");
mpack_node_t genre_node = mpack_node_map_cstr(entry, "Genres");
size_t genre_len = mpack_node_array_length(genre_node);
for (size_t j = 0; j < genre_len; j++) {
mpack_node_t array_node = mpack_node_array_at(genre_node, j);
list_push_len(&data->languages, mpack_node_str(array_node), mpack_node_data_len(array_node),0, NULL, 0, NULL);
}
mpack_node_t lang_node = mpack_node_map_cstr(entry, "Languages");
size_t lang_len = mpack_node_array_length(lang_node);
for (size_t j = 0; j < lang_len; j++) {
mpack_node_t array_node = mpack_node_array_at(lang_node, j);
list_push_len(&data->languages, mpack_node_str(array_node), mpack_node_data_len(array_node),0, NULL, 0, NULL);
}
mpack_node_t streams_node = mpack_node_map_cstr(entry, "Streams");
size_t streams_len = mpack_node_array_length(streams_node);
for (size_t j = 0; j < streams_len; j++) {
mpack_node_t array_node = mpack_node_array_at(streams_node, j);
uri = mpackstr_sdscat(uri, array_node, "Uri");
codec = mpackstr_sdscat(codec, array_node, "Codec");
int64_t bitrate = mpack_node_int(mpack_node_map_cstr(array_node, "Bitrate"));
list_push(&data->uris, uri, bitrate, codec, NULL);
sdsclear(uri);
sdsclear(codec);
}
if (raxTryInsert(webradios, (unsigned char *)key, strlen(key), data, NULL) == 0) {
// insert error
MYMPD_LOG_ERROR(NULL, "Duplicate WebradioDB key found: %s", key);
webradio_data_free(data);
}
sdsclear(key);
}
FREE_SDS(key);
FREE_SDS(uri);
FREE_SDS(codec);
// clean up and check for errors
bool rc = mpack_tree_destroy(&tree) != mpack_ok
? false
: true;
if (rc == false) {
MYMPD_LOG_ERROR("default", "Reading webradios %s failed.", filename);
webradio_free(webradios);
return NULL;
}

MYMPD_LOG_INFO(NULL, "Read %" PRIu64 " webradios %s from disc", webradios->numele, filename);
return webradios;
}
16 changes: 9 additions & 7 deletions src/mpd_worker/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,14 +377,16 @@ void mpd_worker_api(struct t_mpd_worker_state *mpd_worker_state) {
}
break;
case MYMPD_API_WEBRADIODB_UPDATE:
response->data = jsonrpc_respond_message(response->data, request->cmd_id, request->id,
JSONRPC_FACILITY_PLAYLIST, JSONRPC_SEVERITY_INFO, "WebradioDB update started");
push_response(response);
rc = mpd_worker_webradiodb_update(mpd_worker_state);
if (rc == false) {
send_jsonrpc_notify(JSONRPC_FACILITY_GENERAL, JSONRPC_SEVERITY_ERROR, MPD_PARTITION_ALL, "WebradioDB update failed");
if (json_get_bool(request->data, "$.params.force", &bool_buf1, &parse_error) == true) {
response->data = jsonrpc_respond_message(response->data, request->cmd_id, request->id,
JSONRPC_FACILITY_PLAYLIST, JSONRPC_SEVERITY_INFO, "WebradioDB update started");
push_response(response);
rc = mpd_worker_webradiodb_update(mpd_worker_state, bool_buf1);
if (rc == false) {
send_jsonrpc_notify(JSONRPC_FACILITY_GENERAL, JSONRPC_SEVERITY_ERROR, MPD_PARTITION_ALL, "WebradioDB update failed");
}
async = true;
}
async = true;
break;
default:
response->data = jsonrpc_respond_message(response->data, request->cmd_id, request->id,
Expand Down
Loading

0 comments on commit 200dfe4

Please sign in to comment.