servicemapper: work on getting feedback from service mapping

This commit is contained in:
Adam Sutton 2013-12-01 14:52:07 +00:00
parent c7d0335eb1
commit a1029481d9
10 changed files with 294 additions and 138 deletions

View file

@ -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__ */

View file

@ -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;

View file

@ -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))

View file

@ -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;
}

View file

@ -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 );

View file

@ -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

View file

@ -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, {

View file

@ -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)
{

View file

@ -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();
}

View file

@ -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;