]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
service mapper: move to simple idnode, add 'create type based tags', fixes #3363
authorJaroslav Kysela <perex@perex.cz>
Wed, 9 Dec 2015 12:20:31 +0000 (13:20 +0100)
committerJaroslav Kysela <perex@perex.cz>
Wed, 9 Dec 2015 12:20:31 +0000 (13:20 +0100)
src/api/api_service.c
src/prop.c
src/service_mapper.c
src/service_mapper.h
src/webui/static/app/idnode.js
src/webui/static/app/servicemapper.js
src/webui/static/app/tvheadend.js
src/webui/static/app/wizard.js

index 9270d975a5612b5d8bcc7a47b3621da88f8c122b..d5d6c709fb0858fddfbf683d30194ad7bfb15a0e 100644 (file)
 #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 },
   };
index c01b934de1a19d015fcea1a0512fedde54ffd038..d17891446e62d33bbcd40d0458a6ee972d60dee3 100644 (file)
@@ -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 {
index 07c8c4a2b334709cf05353d669ee08025c18af3a..40bdf5289f7cfbd510652de44a1e1c9004f87ace 100644 (file)
@@ -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;
+}
index 199dd6094ec5fe1436153b18af032a2681d20287..c3f3c3d173671ba92d83778dceb8dca5bebb107b 100644 (file)
@@ -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 );
 
index 9faaa0dbb1722866b87b50b6a7cd53dff9bf1bf2..00e46d5372d5e556c07c2f7903f51a6deadbb3c7 100644 (file)
@@ -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
+                        });
                     }
                 }
             });
index a3eac5ffbeceb3478b8d45be22b2805706fa4781..6185e403035f0b3656b8ce67ca0bc73da6f584d6 100644 (file)
@@ -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();
 }
index 806e0ddfd076a066a688f7612f7c8a3a50c4cab9..865ee36cdb323f1fc9af0c3b163185085e0bb2ae 100644 (file)
@@ -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
  */
index 7f9690213d344486ef3991ac38af74d4ca3c3bcc..95f693160c9c5317cb844c448e996e9af32f9390 100644 (file)
@@ -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',