]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Implemented "moved recordings" in order to improve the user experience
authorGlenn-1990 <g_christiaensen@msn.com>
Tue, 4 Oct 2016 20:03:05 +0000 (22:03 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 6 Oct 2016 07:40:47 +0000 (09:40 +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_vfsmgr.c
src/htsp_server.c
src/webui/static/app/dvr.js

index 574c3c60fb0a9d560e3a0dea63b966f468c4c32b..510572eca0784cbca3b24726574ebdf063b36ea5 100644 (file)
@@ -69,10 +69,16 @@ static int is_dvr_entry_finished(dvr_entry_t *entry)
 {
   dvr_entry_sched_state_t state = entry->de_sched_state;
   return state == DVR_COMPLETED && !entry->de_last_error &&
-         (dvr_get_filesize(entry, 0) != -1 || entry->de_file_removed) &&
+         dvr_get_filesize(entry, 0) != -1 && !entry->de_file_removed &&
          entry->de_data_errors < DVR_MAX_DATA_ERRORS;
 }
 
+static int is_dvr_entry_removed(dvr_entry_t *entry)
+{
+  dvr_entry_sched_state_t state = entry->de_sched_state;
+  return ((state == DVR_COMPLETED || state == DVR_MISSED_TIME) && entry->de_file_removed);
+}
+
 static int is_dvr_entry_upcoming(dvr_entry_t *entry)
 {
   dvr_entry_sched_state_t state = entry->de_sched_state;
@@ -85,6 +91,8 @@ static int is_dvr_entry_failed(dvr_entry_t *entry)
     return 0;
   if (is_dvr_entry_upcoming(entry))
     return 0;
+  if (is_dvr_entry_removed(entry))
+    return 0;
   return 1;
 }
 
@@ -131,6 +139,17 @@ api_dvr_entry_grid_failed
       idnode_set_add(ins, (idnode_t*)de, &conf->filter, perm->aa_lang_ui);
 }
 
+static void
+api_dvr_entry_grid_removed
+  ( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
+{
+  dvr_entry_t *de;
+
+  LIST_FOREACH(de, &dvrentries, de_global_link)
+    if (is_dvr_entry_removed(de))
+      idnode_set_add(ins, (idnode_t*)de, &conf->filter, perm->aa_lang_ui);
+}
+
 static int
 api_dvr_entry_create
   ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
@@ -325,6 +344,21 @@ api_dvr_entry_cancel
   return api_idnode_handler(perm, args, resp, api_dvr_cancel, "cancel", 0);
 }
 
+static void
+api_dvr_remove(access_t *perm, idnode_t *self)
+{
+  dvr_entry_t *de = (dvr_entry_t *)self;
+  if (de->de_sched_state != DVR_SCHEDULED && de->de_sched_state != DVR_NOSTATE)
+    dvr_entry_cancel_remove(de, 0);
+}
+
+static int
+api_dvr_entry_remove
+  ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+  return api_idnode_handler(perm, args, resp, api_dvr_remove, "remove", 0);
+}
+
 static void
 api_dvr_move_finished(access_t *perm, idnode_t *self)
 {
@@ -508,13 +542,15 @@ void api_dvr_init ( void )
     { "dvr/entry/grid_upcoming",   ACCESS_RECORDER, api_idnode_grid, api_dvr_entry_grid_upcoming },
     { "dvr/entry/grid_finished",   ACCESS_RECORDER, api_idnode_grid, api_dvr_entry_grid_finished },
     { "dvr/entry/grid_failed",     ACCESS_RECORDER, api_idnode_grid, api_dvr_entry_grid_failed },
+    { "dvr/entry/grid_removed",    ACCESS_RECORDER, api_idnode_grid, api_dvr_entry_grid_removed },
     { "dvr/entry/create",          ACCESS_RECORDER, api_dvr_entry_create, NULL },
     { "dvr/entry/create_by_event", ACCESS_RECORDER, api_dvr_entry_create_by_event, NULL },
     { "dvr/entry/rerecord/toggle", ACCESS_RECORDER, api_dvr_entry_rerecord_toggle, NULL },
     { "dvr/entry/rerecord/deny",   ACCESS_RECORDER, api_dvr_entry_rerecord_deny, NULL },
     { "dvr/entry/rerecord/allow",  ACCESS_RECORDER, api_dvr_entry_rerecord_allow, NULL },
-    { "dvr/entry/stop",            ACCESS_RECORDER, api_dvr_entry_stop, NULL },
-    { "dvr/entry/cancel",          ACCESS_RECORDER, api_dvr_entry_cancel, NULL },
+    { "dvr/entry/stop",            ACCESS_RECORDER, api_dvr_entry_stop, NULL },   /* Stop active recording gracefully */
+    { "dvr/entry/cancel",          ACCESS_RECORDER, api_dvr_entry_cancel, NULL }, /* Cancel scheduled or active recording */
+    { "dvr/entry/remove",          ACCESS_RECORDER, api_dvr_entry_remove, NULL }, /* Remove recorded files from storage */
     { "dvr/entry/filemoved",       ACCESS_ADMIN,    api_dvr_entry_file_moved, NULL },
     { "dvr/entry/move/finished",   ACCESS_RECORDER, api_dvr_entry_move_finished, NULL },
     { "dvr/entry/move/failed",     ACCESS_RECORDER, api_dvr_entry_move_failed, NULL },
index 3e6b1e3d144d0b81b9381eba3d8cf53d5e15a8c5..054dcd129f4261d24164a4649e29d09325657702 100644 (file)
@@ -54,7 +54,6 @@ 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;
@@ -588,9 +587,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, int forcedestroy);
+void dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord);
 
-void dvr_entry_trydestroy(dvr_entry_t *de);
+void dvr_entry_cancel_remove(dvr_entry_t *de, int rerecord);
 
 int dvr_entry_file_moved(const char *src, const char *dst);
 
index 58889e30372893ce991afaa7793bc8e105926ce9..5258f87fb10520f47a3de9bf369b870e09c3702d 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, 0);
+      dvr_entry_cancel_remove(de_prev, 0);
     }
   }
 }
index 643710450a376ed45b0ff038e46a96135d6a8a35..32f2fca09b1dddbb65af7c6916eff78e0338c183 100644 (file)
@@ -178,7 +178,6 @@ 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_retention_minimal = DVR_RET_MIN_DISABLED;
   cfg->dvr_removal_days = DVR_RET_REM_FOREVER;
   cfg->dvr_clone = 1;
   cfg->dvr_tag_files = 1;
@@ -762,29 +761,6 @@ dvr_config_class_retention_list ( void *o, const char *lang )
   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);
-}
-
 static htsmsg_t *
 dvr_config_class_extra_list(void *o, const char *lang)
 {
@@ -924,24 +900,13 @@ 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 information about recordings. Once this period is exceeded, duplicate detection will not be possible for this recording."),
+      .desc     = N_("Number of days to retain information about recordings. Once this period is exceeded, duplicate detection will not be possible anymore."),
       .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",
index 7414a4e6fa53286b9c5d205e665ba726a7e1b555..ba752410e9fccd6e9baf414fc9a1eeca2cac2fdf 100644 (file)
@@ -494,8 +494,10 @@ dvr_entry_retention_timer(dvr_entry_t *de)
       dvr_entry_deferred_destroy(de); // also remove database entry
       return;
     }
-    if (save)
+    if (save) {
       idnode_changed(&de->de_id);
+      htsp_dvr_entry_update(de);
+    }
   }
 
   if (retention < DVR_RET_ONREMOVE) {
@@ -1082,12 +1084,12 @@ dvr_entry_rerecord(dvr_entry_t *de)
       if (fsize1 / 5 < fsize2 / 6) {
         goto not_so_good;
       } else {
-        dvr_entry_cancel_delete(de2, 1, 1);
+        dvr_entry_cancel_delete(de2, 1);
       }
     } else if (de->de_sched_state == DVR_COMPLETED) {
       if(dvr_get_filesize(de, 0) == -1) {
 delete_me:
-        dvr_entry_cancel_delete(de, 0, 1);
+        dvr_entry_cancel_delete(de, 0);
         dvr_entry_rerecord(de2);
         return 1;
       }
@@ -2062,7 +2064,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, 1);
+    dvr_entry_cancel_delete(de, 1);
     return;
   }
 
@@ -2153,7 +2155,7 @@ static void
 dvr_entry_class_delete(idnode_t *self)
 {
   dvr_entry_t *de = (dvr_entry_t *)self;
-  dvr_entry_cancel_delete(de, 0, 0);
+  dvr_entry_cancel_delete(de, 0);
 }
 
 static int
@@ -3507,7 +3509,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, 1);
+      dvr_entry_cancel_delete(de->de_child, 0);
   } else {
     de->de_dont_rerecord = 0;
     dvr_entry_rerecord(de);
@@ -3540,7 +3542,7 @@ dvr_entry_stop(dvr_entry_t *de)
 }
 
 /**
- *
+ * Cancels an upcoming or active recording
  */
 dvr_entry_t *
 dvr_entry_cancel(dvr_entry_t *de, int rerecord)
@@ -3551,10 +3553,11 @@ dvr_entry_cancel(dvr_entry_t *de, int rerecord)
   case DVR_RECORDING:
     dvr_stop_recording(de, SM_CODE_ABORTED, 1, 0);
     break;
-
-  case DVR_SCHEDULED:
+  /* Cancel is not valid for finished recordings */
   case DVR_COMPLETED:
   case DVR_MISSED_TIME:
+    return de;
+  case DVR_SCHEDULED:
   case DVR_NOSTATE:
     dvr_entry_destroy(de, 1);
     de = NULL;
@@ -3575,27 +3578,31 @@ dvr_entry_cancel(dvr_entry_t *de, int rerecord)
 }
 
 /**
- *
+ * Called by 'dvr_entry_cancel_remove' and 'dvr_entry_cancel_delete'
+ * delete = 0 -> remove finished and active recordings (visible as removed)
+ * delete = 1 -> delete finished and active recordings (not visible anymore)
  */
-void
-dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord, int forcedestroy)
+static void
+dvr_entry_cancel_delete_remove(dvr_entry_t *de, int rerecord, int _delete)
 {
   dvr_entry_t *parent = de->de_parent;
   dvr_autorec_entry_t *dae = de->de_autorec;
+  int save;
 
   switch(de->de_sched_state) {
   case DVR_RECORDING:
     dvr_stop_recording(de, SM_CODE_ABORTED, 1, 0);
+  case DVR_MISSED_TIME:
   case DVR_COMPLETED:
-    dvr_entry_delete(de);
-    if (forcedestroy)
-      dvr_entry_destroy(de, 1);
-    else
-      dvr_entry_trydestroy(de);
+    save = dvr_entry_delete(de); /* Remove files */
+    if (_delete || dvr_entry_get_retention_days(de) == DVR_RET_ONREMOVE)
+      dvr_entry_destroy(de, 1);  /* Delete database */
+    else if (save) {
+      idnode_changed(&de->de_id);
+      htsp_dvr_entry_update(de);
+    }
     break;
-
   case DVR_SCHEDULED:
-  case DVR_MISSED_TIME:
   case DVR_NOSTATE:
     dvr_entry_destroy(de, 1);
     break;
@@ -3617,39 +3624,27 @@ dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord, int forcedestroy)
 }
 
 /**
- * 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.
+ * Upcoming recording  -> cancel
+ * Active recording    -> cancel + remove
+ * Finished recordings -> remove
+ * The latter 2 will be visible as "removed recording"
  */
 void
-dvr_entry_trydestroy(dvr_entry_t *de)
+dvr_entry_cancel_remove(dvr_entry_t *de, int rerecord)
 {
-  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_dont_reschedule = 1;
-      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 */
+  dvr_entry_cancel_delete_remove(de, rerecord, 0);
+}
 
-      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);
-    }
-  }
+/**
+ * Upcoming recording  -> cancel
+ * Active recording    -> abort + delete
+ * Finished recordings -> delete
+ * The latter 2 will NOT be visible as "removed recording"
+ */
+void
+dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord)
+{
+  dvr_entry_cancel_delete_remove(de, rerecord, 1);
 }
 
 /**
index 60b8ebc5ca62bacd4d7a160f89637a2c744a6a24..a5ae2d7eebcbc421418495b186b02b7dc6e4f55f 100644 (file)
@@ -262,13 +262,7 @@ dvr_disk_space_cleanup(dvr_config_t *cfg)
               lang_str_get(oldest->de_title, NULL), tbuf, TOMIB(fileSize));
 
       dvr_disk_space_config_lastdelete = mclk();
-      if (dvr_entry_get_retention_days(oldest) == DVR_RET_ONREMOVE) {
-        dvr_entry_delete(oldest);     // delete actual file
-        dvr_entry_destroy(oldest, 1); // also delete database entry
-      } else {
-        if (dvr_entry_delete(oldest)) // delete actual file
-          idnode_changed(&oldest->de_id);
-      }
+      dvr_entry_cancel_remove(oldest, 0); /* Remove stored files and mark as "removed" */
     } else {
       tvhwarn(LS_DVR, "%s \"until space needed\" recordings found for config \"%s\", you are running out of disk space very soon!",
               loops > 0 ? "Not enough" : "No", configName);
index 43a27e04e8eba8e6c307c4cac69016d8e2218034..dc00956224d5641358e264103afa502d9335f813 100644 (file)
@@ -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, 0);
+  dvr_entry_cancel_remove(de, 0);
 
   return htsp_success();
 }
index 1da74d23fefdc897b922f7de808a54f38701c094..941b3e3dae58095927083165d36f319e6887b2d9 100644 (file)
@@ -407,6 +407,36 @@ tvheadend.dvr_finished = function(panel, index) {
             }
         }
     };
+    
+    var removeButton = {
+        name: 'remove',
+        builder: function() {
+            return new Ext.Toolbar.Button({
+                tooltip: _('Remove the selected recording from storage'),
+                iconCls: 'remove',
+                text: _('Remove'),
+                disabled: true
+            });
+        },
+        callback: function(conf, e, store, select) {
+            var r = select.getSelections();
+            if (r && r.length > 0) {
+                var uuids = [];
+                for (var i = 0; i < r.length; i++)
+                    uuids.push(r[i].id);
+                tvheadend.AjaxConfirm({
+                    url: 'api/dvr/entry/remove',
+                    params: {
+                        uuid: Ext.encode(uuids)
+                    },
+                    success: function(d) {
+                        store.reload();
+                    },
+                    question: _('Do you really want to remove the selected recordings from storage?')   
+                });
+            }
+        }
+    };
 
     function selected(s, abuttons) {
         var r = s.getSelections();
@@ -414,6 +444,7 @@ tvheadend.dvr_finished = function(panel, index) {
         abuttons.download.setDisabled(!b);
         abuttons.rerecord.setDisabled(!b);
         abuttons.move.setDisabled(!b);
+        abuttons.remove.setDisabled(!b);
     }
 
     tvheadend.idnode_grid(panel, {
@@ -425,9 +456,7 @@ tvheadend.dvr_finished = function(panel, index) {
         iconCls: 'finishedRec',
         tabIndex: index,
         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.'),
+        del: false,
         list: 'disp_title,disp_subtitle,episode,start_real,stop_real,' +
               'duration,filesize,channelname,owner,creator,' +
               'config_name,sched_status,errors,data_errors,url,comment',
@@ -454,7 +483,7 @@ tvheadend.dvr_finished = function(panel, index) {
                     return tvheadend.playLink('play/dvrfile/' + r.id, title);
                 }
             }],
-        tbar: [downloadButton, rerecordButton, moveButton],
+        tbar: [removeButton, downloadButton, rerecordButton, moveButton],
         selected: selected
     });
 
@@ -598,6 +627,74 @@ tvheadend.dvr_failed = function(panel, index) {
     return panel;
 };
 
+/**
+ *
+ */
+tvheadend.dvr_removed = function(panel, index) {
+
+    var actions = tvheadend.dvrRowActions();
+
+    var rerecordButton = {
+        name: 'rerecord',
+        builder: function() {
+            return new Ext.Toolbar.Button({
+                tooltip: _('Toggle re-record functionality'),
+                iconCls: 'rerecord',
+                text: _('Re-record'),
+                disabled: true
+            });
+        },
+        callback: function(conf, e, store, select) {
+            var r = select.getSelections();
+            if (r && r.length > 0) {
+                var uuids = [];
+                for (var i = 0; i < r.length; i++)
+                    uuids.push(r[i].id);
+                tvheadend.Ajax({
+                    url: 'api/dvr/entry/rerecord/toggle',
+                    params: {
+                        uuid: Ext.encode(uuids)
+                    },
+                    success: function(d) {
+                        store.reload();
+                    }
+                });
+            }
+        }
+    };
+    
+    function selected(s, abuttons) {
+        var r = s.getSelections();
+        abuttons.rerecord.setDisabled(r.length <= 0);
+    }
+
+    tvheadend.idnode_grid(panel, {
+        url: 'api/dvr/entry',
+        gridURL: 'api/dvr/entry/grid_removed',
+        readonly: true,
+        titleS: _('Removed Recording'),
+        titleP: _('Removed Recordings'),
+        iconCls: 'remove',
+        tabIndex: index,
+        uilevel: 'expert',
+        edit: { params: { list: tvheadend.admin ? "retention,owner,comment" : "retention,comment" } },
+        del: true,
+        list: 'disp_title,disp_subtitle,episode,start_real,stop_real,' +
+              'duration,channelname,owner,creator,config_name,' +
+              'sched_status,errors,data_errors,url,comment',
+        sort: {
+          field: 'start_real',
+          direction: 'ASC'
+        },
+        plugins: [actions],
+        lcol: [actions],
+        tbar: [rerecordButton],
+        selected: selected
+    });
+
+    return panel;
+};
+
 /**
  *
  */
@@ -751,7 +848,8 @@ tvheadend.dvr = function(panel, index) {
     tvheadend.dvr_upcoming(p, 0);
     tvheadend.dvr_finished(p, 1);
     tvheadend.dvr_failed(p, 2);
-    tvheadend.autorec_editor(p, 3);
-    tvheadend.timerec_editor(p, 4);
+    tvheadend.dvr_removed(p, 3);
+    tvheadend.autorec_editor(p, 4);
+    tvheadend.timerec_editor(p, 5);
     return p;
 }