diff --git a/docs/html/dvr_autorec.html b/docs/html/dvr_autorec.html
index 1a482ecf..a69e4e04 100644
--- a/docs/html/dvr_autorec.html
+++ b/docs/html/dvr_autorec.html
@@ -51,6 +51,10 @@ Check or clear this box to enable or disable this rule.
The name you've given to the rule, e.g. 'Stuff involving Jeremy Clarkson'.
+
Directory
+
+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".
+
Title (Regexp)
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.
diff --git a/docs/html/dvr_timerec.html b/docs/html/dvr_timerec.html
index 9643d280..083e76bb 100644
--- a/docs/html/dvr_timerec.html
+++ b/docs/html/dvr_timerec.html
@@ -50,6 +50,10 @@ Check or clear this box to enable or disable this rule.
The name you've given to the rule, e.g. 'Evening cartoons for the children'.
+
Directory
+
+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".
+
Title
Not sure how this differs from **Name* *
diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h
index c4f1a93c..91bd2096 100644
--- a/src/dvr/dvr.h
+++ b/src/dvr/dvr.h
@@ -159,6 +159,8 @@ typedef struct dvr_entry {
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) */
@@ -240,6 +242,7 @@ typedef struct dvr_autorec_entry {
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;
@@ -294,6 +297,7 @@ typedef struct dvr_timerec_entry {
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;
@@ -490,7 +494,7 @@ dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
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,
@@ -542,7 +546,8 @@ 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,
- 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)
diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c
index be7ef861..7e5f0709 100644
--- a/src/dvr/dvr_autorec.c
+++ b/src/dvr/dvr_autorec.c
@@ -210,7 +210,8 @@ dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
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;
@@ -231,6 +232,7 @@ dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
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);
@@ -308,6 +310,7 @@ autorec_entry_destroy(dvr_autorec_entry_t *dae, int delconf)
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);
@@ -848,6 +851,12 @@ const idclass_t dvr_autorec_entry_class = {
.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,
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index 91f23346..f96c84c6 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -514,9 +514,15 @@ dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
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);
@@ -1845,6 +1851,13 @@ const idclass_t dvr_entry_class = {
.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",
@@ -2047,7 +2060,7 @@ dvr_entry_delete(dvr_entry_t *de)
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;
diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c
index ab893fbd..7784e2c3 100644
--- a/src/dvr/dvr_rec.c
+++ b/src/dvr/dvr_rec.c
@@ -198,39 +198,50 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
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);
- }
-
- /* 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)
+ } 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);
- }
+ snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
+ free(s);
+ }
- // 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-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
+
+ /* 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);
+ }
}
if (makedirs(path, cfg->dvr_muxcnf.m_directory_permissions) != 0)
diff --git a/src/dvr/dvr_timerec.c b/src/dvr/dvr_timerec.c
index a593318a..5d295776 100644
--- a/src/dvr/dvr_timerec.c
+++ b/src/dvr/dvr_timerec.c
@@ -204,7 +204,8 @@ 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,
- 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;
@@ -221,6 +222,7 @@ dvr_timerec_create_htsp(const char *dvr_config_name, const char *title,
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);
@@ -539,6 +541,12 @@ const idclass_t dvr_timerec_entry_class = {
.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",
diff --git a/src/htsp_server.c b/src/htsp_server.c
index 673540a5..2206d484 100644
--- a/src/htsp_server.c
+++ b/src/htsp_server.c
@@ -68,7 +68,7 @@
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
@@ -766,15 +766,17 @@ htsp_build_autorecentry(dvr_autorec_entry_t *dae, const char *method)
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);
@@ -798,15 +800,17 @@ htsp_build_timerecentry(dvr_timerec_entry_t *dte, const char *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);
@@ -1588,7 +1592,7 @@ htsp_method_addAutorecEntry(htsp_connection_t *htsp, htsmsg_t *in)
{
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;
@@ -1637,6 +1641,8 @@ htsp_method_addAutorecEntry(htsp_connection_t *htsp, htsmsg_t *in)
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))
@@ -1644,7 +1650,7 @@ htsp_method_addAutorecEntry(htsp_connection_t *htsp, htsmsg_t *in)
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();
@@ -1697,7 +1703,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, *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;
@@ -1728,6 +1734,8 @@ htsp_method_addTimerecEntry(htsp_connection_t *htsp, htsmsg_t *in)
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))
@@ -1735,7 +1743,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, creator, comment, name);
+ priority, retention, htsp->htsp_granted_access->aa_username, creator, comment, name, directory);
/* create response */
out = htsmsg_create_map();
diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js
index 2d81cfd8..d617076a 100644
--- a/src/webui/static/app/dvr.js
+++ b/src/webui/static/app/dvr.js
@@ -434,6 +434,7 @@ tvheadend.autorec_editor = function(panel, index) {
columns: {
enabled: { width: 50 },
name: { width: 200 },
+ directory: { width: 200 },
title: { width: 300 },
channel: { width: 200 },
tag: { width: 200 },
@@ -452,13 +453,13 @@ tvheadend.autorec_editor = function(panel, index) {
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: {
@@ -492,6 +493,7 @@ tvheadend.timerec_editor = function(panel, index) {
columns: {
enabled: { width: 50 },
name: { width: 200 },
+ directory: { width: 200 },
title: { width: 300 },
channel: { width: 200 },
weekdays: { width: 160 },
@@ -506,12 +508,12 @@ tvheadend.timerec_editor = function(panel, index) {
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); }