From bfd8cffdc24dc7790c3a412d76818b95c2a0a273 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 10 Jul 2013 12:10:53 +0100 Subject: [PATCH] idnode: several improvements to the auto-updating features. This can now be disabled via tick-box at bottom of the grid and no data is actually sent in the update, just which nodes have been updated. There is still an inefficiency in that a bunch of nodes being updated could result in loads of reloads, but that could be improved with a bit of client side buffering/delay. --- src/idnode.c | 54 ++++++---- src/idnode.h | 1 + src/input/mpegts/mpegts_input.c | 2 +- src/input/mpegts/mpegts_mux.c | 1 + src/webui/extjs.c | 185 ++++++++++++++------------------ src/webui/static/app/idnode.js | 104 +++++++++--------- 6 files changed, 164 insertions(+), 183 deletions(-) diff --git a/src/idnode.c b/src/idnode.c index 995d0f7a..10be0a5d 100644 --- a/src/idnode.c +++ b/src/idnode.c @@ -290,6 +290,7 @@ idnode_get_u32 ptr = ((void*)self) + p->off; switch (p->type) { case PT_INT: + case PT_BOOL: *u32 = *(int*)ptr; return 0; case PT_U16: @@ -401,6 +402,7 @@ idnode_cmp_sort case PT_INT: case PT_U16: case PT_U32: + case PT_BOOL: { uint32_t u32a = 0, u32b = 0; idnode_get_u32(ina, sort->key, &u32a); @@ -412,7 +414,6 @@ idnode_cmp_sort } break; case PT_DBL: - case PT_BOOL: // TODO break; } @@ -577,18 +578,15 @@ int idnode_write0 ( idnode_t *self, htsmsg_t *c, int optmask, int dosave ) { int save = 0; + void (*savefn)(idnode_t*) = NULL; const idclass_t *idc = self->in_class; - for (; idc; idc = idc->ic_super) + for (; idc; idc = idc->ic_super) { save |= prop_write_values(self, idc->ic_properties, c, optmask, NULL); - if (save) { - if (dosave) { - for(; idc != NULL; idc = idc->ic_super) { - if(idc->ic_save != NULL) { - idc->ic_save(self); - break; - } - } - } + if (!savefn && idc->ic_save) + savefn = idc->ic_save; + } + if (save && dosave) { + if (savefn) savefn(self); idnode_notify(self, NULL, 0); } return save; @@ -721,17 +719,20 @@ void idnode_notify (idnode_t *in, const char *chn, int force) { + const char *uuid = idnode_uuid_as_str(in); + /* Forced */ if (chn || force) { - htsmsg_t *m = idnode_serialize0(in, 0); - notify_by_msg(chn ?: "idnodeParamsChanged", m); + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_str(m, "uuid", uuid); + notify_by_msg(chn ?: "idnodeUpdated", m); /* Rate-limited */ } else { pthread_mutex_lock(&idnode_mutex); if (!idnode_queue) idnode_queue = htsmsg_create_map(); - htsmsg_set_u32(idnode_queue, idnode_uuid_as_str(in), 1); + htsmsg_set_u32(idnode_queue, uuid, 1); pthread_cond_signal(&idnode_cond); pthread_mutex_unlock(&idnode_mutex); } @@ -743,6 +744,15 @@ idnode_notify_simple (void *in) idnode_notify(in, NULL, 0); } +void +idnode_notify_title_changed (void *in) +{ + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_str(m, "uuid", idnode_uuid_as_str(in)); + htsmsg_add_str(m, "text", idnode_get_title(in)); + notify_by_msg("idnodeUpdated", m); +} + /* * Thread for handling notifications */ @@ -771,20 +781,20 @@ idnode_thread ( void *p ) HTSMSG_FOREACH(f, q) { node = idnode_find(f->hmf_name, NULL); - if (node) { - m = idnode_serialize0(node, 0); - if (m) - notify_by_msg("idnodeUpdated", m); - } else { - m = htsmsg_create_map(); - htsmsg_add_str(m, "uuid", f->hmf_name); + m = htsmsg_create_map(); + htsmsg_add_str(m, "uuid", f->hmf_name); + if (node) + notify_by_msg("idnodeUpdated", m); + else notify_by_msg("idnodeDeleted", m); - } } /* Finished */ pthread_mutex_unlock(&global_lock); htsmsg_destroy(q); + + /* Wait */ + usleep(500000); pthread_mutex_lock(&idnode_mutex); } diff --git a/src/idnode.h b/src/idnode.h index 6197ee6e..27df9925 100644 --- a/src/idnode.h +++ b/src/idnode.h @@ -120,6 +120,7 @@ idnode_set_t *idnode_find_all(const idclass_t *idc); void idnode_notify (idnode_t *in, const char *chn, int force); void idnode_notify_simple (void *in); +void idnode_notify_title_changed (void *in); htsmsg_t *idclass_serialize0 (const idclass_t *idc, int optmask); htsmsg_t *idnode_serialize0 (idnode_t *self, int optmask); diff --git a/src/input/mpegts/mpegts_input.c b/src/input/mpegts/mpegts_input.c index a575a231..7054bf7a 100644 --- a/src/input/mpegts/mpegts_input.c +++ b/src/input/mpegts/mpegts_input.c @@ -54,7 +54,7 @@ const idclass_t mpegts_input_class = .id = "displayname", .name = "Name", .off = offsetof(mpegts_input_t, mi_displayname), - .notify = idnode_notify_simple, + .notify = idnode_notify_title_changed, }, {} } diff --git a/src/input/mpegts/mpegts_mux.c b/src/input/mpegts/mpegts_mux.c index 0758619e..a2e73435 100644 --- a/src/input/mpegts/mpegts_mux.c +++ b/src/input/mpegts/mpegts_mux.c @@ -452,6 +452,7 @@ mpegts_mux_initial_scan_done ( mpegts_mux_t *mm ) /* Save */ mm->mm_initial_scan_done = 1; mm->mm_config_save(mm); + idnode_updated(&mm->mm_id); } /* ************************************************************************** diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 42c3ad3a..8baa5452 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -2170,90 +2170,109 @@ extjs_tvhlog(http_connection_t *hc, const char *remain, void *opaque) } static int -extjs_idnode - (http_connection_t *hc, const char *remain, void *opaque) +extjs_idnode_tree + ( http_connection_t *hc, const char *uuid, const char *root, + idnode_set_t *(*rootfn)(void), htsmsg_t **out ) { - htsbuf_queue_t *hq = &hc->hc_reply; - int isroot = 0; - htsmsg_t *out = NULL; + int isroot; idnode_t *node = NULL; - const char *uuid = http_arg_get(&hc->hc_req_args, "uuid"); - const char *op = http_arg_get(&hc->hc_req_args, "op"); -#if 0 - const char *root = http_arg_get(&hc->hc_req_args, "root"); - if (uuid == NULL) - uuid = http_arg_get(&hc->hc_req_args, "node"); - if (!strcmp(uuid, "root")) { - isroot = 1; - uuid = root; - } - if (op == NULL) - op = "get"; - if(uuid == NULL) + /* Validate */ + if (!uuid) + return HTTP_STATUS_BAD_REQUEST; + isroot = !strcmp("root", uuid); + if (isroot && !(root || rootfn)) return HTTP_STATUS_BAD_REQUEST; -#endif pthread_mutex_lock(&global_lock); - if(http_access_verify(hc, ACCESS_ADMIN)) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_UNAUTHORIZED; + if (!isroot || root) { + if (!(node = idnode_find(isroot ? root : uuid, NULL))) { + pthread_mutex_unlock(&global_lock); + return HTTP_STATUS_BAD_REQUEST; + } } -#if 0 - node = idnode_find(uuid, NULL); -#endif + *out = htsmsg_create_list(); - if (!strcmp(op, "get")) { - out = htsmsg_create_list(); + /* Root node */ + if (isroot && node) { htsmsg_t *m = idnode_serialize(node); htsmsg_add_u32(m, "leaf", idnode_is_leaf(node)); - htsmsg_add_msg(out, NULL, m); + htsmsg_add_msg(*out, NULL, m); + + /* Children */ + } else { + idnode_set_t *v = node ? idnode_get_childs(node) : rootfn(); + if (v) { + int i; + for(i = 0; i < v->is_count; i++) { + htsmsg_t *m = idnode_serialize(v->is_array[i]); + htsmsg_add_u32(m, "leaf", idnode_is_leaf(v->is_array[i])); + htsmsg_add_msg(*out, NULL, m); + } + idnode_set_free(v); + } + } + pthread_mutex_unlock(&global_lock); + + return 0; +} + +static int +extjs_idnode0 + (http_connection_t *hc, const char *remain, void *opaque, + idnode_set_t *(*rootfn)(void)) +{ + htsbuf_queue_t *hq = &hc->hc_reply; + htsmsg_t *out = NULL; + idnode_t *node = NULL; + const char *uuid, *root, *op = http_arg_get(&hc->hc_req_args, "op"); + + if (!op) return HTTP_STATUS_BAD_REQUEST; + + /* Get details */ + if (!strcmp(op, "get")) { + if (!(uuid = http_arg_get(&hc->hc_req_args, "uuid"))) + return HTTP_STATUS_BAD_REQUEST; + pthread_mutex_lock(&global_lock); + if (!(node = idnode_find(uuid, NULL))) { + pthread_mutex_unlock(&global_lock); + return HTTP_STATUS_BAD_REQUEST; + } + out = htsmsg_create_map(); + htsmsg_t *m = idnode_serialize(node); + htsmsg_add_u32(m, "leaf", idnode_is_leaf(node)); + htsmsg_add_msg(out, "nodes", m); + + /* Update */ } else if (!strcmp(op, "save")) { const char *s; - htsmsg_t *conf; + htsmsg_field_t *f; + htsmsg_t *conf, *nodes; if ((s = http_arg_get(&hc->hc_req_args, "nodes"))) { -printf("s = %s\n", s); - htsmsg_t *nodes = htsmsg_json_deserialize(s); - htsmsg_field_t *f; - if (nodes) { + if ((nodes = htsmsg_json_deserialize(s))) { + pthread_mutex_lock(&global_lock); HTSMSG_FOREACH(f, nodes) { if (!(conf = htsmsg_get_map_by_field(f))) continue; if (!(uuid = htsmsg_get_str(conf, "uuid"))) continue; if (!(node = idnode_find(uuid, NULL))) continue; idnode_update(node, conf); } + pthread_mutex_unlock(&global_lock); htsmsg_destroy(nodes); } - } else if ((s = http_arg_get(&hc->hc_req_args, "conf"))) { - if ((conf = htsmsg_json_deserialize(s))) { - idnode_update(node, conf); - htsmsg_destroy(conf); - } } out = htsmsg_create_map(); - } else if (!strcmp(op, "childs")) { - out = htsmsg_create_list(); - if (isroot) { - htsmsg_t *m = idnode_serialize(node); - htsmsg_add_u32(m, "leaf", idnode_is_leaf(node)); - htsmsg_add_msg(out, NULL, m); - } else { - idnode_set_t *v; - if ((v = idnode_get_childs(node))) { - int i; - for(i = 0; i < v->is_count; i++) { - htsmsg_t *m = idnode_serialize(v->is_array[i]); - htsmsg_add_u32(m, "leaf", idnode_is_leaf(v->is_array[i])); - htsmsg_add_msg(out, NULL, m); - } - idnode_set_free(v); - } - } - } - pthread_mutex_unlock(&global_lock); + /* Children */ + } else if (!strcmp(op, "childs")) { + int e; + uuid = http_arg_get(&hc->hc_req_args, "uuid"); + root = http_arg_get(&hc->hc_req_args, "root"); + if ((e = extjs_idnode_tree(hc, uuid, root, rootfn, &out))) + return e; + } if (!out) return HTTP_STATUS_BAD_REQUEST; @@ -2264,53 +2283,11 @@ printf("s = %s\n", s); return 0; } -/** - * - */ static int -extjs_get_idnode(http_connection_t *hc, const char *remain, void *opaque, - idnode_set_t *(*rootfn)(void)) +extjs_idnode + (http_connection_t *hc, const char *remain, void *opaque) { - htsbuf_queue_t *hq = &hc->hc_reply; - const char *s = http_arg_get(&hc->hc_req_args, "node"); - htsmsg_t *out = NULL; - - if(s == NULL) - return HTTP_STATUS_BAD_REQUEST; - - pthread_mutex_lock(&global_lock); - - if(http_access_verify(hc, ACCESS_ADMIN)) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_UNAUTHORIZED; - } - - out = htsmsg_create_list(); - idnode_set_t *v; - - if(!strcmp(s, "root")) { - v = rootfn(); - } else { - v = idnode_get_childs(idnode_find(s, NULL)); - } - - if(v != NULL) { - int i; - for(i = 0; i < v->is_count; i++) { - htsmsg_t *m = idnode_serialize(v->is_array[i]); - htsmsg_add_u32(m, "leaf", idnode_is_leaf(v->is_array[i])); - htsmsg_add_msg(out, NULL, m); - } - } - - pthread_mutex_unlock(&global_lock); - - if (v) idnode_set_free(v); - - htsmsg_json_serialize(out, hq, 0); - htsmsg_destroy(out); - http_output_content(hc, "text/x-json; charset=UTF-8"); - return 0; + return extjs_idnode0(hc, remain, opaque, NULL); } /** @@ -2319,7 +2296,7 @@ extjs_get_idnode(http_connection_t *hc, const char *remain, void *opaque, static int extjs_tvadapters(http_connection_t *hc, const char *remain, void *opaque) { - return extjs_get_idnode(hc, remain, opaque, &linuxdvb_root); + return extjs_idnode0(hc, remain, opaque, &linuxdvb_root); } diff --git a/src/webui/static/app/idnode.js b/src/webui/static/app/idnode.js index 46d3fb05..6615ee8a 100644 --- a/src/webui/static/app/idnode.js +++ b/src/webui/static/app/idnode.js @@ -152,16 +152,17 @@ tvheadend.idnode_editor = function(item, conf) /* Buttons */ var saveBtn = new Ext.Button({ - text : 'Save', + text : 'Save', handler : function() { + var node = panel.getForm().getFieldValues(); + node.uuid = item.uuid; var params = { - uuid: item.uuid, - op : 'save', - conf: Ext.util.JSON.encode(panel.getForm().getFieldValues()) + op : 'save', + nodes : Ext.util.JSON.encode([node]) }; Ext.Ajax.request({ - url : 'api/idnode', - params : params, + url : 'api/idnode', + params : params, success : function(d) { } }); @@ -177,7 +178,7 @@ tvheadend.idnode_editor = function(item, conf) labelWidth : 200, autoWidth : true, autoHeight : !conf.fixedHeight, - width : 600, + width : 600, //defaults: {width: 330}, defaultType : 'textfield', buttonAlign : 'left', @@ -564,6 +565,14 @@ tvheadend.idnode_grid = function(panel, conf) } /* Grid Panel */ + var auto = new Ext.form.Checkbox({ + checked : true, + listeners : { + check : function ( s, c ) { + if (c) store.reload(); + } + } + }); var grid = new Ext.grid.EditorGridPanel({ stripeRows : true, title : conf.titleP, @@ -582,35 +591,21 @@ tvheadend.idnode_grid = function(panel, conf) pageSize : 50, displayInfo : true, displayMsg : conf.titleP + ' {0} - {1} of {2}', - emptyMsg : 'No ' + conf.titleP.toLowerCase() + ' to display' + emptyMsg : 'No ' + conf.titleP.toLowerCase() + ' to display', + items : [ '-', 'Auto-refresh', auto ] }) }); panel.add(grid); /* Add comet listeners */ - if (conf.comet) { - tvheadend.comet.on(conf.comet, function(o) { - var fs = []; - var d = {}; - for ( i = 0; i < o.params.length; i++) - if (o.params[i].id) { - fs.push(o.params[i].id); - d[o.params[i].id] = o.params[i].value; - } - var rec = Ext.data.Record.create(fs); - rec = new rec(d, o.id); - store.add(rec); - }); - } - tvheadend.comet.on('idnodeParamsChanged', function(o) { - var r = store.getById(o.id); - if (r) { - for ( i = 0; i < o.params.length; i++) - if (o.params[i].id) - r.set(o.params[i].id, o.params[i].value); - r.commit(); - } - }); + var update = function(o) { + if (auto.getValue()) + store.reload(); + }; + if (conf.comet) + tvheadend.comet.on(conf.comet, update); + tvheadend.comet.on('idnodeUpdated', update); + tvheadend.comet.on('idnodeDeleted', update); } /* Request data */ @@ -633,20 +628,22 @@ tvheadend.idnode_grid = function(panel, conf) tvheadend.idnode_tree = function (conf) { var current = null; - + var params = conf.params || {}; + params.op = 'childs'; var loader = new Ext.tree.TreeLoader({ - dataUrl : conf.url, - baseParams : conf.params, + dataUrl : conf.url, + baseParams : params, preloadChildren : conf.preload, + nodeParameter : 'uuid' }); var tree = new Ext.tree.TreePanel({ - loader : loader, - flex : 1, - border : false, - root : new Ext.tree.AsyncTreeNode({ - id : conf.root || 'root', - text : conf.title || '' + loader : loader, + flex : 1, + border : false, + root : new Ext.tree.AsyncTreeNode({ + id : conf.root || 'root', + text : conf.title || '' }), listeners : { click: function(n) { @@ -662,27 +659,22 @@ tvheadend.idnode_tree = function (conf) } }); - tvheadend.comet.on('idnodeNameChanged', function(o) { - var n = tree.getNodeById(o.id); - if(n) { - n.setText(o.text); + // TODO: top-level reload + tvheadend.comet.on('idnodeUpdated', function(o) { + var n = tree.getNodeById(o.uuid); + if (n) { + if (o.text) n.setText(o.text); + loader.load(n); } }); - tvheadend.comet.on('idnodeParamsChanged', function(o) { - var n = tree.getNodeById(o.id); - if(n) { - n.attributes.params = o.params; - } - }); - var panel = new Ext.Panel({ - title : conf.title || '', - layout : 'hbox', - flex : 1, - padding : 5, - border : false, + title : conf.title || '', + layout : 'hbox', + flex : 1, + padding : 5, + border : false, layoutConfig : { align : 'stretch' },