From 2b22bc75a1eca5131f19342cf81d04167ae463a3 Mon Sep 17 00:00:00 2001 From: dero Date: Thu, 5 Mar 2015 21:13:51 +0100 Subject: [PATCH] - better duplicate handling - better subtitle handling --- src/dvr/dvr.h | 22 +++- src/dvr/dvr_autorec.c | 24 +++- src/dvr/dvr_db.c | 241 ++++++++++++++++++++++++++++++------ src/dvr/dvr_timerec.c | 2 +- src/epg.c | 2 +- src/epg.h | 4 + src/htsp_server.c | 13 +- src/main.c | 2 +- src/webui/static/app/dvr.js | 32 ++--- 9 files changed, 275 insertions(+), 67 deletions(-) diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 85b30c72..dccf590d 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -163,6 +163,7 @@ typedef struct dvr_entry { char *de_directory; /* Can be set for autorec entries, will override any 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_desc; /* Description in UTF-8 (from EPG) */ uint32_t de_content_type; /* Content type (from EPG) (only code) */ @@ -244,6 +245,16 @@ typedef struct dvr_entry { #define DVR_CH_NAME(e) ((e)->de_channel == NULL ? (e)->de_channel_name : channel_get_name((e)->de_channel)) +typedef enum { + DVR_AUTOREC_RECORD_ALL = 0, + DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER = 1, + DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE = 2, + DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION = 3, + DVR_AUTOREC_RECORD_ONCE_PER_WEEK = 4, + DVR_AUTOREC_RECORD_ONCE_PER_DAY = 5 +} dvr_autorec_dedup_t; + + /** * Autorec entry */ @@ -291,9 +302,12 @@ typedef struct dvr_autorec_entry { int dae_minduration; int dae_maxduration; int dae_retention; - + time_t dae_start_extra; time_t dae_stop_extra; + + int dae_record; + } dvr_autorec_entry_t; TAILQ_HEAD(dvr_autorec_entry_queue, dvr_autorec_entry); @@ -432,7 +446,7 @@ dvr_entry_t * dvr_entry_create_htsp( const char *dvr_config_uuid, channel_t *ch, time_t start, time_t stop, time_t start_extra, time_t stop_extra, - const char *title, const char *description, + const char *title, const char* subtitle, const char *description, const char *lang, epg_genre_t *content_type, const char *owner, const char *creator, dvr_autorec_entry_t *dae, @@ -441,7 +455,7 @@ dvr_entry_create_htsp( const char *dvr_config_uuid, dvr_entry_t * dvr_entry_update( dvr_entry_t *de, - const char* de_title, const char *de_desc, const char *lang, + const char* de_title, const char* de_subtitle, const char *de_desc, const char *lang, time_t de_start, time_t de_stop, time_t de_start_extra, time_t de_stop_extra, dvr_prio_t pri, int retention ); @@ -506,7 +520,7 @@ dvr_entry_t * dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e, channel_t *ch, time_t start, time_t stop, time_t start_extra, time_t stop_extra, - const char *title, const char *description, + const char *title, const char* subtitle, const char *description, const char *lang, epg_genre_t *content_type, const char *owner, const char *creator, dvr_autorec_entry_t *dae, dvr_timerec_entry_t *tae, diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index e9fddbf1..a5d1f4d5 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -1,6 +1,6 @@ /* * tvheadend, Automatic recordings - * Copyright (C) 2010 Andreas Öman + * Copyright (C) 2010 Andreas �man * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -867,6 +867,20 @@ dvr_autorec_entry_class_content_type_list(void *o) return m; } +static htsmsg_t * +dvr_autorec_entry_class_dedup_list ( void *o ) +{ + static const struct strtab tab[] = { + { "Record all", DVR_AUTOREC_RECORD_ALL }, + { "Record if different episode number", DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER }, + { "Record if different subtitle", DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE }, + { "Record if different description", DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION }, + { "Record once per week", DVR_AUTOREC_RECORD_ONCE_PER_WEEK }, + { "Record once per day", DVR_AUTOREC_RECORD_ONCE_PER_DAY }, + }; + return strtab2htsmsg(tab); +} + const idclass_t dvr_autorec_entry_class = { .ic_class = "dvrautorec", .ic_caption = "DVR Auto-Record Entry", @@ -999,6 +1013,14 @@ const idclass_t dvr_autorec_entry_class = { .def.i = DVR_PRIO_NORMAL, .off = offsetof(dvr_autorec_entry_t, dae_pri), }, + { + .type = PT_U32, + .id = "record", + .name = "Duplicate Handling", + .def.i = DVR_AUTOREC_RECORD_ALL, + .off = offsetof(dvr_autorec_entry_t, dae_record), + .list = dvr_autorec_entry_class_dedup_list, + }, { .type = PT_INT, .id = "retention", diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index e1f5a70e..07139a2d 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -44,6 +44,7 @@ static void dvr_timer_expire(void *aux); static void dvr_timer_start_recording(void *aux); static void dvr_timer_stop_recording(void *aux); static int dvr_entry_class_disp_title_set(void *o, const void *v); +static int dvr_entry_class_disp_subtitle_set(void *o, const void *v); /* * Start / stop time calculators @@ -261,10 +262,9 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de) ".", "S%02d", NULL, "E%02d", NULL); } - if (cfg->dvr_subtitle_in_title) { - if(de->de_bcast && de->de_bcast->episode && de->de_bcast->episode->subtitle) + if (cfg->dvr_subtitle_in_title && de->de_subtitle) { snprintf(output + strlen(output), outlen - strlen(output), - ".%s", lang_str_get(de->de_bcast->episode->subtitle, NULL)); + ".%s", lang_str_get(de->de_subtitle, NULL)); } localtime_r(&de->de_start, &tm); @@ -306,7 +306,7 @@ dvr_entry_set_timer(dvr_entry_t *de) de->de_sched_state = DVR_MISSED_TIME; else _dvr_entry_completed(de); - gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, + gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, de->de_stop + dvr_entry_get_retention(de) * 86400); } else if (de->de_sched_state == DVR_RECORDING) { @@ -429,6 +429,11 @@ dvr_entry_create(const char *uuid, htsmsg_t *conf) (s = htsmsg_get_str(conf, "disp_title")) != NULL) dvr_entry_class_disp_title_set(de, s); + /* special case, becaous PO_NOSAVE, load ignores it */ + if (de->de_subtitle == NULL && + (s = htsmsg_get_str(conf, "disp_subtitle")) != NULL) + dvr_entry_class_disp_subtitle_set(de, s); + de->de_refcnt = 1; LIST_INSERT_HEAD(&dvrentries, de, de_global_link); @@ -456,7 +461,7 @@ dvr_entry_t * dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e, channel_t *ch, time_t start, time_t stop, time_t start_extra, time_t stop_extra, - const char *title, const char *description, + const char *title, const char* subtitle, const char *description, const char *lang, epg_genre_t *content_type, const char *owner, const char *creator, dvr_autorec_entry_t *dae, @@ -487,6 +492,8 @@ dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e, htsmsg_add_u32(conf, "dvb_eid", e->dvb_eid); if (e->episode && e->episode->title) lang_str_serialize(e->episode->title, conf, "title"); + if (e->episode && e->episode->subtitle) + lang_str_serialize(e->episode->subtitle, conf, "subtitle"); if (e->description) lang_str_serialize(e->description, conf, "description"); else if (e->episode && e->episode->description) @@ -508,6 +515,12 @@ dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e, lang_str_serialize(l, conf, "description"); lang_str_destroy(l); } + if (subtitle) { + l = lang_str_create(); + lang_str_add(l, subtitle, lang, 0); + lang_str_serialize(l, conf, "subtitle"); + lang_str_destroy(l); + } } if (content_type) htsmsg_add_u32(conf, "content_type", content_type->code / 16); @@ -552,7 +565,7 @@ dvr_entry_t * dvr_entry_create_htsp(const char *config_uuid, channel_t *ch, time_t start, time_t stop, time_t start_extra, time_t stop_extra, - const char *title, + const char *title, const char* subtitle, const char *description, const char *lang, epg_genre_t *content_type, const char *owner, @@ -566,7 +579,7 @@ dvr_entry_create_htsp(const char *config_uuid, return dvr_entry_create_(cfg ? idnode_uuid_as_str(&cfg->dvr_id) : NULL, NULL, ch, start, stop, start_extra, stop_extra, - title, description, lang, content_type, + title, subtitle, description, lang, content_type, owner, creator, dae, NULL, pri, retention, comment); } @@ -589,47 +602,109 @@ dvr_entry_create_by_event(const char *config_uuid, return dvr_entry_create_(config_uuid, e, e->channel, e->start, e->stop, start_extra, stop_extra, - NULL, NULL, NULL, + NULL, NULL, NULL, NULL, LIST_FIRST(&e->episode->genre), owner, creator, dae, NULL, pri, retention, comment); } +static inline int strempty(const char* c) { + return !c || c[0] == 0; +} + +static inline int lang_str_empty(lang_str_t* str) { + return strempty(lang_str_get(str, NULL)); +} + + /** * */ -static int _dvr_duplicate_event ( epg_broadcast_t *e ) +static dvr_entry_t* _dvr_duplicate_event(dvr_entry_t* de) { - dvr_entry_t *de; - epg_episode_num_t empty_epnum; - int has_epnum = 1; + if (!de->de_autorec) + return NULL; - /* skip episode duplicate check below if no episode number */ - memset(&empty_epnum, 0, sizeof(empty_epnum)); - if (epg_episode_number_cmp(&empty_epnum, &e->episode->epnum) == 0) - has_epnum = 0; + int record = de->de_autorec->dae_record; - LIST_FOREACH(de, &dvrentries, de_global_link) { - if (de->de_bcast) { - if (de->de_bcast->episode == e->episode) return 1; + struct tm de_start; + localtime_r(&de->de_start, &de_start); - if (has_epnum) { - int ep_dup_det = de->de_config->dvr_episode_duplicate; + switch (record) { + case DVR_AUTOREC_RECORD_ALL: + return NULL; + case DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER: + if (strempty(de->de_episode)) + return NULL; + break; + case DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE: + if (lang_str_empty(de->de_subtitle)) + return NULL; + break; + case DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION: + if (lang_str_empty(de->de_desc)) + return NULL; + break; + case DVR_AUTOREC_RECORD_ONCE_PER_WEEK: + de_start.tm_mday -= (de_start.tm_wday + 6) % 7; // week = mon-sun + mktime(&de_start); // adjusts de_start + break; + } - if (ep_dup_det) { - const char* de_title = lang_str_get(de->de_bcast->episode->title, NULL); - const char* e_title = lang_str_get(e->episode->title, NULL); + // title not defined, can't be deduped + if (lang_str_empty(de->de_title)) + return NULL; - /* duplicate if title and episode match */ - if (de_title && e_title && strcmp(de_title, e_title) == 0 - && epg_episode_number_cmp(&de->de_bcast->episode->epnum, &e->episode->epnum) == 0) { - return 1; - } - } + dvr_entry_t *de2; + + LIST_FOREACH(de2, &dvrentries, de_global_link) { + if (de == de2) + continue; + + // only earlier recordings qualify as master + if (de2->de_start > de->de_start) + continue; + + // only successful earlier recordings qualify as master + if (de2->de_sched_state == DVR_MISSED_TIME || (de2->de_sched_state == DVR_COMPLETED && de2->de_last_error != SM_CODE_OK)) + continue; + + // if titles are not defined or do not match, don't dedup + if (lang_str_compare(de->de_title, de2->de_title)) + continue; + + switch (record) { + case DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER: + if (!strcmp(de->de_episode, de2->de_episode)) + return de2; + break; + case DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE: + if (!lang_str_compare(de->de_subtitle, de2->de_subtitle)) + return de2; + break; + case DVR_AUTOREC_RECORD_DIFFERENT_DESCRIPTION: + if (!lang_str_compare(de->de_desc, de2->de_desc)) + return de2; + break; + case DVR_AUTOREC_RECORD_ONCE_PER_WEEK: { + struct tm de2_start; + localtime_r(&de2->de_start, &de2_start); + de2_start.tm_mday -= (de2_start.tm_wday + 6) % 7; // week = mon-sun + mktime(&de2_start); // adjusts de2_start + if (de_start.tm_year == de2_start.tm_year && de_start.tm_yday == de2_start.tm_yday) + return de2; + break; + } + case DVR_AUTOREC_RECORD_ONCE_PER_DAY: { + struct tm de2_start; + localtime_r(&de2->de_start, &de2_start); + if (de_start.tm_year == de2_start.tm_year && de_start.tm_yday == de2_start.tm_yday) + return de2; + break; } } } - return 0; + return NULL; } /** @@ -641,7 +716,11 @@ dvr_entry_create_by_autorec(epg_broadcast_t *e, dvr_autorec_entry_t *dae) char buf[200]; /* Dup detection */ - if (_dvr_duplicate_event(e)) return; + dvr_entry_t* de; + LIST_FOREACH(de, &dvrentries, de_global_link) { + if (de->de_bcast == e || (de->de_bcast && de->de_bcast->episode == e->episode)) + return; + } snprintf(buf, sizeof(buf), "Auto recording%s%s", dae->dae_creator ? " by: " : "", @@ -651,6 +730,8 @@ dvr_entry_create_by_autorec(epg_broadcast_t *e, dvr_autorec_entry_t *dae) dae->dae_start_extra, dae->dae_stop_extra, dae->dae_owner, buf, dae, dae->dae_pri, dae->dae_retention, dae->dae_comment); + + } /** @@ -684,6 +765,7 @@ 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_bcast) de->de_bcast->putref((epg_object_t*)de->de_bcast); free(de->de_channel_name); @@ -769,7 +851,7 @@ dvr_timer_expire(void *aux) } static dvr_entry_t *_dvr_entry_update - ( dvr_entry_t *de, epg_broadcast_t *e, const char *title, + ( dvr_entry_t *de, epg_broadcast_t *e, const char *title, const char* subtitle, 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 ) { @@ -819,7 +901,13 @@ static dvr_entry_t *_dvr_entry_update if (!de->de_title) de->de_title = lang_str_create(); save = lang_str_add(de->de_title, title, lang, 1); } - + + /* Subtitle*/ + if (subtitle) { + if (!de->de_subtitle) de->de_subtitle = lang_str_create(); + save = lang_str_add(de->de_subtitle, subtitle, lang, 1); + } + /* EID */ if (e && e->dvb_eid != de->de_dvb_eid) { de->de_dvb_eid = e->dvb_eid; @@ -873,12 +961,12 @@ static dvr_entry_t *_dvr_entry_update dvr_entry_t * dvr_entry_update (dvr_entry_t *de, - const char* de_title, const char *de_desc, const char *lang, + const char* de_title, const char* de_subtitle, const char *de_desc, const char *lang, time_t de_start, time_t de_stop, time_t de_start_extra, time_t de_stop_extra, dvr_prio_t pri, int retention) { - return _dvr_entry_update(de, NULL, de_title, de_desc, lang, + return _dvr_entry_update(de, NULL, de_title, de_subtitle, de_desc, lang, de_start, de_stop, de_start_extra, de_stop_extra, pri, retention); } @@ -930,7 +1018,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e) e->start, e->stop); e->getref(e); de->de_bcast = e; - _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0); + _dvr_entry_update(de, e, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0); break; } } @@ -943,7 +1031,7 @@ void dvr_event_updated ( epg_broadcast_t *e ) dvr_entry_t *de; de = dvr_entry_find_by_event(e); if (de) - _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0); + _dvr_entry_update(de, e, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0); else { LIST_FOREACH(de, &dvrentries, de_global_link) { if (de->de_sched_state != DVR_SCHEDULED) continue; @@ -959,7 +1047,7 @@ void dvr_event_updated ( epg_broadcast_t *e ) e->start, e->stop); e->getref(e); de->de_bcast = e; - _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0); + _dvr_entry_update(de, e, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0); break; } } @@ -1020,6 +1108,12 @@ dvr_timer_start_recording(void *aux) return; } + // if duplicate, then delete it now, don't record! + if (_dvr_duplicate_event(de)) { + dvr_entry_cancel_delete(de); + return; + } + de->de_sched_state = DVR_RECORDING; de->de_rec_state = DVR_RS_PENDING; @@ -1539,13 +1633,46 @@ dvr_entry_class_disp_title_get(void *o) return &s; } + +static int +dvr_entry_class_disp_subtitle_set(void *o, const void *v) +{ + dvr_entry_t *de = (dvr_entry_t *)o; + const char *s = ""; + if (v == NULL || *((char *)v) == '\0') + v = "UnknownSubtitle"; + if (de->de_subtitle) + s = lang_str_get(de->de_subtitle, NULL); + if (strcmp(s, v)) { + lang_str_destroy(de->de_subtitle); + de->de_subtitle = lang_str_create(); + lang_str_add(de->de_subtitle, v, NULL, 0); + return 1; + } + return 0; +} + +static const void * +dvr_entry_class_disp_subtitle_get(void *o) +{ + dvr_entry_t *de = (dvr_entry_t *)o; + static const char *s; + s = ""; + if (de->de_subtitle) { + s = lang_str_get(de->de_subtitle, NULL); + if (s == NULL) + s = ""; + } + return &s; +} + static const void * dvr_entry_class_disp_description_get(void *o) { dvr_entry_t *de = (dvr_entry_t *)o; static const char *s; s = ""; - if (de->de_title) { + if (de->de_desc) { s = lang_str_get(de->de_desc, NULL); if (s == NULL) s = ""; @@ -1654,6 +1781,16 @@ dvr_entry_class_channel_icon_url_get(void *o) return &s; } +static const void * +dvr_entry_class_duplicate_get(void *o) +{ + static time_t null = 0; + dvr_entry_t *de = (dvr_entry_t *)o; + de = _dvr_duplicate_event(de); + return de ? &de->de_start : &null; +} + + htsmsg_t * dvr_entry_class_duration_list(void *o, const char *not_set, int max, int step) { @@ -1803,6 +1940,21 @@ const idclass_t dvr_entry_class = { .set = dvr_entry_class_disp_title_set, .opts = PO_NOSAVE, }, + { + .type = PT_LANGSTR, + .id = "subtitle", + .name = "Subtitle", + .off = offsetof(dvr_entry_t, de_subtitle), + .opts = PO_RDONLY, + }, + { + .type = PT_STR, + .id = "disp_subtitle", + .name = "Subtitle", + .get = dvr_entry_class_disp_subtitle_get, + .set = dvr_entry_class_disp_subtitle_set, + .opts = PO_NOSAVE, + }, { .type = PT_LANGSTR, .id = "description", @@ -1984,6 +2136,13 @@ const idclass_t dvr_entry_class = { .get = dvr_entry_class_sched_status_get, .opts = PO_RDONLY | PO_NOSAVE | PO_HIDDEN, }, + { + .type = PT_TIME, + .id = "duplicate", + .name = "Rerun of", + .get = dvr_entry_class_duplicate_get, + .opts = PO_RDONLY | PO_NOSAVE, + }, { .type = PT_STR, .id = "comment", @@ -2038,7 +2197,7 @@ static struct strtab priotab[] = { { "high", DVR_PRIO_HIGH }, { "normal", DVR_PRIO_NORMAL }, { "low", DVR_PRIO_LOW }, - { "unimportant", DVR_PRIO_UNIMPORTANT }, + { "unimportant", DVR_PRIO_UNIMPORTANT } }; dvr_prio_t diff --git a/src/dvr/dvr_timerec.c b/src/dvr/dvr_timerec.c index 20c80e7f..8d54d893 100644 --- a/src/dvr/dvr_timerec.c +++ b/src/dvr/dvr_timerec.c @@ -155,7 +155,7 @@ dvr_timerec_check(dvr_timerec_entry_t *dte) dte->dte_creator ?: ""); de = dvr_entry_create_(idnode_uuid_as_str(&dte->dte_config->dvr_id), NULL, dte->dte_channel, - start, stop, 0, 0, title, + start, stop, 0, 0, title, NULL, NULL, NULL, NULL, dte->dte_owner, buf, NULL, dte, dte->dte_pri, dte->dte_retention, dte->dte_comment); diff --git a/src/epg.c b/src/epg.c index 95e596f7..a000ae09 100644 --- a/src/epg.c +++ b/src/epg.c @@ -1289,7 +1289,7 @@ const char *epg_episode_get_subtitle return lang_str_get(e->subtitle, lang); } -const char *epg_episode_get_summary +const char *epg_episode_get_summary ( const epg_episode_t *e, const char *lang ) { if (!e || !e->summary) return NULL; diff --git a/src/epg.h b/src/epg.h index a93e64b6..63b443f5 100644 --- a/src/epg.h +++ b/src/epg.h @@ -290,6 +290,8 @@ const char *epg_episode_get_title ( const epg_episode_t *e, const char *lang ); const char *epg_episode_get_subtitle ( const epg_episode_t *e, const char *lang ); +lang_str_t *epg_episode_get_subtitle2 + ( const epg_episode_t *e ); const char *epg_episode_get_summary ( const epg_episode_t *e, const char *lang ); const char *epg_episode_get_description @@ -511,6 +513,8 @@ const char *epg_broadcast_get_title ( epg_broadcast_t *b, const char *lang ); const char *epg_broadcast_get_subtitle ( epg_broadcast_t *b, const char *lang ); +lang_str_t *epg_broadcast_get_subtitle2 + ( epg_broadcast_t *b ); const char *epg_broadcast_get_summary ( epg_broadcast_t *b, const char *lang ); const char *epg_broadcast_get_description diff --git a/src/htsp_server.c b/src/htsp_server.c index f61b9235..a4ce59fd 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1413,7 +1413,7 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in) epg_broadcast_t *e = NULL; dvr_entry_t *de; dvr_entry_sched_state_t dvr_status; - const char *dvr_config_name, *title, *desc, *creator, *lang, *comment; + const char *dvr_config_name, *title, *desc, *subtitle, *creator, *lang, *comment; int64_t start, stop, start_extra, stop_extra; uint32_t u32, priority, retention; channel_t *ch = NULL; @@ -1454,6 +1454,10 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in) !(title = htsmsg_get_str(in, "title"))) return htsp_error("Invalid arguments"); + /* Optional attributes */ + if (!(subtitle = htsmsg_get_str(in, "subtitle"))) + subtitle = ""; + /* Optional attributes */ if (!(desc = htsmsg_get_str(in, "description"))) desc = ""; @@ -1461,7 +1465,7 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in) // create the dvr entry de = dvr_entry_create_htsp(dvr_config_name, ch, start, stop, start_extra, stop_extra, - title, desc, lang, 0, + title, subtitle, desc, lang, 0, htsp->htsp_granted_access->aa_username, creator, NULL, priority, retention, comment); @@ -1506,7 +1510,7 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in) uint32_t dvrEntryId; dvr_entry_t *de; time_t start, stop, start_extra, stop_extra, priority, retention; - const char *title, *desc, *lang; + const char *title, *subtitle, *desc, *lang; if(htsmsg_get_u32(in, "id", &dvrEntryId)) return htsp_error("Missing argument 'id'"); @@ -1528,11 +1532,12 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in) retention = htsmsg_get_u32_or_default(in, "retention", 0); priority = htsmsg_get_u32_or_default(in, "priority", DVR_PRIO_NORMAL); title = htsmsg_get_str(in, "title"); + subtitle = htsmsg_get_str(in, "title"); desc = htsmsg_get_str(in, "description"); lang = htsmsg_get_str(in, "language"); if (!lang) lang = htsp->htsp_language; - de = dvr_entry_update(de, title, desc, lang, start, stop, + de = dvr_entry_update(de, title, subtitle, desc, lang, start, stop, start_extra, stop_extra, priority, retention); //create response diff --git a/src/main.c b/src/main.c index ad066fab..bb8d5625 100644 --- a/src/main.c +++ b/src/main.c @@ -799,7 +799,7 @@ main(int argc, char **argv) OPENSSL_config(NULL); SSL_load_error_strings(); SSL_library_init(); - + /* Initialise configuration */ uuid_init(); idnode_init(); diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index 0a69f073..0d19c447 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -11,14 +11,15 @@ tvheadend.dvrDetails = function(uuid) { var params = d[0].params; var chicon = params[0].value; var title = params[1].value; - var episode = params[2].value; - var start_real = params[3].value; - var stop_real = params[4].value; - var duration = params[5].value; - var desc = params[6].value; - var status = params[7].value; - var filesize = params[8].value; - var comment = params[9].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 content = ''; var but; @@ -27,6 +28,8 @@ tvheadend.dvrDetails = function(uuid) { if (title) content += '
' + title + '
'; + if (subtitle) + content += '
' + subtitle + '
'; if (episode) content += '
' + episode + '
'; if (start_real) @@ -64,7 +67,7 @@ tvheadend.dvrDetails = function(uuid) { url: 'api/idnode/load', params: { uuid: uuid, - list: 'channel_icon,disp_title,episode,start_real,stop_real,' + + list: 'channel_icon,disp_title,disp_subtitle,episode,start_real,stop_real,' + 'duration,disp_description,status,filesize,comment' }, success: function(d) { @@ -214,7 +217,7 @@ tvheadend.dvr_upcoming = function(panel, index) { } }, del: true, - list: 'disp_title,episode,pri,start_real,stop_real,' + + list: 'duplicate,disp_title,disp_subtitle,episode,pri,start_real,stop_real,' + 'duration,filesize,channel,owner,creator,config_name,' + 'sched_status,errors,data_errors,comment', columns: { @@ -282,7 +285,7 @@ tvheadend.dvr_finished = function(panel, index) { del: true, delquestion: 'Do you really want to delete the selected recordings?

' + 'The associated file will be removed from the storage.', - list: 'disp_title,episode,start_real,stop_real,' + + list: 'disp_title,disp_subtitle,episode,start_real,stop_real,' + 'duration,filesize,channelname,owner,creator,' + 'config_name,sched_status,errors,data_errors,url,comment', columns: { @@ -362,7 +365,7 @@ tvheadend.dvr_failed = function(panel, index) { del: true, delquestion: 'Do you really want to delete the selected recordings?

' + 'The associated file will be removed from the storage.', - list: 'disp_title,episode,start_real,stop_real,' + + list: 'disp_title,disp_subtitle,episode,start_real,stop_real,' + 'duration,filesize,channelname,owner,creator,config_name,' + 'status,sched_status,errors,data_errors,url,comment', columns: { @@ -457,6 +460,7 @@ tvheadend.autorec_editor = function(panel, index) { renderer: function(st) { return tvheadend.weekdaysRenderer(st); } }, pri: { width: 80 }, + dedup: { width: 160 }, retention: { width: 80 }, config_name: { width: 120 }, owner: { width: 100 }, @@ -467,13 +471,13 @@ tvheadend.autorec_editor = function(panel, index) { url: 'api/dvr/autorec', params: { list: 'enabled,name,directory,title,fulltext,channel,tag,content_type,minduration,' + - 'maxduration,weekdays,start,start_window,pri,config_name,comment' + 'maxduration,weekdays,start,start_window,pri,dedup,config_name,comment' }, create: { } }, del: true, list: 'enabled,name,directory,title,fulltext,channel,tag,content_type,minduration,' + - 'maxduration,weekdays,start,start_window,pri,config_name,owner,creator,comment', + 'maxduration,weekdays,start,start_window,pri,dedup,config_name,owner,creator,comment', sort: { field: 'name', direction: 'ASC'