Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Implemented "moved recordings" in order to improve the user experience
  • Loading branch information
Glenn-1990 authored and perexg committed Oct 6, 2016
1 parent 91d28cc commit 4cdf9c9
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 105 deletions.
42 changes: 39 additions & 3 deletions src/api/api_dvr.c
Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 },
Expand Down
5 changes: 2 additions & 3 deletions src/dvr/dvr.h
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion src/dvr/dvr_autorec.c
Expand Up @@ -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);
}
}
}
Expand Down
37 changes: 1 addition & 36 deletions src/dvr/dvr_config.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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",
Expand Down
91 changes: 43 additions & 48 deletions src/dvr/dvr_db.c
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
}

/**
Expand Down
8 changes: 1 addition & 7 deletions src/dvr/dvr_vfsmgr.c
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/htsp_server.c
Expand Up @@ -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();
}
Expand Down

0 comments on commit 4cdf9c9

Please sign in to comment.