From: Jaroslav Kysela Date: Thu, 8 Oct 2015 18:34:54 +0000 (+0200) Subject: DVR: Implement automatic file removal, fixes #3141 X-Git-Tag: v4.2.1~1963 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8a3a07fa2c18f01befa1d4201915071425cd5e89;p=thirdparty%2Ftvheadend.git DVR: Implement automatic file removal, fixes #3141 --- diff --git a/src/api/api_dvr.c b/src/api/api_dvr.c index 9eaaf1878..943564567 100644 --- a/src/api/api_dvr.c +++ b/src/api/api_dvr.c @@ -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); } diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 7cd363162..f04247178 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -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 ); /** * diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 946078148..ae69d6b71 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -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); } diff --git a/src/dvr/dvr_config.c b/src/dvr/dvr_config.c index a04ee627b..d74c5a7f5 100644 --- a/src/dvr/dvr_config.c +++ b/src/dvr/dvr_config.c @@ -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", diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index b99e03ab5..23f1b463d 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -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: diff --git a/src/dvr/dvr_timerec.c b/src/dvr/dvr_timerec.c index 423db595d..4d539a72b 100644 --- a/src/dvr/dvr_timerec.c +++ b/src/dvr/dvr_timerec.c @@ -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); } diff --git a/src/htsp_server.c b/src/htsp_server.c index 69f9cc8e3..49b4705d8 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -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 */ diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c index 1d5df9b65..f629c4289 100644 --- a/src/webui/simpleui.c +++ b/src/webui/simpleui.c @@ -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); } diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index 56ee0dafe..f8c907792 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -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?') + '

' + _('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 },