]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
DVR: Implement automatic file removal, fixes #3141
authorJaroslav Kysela <perex@perex.cz>
Thu, 8 Oct 2015 18:34:54 +0000 (20:34 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 8 Oct 2015 18:34:54 +0000 (20:34 +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/htsp_server.c
src/webui/simpleui.c
src/webui/static/app/dvr.js

index 9eaaf18786817083a26f8d8c16816d79995bf3a4..943564567898581991cfac0730c5050f94707fed 100644 (file)
@@ -240,7 +240,7 @@ api_dvr_entry_create_by_event
                                        e, 0, 0,
                                        perm->aa_username,
                                        perm->aa_representative,
-                                       NULL, DVR_PRIO_NORMAL, 0, comment);
+                                       NULL, DVR_PRIO_NORMAL, 0, 0, comment);
         if (de)
           dvr_entry_save(de);
       }
index 7cd363162455bfd3921012004ee2b7c9847b28a7..f04247178a0568be2ea950e3bfa7e2bd196e1495 100644 (file)
@@ -41,6 +41,7 @@ typedef struct dvr_config {
   char *dvr_storage;
   int dvr_clone;
   uint32_t dvr_retention_days;
+  uint32_t dvr_removal_days;
   char *dvr_charset;
   char *dvr_charset_id;
   char *dvr_postproc;
@@ -164,7 +165,8 @@ typedef struct dvr_entry {
   int de_pri;
   int de_dont_reschedule;
   int de_mc;
-  int de_retention;
+  uint32_t de_retention;
+  uint32_t de_removal;
 
   /**
    * EPG information / links
@@ -294,7 +296,8 @@ typedef struct dvr_autorec_entry {
 
   int dae_minduration;
   int dae_maxduration;
-  int dae_retention;
+  uint32_t dae_retention;
+  uint32_t dae_removal;
 
   time_t dae_start_extra;
   time_t dae_stop_extra;
@@ -339,7 +342,8 @@ typedef struct dvr_timerec_entry {
 
   dvr_entry_t *dte_spawn;
 
-  int dte_retention;
+  uint32_t dte_retention;
+  uint32_t dte_removal;
 } dvr_timerec_entry_t;
 
 TAILQ_HEAD(dvr_timerec_entry_queue, dvr_timerec_entry);
@@ -382,6 +386,11 @@ void dvr_config_save(dvr_config_t *cfg);
 
 void dvr_config_destroy_by_profile(profile_t *pro, int delconf);
 
+static inline uint32_t dvr_retention_cleanup(uint32_t val)
+{
+  return (val > 0xffffffff - 86400) ? (0xffffffff - 86400) : val;
+}
+
 /*
  *
  */
@@ -396,7 +405,9 @@ static inline int dvr_entry_is_valid(dvr_entry_t *de)
 
 int dvr_entry_get_mc(dvr_entry_t *de);
 
-int dvr_entry_get_retention( dvr_entry_t *de );
+uint32_t dvr_entry_get_retention_days( dvr_entry_t *de );
+
+uint32_t dvr_entry_get_removal_days( dvr_entry_t *de );
 
 int dvr_entry_get_start_time( dvr_entry_t *de );
 
@@ -439,7 +450,7 @@ dvr_entry_create_by_event( int enabled,
                            time_t start_extra, time_t stop_extra,
                            const char *owner, const char *creator,
                            dvr_autorec_entry_t *dae,
-                           dvr_prio_t pri, int retention,
+                           dvr_prio_t pri, int retention, int removal,
                            const char *comment );
 
 dvr_entry_t *
@@ -451,7 +462,7 @@ dvr_entry_create_htsp( int enabled, const char *dvr_config_uuid,
                        const char *lang, epg_genre_t *content_type,
                        const char *owner, const char *creator,
                        dvr_autorec_entry_t *dae,
-                       dvr_prio_t pri, int retention,
+                       dvr_prio_t pri, int retention, int removal,
                        const char *comment );
 
 dvr_entry_t *
@@ -460,7 +471,7 @@ dvr_entry_update( dvr_entry_t *de, int enabled, channel_t *ch,
                   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 );
+                  dvr_prio_t pri, int retention, int removal );
 
 void dvr_destroy_by_channel(channel_t *ch, int delconf);
 
@@ -497,7 +508,7 @@ dvr_entry_t *dvr_entry_cancel(dvr_entry_t *de);
 
 void dvr_entry_dec_ref(dvr_entry_t *de);
 
-void dvr_entry_delete(dvr_entry_t *de);
+void dvr_entry_delete(dvr_entry_t *de, int no_missed_time_resched);
 
 void dvr_entry_cancel_delete(dvr_entry_t *de);
 
@@ -527,13 +538,13 @@ dvr_entry_create_(int enabled, const char *config_uuid, epg_broadcast_t *e,
                   const char *lang, epg_genre_t *content_type,
                   const char *owner, const char *creator,
                   dvr_autorec_entry_t *dae, dvr_timerec_entry_t *tae,
-                  dvr_prio_t pri, int retention, const char *comment);
+                  dvr_prio_t pri, int retention, int removal, const char *comment);
 
 dvr_autorec_entry_t *
 dvr_autorec_create_htsp(const char *dvr_config_name, const char *title, int fulltext,
                             channel_t *ch, uint32_t enabled, int32_t start,
                             int32_t start_window, uint32_t days, time_t start_extra,
-                            time_t stop_extra, dvr_prio_t pri, int retention,
+                            time_t stop_extra, dvr_prio_t pri, int retention, int removal,
                             int min_duration, int max_duration, dvr_autorec_dedup_t dup_detect,
                             const char *owner, const char *creator,
                             const char *comment, const char *name, const char *directory);
@@ -589,7 +600,9 @@ static inline int
   return 0;
 }
 
-int dvr_autorec_get_retention( dvr_autorec_entry_t *dae );
+uint32_t dvr_autorec_get_retention_days( dvr_autorec_entry_t *dae );
+
+uint32_t dvr_autorec_get_removal_days( dvr_autorec_entry_t *dae );
 
 int dvr_autorec_get_extra_time_post( dvr_autorec_entry_t *dae );
 
@@ -605,7 +618,7 @@ dvr_timerec_create(const char *uuid, htsmsg_t *conf);
 dvr_timerec_entry_t*
 dvr_timerec_create_htsp(const char *dvr_config_name, const char *title,
                             channel_t *ch, uint32_t enabled, uint32_t start, uint32_t stop,
-                            uint32_t weekdays, dvr_prio_t pri, int retention,
+                            uint32_t weekdays, dvr_prio_t pri, int retention, int removal,
                             const char *owner, const char *creator, const char *comment, 
                             const char *name, const char *directory);
 
@@ -642,7 +655,9 @@ static inline int dvr_timerec_entry_verify
   return 0;
 }
 
-int dvr_timerec_get_retention( dvr_timerec_entry_t *dte );
+uint32_t dvr_timerec_get_retention_days( dvr_timerec_entry_t *dte );
+
+uint32_t dvr_timerec_get_removal_days( dvr_timerec_entry_t *dte );
 
 /**
  *
index 946078148a2c2b66e0ac581d87214fc9e56d304a..ae69d6b717676dc342214eeaa83be33903a84232 100644 (file)
@@ -242,7 +242,7 @@ dvr_autorec_entry_t*
 dvr_autorec_create_htsp(const char *dvr_config_name, const char *title, int fulltext,
                             channel_t *ch, uint32_t enabled, int32_t start, int32_t start_window,
                             uint32_t weekdays, time_t start_extra, time_t stop_extra,
-                            dvr_prio_t pri, int retention,
+                            dvr_prio_t pri, int retention, int removal,
                             int min_duration, int max_duration, dvr_autorec_dedup_t dup_detect,
                             const char *owner, const char *creator, const char *comment, 
                             const char *name, const char *directory)
@@ -255,6 +255,7 @@ dvr_autorec_create_htsp(const char *dvr_config_name, const char *title, int full
 
   htsmsg_add_u32(conf, "enabled",     enabled > 0 ? 1 : 0);
   htsmsg_add_u32(conf, "retention",   retention);
+  htsmsg_add_u32(conf, "removal",     removal);
   htsmsg_add_u32(conf, "pri",         pri);
   htsmsg_add_u32(conf, "minduration", min_duration);
   htsmsg_add_u32(conf, "maxduration", max_duration);
@@ -1057,10 +1058,18 @@ const idclass_t dvr_autorec_entry_class = {
       .list     = dvr_autorec_entry_class_dedup_list,
     },
     {
-      .type     = PT_INT,
+      .type     = PT_U32,
       .id       = "retention",
-      .name     = N_("Retention"),
+      .name     = N_("DVR Log Retention (days)"),
       .off      = offsetof(dvr_autorec_entry_t, dae_retention),
+      .opts     = PO_HIDDEN,
+    },
+    {
+      .type     = PT_U32,
+      .id       = "removal",
+      .name     = N_("File removal (days)"),
+      .off      = offsetof(dvr_autorec_entry_t, dae_removal),
+      .opts     = PO_HIDDEN,
     },
     {
       .type     = PT_STR,
@@ -1332,10 +1341,21 @@ dvr_autorec_get_extra_time_post( dvr_autorec_entry_t *dae )
 /**
  *
  */
-int
-dvr_autorec_get_retention( dvr_autorec_entry_t *dae )
+uint32_t
+dvr_autorec_get_retention_days( dvr_autorec_entry_t *dae )
 {
   if (dae->dae_retention > 0)
     return dae->dae_retention;
-  return dae->dae_config->dvr_retention_days;
+  return dvr_retention_cleanup(dae->dae_config->dvr_retention_days);
+}
+
+/**
+ *
+ */
+uint32_t
+dvr_autorec_get_removal_days( dvr_autorec_entry_t *dae )
+{
+  if (dae->dae_removal > 0)
+    return dae->dae_removal;
+  return dvr_retention_cleanup(dae->dae_config->dvr_removal_days);
 }
index a04ee627bfda9e800a3ce6a2585805c132a05796..d74c5a7f57883dec8305dbd4ccfabfc4b8ece9aa 100644 (file)
@@ -512,6 +512,8 @@ dvr_config_save(dvr_config_t *cfg)
   lock_assert(&global_lock);
 
   dvr_config_storage_check(cfg);
+  if (cfg->dvr_removal_days > cfg->dvr_retention_days)
+    cfg->dvr_removal_days = cfg->dvr_retention_days;
   idnode_save(&cfg->dvr_id, m);
   hts_settings_save(m, "dvr/config/%s", idnode_uuid_as_sstr(&cfg->dvr_id));
   htsmsg_destroy(m);
@@ -813,6 +815,13 @@ const idclass_t dvr_config_class = {
       .def.u32  = 31,
       .group    = 1,
     },
+    {
+      .type     = PT_U32,
+      .id       = "removal-days",
+      .name     = N_("DVR File Removal Time (days)"),
+      .off      = offsetof(dvr_config_t, dvr_removal_days),
+      .group    = 1,
+    },
     {
       .type     = PT_BOOL,
       .id       = "clone",
index b99e03ab5af6203ffab66ddd2dd23c15cf40ae0a..23f1b463d841f657950fd1c5f937d361578bd0bf 100644 (file)
@@ -42,6 +42,7 @@ static gtimer_t dvr_dbus_timer;
 
 static void dvr_entry_destroy(dvr_entry_t *de, int delconf);
 static void dvr_timer_expire(void *aux);
+static void dvr_timer_remove_files(void *aux);
 static void dvr_entry_start_recording(dvr_entry_t *de, int clone);
 static void dvr_timer_start_recording(void *aux);
 static void dvr_timer_stop_recording(void *aux);
@@ -179,12 +180,20 @@ dvr_entry_get_mc( dvr_entry_t *de )
   return profile_get_mc(de->de_config->dvr_profile);
 }
 
-int
-dvr_entry_get_retention( dvr_entry_t *de )
+uint32_t
+dvr_entry_get_retention_days( dvr_entry_t *de )
 {
   if (de->de_retention > 0)
     return de->de_retention;
-  return de->de_config->dvr_retention_days;
+  return dvr_retention_cleanup(de->de_config->dvr_retention_days);
+}
+
+uint32_t
+dvr_entry_get_removal_days ( dvr_entry_t *de )
+{
+  if (de->de_removal > 0)
+    return de->de_removal;
+  return dvr_retention_cleanup(de->de_config->dvr_removal_days);
 }
 
 /*
@@ -231,8 +240,22 @@ dvr_dbus_timer_cb( void *aux )
 static void
 dvr_entry_retention_timer(dvr_entry_t *de)
 {
-  gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de,
-                 de->de_stop + dvr_entry_get_retention(de) * 86400);
+  time_t stop = de->de_stop;
+  uint32_t removal = dvr_entry_get_removal_days(de);
+  uint32_t retention = dvr_entry_get_retention_days(de);
+  if (removal > retention)
+    removal = retention;
+  stop = de->de_stop + removal * (time_t)86400;
+  if (removal > 0 || retention == 0) {
+    if (stop > dispatch_clock) {
+      gtimer_arm_abs(&de->de_timer, dvr_timer_remove_files, de, stop);
+      return;
+    }
+    if (dvr_get_filename(de))
+      dvr_entry_delete(de, 1);
+  }
+  stop = de->de_stop + retention * (time_t)86400;
+  gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, stop);
 }
 
 /*
@@ -241,7 +264,7 @@ dvr_entry_retention_timer(dvr_entry_t *de)
 static void
 dvr_entry_nostate(dvr_entry_t *de, int error_code)
 {
-  dvr_entry_set_state(de, DVR_NOSTATE, DVR_RS_PENDING, de->de_last_error);
+  dvr_entry_set_state(de, DVR_NOSTATE, DVR_RS_PENDING, error_code);
   dvr_entry_retention_timer(de);
 }
 
@@ -552,7 +575,7 @@ dvr_entry_create_(int enabled, const char *config_uuid, epg_broadcast_t *e,
                   const char *owner,
                   const char *creator, dvr_autorec_entry_t *dae,
                   dvr_timerec_entry_t *dte,
-                  dvr_prio_t pri, int retention,
+                  dvr_prio_t pri, int retention, int removal,
                   const char *comment)
 {
   dvr_entry_t *de;
@@ -570,6 +593,7 @@ dvr_entry_create_(int enabled, const char *config_uuid, epg_broadcast_t *e,
   htsmsg_add_str(conf, "channel", idnode_uuid_as_sstr(&ch->ch_id));
   htsmsg_add_u32(conf, "pri", pri);
   htsmsg_add_u32(conf, "retention", retention);
+  htsmsg_add_u32(conf, "removal", removal);
   htsmsg_add_str(conf, "config_name", config_uuid ?: "");
   htsmsg_add_s64(conf, "start_extra", start_extra);
   htsmsg_add_s64(conf, "stop_extra", stop_extra);
@@ -658,7 +682,7 @@ dvr_entry_create_htsp(int enabled, const char *config_uuid,
                       epg_genre_t *content_type,
                       const char *owner,
                       const char *creator, dvr_autorec_entry_t *dae,
-                      dvr_prio_t pri, int retention,
+                      dvr_prio_t pri, int retention, int removal,
                       const char *comment)
 {
   char ubuf[UUID_HEX_SIZE];
@@ -670,7 +694,7 @@ dvr_entry_create_htsp(int enabled, const char *config_uuid,
                            NULL,
                            ch, start, stop, start_extra, stop_extra,
                            title, subtitle, description, lang, content_type,
-                           owner, creator, dae, NULL, pri, retention,
+                           owner, creator, dae, NULL, pri, retention, removal,
                            comment);
 }
 
@@ -683,7 +707,7 @@ dvr_entry_create_by_event(int enabled, const char *config_uuid,
                           time_t start_extra, time_t stop_extra,
                           const char *owner,
                           const char *creator, dvr_autorec_entry_t *dae,
-                          dvr_prio_t pri, int retention,
+                          dvr_prio_t pri, int retention, int removal,
                           const char *comment)
 {
   if(!e->channel || !e->episode || !e->episode->title)
@@ -694,8 +718,8 @@ dvr_entry_create_by_event(int enabled, const char *config_uuid,
                            start_extra, stop_extra,
                            NULL, NULL, NULL, NULL,
                            LIST_FIRST(&e->episode->genre),
-                           owner, creator, dae, NULL, pri, retention,
-                           comment);
+                           owner, creator, dae, NULL, pri,
+                           retention, removal, comment);
 }
 
 /**
@@ -844,8 +868,8 @@ dvr_entry_create_by_autorec(int enabled, epg_broadcast_t *e, dvr_autorec_entry_t
 
   dvr_entry_create_by_event(enabled, idnode_uuid_as_str(&dae->dae_config->dvr_id, ubuf),
                             e, dae->dae_start_extra, dae->dae_stop_extra,
-                            dae->dae_owner, dae->dae_creator,
-                            dae, dae->dae_pri, dae->dae_retention, buf);
+                            dae->dae_owner, dae->dae_creator, dae, dae->dae_pri,
+                            dae->dae_retention, dae->dae_removal, buf);
 }
 
 /**
@@ -964,15 +988,29 @@ dvr_timer_expire(void *aux)
 {
   dvr_entry_t *de = aux;
   dvr_entry_destroy(de, 1);
 }
 
+
+/**
+ *
+ */
+static void
+dvr_timer_remove_files(void *aux)
+{
+  dvr_entry_t *de = aux;
+  dvr_entry_retention_timer(de);
+}
+
+
+/**
+ *
+ */
 static dvr_entry_t *_dvr_entry_update
   ( dvr_entry_t *de, int enabled, epg_broadcast_t *e, channel_t *ch,
     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 )
+    dvr_prio_t pri, int retention, int removal )
 {
   char buf[40];
   int save = 0, updated = 0;
@@ -1038,6 +1076,10 @@ static dvr_entry_t *_dvr_entry_update
     de->de_retention = retention;
     save = 1;
   }
+  if (removal && (removal != de->de_removal)) {
+    de->de_removal = removal;
+    save = 1;
+  }
   if (save) {
     updated = 1;
     dvr_entry_set_timer(de);
@@ -1134,11 +1176,11 @@ dvr_entry_update
     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 )
+    dvr_prio_t pri, int retention, int removal )
 {
   return _dvr_entry_update(de, enabled, NULL, ch, title, subtitle, desc, lang,
                            start, stop, start_extra, stop_extra,
-                           pri, retention);
+                           pri, retention, removal);
 }
 
 /**
@@ -1185,7 +1227,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e)
                    channel_get_name(e->channel),
                    e->start, e->stop);
           dvr_entry_assign_broadcast(de, e);
-          _dvr_entry_update(de, -1, e, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
+          _dvr_entry_update(de, -1, e, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0);
           return;
         }
       }
@@ -1199,7 +1241,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, -1, e, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
+    _dvr_entry_update(de, -1, e, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0);
   else {
     LIST_FOREACH(de, &dvrentries, de_global_link) {
       if (de->de_sched_state != DVR_SCHEDULED) continue;
@@ -1214,7 +1256,7 @@ void dvr_event_updated ( epg_broadcast_t *e )
                  channel_get_name(e->channel),
                  e->start, e->stop);
         dvr_entry_assign_broadcast(de, e);
-        _dvr_entry_update(de, -1, e, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0);
+        _dvr_entry_update(de, -1, e, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0);
         break;
       }
     }
@@ -1248,8 +1290,7 @@ dvr_stop_recording(dvr_entry_t *de, int stopcode, int saveconf, int clone)
   if (saveconf)
     dvr_entry_save(de);
 
-  gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, 
-                de->de_stop + dvr_entry_get_retention(de) * 86400);
+  dvr_entry_retention_timer(de);
 }
 
 
@@ -1690,7 +1731,14 @@ static int
 dvr_entry_class_retention_set(void *o, const void *v)
 {
   dvr_entry_t *de = (dvr_entry_t *)o;
-  return dvr_entry_class_int_set(de, &de->de_retention, *(int *)v);
+  return dvr_entry_class_int_set(de, (int *)&de->de_retention, *(int *)v);
+}
+
+static int
+dvr_entry_class_removal_set(void *o, const void *v)
+{
+  dvr_entry_t *de = (dvr_entry_t *)o;
+  return dvr_entry_class_int_set(de, (int *)&de->de_removal, *(int *)v);
 }
 
 static int
@@ -2196,11 +2244,20 @@ const idclass_t dvr_entry_class = {
       .opts     = PO_SORTKEY,
     },
     {
-      .type     = PT_INT,
+      .type     = PT_U32,
       .id       = "retention",
-      .name     = N_("Retention"),
+      .name     = N_("DVR Log Retention (days)"),
       .off      = offsetof(dvr_entry_t, de_retention),
       .set      = dvr_entry_class_retention_set,
+      .opts     = PO_HIDDEN
+    },
+    {
+      .type     = PT_U32,
+      .id       = "removal",
+      .name     = N_("File removal (days)"),
+      .off      = offsetof(dvr_entry_t, de_removal),
+      .set      = dvr_entry_class_removal_set,
+      .opts     = PO_HIDDEN
     },
     {
       .type     = PT_INT,
@@ -2451,7 +2508,7 @@ dvr_val2pri(dvr_prio_t v)
  *
  */
 void
-dvr_entry_delete(dvr_entry_t *de)
+dvr_entry_delete(dvr_entry_t *de, int no_missed_time_resched)
 {
   dvr_config_t *cfg = de->de_config;
   htsmsg_t *m;
@@ -2468,11 +2525,12 @@ dvr_entry_delete(dvr_entry_t *de)
     *tbuf = 0;
 
   tvhlog(LOG_INFO, "dvr", "delete entry %s \"%s\" on \"%s\" start time %s, "
-        "scheduled for recording by \"%s\", retention %d days",
+        "scheduled for recording by \"%s\", retention %d days, removal %d days",
          idnode_uuid_as_sstr(&de->de_id),
         lang_str_get(de->de_title, NULL), DVR_CH_NAME(de), tbuf,
         de->de_creator ?: "",
-        dvr_entry_get_retention(de));
+        dvr_entry_get_retention_days(de),
+        dvr_entry_get_removal_days(de));
 
   if(!htsmsg_is_empty(de->de_files)) {
 #if ENABLE_INOTIFY
@@ -2486,13 +2544,18 @@ dvr_entry_delete(dvr_entry_t *de)
       m = htsmsg_field_get_map(f);
       if (m == NULL) continue;
       filename = htsmsg_get_str(m, "filename");
+      if (filename == NULL) continue;
       r = deferred_unlink(filename, rdir);
       if(r && r != -ENOENT)
         tvhlog(LOG_WARNING, "dvr", "Unable to remove file '%s' from disk -- %s",
               filename, strerror(-errno));
+      htsmsg_delete_field(m, "filename");
     }
   }
-  dvr_entry_destroy(de, 1);
+  if (no_missed_time_resched)
+    dvr_entry_set_state(de, DVR_MISSED_TIME, DVR_RS_PENDING, de->de_last_error);
+  else
+    dvr_entry_missed_time(de, de->de_last_error);
 }
 
 /**
@@ -2502,6 +2565,7 @@ dvr_entry_t *
 dvr_entry_stop(dvr_entry_t *de)
 {
   if(de->de_sched_state == DVR_RECORDING) {
+    de->de_dont_reschedule = 1;
     dvr_stop_recording(de, SM_CODE_OK, 1, 0);
     return de;
   }
@@ -2551,7 +2615,8 @@ dvr_entry_cancel_delete(dvr_entry_t *de)
     de->de_dont_reschedule = 1;
     dvr_stop_recording(de, SM_CODE_ABORTED, 1, 0);
   case DVR_COMPLETED:
-    dvr_entry_delete(de);
+    dvr_entry_delete(de, 1);
+    dvr_entry_destroy(de, 1);
     break;
 
   case DVR_MISSED_TIME:
index 423db595d2890998bbf17d717dc98618e71909f5..4d539a72b3ac513d06452386847da284ffb55b8b 100644 (file)
@@ -159,7 +159,7 @@ dvr_timerec_check(dvr_timerec_entry_t *dte)
                          start, stop, 0, 0, title, NULL,
                          NULL, NULL, NULL, dte->dte_owner, dte->dte_creator,
                          NULL, dte, dte->dte_pri, dte->dte_retention,
-                         buf);
+                         dte->dte_removal, buf);
 
   return;
 
@@ -204,7 +204,7 @@ dvr_timerec_create(const char *uuid, htsmsg_t *conf)
 dvr_timerec_entry_t*
 dvr_timerec_create_htsp(const char *dvr_config_name, const char *title,
                             channel_t *ch, uint32_t enabled, uint32_t start, uint32_t stop,
-                            uint32_t weekdays, dvr_prio_t pri, int retention,
+                            uint32_t weekdays, dvr_prio_t pri, int retention, int removal,
                             const char *owner, const char *creator, const char *comment, 
                             const char *name, const char *directory)
 {
@@ -216,6 +216,7 @@ dvr_timerec_create_htsp(const char *dvr_config_name, const char *title,
 
   htsmsg_add_u32(conf, "enabled",     enabled > 0 ? 1 : 0);
   htsmsg_add_u32(conf, "retention",   retention);
+  htsmsg_add_u32(conf, "removal",     removal);
   htsmsg_add_u32(conf, "pri",         pri);
   htsmsg_add_str(conf, "title",       title);
   htsmsg_add_str(conf, "config_name", dvr_config_name ?: "");
@@ -627,11 +628,17 @@ const idclass_t dvr_timerec_entry_class = {
       .opts     = PO_SORTKEY,
     },
     {
-      .type     = PT_INT,
+      .type     = PT_U32,
       .id       = "retention",
-      .name     = N_("Retention"),
+      .name     = N_("DVR Log Retention (days)"),
       .off      = offsetof(dvr_timerec_entry_t, dte_retention),
     },
+    {
+      .type     = PT_U32,
+      .id       = "removal",
+      .name     = N_("File removal (days)"),
+      .off      = offsetof(dvr_timerec_entry_t, dte_removal),
+    },
     {
       .type     = PT_STR,
       .id       = "config_name",
@@ -768,10 +775,21 @@ timerec_destroy_by_config(dvr_config_t *kcfg, int delconf)
 /**
  *
  */
-int
-dvr_timerec_get_retention( dvr_timerec_entry_t *dte )
+uint32_t
+dvr_timerec_get_retention_days( dvr_timerec_entry_t *dte )
 {
   if (dte->dte_retention > 0)
     return dte->dte_retention;
-  return dte->dte_config->dvr_retention_days;
+  return dvr_retention_cleanup(dte->dte_config->dvr_retention_days);
+}
+
+/**
+ *
+ */
+uint32_t
+dvr_timerec_get_removal_days( dvr_timerec_entry_t *dte )
+{
+  if (dte->dte_removal > 0)
+    return dte->dte_removal;
+  return dvr_retention_cleanup(dte->dte_config->dvr_removal_days);
 }
index 69f9cc8e38af6994c8fb35af7d7a113869e30fdb..49b4705d8fef3d04c6b121cc8e67356a62f0e0be 100644 (file)
@@ -722,7 +722,8 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method, const char *lang)
   htsmsg_add_s64(out, "stop",        de->de_stop);
   htsmsg_add_s64(out, "startExtra",  dvr_entry_get_extra_time_pre(de));
   htsmsg_add_s64(out, "stopExtra",   dvr_entry_get_extra_time_post(de));
-  htsmsg_add_u32(out, "retention",   dvr_entry_get_retention(de));
+  htsmsg_add_u32(out, "retention",   dvr_entry_get_retention_days(de));
+  htsmsg_add_u32(out, "removal",     dvr_entry_get_removal_days(de));
   htsmsg_add_u32(out, "priority",    de->de_pri);
   htsmsg_add_u32(out, "contentType", de->de_content_type);
 
@@ -820,7 +821,8 @@ htsp_build_autorecentry(dvr_autorec_entry_t *dae, const char *method)
   htsmsg_add_u32(out, "enabled",     dae->dae_enabled);
   htsmsg_add_u32(out, "maxDuration", dae->dae_maxduration);
   htsmsg_add_u32(out, "minDuration", dae->dae_minduration);
-  htsmsg_add_u32(out, "retention",   dvr_autorec_get_retention(dae));
+  htsmsg_add_u32(out, "retention",   dvr_autorec_get_retention_days(dae));
+  htsmsg_add_u32(out, "removal",     dvr_autorec_get_removal_days(dae));
   htsmsg_add_u32(out, "daysOfWeek",  dae->dae_weekdays);
   if (dae->dae_start >= 0 && dae->dae_start_window >= 0) {
     if (dae->dae_start > dae->dae_start_window)
@@ -871,7 +873,8 @@ htsp_build_timerecentry(dvr_timerec_entry_t *dte, const char *method)
   htsmsg_add_str(out, "id",          idnode_uuid_as_sstr(&dte->dte_id));
   htsmsg_add_u32(out, "enabled",     dte->dte_enabled);
   htsmsg_add_u32(out, "daysOfWeek",  dte->dte_weekdays);
-  htsmsg_add_u32(out, "retention",   dvr_timerec_get_retention(dte));
+  htsmsg_add_u32(out, "retention",   dvr_timerec_get_retention_days(dte));
+  htsmsg_add_u32(out, "removal",     dvr_timerec_get_removal_days(dte));
   htsmsg_add_u32(out, "priority",    dte->dte_pri);
   htsmsg_add_s32(out, "start",       dte->dte_start);
   htsmsg_add_s32(out, "stop",        dte->dte_stop);
@@ -1516,7 +1519,7 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   dvr_entry_sched_state_t dvr_status;
   const char *dvr_config_name, *title, *desc, *subtitle, *lang, *comment;
   int64_t start, stop, start_extra, stop_extra;
-  uint32_t u32, priority, retention;
+  uint32_t u32, priority, retention, removal;
   channel_t *ch = NULL;
   int enabled;
 
@@ -1537,6 +1540,8 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
     priority = DVR_PRIO_NORMAL;
   if(htsmsg_get_u32(in, "retention", &retention))
     retention = 0;
+  if(htsmsg_get_u32(in, "removal", &removal))
+    removal = 0;
   comment = htsmsg_get_str(in, "comment");
   if (!(lang  = htsmsg_get_str(in, "language")))
     lang = htsp->htsp_language;
@@ -1570,7 +1575,7 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
                                title, subtitle, desc, lang, 0,
                                htsp->htsp_granted_access->aa_username,
                                htsp->htsp_granted_access->aa_representative,
-                               NULL, priority, retention, comment);
+                               NULL, priority, retention, removal, comment);
 
   /* Event timer */
   } else {
@@ -1578,7 +1583,7 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
                                    start_extra, stop_extra,
                                    htsp->htsp_granted_access->aa_username,
                                    htsp->htsp_granted_access->aa_representative,
-                                   NULL, priority, retention, comment);
+                                   NULL, priority, retention, removal, comment);
   }
 
   dvr_status = de != NULL ? de->de_sched_state : DVR_NOSTATE;
@@ -1611,7 +1616,7 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   htsmsg_t *out;
   uint32_t dvrEntryId, u32;
   dvr_entry_t *de;
-  time_t start, stop, start_extra, stop_extra, priority, retention;
+  time_t start, stop, start_extra, stop_extra, priority, retention, removal;
   const char *title, *subtitle, *desc, *lang;
   channel_t *channel = NULL;
   int enabled;
@@ -1640,6 +1645,7 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   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",  0);
+  removal     = htsmsg_get_u32_or_default(in, "removal",    0);
   priority    = htsmsg_get_u32_or_default(in, "priority",   DVR_PRIO_NORMAL);
   title       = htsmsg_get_str(in, "title");
   subtitle    = htsmsg_get_str(in, "subtitle");
@@ -1647,7 +1653,7 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   lang        = htsmsg_get_str(in, "language") ?: htsp->htsp_language;
 
   de = dvr_entry_update(de, enabled, channel, title, subtitle, desc, lang, start, stop,
-                        start_extra, stop_extra, priority, retention);
+                        start_extra, stop_extra, priority, retention, removal);
 
   //create response
   out = htsmsg_create_map();
@@ -1731,7 +1737,7 @@ htsp_method_addAutorecEntry(htsp_connection_t *htsp, htsmsg_t *in)
   const char *dvr_config_name, *title, *comment, *name, *directory;
   int64_t start_extra, stop_extra;
   uint32_t u32, days_of_week, priority, min_duration, max_duration, dup_detect;
-  uint32_t retention, enabled, fulltext;
+  uint32_t retention, removal, enabled, fulltext;
   int32_t approx_time, start, start_window;
   channel_t *ch = NULL;
 
@@ -1749,6 +1755,8 @@ htsp_method_addAutorecEntry(htsp_connection_t *htsp, htsmsg_t *in)
     min_duration = 0;    // 0 = any
   if(htsmsg_get_u32(in, "retention", &retention))
     retention = 0;       // 0 = dvr config
+  if(htsmsg_get_u32(in, "removal", &retention))
+    removal = 0;         // 0 = dvr config
   if(htsmsg_get_u32(in, "daysOfWeek", &days_of_week))
     days_of_week = 0x7f; // all days
   if(htsmsg_get_u32(in, "priority", &priority))
@@ -1790,7 +1798,8 @@ htsp_method_addAutorecEntry(htsp_connection_t *htsp, htsmsg_t *in)
 
   dae = dvr_autorec_create_htsp(dvr_config_name, title, fulltext,
       ch, enabled, start, start_window, days_of_week,
-      start_extra, stop_extra, priority, retention, min_duration, max_duration, dup_detect,
+      start_extra, stop_extra, priority, retention, removal,
+      min_duration, max_duration, dup_detect,
       htsp->htsp_granted_access->aa_username, htsp->htsp_granted_access->aa_representative,
       comment, name, directory);
 
@@ -1849,7 +1858,7 @@ htsp_method_addTimerecEntry(htsp_connection_t *htsp, htsmsg_t *in)
   htsmsg_t *out;
   dvr_timerec_entry_t *dte;
   const char *dvr_config_name, *title, *comment, *name, *directory;
-  uint32_t u32, days_of_week, priority, retention, start = 0, stop = 0, enabled;
+  uint32_t u32, days_of_week, priority, retention, removal, start = 0, stop = 0, enabled;
   channel_t *ch = NULL;
 
   /* Options */
@@ -1867,6 +1876,8 @@ htsp_method_addTimerecEntry(htsp_connection_t *htsp, htsmsg_t *in)
 
   if(htsmsg_get_u32(in, "retention", &retention))
     retention = 0;       // 0 = dvr config
+  if(htsmsg_get_u32(in, "removal", &removal))
+    removal = 0;         // 0 = dvr config
   if(htsmsg_get_u32(in, "daysOfWeek", &days_of_week))
     days_of_week = 0x7f; // all days
   if(htsmsg_get_u32(in, "priority", &priority))
@@ -1887,7 +1898,7 @@ htsp_method_addTimerecEntry(htsp_connection_t *htsp, htsmsg_t *in)
 
   /* Add actual timerec */
   dte = dvr_timerec_create_htsp(dvr_config_name, title, ch, enabled, start, stop, days_of_week,
-      priority, retention, htsp->htsp_granted_access->aa_username,
+      priority, retention, removal, htsp->htsp_granted_access->aa_username,
       htsp->htsp_granted_access->aa_representative, comment, name, directory);
 
   /* create response */
index 1d5df9b6577e08f0b05e418f9752527e38721565..f629c4289b00d626624e18adca6d4966ac28bdbc 100644 (file)
@@ -319,7 +319,7 @@ 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, 0, "simpleui");
+                                  DVR_PRIO_NORMAL, 0, 0, "simpleui");
   } else if(de != NULL && (http_arg_get(&hc->hc_req_args, "cancel")) != NULL) {
     de = dvr_entry_cancel(de);
   }
index 56ee0dafe131af9829a005acbdbe176da4a0971c..f8c9077924cdb175e17b29d9210a66dedd0115b6 100644 (file)
@@ -158,7 +158,8 @@ tvheadend.dvr_upcoming = function(panel, index) {
     var list = 'disp_title,start,start_extra,stop,stop_extra,' +
                'channel,config_name,comment';
     var elist = 'enabled,' +
-                (tvheadend.accessUpdate.admin ? list + ',owner,creator' : list);
+                (tvheadend.accessUpdate.admin ?
+                  list + ',retention,removal,owner,creator' : list);
 
     var stopButton = {
         name: 'stop',
@@ -320,7 +321,7 @@ tvheadend.dvr_finished = function(panel, index) {
         titleP: _('Finished Recordings'),
         iconCls: 'finishedRec',
         tabIndex: index,
-        edit: { params: { list: tvheadend.admin ? "owner,comment" : "comment" } },
+        edit: { params: { list: tvheadend.admin ? "owner,retention,removal,comment" : "comment" } },
         del: true,
         delquestion: _('Do you really want to delete the selected recordings?') + '<br/><br/>' +
                      _('The associated file will be removed from storage.'),
@@ -503,6 +504,7 @@ tvheadend.autorec_editor = function(panel, index) {
             pri:          { width: 80 },
             dedup:        { width: 160 },
             retention:    { width: 80 },
+            removal:      { width: 80 },
             config_name:  { width: 120 },
             owner:        { width: 100 },
             creator:      { width: 200 },
@@ -556,6 +558,8 @@ tvheadend.timerec_editor = function(panel, index) {
             start:        { width: 120 },
             stop:         { width: 120 },
             pri:          { width: 80 },
+            retention:    { width: 80 },
+            removal:      { width: 80 },
             config_name:  { width: 120 },
             owner:        { width: 100 },
             creator:      { width: 200 },