Implement updating of properties from webui

This commit is contained in:
Andreas Öman 2013-03-31 13:28:02 +02:00
parent f6ce21f747
commit 12a963e839
12 changed files with 454 additions and 176 deletions

View file

@ -65,6 +65,7 @@ static const idclass_t dvb_mux_class = {
.ic_class = "dvbmux",
.ic_get_title = dvb_mux_get_title,
.ic_get_childs = dvb_mux_get_childs,
.ic_save = (void *)dvb_mux_save,
.ic_properties = (const property_t[]){
{
"enabled", "Enabled", PT_BOOL,
@ -517,6 +518,7 @@ dvb_mux_save(dvb_mux_t *dm)
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_u32(m, "enabled", dm->dm_enabled);
htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&dm->dm_id));
htsmsg_add_u32(m, "transportstreamid", dm->dm_transport_stream_id);
htsmsg_add_u32(m, "originalnetworkid", dm->dm_network_id);
@ -727,14 +729,20 @@ dvb_mux_create_by_msg(dvb_network_t *dn, htsmsg_t *m, const char *fname)
if(htsmsg_get_u32(m, "enabled", &enabled))
enabled = 1;
const char *uuid = htsmsg_get_str(m, "uuid");
dm = dvb_mux_create(dn, &dmc,
onid, tsid, htsmsg_get_str(m, "network"), NULL, enabled,
htsmsg_get_u32_or_default(m, "needscan", 1),
htsmsg_get_str(m, "uuid"));
uuid);
if(dm != NULL) {
if((s = htsmsg_get_str(m, "default_authority")))
dm->dm_default_authority = strdup(s);
if(uuid == NULL)
// If mux didn't have UUID, write it to make sure UUID is stable
dvb_mux_save(dm);
}
return NULL;
}

View file

@ -25,13 +25,24 @@
#include "settings.h"
#include "dvb_support.h"
const static struct strtab typetab[] = {
{"DVB-T", FE_OFDM},
{"DVB-C", FE_QAM},
{"DVB-S", FE_QPSK},
{"ATSC", FE_ATSC},
};
struct dvb_network_list dvb_networks;
static idnode_t **dvb_network_get_childs(struct idnode *self);
static void dvb_network_save(idnode_t *in);
static const idclass_t dvb_network_class = {
.ic_class = "dvbnetwork",
.ic_get_childs = dvb_network_get_childs,
.ic_save = dvb_network_save,
.ic_properties = (const property_t[]){
{
"autodiscovery", "Auto discovery", PT_BOOL,
@ -42,11 +53,7 @@ static const idclass_t dvb_network_class = {
}, {
"disable_pmt_monitor", "Disable PMT monitor", PT_BOOL,
offsetof(dvb_network_t, dn_disable_pmt_monitor)
}, {
"disable_pmt_monitor", "Disable PMT monitor", PT_BOOL,
offsetof(dvb_network_t, dn_disable_pmt_monitor)
}, {
}},
}, {}},
};
/**
@ -116,15 +123,19 @@ dvb_network_get_childs(struct idnode *self)
static void
dvb_network_load(htsmsg_t *m, const char *uuid)
{
uint32_t fetype;
const char *s = htsmsg_get_str(m, "type");
if(s == NULL)
return;
if(htsmsg_get_u32(m, "fetype", &fetype))
int fetype = str2val(s, typetab);
if(fetype == -1)
return;
dvb_network_t *dn = dvb_network_create(fetype, uuid);
if(dn == NULL)
return;
htsmsg_delete_field(m, "type");
prop_write_values(dn, dvb_network_class.ic_properties, m);
dvb_mux_load(dn);
dvb_network_schedule_initial_scan(dn);
@ -135,16 +146,19 @@ dvb_network_load(htsmsg_t *m, const char *uuid)
*
*/
static void
dvb_network_save(dvb_network_t *dn)
dvb_network_save(idnode_t *in)
{
dvb_network_t *dn = (dvb_network_t *)in;
htsmsg_t *m = htsmsg_create_map();
lock_assert(&global_lock);
prop_read_values(dn, dvb_network_class.ic_properties, m);
htsmsg_add_str(m, "type", val2str(dn->dn_fe_type, typetab));
prop_read_values(in, dvb_network_class.ic_properties, m);
hts_settings_save(m, "dvb/networks/%s/config",
idnode_uuid_as_str(&dn->dn_id));
idnode_uuid_as_str(in));
htsmsg_destroy(m);
}
@ -185,11 +199,6 @@ dvb_network_init(void)
htsmsg_t *l, *c;
htsmsg_field_t *f;
if(0) {
dvb_network_save(dvb_network_create(FE_QAM, NULL));
exit(0);
}
if((l = hts_settings_load_r(1, "dvb/networks")) == NULL)
return;

View file

@ -175,6 +175,7 @@ dvb_service_save(service_t *t)
lock_assert(&global_lock);
htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&t->s_id));
htsmsg_add_u32(m, "service_id", t->s_dvb_service_id);
htsmsg_add_u32(m, "pmt", t->s_pmt_pid);
htsmsg_add_u32(m, "stype", t->s_servicetype);
@ -246,7 +247,8 @@ dvb_service_load(dvb_mux_t *dm)
if(htsmsg_get_u32(c, "pmt", &pmt))
continue;
t = dvb_service_find(dm, sid, pmt, f->hmf_name);
const char *uuid = htsmsg_get_str(c, "uuid");
t = dvb_service_find(dm, sid, pmt, uuid);
htsmsg_get_u32(c, "stype", &t->s_servicetype);
if(htsmsg_get_u32(c, "scrambled", &u32))
@ -285,6 +287,12 @@ dvb_service_load(dvb_mux_t *dm)
if(s && u32)
service_map_channel(t, channel_find_by_name(s, 1, 0), 0);
if(uuid == NULL) {
// If service config on disk lacked UUID (for whatever reason),
// write it back
dvb_service_save(t);
}
}
htsmsg_destroy(l);
}
@ -422,53 +430,6 @@ dvb_service_find2(dvb_mux_t *dm, uint16_t sid, int pmt_pid,
return t;
}
#if 0
/**
*
*/
static htsmsg_t *
dvb_service_serialize(service_t *s)
{
dvb_mux_t *dm = s->s_dvb_mux;
htsmsg_t *m = htsmsg_create_map();
char buf[100];
if(s->s_svcname) {
htsmsg_add_str(m, "text", s->s_svcname);
} else {
snprintf(buf, sizeof(buf), "Service-0x%04x",
s->s_dvb_service_id);
htsmsg_add_str(m, "text", buf);
}
htsmsg_add_u32(m, "enabled", s->s_enabled);
htsmsg_add_u32(m, "channel", s->s_channel_number);
htsmsg_add_u32(m, "sid", s->s_dvb_service_id);
htsmsg_add_u32(m, "pmt", s->s_pmt_pid);
htsmsg_add_u32(m, "pcr", s->s_pcr_pid);
htsmsg_add_str(m, "type", service_servicetype_txt(s));
htsmsg_add_str(m, "svcname", s->s_svcname ?: "");
htsmsg_add_str(m, "provider", s->s_provider ?: "");
htsmsg_add_str(m, "network", dm->dm_network_name ?: "");
htsmsg_add_str(m, "mux", dvb_mux_nicefreq(dm));
if(s->s_ch != NULL)
htsmsg_add_str(m, "channelname", s->s_ch->ch_name);
if(s->s_dvb_charset != NULL)
htsmsg_add_str(m, "dvb_charset", s->s_dvb_charset);
htsmsg_add_u32(m, "dvb_eit_enable", s->s_dvb_eit_enable);
return m;
}
#endif
/**
*

View file

@ -166,6 +166,27 @@ idnode_unlink(idnode_t *in)
}
/**
* Recursive to get superclass nodes first
*/
static void
add_descriptors(struct idnode *self, const idclass_t *ic, htsmsg_t *p)
{
if(ic->ic_super != NULL)
add_descriptors(self, ic->ic_super, p);
if(TAILQ_FIRST(&p->hm_fields) != NULL) {
// Only add separator if not empty
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "caption", ic->ic_caption ?: ic->ic_class);
htsmsg_add_str(m, "type", "separator");
htsmsg_add_msg(p, NULL, m);
}
prop_add_descriptors_to_msg(self, ic->ic_properties, p);
}
/**
*
*/
@ -179,24 +200,70 @@ idnode_serialize(struct idnode *self)
} else {
m = htsmsg_create_map();
htsmsg_t *p = htsmsg_create_map();
htsmsg_t *pn = htsmsg_create_map();
if(c->ic_get_title != NULL) {
htsmsg_add_str(m, "text", c->ic_get_title(self));
} else {
htsmsg_add_str(m, "text", idnode_uuid_as_str(self));
}
for(;c != NULL; c = c->ic_super) {
prop_read_values(self, c->ic_properties, p);
prop_read_names(c->ic_properties, pn);
}
htsmsg_t *p = htsmsg_create_list();
add_descriptors(self, c, p);
htsmsg_add_msg(m, "properties", p);
htsmsg_add_msg(m, "propertynames", pn);
htsmsg_add_msg(m, "descriptors", p);
htsmsg_add_str(m, "id", idnode_uuid_as_str(self));
}
return m;
}
/**
*
*/
static void
idnode_save(idnode_t *in)
{
const idclass_t *ic = in->in_class;
for(; ic != NULL; ic = ic->ic_super) {
if(ic->ic_save != NULL) {
ic->ic_save(in);
return;
}
}
}
/**
*
*/
void
idnode_set_prop(idnode_t *in, const char *key, const char *value)
{
const idclass_t *ic = in->in_class;
int do_save = 0;
for(;ic != NULL; ic = ic->ic_super) {
int x = prop_set(in, ic->ic_properties, key, value);
if(x == -1)
continue;
do_save |= x;
break;
}
if(do_save)
idnode_save(in);
}
/**
*
*/
void
idnode_update_all_props(idnode_t *in,
const char *(*getvalue)(void *opaque, const char *key),
void *opaque)
{
const idclass_t *ic = in->in_class;
int do_save = 0;
for(;ic != NULL; ic = ic->ic_super)
do_save |= prop_update_all(in, ic->ic_properties, getvalue, opaque);
if(do_save)
idnode_save(in);
}

View file

@ -9,9 +9,11 @@ struct idnode;
typedef struct idclass {
const struct idclass *ic_super;
const char *ic_class;
const char *ic_caption;
struct htsmsg *(*ic_serialize)(struct idnode *self);
struct idnode **(*ic_get_childs)(struct idnode *self);
const char *(*ic_get_title)(struct idnode *self);
void (*ic_save)(struct idnode *self);
const property_t *ic_properties;
} idclass_t;
@ -33,3 +35,10 @@ void *idnode_find(const char *uuid, const idclass_t *class);
void idnode_unlink(idnode_t *in);
htsmsg_t *idnode_serialize(struct idnode *self);
void idnode_set_prop(idnode_t *in, const char *key, const char *value);
void idnode_update_all_props(idnode_t *in,
const char *(*getvalue)(void *opaque,
const char *key),
void *opaque);

View file

@ -7,13 +7,33 @@
/**
*
*/
static int
str_to_bool(const char *s)
{
if(s == NULL)
return 0;
int v = atoi(s);
if(v)
return 1;
if(!strcasecmp(s, "on") ||
!strcasecmp(s, "true") ||
!strcasecmp(s, "yes"))
return 1;
return 0;
}
static const property_t *
prop_find(const property_t *p, const char *id)
{
int i;
int i = 0;
for(;p[i].id; i++)
if(!strcmp(id, p[i].id))
return p;
return p + i;
return NULL;
}
@ -63,68 +83,142 @@ prop_write_values(void *obj, const property_t *pl, htsmsg_t *m)
/**
*
*/
void
prop_read_values(void *obj, const property_t *p, htsmsg_t *m)
static void
prop_read_value(void *obj, const property_t *p, htsmsg_t *m, const char *name)
{
const char *s;
if(p == NULL)
return;
int i = 0;
for(;p[i].id; i++) {
void *val = obj + p[i].off;
switch(p[i].type) {
case PT_BOOL:
htsmsg_add_bool(m, p[i].id, *(int *)val);
break;
case PT_INT:
htsmsg_add_s32(m, p[i].id, *(int *)val);
break;
case PT_STR:
if(p->str_get != NULL)
s = p->str_get(obj);
else
s = *(const char **)val;
if(s != NULL)
htsmsg_add_str(m, p[i].id, s);
break;
}
const void *val = obj + p->off;
switch(p->type) {
case PT_BOOL:
htsmsg_add_bool(m, name, *(int *)val);
break;
case PT_INT:
htsmsg_add_s32(m, name, *(int *)val);
break;
case PT_STR:
if(p->str_get != NULL)
s = p->str_get(obj);
else
s = *(const char **)val;
if(s != NULL)
htsmsg_add_str(m, name, s);
break;
}
}
/**
*
*/
htsmsg_t *
prop_get_values(void *ptr, const property_t *p)
{
htsmsg_t *m = htsmsg_create_map();
prop_read_values(ptr, p, m);
return m;
}
/**
*
*/
void
prop_read_names(const property_t *p, htsmsg_t *m)
prop_read_values(void *obj, const property_t *p, htsmsg_t *m)
{
if(p == NULL)
return;
int i = 0;
for(;p[i].id; i++)
htsmsg_add_str(m, p[i].id, p[i].name);
prop_read_value(obj, p+i, m, p[i].id);
}
/**
*
*/
htsmsg_t *
prop_get_names(const property_t *p)
const static struct strtab typetab[] = {
{ "bool", PT_BOOL },
{ "int", PT_INT },
{ "str", PT_STR },
};
/**
*
*/
void
prop_add_descriptors_to_msg(void *obj, const property_t *p, htsmsg_t *msg)
{
htsmsg_t *m = htsmsg_create_map();
prop_read_names(p, m);
return m;
if(p == NULL)
return;
int i = 0;
for(;p[i].id; i++) {
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "id", p[i].id);
htsmsg_add_str(m, "caption", p[i].name);
htsmsg_add_str(m, "type", val2str(p[i].type, typetab) ?: "unknown");
prop_read_value(obj, p+i, m, "value");
htsmsg_add_msg(msg, NULL, m);
}
}
/**
* value can be NULL
*
* Return 1 if we actually changed something
*/
static int
prop_seti(void *obj, const property_t *p, const char *value)
{
int i32;
const char *s;
void *val = obj + p->off;
switch(p->type) {
case PT_BOOL:
i32 = str_to_bool(value);
if(0)
case PT_INT:
i32 = value ? atoi(value) : 0;
if(*(int *)val == i32)
return 0; // Already set
*(int *)val = i32;
break;
case PT_STR:
if(p->str_get != NULL)
s = p->str_get(obj);
else
s = *(const char **)val;
if(!strcmp(s ?: "", value ?: ""))
return 0;
if(p->str_set != NULL)
p->str_set(obj, value);
else
mystrset(val, value);
break;
}
return 1;
}
/**
* Return 1 if something changed
*/
int
prop_set(void *obj, const property_t *p, const char *key, const char *value)
{
if((p = prop_find(p, key)) == NULL)
return -1;
return prop_seti(obj, p, value);
}
/**
*
*/
int
prop_update_all(void *obj, const property_t *p,
const char *(*getvalue)(void *opaque, const char *key),
void *opaque)
{
int i = 0;
int r = 0;
for(; p[i].id; i++)
r |= prop_seti(obj, p+i, getvalue(opaque, p[i].id));
return r;
}

View file

@ -22,8 +22,14 @@ typedef struct property {
void prop_read_values(void *ptr, const property_t *p, htsmsg_t *m);
htsmsg_t *prop_get_values(void *ptr, const property_t *p);
void prop_read_names(const property_t *p, htsmsg_t *m);
htsmsg_t *prop_get_names(const property_t *p);
void prop_add_descriptors_to_msg(void *obj, const property_t *p, htsmsg_t *msg);
void prop_write_values(void *ptr, const property_t *pl, htsmsg_t *m);
void prop_read_values(void *obj, const property_t *p, htsmsg_t *m);
int prop_set(void *obj, const property_t *p, const char *key, const char *val);
int prop_update_all(void *obj, const property_t *p,
const char *(*getvalue)(void *opaque, const char *key),
void *opaque);

View file

@ -52,10 +52,12 @@
static void service_data_timeout(void *aux);
static const char *service_channel_get(void *obj);
static void service_channel_set(void *obj, const char *str);
static void service_save(struct idnode *self);
const idclass_t service_class = {
.ic_class = "service",
.ic_caption = "Service",
.ic_save = service_save,
.ic_properties = (const property_t[]){
{
"channel", "Channel", PT_STR,
@ -675,7 +677,7 @@ static const char *service_channel_get(void *obj)
static void
service_channel_set(void *obj, const char *str)
{
service_map_channel(obj, str ? channel_find_by_name(str, 1, 0) : NULL, 1);
service_map_channel(obj, str ? channel_find_by_name(str, 1, 0) : NULL, 0);
}
@ -931,6 +933,16 @@ service_request_save(service_t *t, int restart)
}
/**
*
*/
static void
service_save(struct idnode *self)
{
service_t *s = (service_t *)self;
s->s_config_save(s);
}
/**
*
*/

View file

@ -1900,6 +1900,103 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque)
return 0;
}
/**
*
*/
int
extjs_get_idnode(http_connection_t *hc, const char *remain, void *opaque,
idnode_t **(*rootfn)(void))
{
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_t **v;
if(!strcmp(s, "root")) {
v = rootfn();
} else {
idnode_t *n = idnode_find(s, NULL);
v = n != NULL && n->in_class->ic_get_childs != NULL ?
n->in_class->ic_get_childs(n) : NULL;
}
if(v != NULL) {
int i;
for(i = 0; v[i] != NULL; i++) {
htsmsg_t *m = idnode_serialize(v[i]);
if(v[i]->in_class->ic_get_childs == NULL)
htsmsg_add_u32(m, "leaf", 1);
htsmsg_add_msg(out, NULL, m);
}
}
pthread_mutex_unlock(&global_lock);
free(v);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
static const char *
get_prop_value(void *opaque, const char *key)
{
http_connection_t *hc = opaque;
return http_arg_get(&hc->hc_req_args, key);
}
/**
*
*/
static int
extjs_item_update(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hc->hc_reply;
htsmsg_t *out = NULL;
if(remain == NULL)
return HTTP_STATUS_BAD_REQUEST;
pthread_mutex_lock(&global_lock);
idnode_t *n = idnode_find(remain, NULL);
if(n == NULL) {
pthread_mutex_unlock(&global_lock);
return 404;
}
idnode_update_all_props(n, get_prop_value, hc);
pthread_mutex_unlock(&global_lock);
out = htsmsg_create_map();
htsmsg_add_u32(out, "success", 1);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
/**
* WEB user interface
*/
@ -1926,6 +2023,7 @@ extjs_start(void)
http_path_add("/iptv/services", NULL, extjs_iptvservices, ACCESS_ADMIN);
http_path_add("/servicedetails", NULL, extjs_servicedetails, ACCESS_ADMIN);
http_path_add("/tv/adapter", NULL, extjs_tvadapter, ACCESS_ADMIN);
http_path_add("/item/update", NULL, extjs_item_update, ACCESS_ADMIN);
#if ENABLE_LINUXDVB
extjs_start_dvb();

View file

@ -684,53 +684,10 @@ extjs_list_dvb_adapters(htsmsg_t *array)
static int
extjs_dvbnetworks(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_t **v;
if(!strcmp(s, "root")) {
v = dvb_network_root();
} else {
idnode_t *n = idnode_find(s, NULL);
v = n != NULL && n->in_class->ic_get_childs != NULL ?
n->in_class->ic_get_childs(n) : NULL;
}
if(v != NULL) {
int i;
for(i = 0; v[i] != NULL; i++) {
htsmsg_t *m = idnode_serialize(v[i]);
if(v[i]->in_class->ic_get_childs == NULL)
htsmsg_add_u32(m, "leaf", 1);
htsmsg_add_msg(out, NULL, m);
}
}
pthread_mutex_unlock(&global_lock);
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_get_idnode(hc, remain, opaque, &dvb_network_root);
}
/**
* DVB WEB user interface
*/

View file

@ -1,14 +1,66 @@
tvheadend.item_editor = function(item) {
var propsGrid = new Ext.grid.PropertyGrid({
flex:1,
padding: 5,
propertyNames: item.propertynames,
source: item.properties
var fields = []
console.log(fields);
for (var idx in item.descriptors) {
var f = item.descriptors[idx];
switch(f.type) {
case 'str':
fields.push({
fieldLabel: f.caption,
name: f.id,
value: f.value
});
break;
case 'bool':
fields.push({
xtype: 'checkbox',
fieldLabel: f.caption,
name: f.id,
checked: f.value
});
break;
case 'separator':
fields.push({
xtype: 'label',
fieldLabel: f.caption,
});
break;
}
}
var panel = new Ext.FormPanel({
labelWidth: 75, // label settings here cascade unless overridden
url:'save-form.php',
frame:true,
title: 'Parameters',
bodyStyle:'padding:5px 5px 0',
width: 350,
defaults: {width: 230},
defaultType: 'textfield',
items: fields,
buttons: [{
text: 'Save',
handler: function(){
if(panel.getForm().isValid()){
panel.getForm().submit({
url: 'item/update/' + item.id,
waitMsg : 'Saving Data...'
});
}
}
},{
text: 'Reset',
handler: function(){
panel.getForm().reset();
}
}]
});
return propsGrid;
return panel;
}

View file

@ -20,6 +20,8 @@
#define WEBUI_H_
#include "htsmsg.h"
#include "idnode.h"
#include "http.h"
void webui_init(void);
@ -45,6 +47,9 @@ void extjs_service_update(htsmsg_t *in);
void extjs_service_delete(htsmsg_t *in);
int extjs_get_idnode(http_connection_t *hc, const char *remain, void *opaque,
idnode_t **(*rootfn)(void));
/**
*
*/