diff --git a/docs/html/config_dvr.html b/docs/html/config_dvr.html
index fd82ab2a..1b5a652b 100644
--- a/docs/html/config_dvr.html
+++ b/docs/html/config_dvr.html
@@ -52,6 +52,10 @@
Extra time after recordings (minutes)
Specify the number of minutes to record after the events scheduled stop time. Used to cope with small scheduling errors.
+ Episode duplicate detection
+ If checked, broadcasts with matching title and matching non-zero episode number
+ are considered duplicates.
+
Post-processor command
Command to run after finishing a recording. The command will be run in background and is executed even if a recording is aborted or an error occurred. Use the %e error formatting string to check for errors, the error string is empty if recording finished successfully.
diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h
index fac68f1b..71bc1631 100644
--- a/src/dvr/dvr.h
+++ b/src/dvr/dvr.h
@@ -71,6 +71,7 @@ extern struct dvr_entry_list dvrentries;
#define DVR_SKIP_COMMERCIALS 0x400
#define DVR_SUBTITLE_IN_TITLE 0x800
#define DVR_EPISODE_BEFORE_DATE 0x1000
+#define DVR_EPISODE_DUPLICATE_DETECTION 0x2000
typedef enum {
DVR_PRIO_IMPORTANT,
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index 81350813..5c3f6d6a 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -453,11 +453,40 @@ dvr_entry_create_by_event(const char *config_name,
creator, dae, pri);
}
+/**
+ *
+ */
static int _dvr_duplicate_event ( epg_broadcast_t *e )
{
dvr_entry_t *de;
+ epg_episode_num_t empty_epnum;
+ int has_epnum = 1;
+
+ /* 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;
+
LIST_FOREACH(de, &dvrentries, de_global_link) {
- if (de->de_bcast && (de->de_bcast->episode == e->episode)) return 1;
+ if (de->de_bcast) {
+ if (de->de_bcast->episode == e->episode) return 1;
+
+ if (has_epnum) {
+ dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
+ int ep_dup_det = (cfg->dvr_flags & DVR_EPISODE_DUPLICATE_DETECTION);
+
+ 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);
+
+ /* 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;
+ }
+ }
+ }
+ }
}
return 0;
}
@@ -1193,6 +1222,9 @@ dvr_init(void)
if(!htsmsg_get_u32(m, "episode-before-date", &u32) && u32)
cfg->dvr_flags |= DVR_EPISODE_BEFORE_DATE;
+ if(!htsmsg_get_u32(m, "episode-duplicate-detection", &u32) && u32)
+ cfg->dvr_flags |= DVR_EPISODE_DUPLICATE_DETECTION;
+
dvr_charset_update(cfg, htsmsg_get_str(m, "charset"));
tvh_str_set(&cfg->dvr_postproc, htsmsg_get_str(m, "postproc"));
@@ -1420,6 +1452,7 @@ dvr_save(dvr_config_t *cfg)
htsmsg_add_u32(m, "skip-commercials", !!(cfg->dvr_flags & DVR_SKIP_COMMERCIALS));
htsmsg_add_u32(m, "subtitle-in-title", !!(cfg->dvr_flags & DVR_SUBTITLE_IN_TITLE));
htsmsg_add_u32(m, "episode-before-date", !!(cfg->dvr_flags & DVR_EPISODE_BEFORE_DATE));
+ htsmsg_add_u32(m, "episode-duplicate-detection", !!(cfg->dvr_flags & DVR_EPISODE_DUPLICATE_DETECTION));
if (cfg->dvr_charset != NULL)
htsmsg_add_str(m, "charset", cfg->dvr_charset);
if (cfg->dvr_postproc != NULL)
diff --git a/src/webui/extjs.c b/src/webui/extjs.c
index fa6d9d05..ea35b550 100755
--- a/src/webui/extjs.c
+++ b/src/webui/extjs.c
@@ -1160,6 +1160,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
htsmsg_add_u32(r, "commSkip", !!(cfg->dvr_flags & DVR_SKIP_COMMERCIALS));
htsmsg_add_u32(r, "subtitleInTitle", !!(cfg->dvr_flags & DVR_SUBTITLE_IN_TITLE));
htsmsg_add_u32(r, "episodeBeforeDate", !!(cfg->dvr_flags & DVR_EPISODE_BEFORE_DATE));
+ htsmsg_add_u32(r, "episodeDuplicateDetection", !!(cfg->dvr_flags & DVR_EPISODE_DUPLICATE_DETECTION));
out = json_single_record(r, "dvrSettings");
@@ -1233,6 +1234,8 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
flags |= DVR_SUBTITLE_IN_TITLE;
if(http_arg_get(&hc->hc_req_args, "episodeBeforeDate") != NULL)
flags |= DVR_EPISODE_BEFORE_DATE;
+ if(http_arg_get(&hc->hc_req_args, "episodeDuplicateDetection") != NULL)
+ flags |= DVR_EPISODE_DUPLICATE_DETECTION;
dvr_flags_set(cfg,flags);
diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js
index 10fbcf8b..cdc8f43c 100644
--- a/src/webui/static/app/dvr.js
+++ b/src/webui/static/app/dvr.js
@@ -799,7 +799,7 @@ tvheadend.dvrsettings = function() {
'channelInTitle', 'container', 'cache', 'charset', 'dateInTitle', 'timeInTitle',
'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs',
'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip', 'subtitleInTitle',
- 'episodeBeforeDate', 'rewritePAT', 'rewritePMT']);
+ 'episodeBeforeDate', 'rewritePAT', 'rewritePMT', 'episodeDuplicateDetection']);
var confcombo = new Ext.form.ComboBox({
store: tvheadend.configNames,
@@ -925,6 +925,11 @@ tvheadend.dvrsettings = function() {
name: 'commSkip'
});
+ var episodeDuplicateDetection = new Ext.form.Checkbox({
+ fieldLabel: 'Episode Duplicate Detect',
+ name: 'episodeDuplicateDetection'
+ });
+
/* Subdirectories and filename handling */
/* NB: directoryPermissions is defined as a TextField for validation purposes (leading zeros), but is ultimately a number */
@@ -1015,7 +1020,7 @@ tvheadend.dvrsettings = function() {
autoHeight: true,
collapsible: true,
animCollapse: true,
- items: [recordingPath, recordingPermissions, charset, PATrewrite, PMTrewrite, tagMetadata, skipCommercials]
+ items: [recordingPath, recordingPermissions, charset, PATrewrite, PMTrewrite, tagMetadata, skipCommercials, episodeDuplicateDetection]
});
/* Sub-Panel - Directory operations */