From: E.Smith <31170571+azlm8t@users.noreply.github.com> Date: Wed, 1 May 2019 11:37:32 +0000 (+0100) Subject: xmltv: Allow sending basic xmltv format, fixes #5630 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dca55a1d393686c9ab1619f3c2e891685d40d428;p=thirdparty%2Ftvheadend.git xmltv: Allow sending basic xmltv format, fixes #5630 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. --- diff --git a/docs/property/xmltv_output_format.md b/docs/property/xmltv_output_format.md new file mode 100644 index 000000000..fb905bad1 --- /dev/null +++ b/docs/property/xmltv_output_format.md @@ -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). diff --git a/src/access.c b/src/access.c index 96bc3616d..1d8138338 100644 --- a/src/access.c +++ b/src/access.c @@ -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", diff --git a/src/access.h b/src/access.h index 9f84c7fdc..545ebec6f 100644 --- a/src/access.h +++ b/src/access.h @@ -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; diff --git a/src/webui/static/app/acleditor.js b/src/webui/static/app/acleditor.js index bed8d54f2..68c1a928a 100644 --- a/src/webui/static/app/acleditor.js +++ b/src/webui/static/app/acleditor.js @@ -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', diff --git a/src/webui/xmltv.c b/src/webui/xmltv.c index 149d582e7..ebb050202 100644 --- a/src/webui/xmltv.c +++ b/src/webui/xmltv.c @@ -62,18 +62,46 @@ http_xmltv_end(htsbuf_queue_t *hq) htsbuf_append_str(hq, "\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, "\n ", - idnode_uuid_as_str(&ch->ch_id, ubuf)); + htsbuf_qprintf(hq, "\n "); htsbuf_append_and_escape_xml(hq, channel_get_name(ch, "")); htsbuf_append_str(hq, "\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, "\n", - start, stop, idnode_uuid_as_str(&ch->ch_id, ubuf)); - RB_FOREACH(lse, ebc->title, link) { - htsbuf_qprintf(hq, " ", lse->lang); - htsbuf_append_and_escape_xml(hq, lse->str); - htsbuf_append_str(hq, "\n"); - } if (ebc->subtitle) RB_FOREACH(lse, ebc->subtitle, link) { - htsbuf_qprintf(hq, " ", lse->lang); - htsbuf_append_and_escape_xml(hq, lse->str); - htsbuf_append_str(hq, "\n"); + /* Ignore empty sub-titles */ + if (!strempty(lse->str)) { + htsbuf_qprintf(hq, " ", lse->lang); + htsbuf_append_and_escape_xml(hq, lse->str); + htsbuf_append_str(hq, "\n"); + } } + if (ebc->description) RB_FOREACH(lse, ebc->description, link) { htsbuf_qprintf(hq, " ", 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, "\n"); + RB_FOREACH(lse, ebc->title, link) { + htsbuf_qprintf(hq, " ", lse->lang); + htsbuf_append_and_escape_xml(hq, lse->str); + htsbuf_append_str(hq, "\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);