]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
DVR: add code for the summary field, fixes #4816
authorJaroslav Kysela <perex@perex.cz>
Fri, 12 Jan 2018 09:11:15 +0000 (10:11 +0100)
committerJaroslav Kysela <perex@perex.cz>
Fri, 12 Jan 2018 09:11:22 +0000 (10:11 +0100)
docs/property/pathname.md
docs/property/postprocessor.md
docs/property/preprocessor.md
src/dvr/dvr.h
src/dvr/dvr_autorec.c
src/dvr/dvr_db.c
src/dvr/dvr_rec.c
src/htsp_server.c
src/webui/static/app/dvr.js

index be6940dec11ebe22d7eae5a5ad61b61564762efb..2cfea0ab8dc4d941489d5316e3c96e21c3857b4c 100644 (file)
@@ -6,8 +6,10 @@
 Format    | Description                                      | Example
 :--------:|--------------------------------------------------|--------
 `$t$n.$x` | Default format (title, unique number, extension) | Tennis - Wimbledon-1.mkv
-`$s`      | Event subtitle name                              | Sport
 `$t`      | Event title name                                 | Tennis - Wimbledon
+`$s`      | Event subtitle name or summary text              | Live Tennis Broadcast from Wimbledon
+`$u`      | Event subtitle name                              | Tennis
+`$m`      | Event summary text                               | Live Tennis Broadcast from Wimbledon
 `$e`      | Event episode name                               | S02-E06
 `$c`      | Channel name                                     | SkySport
 `$g`      | Content type                                     | Movie : Science fiction
index e41621081c4f75f6ed16df5a5434b70d02777749..a1b2b98d63556b15f29d7c7ca32f441fe0d8a74b 100644 (file)
@@ -14,7 +14,9 @@ Format | Description                               | Example value
 `%O`   | Owner of this recording                   |  user
 `%C`   | Who created this recording                |  user
 `%t`   | Program title                             |  News
-`%s`   | Program subtitle                          |  Afternoon
+`%s`   | Program subtitle or summary               |  Afternoon fast news
+`%u`   | Program subtitle                          |  Afternoon
+`%m`   | Program summary                           |  Afternoon fast news
 `%p`   | Program episode                           |  S02.E07
 `%d`   | Program description                       |  News and stories…
 `%g`   | Program content type                      |  Current affairs
index 13463944f53273739bd83e2c14f52affc9718aa7..aea2a582e3a41654bd2e4b55dcf8cde8809cc143 100644 (file)
@@ -12,7 +12,9 @@ Format | Description                               | Example value
 `%O`   | Owner of this recording                   |  user
 `%C`   | Who created this recording                |  user
 `%t`   | Program title                             |  News
-`%s`   | Program subtitle                          |  Afternoon
+`%s`   | Program subtitle or summary               |  Afternoon fast news
+`%u`   | Program subtitle                          |  Afternoon
+`%m`   | Program summary                           |  Afternoon fast news
 `%p`   | Program episode                           |  S02.E07
 `%d`   | Program description                       |  News and stories…
 `%S`   | Start time stamp of recording, UNIX epoch |  1224421200
index 33a54b4d7d3213d82a047119b212ffa610888cd5..412e2585e49aa29bb938bda072d89ec50ddd2193 100644 (file)
@@ -212,6 +212,7 @@ typedef struct dvr_entry {
                          directory setting from the configuration */
   lang_str_t *de_title;      /* Title in UTF-8 (from EPG) */
   lang_str_t *de_subtitle;   /* Subtitle in UTF-8 (from EPG) */
+  lang_str_t *de_summary;    /* Summary in UTF-8 (from EPG) */
   lang_str_t *de_desc;       /* Description in UTF-8 (from EPG) */
   uint32_t de_content_type;  /* Content type (from EPG) (only code) */
   uint16_t de_copyright_year; /* Copyright year (from EPG) */
@@ -555,7 +556,7 @@ dvr_entry_t *
 dvr_entry_update( dvr_entry_t *de, int enabled,
                   const char *dvr_config_uuid, channel_t *ch,
                   const char *title, const char *subtitle,
-                  const char *desc, const char *lang,
+                  const char *summary, const char *desc, const char *lang,
                   time_t start, time_t stop,
                   time_t start_extra, time_t stop_extra,
                   dvr_prio_t pri, int retention, int removal,
index 986e4cf71f1539952c9ca36989b1cb94750b2b39..1c79782598f71e4930c752945f84f918b70a8b17 100644 (file)
@@ -218,11 +218,14 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
   }
 
   if (e->category) {
-    if (dae->dae_cat1 && *dae->dae_cat1 && !string_list_contains_string(e->category, dae->dae_cat1))
+    if (dae->dae_cat1 && *dae->dae_cat1 &&
+        !string_list_contains_string(e->category, dae->dae_cat1))
       return 0;
-    if (dae->dae_cat2 && *dae->dae_cat2 && !string_list_contains_string(e->category, dae->dae_cat2))
+    if (dae->dae_cat2 && *dae->dae_cat2 &&
+        !string_list_contains_string(e->category, dae->dae_cat2))
       return 0;
-    if (dae->dae_cat3 && *dae->dae_cat3 && !string_list_contains_string(e->category, dae->dae_cat3))
+    if (dae->dae_cat3 && *dae->dae_cat3 &&
+        !string_list_contains_string(e->category, dae->dae_cat3))
       return 0;
   } else if ((dae->dae_cat1 && *dae->dae_cat1) ||
              (dae->dae_cat2 && *dae->dae_cat2) ||
index 203f1b0dea9871e0e8f1e762fbb440aca7e2cd3e..d52453a4f2fe522e32893d7c80939d1d5440b7a9 100644 (file)
@@ -963,6 +963,7 @@ dvr_entry_create_from_htsmsg(htsmsg_t *conf, epg_broadcast_t *e)
   time_t t;
   char ubuf[UUID_HEX_SIZE];
   epg_genre_t *genre;
+  int summary_used = 0;
 
   if (e) {
     htsmsg_add_u32(conf, "broadcast", e->id);
@@ -980,10 +981,15 @@ dvr_entry_create_from_htsmsg(htsmsg_t *conf, epg_broadcast_t *e)
       lang_str_serialize(e->description, conf, "description");
     else if (e->episode && e->episode->description)
       lang_str_serialize(e->episode->description, conf, "description");
-    else if (e->summary)
+    else if (e->summary) {
       lang_str_serialize(e->summary, conf, "description");
-    else if (e->episode && e->episode->summary)
+      summary_used = 1;
+    } else if (e->episode && e->episode->summary)
       lang_str_serialize(e->episode->summary, conf, "description");
+    if (!summary_used && e->summary)
+      lang_str_serialize(e->summary, conf, "summary");
+    else if (e->episode && e->episode->summary)
+      lang_str_serialize(e->episode->summary, conf, "summary");
     if (e->episode && (s = dvr_entry_get_episode(e, tbuf, sizeof(tbuf))))
       htsmsg_add_str(conf, "episode", s);
     if (e->episode && e->episode->copyright_year)
@@ -1731,8 +1737,9 @@ dvr_entry_dec_ref(dvr_entry_t *de)
   free(de->de_creator);
   free(de->de_comment);
   if (de->de_title) lang_str_destroy(de->de_title);
-  if (de->de_subtitle)  lang_str_destroy(de->de_subtitle);
-  if (de->de_desc)  lang_str_destroy(de->de_desc);
+  if (de->de_subtitle) lang_str_destroy(de->de_subtitle);
+  if (de->de_summary) lang_str_destroy(de->de_summary);
+  if (de->de_desc) lang_str_destroy(de->de_desc);
   dvr_entry_assign_broadcast(de, NULL);
   free(de->de_channel_name);
   free(de->de_episode);
@@ -1907,7 +1914,8 @@ static char *dvr_updated_str(char *buf, size_t buflen, int flags)
 static dvr_entry_t *_dvr_entry_update
   ( dvr_entry_t *de, int enabled, const char *dvr_config_uuid,
     epg_broadcast_t *e, channel_t *ch,
-    const char *title, const char *subtitle, const char *desc,
+    const char *title, const char *subtitle,
+    const char *summary, const char *desc,
     const char *lang, time_t start, time_t stop,
     time_t start_extra, time_t stop_extra,
     dvr_prio_t pri, int retention, int removal,
@@ -1970,6 +1978,9 @@ static dvr_entry_t *_dvr_entry_update
       if (subtitle) {
         save |= lang_str_set(&de->de_subtitle, subtitle, lang) ? DVR_UPDATED_SUBTITLE : 0;
       }
+      if (summary) {
+        save |= lang_str_set(&de->de_summary, summary, lang) ? DVR_UPDATED_SUMMARY : 0;
+      }
     }
     goto dosave;
   }
@@ -2038,6 +2049,13 @@ static dvr_entry_t *_dvr_entry_update
     save |= lang_str_set(&de->de_subtitle, subtitle, lang) ? DVR_UPDATED_SUBTITLE : 0;
   }
 
+  /* Summary */
+  if (e && e->episode && e->episode->summary) {
+    save |= lang_str_set2(&de->de_summary, e->episode->summary) ? DVR_UPDATED_SUMMARY : 0;
+  } else if (summary) {
+    save |= lang_str_set(&de->de_summary, summary, lang) ? DVR_UPDATED_SUMMARY : 0;
+  }
+
   /* EID */
   if (e && e->dvb_eid != de->de_dvb_eid) {
     de->de_dvb_eid = e->dvb_eid;
@@ -2109,13 +2127,13 @@ dvr_entry_update
   ( dvr_entry_t *de, int enabled,
     const char *dvr_config_uuid, channel_t *ch,
     const char *title, const char *subtitle,
-    const char *desc, const char *lang,
+    const char *summary, const char *desc, const char *lang,
     time_t start, time_t stop,
     time_t start_extra, time_t stop_extra,
     dvr_prio_t pri, int retention, int removal, int playcount, int playposition )
 {
   return _dvr_entry_update(de, enabled, dvr_config_uuid,
-                           NULL, ch, title, subtitle, desc, lang,
+                           NULL, ch, title, subtitle, summary, desc, lang,
                            start, stop, start_extra, stop_extra,
                            pri, retention, removal, playcount, playposition);
 }
@@ -2171,7 +2189,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e)
                           gmtime2local(e2->start, t1buf, sizeof(t1buf)),
                           gmtime2local(e2->stop, t2buf, sizeof(t2buf)));
           _dvr_entry_update(de, -1, NULL, e2, NULL, NULL, NULL, NULL, NULL,
-                            0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1);
+                            NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1);
           return;
         }
       }
@@ -2211,7 +2229,7 @@ void dvr_event_updated(epg_broadcast_t *e)
     return;
   LIST_FOREACH(de, &e->dvr_entries, de_bcast_link) {
     assert(de->de_bcast == e);
-    _dvr_entry_update(de, -1, NULL, e, NULL, NULL, NULL, NULL,
+    _dvr_entry_update(de, -1, NULL, e, NULL, NULL, NULL, NULL, NULL,
                       NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1);
     found++;
   }
@@ -2226,7 +2244,7 @@ void dvr_event_updated(epg_broadcast_t *e)
                             "link to event %s on %s",
                             epg_broadcast_get_title(e, NULL),
                             channel_get_name(e->channel, channel_blank_name));
-      _dvr_entry_update(de, -1, NULL, e, NULL, NULL, NULL, NULL,
+      _dvr_entry_update(de, -1, NULL, e, NULL, NULL, NULL, NULL, NULL,
                         NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1);
       break;
     }
@@ -3126,6 +3144,35 @@ dvr_entry_class_disp_subtitle_get(void *o)
   return &prop_ptr;
 }
 
+static int
+dvr_entry_class_disp_summary_set(void *o, const void *v)
+{
+  dvr_entry_t *de = (dvr_entry_t *)o;
+  const char *lang = idnode_lang(o);
+  const char *s = "";
+  v = tvh_str_default(v, "UnknownSummary");
+  if (de->de_subtitle)
+    s = lang_str_get(de->de_summary, lang);
+  if (strcmp(s, v)) {
+    lang_str_set(&de->de_summary, v, lang);
+    return 1;
+  }
+  return 0;
+}
+
+static const void *
+dvr_entry_class_disp_summary_get(void *o)
+{
+  dvr_entry_t *de = (dvr_entry_t *)o;
+  if (de->de_summary)
+    prop_ptr = lang_str_get(de->de_summary, idnode_lang(o));
+  else
+    prop_ptr = NULL;
+  if (prop_ptr == NULL)
+    prop_ptr = "";
+  return &prop_ptr;
+}
+
 static const void *
 dvr_entry_class_disp_description_get(void *o)
 {
@@ -3547,6 +3594,23 @@ const idclass_t dvr_entry_class = {
       .set      = dvr_entry_class_disp_subtitle_set,
       .opts     = PO_NOSAVE,
     },
+    {
+      .type     = PT_LANGSTR,
+      .id       = "summary",
+      .name     = N_("Summary"),
+      .desc     = N_("Summary of the program (if any)."),
+      .off      = offsetof(dvr_entry_t, de_summary),
+      .opts     = PO_RDONLY,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "disp_summary",
+      .name     = N_("Summary"),
+      .desc     = N_("Summary of the program (if any) (display only)."),
+      .get      = dvr_entry_class_disp_summary_get,
+      .set      = dvr_entry_class_disp_summary_set,
+      .opts     = PO_NOSAVE,
+    },
     {
       .type     = PT_LANGSTR,
       .id       = "description",
index 96ec2943f4a12a0b6b3403cf1dc0979e12a90321..ad05a3678b4ff1271664f7764be762d4e6372752 100644 (file)
@@ -330,12 +330,27 @@ dvr_sub_title(const char *id, const char *fmt, const void *aux, char *tmp, size_
   return dvr_do_prefix(id, fmt, lang_str_get(((dvr_entry_t *)aux)->de_title, NULL), tmp, tmplen);
 }
 
+static const char *
+dvr_sub_subtitle_or_summary(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
+{
+  const dvr_entry_t *de = aux;
+  const char *s = lang_str_get(de->de_subtitle, NULL);
+  if (s == NULL) s = lang_str_get(de->de_summary, NULL);
+  return dvr_do_prefix(id, fmt, s, tmp, tmplen);
+}
+
 static const char *
 dvr_sub_subtitle(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
 {
   return dvr_do_prefix(id, fmt, lang_str_get(((dvr_entry_t *)aux)->de_subtitle, NULL), tmp, tmplen);
 }
 
+static const char *
+dvr_sub_summary(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
+{
+  return dvr_do_prefix(id, fmt, lang_str_get(((dvr_entry_t *)aux)->de_summary, NULL), tmp, tmplen);
+}
+
 static const char *
 dvr_sub_description(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
 {
@@ -368,8 +383,19 @@ _dvr_sub_scraper_friendly(const char *id, const char *fmt, const void *aux, char
   *tmp = 0;
   const char *title    = lang_str_get(de->de_title, NULL);
   const char *subtitle = lang_str_get(de->de_subtitle, NULL);
+  const char *summary  = lang_str_get(de->de_summary, NULL);
   const char *desc     = lang_str_get(de->de_desc, NULL);
 
+  /* Pick summary as subtitle if appropriate */
+  if (summary) {
+    if (subtitle == NULL) {
+      subtitle = summary;
+    } else if (strlen(subtitle) < strlen(summary)) {
+      if (strlen(subtitle) < 10)
+        subtitle = summary;
+    }
+  }
+
   if (subtitle && desc && strcmp(subtitle, desc) == 0) {
     /* Subtitle and description are identical so assume they are from
      * bad OTA EIT.  Some OTA EIT often has a (long) summary which is
@@ -379,16 +405,16 @@ _dvr_sub_scraper_friendly(const char *id, const char *fmt, const void *aux, char
     subtitle = NULL;
   }
 
-  char title_buf[512] = { 0 };
-  char subtitle_buf[512] = { 0 };
+  char *title_buf = title ? alloca(strlen(title) + 1) : NULL;
+  char *subtitle_buf = subtitle ? alloca(strlen(subtitle) + 1) : NULL;
   /* Copy a cleaned version in to our buffers.
    * Since dvr_clean_directory_separator _can_ modify source if source!=dest
    * it means we have to remove our const when we call it.
    */
   if (title)
-    dvr_clean_directory_separator((char*)title,    title_buf,    sizeof title_buf);
+    dvr_clean_directory_separator((char*)title,    title_buf,    sizeof(title_buf));
   if (subtitle)
-    dvr_clean_directory_separator((char*)subtitle, subtitle_buf, sizeof subtitle_buf);
+    dvr_clean_directory_separator((char*)subtitle, subtitle_buf, sizeof(subtitle_buf));
 
   int is_movie = 0;
   /* Override options on the format tag. This is useful because my OTA
@@ -402,9 +428,7 @@ _dvr_sub_scraper_friendly(const char *id, const char *fmt, const void *aux, char
     if (de->de_bcast && de->de_bcast->category) {
       /* We've parsed categories from xmltv. So check if it has the movie category. */
       is_movie =
-        string_list_contains_string(de->de_bcast->category, "Movie") ||
         string_list_contains_string(de->de_bcast->category, "movie") ||
-        string_list_contains_string(de->de_bcast->category, "Film") ||
         string_list_contains_string(de->de_bcast->category, "film");
     } else {
       /* No xmltv categories parsed. So have to use less-accurate genre instead. */
@@ -625,13 +649,27 @@ static htsstr_substitute_t dvr_subs_entry[] = {
   { .id = "?.t", .getval = dvr_sub_title },
   { .id = "?,t", .getval = dvr_sub_title },
   { .id = "?;t", .getval = dvr_sub_title },
-  { .id = "?s",  .getval = dvr_sub_subtitle },
-  { .id = "? s", .getval = dvr_sub_subtitle },
-  { .id = "?-s", .getval = dvr_sub_subtitle },
-  { .id = "?_s", .getval = dvr_sub_subtitle },
-  { .id = "?.s", .getval = dvr_sub_subtitle },
-  { .id = "?,s", .getval = dvr_sub_subtitle },
-  { .id = "?;s", .getval = dvr_sub_subtitle },
+  { .id = "?s",  .getval = dvr_sub_subtitle_or_summary },
+  { .id = "? s", .getval = dvr_sub_subtitle_or_summary },
+  { .id = "?-s", .getval = dvr_sub_subtitle_or_summary },
+  { .id = "?_s", .getval = dvr_sub_subtitle_or_summary },
+  { .id = "?.s", .getval = dvr_sub_subtitle_or_summary },
+  { .id = "?,s", .getval = dvr_sub_subtitle_or_summary },
+  { .id = "?;s", .getval = dvr_sub_subtitle_or_summary },
+  { .id = "?u",  .getval = dvr_sub_subtitle },
+  { .id = "? u", .getval = dvr_sub_subtitle },
+  { .id = "?-u", .getval = dvr_sub_subtitle },
+  { .id = "?_u", .getval = dvr_sub_subtitle },
+  { .id = "?.u", .getval = dvr_sub_subtitle },
+  { .id = "?,u", .getval = dvr_sub_subtitle },
+  { .id = "?;u", .getval = dvr_sub_subtitle },
+  { .id = "?m",  .getval = dvr_sub_summary },
+  { .id = "? m", .getval = dvr_sub_summary },
+  { .id = "?-m", .getval = dvr_sub_summary },
+  { .id = "?_m", .getval = dvr_sub_summary },
+  { .id = "?.m", .getval = dvr_sub_summary },
+  { .id = "?,m", .getval = dvr_sub_summary },
+  { .id = "?;m", .getval = dvr_sub_summary },
   { .id = "e",   .getval = dvr_sub_episode },
   { .id = " e",  .getval = dvr_sub_episode },
   { .id = "-e",  .getval = dvr_sub_episode },
@@ -765,7 +803,9 @@ static htsstr_substitute_t dvr_subs_tally[] = {
 
 static htsstr_substitute_t dvr_subs_postproc_entry[] = {
   { .id = "t",  .getval = dvr_sub_title },
-  { .id = "s",  .getval = dvr_sub_subtitle },
+  { .id = "s",  .getval = dvr_sub_subtitle_or_summary },
+  { .id = "u",  .getval = dvr_sub_subtitle },
+  { .id = "m",  .getval = dvr_sub_summary },
   { .id = "p",  .getval = dvr_sub_episode },
   { .id = "d",  .getval = dvr_sub_description },
   { .id = "g",  .getval = dvr_sub_genre },
index 991b74cadfa362abdb35acfd32cf1a5efc6a4690..12bb2e99e67fe46f080475ef3349af0884c5832f 100644 (file)
@@ -1955,6 +1955,9 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
     s = htsmsg_get_str(in, "subtitle");
     if (s)
       lang_str_serialize_one(conf, "subtitle", s, lang);
+    s = htsmsg_get_str(in, "summary");
+    if (s)
+      lang_str_serialize_one(conf, "summary", s, lang);
     s = htsmsg_get_str(in, "description");
     if (s)
       lang_str_serialize_one(conf, "description", s, lang);
@@ -2024,7 +2027,7 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   uint32_t u32;
   dvr_entry_t *de;
   time_t start, stop, start_extra, stop_extra, priority;
-  const char *dvr_config_name, *title, *subtitle, *desc, *lang;
+  const char *dvr_config_name, *title, *subtitle, *summary, *desc, *lang;
   channel_t *channel = NULL;
   int enabled, retention, removal, playcount = -1, playposition = -1;
 
@@ -2052,6 +2055,7 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   priority    = htsmsg_get_u32_or_default(in, "priority",   DVR_PRIO_NOTSET);
   title       = htsmsg_get_str(in, "title");
   subtitle    = htsmsg_get_str(in, "subtitle");
+  summary     = htsmsg_get_str(in, "summary");
   desc        = htsmsg_get_str(in, "description");
   lang        = htsmsg_get_str(in, "language") ?: htsp->htsp_language;
 
@@ -2080,7 +2084,7 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
     playposition = u32 > INT_MAX ? INT_MAX : u32;
 
   de = dvr_entry_update(de, enabled, dvr_config_name, channel, title, subtitle,
-                        desc, lang, start, stop, start_extra, stop_extra,
+                        summary, desc, lang, start, stop, start_extra, stop_extra,
                         priority, retention, removal, playcount, playposition);
 
   return htsp_success();
index c56f6526f156dcd000adc503c6b039f482e735a5..5f75f8e3a2331e911142290fac9d621e7a0f9b1f 100644 (file)
@@ -12,24 +12,25 @@ tvheadend.dvrDetails = function(uuid) {
         var chicon = params[0].value;
         var title = params[1].value;
         var subtitle = params[2].value;
-        var episode = params[3].value;
-        var start_real = params[4].value;
-        var stop_real = params[5].value;
-        var duration = params[6].value;
-        var desc = params[7].value;
-        var status = params[8].value;
-        var filesize = params[9].value;
-        var comment = params[10].value;
-        var duplicate = params[11].value;
-        var autorec_caption = params[12].value;
-        var timerec_caption = params[13].value;
-        var image = params[14].value;
-        var copyright_year = params[15].value;
-        var credits = params[16].value;
-        var keyword = params[17].value;
-        var category = params[18].value;
-        var first_aired = params[19].value;
-        var genre = params[20].value;
+        var summary = params[3].value;
+        var episode = params[4].value;
+        var start_real = params[5].value;
+        var stop_real = params[6].value;
+        var duration = params[7].value;
+        var desc = params[8].value;
+        var status = params[9].value;
+        var filesize = params[10].value;
+        var comment = params[11].value;
+        var duplicate = params[12].value;
+        var autorec_caption = params[13].value;
+        var timerec_caption = params[14].value;
+        var image = params[15].value;
+        var copyright_year = params[16].value;
+        var credits = params[17].value;
+        var keyword = params[18].value;
+        var category = params[19].value;
+        var first_aired = params[20].value;
+        var genre = params[21].value;
         var content = '';
         var but;
 
@@ -75,6 +76,8 @@ tvheadend.dvrDetails = function(uuid) {
         }
 
         content += '<hr class="x-epg-hr"/>';
+        if (summary && (!subtitle || subtitle != summary))
+            content += '<div class="x-epg-summary">' + summary + '</div>';
         if (desc) {
             content += '<div class="x-epg-desc">' + desc + '</div>';
             content += '<hr class="x-epg-hr"/>';
@@ -141,7 +144,7 @@ tvheadend.dvrDetails = function(uuid) {
         url: 'api/idnode/load',
         params: {
             uuid: uuid,
-            list: 'channel_icon,disp_title,disp_subtitle,episode,start_real,stop_real,' +
+            list: 'channel_icon,disp_title,disp_subtitle,disp_summary,episode,start_real,stop_real,' +
                   'duration,disp_description,status,filesize,comment,duplicate,' +
                   'autorec_caption,timerec_caption,image,copyright_year,credits,keyword,category,' +
                   'first_aired,genre',
@@ -416,10 +419,8 @@ tvheadend.dvr_upcoming = function(panel, index) {
             }
         },
         del: true,
-        list: 'category,enabled,duplicate,disp_title,disp_subtitle,episode,channel,' +
-              'image,' +
-              'copyright_year,' +
-              'start_real,stop_real,duration,pri,filesize,' +
+        list: 'category,enabled,duplicate,disp_title,disp_subtitle,disp_summary,episode,' +
+              'channel,image,copyright_year,start_real,stop_real,duration,pri,filesize,' +
               'sched_status,errors,data_errors,config_name,owner,creator,comment,genre',
         columns: {
             disp_title: {
@@ -428,6 +429,9 @@ tvheadend.dvr_upcoming = function(panel, index) {
             disp_subtitle: {
               renderer: tvheadend.displayWithDuplicateRenderer()
             },
+            disp_summary: {
+              renderer: tvheadend.displayWithDuplicateRenderer()
+            },
             filesize: {
                 renderer: tvheadend.filesizeRenderer()
             },
@@ -593,9 +597,8 @@ tvheadend.dvr_finished = function(panel, index) {
             }
         },
         del: false,
-        list: 'disp_title,disp_subtitle,episode,channelname,' +
-              'start_real,stop_real,duration,filesize,' +
-              'copyright_year,' +
+        list: 'disp_title,disp_subtitle,disp_summary,episode,channelname,' +
+              'start_real,stop_real,duration,filesize,copyright_year,' +
               'sched_status,errors,data_errors,playcount,url,config_name,owner,creator,comment,',
         columns: {
             disp_title: {
@@ -710,10 +713,8 @@ tvheadend.dvr_failed = function(panel, index) {
         del: true,
         delquestion: _('Do you really want to delete the selected recordings?') + '<br/><br/>' +
                      _('The associated file will be removed from storage.'),
-        list: 'disp_title,disp_subtitle,episode,channelname,' +
-              'image,' +
-              'copyright_year,' +
-              'start_real,stop_real,duration,filesize,status,' +
+        list: 'disp_title,disp_subtitle,disp_summary,episode,channelname,' +
+              'image,copyright_year,start_real,stop_real,duration,filesize,status,' +
               'sched_status,errors,data_errors,playcount,url,config_name,owner,creator,comment',
         columns: {
             disp_title: {
@@ -787,10 +788,8 @@ tvheadend.dvr_removed = function(panel, index) {
         uilevel: 'expert',
         edit: { params: { list: tvheadend.admin ? "retention,owner,comment" : "retention,comment" } },
         del: true,
-        list: 'disp_title,disp_subtitle,episode,channelname,' +
-              'image,' +
-              'copyright_year,' +
-              'start_real,stop_real,duration,status,' +
+        list: 'disp_title,disp_subtitle,disp_summary,episode,channelname,image,' +
+              'copyright_year,start_real,stop_real,duration,status,' +
               'sched_status,errors,data_errors,url,config_name,owner,creator,comment',
         columns: {
             disp_title: {