From: Jaroslav Kysela Date: Wed, 9 Dec 2015 12:20:31 +0000 (+0100) Subject: service mapper: move to simple idnode, add 'create type based tags', fixes #3363 X-Git-Tag: v4.2.1~1337 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=def7c48151e1542242d46db037dc77b846ffc34b;p=thirdparty%2Ftvheadend.git service mapper: move to simple idnode, add 'create type based tags', fixes #3363 --- diff --git a/src/api/api_service.c b/src/api/api_service.c index 9270d975a..d5d6c709f 100644 --- a/src/api/api_service.c +++ b/src/api/api_service.c @@ -27,30 +27,6 @@ #include "api.h" #include "notify.h" -static int -api_mapper_start - ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) -{ - service_mapper_conf_t conf = { 0 }; - htsmsg_t *uuids; -#define get_u32(x)\ - conf.x = htsmsg_get_bool_or_default(args, #x, 0) - - /* Get config */ - uuids = htsmsg_get_list(args, "uuids"); - get_u32(check_availability); - get_u32(encrypted); - get_u32(merge_same_name); - get_u32(provider_tags); - get_u32(network_tags); - - pthread_mutex_lock(&global_lock); - service_mapper_start(&conf, uuids); - pthread_mutex_unlock(&global_lock); - - return 0; -} - static int api_mapper_stop ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) @@ -188,11 +164,11 @@ void api_service_init ( void ) { extern const idclass_t service_class; static api_hook_t ah[] = { - { "service/mapper/start", ACCESS_ADMIN, api_mapper_start, NULL }, + { "service/mapper/load", ACCESS_ADMIN, api_idnode_load_simple, &service_mapper_conf }, + { "service/mapper/save", ACCESS_ADMIN, api_idnode_save_simple, &service_mapper_conf }, { "service/mapper/stop", ACCESS_ADMIN, api_mapper_stop, NULL }, { "service/mapper/status", ACCESS_ADMIN, api_mapper_status, NULL }, - { "service/list", ACCESS_ADMIN, api_idnode_load_by_class, - (void*)&service_class }, + { "service/list", ACCESS_ADMIN, api_idnode_load_by_class, (void*)&service_class }, { "service/streams", ACCESS_ADMIN, api_service_streams, NULL }, { NULL }, }; diff --git a/src/prop.c b/src/prop.c index c01b934de..d17891446 100644 --- a/src/prop.c +++ b/src/prop.c @@ -271,7 +271,8 @@ prop_read_value /* List */ if (p->islist) { assert(p->get); /* requirement */ - htsmsg_add_msg(m, name, (htsmsg_t*)val); + if (val) + htsmsg_add_msg(m, name, (htsmsg_t*)val); /* Single */ } else { diff --git a/src/service_mapper.c b/src/service_mapper.c index 07c8c4a2b..40bdf5289 100644 --- a/src/service_mapper.c +++ b/src/service_mapper.c @@ -43,29 +43,10 @@ typedef struct service_mapper_item { static service_mapper_status_t service_mapper_stat; static pthread_cond_t service_mapper_cond; static TAILQ_HEAD(, service_mapper_item) service_mapper_queue; +service_mapper_t service_mapper_conf; static void *service_mapper_thread ( void *p ); -/** - * Initialise - */ -pthread_t service_mapper_tid; - -void -service_mapper_init ( void ) -{ - TAILQ_INIT(&service_mapper_queue); - pthread_cond_init(&service_mapper_cond, NULL); - tvhthread_create(&service_mapper_tid, NULL, service_mapper_thread, NULL, "svcmap"); -} - -void -service_mapper_done ( void ) -{ - pthread_cond_signal(&service_mapper_cond); - pthread_join(service_mapper_tid, NULL); -} - /* * Get status */ @@ -78,7 +59,7 @@ service_mapper_status ( void ) /* * Start a new mapping */ -void +static void service_mapper_start ( const service_mapper_conf_t *conf, htsmsg_t *uuids ) { int e, tr, qd = 0; @@ -256,14 +237,16 @@ service_mapper_process service_mapper_link(s, chn, chn); /* Type tags */ - if (service_is_hdtv(s)) { - channel_tag_map(channel_tag_find_by_name("TV channels", 1), chn, chn); - channel_tag_map(channel_tag_find_by_name("HDTV", 1), chn, chn); - } else if (service_is_sdtv(s)) { - channel_tag_map(channel_tag_find_by_name("TV channels", 1), chn, chn); - channel_tag_map(channel_tag_find_by_name("SDTV", 1), chn, chn); - } else if (service_is_radio(s)) { - channel_tag_map(channel_tag_find_by_name("Radio", 1), chn, chn); + if (conf->type_tags) { + if (service_is_hdtv(s)) { + channel_tag_map(channel_tag_find_by_name("TV channels", 1), chn, chn); + channel_tag_map(channel_tag_find_by_name("HDTV", 1), chn, chn); + } else if (service_is_sdtv(s)) { + channel_tag_map(channel_tag_find_by_name("TV channels", 1), chn, chn); + channel_tag_map(channel_tag_find_by_name("SDTV", 1), chn, chn); + } else if (service_is_radio(s)) { + channel_tag_map(channel_tag_find_by_name("Radio", 1), chn, chn); + } } /* Custom tags */ @@ -434,3 +417,158 @@ service_mapper_reset_stats (void) service_mapper_stat.fail = 0; service_mapper_stat.active = NULL; } + +/* + * Save settings + */ +static void service_mapper_conf_class_save ( idnode_t *self ) +{ + htsmsg_t *m; + + m = htsmsg_create_map(); + idnode_save(&service_mapper_conf.idnode, m); + hts_settings_save(m, "service_mapper/config"); + htsmsg_destroy(m); + + if (!htsmsg_is_empty(service_mapper_conf.services)) + service_mapper_start(&service_mapper_conf.d, service_mapper_conf.services); + htsmsg_destroy(service_mapper_conf.services); + service_mapper_conf.services = NULL; +} + +/* + * Class + */ + +static const void * +service_mapper_services_get ( void *obj ) +{ + return NULL; +} + +static char * +service_mapper_services_rend ( void *obj, const char *lang ) +{ + return strdup(""); +} + +static int +service_mapper_services_set ( void *obj, const void *p ) +{ + service_mapper_t *sm = obj; + htsmsg_destroy(sm->services); + sm->services = htsmsg_copy((htsmsg_t *)p); + return 1; +} + +static htsmsg_t * +service_mapper_services_enum ( void *obj, const char *lang ) +{ + htsmsg_t *e, *m = htsmsg_create_map(); + htsmsg_add_str(m, "type", "api"); + htsmsg_add_str(m, "uri", "service/list"); + htsmsg_add_str(m, "event", "service"); + e = htsmsg_create_map(); + htsmsg_add_bool(e, "enum", 1); + htsmsg_add_msg(m, "params", e); + return m; +} + +static const idclass_t service_mapper_conf_class = { + .ic_snode = &service_mapper_conf.idnode, + .ic_class = "service_mapper", + .ic_caption = N_("Service mapper"), + .ic_event = "service_mapper", + .ic_perm_def = ACCESS_ADMIN, + .ic_save = service_mapper_conf_class_save, + .ic_properties = (const property_t[]){ + { + .type = PT_STR, + .islist = 1, + .id = "services", + .name = N_("Services"), + .get = service_mapper_services_get, + .set = service_mapper_services_set, + .list = service_mapper_services_enum, + .rend = service_mapper_services_rend + }, + { + .type = PT_BOOL, + .id = "check_availability", + .name = N_("Check availability"), + .off = offsetof(service_mapper_t, d.check_availability), + .opts = PO_ADVANCED + }, + { + .type = PT_BOOL, + .id = "encrypted", + .name = N_("Map encrypted services"), + .off = offsetof(service_mapper_t, d.encrypted), + }, + { + .type = PT_BOOL, + .id = "merge_same_name", + .name = N_("Merge same name"), + .off = offsetof(service_mapper_t, d.merge_same_name), + }, + { + .type = PT_BOOL, + .id = "type_tags", + .name = N_("Create type based tags"), + .desc = N_("Create SDTV/HDTV/Radio tags"), + .off = offsetof(service_mapper_t, d.type_tags), + .opts = PO_ADVANCED + }, + { + .type = PT_BOOL, + .id = "provider_tags", + .name = N_("Create provider name tags"), + .off = offsetof(service_mapper_t, d.provider_tags), + .opts = PO_ADVANCED + }, + { + .type = PT_BOOL, + .id = "network_tags", + .name = N_("Create network name tags"), + .off = offsetof(service_mapper_t, d.network_tags), + .opts = PO_ADVANCED + }, + {} + } +}; + +/* + * + */ + +pthread_t service_mapper_tid; + +void service_mapper_init ( void ) +{ + htsmsg_t *m; + + TAILQ_INIT(&service_mapper_queue); + pthread_cond_init(&service_mapper_cond, NULL); + tvhthread_create(&service_mapper_tid, NULL, service_mapper_thread, NULL, "svcmap"); + + /* Defaults */ + memset(&service_mapper_conf, 0, sizeof(service_mapper_conf)); + service_mapper_conf.idnode.in_class = &service_mapper_conf_class; + service_mapper_conf.d.type_tags = 1; + service_mapper_conf.d.encrypted = 1; + + /* Load settings */ + if ((m = hts_settings_load("service_mapper/config"))) { + idnode_load(&service_mapper_conf.idnode, m); + htsmsg_destroy(m); + } +} + + +void service_mapper_done ( void ) +{ + pthread_cond_signal(&service_mapper_cond); + pthread_join(service_mapper_tid, NULL); + htsmsg_destroy(service_mapper_conf.services); + service_mapper_conf.services = NULL; +} diff --git a/src/service_mapper.h b/src/service_mapper.h index 199dd6094..c3f3c3d17 100644 --- a/src/service_mapper.h +++ b/src/service_mapper.h @@ -23,13 +23,20 @@ struct bouquet; typedef struct service_mapper_conf { - uint8_t check_availability; ///< Check service is receivable - uint8_t encrypted; ///< Include encrypted services - uint8_t merge_same_name; ///< Merge entries with the same name - uint8_t provider_tags; ///< Create tags based on provider name - uint8_t network_tags; ///< Create tags based on network name (useful for multi adapter equipments) + int check_availability; ///< Check service is receivable + int encrypted; ///< Include encrypted services + int merge_same_name; ///< Merge entries with the same name + int type_tags; ///< Create tags based on the service type (SDTV/HDTV/Radio) + int provider_tags; ///< Create tags based on provider name + int network_tags; ///< Create tags based on network name (useful for multi adapter equipments) } service_mapper_conf_t; +typedef struct service_mapper { + idnode_t idnode; + service_mapper_conf_t d; + htsmsg_t *services; +} service_mapper_t; + typedef struct service_mapper_status { int total; @@ -39,13 +46,11 @@ typedef struct service_mapper_status service_t *active; } service_mapper_status_t; +extern service_mapper_t service_mapper_conf; + void service_mapper_init ( void ); void service_mapper_done ( void ); -// Start new mapping -void service_mapper_start - ( const service_mapper_conf_t *conf, htsmsg_t *uuids ); - // Stop pending services (remove from Q) void service_mapper_stop ( void ); diff --git a/src/webui/static/app/idnode.js b/src/webui/static/app/idnode.js index 9faaa0dbb..00e46d537 100644 --- a/src/webui/static/app/idnode.js +++ b/src/webui/static/app/idnode.js @@ -1073,7 +1073,7 @@ tvheadend.idnode_editor = function(_uilevel, item, conf) text: conf.saveText || _('Save'), iconCls: conf.saveIconCls || 'save', handler: function() { - if (panel.getForm().isDirty()) { + if (panel.getForm().isDirty() || conf.alwaysDirty) { var node = panel.getForm().getFieldValues(); node.uuid = conf.uuids ? conf.uuids : item.uuid; tvheadend.Ajax({ @@ -1174,25 +1174,74 @@ tvheadend.idnode_editor = function(_uilevel, item, conf) /* * */ -tvheadend.idnode_editor_win = function(_uilevel, item, conf) +tvheadend.idnode_editor_win = function(_uilevel, conf) { - var p = tvheadend.idnode_editor(_uilevel, item, conf); - var width = p.fixedWidth; - var w = new Ext.ux.Window({ - title: conf.winTitle, - iconCls: conf.iconCls || 'edit', - layout: 'fit', - autoWidth: width ? false : true, - autoHeight: true, - autoScroll: true, - plain: true, - items: p - }); - conf.win = w; - if (width) - w.setWidth(width); - w.show(); - return w; + function display(item, conf, title) { + var p = tvheadend.idnode_editor(_uilevel, item, conf); + var width = p.fixedWidth; + var w = new Ext.ux.Window({ + title: title, + iconCls: conf.iconCls || 'edit', + layout: 'fit', + autoWidth: width ? false : true, + autoHeight: true, + autoScroll: true, + plain: true, + items: p + }); + conf.win = w; + if (width) + w.setWidth(width); + w.show(); + return w; + } + + if (!conf.cancel) + conf.cancel = function(conf) { + conf.win.close(); + conf.win = null; + } + + var params = conf.params || {}; + + var uuids = null; + if (conf.selections) { + var r = conf.selections; + if (!r || r.length <= 0) + return; + + uuids = []; + for (var i = 0; i < r.length; i++) + uuids.push(r[i].id); + + params['uuid'] = r[0].id; + } + + params['meta'] = 1; + + conf.win = null; + + tvheadend.Ajax({ + url: conf.loadURL ? conf.loadURL : 'api/idnode/load', + params: params, + success: function(d) { + d = json_decode(d); + d = d[0]; + if (conf.modifyData) + conf.modifyData(conf, d); + var title = conf.title; + if (!title) { + if (uuids && uuids.length > 1) + title = String.format(_('Edit {0} ({1} entries)'), + conf.titleS, uuids.length); + else + title = String.format(_('Edit {0}'), conf.titleS); + } + if (uuids && uuids.length > 1) + conf.uuids = uuids; + display(d, conf, title); + } + }); } /* @@ -1768,39 +1817,10 @@ tvheadend.idnode_grid = function(panel, conf) w.show(); } } else { - var r = select.getSelections(); - if (r && r.length > 0) { - var uuids = []; - for (var i = 0; i < r.length; i++) - uuids.push(r[i].id); - var params = {}; - if (conf.edit && conf.edit.params) - params = conf.edit.params; - params['uuid'] = r[0].id; - params['meta'] = 1; - tvheadend.Ajax({ - url: 'api/idnode/load', - params: params, - success: function(d) { - d = json_decode(d); - var c = { - win: null, - cancel: function(conf) { - conf.win.close(); - conf.win = null; - } - }; - if (uuids.length > 1) { - c.winTitle = String.format(_('Edit {0} ({1} entries)'), - conf.titleS, uuids.length); - c.uuids = uuids; - } else { - c.winTitle = String.format(_('Edit {0}'), conf.titleS); - } - tvheadend.idnode_editor_win(uilevel, d[0], c); - } - }); - } + tvheadend.idnode_editor_win(uilevel, { + selections: select.getSelections(), + params: conf.edit && conf.edit.params ? conf.edit.params : null + }); } } }); diff --git a/src/webui/static/app/servicemapper.js b/src/webui/static/app/servicemapper.js index a3eac5ffb..6185e4030 100644 --- a/src/webui/static/app/servicemapper.js +++ b/src/webui/static/app/servicemapper.js @@ -2,8 +2,6 @@ * Status dialog */ -tvheadend.service_mapper_status_panel = null; - tvheadend.service_mapper_status = function(panel, index) { /* Fields */ @@ -30,6 +28,7 @@ tvheadend.service_mapper_status = function(panel, index) /* Panel */ var mpanel = new Ext.FormPanel({ + id: 'service_mapper', method: 'get', title: _('Service Mapper'), iconCls: 'serviceMapper', @@ -83,109 +82,33 @@ 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 - }); - var nettagCheck = new Ext.form.Checkbox({ - name: 'network_tags', - fieldLabel: _('Create network tags'), - checked: false - }); - - // TODO: provider list - items = [availCheck, ftaCheck, mergeCheck, provtagCheck, nettagCheck]; - - /* 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)}; - } + function modify_data(conf, d) { + for (var i = 0; i < d.params.length; i++) + if (d.params[i].id === 'services') + break; + if (select && i < d.params.length) { + var r = select.getSelections(); + if (r.length > 0) { + var uuids = []; + for (var j = 0; j < r.length; j++) + uuids.push(r[j].id); + d.params[i].value = 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'), - iconCls: 'clone', - layout: 'fit', - autoWidth: true, - autoHeight: true, - plain: false, - items: tvheadend.service_mapper_status_panel - // TODO: buttons - }); - win.show(); + } + } + + tvheadend.idnode_editor_win(tvheadend.uilevel, { + loadURL: 'api/service/mapper/load', + saveURL: 'api/service/mapper/save', + saveText: _('Map services'), + alwaysDirty: true, + noApply: true, + modifyData: modify_data, + postsave: function() { + tvheadend.select_tab('service_mapper'); + }, + help: function() { + new tvheadend.help(_('Map services'), 'config_mapper.html'); } }); - - 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'), - iconCls: 'clone', - layout: 'fit', - autoWidth: true, - autoHeight: true, - plain: true, - items: panel - }); - - win.show(); } diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index 806e0ddfd..865ee36cd 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -46,6 +46,21 @@ tvheadend.uilevel_match = function(target, current) { return true; } +/* + * Select specific tab + */ +tvheadend.select_tab = function(id) +{ + var i = Ext.getCmp(id); + var c = i ? i.ownerCt : null; + while (c) { + if ('activeTab' in c) + c.setActiveTab(i); + i = c; + c = c.ownerCt; + } +} + /** * Displays a help popup window */ diff --git a/src/webui/static/app/wizard.js b/src/webui/static/app/wizard.js index 7f9690213..95f693160 100644 --- a/src/webui/static/app/wizard.js +++ b/src/webui/static/app/wizard.js @@ -119,16 +119,8 @@ tvheadend.wizard_start = function(page) { tvheadend.wizard = page; - if (page in tabMapping) { - var i = Ext.getCmp(tabMapping[page]); - var c = i ? i.ownerCt : null; - while (c) { - if ('activeTab' in c) - c.setActiveTab(i); - i = c; - c = c.ownerCt; - } - } + if (page in tabMapping) + tvheadend.select_tab(tabMapping[page]); tvheadend.Ajax({ url: 'api/wizard/' + page + '/load',