servicemapper: work on getting feedback from service mapping
This commit is contained in:
parent
c7d0335eb1
commit
a1029481d9
10 changed files with 294 additions and 138 deletions
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
185
src/webui/static/app/servicemapper.js
Normal file
185
src/webui/static/app/servicemapper.js
Normal 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();
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue