From a1029481d9f060b6def74e601bd98f8188d03495 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 1 Dec 2013 14:52:07 +0000 Subject: [PATCH] servicemapper: work on getting feedback from service mapping --- src/api.h | 5 + src/api/api_service.c | 30 ++++- src/main.c | 1 + src/service_mapper.c | 86 ++++++++---- src/service_mapper.h | 11 +- src/webui/extjs.c | 1 + src/webui/static/app/chconf.js | 100 +------------- src/webui/static/app/mpegts.js | 10 +- src/webui/static/app/servicemapper.js | 185 ++++++++++++++++++++++++++ src/webui/static/app/status.js | 3 +- 10 files changed, 294 insertions(+), 138 deletions(-) create mode 100644 src/webui/static/app/servicemapper.js diff --git a/src/api.h b/src/api.h index 39e9df86..b0b6b03b 100644 --- a/src/api.h +++ b/src/api.h @@ -93,4 +93,9 @@ int api_idnode_tree int api_idnode_load_by_class ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ); +/* + * Service mapper + */ +void api_service_mapper_notify ( void ); + #endif /* __TVH_API_H__ */ diff --git a/src/api/api_service.c b/src/api/api_service.c index eca273e7..09182e84 100644 --- a/src/api/api_service.c +++ b/src/api/api_service.c @@ -25,6 +25,7 @@ #include "service_mapper.h" #include "access.h" #include "api.h" +#include "notify.h" static int api_mapper_start @@ -60,22 +61,37 @@ api_mapper_stop return 0; } +static htsmsg_t * +api_mapper_status_msg ( void ) +{ + htsmsg_t *m; + service_mapper_status_t stat = service_mapper_status(); + m = htsmsg_create_map(); + htsmsg_add_u32(m, "total", stat.total); + htsmsg_add_u32(m, "ok", stat.ok); + htsmsg_add_u32(m, "fail", stat.fail); + htsmsg_add_u32(m, "ignore", stat.ignore); + if (stat.active) + htsmsg_add_str(m, "active", idnode_uuid_as_str(&stat.active->s_id)); + return m; +} + static int api_mapper_status ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) { - int num; - pthread_mutex_lock(&global_lock); - num = service_mapper_qlen(); + *resp = api_mapper_status_msg(); pthread_mutex_unlock(&global_lock); - - *resp = htsmsg_create_map(); - htsmsg_add_u32(*resp, "remain", num); - return 0; } +void +api_service_mapper_notify ( void ) +{ + notify_by_msg("servicemapper", api_mapper_status_msg()); +} + void api_service_init ( void ) { extern const idclass_t service_class; diff --git a/src/main.c b/src/main.c index 07dd459d..1d088719 100644 --- a/src/main.c +++ b/src/main.c @@ -578,6 +578,7 @@ main(int argc, char **argv) tmp[strlen(tmp)-1] = '\0'; tvheadend_webroot = tmp; } + tvheadend_webui_debug = opt_uidebug; /* Setup logging */ if (isatty(2)) diff --git a/src/service_mapper.c b/src/service_mapper.c index 49f26ab7..3f007a9d 100644 --- a/src/service_mapper.c +++ b/src/service_mapper.c @@ -31,10 +31,12 @@ #include "streaming.h" #include "service.h" #include "plumbing/tsfix.h" +#include "api.h" -static pthread_cond_t service_mapper_cond; -static struct service_queue service_mapper_queue; -static service_mapper_conf_t service_mapper_conf; +static service_mapper_status_t service_mapper_stat; +static pthread_cond_t service_mapper_cond; +static struct service_queue service_mapper_queue; +static service_mapper_conf_t service_mapper_conf; static void service_mapper_process ( service_t *s ); static void *service_mapper_thread ( void *p ); @@ -52,16 +54,12 @@ service_mapper_init ( void ) } /* - * Get Q length + * Get status */ -int -service_mapper_qlen ( void ) +service_mapper_status_t +service_mapper_status ( void ) { - service_t *s; - int c = 0; - TAILQ_FOREACH(s, &service_mapper_queue, s_sm_link) - c++; - return c; + return service_mapper_stat; } /* @@ -73,9 +71,6 @@ service_mapper_start ( const service_mapper_conf_t *conf, htsmsg_t *uuids ) int e, tr, qd = 0; service_t *s; - /* Copy config */ - service_mapper_conf = *conf; - /* Check each service */ TAILQ_FOREACH(s, &service_all, s_all_link) { if (uuids) { @@ -88,12 +83,19 @@ service_mapper_start ( const service_mapper_conf_t *conf, htsmsg_t *uuids ) } if (!f) continue; } + tvhtrace("service_mapper", "check service %s (%s)", + s->s_nicename, idnode_uuid_as_str(&s->s_id)); + + /* Already mapped (or in progress) */ + if (s->s_sm_onqueue) continue; + if (LIST_FIRST(&s->s_channels)) continue; + tvhtrace("service_mapper", " not mapped"); + service_mapper_stat.total++; + service_mapper_stat.ignore++; /* Disabled */ if (!s->s_is_enabled(s)) continue; - - /* Already mapped */ - if (LIST_FIRST(&s->s_channels)) continue; + tvhtrace("service_mapper", " enabled"); /* Get service info */ pthread_mutex_lock(&s->s_stream_mutex); @@ -103,23 +105,28 @@ service_mapper_start ( const service_mapper_conf_t *conf, htsmsg_t *uuids ) /* Skip non-TV / Radio */ if (!tr) continue; + tvhtrace("service_mapper", " radio or tv"); /* Skip encrypted */ if (!conf->encrypted && e) continue; + service_mapper_stat.ignore--; /* Queue */ if (conf->check_availability) { - if (!s->s_sm_onqueue) { - qd = 1; - TAILQ_INSERT_TAIL(&service_mapper_queue, s, s_sm_link); - s->s_sm_onqueue = 1; - } + tvhtrace("service_mapper", " queue for checking"); + qd = 1; + TAILQ_INSERT_TAIL(&service_mapper_queue, s, s_sm_link); + s->s_sm_onqueue = 1; /* Process */ } else { + tvhtrace("service_mapper", " process"); service_mapper_process(s); } } + + /* Notify */ + api_service_mapper_notify(); /* Signal */ if (qd) pthread_cond_signal(&service_mapper_cond); @@ -132,8 +139,13 @@ void service_mapper_stop ( void ) { service_t *s; - while ((s = TAILQ_FIRST(&service_mapper_queue))) + while ((s = TAILQ_FIRST(&service_mapper_queue))) { + service_mapper_stat.total--; service_mapper_remove(s); + } + + /* Notify */ + api_service_mapper_notify(); } /* @@ -146,6 +158,9 @@ service_mapper_remove ( service_t *s ) TAILQ_REMOVE(&service_mapper_queue, s, s_sm_link); s->s_sm_onqueue = 0; } + + /* Notify */ + api_service_mapper_notify(); } /* @@ -203,12 +218,16 @@ service_mapper_process ( service_t *s ) const char *name; /* Ignore */ - if (s->s_status == SERVICE_ZOMBIE) + if (s->s_status == SERVICE_ZOMBIE) { + service_mapper_stat.ignore++; goto exit; + } /* Safety check (in-case something has been mapped manually in the interim) */ - if (LIST_FIRST(&s->s_channels)) + if (LIST_FIRST(&s->s_channels)) { + service_mapper_stat.ignore++; goto exit; + } /* Find existing channel */ name = service_get_channel_name(s); @@ -241,6 +260,9 @@ service_mapper_process ( service_t *s ) /* save */ channel_save(chn); } + service_mapper_stat.ok++; + + tvhinfo("service_mapper", "%s: success", s->s_nicename); /* Remove */ exit: @@ -282,7 +304,7 @@ service_mapper_thread ( void *aux ) } /* Subscribe */ - tvhinfo("service_mapper", "%s: checking availability", s->s_nicename); + tvhinfo("service_mapper", "checking %s", s->s_nicename); sub = subscription_create_from_service(s, SUBSCRIPTION_PRIO_MAPPER, "service_mapper", &sq.sq_st, 0, NULL, NULL, "service_mapper"); @@ -293,7 +315,10 @@ service_mapper_thread ( void *aux ) continue; } + tvhinfo("service_mapper", "waiting for input"); service_ref(s); + service_mapper_stat.active = s; + api_service_mapper_notify(); pthread_mutex_unlock(&global_lock); /* Wait */ @@ -319,7 +344,7 @@ service_mapper_thread ( void *aux ) } } else if (sm->sm_type == SMT_NOSTART) { run = 0; - err = "could not start"; + err = streaming_code2txt(sm->sm_code); } streaming_msg_free(sm); @@ -332,12 +357,15 @@ service_mapper_thread ( void *aux ) pthread_mutex_lock(&global_lock); subscription_unsubscribe(sub); - if(err) + if(err) { tvhinfo("service_mapper", "%s: failed [err %s]", s->s_nicename, err); - else + service_mapper_stat.fail++; + } else service_mapper_process(s); service_unref(s); + service_mapper_stat.active = NULL; + api_service_mapper_notify(); } return NULL; } diff --git a/src/service_mapper.h b/src/service_mapper.h index 3d4941f0..702bdd7a 100644 --- a/src/service_mapper.h +++ b/src/service_mapper.h @@ -27,6 +27,15 @@ typedef struct service_mapper_conf int provider_tags; ///< Create tags based on provider name } service_mapper_conf_t; +typedef struct service_mapper_status +{ + int total; + int ok; + int fail; + int ignore; + service_t *active; +} service_mapper_status_t; + void service_mapper_init ( void ); // Start new mapping @@ -40,7 +49,7 @@ void service_mapper_stop ( void ); void service_mapper_remove ( struct service *t ); // Get current Q size -int service_mapper_qlen ( void ); +service_mapper_status_t service_mapper_status ( void ); // Link service to channel int service_mapper_link ( struct service *s, struct channel *c ); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index f0fbb5ca..a99b4530 100755 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -166,6 +166,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) extjs_load(hq, "static/app/tvhlog.js"); extjs_load(hq, "static/app/status.js"); extjs_load(hq, "static/tv.js"); + extjs_load(hq, "static/app/servicemapper.js"); /** * Finally, the app itself diff --git a/src/webui/static/app/chconf.js b/src/webui/static/app/chconf.js index 092db637..482b085b 100644 --- a/src/webui/static/app/chconf.js +++ b/src/webui/static/app/chconf.js @@ -18,96 +18,6 @@ tvheadend.comet.on('channeltags', function(m) { if (m.reload != null) tvheadend.channelTags.reload(); }); -/* - * Service mapping - */ -tvheadend.mapServices = function(t, e, store, select) -{ - var panel = null; - var win = null; - - /* Form fields */ - var availCheck = new Ext.form.Checkbox({ - name : 'check_availability', - fieldLabel : 'Check availability', - checked : false - }); - var ftaCheck = new Ext.form.Checkbox({ - name : 'encrypted', - fieldLabel : 'Include encrypted services', - checked : false, - // TODO: make dependent on CSA config - }); - var mergeCheck = new Ext.form.Checkbox({ - name : 'merge_same_name', - fieldLabel : 'Merge same name', - checked : false - }); - var provtagCheck = new Ext.form.Checkbox({ - name : 'provider_tags', - fieldLabel : 'Create provider tags', - checked : false - }); - // TODO: provider list - items = [ availCheck, ftaCheck, mergeCheck, provtagCheck ]; - - /* Form */ - var undoBtn = new Ext.Button({ - text : 'Cancel', - handler : function () { - win.close(); - } - }); - - var saveBtn = new Ext.Button({ - text : 'Map', - tooltip : 'Begin mapping', - handler : function () { - p = null; - if (select) { - var r = select.getSelections(); - if (r.length > 0) { - var uuids = []; - for (var i = 0; i < r.length; i++) - uuids.push(r[i].id); - p = { uuids: Ext.encode(uuids) }; - } - } - panel.getForm().submit({ - url : 'api/service/mapper/start', - waitMessage : 'Mapping services...', - params : p - }); - } - }); - - panel = new Ext.FormPanel({ - frame : true, - border : true, - bodyStyle : 'padding: 5px', - labelAlign : 'left', - labelWidth : 200, - autoWidth : true, - autoHeight : true, - defaultType : 'textfield', - buttonAlign : 'left', - items : items, - buttons : [ undoBtn, saveBtn ] - }); - - /* Create window */ - win = new Ext.Window({ - title : 'Map services', - layout : 'fit', - autoWidth : true, - autoHeight : true, - plain : true, - items : panel - }); - - win.show(); -} - /** * Channels */ @@ -134,11 +44,11 @@ tvheadend.comet.on('channels', function(m) { tvheadend.channel_tab = function(panel) { var mapButton = new Ext.Toolbar.Button({ - tooltip : 'Map services to channels', - iconCls : '', - text : 'Map Services', - handler : tvheadend.mapServices, - disabled : false, + tooltip : 'Map services to channels', + iconCls : '', + text : 'Map Services', + handler : tvheadend.service_mapper, + disabled : false, }); tvheadend.idnode_grid(panel, { diff --git a/src/webui/static/app/mpegts.js b/src/webui/static/app/mpegts.js index 8f1fa4c5..968845ed 100644 --- a/src/webui/static/app/mpegts.js +++ b/src/webui/static/app/mpegts.js @@ -89,11 +89,11 @@ tvheadend.muxes = function(panel) tvheadend.services = function(panel) { var mapButton = new Ext.Toolbar.Button({ - tooltip : 'Map services to channels', - iconCls : 'clone', - text : 'Map All', - callback: tvheadend.mapServices, - disabled : false, + tooltip : 'Map services to channels', + iconCls : 'clone', + text : 'Map All', + callback : tvheadend.service_mapper, + disabled : false, }); var selected = function (s) { diff --git a/src/webui/static/app/servicemapper.js b/src/webui/static/app/servicemapper.js new file mode 100644 index 00000000..39e5575a --- /dev/null +++ b/src/webui/static/app/servicemapper.js @@ -0,0 +1,185 @@ +/* + * Status dialog + */ + +tvheadend.service_mapper_status_panel = null; + +tvheadend.service_mapper_status = function () +{ + var panel; + + /* Fields */ + var ok = new Ext.form.Label({ + fieldLabel : 'Mapped', + text : '0' + }); + var fail = new Ext.form.Label({ + fieldLabel : 'Failed', + text : '0' + }); + var ignore = new Ext.form.Label({ + fieldLabel : 'Ignored', + text : '0' + }); + var active = new Ext.form.Label({ + width : 200, + fieldLabel : 'Active', + text : '' + }); + var prog = new Ext.ProgressBar({ + text : '0 / 0' + }); + + /* Panel */ + panel = new Ext.FormPanel({ + method : 'get', + title : 'Service Mapper', + frame : true, + border : true, + bodyStyle : 'padding: 5px', + labelAlign : 'left', + labelWidth : 200, + width : 400, + autoHeight : true, + defaultType : 'textfield', + buttonAlign : 'left', + items : [ ok, ignore, fail, active, prog ] + }); + + /* Comet */ + tvheadend.comet.on('servicemapper', function(m) { + var n = m.ok + m.ignore + m.fail; + ok.setText('' + m.ok); + ignore.setText('' + m.ignore); + fail.setText('' + m.fail); + active.setText(''); + prog.updateProgress(n / m.total, '' + n + ' / ' + m.total); + + if (m.active) { + Ext.Ajax.request({ + url : 'api/idnode/load', + params : { + uuid : m.active + }, + success : function (d) { + d = Ext.util.JSON.decode(d.responseText); + try { + active.setText(d.entries[0].text); + } catch (e) { + } + } + }); + } + }); + + tvheadend.service_mapper_status_panel = panel; + return panel; +} + +/* + * Start mapping + */ +tvheadend.service_mapper = function(t, e, store, select) +{ + var panel = null; + var win = null; + + /* Form fields */ + var availCheck = new Ext.form.Checkbox({ + name : 'check_availability', + fieldLabel : 'Check availability', + checked : false + }); + var ftaCheck = new Ext.form.Checkbox({ + name : 'encrypted', + fieldLabel : 'Include encrypted services', + checked : false, + // TODO: make dependent on CSA config + }); + var mergeCheck = new Ext.form.Checkbox({ + name : 'merge_same_name', + fieldLabel : 'Merge same name', + checked : false + }); + var provtagCheck = new Ext.form.Checkbox({ + name : 'provider_tags', + fieldLabel : 'Create provider tags', + checked : false + }); + + // TODO: provider list + items = [ availCheck, ftaCheck, mergeCheck, provtagCheck ]; + + /* Form */ + var undoBtn = new Ext.Button({ + text : 'Cancel', + handler : function () { + win.close(); + } + }); + + var saveBtn = new Ext.Button({ + text : 'Map', + tooltip : 'Begin mapping', + handler : function () { + p = null; + if (select) { + var r = select.getSelections(); + if (r.length > 0) { + var uuids = []; + for (var i = 0; i < r.length; i++) + uuids.push(r[i].id); + p = { uuids: Ext.encode(uuids) }; + } + } + + + panel.getForm().submit({ + url : 'api/service/mapper/start', + waitMessage : 'Mapping services...', + params : p + }); + + win.hide(); + + /* Dialog */ + win = new Ext.Window({ + title : 'Service Mapper Status', + layout : 'fit', + autoWidth : true, + autoHeight : true, + plain : false, + items : tvheadend.service_mapper_status_panel + // TODO: buttons + }); + win.show(); + } + }); + + panel = new Ext.FormPanel({ + method : 'post', + frame : true, + border : true, + bodyStyle : 'padding: 5px', + labelAlign : 'left', + labelWidth : 200, + autoWidth : true, + autoHeight : true, + defaultType : 'textfield', + buttonAlign : 'left', + items : items, + buttons : [ undoBtn, saveBtn ] + }); + + /* Create window */ + win = new Ext.Window({ + title : 'Map services', + layout : 'fit', + autoWidth : true, + autoHeight : true, + plain : true, + items : panel + }); + + win.show(); +} diff --git a/src/webui/static/app/status.js b/src/webui/static/app/status.js index 773b711d..55a11ba2 100644 --- a/src/webui/static/app/status.js +++ b/src/webui/static/app/status.js @@ -356,7 +356,8 @@ tvheadend.status = function() { items : [ new tvheadend.status_streams, new tvheadend.status_subs, - new tvheadend.status_conns + new tvheadend.status_conns, + new tvheadend.service_mapper_status ] }); return panel;