]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
channel: added support for getting icons from underlying services feature/picons 902/head
authorAdam Sutton <dev@adamsutton.me.uk>
Fri, 16 May 2014 23:04:03 +0000 (00:04 +0100)
committerAdam Sutton <dev@adamsutton.me.uk>
Wed, 11 Jun 2014 01:13:18 +0000 (02:13 +0100)
Also added an initial implementation of picon support.

12 files changed:
src/channels.c
src/channels.h
src/config.c
src/config.h
src/htsp_server.c
src/imagecache.h
src/input/mpegts/mpegts_service.c
src/service.c
src/service.h
src/webui/extjs.c
src/webui/statedump.c
src/webui/static/app/config.js

index 22ff546dc07d356c93509f7935c35299968c3322..926a1c291ae61b4a486444e40dc6821414122ea9 100644 (file)
@@ -29,6 +29,7 @@
 #include <string.h>
 
 #include "settings.h"
+#include "config.h"
 
 #include "tvheadend.h"
 #include "epg.h"
@@ -181,29 +182,15 @@ channel_class_tags_enum ( void *obj )
 static void
 channel_class_icon_notify ( void *obj )
 {
-  channel_t *ch = obj;
-  if (ch->ch_icon)
-    imagecache_get_id(ch->ch_icon);
+  (void)channel_get_icon(obj);
 }
 
 static const void *
-channel_class_get_imagecache ( void *obj )
+channel_class_get_icon ( void *obj )
 {
-  static char buf[512], *r;
-  uint32_t id;
-  channel_t *ch = obj;
-
-  if (!ch->ch_icon) {
-    r = NULL;
-  } else if ((id = imagecache_get_id(ch->ch_icon))) {
-    snprintf(buf, sizeof(buf), "imagecache/%d", id);
-    r = buf;
-  } else {
-    strncpy(buf, ch->ch_icon, sizeof(buf));
-    r = buf;
-  }
-
-  return &r;
+  static const char *s;
+  s = channel_get_icon(obj);
+  return &s;
 }
 
 static const char *
@@ -318,16 +305,16 @@ const idclass_t channel_class = {
     },
     {
       .type     = PT_STR,
-      .id       = "icon",
-      .name     = "Icon",
-      .off      = offsetof(channel_t, ch_icon),
+      .id       = "usericon",
+      .name     = "User Icon",
+      .off      = offsetof(channel_t, ch_usericon),
       .notify   = channel_class_icon_notify,
     },
     {
       .type     = PT_STR,
-      .id       = "icon_public_url",
-      .name     = "Icon URL",
-      .get      = channel_class_get_imagecache,
+      .id       = "icon",
+      .name     = "Icon",
+      .get      = channel_class_get_icon,
       .opts     = PO_RDONLY | PO_NOSAVE | PO_HIDDEN,
     },
     {
@@ -502,6 +489,52 @@ channel_get_number ( channel_t *ch )
   return 0;
 }
 
+const char *
+channel_get_icon ( channel_t *ch )
+{
+  static __thread char buf[512], buf2[512];
+  channel_service_mapping_t *csm;
+  const char *picon = config_get_picon_path(),
+             *icon  = ch->ch_usericon;
+  uint32_t id;
+
+  /* No user icon - try access from services */
+  if (!icon && picon) {
+    LIST_FOREACH(csm, &ch->ch_services, csm_chn_link) {
+      if (!(icon = service_get_channel_icon(csm->csm_svc))) continue;
+      if (strncmp(icon, "picon://", 8)) {
+        icon = NULL;
+        continue;
+      }
+      sprintf(buf2, "%s/%s", picon, icon+8);
+      ch->ch_usericon = strdup(icon);
+      channel_save(ch);
+      idnode_updated(&ch->ch_id);
+    }
+  }
+
+  /* Nothing */
+  if (!icon || !*icon)
+    return NULL;
+
+  /* Picon? */
+  if (!strncmp(icon, "picon://", 8)) {
+    if (!picon) return NULL;
+    sprintf(buf2, "%s/%s", picon, icon+8);
+    icon = buf2;
+  }
+  
+  /* Lookup imagecache ID */
+  if ((id = imagecache_get_id(icon))) {
+    snprintf(buf, sizeof(buf), "imagecache/%d", id);
+
+  } else {
+    strncpy(buf, icon, sizeof(buf));
+  }
+    
+  return buf;
+}
+
 /* **************************************************************************
  * Creation/Deletion
  * *************************************************************************/
@@ -582,7 +615,7 @@ channel_delete ( channel_t *ch, int delconf )
   RB_REMOVE(&channels, ch, ch_link);
   idnode_unlink(&ch->ch_id);
   free(ch->ch_name);
-  free(ch->ch_icon);
+  free(ch->ch_usericon);
   free(ch);
 }
 
index 370ab7c55d89a635d3a3d6fe142265f569af99f1..0996d0b28cafddd7223a48a35ac50f18d0e97a55 100644 (file)
@@ -47,7 +47,7 @@ typedef struct channel
   /* Channel info */
   char *ch_name; // Note: do not access directly!
   int   ch_number;
-  char *ch_icon;
+  char *ch_usericon;
   struct channel_tag_mapping_list ch_ctms;
 
   /* Service/subscriptions */
index f7e97bd3b1cf0ea94b86f0e768011465953c41e2..49ab880849eedc64a7d18f11eef0750b491c1568 100644 (file)
@@ -492,7 +492,7 @@ config_migrate_v3 ( void )
 }
 
 /*
- * v3 -> v4 : fix broken DVB network / mux files
+ * v4 -> v5 : fix broken DVB network / mux files
  */
 static void
 config_migrate_v5 ( void )
@@ -517,6 +517,28 @@ config_migrate_v5 ( void )
   }
 }
 
+/*
+ * v5 -> v6 : change channel icon param
+ */
+static void
+config_migrate_v6 ( void )
+{
+  htsmsg_t *c, *e;
+  htsmsg_field_t *f;
+  const char *str;
+
+  /* Remove linux prefix from class */
+  if ((c = hts_settings_load_r(1, "channel"))) {
+    HTSMSG_FOREACH(f, c) {
+      if (!(e   = htsmsg_field_get_map(f)))            continue;
+      if (!(str = htsmsg_get_str(e, "icon")))   continue;
+      htsmsg_add_str(e, "usericon", str);
+      htsmsg_delete_field(e, "icon");
+      hts_settings_save(e, "channel/%s", f->hmf_name);
+    }
+  }
+}
+
 /*
  * Migration table
  */
@@ -525,7 +547,8 @@ static const config_migrate_t config_migrate_table[] = {
   config_migrate_v2,
   config_migrate_v3,
   config_migrate_v3, // Re-run due to bug in previous version of function
-  config_migrate_v5
+  config_migrate_v5,
+  config_migrate_v6,
 };
 
 /*
@@ -670,3 +693,13 @@ int config_set_muxconfpath ( const char *path )
 {
   return _config_set_str("muxconfpath", path);
 }
+
+const char *config_get_picon_path ( void )
+{
+  return htsmsg_get_str(config, "piconpath");
+}
+
+int config_set_picon_path ( const char *str )
+{
+  return _config_set_str("piconpath", str);
+}
index 4473947d265c1ae030f72f87d3c3819be6b3479e..0542d238885ed6aab139b3415d7198e095d4727a 100644 (file)
@@ -37,4 +37,8 @@ const char *config_get_language    ( void );
 int         config_set_language    ( const char *str )
   __attribute__((warn_unused_result));
 
+const char *config_get_picon_path  ( void );
+int         config_set_picon_path  ( const char *str )
+  __attribute__((warn_unused_result));
+
 #endif /* __TVH_CONFIG__H__ */
index 6b47911b6d7e762222f5e190f943f95095bc3f07..38b6830403a812231bb3f084ff78a626715e9b95 100644 (file)
@@ -530,6 +530,7 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp)
   channel_tag_t *ct;
   service_t *t;
   epg_broadcast_t *now, *next = NULL;
+  const char *icon;
 
   htsmsg_t *out = htsmsg_create_map();
   htsmsg_t *tags = htsmsg_create_list();
@@ -539,31 +540,30 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp)
   htsmsg_add_u32(out, "channelNumber", channel_get_number(ch));
 
   htsmsg_add_str(out, "channelName", channel_get_name(ch));
-  if(ch->ch_icon != NULL) {
-    uint32_t id;
-    struct sockaddr_storage addr;
-    socklen_t addrlen;
-    if ((id = imagecache_get_id(ch->ch_icon))) {
+  if ((icon = channel_get_icon(ch))) {
+
+    /* Handle older clients */
+    if ((strstr(icon, "imagecache") == icon) && htsp->htsp_version < 8) {
+      struct sockaddr_storage addr;
+      socklen_t addrlen;
       size_t p = 0;
       char url[256];
       char buf[50];
-      if (htsp->htsp_version < 8) {
-        addrlen = sizeof(addr);
-        getsockname(htsp->htsp_fd, (struct sockaddr*)&addr, &addrlen);
-        tcp_get_ip_str((struct sockaddr*)&addr, buf, 50);
-        strcpy(url, "http://");
-        p = strlen(url);
-        p += snprintf(url+p, sizeof(url)-p, "%s%s%s:%d%s",
-                      (addr.ss_family == AF_INET6)?"[":"",
-                      buf,
-                      (addr.ss_family == AF_INET6)?"]":"",
-                      tvheadend_webui_port,
-                      tvheadend_webroot ?: "");
-      }
-      snprintf(url+p, sizeof(url)-p, "/imagecache/%d", id);
+      addrlen = sizeof(addr);
+      getsockname(htsp->htsp_fd, (struct sockaddr*)&addr, &addrlen);
+      tcp_get_ip_str((struct sockaddr*)&addr, buf, 50);
+      strcpy(url, "http://");
+      p = strlen(url);
+      p += snprintf(url+p, sizeof(url)-p, "%s%s%s:%d%s",
+                    (addr.ss_family == AF_INET6)?"[":"",
+                    buf,
+                    (addr.ss_family == AF_INET6)?"]":"",
+                    tvheadend_webui_port,
+                    tvheadend_webroot ?: "");
+      snprintf(url+p, sizeof(url)-p, "/%s", icon);
       htsmsg_add_str(out, "channelIcon", url);
     } else {
-      htsmsg_add_str(out, "channelIcon", ch->ch_icon);
+      htsmsg_add_str(out, "channelIcon", icon);
     }
   }
 
index c5cdc4e6a6c4504ad49f956b940f32d8292b70dc..e0009974a7b60620ef07a921ec75542a66508d0a 100644 (file)
@@ -44,16 +44,4 @@ uint32_t imagecache_get_id  ( const char *url );
 
 int      imagecache_open    ( uint32_t id );
 
-#define htsmsg_add_imageurl(_msg, _fld, _fmt, _url)\
-  {\
-    char _tmp[64];\
-    uint32_t _id = imagecache_get_id(_url);\
-    if (_id) {\
-      snprintf(_tmp, sizeof(_tmp), _fmt, _id);\
-      htsmsg_add_str(_msg, _fld, _tmp);\
-    } else {\
-      htsmsg_add_str(_msg, _fld, _url);\
-    }\
-  }
-
 #endif /* __IMAGE_CACHE_H__ */
index 15f4a9a2bbf9c4824eb53d7cb99ce0599a562757..087915e83be9320629196d466e9b4189c86cbb75 100644 (file)
@@ -23,6 +23,7 @@
 #include "input.h"
 #include "settings.h"
 #include "dvb_charset.h"
+#include "config.h"
 
 /* **************************************************************************
  * Class definition
@@ -356,6 +357,55 @@ mpegts_service_provider_name ( service_t *s )
   return ((mpegts_service_t*)s)->s_dvb_provider;
 }
 
+static const char *
+mpegts_service_channel_icon ( service_t *s )
+{
+  mpegts_service_t *ms = (mpegts_service_t*)s;
+
+  /* DVB? */
+#if ENABLE_MPEGTS_DVB
+  extern const idclass_t dvb_mux_class;
+  if (ms->s_dvb_mux &&
+      idnode_is_instance(&ms->s_dvb_mux->mm_id, &dvb_mux_class)) {
+    int32_t hash = 0;
+    static __thread char buf[1024];
+    dvb_mux_t *mmd = (dvb_mux_t*)ms->s_dvb_mux;
+
+    switch ( mmd->lm_tuning.dmc_fe_type) {
+      case DVB_TYPE_S:
+        if (mmd->lm_tuning.u.dmc_fe_qpsk.orbital_dir == 'E')
+          hash = mmd->lm_tuning.u.dmc_fe_qpsk.orbital_pos;
+        else
+          hash = 0xFFFF - mmd->lm_tuning.u.dmc_fe_qpsk.orbital_pos;
+        hash <<= 16;
+        break;
+      case DVB_TYPE_C:
+        hash = 0xFFFF0000;
+        break;
+      case DVB_TYPE_T:
+        hash = 0xEEEE0000;
+        break;
+      case DVB_TYPE_ATSC:
+        hash = 0xDDDD0000;
+        break;
+      default:
+        return NULL;
+    }
+
+    snprintf(buf, sizeof(buf),
+             "picon://1_0_%X_%X_%X_%X_%X_0_0_0.png",
+             ms->s_dvb_servicetype,
+             ms->s_dvb_service_id,
+             ms->s_dvb_mux->mm_tsid,
+             ms->s_dvb_mux->mm_onid,
+             hash);
+    return buf;
+  }
+#endif
+
+  return NULL;
+}
+
 void
 mpegts_service_delete ( service_t *t, int delconf )
 {
@@ -419,6 +469,7 @@ mpegts_service_create0
   s->s_channel_number = mpegts_service_channel_number;
   s->s_channel_name   = mpegts_service_channel_name;
   s->s_provider_name  = mpegts_service_provider_name;
+  s->s_channel_icon   = mpegts_service_channel_icon;
 
   pthread_mutex_lock(&s->s_stream_mutex);
   service_make_nicename((service_t*)s);
index 01e26cb5d0042f2d15ee7f20c5bb64156f7a0d38..adba2e58cd4916f485daa72fefe764a27c8e8624 100644 (file)
@@ -1393,6 +1393,17 @@ service_get_channel_number ( service_t *s )
   return 0;
 }
 
+/*
+ * Get name for channel from service
+ */
+const char *
+service_get_channel_icon ( service_t *s )
+{
+  const char *r = NULL;
+  if (s->s_channel_icon) r = s->s_channel_icon(s);
+  return r;
+}
+
 /**
  * Get the encryption CAID from a service
  * only the first CA stream in a service is returned
index 977d2912844c6a7d58367ee13b12dfbf288a5735..0f51c6016ffb13f76f0571fa97fbe36a6afb4ae8 100644 (file)
@@ -294,6 +294,7 @@ typedef struct service {
   int         (*s_channel_number) (struct service *);
   const char *(*s_channel_name)   (struct service *);
   const char *(*s_provider_name)  (struct service *);
+  const char *(*s_channel_icon)   (struct service *);
 
   /**
    * Name usable for displaying to user
@@ -538,5 +539,6 @@ void sort_elementary_streams(service_t *t);
 
 const char *service_get_channel_name (service_t *s);
 int         service_get_channel_number (service_t *s);
+const char *service_get_channel_icon (service_t *s);
 
 #endif // SERVICE_H__
index fc8a57fee584aa385fc31fa2fd31b2eb2412a506..cd250705f504fb8c68a1e3ee07609e1d48ccc5e2 100755 (executable)
@@ -789,8 +789,8 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
 
     htsmsg_add_str(m, "channel", channel_get_name(ch));
     htsmsg_add_u32(m, "channelid", channel_get_id(ch));
-    if(ch->ch_icon != NULL)
-      htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon);
+    if ((s = channel_get_icon(ch)))
+      htsmsg_add_str(m, "chicon", s);
 
     if((s = epg_episode_get_title(ee, lang)))
       htsmsg_add_str(m, "title", s);
@@ -876,8 +876,8 @@ extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque)
           m = htsmsg_create_map();
           htsmsg_add_u32(m, "id", ebc->id);
           htsmsg_add_str(m, "channel", channel_get_name(ch));
-          if (ch->ch_icon)
-            htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon);
+          if ((s = channel_get_icon(ch)))
+            htsmsg_add_str(m, "chicon", s);
           htsmsg_add_u32(m, "start", ebc->start);
           htsmsg_add_msg(array, NULL, m);
         }
@@ -1331,9 +1331,8 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque,
     htsmsg_add_str(m, "channel", DVR_CH_NAME(de));
     if(de->de_channel != NULL) {
       htsmsg_add_str(m, "channelid", channel_get_uuid(de->de_channel));
-      if (de->de_channel->ch_icon)
-        htsmsg_add_imageurl(m, "chicon", "imagecache/%d",
-                            de->de_channel->ch_icon);
+      if ((s = channel_get_icon(de->de_channel)))
+        htsmsg_add_str(m, "chicon", s);
     }
 
     htsmsg_add_str(m, "config_name", de->de_config_name);
@@ -1599,6 +1598,8 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque)
       save |= config_set_muxconfpath(str);
     if ((str = http_arg_get(&hc->hc_req_args, "language")))
       save |= config_set_language(str);
+    if ((str = http_arg_get(&hc->hc_req_args, "piconpath")))
+      save |= config_set_picon_path(str);
     if (save)
       config_save();
 
index 79dfc0fdc0ff7a39455a49a8471831fbfc7c3534..f3be137459665859e2505ad159e33d80b7d50bee 100644 (file)
@@ -66,7 +66,7 @@ dumpchannels(htsbuf_queue_t *hq)
                   ch->ch_refcount,
                   ch->ch_zombie,
                   channel_get_number(ch),
-                  ch->ch_icon ?: "<none set>");
+                  channel_get_icon(ch) ?: "<none set>");
   }
 }
 
index cd92fc58783b7f3cebc3674b3a60ef65c188d70b..ee3c23822f57723c45fb5dd70bfd21d04264b5c6 100644 (file)
@@ -43,7 +43,8 @@ tvheadend.miscconf = function() {
     [
         'muxconfpath', 'language',
         'tvhtime_update_enabled', 'tvhtime_ntp_enabled',
-        'tvhtime_tolerance', 'transcoding_enabled'
+        'tvhtime_tolerance', 'transcoding_enabled',
+        'piconpath'
     ]);
 
     /* ****************************************************************
@@ -126,6 +127,25 @@ tvheadend.miscconf = function() {
         items: [tvhtimeUpdateEnabled, tvhtimeNtpEnabled, tvhtimeTolerance]
     });
 
+    /*
+    * Picons
+    */
+
+    var piconPath = new Ext.form.TextField({
+        name: 'piconpath',
+        fieldLabel: 'Picon path (e.g. file:///tmp/picons)',
+        width: 400
+    });
+
+    var piconPanel = new Ext.form.FieldSet({
+        title: 'Picon',
+        width: 700,
+        autoHeight: true,
+        collapsible: true,
+        animCollapse: true,
+        items: [piconPath]
+    });
+
     /*
     * Image cache
     */
@@ -232,7 +252,7 @@ tvheadend.miscconf = function() {
         layout: 'form',
         defaultType: 'textfield',
         autoHeight: true,
-        items: [languageWrap, dvbscanWrap, tvhtimePanel, transcodingPanel]
+        items: [languageWrap, dvbscanWrap, tvhtimePanel, transcodingPanel, piconPanel]
     });
 
     var _items = [confpanel];