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
`%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
`%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
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) */
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,
}
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) ||
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);
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)
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);
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,
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;
}
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;
( 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);
}
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;
}
}
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++;
}
"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;
}
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)
{
.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",
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)
{
*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
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
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. */
{ .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 },
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 },
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);
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;
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;
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();
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;
}
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"/>';
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',
}
},
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: {
disp_subtitle: {
renderer: tvheadend.displayWithDuplicateRenderer()
},
+ disp_summary: {
+ renderer: tvheadend.displayWithDuplicateRenderer()
+ },
filesize: {
renderer: tvheadend.filesizeRenderer()
},
}
},
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: {
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: {
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: {