From: Glenn-1990 Date: Sun, 11 Sep 2016 18:18:41 +0000 (+0200) Subject: implement minimal retention period X-Git-Tag: v4.2.1~295 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eed35c0c3a93bc9380deb09e742aab705c109831;p=thirdparty%2Ftvheadend.git implement minimal retention period --- diff --git a/src/api/api_dvr.c b/src/api/api_dvr.c index be0b039e5..d7e3074e1 100644 --- a/src/api/api_dvr.c +++ b/src/api/api_dvr.c @@ -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); } diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 8f120af46..3e6b1e3d1 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -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); diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 66a3469fc..58889e303 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -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; } diff --git a/src/dvr/dvr_config.c b/src/dvr/dvr_config.c index 45e6f9087..643710450 100644 --- a/src/dvr/dvr_config.c +++ b/src/dvr/dvr_config.c @@ -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, diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 78524861e..9f710fac3 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -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); + } + } } /** diff --git a/src/dvr/dvr_timerec.c b/src/dvr/dvr_timerec.c index f752db6d1..c84a86b8c 100644 --- a/src/dvr/dvr_timerec.c +++ b/src/dvr/dvr_timerec.c @@ -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; } diff --git a/src/dvr/dvr_vfsmgr.c b/src/dvr/dvr_vfsmgr.c index 93a8e6900..60b8ebc5c 100644 --- a/src/dvr/dvr_vfsmgr.c +++ b/src/dvr/dvr_vfsmgr.c @@ -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) diff --git a/src/htsp_server.c b/src/htsp_server.c index f1177c283..43a27e04e 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -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(); } diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c index cbe94e6b9..6784e30bf 100644 --- a/src/webui/simpleui.c +++ b/src/webui/simpleui.c @@ -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); }