]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
implement minimal retention period
authorGlenn-1990 <g_christiaensen@msn.com>
Sun, 11 Sep 2016 18:18:41 +0000 (20:18 +0200)
committerJaroslav Kysela <perex@perex.cz>
Fri, 30 Sep 2016 13:50:12 +0000 (15:50 +0200)
src/api/api_dvr.c
src/dvr/dvr.h
src/dvr/dvr_autorec.c
src/dvr/dvr_config.c
src/dvr/dvr_db.c
src/dvr/dvr_timerec.c
src/dvr/dvr_vfsmgr.c
src/htsp_server.c
src/webui/simpleui.c

index be0b039e5950e4b65025b30e9f408f171ae9d42f..d7e3074e1575f19b8ecf11b39d00bf3feab7e6b4 100644 (file)
@@ -245,8 +245,8 @@ api_dvr_entry_create_by_event
                                        e, 0, 0,
                                        perm->aa_username,
                                        perm->aa_representative,
-                                       NULL, DVR_PRIO_NORMAL, DVR_RET_DVRCONFIG,
-                                       DVR_RET_DVRCONFIG, comment);
+                                       NULL, DVR_PRIO_NORMAL, DVR_RET_REM_DVRCONFIG,
+                                       DVR_RET_REM_DVRCONFIG, comment);
         if (de)
           idnode_changed(&de->de_id);
       }
index 8f120af46ac5d4c3829eb98db9150d8a2aa2acc3..3e6b1e3d144d0b81b9381eba3d8cf53d5e15a8c5 100644 (file)
@@ -54,6 +54,7 @@ typedef struct dvr_config {
   int dvr_clone;
   uint32_t dvr_rerecord_errors;
   uint32_t dvr_retention_days;
+  uint32_t dvr_retention_minimal;
   uint32_t dvr_removal_days;
   uint32_t dvr_autorec_max_count;
   uint32_t dvr_autorec_max_sched_count;
@@ -128,24 +129,25 @@ typedef enum {
 } dvr_rs_state_t;
 
 typedef enum {
-  DVR_RET_DVRCONFIG = 0,
-  DVR_RET_1DAY      = 1,
-  DVR_RET_3DAY      = 3,
-  DVR_RET_5DAY      = 5,
-  DVR_RET_1WEEK     = 7,
-  DVR_RET_2WEEK     = 14,
-  DVR_RET_3WEEK     = 21,
-  DVR_RET_1MONTH    = (30+1),
-  DVR_RET_2MONTH    = (60+2),
-  DVR_RET_3MONTH    = (90+2),
-  DVR_RET_6MONTH    = (180+3),
-  DVR_RET_1YEAR     = (365+1),
-  DVR_RET_2YEARS    = (2*365+1),
-  DVR_RET_3YEARS    = (3*365+1),
-  DVR_RET_ONREMOVE  = INT32_MAX-1, // for retention only
-  DVR_RET_SPACE     = INT32_MAX-1, // for removal only
-  DVR_RET_FOREVER   = INT32_MAX
-} dvr_retention_t;
+  DVR_RET_MIN_DISABLED  = 0, /* For dvr config minimal retention only */
+  DVR_RET_REM_DVRCONFIG = 0,
+  DVR_RET_REM_1DAY      = 1,
+  DVR_RET_REM_3DAY      = 3,
+  DVR_RET_REM_5DAY      = 5,
+  DVR_RET_REM_1WEEK     = 7,
+  DVR_RET_REM_2WEEK     = 14,
+  DVR_RET_REM_3WEEK     = 21,
+  DVR_RET_REM_1MONTH    = (30+1),
+  DVR_RET_REM_2MONTH    = (60+2),
+  DVR_RET_REM_3MONTH    = (90+2),
+  DVR_RET_REM_6MONTH    = (180+3),
+  DVR_RET_REM_1YEAR     = (365+1),
+  DVR_RET_REM_2YEARS    = (2*365+1),
+  DVR_RET_REM_3YEARS    = (3*365+1),
+  DVR_RET_ONREMOVE      = INT32_MAX-1, /* For retention only */
+  DVR_REM_SPACE         = INT32_MAX-1, /* For removal only */
+  DVR_RET_REM_FOREVER   = INT32_MAX
+} dvr_retention_removal_t;
 
 typedef struct dvr_entry {
 
@@ -205,6 +207,7 @@ typedef struct dvr_entry {
   int de_pri;
   int de_dont_reschedule;
   int de_dont_rerecord;
+  uint32_t de_file_removed;
   uint32_t de_retention;
   uint32_t de_removal;
 
@@ -450,7 +453,7 @@ void dvr_config_destroy_by_profile(profile_t *pro, int delconf);
 
 static inline uint32_t dvr_retention_cleanup(uint32_t val)
 {
-  return val > DVR_RET_FOREVER ? DVR_RET_FOREVER : val;
+  return val > DVR_RET_REM_FOREVER ? DVR_RET_REM_FOREVER : val;
 }
 
 /*
@@ -585,7 +588,9 @@ void dvr_entry_dec_ref(dvr_entry_t *de);
 
 int dvr_entry_delete(dvr_entry_t *de);
 
-void dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord);
+void dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord, int forcedestroy);
+
+void dvr_entry_trydestroy(dvr_entry_t *de);
 
 int dvr_entry_file_moved(const char *src, const char *dst);
 
index 66a3469fc5d785c0b79cd083cbffec1e760191ec..58889e30372893ce991afaa7793bc8e105926ce9 100644 (file)
@@ -125,7 +125,7 @@ dvr_autorec_completed(dvr_autorec_entry_t *dae, int error_code)
     if (de_prev) {
       tvhinfo(LS_DVR, "autorec %s removing recordings %s (allowed count %u total %u)",
               dae->dae_name, idnode_uuid_as_str(&de_prev->de_id, ubuf), max_count, total);
-      dvr_entry_cancel_delete(de_prev, 0);
+      dvr_entry_cancel_delete(de_prev, 0, 0);
     }
   }
 }
@@ -1189,7 +1189,7 @@ const idclass_t dvr_autorec_entry_class = {
       .id       = "retention",
       .name     = N_("DVR log retention"),
       .desc     = N_("Number of days to retain infomation about recording."),
-      .def.i    = DVR_RET_DVRCONFIG,
+      .def.i    = DVR_RET_REM_DVRCONFIG,
       .off      = offsetof(dvr_autorec_entry_t, dae_retention),
       .list     = dvr_entry_class_retention_list,
       .opts     = PO_HIDDEN | PO_EXPERT | PO_DOC_NLIST,
@@ -1199,7 +1199,7 @@ const idclass_t dvr_autorec_entry_class = {
       .id       = "removal",
       .name     = N_("DVR file retention period"),
       .desc     = N_("Number of days to keep the recorded file."),
-      .def.i    = DVR_RET_DVRCONFIG,
+      .def.i    = DVR_RET_REM_DVRCONFIG,
       .off      = offsetof(dvr_autorec_entry_t, dae_removal),
       .list     = dvr_entry_class_removal_list,
       .opts     = PO_HIDDEN | PO_ADVANCED | PO_DOC_NLIST,
@@ -1506,12 +1506,12 @@ uint32_t
 dvr_autorec_get_retention_days( dvr_autorec_entry_t *dae )
 {
   if (dae->dae_retention > 0) {
-    if (dae->dae_retention > DVR_RET_FOREVER)
-      return DVR_RET_FOREVER;
+    if (dae->dae_retention > DVR_RET_REM_FOREVER)
+      return DVR_RET_REM_FOREVER;
 
     uint32_t removal = dvr_autorec_get_removal_days(dae);
     /* As we need the db entry when deleting the file on disk */
-    if (removal != DVR_RET_FOREVER && removal > dae->dae_retention)
+    if (removal != DVR_RET_REM_FOREVER && removal > dae->dae_retention)
       return DVR_RET_ONREMOVE;
 
     return dae->dae_retention;
@@ -1526,8 +1526,8 @@ uint32_t
 dvr_autorec_get_removal_days( dvr_autorec_entry_t *dae )
 {
   if (dae->dae_removal > 0) {
-    if (dae->dae_removal > DVR_RET_FOREVER)
-      return DVR_RET_FOREVER;
+    if (dae->dae_removal > DVR_RET_REM_FOREVER)
+      return DVR_RET_REM_FOREVER;
 
     return dae->dae_removal;
   }
index 45e6f9087ea8c8d20bae2b85834924ccb487cb69..643710450a376ed45b0ff038e46a96135d6a8a35 100644 (file)
@@ -178,7 +178,8 @@ dvr_config_create(const char *name, const char *uuid, htsmsg_t *conf)
   cfg->dvr_enabled = 1;
   cfg->dvr_config_name = strdup(name);
   cfg->dvr_retention_days = DVR_RET_ONREMOVE;
-  cfg->dvr_removal_days = DVR_RET_FOREVER;
+  cfg->dvr_retention_minimal = DVR_RET_MIN_DISABLED;
+  cfg->dvr_removal_days = DVR_RET_REM_FOREVER;
   cfg->dvr_clone = 1;
   cfg->dvr_tag_files = 1;
   cfg->dvr_skip_commercials = 1;
@@ -531,13 +532,13 @@ dvr_config_changed(dvr_config_t *cfg)
   dvr_config_storage_check(cfg);
   if (cfg->dvr_cleanup_threshold_free < 50)
     cfg->dvr_cleanup_threshold_free = 50; // as checking is only periodically, lower is not save
-  if (cfg->dvr_removal_days != DVR_RET_FOREVER &&
+  if (cfg->dvr_removal_days != DVR_RET_REM_FOREVER &&
       cfg->dvr_removal_days > cfg->dvr_retention_days)
     cfg->dvr_retention_days = DVR_RET_ONREMOVE;
-  if (cfg->dvr_removal_days > DVR_RET_FOREVER)
-    cfg->dvr_removal_days = DVR_RET_FOREVER;
-  if (cfg->dvr_retention_days > DVR_RET_FOREVER)
-    cfg->dvr_retention_days = DVR_RET_FOREVER;
+  if (cfg->dvr_removal_days > DVR_RET_REM_FOREVER)
+    cfg->dvr_removal_days = DVR_RET_REM_FOREVER;
+  if (cfg->dvr_retention_days > DVR_RET_REM_FOREVER)
+    cfg->dvr_retention_days = DVR_RET_REM_FOREVER;
 }
 
 
@@ -719,21 +720,21 @@ static htsmsg_t *
 dvr_config_class_removal_list ( void *o, const char *lang )
 {
   static const struct strtab_u32 tab[] = {
-    { N_("1 day"),              DVR_RET_1DAY },
-    { N_("3 days"),             DVR_RET_3DAY },
-    { N_("5 days"),             DVR_RET_5DAY },
-    { N_("1 week"),             DVR_RET_1WEEK },
-    { N_("2 weeks"),            DVR_RET_2WEEK },
-    { N_("3 weeks"),            DVR_RET_3WEEK },
-    { N_("1 month"),            DVR_RET_1MONTH },
-    { N_("2 months"),           DVR_RET_2MONTH },
-    { N_("3 months"),           DVR_RET_3MONTH },
-    { N_("6 months"),           DVR_RET_6MONTH },
-    { N_("1 year"),             DVR_RET_1YEAR },
-    { N_("2 years"),            DVR_RET_2YEARS },
-    { N_("3 years"),            DVR_RET_3YEARS },
-    { N_("Maintained space"),   DVR_RET_SPACE },
-    { N_("Forever"),            DVR_RET_FOREVER },
+    { N_("1 day"),              DVR_RET_REM_1DAY },
+    { N_("3 days"),             DVR_RET_REM_3DAY },
+    { N_("5 days"),             DVR_RET_REM_5DAY },
+    { N_("1 week"),             DVR_RET_REM_1WEEK },
+    { N_("2 weeks"),            DVR_RET_REM_2WEEK },
+    { N_("3 weeks"),            DVR_RET_REM_3WEEK },
+    { N_("1 month"),            DVR_RET_REM_1MONTH },
+    { N_("2 months"),           DVR_RET_REM_2MONTH },
+    { N_("3 months"),           DVR_RET_REM_3MONTH },
+    { N_("6 months"),           DVR_RET_REM_6MONTH },
+    { N_("1 year"),             DVR_RET_REM_1YEAR },
+    { N_("2 years"),            DVR_RET_REM_2YEARS },
+    { N_("3 years"),            DVR_RET_REM_3YEARS },
+    { N_("Maintained space"),   DVR_REM_SPACE },
+    { N_("Forever"),            DVR_RET_REM_FOREVER },
   };
   return strtab2htsmsg_u32(tab, 1, lang);
 }
@@ -742,21 +743,44 @@ static htsmsg_t *
 dvr_config_class_retention_list ( void *o, const char *lang )
 {
   static const struct strtab_u32 tab[] = {
-    { N_("1 day"),              DVR_RET_1DAY },
-    { N_("3 days"),             DVR_RET_3DAY },
-    { N_("5 days"),             DVR_RET_5DAY },
-    { N_("1 week"),             DVR_RET_1WEEK },
-    { N_("2 weeks"),            DVR_RET_2WEEK },
-    { N_("3 weeks"),            DVR_RET_3WEEK },
-    { N_("1 month"),            DVR_RET_1MONTH },
-    { N_("2 months"),           DVR_RET_2MONTH },
-    { N_("3 months"),           DVR_RET_3MONTH },
-    { N_("6 months"),           DVR_RET_6MONTH },
-    { N_("1 year"),             DVR_RET_1YEAR },
-    { N_("2 years"),            DVR_RET_2YEARS },
-    { N_("3 years"),            DVR_RET_3YEARS },
+    { N_("1 day"),              DVR_RET_REM_1DAY },
+    { N_("3 days"),             DVR_RET_REM_3DAY },
+    { N_("5 days"),             DVR_RET_REM_5DAY },
+    { N_("1 week"),             DVR_RET_REM_1WEEK },
+    { N_("2 weeks"),            DVR_RET_REM_2WEEK },
+    { N_("3 weeks"),            DVR_RET_REM_3WEEK },
+    { N_("1 month"),            DVR_RET_REM_1MONTH },
+    { N_("2 months"),           DVR_RET_REM_2MONTH },
+    { N_("3 months"),           DVR_RET_REM_3MONTH },
+    { N_("6 months"),           DVR_RET_REM_6MONTH },
+    { N_("1 year"),             DVR_RET_REM_1YEAR },
+    { N_("2 years"),            DVR_RET_REM_2YEARS },
+    { N_("3 years"),            DVR_RET_REM_3YEARS },
     { N_("On file removal"),    DVR_RET_ONREMOVE },
-    { N_("Forever"),            DVR_RET_FOREVER },
+    { N_("Forever"),            DVR_RET_REM_FOREVER },
+  };
+  return strtab2htsmsg_u32(tab, 1, lang);
+}
+
+static htsmsg_t *
+dvr_config_class_retention_list_minimal ( void *o, const char *lang )
+{
+  static const struct strtab_u32 tab[] = {
+    { N_("Disabled"),           DVR_RET_MIN_DISABLED },
+    { N_("1 day"),              DVR_RET_REM_1DAY },
+    { N_("3 days"),             DVR_RET_REM_3DAY },
+    { N_("5 days"),             DVR_RET_REM_5DAY },
+    { N_("1 week"),             DVR_RET_REM_1WEEK },
+    { N_("2 weeks"),            DVR_RET_REM_2WEEK },
+    { N_("3 weeks"),            DVR_RET_REM_3WEEK },
+    { N_("1 month"),            DVR_RET_REM_1MONTH },
+    { N_("2 months"),           DVR_RET_REM_2MONTH },
+    { N_("3 months"),           DVR_RET_REM_3MONTH },
+    { N_("6 months"),           DVR_RET_REM_6MONTH },
+    { N_("1 year"),             DVR_RET_REM_1YEAR },
+    { N_("2 years"),            DVR_RET_REM_2YEARS },
+    { N_("3 years"),            DVR_RET_REM_3YEARS },
+    { N_("Forever"),            DVR_RET_REM_FOREVER },
   };
   return strtab2htsmsg_u32(tab, 1, lang);
 }
@@ -900,20 +924,31 @@ const idclass_t dvr_config_class = {
       .type     = PT_U32,
       .id       = "retention-days",
       .name     = N_("DVR log retention period"),
-      .desc     = N_("Number of days to retain infomation about recordings."),
+      .desc     = N_("Number of days to retain information about recordings. Once this period is exceeded, duplicate detection will not be possible for this recording."),
       .off      = offsetof(dvr_config_t, dvr_retention_days),
       .def.u32  = DVR_RET_ONREMOVE,
       .list     = dvr_config_class_retention_list,
       .opts     = PO_EXPERT | PO_DOC_NLIST,
       .group    = 1,
     },
+    {
+      .type     = PT_U32,
+      .id       = "retention-minimal",
+      .name     = N_("Minimal log retention period"),
+      .desc     = N_("Minimal number of days to retain information from recordings that where deleted manually. Once this period is exceeded, duplicate detection will not be possible for this recording."),
+      .off      = offsetof(dvr_config_t, dvr_retention_minimal),
+      .def.u32  = DVR_RET_MIN_DISABLED,
+      .list     = dvr_config_class_retention_list_minimal,
+      .opts     = PO_EXPERT | PO_DOC_NLIST,
+      .group    = 1,
+    },
     {
       .type     = PT_U32,
       .id       = "removal-days",
       .name     = N_("DVR file retention period"),
       .desc     = N_("Number of days to keep the recorded files."),
       .off      = offsetof(dvr_config_t, dvr_removal_days),
-      .def.u32  = DVR_RET_FOREVER,
+      .def.u32  = DVR_RET_REM_FOREVER,
       .list     = dvr_config_class_removal_list,
       .opts     = PO_DOC_NLIST,
       .group    = 1,
index 78524861e606aeb8a83217d7dab7076397380434..9f710fac31e40795db20ce7ab8174a08fde12fdd 100644 (file)
@@ -360,9 +360,9 @@ dvr_entry_get_removal_string ( dvr_entry_t *de )
   char buf[24];
   uint32_t removal = dvr_entry_get_removal_days(de);
 
-  if (removal < DVR_RET_SPACE)
+  if (removal < DVR_REM_SPACE)
     snprintf(buf, sizeof(buf), "%i days", removal);
-  else if (removal == DVR_RET_SPACE)
+  else if (removal == DVR_REM_SPACE)
     return strdup("Until space needed");
   else
     return strdup("Forever");
@@ -374,12 +374,12 @@ uint32_t
 dvr_entry_get_retention_days( dvr_entry_t *de )
 {
   if (de->de_retention > 0) {
-    if (de->de_retention > DVR_RET_FOREVER)
-      return DVR_RET_FOREVER;
+    if (de->de_retention > DVR_RET_REM_FOREVER)
+      return DVR_RET_REM_FOREVER;
 
     /* As we need the db entry when deleting the file on disk */
-    if (dvr_entry_get_removal_days(de) != DVR_RET_FOREVER &&
-        dvr_entry_get_removal_days(de) > de->de_retention)
+    if (dvr_entry_get_removal_days(de) != DVR_RET_REM_FOREVER &&
+        dvr_entry_get_removal_days(de) > de->de_retention && !de->de_file_removed)
       return DVR_RET_ONREMOVE;
 
     return de->de_retention;
@@ -391,8 +391,8 @@ uint32_t
 dvr_entry_get_removal_days ( dvr_entry_t *de )
 {
   if (de->de_removal > 0) {
-    if (de->de_removal > DVR_RET_FOREVER)
-      return DVR_RET_FOREVER;
+    if (de->de_removal > DVR_RET_REM_FOREVER)
+      return DVR_RET_REM_FOREVER;
 
     return de->de_removal;
   }
@@ -481,7 +481,7 @@ dvr_entry_retention_timer(dvr_entry_t *de)
   uint32_t retention = dvr_entry_get_retention_days(de);
   int save;
 
-  if ((removal > 0 || retention == 0) && removal < DVR_RET_SPACE) {
+  if ((removal > 0 || retention == 0) && removal < DVR_REM_SPACE && !de->de_file_removed) {
     stop = time_t_out_of_range((int64_t)de->de_stop + removal * (int64_t)86400);
     if (stop > gclk()) {
       dvr_entry_retention_arm(de, dvr_timer_remove_files, stop);
@@ -594,7 +594,9 @@ dvr_entry_status(dvr_entry_t *de)
     }
     if(de->de_data_errors >= DVR_MAX_DATA_ERRORS) /* user configurable threshold? */
       return N_("Too many data errors");
-    if(dvr_get_filesize(de, 0) == -1)
+    if (de->de_file_removed)
+      return N_("File removed");
+    if (dvr_get_filesize(de, 0) == -1)
       return N_("File missing");
     if(de->de_last_error)
       return streaming_code2txt(de->de_last_error);
@@ -630,7 +632,8 @@ dvr_entry_schedstatus(dvr_entry_t *de)
     break;
   case DVR_COMPLETED:
     s = "completed";
-    if(de->de_last_error || dvr_get_filesize(de, 0) == -1)
+    if(de->de_last_error ||
+      (dvr_get_filesize(de, 0) == -1 && !de->de_file_removed))
       s = "completedError";
     rerecord = de->de_dont_rerecord ? 0 : dvr_entry_get_rerecord_errors(de);
     if(rerecord && (de->de_errors || de->de_data_errors > rerecord))
@@ -1079,18 +1082,18 @@ dvr_entry_rerecord(dvr_entry_t *de)
       if (fsize1 / 5 < fsize2 / 6) {
         goto not_so_good;
       } else {
-        dvr_entry_cancel_delete(de2, 1);
+        dvr_entry_cancel_delete(de2, 1, 1);
       }
     } else if (de->de_sched_state == DVR_COMPLETED) {
       if(dvr_get_filesize(de, 0) == -1) {
 delete_me:
-        dvr_entry_cancel_delete(de, 0);
+        dvr_entry_cancel_delete(de, 0, 1);
         dvr_entry_rerecord(de2);
         return 1;
       }
 not_so_good:
       de->de_retention = DVR_RET_ONREMOVE;
-      de->de_removal = DVR_RET_1DAY;
+      de->de_removal = DVR_RET_REM_1DAY;
       dvr_entry_change_parent_child(de->de_parent, NULL, NULL, 1);
       dvr_entry_completed(de, SM_CODE_WEAK_STREAM);
       return 0;
@@ -2059,7 +2062,7 @@ dvr_timer_start_recording(void *aux)
 
   // if duplicate, then delete it now, don't record!
   if (_dvr_duplicate_event(de)) {
-    dvr_entry_cancel_delete(de, 1);
+    dvr_entry_cancel_delete(de, 1, 1);
     return;
   }
 
@@ -2149,7 +2152,8 @@ dvr_entry_class_save(idnode_t *self, char *filename, size_t fsize)
 static void
 dvr_entry_class_delete(idnode_t *self)
 {
-  dvr_entry_cancel_delete((dvr_entry_t *)self, 0);
+  dvr_entry_t *de = (dvr_entry_t *)self;
+  dvr_entry_cancel_delete(de, 0, 0);
 }
 
 static int
@@ -2457,22 +2461,22 @@ htsmsg_t *
 dvr_entry_class_retention_list ( void *o, const char *lang )
 {
   static const struct strtab_u32 tab[] = {
-    { N_("DVR configuration"),  DVR_RET_DVRCONFIG },
-    { N_("1 day"),              DVR_RET_1DAY },
-    { N_("3 days"),             DVR_RET_3DAY },
-    { N_("5 days"),             DVR_RET_5DAY },
-    { N_("1 week"),             DVR_RET_1WEEK },
-    { N_("2 weeks"),            DVR_RET_2WEEK },
-    { N_("3 weeks"),            DVR_RET_3WEEK },
-    { N_("1 month"),            DVR_RET_1MONTH },
-    { N_("2 months"),           DVR_RET_2MONTH },
-    { N_("3 months"),           DVR_RET_3MONTH },
-    { N_("6 months"),           DVR_RET_6MONTH },
-    { N_("1 year"),             DVR_RET_1YEAR },
-    { N_("2 years"),            DVR_RET_2YEARS },
-    { N_("3 years"),            DVR_RET_3YEARS },
+    { N_("DVR configuration"),  DVR_RET_REM_DVRCONFIG },
+    { N_("1 day"),              DVR_RET_REM_1DAY },
+    { N_("3 days"),             DVR_RET_REM_3DAY },
+    { N_("5 days"),             DVR_RET_REM_5DAY },
+    { N_("1 week"),             DVR_RET_REM_1WEEK },
+    { N_("2 weeks"),            DVR_RET_REM_2WEEK },
+    { N_("3 weeks"),            DVR_RET_REM_3WEEK },
+    { N_("1 month"),            DVR_RET_REM_1MONTH },
+    { N_("2 months"),           DVR_RET_REM_2MONTH },
+    { N_("3 months"),           DVR_RET_REM_3MONTH },
+    { N_("6 months"),           DVR_RET_REM_6MONTH },
+    { N_("1 year"),             DVR_RET_REM_1YEAR },
+    { N_("2 years"),            DVR_RET_REM_2YEARS },
+    { N_("3 years"),            DVR_RET_REM_3YEARS },
     { N_("On file removal"),    DVR_RET_ONREMOVE },
-    { N_("Forever"),            DVR_RET_FOREVER },
+    { N_("Forever"),            DVR_RET_REM_FOREVER },
   };
   return strtab2htsmsg_u32(tab, 1, lang);
 }
@@ -2481,22 +2485,22 @@ htsmsg_t *
 dvr_entry_class_removal_list ( void *o, const char *lang )
 {
   static const struct strtab_u32 tab[] = {
-    { N_("DVR configuration"),  DVR_RET_DVRCONFIG },
-    { N_("1 day"),              DVR_RET_1DAY },
-    { N_("3 days"),             DVR_RET_3DAY },
-    { N_("5 days"),             DVR_RET_5DAY },
-    { N_("1 week"),             DVR_RET_1WEEK },
-    { N_("2 weeks"),            DVR_RET_2WEEK },
-    { N_("3 weeks"),            DVR_RET_3WEEK },
-    { N_("1 month"),            DVR_RET_1MONTH },
-    { N_("2 months"),           DVR_RET_2MONTH },
-    { N_("3 months"),           DVR_RET_3MONTH },
-    { N_("6 months"),           DVR_RET_6MONTH },
-    { N_("1 year"),             DVR_RET_1YEAR },
-    { N_("2 years"),            DVR_RET_2YEARS },
-    { N_("3 years"),            DVR_RET_3YEARS },
-    { N_("Maintained space"),   DVR_RET_SPACE },
-    { N_("Forever"),            DVR_RET_FOREVER },
+    { N_("DVR configuration"),  DVR_RET_REM_DVRCONFIG },
+    { N_("1 day"),              DVR_RET_REM_1DAY },
+    { N_("3 days"),             DVR_RET_REM_3DAY },
+    { N_("5 days"),             DVR_RET_REM_5DAY },
+    { N_("1 week"),             DVR_RET_REM_1WEEK },
+    { N_("2 weeks"),            DVR_RET_REM_2WEEK },
+    { N_("3 weeks"),            DVR_RET_REM_3WEEK },
+    { N_("1 month"),            DVR_RET_REM_1MONTH },
+    { N_("2 months"),           DVR_RET_REM_2MONTH },
+    { N_("3 months"),           DVR_RET_REM_3MONTH },
+    { N_("6 months"),           DVR_RET_REM_6MONTH },
+    { N_("1 year"),             DVR_RET_REM_1YEAR },
+    { N_("2 years"),            DVR_RET_REM_2YEARS },
+    { N_("3 years"),            DVR_RET_REM_3YEARS },
+    { N_("Maintained space"),   DVR_REM_SPACE },
+    { N_("Forever"),            DVR_RET_REM_FOREVER },
   };
   return strtab2htsmsg_u32(tab, 1, lang);
 }
@@ -3091,7 +3095,7 @@ const idclass_t dvr_entry_class = {
       .name     = N_("DVR log retention"),
       .desc     = N_("Number of days to retain entry information."),
       .off      = offsetof(dvr_entry_t, de_retention),
-      .def.i    = DVR_RET_DVRCONFIG,
+      .def.i    = DVR_RET_REM_DVRCONFIG,
       .list     = dvr_entry_class_retention_list,
       .opts     = PO_HIDDEN | PO_EXPERT | PO_DOC_NLIST,
     },
@@ -3101,7 +3105,7 @@ const idclass_t dvr_entry_class = {
       .name     = N_("DVR file retention period"),
       .desc     = N_("Number of days to keep the file."),
       .off      = offsetof(dvr_entry_t, de_removal),
-      .def.i    = DVR_RET_DVRCONFIG,
+      .def.i    = DVR_RET_REM_DVRCONFIG,
       .list     = dvr_entry_class_removal_list,
       .opts     = PO_HIDDEN | PO_ADVANCED | PO_DOC_NLIST,
     },
@@ -3202,6 +3206,14 @@ const idclass_t dvr_entry_class = {
       .off      = offsetof(dvr_entry_t, de_dont_rerecord),
       .opts     = PO_HIDDEN | PO_ADVANCED,
     },
+    {
+      .type     = PT_U32,
+      .id       = "fileremoved",
+      .name     = N_("File removed"),
+      .desc     = N_("The recorded file was removed intentionally"),
+      .off      = offsetof(dvr_entry_t, de_file_removed),
+      .opts     = PO_HIDDEN | PO_NOUI,
+    },
     {
       .type     = PT_STR,
       .id       = "autorec",
@@ -3476,6 +3488,7 @@ dvr_entry_delete(dvr_entry_t *de)
       htsmsg_delete_field(m, "filename");
       ret = 1;
     }
+    de->de_file_removed = 1;
   }
 
   return ret;
@@ -3494,7 +3507,7 @@ dvr_entry_set_rerecord(dvr_entry_t *de, int cmd)
   if (cmd == 0 && !de->de_dont_rerecord) {
     de->de_dont_rerecord = 1;
     if (de->de_child)
-      dvr_entry_cancel_delete(de->de_child, 0);
+      dvr_entry_cancel_delete(de->de_child, 0, 1);
   } else {
     de->de_dont_rerecord = 0;
     dvr_entry_rerecord(de);
@@ -3565,7 +3578,7 @@ dvr_entry_cancel(dvr_entry_t *de, int rerecord)
  *
  */
 void
-dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord)
+dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord, int forcedestroy)
 {
   dvr_entry_t *parent = de->de_parent;
   dvr_autorec_entry_t *dae = de->de_autorec;
@@ -3575,7 +3588,10 @@ dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord)
     dvr_stop_recording(de, SM_CODE_ABORTED, 1, 0);
   case DVR_COMPLETED:
     dvr_entry_delete(de);
-    dvr_entry_destroy(de, 1);
+    if (forcedestroy)
+      dvr_entry_destroy(de, 1);
+    else
+      dvr_entry_trydestroy(de);
     break;
 
   case DVR_SCHEDULED:
@@ -3598,7 +3614,41 @@ dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord)
   // Trigger autorec update in case of max sched count limit
   if (dae && dae->dae_max_sched_count > 0)
     dvr_autorec_changed(dae, 0);
+}
+
+/**
+ * Destroy db entry if possible.
+ * The deletion of the db entry can be prevented by the minimal retention setting.
+ * Prevention is needed in order to keep duplicate detection happy.
+ */
+void
+dvr_entry_trydestroy(dvr_entry_t *de)
+{
+  char ubuf[UUID_HEX_SIZE];
+  uint32_t minretention, removal;
 
+  if (!de->de_config || de->de_config->dvr_retention_minimal == DVR_RET_MIN_DISABLED)
+    dvr_entry_destroy(de, 1);
+  else {
+    minretention = time_t_out_of_range((int64_t)de->de_stop + de->de_config->dvr_retention_minimal * (int64_t)86400);
+    if (minretention < gclk()) /* Minimal retention period expired -> deleting db entry allowed  */
+      dvr_entry_destroy(de, 1);
+    else {
+      removal = (gclk() - (int64_t)de->de_stop)/(int64_t)86400;
+
+      de->de_removal      = removal > DVR_RET_REM_DVRCONFIG ?
+          removal : DVR_RET_REM_1DAY;                             /* Update removal to the current value */
+      de->de_retention    = de->de_config->dvr_retention_minimal; /* Update the retention to the minimum allowed value */
+      idnode_changed(&de->de_id);
+      dvr_entry_retention_timer(de);                              /* Rearm timer as retention was changed */
+
+      tvhinfo(LS_DVR, "delete entry %s not allowed \"%s\" on \"%s\", current retention period %"PRIu32", "
+         "minimal retention period %"PRIu32"",
+        idnode_uuid_as_str(&de->de_id, ubuf),
+        lang_str_get(de->de_title, NULL), DVR_CH_NAME(de),
+        removal, de->de_config->dvr_retention_minimal);
+    }
+  }
 }
 
 /**
index f752db6d17ab3c224b64cc443c7d110572fdc212..c84a86b8c37a0e60d82199041aaeb9f85b30a1bd 100644 (file)
@@ -625,7 +625,7 @@ const idclass_t dvr_timerec_entry_class = {
       .id       = "retention",
       .name     = N_("DVR log retention"),
       .desc     = N_("Number of days to retain entry information."),
-      .def.i    = DVR_RET_DVRCONFIG,
+      .def.i    = DVR_RET_REM_DVRCONFIG,
       .off      = offsetof(dvr_timerec_entry_t, dte_retention),
       .list     = dvr_entry_class_retention_list,
       .opts     = PO_EXPERT | PO_DOC_NLIST,
@@ -635,7 +635,7 @@ const idclass_t dvr_timerec_entry_class = {
       .id       = "removal",
       .name     = N_("DVR file retention period"),
       .desc     = N_("Number of days to keep the recorded file."),
-      .def.i    = DVR_RET_DVRCONFIG,
+      .def.i    = DVR_RET_REM_DVRCONFIG,
       .off      = offsetof(dvr_timerec_entry_t, dte_removal),
       .list     = dvr_entry_class_removal_list,
       .opts     = PO_ADVANCED | PO_DOC_NLIST,
@@ -788,11 +788,11 @@ uint32_t
 dvr_timerec_get_retention_days( dvr_timerec_entry_t *dte )
 {
   if (dte->dte_retention > 0) {
-    if (dte->dte_retention > DVR_RET_FOREVER)
-      return DVR_RET_FOREVER;
+    if (dte->dte_retention > DVR_RET_REM_FOREVER)
+      return DVR_RET_REM_FOREVER;
 
     /* As we need the db entry when deleting the file on disk */
-    if (dvr_timerec_get_removal_days(dte) != DVR_RET_FOREVER &&
+    if (dvr_timerec_get_removal_days(dte) != DVR_RET_REM_FOREVER &&
         dvr_timerec_get_removal_days(dte) > dte->dte_retention)
       return DVR_RET_ONREMOVE;
 
@@ -808,8 +808,8 @@ uint32_t
 dvr_timerec_get_removal_days( dvr_timerec_entry_t *dte )
 {
   if (dte->dte_removal > 0) {
-    if (dte->dte_removal > DVR_RET_FOREVER)
-      return DVR_RET_FOREVER;
+    if (dte->dte_removal > DVR_RET_REM_FOREVER)
+      return DVR_RET_REM_FOREVER;
 
     return dte->dte_removal;
   }
index 93a8e6900a220d5bd31cc96b4d0c4abc58a15876..60b8ebc5ca62bacd4d7a160f89637a2c744a6a24 100644 (file)
@@ -231,7 +231,7 @@ dvr_disk_space_cleanup(dvr_config_t *cfg)
       if (dvr_entry_get_stop_time(de) > stoptime)
         continue;
 
-      if (dvr_entry_get_removal_days(de) != DVR_RET_SPACE) // only remove the allowed ones
+      if (dvr_entry_get_removal_days(de) != DVR_REM_SPACE) // only remove the allowed ones
         continue;
 
       if (dvr_get_filename(de) == NULL || dvr_get_filesize(de, DVR_FILESIZE_TOTAL) <= 0)
index f1177c283611d83915bf69d655a908b3334cb872..43a27e04e8eba8e6c307c4cac69016d8e2218034 100644 (file)
@@ -629,9 +629,9 @@ htsp_serierec_convert(htsp_connection_t *htsp, htsmsg_t *in, channel_t *ch, int
   if (!(retval = htsmsg_get_u32(in, "enabled", &u32)) || add)
     htsmsg_add_u32(conf, "enabled", !retval ? (u32 > 0 ? 1 : 0) : 1); // default on
   if (!(retval = htsmsg_get_u32(in, "retention", &u32)) || add)
-    htsmsg_add_u32(conf, "retention", !retval ? u32 : DVR_RET_DVRCONFIG);
+    htsmsg_add_u32(conf, "retention", !retval ? u32 : DVR_RET_REM_DVRCONFIG);
   if (!(retval = htsmsg_get_u32(in, "removal", &u32)) || add)
-    htsmsg_add_u32(conf, "removal", !retval ? u32 : DVR_RET_DVRCONFIG);
+    htsmsg_add_u32(conf, "removal", !retval ? u32 : DVR_RET_REM_DVRCONFIG);
   if(!(retval = htsmsg_get_u32(in, "priority", &u32)) || add)
     htsmsg_add_u32(conf, "pri", !retval ? u32 : DVR_PRIO_NORMAL);
   if ((str = htsmsg_get_str(in, "name")) || add)
@@ -1803,9 +1803,9 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   if(htsmsg_get_u32(in, "priority", &priority))
     priority = DVR_PRIO_NORMAL;
   if(htsmsg_get_u32(in, "retention", &retention))
-    retention = DVR_RET_DVRCONFIG;
+    retention = DVR_RET_REM_DVRCONFIG;
   if(htsmsg_get_u32(in, "removal", &removal))
-    removal = DVR_RET_DVRCONFIG;
+    removal = DVR_RET_REM_DVRCONFIG;
   comment = htsmsg_get_str(in, "comment");
   if (!(lang  = htsmsg_get_str(in, "language")))
     lang = htsp->htsp_language;
@@ -1934,8 +1934,8 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   stop        = htsmsg_get_s64_or_default(in, "stop",       0);
   start_extra = htsmsg_get_s64_or_default(in, "startExtra", 0);
   stop_extra  = htsmsg_get_s64_or_default(in, "stopExtra",  0);
-  retention   = htsmsg_get_u32_or_default(in, "retention",  DVR_RET_DVRCONFIG);
-  removal     = htsmsg_get_u32_or_default(in, "removal",    DVR_RET_DVRCONFIG);
+  retention   = htsmsg_get_u32_or_default(in, "retention",  DVR_RET_REM_DVRCONFIG);
+  removal     = htsmsg_get_u32_or_default(in, "removal",    DVR_RET_REM_DVRCONFIG);
   priority    = htsmsg_get_u32_or_default(in, "priority",   DVR_PRIO_NORMAL);
   title       = htsmsg_get_str(in, "title");
   subtitle    = htsmsg_get_str(in, "subtitle");
@@ -1998,7 +1998,7 @@ htsp_method_deleteDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   if (de == NULL)
     return out;
 
-  dvr_entry_cancel_delete(de, 0);
+  dvr_entry_cancel_delete(de, 0, 0);
 
   return htsp_success();
 }
index cbe94e6b9c9f0604d09e24763b77ba018e5311ef..6784e30bf3f913c793b289628d5cca4456a36b7e 100644 (file)
@@ -337,8 +337,8 @@ page_einfo(http_connection_t *hc, const char *remain, void *opaque)
   if((http_arg_get(&hc->hc_req_args, "rec")) != NULL) {
     de = dvr_entry_create_by_event(1, NULL, e, 0, 0, hc->hc_username ?: NULL,
                                    hc->hc_representative ?: NULL, NULL,
-                                   DVR_PRIO_NORMAL, DVR_RET_DVRCONFIG,
-                                  DVR_RET_DVRCONFIG, "simpleui");
+                                   DVR_PRIO_NORMAL, DVR_RET_REM_DVRCONFIG,
+                                   DVR_RET_REM_DVRCONFIG, "simpleui");
   } else if(de != NULL && (http_arg_get(&hc->hc_req_args, "cancel")) != NULL) {
     de = dvr_entry_cancel(de, 0);
   }