]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
webui: add possibility to colorify channel names with numbers and/or source (like...
authorJaroslav Kysela <perex@perex.cz>
Thu, 4 Jan 2018 19:46:43 +0000 (20:46 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 4 Jan 2018 19:53:47 +0000 (20:53 +0100)
12 files changed:
src/api/api_channel.c
src/channels.c
src/channels.h
src/config.c
src/config.h
src/input/mpegts/mpegts_service.c
src/service.c
src/service.h
src/webui/comet.c
src/webui/static/app/chconf.js
src/webui/static/app/epg.js
src/webui/static/app/tvheadend.js

index 4b6a2d94d2cc828bc7de37cf008de813306b6841..34706847381069d3e6b33202ef4eb7135f4790b3 100644 (file)
@@ -33,15 +33,18 @@ api_channel_is_all(access_t *perm, htsmsg_t *args)
          !access_verify2(perm, ACCESS_ADMIN);
 }
 
-// TODO: this will need converting to an idnode system
 static int
 api_channel_list
   ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
 {
   channel_t *ch;
   htsmsg_t *l;
-  int cfg = api_channel_is_all(perm, args);
-  char buf[128], ubuf[UUID_HEX_SIZE];
+  const int cfg = api_channel_is_all(perm, args);
+  const int numbers = htsmsg_get_s32_or_default(args, "numbers", 0);
+  const int sources = htsmsg_get_s32_or_default(args, "sources", 0);
+  const int flags = (numbers ? CHANNEL_ENAME_NUMBERS : 0) |
+                    (sources ? CHANNEL_ENAME_SOURCES : 0);
+  char buf[128], buf1[128], ubuf[UUID_HEX_SIZE];
   const char *name, *blank;
 
   blank = tvh_gettext_lang(perm->aa_lang_ui, channel_blank_name);
@@ -50,10 +53,11 @@ api_channel_list
   CHANNEL_FOREACH(ch) {
     if (!cfg && !channel_access(ch, perm, 0)) continue;
     if (!ch->ch_enabled) {
-      snprintf(buf, sizeof(buf), "{%s}", channel_get_name(ch, blank));
+      snprintf(buf, sizeof(buf), "{%s}",
+               channel_get_ename(ch, buf1, sizeof(buf1), blank, flags));
       name = buf;
     } else {
-      name = channel_get_name(ch, blank);
+      name = channel_get_ename(ch, buf1, sizeof(buf1), blank, flags);
     }
     htsmsg_add_msg(l, NULL, htsmsg_create_key_val(idnode_uuid_as_str(&ch->ch_id, ubuf), name));
   }
index a7cf3239a3007feb13856b80a32644fa2f139a65..c0f7b5a0bbe88c8996fab6d410a06c5b18074bf9 100644 (file)
@@ -236,6 +236,10 @@ channel_class_get_list(void *o, const char *lang)
   htsmsg_add_str(m, "uri",   "channel/list");
   htsmsg_add_str(m, "event", "channel");
   htsmsg_add_u32(p, "all",  1);
+  if (config.chname_num)
+    htsmsg_add_u32(p, "numbers", 1);
+  if (config.chname_src)
+    htsmsg_add_u32(p, "sources", 1);
   htsmsg_add_msg(m, "params", p);
   return m;
 }
@@ -773,6 +777,39 @@ channel_get_name ( channel_t *ch, const char *blank )
   return blank;
 }
 
+char *
+channel_get_ename
+  ( channel_t *ch, char *dst, size_t dstlen, const char *blank, uint32_t flags )
+{
+  size_t l = 0;
+  int64_t number;
+  char buf[128];
+  const char *s;
+
+  dst[0] = '\0';
+  if (flags & CHANNEL_ENAME_NUMBERS) {
+    number = channel_get_number(ch);
+    if (number > 0) {
+      if (number % CHANNEL_SPLIT) {
+        tvh_strlcatf(dst, dstlen, l, "%u.%u",
+                     channel_get_major(number),
+                     channel_get_minor(number));
+      } else {
+        tvh_strlcatf(dst, dstlen, l, "%u", channel_get_major(number));
+      }
+    }
+  }
+  s = channel_get_name(ch, blank);
+  if (s)
+    tvh_strlcatf(dst, dstlen, l, "%s%s", l > 0 ? " " : "", s);
+  if (flags & CHANNEL_ENAME_SOURCES) {
+    s = channel_get_source(ch, buf, sizeof(buf));
+    if (s)
+      tvh_strlcatf(dst, dstlen, l, "%s[%s]", l > 0 ? " " : "", s);
+  }
+  return dst;
+}
+
 int
 channel_set_name ( channel_t *ch, const char *name )
 {
@@ -852,6 +889,19 @@ channel_set_number ( channel_t *ch, uint32_t major, uint32_t minor )
   return save;
 }
 
+char *
+channel_get_source ( channel_t *ch, char *dst, size_t dstlen )
+{
+  const char *s;
+  idnode_list_mapping_t *ilm;
+  size_t l = 0;
+  dst[0] = '\0';
+  LIST_FOREACH(ilm, &ch->ch_services, ilm_in2_link)
+    if ((s = service_get_source((service_t *)ilm->ilm_in1)))
+      tvh_strlcatf(dst, dstlen, l, "%s%s", l > 0 ? "," : "", s);
+  return l > 0 ? dst : NULL;
+}
+
 static char *
 svcnamepicons(const char *svcname)
 {
index 7696763215cb4e017794b5c401d6196ad10cce30..e16804cfb3f108f8d9e0946bbb384c81d1a7a088 100644 (file)
@@ -181,6 +181,12 @@ int channel_set_name ( channel_t *ch, const char *name );
 /// @return number channels that matched "from".
 int channel_rename_and_save ( const char *from, const char *to );
 
+#define CHANNEL_ENAME_NUMBERS (1<<0)
+#define CHANNEL_ENAME_SOURCES (1<<1)
+
+char *channel_get_ename ( channel_t *ch, char *dst, size_t dstlen,
+                          const char *blank, uint32_t flags );
+
 #define CHANNEL_SPLIT ((int64_t)1000000)
 
 static inline uint32_t channel_get_major ( int64_t chnum ) { return chnum / CHANNEL_SPLIT; }
@@ -189,6 +195,8 @@ static inline uint32_t channel_get_minor ( int64_t chnum ) { return chnum % CHAN
 int64_t channel_get_number ( channel_t *ch );
 int channel_set_number ( channel_t *ch, uint32_t major, uint32_t minor );
 
+char *channel_get_source ( channel_t *ch, char *dst, size_t dstlen );
+
 const char *channel_get_icon ( channel_t *ch );
 int channel_set_icon ( channel_t *ch, const char *icon );
 
index 649dec787171b5fd5b3b2bda9a9702ac9e149bd5..2035da2204aa764d029e1a4401948d73335d81bc 100644 (file)
@@ -1697,6 +1697,7 @@ config_boot ( const char *path, gid_t gid, uid_t uid )
   config.epg_update_window = 24*3600;
   config_scanfile_ok = 0;
   config.theme_ui = strdup("blue");
+  config.chname_num = 1;
 
   idclass_register(&config_class);
 
@@ -2188,6 +2189,23 @@ const idclass_t config_class = {
       .opts   = PO_LORDER | PO_ADVANCED | PO_DOC_NLIST,
       .group  = 2
     },
+    {
+      .type   = PT_BOOL,
+      .id     = "chname_num",
+      .name   = N_("Channel name with numbers"),
+      .desc   = N_("Add channel numbers to the channel name list"),
+      .off    = offsetof(config_t, chname_num),
+      .group  = 2,
+      .def.i  = 1
+    },
+    {
+      .type   = PT_BOOL,
+      .id     = "chname_src",
+      .name   = N_("Channel name with sources"),
+      .desc   = N_("Add sources (like DVB-T string) to the channel name list"),
+      .off    = offsetof(config_t, chname_src),
+      .group  = 2
+    },
     {
       .type   = PT_STR,
       .islist = 1,
index 6524db823ac57eaab2e75626715bc5dcc512a955..b67f92a64ff2503e0b6a8e793ea318cbe35f6bed 100644 (file)
@@ -43,6 +43,8 @@ typedef struct config {
   char *http_server_name;
   char *language;
   char *info_area;
+  int chname_num;
+  int chname_src;
   char *language_ui;
   char *theme_ui;
   char *muxconf_path;
index 8213a33a5442b3b15cfccbc2abdbd27b34ae83f1..9b682d659f18426d8d5bd6d28f73658afd5474c2 100644 (file)
@@ -545,6 +545,24 @@ mpegts_service_channel_name ( service_t *s )
   return ((mpegts_service_t*)s)->s_dvb_svcname;
 }
 
+static const char *
+mpegts_service_source ( service_t *s )
+{
+  mpegts_service_t *ms = (mpegts_service_t*)s;
+  const idclass_t *mux_idc = ms->s_dvb_mux->mm_id.in_class;
+  if (mux_idc == &dvb_mux_dvbs_class)   return "DVB-S";
+  if (mux_idc == &dvb_mux_dvbc_class)   return "DVB-C";
+  if (mux_idc == &dvb_mux_dvbt_class)   return "DVB-T";
+  if (mux_idc == &dvb_mux_atsc_t_class) return "ATSC-T";
+  if (mux_idc == &dvb_mux_atsc_c_class) return "ATSC-C";
+  if (mux_idc == &dvb_mux_isdb_t_class) return "ISDB-T";
+  if (mux_idc == &dvb_mux_isdb_c_class) return "ISDB-C";
+  if (mux_idc == &dvb_mux_isdb_s_class) return "ISDB-S";
+  if (mux_idc == &dvb_mux_dtmb_class)   return "DTMB";
+  if (mux_idc == &dvb_mux_dab_class)    return "DAB";
+  return NULL;
+}
+
 static const char *
 mpegts_service_provider_name ( service_t *s )
 {
@@ -817,6 +835,7 @@ mpegts_service_create0
   s->s_grace_period   = mpegts_service_grace_period;
   s->s_channel_number = mpegts_service_channel_number;
   s->s_channel_name   = mpegts_service_channel_name;
+  s->s_source         = mpegts_service_source;
   s->s_provider_name  = mpegts_service_provider_name;
   s->s_channel_icon   = mpegts_service_channel_icon;
   s->s_mapped         = mpegts_service_mapped;
@@ -1102,6 +1121,7 @@ mpegts_service_create_raw ( mpegts_mux_t *mm )
   s->s_grace_period   = mpegts_service_grace_period;
   s->s_channel_number = mpegts_service_channel_number;
   s->s_channel_name   = mpegts_service_channel_name;
+  s->s_source         = mpegts_service_source;
   s->s_provider_name  = mpegts_service_provider_name;
   s->s_channel_icon   = mpegts_service_channel_icon;
   s->s_mapped         = mpegts_service_mapped;
index beb33c781c95338908e57b82953937f5df2fca3a..a2e8d021642009bfc275e7da9d7cd674da5a7d55 100644 (file)
@@ -2018,6 +2018,16 @@ service_get_channel_number ( service_t *s )
   return 0;
 }
 
+/*
+ * Get source identificator for service
+ */
+const char *
+service_get_source ( service_t *s )
+{
+  if (s->s_source) return s->s_source(s);
+  return 0;
+}
+
 /*
  * Get name for channel from service
  */
index 993bab0d0f075fd41ac0fb68021fd8d607611225..6bceaf46ce78a234091ae35b5978a7e84c2b77dd 100644 (file)
@@ -356,6 +356,7 @@ typedef struct service {
    */
   int64_t     (*s_channel_number) (struct service *);
   const char *(*s_channel_name)   (struct service *);
+  const char *(*s_source)         (struct service *);
   const char *(*s_channel_epgid)  (struct service *);
   htsmsg_t   *(*s_channel_tags)   (struct service *);
   const char *(*s_provider_name)  (struct service *);
@@ -667,6 +668,7 @@ void sort_elementary_streams(service_t *t);
 const char *service_get_channel_name (service_t *s);
 const char *service_get_full_channel_name (service_t *s);
 int64_t     service_get_channel_number (service_t *s);
+const char *service_get_source (service_t *s);
 const char *service_get_channel_icon (service_t *s);
 const char *service_get_channel_epgid (service_t *s);
 
index 35ed4a66cfa0a822be08228dc4ce844aa2044cc9..4a41292bc925d360fbbb96207000db417ce9b1ea 100644 (file)
@@ -189,6 +189,8 @@ comet_access_update(http_connection_t *hc, comet_mailbox_t *cmb)
   }
   htsmsg_add_str(m, "theme", access_get_theme(hc->hc_access));
   htsmsg_add_u32(m, "quicktips", config.ui_quicktips);
+  htsmsg_add_u32(m, "chname_num", config.chname_num);
+  htsmsg_add_u32(m, "chname_src", config.chname_src);
   if (!access_noacl)
     htsmsg_add_str(m, "username", username);
   if (hc->hc_peer_ipstr)
index 253f338e3cd25703c54631f6c3326a9897b12222..3f00b7f2fa1db36f5f9458b86562a8189baca573 100644 (file)
@@ -1,24 +1,38 @@
-tvheadend.channelTags = tvheadend.idnode_get_enum({
-    url: 'api/channeltag/list',
-    event: 'channeltag',
-    listeners: {
-        'load': function(scope, records, options) {
-            var placeholder = Ext.data.Record.create(['key', 'val']);
-            scope.insert(0,new placeholder({key: '-1', val: _('(Clear filter)')}));
+tvheadend.getChannelTags = function() {
+    if (tvheadend._chtags)
+        return tvheadend._chtags;
+    tvheadend._chtags = tvheadend.idnode_get_enum({
+        url: 'api/channeltag/list',
+        event: 'channeltag',
+        listeners: {
+            'load': function(scope, records, options) {
+                var placeholder = Ext.data.Record.create(['key', 'val']);
+                scope.insert(0,new placeholder({key: '-1', val: _('(Clear filter)')}));
+            }
         }
-    }
-});
-
-tvheadend.channels = tvheadend.idnode_get_enum({
-    url: 'api/channel/list',
-    event: 'channel',
-    listeners: {
-        'load': function(scope, records, options) {
-            var placeholder = Ext.data.Record.create(['key', 'val']);
-            scope.insert(0,new placeholder({key: '-1', val: _('(Clear filter)')}));
+    });
+    return tvheadend._chtags;
+}
+
+tvheadend.getChannels = function() {
+    if (tvheadend._channels)
+        return tvheadend._channels;
+    tvheadend._channels = tvheadend.idnode_get_enum({
+        url: 'api/channel/list',
+        params: {
+            'numbers': tvheadend.chname_num,
+            'sources': tvheadend.chname_src,
+        },
+        event: 'channel',
+        listeners: {
+            'load': function(scope, records, options) {
+                var placeholder = Ext.data.Record.create(['key', 'val']);
+                scope.insert(0,new placeholder({key: '-1', val: _('(Clear filter)')}));
+            }
         }
-    }
-});
+    });
+    return tvheadend._channels;
+}
 
 tvheadend.channel_tab = function(panel, index)
 {
index a75cd2491ca13030a97bdeaf35f1b0626a0a1bb0..1cf216853fc53d90a656b55b17b65e555f0f1771 100644 (file)
@@ -58,25 +58,21 @@ tvheadend.contentGroupFullLookupName = function(code) {
 };
 
 tvheadend.channelLookupName = function(key) {
-    channelString = "";
-
-    var index = tvheadend.channels.find('key', key);
-
+    var s = "";
+    var channels = tvheadend.getChannels();
+    var index = channels.find('key', key);
     if (index !== -1)
-        var channelString = tvheadend.channels.getAt(index).get('val');
-
-    return channelString;
+        s = channels.getAt(index).get('val');
+    return s;
 };
 
 tvheadend.channelTagLookupName = function(key) {
-    tagString = "";
-
-    var index = tvheadend.channelTags.find('key', key);
-
+    var s = "";
+    var tags = tvheadend.getChannelTags();
+    var index = tvheadend.tags.find('key', key);
     if (index !== -1)
-        var tagString = tvheadend.channelTags.getAt(index).get('val');
-
-    return tagString;
+        s = tags.getAt(index).get('val');
+    return s;
 };
 
 // Store for duration filters - EPG, autorec dialog and autorec rules in the DVR grid
@@ -766,7 +762,7 @@ tvheadend.epg = function() {
         loadingText: _('Loading...'),
         width: 200,
         displayField: 'val',
-        store: tvheadend.channels,
+        store: tvheadend.getChannels(),
         mode: 'local',
         editable: true,
         forceSelection: true,
@@ -789,7 +785,7 @@ tvheadend.epg = function() {
         loadingText: _('Loading...'),
         width: 200,
         displayField: 'val',
-        store: tvheadend.channelTags,
+        store: tvheadend.getChannelTags(),
         mode: 'local',
         editable: true,
         forceSelection: true,
index 40c41a4bbab9c49f658ceded75073d18758b99c5..c776f610c3f5de23386149fbf0b96577e71bd30a 100644 (file)
@@ -6,6 +6,8 @@ tvheadend.dialog = null;
 tvheadend.uilevel = 'expert';
 tvheadend.uilevel_nochange = false;
 tvheadend.quicktips = true;
+tvheadend.chname_num = true;
+tvheadend.chname_src = false;
 tvheadend.wizard = null;
 tvheadend.docs_toc = null;
 tvheadend.doc_history = [];
@@ -810,7 +812,7 @@ tvheadend.VideoPlayer = function(channelId) {
 
     var initialChannelName;
     if (channelId) {
-        var record = tvheadend.channels.getById(channelId);
+        var record = tvheadend.getChannels().getById(channelId);
         initialChannelName = record.data.val;
     }
 
@@ -818,7 +820,7 @@ tvheadend.VideoPlayer = function(channelId) {
         loadingText: _('Loading...'),
         width: 200,
         displayField: 'val',
-        store: tvheadend.channels,
+        store: tvheadend.getChannels(),
         mode: 'local',
         editable: true,
         triggerAction: 'all',
@@ -976,13 +978,15 @@ function diskspaceUpdate(o) {
  * This function creates top level tabs based on access so users without
  * access to subsystems won't see them.
  *
- * Obviosuly, access is verified in the server too.
+ * Obviously, access is verified in the server too.
  */
 function accessUpdate(o) {
     tvheadend.accessUpdate = o;
     if (!tvheadend.capabilities)
         return;
 
+    var panel = tvheadend.rootTabPanel;
+
     tvheadend.admin = o.admin == true;
 
     if (o.uilevel)
@@ -992,18 +996,20 @@ function accessUpdate(o) {
         tvheadend.theme = o.theme;
 
     tvheadend.quicktips = o.quicktips ? true : false;
+    tvheadend.chname_num = o.chname_num ? 1 : 0;
+    tvheadend.chname_src = o.chname_src ? 1 : 0;
 
     if (o.uilevel_nochange)
         tvheadend.uilevel_nochange = true;
 
     if ('info_area' in o)
-        tvheadend.rootTabPanel.setInfoArea(o.info_area);
+        panel.setInfoArea(o.info_area);
     if ('username' in o)
-        tvheadend.rootTabPanel.setLogin(o.username);
+        panel.setLogin(o.username);
     if ('address' in o)
-        tvheadend.rootTabPanel.setAddress(o.address);
+        panel.setAddress(o.address);
     if ('freediskspace' in o && 'useddiskspace' in o && 'totaldiskspace' in o)
-        tvheadend.rootTabPanel.setDiskSpace(o.freediskspace, o.useddiskspace, o.totaldiskspace);
+        panel.setDiskSpace(o.freediskspace, o.useddiskspace, o.totaldiskspace);
 
     if ('cookie_expires' in o && o.cookie_expires > 0)
         tvheadend.cookieProvider.expires =
@@ -1012,9 +1018,15 @@ function accessUpdate(o) {
     if (tvheadend.autorecButton)
         tvheadend.autorecButton.setDisabled(o.dvr != true);
 
+    if (tvheadend.epgpanel == null) {
+        tvheadend.epgpanel = tvheadend.epg();
+        panel.add(tvheadend.epgpanel);
+        panel.setActiveTab(0);
+    }
+
     if (o.dvr == true && tvheadend.dvrpanel == null) {
         tvheadend.dvrpanel = tvheadend.dvr();
-        tvheadend.rootTabPanel.add(tvheadend.dvrpanel);
+        panel.add(tvheadend.dvrpanel);
     }
 
     if (o.admin == true && tvheadend.confpanel == null) {
@@ -1148,14 +1160,14 @@ function accessUpdate(o) {
         }
 
         /* Finish */
-        tvheadend.rootTabPanel.add(cp);
+        panel.add(cp);
         tvheadend.confpanel = cp;
         cp.doLayout();
     }
 
     if (o.admin == true && tvheadend.statuspanel == null) {
         tvheadend.statuspanel = new tvheadend.status;
-        tvheadend.rootTabPanel.add(tvheadend.statuspanel);
+        panel.add(tvheadend.statuspanel);
     }
 
     if (tvheadend.aboutPanel == null) {
@@ -1167,10 +1179,10 @@ function accessUpdate(o) {
             iconCls: 'info',
             autoLoad: 'about.html'
         });
-        tvheadend.rootTabPanel.add(tvheadend.aboutPanel);
+        panel.add(tvheadend.aboutPanel);
     }
 
-    tvheadend.rootTabPanel.doLayout();
+    panel.doLayout();
 
     if (o.wizard)
         tvheadend.wizard_start(o.wizard);
@@ -1362,9 +1374,7 @@ tvheadend.app = function() {
             });
 
             tvheadend.rootTabPanel = new tvheadend.RootTabPanel({
-                region: 'center',
-                activeTab: 0,
-                items: [tvheadend.epg()]
+                region: 'center'
             });
 
             var viewport = new Ext.Viewport({