override directory settings for matching recordings.
Useful for e.g. recording multiple different shows into a single
directory (e.g. news).
Fixes #2123, fixes #2448
<dd>
The name you've given to the rule, e.g. 'Stuff involving Jeremy Clarkson'.
<p>
+<dt><b>Directory</b>
+<dd>
+When specified, this setting overrides the subdirectory rules specified by the DVR configuration and puts all recordings done by this entry into the specified subdirectory. Useful for e.g. recording multiple different news broadcasts into one common subdirectory called "News".
+<p>
<dt><b>Title (Regexp)</b>
<dd>
The title of the programme to look for. Note that this accepts case-insensitive regular expressions, so you can use pattern matching as Tvheadend scans the EPG for programmes to record.
<dd>
The name you've given to the rule, e.g. 'Evening cartoons for the children'.
<p>
+<dt><b>Directory</b>
+<dd>
+When specified, this setting overrides the subdirectory rules specified by the DVR configuration and puts all recordings done by this entry into the specified subdirectory. Useful for e.g. recording multiple different news broadcasts into one common subdirectory called "News".
+<p>
<dt><b>Title</b>
<dd>
Not sure how this differs from **Name* *
char *de_comment;
char *de_filename; /* Initially null if no filename has been
generated yet */
+ char *de_directory; /* Can be set for autorec entries, will override any
+ directory setting from the configuration */
lang_str_t *de_title; /* Title in UTF-8 (from EPG) */
lang_str_t *de_desc; /* Description in UTF-8 (from EPG) */
uint32_t de_content_type; /* Content type (from EPG) (only code) */
TAILQ_ENTRY(dvr_autorec_entry) dae_link;
char *dae_name;
+ char *dae_directory;
dvr_config_t *dae_config;
LIST_ENTRY(dvr_autorec_entry) dae_config_link;
TAILQ_ENTRY(dvr_timerec_entry) dte_link;
char *dte_name;
+ char *dte_directory;
dvr_config_t *dte_config;
LIST_ENTRY(dvr_timerec_entry) dte_config_link;
time_t stop_extra, dvr_prio_t pri, int retention,
int min_duration, int max_duration,
const char *owner, const char *creator,
- const char *comment, const char *name);
+ const char *comment, const char *name, const char *directory);
dvr_autorec_entry_t *
dvr_autorec_add_series_link(const char *dvr_config_name,
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,
- const char *owner, const char *creator, const char *comment, const char *name);
+ const char *owner, const char *creator, const char *comment,
+ const char *name, const char *directory);
static inline dvr_timerec_entry_t *
dvr_timerec_find_by_uuid(const char *uuid)
uint32_t weekdays, time_t start_extra, time_t stop_extra,
dvr_prio_t pri, int retention,
int min_duration, int max_duration,
- const char *owner, const char *creator, const char *comment, const char *name)
+ const char *owner, const char *creator, const char *comment,
+ const char *name, const char *directory)
{
dvr_autorec_entry_t *dae;
htsmsg_t *conf, *days;
htsmsg_add_str(conf, "creator", creator ?: "");
htsmsg_add_str(conf, "comment", comment ?: "");
htsmsg_add_str(conf, "name", name ?: "");
+ htsmsg_add_str(conf, "directory", directory ?: "");
if (start >= 0)
htsmsg_add_s32(conf, "start", start);
LIST_REMOVE(dae, dae_config_link);
free(dae->dae_name);
+ free(dae->dae_directory);
free(dae->dae_owner);
free(dae->dae_creator);
free(dae->dae_comment);
.id = "name",
.name = "Name",
.off = offsetof(dvr_autorec_entry_t, dae_name),
+ },
+ {
+ .type = PT_STR,
+ .id = "directory",
+ .name = "Directory",
+ .off = offsetof(dvr_autorec_entry_t, dae_directory),
},
{
.type = PT_STR,
if (e)
htsmsg_add_u32(conf, "broadcast", e->id);
if (dae)
+ {
htsmsg_add_str(conf, "autorec", idnode_uuid_as_str(&dae->dae_id));
+ htsmsg_add_str(conf, "directory", dae->dae_directory ?: "");
+ }
if (dte)
+ {
htsmsg_add_str(conf, "timerec", idnode_uuid_as_str(&dte->dte_id));
+ htsmsg_add_str(conf, "directory", dte->dte_directory ?: "");
+ }
de = dvr_entry_create(NULL, conf);
.off = offsetof(dvr_entry_t, de_filename),
.opts = PO_RDONLY,
},
+ {
+ .type = PT_STR,
+ .id = "directory",
+ .name = "Directory",
+ .off = offsetof(dvr_entry_t, de_directory),
+ .opts = PO_RDONLY,
+ },
{
.type = PT_U32,
.id = "errorcode",
snprintf(path, sizeof(path), "%s", cfg->dvr_storage);
- if(cfg->dvr_title_dir || cfg->dvr_channel_dir || cfg->dvr_dir_per_day) {
+ if(cfg->dvr_title_dir || cfg->dvr_channel_dir || cfg->dvr_dir_per_day || de->de_directory) {
char *p;
int l;
if (path[strlen(path)-1] == '/')
path[strlen(path)-1] = '\0';
- /* Append per-day directory */
- if (cfg->dvr_dir_per_day) {
- localtime_r(&de->de_start, &tm);
- strftime(fullname, sizeof(fullname), "%F", &tm);
- s = cleanup_filename(fullname, cfg);
+ /* Use the specified directory if set, otherwise construct it from the DVR
+ configuration */
+ if (de->de_directory) {
+ char *directory = strdup(de->de_directory);
+ s = cleanup_filename(directory, cfg);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
- }
+ } else {
+ /* Append per-day directory */
+ if (cfg->dvr_dir_per_day) {
+ localtime_r(&de->de_start, &tm);
+ strftime(fullname, sizeof(fullname), "%F", &tm);
+ s = cleanup_filename(fullname, cfg);
+ if (s == NULL)
+ return -1;
+ snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
+ free(s);
+ }
- /* Append per-channel directory */
- if (cfg->dvr_channel_dir) {
- char *chname = strdup(DVR_CH_NAME(de));
- s = cleanup_filename(chname, cfg);
- free(chname);
- if (s == NULL)
+ /* Append per-channel directory */
+ if (cfg->dvr_channel_dir) {
+ char *chname = strdup(DVR_CH_NAME(de));
+ s = cleanup_filename(chname, cfg);
+ free(chname);
+ if (s == NULL)
return -1;
- snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
- free(s);
- }
+ snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
+ free(s);
+ }
- // TODO: per-brand, per-season
+ // TODO: per-brand, per-season
- /* Append per-title directory */
- if (cfg->dvr_title_dir) {
- char *title = strdup(lang_str_get(de->de_title, NULL));
- s = cleanup_filename(title, cfg);
- free(title);
- if (s == NULL)
+ /* Append per-title directory */
+ if (cfg->dvr_title_dir) {
+ char *title = strdup(lang_str_get(de->de_title, NULL));
+ s = cleanup_filename(title, cfg);
+ free(title);
+ if (s == NULL)
return -1;
- snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
- free(s);
+ snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
+ free(s);
+ }
}
if (makedirs(path, cfg->dvr_muxcnf.m_directory_permissions) != 0)
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,
- const char *owner, const char *creator, const char *comment, const char *name)
+ const char *owner, const char *creator, const char *comment,
+ const char *name, const char *directory)
{
dvr_timerec_entry_t *dte;
htsmsg_t *conf, *days;
htsmsg_add_str(conf, "creator", creator ?: "");
htsmsg_add_str(conf, "comment", comment ?: "");
htsmsg_add_str(conf, "name", name ?: "");
+ htsmsg_add_str(conf, "directory", directory ?: "");
htsmsg_add_u32(conf, "start", start);
htsmsg_add_u32(conf, "stop", stop);
.off = offsetof(dvr_timerec_entry_t, dte_title),
.def.s = "Time-%x-%R",
},
+ {
+ .type = PT_STR,
+ .id = "directory",
+ .name = "Directory",
+ .off = offsetof(dvr_timerec_entry_t, dte_directory),
+ },
{
.type = PT_STR,
.id = "channel",
static void *htsp_server, *htsp_server_2;
-#define HTSP_PROTO_VERSION 18
+#define HTSP_PROTO_VERSION 19
#define HTSP_ASYNC_OFF 0x00
#define HTSP_ASYNC_ON 0x01
htsmsg_add_s64(out, "stopExtra", dae->dae_stop_extra);
if(dae->dae_title)
- htsmsg_add_str(out, "title", dae->dae_title);
+ htsmsg_add_str(out, "title", dae->dae_title);
if(dae->dae_name)
- htsmsg_add_str(out, "name", dae->dae_name);
+ htsmsg_add_str(out, "name", dae->dae_name);
+ if(dae->dae_directory)
+ htsmsg_add_str(out, "directory", dae->dae_directory);
if(dae->dae_owner)
- htsmsg_add_str(out, "owner", dae->dae_owner);
+ htsmsg_add_str(out, "owner", dae->dae_owner);
if(dae->dae_creator)
- htsmsg_add_str(out, "creator", dae->dae_creator);
+ htsmsg_add_str(out, "creator", dae->dae_creator);
if(dae->dae_channel)
- htsmsg_add_u32(out, "channel", channel_get_id(dae->dae_channel));
+ htsmsg_add_u32(out, "channel", channel_get_id(dae->dae_channel));
htsmsg_add_str(out, "method", method);
htsmsg_add_s32(out, "stop", dte->dte_stop);
if(dte->dte_title)
- htsmsg_add_str(out, "title", dte->dte_title);
+ htsmsg_add_str(out, "title", dte->dte_title);
if(dte->dte_name)
- htsmsg_add_str(out, "name", dte->dte_name);
+ htsmsg_add_str(out, "name", dte->dte_name);
+ if(dte->dte_directory)
+ htsmsg_add_str(out, "directory", dte->dte_directory);
if(dte->dte_owner)
- htsmsg_add_str(out, "owner", dte->dte_owner);
+ htsmsg_add_str(out, "owner", dte->dte_owner);
if(dte->dte_creator)
- htsmsg_add_str(out, "creator", dte->dte_creator);
+ htsmsg_add_str(out, "creator", dte->dte_creator);
if(dte->dte_channel)
- htsmsg_add_u32(out, "channel", channel_get_id(dte->dte_channel));
+ htsmsg_add_u32(out, "channel", channel_get_id(dte->dte_channel));
htsmsg_add_str(out, "method", method);
{
htsmsg_t *out;
dvr_autorec_entry_t *dae;
- const char *dvr_config_name, *title, *creator, *comment, *name;
+ const char *dvr_config_name, *title, *creator, *comment, *name, *directory;
int64_t start_extra, stop_extra;
uint32_t u32, days_of_week, priority, min_duration, max_duration, retention, enabled;
int32_t approx_time, start, start_window;
comment = "";
if (!(name = htsmsg_get_str(in, "name")))
name = "";
+ if (!(directory = htsmsg_get_str(in, "directory")))
+ directory = "";
/* Check access */
if (ch && !htsp_user_access_channel(htsp, ch))
dae = dvr_autorec_create_htsp(dvr_config_name, title, ch, enabled, start, start_window, days_of_week,
start_extra, stop_extra, priority, retention, min_duration, max_duration,
- htsp->htsp_granted_access->aa_username, creator, comment, name);
+ htsp->htsp_granted_access->aa_username, creator, comment, name, directory);
/* create response */
out = htsmsg_create_map();
{
htsmsg_t *out;
dvr_timerec_entry_t *dte;
- const char *dvr_config_name, *title, *creator, *comment, *name;
+ const char *dvr_config_name, *title, *creator, *comment, *name, *directory;
uint32_t u32, days_of_week, priority, retention, start, stop, enabled;
channel_t *ch = NULL;
comment = "";
if (!(name = htsmsg_get_str(in, "name")))
name = "";
+ if (!(directory = htsmsg_get_str(in, "directory")))
+ directory = "";
/* Check access */
if (ch && !htsp_user_access_channel(htsp, ch))
/* 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, creator, comment, name);
+ priority, retention, htsp->htsp_granted_access->aa_username, creator, comment, name, directory);
/* create response */
out = htsmsg_create_map();
columns: {
enabled: { width: 50 },
name: { width: 200 },
+ directory: { width: 200 },
title: { width: 300 },
channel: { width: 200 },
tag: { width: 200 },
add: {
url: 'api/dvr/autorec',
params: {
- list: 'enabled,name,title,channel,tag,content_type,minduration,' +
+ list: 'enabled,name,directory,title,channel,tag,content_type,minduration,' +
'maxduration,weekdays,start,start_window,pri,config_name,comment'
},
create: { }
},
del: true,
- list: 'enabled,name,title,channel,tag,content_type,minduration,' +
+ list: 'enabled,name,directory,title,channel,tag,content_type,minduration,' +
'maxduration,weekdays,start,start_window,pri,config_name,owner,creator,comment',
columns: {
weekdays: {
columns: {
enabled: { width: 50 },
name: { width: 200 },
+ directory: { width: 200 },
title: { width: 300 },
channel: { width: 200 },
weekdays: { width: 160 },
add: {
url: 'api/dvr/timerec',
params: {
- list: 'enabled,name,title,channel,weekdays,start,stop,pri,config_name,comment'
+ list: 'enabled,name,directory,title,channel,weekdays,start,stop,pri,config_name,comment'
},
create: { }
},
del: true,
- list: 'enabled,name,title,channel,weekdays,start,stop,pri,config_name,owner,creator,comment',
+ list: 'enabled,name,directory,title,channel,weekdays,start,stop,pri,config_name,owner,creator,comment',
columns: {
weekdays: {
renderer: function(st) { return tvheadend.weekdaysRenderer(st); }