]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
xmltv: Allow sending basic xmltv format, fixes #5630
authorE.Smith <31170571+azlm8t@users.noreply.github.com>
Wed, 1 May 2019 11:37:32 +0000 (12:37 +0100)
committerJaroslav Kysela <perex@perex.cz>
Tue, 15 Oct 2019 07:04:12 +0000 (09:04 +0200)
Some devices have very limited memory and can not handle our full
xmltv output. For example, a current make of TV requires the xmltv
should be less than 5MB and only parses titles, not descriptions.

So we now add an enum to the user access so the user can specify
a different format.

Basic format gives you a limited xmltv document.

Basic (no hash) gives you the limited xmltv document but avoids
using hashes for channel ids and uses the name instead since some
(broken) TVs require this.

docs/property/xmltv_output_format.md [new file with mode: 0644]
src/access.c
src/access.h
src/webui/static/app/acleditor.js
src/webui/xmltv.c

diff --git a/docs/property/xmltv_output_format.md b/docs/property/xmltv_output_format.md
new file mode 100644 (file)
index 0000000..fb905ba
--- /dev/null
@@ -0,0 +1,9 @@
+:
+
+Option                           | Description
+---------------------------------|------------
+**All**                          | Include all information.
+**Basic**                        | Limited information for low memory devices.
+**Basic Alternative (No Hash)**  | Limited information for low memory devices that don't correctly process tv channel names.
+
+This setting can be overridden on a per-user basis, see [Access Entries](class/access).
index 96bc3616d5ff481f8d8368a4722e2d1adcae891b..1d8138338318a9f909ef9e337e0429f2efe4a9dc 100644 (file)
@@ -292,6 +292,7 @@ access_copy(access_t *src)
     dst->aa_chtags_exclude = htsmsg_copy(src->aa_chtags_exclude);
   if (src->aa_auth)
     dst->aa_auth = strdup(src->aa_auth);
+  dst->aa_xmltv_output_format = src->aa_xmltv_output_format;
   return dst;
 }
 
@@ -687,6 +688,8 @@ access_update(access_t *a, access_entry_t *ae)
     else
       a->aa_rights |= ae->ae_rights;
   }
+
+  a->aa_xmltv_output_format = ae->ae_xmltv_output_format;
 }
 
 /**
@@ -1412,6 +1415,18 @@ access_entry_conn_limit_type_enum ( void *p, const char *lang )
   return strtab2htsmsg(conn_limit_type_tab, 1, lang);
 }
 
+static htsmsg_t *
+access_entry_xmltv_output_format_enum ( void *p, const char *lang )
+{
+  static struct strtab
+  xmltv_output_format_tab[] = {
+    { N_("All"),                           ACCESS_XMLTV_OUTPUT_FORMAT_ALL },
+    { N_("Basic"),                         ACCESS_XMLTV_OUTPUT_FORMAT_BASIC },
+    { N_("Basic Alternative (No Hash)"),   ACCESS_XMLTV_OUTPUT_FORMAT_BASIC_NO_HASH },
+  };
+  return strtab2htsmsg(xmltv_output_format_tab, 1, lang);
+}
+
 htsmsg_t *
 language_get_list ( void *obj, const char *lang )
 {
@@ -1656,6 +1671,7 @@ PROP_DOC(connection_limit)
 PROP_DOC(persistent_viewlevel)
 PROP_DOC(streaming_profile)
 PROP_DOC(change_parameters)
+PROP_DOC(xmltv_output_format)
 
 const idclass_t access_entry_class = {
   .ic_class      = "access",
@@ -1903,6 +1919,16 @@ const idclass_t access_entry_class = {
       .rend     = access_entry_chtag_rend,
       .opts     = PO_ADVANCED,
     },
+    {
+      .type     = PT_INT,
+      .id       = "xmltv_output_format",
+      .name     = N_("Format for xmltv output"),
+      .desc     = N_("Specify format for xmltv output."),
+      .doc      = prop_doc_xmltv_output_format,
+      .off      = offsetof(access_entry_t, ae_xmltv_output_format),
+      .list     = access_entry_xmltv_output_format_enum,
+      .opts     = PO_ADVANCED | PO_DOC_NLIST,
+    },
     {
       .type     = PT_STR,
       .id       = "comment",
index 9f84c7fdc7651f6a654594f55f39b914ac52df4e..545ebec6fb63decf1370a1869d25f7f260dcaa4c 100644 (file)
@@ -94,6 +94,12 @@ enum {
   ACCESS_CONN_LIMIT_TYPE_DVR,
 };
 
+enum {
+  ACCESS_XMLTV_OUTPUT_FORMAT_ALL = 0,
+  ACCESS_XMLTV_OUTPUT_FORMAT_BASIC,
+  ACCESS_XMLTV_OUTPUT_FORMAT_BASIC_NO_HASH,
+};
+
 typedef struct access_entry {
   idnode_t ae_id;
 
@@ -124,6 +130,7 @@ typedef struct access_entry {
   int ae_conn_limit_type;
   uint32_t ae_conn_limit;
   int ae_change_conn_limit;
+  int ae_xmltv_output_format;
 
   int ae_dvr;
   int ae_htsp_dvr;
@@ -171,6 +178,7 @@ typedef struct access {
   htsmsg_t *aa_chtags;
   int       aa_match;
   uint32_t  aa_conn_limit;
+  uint32_t  aa_xmltv_output_format;
   uint32_t  aa_conn_limit_streaming;
   uint32_t  aa_conn_limit_dvr;
   uint32_t  aa_conn_streaming;
index bed8d54f2d1e829a4a530881c04c647920af73eb..68c1a928abc61c97d8ad1e57ce84de7aa946c021 100644 (file)
@@ -15,7 +15,7 @@ tvheadend.acleditor = function(panel, index)
                 'streaming,profile,conn_limit_type,conn_limit,' +
                 'dvr,htsp_anonymize,dvr_config,' +
                 'channel_min,channel_max,channel_tag_exclude,' +
-                'channel_tag,comment';
+                'channel_tag,xmltv_output_format,comment';
 
     tvheadend.idnode_grid(panel, {
         id: 'access_entry',
index 149d582e7dcd04ceb9ae2b88e33a89ca251fcbef..ebb050202e23614096817eefee6eb073e8e42f91 100644 (file)
@@ -62,18 +62,46 @@ http_xmltv_end(htsbuf_queue_t *hq)
   htsbuf_append_str(hq, "</tv>\n");
 }
 
+
+/** Determine name to use for the channel based on the
+ * user's settings. This is done because some TVs have
+ * broken parsers that require a "user readable" name
+ * for the channel.
+ *
+ * @param buf Buffer that is used if we return an idnode.
+ *
+ * @return Buffer containing the name. This might not
+ * be the same as the passed in temporary buffer.
+ *
+ */
+static const char *
+http_xmltv_channel_get_name(const http_connection_t *hc,
+                            const channel_t *ch,
+                            char *buf,
+                            size_t buf_len)
+{
+  const int of = hc->hc_access->aa_xmltv_output_format;
+
+  if (of == ACCESS_XMLTV_OUTPUT_FORMAT_BASIC_NO_HASH)
+    return channel_get_name(ch, idnode_uuid_as_str(&ch->ch_id, buf));
+  else
+    return idnode_uuid_as_str(&ch->ch_id, buf);
+}
+
+
 /*
  *
  */
 static void
-http_xmltv_channel_add(htsbuf_queue_t *hq, int flags, const char *hostpath, channel_t *ch)
+http_xmltv_channel_add(http_connection_t *hc, htsbuf_queue_t *hq, int flags, const char *hostpath, channel_t *ch)
 {
   const char *icon = channel_get_icon(ch);
   char ubuf[UUID_HEX_SIZE];
   const char *tag;
   int64_t lcn;
-  htsbuf_qprintf(hq, "<channel id=\"%s\">\n  <display-name>",
-                 idnode_uuid_as_str(&ch->ch_id, ubuf));
+  htsbuf_qprintf(hq, "<channel id=\"");
+  htsbuf_append_and_escape_xml(hq, http_xmltv_channel_get_name(hc, ch, ubuf, sizeof ubuf));
+  htsbuf_qprintf(hq, "\">\n  <display-name>");
   htsbuf_append_and_escape_xml(hq, channel_get_name(ch, ""));
   htsbuf_append_str(hq, "</display-name>\n");
   lcn = channel_get_number(ch);
@@ -133,35 +161,28 @@ _http_xmltv_add_episode_num(htsbuf_queue_t *hq, uint16_t num, uint16_t cnt)
   }
 }
 
-/*
- *
+/** Output long description fields of the programme which are
+ * not output for basic/limited devices.
  */
 static void
-http_xmltv_programme_one(htsbuf_queue_t *hq, const char *hostpath,
-                         channel_t *ch, epg_broadcast_t *ebc)
+http_xmltv_programme_one_long(const http_connection_t *hc,
+                              htsbuf_queue_t *hq, const char *hostpath,
+                              const channel_t *ch, const epg_broadcast_t *ebc)
 {
-  epg_episode_num_t epnum;
-  char start[32], stop[32], ubuf[UUID_HEX_SIZE];
   lang_str_ele_t *lse;
   epg_genre_t *genre;
   char buf[64];
 
-  if (ebc->title == NULL) return;
-  http_xmltv_time(start, ebc->start);
-  http_xmltv_time(stop, ebc->stop);
-  htsbuf_qprintf(hq, "<programme start=\"%s\" stop=\"%s\" channel=\"%s\">\n",
-                 start, stop, idnode_uuid_as_str(&ch->ch_id, ubuf));
-  RB_FOREACH(lse, ebc->title, link) {
-    htsbuf_qprintf(hq, "  <title lang=\"%s\">", lse->lang);
-    htsbuf_append_and_escape_xml(hq, lse->str);
-    htsbuf_append_str(hq, "</title>\n");
-  }
   if (ebc->subtitle)
     RB_FOREACH(lse, ebc->subtitle, link) {
-      htsbuf_qprintf(hq, "  <sub-title lang=\"%s\">", lse->lang);
-      htsbuf_append_and_escape_xml(hq, lse->str);
-      htsbuf_append_str(hq, "</sub-title>\n");
+      /* Ignore empty sub-titles */
+      if (!strempty(lse->str)) {
+          htsbuf_qprintf(hq, "  <sub-title lang=\"%s\">", lse->lang);
+          htsbuf_append_and_escape_xml(hq, lse->str);
+          htsbuf_append_str(hq, "</sub-title>\n");
+        }
     }
+
   if (ebc->description)
     RB_FOREACH(lse, ebc->description, link) {
       htsbuf_qprintf(hq, "  <desc lang=\"%s\">", lse->lang);
@@ -195,6 +216,41 @@ http_xmltv_programme_one(htsbuf_queue_t *hq, const char *hostpath,
     }
   }
   _http_xmltv_programme_write_string_list(hq, ebc->keyword, "keyword");
+}
+
+/*
+ *
+ */
+static void
+http_xmltv_programme_one(const http_connection_t *hc,
+                         htsbuf_queue_t *hq, const char *hostpath,
+                         const channel_t *ch, const epg_broadcast_t *ebc)
+{
+  epg_episode_num_t epnum;
+  char start[32], stop[32], ubuf[UUID_HEX_SIZE];
+  lang_str_ele_t *lse;
+  const int of = hc->hc_access->aa_xmltv_output_format;
+
+  if (ebc->title == NULL) return;
+  http_xmltv_time(start, ebc->start);
+  http_xmltv_time(stop, ebc->stop);
+  htsbuf_qprintf(hq, "<programme start=\"%s\" stop=\"%s\" channel=\"",
+                 start, stop);
+  htsbuf_append_and_escape_xml(hq, http_xmltv_channel_get_name(hc, ch, ubuf, sizeof ubuf));
+  htsbuf_qprintf(hq, "\">\n");
+  RB_FOREACH(lse, ebc->title, link) {
+    htsbuf_qprintf(hq, "  <title lang=\"%s\">", lse->lang);
+    htsbuf_append_and_escape_xml(hq, lse->str);
+    htsbuf_append_str(hq, "</title>\n");
+  }
+
+  /* Basic formats are for low-memory devices that
+   * only want very basic information.
+   */
+  if (of != ACCESS_XMLTV_OUTPUT_FORMAT_BASIC &&
+      of != ACCESS_XMLTV_OUTPUT_FORMAT_BASIC_NO_HASH) {
+    http_xmltv_programme_one_long(hc, hq, hostpath, ch, ebc);
+  }
 
   /* We can't use epg_broadcast_epnumber_format since we need a specific
    * format whereas that can return an arbitrary text string.
@@ -216,12 +272,12 @@ http_xmltv_programme_one(htsbuf_queue_t *hq, const char *hostpath,
  *
  */
 static void
-http_xmltv_programme_add(htsbuf_queue_t *hq, const char *hostpath, channel_t *ch)
+http_xmltv_programme_add(const http_connection_t *hc, htsbuf_queue_t *hq, const char *hostpath, channel_t *ch)
 {
   epg_broadcast_t *ebc;
 
   RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link)
-    http_xmltv_programme_one(hq, hostpath, ch, ebc);
+    http_xmltv_programme_one(hc, hq, hostpath, ch, ebc);
 }
 
 /**
@@ -237,8 +293,8 @@ http_xmltv_channel(http_connection_t *hc, int flags, channel_t *channel)
 
   http_get_hostpath(hc, hostpath, sizeof(hostpath));
   http_xmltv_begin(&hc->hc_reply);
-  http_xmltv_channel_add(&hc->hc_reply, flags, hostpath, channel);
-  http_xmltv_programme_add(&hc->hc_reply, hostpath, channel);
+  http_xmltv_channel_add(hc, &hc->hc_reply, flags, hostpath, channel);
+  http_xmltv_programme_add(hc, &hc->hc_reply, hostpath, channel);
   http_xmltv_end(&hc->hc_reply);
   return 0;
 }
@@ -264,13 +320,13 @@ http_xmltv_tag(http_connection_t *hc, int flags, channel_tag_t *tag)
     ch = (channel_t *)ilm->ilm_in2;
     if (http_access_verify_channel(hc, ACCESS_STREAMING, ch))
       continue;
-    http_xmltv_channel_add(&hc->hc_reply, flags, hostpath, ch);
+    http_xmltv_channel_add(hc, &hc->hc_reply, flags, hostpath, ch);
   }
   LIST_FOREACH(ilm, &tag->ct_ctms, ilm_in1_link) {
     ch = (channel_t *)ilm->ilm_in2;
     if (http_access_verify_channel(hc, ACCESS_STREAMING, ch))
       continue;
-    http_xmltv_programme_add(&hc->hc_reply, hostpath, ch);
+    http_xmltv_programme_add(hc, &hc->hc_reply, hostpath, ch);
   }
   http_xmltv_end(&hc->hc_reply);
 
@@ -295,12 +351,12 @@ http_xmltv_channel_list(http_connection_t *hc, int flags)
   CHANNEL_FOREACH(ch) {
     if (http_access_verify_channel(hc, ACCESS_STREAMING, ch))
       continue;
-    http_xmltv_channel_add(&hc->hc_reply, flags, hostpath, ch);
+    http_xmltv_channel_add(hc, &hc->hc_reply, flags, hostpath, ch);
   }
   CHANNEL_FOREACH(ch) {
     if (http_access_verify_channel(hc, ACCESS_STREAMING, ch))
       continue;
-    http_xmltv_programme_add(&hc->hc_reply, hostpath, ch);
+    http_xmltv_programme_add(hc, &hc->hc_reply, hostpath, ch);
   }
   http_xmltv_end(&hc->hc_reply);