e, 0, 0,
perm->aa_username,
perm->aa_representative,
- NULL, DVR_PRIO_NORMAL, 0, 0, comment);
+ NULL, DVR_PRIO_NORMAL, DVR_RET_DVRCONFIG, DVR_RET_DVRCONFIG, comment);
if (de)
dvr_entry_save(de);
}
uint32_t dvr_extra_time_post;
uint32_t dvr_update_window;
int dvr_running;
+ uint32_t dvr_cleanup_threshold;
muxer_config_t dvr_muxcnf;
DVR_RS_EPG_WAIT,
DVR_RS_FINISHED
} 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,
+ DVR_RET_2MONTH = 60,
+ DVR_RET_3MONTH = 90,
+ DVR_RET_6MONTH = 180,
+ DVR_RET_1YEAR = 365,
+ DVR_RET_ONREMOVE = UINT32_MAX-1, // for retention only
+ DVR_RET_SPACENEED = UINT32_MAX-1, // for removal only
+ DVR_RET_FOREVER = UINT32_MAX
+} dvr_retention_t;
typedef struct dvr_entry {
int dvr_entry_get_mc(dvr_entry_t *de);
+const char *dvr_entry_get_retention_string ( dvr_entry_t *de );
+
+const char *dvr_entry_get_removal_string ( 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 );
int64_t dvr_get_filesize(dvr_entry_t *de);
+int64_t dvr_entry_claenup(dvr_entry_t *de, int64_t requiredBytes);
+
void dvr_entry_set_rerecord(dvr_entry_t *de, int cmd);
dvr_entry_t *dvr_entry_stop(dvr_entry_t *de);
void dvr_entry_cancel_delete(dvr_entry_t *de, int rerecord);
+void dvr_entry_destroy(dvr_entry_t *de, int delconf);
+
htsmsg_t *dvr_entry_class_mc_list (void *o, const char *lang);
htsmsg_t *dvr_entry_class_pri_list(void *o, const char *lang);
htsmsg_t *dvr_entry_class_config_name_list(void *o, const char *lang);
htsmsg_t *dvr_entry_class_duration_list(void *o, const char *not_set, int max, int step, const char *lang);
+htsmsg_t *dvr_entry_class_retention_list ( void *o, const char *lang );
+htsmsg_t *dvr_entry_class_removal_list ( void *o, const char *lang );
int dvr_entry_verify(dvr_entry_t *de, access_t *a, int readonly);
{
.type = PT_U32,
.id = "retention",
- .name = N_("DVR log retention (days)"),
+ .name = N_("DVR log retention"),
+ .def.i = DVR_RET_DVRCONFIG,
.off = offsetof(dvr_autorec_entry_t, dae_retention),
+ .list = dvr_entry_class_retention_list,
.opts = PO_HIDDEN | PO_EXPERT,
},
{
.type = PT_U32,
.id = "removal",
- .name = N_("DVR file retention period (days)"),
+ .name = N_("DVR file retention period"),
+ .def.i = DVR_RET_DVRCONFIG,
.off = offsetof(dvr_autorec_entry_t, dae_removal),
+ .list = dvr_entry_class_removal_list,
.opts = PO_HIDDEN | PO_EXPERT,
},
{
uint32_t
dvr_autorec_get_retention_days( dvr_autorec_entry_t *dae )
{
- if (dae->dae_retention > 0)
+ if (dae->dae_retention > 0) {
+ /* As we need the db entry when deleting the file on disk */
+ if (dvr_autorec_get_removal_days(dae) != DVR_RET_FOREVER &&
+ dvr_autorec_get_removal_days(dae) > dae->dae_retention)
+ return DVR_RET_ONREMOVE;
+
return dae->dae_retention;
+ }
return dvr_retention_cleanup(dae->dae_config->dvr_retention_days);
}
cfg->dvr_enabled = 1;
cfg->dvr_config_name = strdup(name);
- cfg->dvr_retention_days = 31;
+ cfg->dvr_retention_days = DVR_RET_ONREMOVE;
+ cfg->dvr_removal_days = DVR_RET_FOREVER;
cfg->dvr_clone = 1;
cfg->dvr_tag_files = 1;
cfg->dvr_skip_commercials = 1;
cfg->dvr_warm_time = 30;
cfg->dvr_update_window = 24 * 3600;
cfg->dvr_pathname = strdup("$t$n.$x");
+ cfg->dvr_cleanup_threshold = 2000;
/* Muxer config */
cfg->dvr_muxcnf.m_cache = MC_CACHE_DONTKEEP;
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;
+ if (cfg->dvr_cleanup_threshold < 100)
+ cfg->dvr_cleanup_threshold = 100; // as checking is only periodically, lower is not save
+ if (cfg->dvr_removal_days != DVR_RET_FOREVER &&
+ cfg->dvr_removal_days > cfg->dvr_retention_days)
+ cfg->dvr_retention_days = DVR_RET_ONREMOVE;
idnode_save(&cfg->dvr_id, m);
hts_settings_save(m, "dvr/config/%s", idnode_uuid_as_sstr(&cfg->dvr_id));
htsmsg_destroy(m);
return strtab2htsmsg(tab, 1, lang);
}
+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_("Until space needed"), DVR_RET_SPACENEED },
+ { N_("Forever"), DVR_RET_FOREVER },
+ };
+ return strtab2htsmsg_u32(tab, 1, lang);
+}
+
+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_("On file removal"), DVR_RET_ONREMOVE },
+ { N_("Forever"), DVR_RET_FOREVER },
+ };
+ return strtab2htsmsg_u32(tab, 1, lang);
+}
+
static htsmsg_t *
dvr_config_class_extra_list(void *o, const char *lang)
{
{
.type = PT_U32,
.id = "retention-days",
- .name = N_("DVR log retention period (days)"),
+ .name = N_("DVR log retention period"),
.off = offsetof(dvr_config_t, dvr_retention_days),
- .def.u32 = 31,
+ .def.u32 = DVR_RET_ONREMOVE,
+ .list = dvr_config_class_retention_list,
.group = 1,
},
{
.type = PT_U32,
.id = "removal-days",
- .name = N_("DVR file retention period (days)"),
+ .name = N_("DVR file retention period"),
.off = offsetof(dvr_config_t, dvr_removal_days),
+ .def.u32 = DVR_RET_FOREVER,
+ .list = dvr_config_class_removal_list,
.group = 1,
},
{
.off = offsetof(dvr_config_t, dvr_storage),
.group = 2,
},
+ {
+ .type = PT_U32,
+ .id = "storage-cleanup",
+ .name = N_("\"Until space needed\" cleanup below (MB)"),
+ .off = offsetof(dvr_config_t, dvr_cleanup_threshold),
+ .def.i = 2000, //2000 MB
+ .group = 2,
+ },
{
.type = PT_PERM,
.id = "file-permissions",
static gtimer_t dvr_dbus_timer;
#endif
-static void dvr_entry_destroy(dvr_entry_t *de, int delconf);
static void dvr_entry_set_timer(dvr_entry_t *de);
static void dvr_timer_rerecord(void *aux);
static void dvr_timer_expire(void *aux);
return extra;
}
+const char *
+dvr_entry_get_retention_string ( dvr_entry_t *de )
+{
+ char buf[24];
+ uint32_t retention = dvr_entry_get_retention_days(de);
+
+ if (retention < DVR_RET_ONREMOVE)
+ snprintf(buf, sizeof(buf), "%i days", retention);
+ else if (retention == DVR_RET_ONREMOVE)
+ return strdup("On file removal");
+ else
+ return strdup("Forever");
+
+ return strdup(buf);
+}
+
+const char *
+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_SPACENEED)
+ snprintf(buf, sizeof(buf), "%i days", removal);
+ else if (removal == DVR_RET_SPACENEED)
+ return strdup("Until space needed");
+ else
+ return strdup("Forever");
+
+ return strdup(buf);
+}
+
uint32_t
dvr_entry_get_retention_days( dvr_entry_t *de )
{
- if (de->de_retention > 0)
+ if (de->de_retention > 0) {
+ /* 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)
+ return DVR_RET_ONREMOVE;
+
return de->de_retention;
+ }
return dvr_retention_cleanup(de->de_config->dvr_retention_days);
}
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 ((removal > 0 || retention == 0) && removal < DVR_RET_SPACENEED) {
if (stop > dispatch_clock) {
dvr_entry_retention_arm(de, dvr_timer_remove_files, stop);
return;
}
if (dvr_get_filename(de))
- dvr_entry_delete(de, 1);
+ dvr_entry_delete(de, 1); // delete actual file
+ if (retention == DVR_RET_ONREMOVE) {
+ dvr_entry_destroy(de, 1); // also remove database entry
+ return;
+ }
+ }
+
+ if (retention < DVR_RET_ONREMOVE) {
+ stop = de->de_stop + retention * (time_t)86400;
+ dvr_entry_retention_arm(de, dvr_timer_expire, stop);
}
- stop = de->de_stop + retention * (time_t)86400;
- dvr_entry_retention_arm(de, dvr_timer_expire, stop);
}
/*
return 1;
}
not_so_good:
- de->de_retention = 1;
+ de->de_retention = DVR_RET_ONREMOVE;
+ de->de_removal = DVR_RET_1DAY;
dvr_entry_change_parent_child(de->de_parent, NULL, NULL, 1);
dvr_entry_completed(de, SM_CODE_WEAK_STREAM);
return 0;
/**
*
*/
-static void
+void
dvr_entry_destroy(dvr_entry_t *de, int delconf)
{
if (delconf)
return;
}
- gtimer_arm_abs(&de->de_timer, dvr_timer_stop_recording, de,
+ gtimer_arm_abs(&de->de_timer, dvr_timer_stop_recording, de,
dvr_entry_get_stop_time(de));
}
return strtab2htsmsg(tab, 1, lang);
}
-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, (int *)&de->de_retention, *(int *)v);
+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_("On file removal"), DVR_RET_ONREMOVE },
+ { N_("Forever"), DVR_RET_FOREVER },
+ };
+ return strtab2htsmsg_u32(tab, 1, lang);
}
-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);
+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_("Until space needed"), DVR_RET_SPACENEED },
+ { N_("Forever"), DVR_RET_FOREVER },
+ };
+ return strtab2htsmsg_u32(tab, 1, lang);
}
static int
{
.type = PT_U32,
.id = "retention",
- .name = N_("DVR log retention (days)"),
+ .name = N_("DVR log retention"),
.off = offsetof(dvr_entry_t, de_retention),
- .set = dvr_entry_class_retention_set,
+ .def.i = DVR_RET_DVRCONFIG,
+ .list = dvr_entry_class_retention_list,
.opts = PO_HIDDEN | PO_ADVANCED
},
{
.type = PT_U32,
.id = "removal",
- .name = N_("DVR file retention period (days)"),
+ .name = N_("DVR file retention period"),
.off = offsetof(dvr_entry_t, de_removal),
- .set = dvr_entry_class_removal_set,
+ .def.i = DVR_RET_DVRCONFIG,
+ .list = dvr_entry_class_removal_list,
.opts = PO_HIDDEN | PO_ADVANCED
},
{
*tbuf = 0;
tvhlog(LOG_INFO, "dvr", "delete entry %s \"%s\" on \"%s\" start time %s, "
- "scheduled for recording by \"%s\", retention %d days, removal %d days",
+ "scheduled for recording by \"%s\", retention \"%s\" removal \"%s\"",
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_days(de),
- dvr_entry_get_removal_days(de));
+ dvr_entry_get_retention_string(de),
+ dvr_entry_get_removal_string(de));
if(!htsmsg_is_empty(de->de_files)) {
#if ENABLE_INOTIFY
/**
*
*/
+static int dvr_disk_space_config_idx;
+static int dvr_disk_space_config_size;
+static time_t dvr_disk_space_config_lastdelete;
static int64_t dvr_bfree;
static int64_t dvr_btotal;
static pthread_mutex_t dvr_disk_space_mutex;
static gtimer_t dvr_disk_space_timer;
static tasklet_t dvr_disk_space_tasklet;
+/**
+ * Cleanup old recordings for this config until the dvr_cleanup_threshold is reached
+ * Only "Keep until space needed" recordings are deleted, starting with the oldest one
+ */
+static int64_t
+dvr_disk_space_cleanup(dvr_config_t *cfg)
+{
+ dvr_entry_t *de, *oldest;
+ time_t stoptime;
+ int64_t requiredBytes, availBytes;
+ int64_t clearedBytes = 0, fileSize;
+ unsigned long int filesystemId;
+ struct statvfs diskdata;
+ struct tm tm;
+ int loops = 0;
+ char tbuf[64];
+ const char *configName;
+
+ if (!cfg || !cfg->dvr_enabled || statvfs(cfg->dvr_storage, &diskdata) == -1)
+ return -1;
+
+ filesystemId = diskdata.f_fsid;
+ availBytes = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
+ requiredBytes = (int64_t)cfg->dvr_cleanup_threshold*(int64_t)1024*(int64_t)1024;
+ configName = cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile";
+
+ /* When deleting a file from the disk, the system needs some time to actually do this */
+ /* If calling this function to fast after the previous call, statvfs might be wrong/not updated yet */
+ /* So we are risking to delete more files than needed, so allow 10s for the system to handle previous deletes */
+ if (dvr_disk_space_config_lastdelete + 10 > dispatch_clock) {
+ tvhlog(LOG_WARNING, "dvr","disk space cleanup for config \"%s\" is not allowed now", configName);
+ return -1;
+ }
+
+ if (diskdata.f_bsize * (int64_t)diskdata.f_blocks < requiredBytes) {
+ tvhlog(LOG_WARNING, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MB\" is smaller than the total disk size!",
+ configName, requiredBytes/(int64_t)1024/(int64_t)1024);
+ return -1;
+ }
+
+ tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MB\", current free space \"%ld MB\"",
+ configName, requiredBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
+
+ while (availBytes < requiredBytes) {
+ oldest = NULL;
+ stoptime = dispatch_clock;
+
+ LIST_FOREACH(de, &dvrentries, de_global_link) {
+ if (de->de_sched_state != DVR_COMPLETED &&
+ de->de_sched_state != DVR_MISSED_TIME)
+ continue;
+
+ if (dvr_entry_get_stop_time(de) > stoptime)
+ continue;
+
+ if (dvr_entry_get_removal_days(de) != DVR_RET_SPACENEED) // only remove the allowed ones
+ continue;
+
+ if (dvr_get_filename(de) == NULL || dvr_get_filesize(de) <= 0)
+ continue;
+
+ if(statvfs(dvr_get_filename(de), &diskdata) == -1)
+ continue;
+
+ /* Checking for the same config is useless as it's storage path might be changed meanwhile */
+ /* Check for the same file system instead */
+ if (filesystemId == 0 || diskdata.f_fsid != filesystemId)
+ continue;
+
+ oldest = de; // the oldest one until now
+ stoptime = dvr_entry_get_stop_time(de);
+ }
+
+ if (oldest) {
+ fileSize = dvr_get_filesize(oldest);
+ availBytes += fileSize;
+ clearedBytes += fileSize;
+
+ localtime_r(&stoptime, &tm);
+ if (strftime(tbuf, sizeof(tbuf), "%F %T", &tm) <= 0)
+ *tbuf = 0;
+ tvhlog(LOG_INFO, "dvr","Delete \"until space needed\" recording \"%s\" with stop time \"%s\" and file size \"%ld MB\"",
+ lang_str_get(oldest->de_title, NULL), tbuf, fileSize/(int64_t)1024/(int64_t)1024);
+
+ dvr_disk_space_config_lastdelete = dispatch_clock;
+ dvr_entry_delete(oldest, 1); // delete actual file
+ if (dvr_entry_get_retention_days(oldest) == DVR_RET_ONREMOVE)
+ dvr_entry_destroy(oldest, 1); // also delete database entry
+ }
+ else {
+ tvhlog(LOG_WARNING, "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);
+ goto finish;
+ }
+
+ loops++;
+ if (loops >= 10) {
+ tvhlog(LOG_WARNING, "dvr","Not able to clear the required disk space after deleting %i \"until space needed\" recordings...", loops);
+ goto finish;
+ }
+ }
+
+finish:
+ tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", cleared \"%ld MB\" of disk space, new free disk space \"%ld MB\"",
+ configName, clearedBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
+
+ return clearedBytes;
+}
+
+/**
+ * Check for each dvr config if the free disk size is below the dvr_cleanup_threshold
+ * If so and we are using the dvr config ATM (active recording), we start the cleanup procedure
+ */
+static void
+dvr_disk_space_check()
+{
+ dvr_config_t *cfg;
+ dvr_entry_t *de;
+ struct statvfs diskdata;
+ int64_t requiredBytes, availBytes;
+ int idx = 0, cleanupDone = 0;
+
+ dvr_disk_space_config_idx++;
+ if (dvr_disk_space_config_idx > dvr_disk_space_config_size)
+ dvr_disk_space_config_idx = 1;
+
+ LIST_FOREACH(cfg, &dvrconfigs, config_link)
+ {
+ idx++;
+
+ if (cfg->dvr_enabled &&
+ !cleanupDone &&
+ idx >= dvr_disk_space_config_idx &&
+ statvfs(cfg->dvr_storage, &diskdata) != -1)
+ {
+ availBytes = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
+ requiredBytes = (int64_t)cfg->dvr_cleanup_threshold*(int64_t)1024*(int64_t)1024;
+
+ if (availBytes < requiredBytes) {
+ LIST_FOREACH(de, &dvrentries, de_global_link) {
+
+ /* only start cleanup if we are actually writing files right now */
+ if (de->de_sched_state != DVR_RECORDING || !de->de_config || de->de_config != cfg)
+ continue;
+
+ tvhlog(LOG_WARNING, "dvr","running out of free disk space for dvr config \"%s\", required free space \"%ld MB\", current free space \"%ld MB\"",
+ cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile",
+ requiredBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
+
+ /* only cleanup one directory at the time as the system needs time to delete the actual files */
+ dvr_disk_space_cleanup(de->de_config);
+ cleanupDone = 1;
+ dvr_disk_space_config_idx = idx;
+ break;
+ }
+ }
+ else {
+ tvhlog(LOG_DEBUG, "dvr","checking free disk space for config \"%s\" : OK",
+ cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile");
+ }
+ }
+ }
+
+ if (!cleanupDone)
+ dvr_disk_space_config_idx = 0;
+
+ dvr_disk_space_config_size = idx;
+}
/**
*
*/
if (!dearmed) {
htsmsg_t *m = htsmsg_create_map();
pthread_mutex_lock(&dvr_disk_space_mutex);
+
+ /* update disk space from default dvr config */
dvr_get_disk_space_update((char *)opaque);
htsmsg_add_s64(m, "freediskspace", dvr_bfree);
htsmsg_add_s64(m, "totaldiskspace", dvr_btotal);
+
+ /* check free disk space for each dvr config and start cleanup if needed */
+ dvr_disk_space_check();
pthread_mutex_unlock(&dvr_disk_space_mutex);
notify_by_msg("diskspaceUpdate", m, 0);
path = strdup(cfg->dvr_storage);
tasklet_arm(&dvr_disk_space_tasklet, dvr_get_disk_space_tcb, path);
}
- gtimer_arm(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, 60);
+ gtimer_arm(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, 15);
}
/**
dvr_config_t *cfg = dvr_config_find_by_name_default(NULL);
pthread_mutex_init(&dvr_disk_space_mutex, NULL);
dvr_get_disk_space_update(cfg->dvr_storage);
- gtimer_arm(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, 60);
+ gtimer_arm(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, 5);
}
/**
{
.type = PT_U32,
.id = "retention",
- .name = N_("DVR log retention (days)"),
+ .name = N_("DVR log retention"),
+ .def.i = DVR_RET_DVRCONFIG,
.off = offsetof(dvr_timerec_entry_t, dte_retention),
+ .list = dvr_entry_class_retention_list,
.opts = PO_EXPERT
},
{
.type = PT_U32,
.id = "removal",
- .name = N_("DVR file retention period (days)"),
+ .name = N_("DVR file retention period"),
+ .def.i = DVR_RET_DVRCONFIG,
.off = offsetof(dvr_timerec_entry_t, dte_removal),
+ .list = dvr_entry_class_removal_list,
.opts = PO_EXPERT
},
{
uint32_t
dvr_timerec_get_retention_days( dvr_timerec_entry_t *dte )
{
- if (dte->dte_retention > 0)
+ if (dte->dte_retention > 0) {
+ /* As we need the db entry when deleting the file on disk */
+ if (dvr_timerec_get_removal_days(dte) != DVR_RET_FOREVER &&
+ dvr_timerec_get_removal_days(dte) > dte->dte_retention)
+ return DVR_RET_ONREMOVE;
+
return dte->dte_retention;
+ }
return dvr_retention_cleanup(dte->dte_config->dvr_retention_days);
}
int val;
};
+struct strtab_u32 {
+ const char *str;
+ uint32_t val;
+};
+
static int str2val0(const char *str, const struct strtab tab[], int l)
__attribute((unused));
#define strtab2htsmsg(tab,i18n,lang) strtab2htsmsg0(tab, sizeof(tab) / sizeof(tab[0]), i18n, lang)
+static inline htsmsg_t *
+strtab2htsmsg0_u32(const struct strtab_u32 tab[], uint32_t n, int i18n, const char *lang)
+{
+ uint32_t i;
+ htsmsg_t *e, *l = htsmsg_create_list();
+ for (i = 0; i < n; i++) {
+ e = htsmsg_create_map();
+ htsmsg_add_u32(e, "key", tab[i].val);
+ htsmsg_add_str(e, "val", i18n ? tvh_gettext_lang(lang, tab[i].str) : tab[i].str);
+ htsmsg_add_msg(l, NULL, e);
+ }
+ return l;
+}
+
+#define strtab2htsmsg_u32(tab,i18n,lang) strtab2htsmsg0_u32(tab, sizeof(tab) / sizeof(tab[0]), i18n, lang)
+
#endif /* STRTAB_H_ */
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 : 0); // 0 = dvr config
+ htsmsg_add_u32(conf, "retention", !retval ? u32 : DVR_RET_DVRCONFIG);
if (!(retval = htsmsg_get_u32(in, "removal", &u32)) || add)
- htsmsg_add_u32(conf, "removal", !retval ? u32 : 0); // 0 = dvr config
+ htsmsg_add_u32(conf, "removal", !retval ? u32 : DVR_RET_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)
*
*/
static htsmsg_t *
-htsp_build_dvrentry(dvr_entry_t *de, const char *method, const char *lang)
+htsp_build_dvrentry(htsp_connection_t *htsp, dvr_entry_t *de, const char *method, const char *lang)
{
htsmsg_t *out = htsmsg_create_map(), *l, *m, *e;
htsmsg_field_t *f;
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_days(de));
+
+ if (htsp->htsp_version > 25)
+ htsmsg_add_u32(out, "retention", dvr_entry_get_retention_days(de));
+ else
+ htsmsg_add_u32(out, "retention", dvr_entry_get_retention_days(de) == DVR_RET_ONREMOVE ?
+ dvr_entry_get_removal_days(de) : 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);
*
*/
static htsmsg_t *
-htsp_build_autorecentry(dvr_autorec_entry_t *dae, const char *method)
+htsp_build_autorecentry(htsp_connection_t *htsp, dvr_autorec_entry_t *dae, const char *method)
{
htsmsg_t *out = htsmsg_create_map();
int tad;
htsmsg_add_u32(out, "enabled", dae->dae_enabled >= 1 ? 1 : 0);
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_days(dae));
+
+ if (htsp->htsp_version > 25)
+ htsmsg_add_u32(out, "retention", dvr_autorec_get_retention_days(dae));
+ else
+ htsmsg_add_u32(out, "retention", dvr_autorec_get_retention_days(dae) == DVR_RET_ONREMOVE ?
+ dvr_autorec_get_removal_days(dae) : 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) {
*
*/
static htsmsg_t *
-htsp_build_timerecentry(dvr_timerec_entry_t *dte, const char *method)
+htsp_build_timerecentry(htsp_connection_t *htsp, dvr_timerec_entry_t *dte, const char *method)
{
htsmsg_t *out = htsmsg_create_map();
htsmsg_add_str(out, "id", idnode_uuid_as_sstr(&dte->dte_id));
htsmsg_add_u32(out, "enabled", dte->dte_enabled >= 1 ? 1 : 0);
htsmsg_add_u32(out, "daysOfWeek", dte->dte_weekdays);
- htsmsg_add_u32(out, "retention", dvr_timerec_get_retention_days(dte));
+
+ if (htsp->htsp_version > 25)
+ htsmsg_add_u32(out, "retention", dvr_timerec_get_retention_days(dte));
+ else
+ htsmsg_add_u32(out, "retention", dvr_timerec_get_retention_days(dte) == DVR_RET_ONREMOVE ?
+ dvr_timerec_get_removal_days(dte) : 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);
/* Send all autorecs */
TAILQ_FOREACH(dae, &autorec_entries, dae_link)
if (!dvr_autorec_entry_verify(dae, htsp->htsp_granted_access, 1))
- htsp_send_message(htsp, htsp_build_autorecentry(dae, "autorecEntryAdd"), NULL);
+ htsp_send_message(htsp, htsp_build_autorecentry(htsp, dae, "autorecEntryAdd"), NULL);
/* Send all timerecs */
TAILQ_FOREACH(dte, &timerec_entries, dte_link)
if (!dvr_timerec_entry_verify(dte, htsp->htsp_granted_access, 1))
- htsp_send_message(htsp, htsp_build_timerecentry(dte, "timerecEntryAdd"), NULL);
+ htsp_send_message(htsp, htsp_build_timerecentry(htsp, dte, "timerecEntryAdd"), NULL);
/* Send all DVR entries */
LIST_FOREACH(de, &dvrentries, de_global_link)
if (!dvr_entry_verify(de, htsp->htsp_granted_access, 1) &&
htsp_user_access_channel(htsp, de->de_channel))
- htsp_send_message(htsp, htsp_build_dvrentry(de, "dvrEntryAdd", htsp->htsp_language), NULL);
+ htsp_send_message(htsp, htsp_build_dvrentry(htsp, de, "dvrEntryAdd", htsp->htsp_language), NULL);
/* Send EPG updates */
if (epg) {
if(htsmsg_get_u32(in, "priority", &priority))
priority = DVR_PRIO_NORMAL;
if(htsmsg_get_u32(in, "retention", &retention))
- retention = 0;
+ retention = DVR_RET_DVRCONFIG;
if(htsmsg_get_u32(in, "removal", &removal))
- removal = 0;
+ removal = DVR_RET_DVRCONFIG;
comment = htsmsg_get_str(in, "comment");
if (!(lang = htsmsg_get_str(in, "language")))
lang = htsp->htsp_language;
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", 0);
- removal = htsmsg_get_u32_or_default(in, "removal", 0);
+ retention = htsmsg_get_u32_or_default(in, "retention", DVR_RET_DVRCONFIG);
+ removal = htsmsg_get_u32_or_default(in, "removal", DVR_RET_DVRCONFIG);
priority = htsmsg_get_u32_or_default(in, "priority", DVR_PRIO_NORMAL);
title = htsmsg_get_str(in, "title");
subtitle = htsmsg_get_str(in, "subtitle");
if (!dvr_entry_verify(de, htsp->htsp_granted_access, 1) &&
htsp_user_access_channel(htsp, de->de_channel)) {
htsmsg_t *m = msg ? htsmsg_copy(msg)
- : htsp_build_dvrentry(de, method, htsp->htsp_language);
+ : htsp_build_dvrentry(htsp, de, method, htsp->htsp_language);
htsp_send_message(htsp, m, NULL);
}
}
if ((dae->dae_channel == NULL || htsp_user_access_channel(htsp, dae->dae_channel)) &&
!dvr_autorec_entry_verify(dae, htsp->htsp_granted_access, 1)) {
htsmsg_t *m = msg ? htsmsg_copy(msg)
- : htsp_build_autorecentry(dae, method);
+ : htsp_build_autorecentry(htsp, dae, method);
htsp_send_message(htsp, m, NULL);
}
}
if ((dte->dte_channel == NULL || htsp_user_access_channel(htsp, dte->dte_channel)) &&
!dvr_timerec_entry_verify(dte, htsp->htsp_granted_access, 1)) {
htsmsg_t *m = msg ? htsmsg_copy(msg)
- : htsp_build_timerecentry(dte, method);
+ : htsp_build_timerecentry(htsp, dte, method);
htsp_send_message(htsp, m, NULL);
}
}
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, 0, "simpleui");
+ DVR_PRIO_NORMAL, DVR_RET_DVRCONFIG,
+ DVR_RET_DVRCONFIG, "simpleui");
} else if(de != NULL && (http_arg_get(&hc->hc_req_args, "cancel")) != NULL) {
de = dvr_entry_cancel(de, 0);
}