]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Add support for multiple DVR configurations.
authorAndreas Öman <andreas@lonelycoder.com>
Thu, 21 Oct 2010 12:36:29 +0000 (12:36 +0000)
committerAndreas Öman <andreas@lonelycoder.com>
Thu, 21 Oct 2010 12:36:29 +0000 (12:36 +0000)
Patch by sbi

Ticket #303

12 files changed:
src/dvb/dvb_fe.c
src/dvr/dvr.h
src/dvr/dvr_autorec.c
src/dvr/dvr_db.c
src/dvr/dvr_rec.c
src/htsp.c
src/tvhead.h
src/webui/extjs.c
src/webui/simpleui.c
src/webui/static/app/comet.js
src/webui/static/app/dvr.js
src/webui/static/app/epg.js

index c68876ed02166f5ddc505b2c49c20a67b52712c8..4fa1f6229c2cdf2b105f6667bc228172db7e0c32 100644 (file)
@@ -39,7 +39,7 @@
 #include "diseqc.h"
 #include "notify.h"
 #include "transports.h"
-
+#include "dvr/dvr.h"
 
 /**
  * Front end monitor
@@ -197,7 +197,6 @@ dvb_adapter_open_dump_file(th_dvb_adapter_t *tda)
   struct dmx_pes_filter_params dmx_param;
   char fullname[1000];
   char path[500];
-  extern char *dvr_storage;
   const char *fname = tda->tda_mux_current->tdmi_identifier;
 
   int fd = tvh_open(tda->tda_demux_path, O_RDWR, 0);
@@ -220,7 +219,8 @@ dvb_adapter_open_dump_file(th_dvb_adapter_t *tda)
     return;
   }
 
-  snprintf(path, sizeof(path), "%s/muxdumps", dvr_storage);
+  snprintf(path, sizeof(path), "%s/muxdumps", 
+      dvr_config_find_by_name_default("")->dvr_storage);
 
   if(mkdir(path, 0777) && errno != EEXIST) {
     tvhlog(LOG_ERR, "dvb", "\"%s\" unable to create mux dump dir %s -- %s",
index 918b51ee7e5459c19bb7c906b3b6f6755b505028..888fc98678afa2ae99f875202aa3105b4562b13f 100644 (file)
 #include "channels.h"
 #include "subscriptions.h"
 
-extern char *dvr_storage;
-extern char *dvr_format;
-extern char *dvr_file_postfix;
-extern uint32_t dvr_retention_days;
-extern int dvr_flags;
-extern char *dvr_postproc;
-extern int dvr_extra_time_pre;
-extern int dvr_extra_time_post;
+typedef struct dvr_config {
+  char *dvr_config_name;
+  char *dvr_storage;
+  char *dvr_format;
+  char *dvr_file_postfix;
+  uint32_t dvr_retention_days;
+  int dvr_flags;
+  char *dvr_postproc;
+  int dvr_extra_time_pre;
+  int dvr_extra_time_post;
+
+  LIST_ENTRY(dvr_config) config_link;
+} dvr_config_t;
+
+extern struct dvr_config_list dvrconfigs;
+
 extern struct dvr_entry_list dvrentries;
 
 #define DVR_DIR_PER_DAY                0x1
@@ -98,6 +106,8 @@ typedef struct dvr_entry {
    * These meta fields will stay valid as long as reference count > 0
    */
 
+  char *de_config_name;
+
   time_t de_start;
   time_t de_stop;
 
@@ -172,6 +182,8 @@ typedef struct dvr_autorec_entry {
   TAILQ_ENTRY(dvr_autorec_entry) dae_link;
   char *dae_id;
 
+  char *dae_config_name;
+
   int dae_enabled;
   char *dae_creator;
   char *dae_comment;
@@ -202,6 +214,14 @@ typedef struct dvr_autorec_entry {
  * Prototypes
  */
 
+dvr_config_t *dvr_config_find_by_name(const char *name);
+
+dvr_config_t *dvr_config_find_by_name_default(const char *name);
+
+dvr_config_t *dvr_config_create(const char *name);
+
+void dvr_config_delete(const char *name);
+
 void dvr_entry_notify(dvr_entry_t *de);
 
 const char *dvr_entry_status(dvr_entry_t *de);
@@ -210,11 +230,13 @@ const char *dvr_entry_schedstatus(dvr_entry_t *de);
 
 void dvr_entry_create_by_autorec(event_t *e, dvr_autorec_entry_t *dae);
 
-dvr_entry_t *dvr_entry_create_by_event(event_t *e, const char *creator,
+dvr_entry_t *dvr_entry_create_by_event(const char *dvr_config_name,
+                                       event_t *e, const char *creator,
                                       dvr_autorec_entry_t *dae,
                                       dvr_prio_t pri);
 
-dvr_entry_t *dvr_entry_create(channel_t *ch, time_t start, time_t stop, 
+dvr_entry_t *dvr_entry_create(const char *dvr_config_name,
+                              channel_t *ch, time_t start, time_t stop, 
                              const char *title, const char *description,
                              const char *creator, dvr_autorec_entry_t *dae,
                              epg_episode_t *ee, uint8_t content_type,
@@ -240,17 +262,17 @@ dvr_entry_t *dvr_entry_cancel(dvr_entry_t *de);
 
 void dvr_entry_dec_ref(dvr_entry_t *de);
 
-void dvr_storage_set(const char *storage);
+void dvr_storage_set(dvr_config_t *cfg, const char *storage);
 
-void dvr_postproc_set(const char *postproc);
+void dvr_postproc_set(dvr_config_t *cfg, const char *postproc);
 
-void dvr_retention_set(int days);
+void dvr_retention_set(dvr_config_t *cfg, int days);
 
-void dvr_flags_set(int flags);
+void dvr_flags_set(dvr_config_t *cfg, int flags);
 
-void dvr_extra_time_pre_set(int d);
+void dvr_extra_time_pre_set(dvr_config_t *cfg, int d);
 
-void dvr_extra_time_post_set(int d);
+void dvr_extra_time_post_set(dvr_config_t *cfg, int d);
 
 /**
  * Query interface
@@ -268,7 +290,8 @@ void dvr_query_sort(dvr_query_result_t *dqr);
 /**
  *
  */
-void dvr_autorec_add(const char *title, const char *channel,
+void dvr_autorec_add(const char *dvr_config_name,
+                     const char *title, const char *channel,
                     const char *tag, uint8_t content_type,
                     const char *creator, const char *comment);
 
index eaac0daa18a073b3a6b19626bbd171c2682c2d8d..bc75702cb923a28b3f68af65d9df13403cb6dfcd 100644 (file)
@@ -166,6 +166,7 @@ autorec_entry_destroy(dvr_autorec_entry_t *dae)
 
   free(dae->dae_id);
 
+  free(dae->dae_config_name);
   free(dae->dae_creator);
   free(dae->dae_comment);
 
@@ -227,6 +228,8 @@ autorec_record_build(dvr_autorec_entry_t *dae)
   htsmsg_add_str(e, "id", dae->dae_id);
   htsmsg_add_u32(e, "enabled",  !!dae->dae_enabled);
 
+  if (dae->dae_config_name != NULL)
+    htsmsg_add_str(e, "config_name", dae->dae_config_name);
   if(dae->dae_creator != NULL)
     htsmsg_add_str(e, "creator", dae->dae_creator);
   if(dae->dae_comment != NULL)
@@ -307,6 +310,7 @@ autorec_record_update(void *opaque, const char *id, htsmsg_t *values,
   if((dae = autorec_entry_find(id, maycreate)) == NULL)
     return NULL;
 
+  tvh_str_update(&dae->dae_config_name, htsmsg_get_str(values, "config_name"));
   tvh_str_update(&dae->dae_creator, htsmsg_get_str(values, "creator"));
   tvh_str_update(&dae->dae_comment, htsmsg_get_str(values, "comment"));
 
@@ -417,7 +421,8 @@ dvr_autorec_init(void)
  *
  */
 void
-dvr_autorec_add(const char *title, const char *channel,
+dvr_autorec_add(const char *config_name,
+                const char *title, const char *channel,
                const char *tag, uint8_t content_type,
                const char *creator, const char *comment)
 {
@@ -429,6 +434,7 @@ dvr_autorec_add(const char *title, const char *channel,
   if((dae = autorec_entry_find(NULL, 1)) == NULL)
     return;
 
+  tvh_str_set(&dae->dae_config_name, config_name);
   tvh_str_set(&dae->dae_creator, creator);
   tvh_str_set(&dae->dae_comment, comment);
 
index c07011e29e2ff9125d9600fa4aa3664dc83f14fb..0764a48522947ffb88120d4db6d5b22ad1b26f94 100644 (file)
 #include "htsp.h"
 #include "streaming.h"
 
-char *dvr_storage;
-char *dvr_format;
-char *dvr_file_postfix;
-uint32_t dvr_retention_days;
-int dvr_flags;
-int dvr_extra_time_pre;
-int dvr_extra_time_post;
-char *dvr_postproc;
-int dvr_iov_max;
-
 static int de_tally;
 
+int dvr_iov_max;
+
+struct dvr_config_list dvrconfigs;
 struct dvr_entry_list dvrentries;
 
 static void dvr_entry_save(dvr_entry_t *de);
@@ -129,6 +122,17 @@ dvrdb_changed(void)
   notify_by_msg("dvrdb", m);
 }
 
+/**
+ *
+ */
+static void
+dvrconfig_changed(void)
+{
+  htsmsg_t *m = htsmsg_create_map();
+  htsmsg_add_u32(m, "reload", 1);
+  notify_by_msg("dvrconfig", m);
+}
+
 
 /**
  *
@@ -155,8 +159,9 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de)
   struct tm tm;
   char buf[40];
   int i;
+  dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
 
-  if(dvr_flags & DVR_CHANNEL_IN_TITLE)
+  if(cfg->dvr_flags & DVR_CHANNEL_IN_TITLE)
     snprintf(output, outlen, "%s-", de->de_channel->ch_name);
   else
     output[0] = 0;
@@ -166,17 +171,17 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de)
 
   localtime_r(&de->de_start, &tm);
   
-  if(dvr_flags & DVR_DATE_IN_TITLE) {
+  if(cfg->dvr_flags & DVR_DATE_IN_TITLE) {
     strftime(buf, sizeof(buf), "%F", &tm);
     snprintf(output + strlen(output), outlen - strlen(output), ".%s", buf);
   }
 
-  if(dvr_flags & DVR_TIME_IN_TITLE) {
+  if(cfg->dvr_flags & DVR_TIME_IN_TITLE) {
     strftime(buf, sizeof(buf), "%H-%M", &tm);
     snprintf(output + strlen(output), outlen - strlen(output), ".%s", buf);
   }
 
-  if(dvr_flags & DVR_EPISODE_IN_TITLE) {
+  if(cfg->dvr_flags & DVR_EPISODE_IN_TITLE) {
 
     if(de->de_episode.ee_season && de->de_episode.ee_episode)
       snprintf(output + strlen(output), outlen - strlen(output), 
@@ -189,7 +194,7 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de)
               de->de_episode.ee_episode);
   }
 
-  if(dvr_flags & DVR_CLEAN_TITLE) {
+  if(cfg->dvr_flags & DVR_CLEAN_TITLE) {
         for (i=0;i<strlen(output);i++) {
                 if (
                         output[i]<32 ||
@@ -212,6 +217,7 @@ dvr_entry_link(dvr_entry_t *de)
 {
   time_t now, preamble;
   char buf[100];
+  dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
 
   dvr_make_title(buf, sizeof(buf), de);
 
@@ -231,7 +237,7 @@ dvr_entry_link(dvr_entry_t *de)
     else
       de->de_sched_state = DVR_COMPLETED;
     gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, 
-              de->de_stop + dvr_retention_days * 86400);
+              de->de_stop + cfg->dvr_retention_days * 86400);
 
   } else {
     de->de_sched_state = DVR_SCHEDULED;
@@ -246,7 +252,8 @@ dvr_entry_link(dvr_entry_t *de)
  *
  */
 dvr_entry_t *
-dvr_entry_create(channel_t *ch, time_t start, time_t stop, 
+dvr_entry_create(const char *config_name,
+                 channel_t *ch, time_t start, time_t stop, 
                 const char *title, const char *description,
                 const char *creator, dvr_autorec_entry_t *dae,
                 epg_episode_t *ee, uint8_t content_type, dvr_prio_t pri)
@@ -255,6 +262,7 @@ dvr_entry_create(channel_t *ch, time_t start, time_t stop,
   char tbuf[30];
   struct tm tm;
   time_t t;
+  dvr_config_t *cfg = dvr_config_find_by_name_default(config_name);
 
   LIST_FOREACH(de, &ch->ch_dvrs, de_channel_link)
     if(de->de_start == start && de->de_sched_state != DVR_COMPLETED)
@@ -272,11 +280,12 @@ dvr_entry_create(channel_t *ch, time_t start, time_t stop,
   if (ch->ch_dvr_extra_time_pre)
     de->de_start_extra = ch->ch_dvr_extra_time_pre;
   else
-    de->de_start_extra = dvr_extra_time_pre;
+    de->de_start_extra = cfg->dvr_extra_time_pre;
   if (ch->ch_dvr_extra_time_post)
     de->de_stop_extra  = ch->ch_dvr_extra_time_post;
   else
-    de->de_stop_extra  = dvr_extra_time_post;
+    de->de_stop_extra  = cfg->dvr_extra_time_post;
+  de->de_config_name = strdup(cfg->dvr_config_name);
   de->de_creator = strdup(creator);
   de->de_title   = strdup(title);
   de->de_desc    = description ? strdup(description) : NULL;
@@ -315,13 +324,15 @@ dvr_entry_create(channel_t *ch, time_t start, time_t stop,
  *
  */
 dvr_entry_t *
-dvr_entry_create_by_event(event_t *e, const char *creator, 
+dvr_entry_create_by_event(const char *config_name,
+                          event_t *e, const char *creator, 
                          dvr_autorec_entry_t *dae, dvr_prio_t pri)
 {
   if(e->e_channel == NULL || e->e_title == NULL)
     return NULL;
 
-  return dvr_entry_create(e->e_channel, e->e_start, e->e_stop, 
+  return dvr_entry_create(config_name,
+                          e->e_channel, e->e_start, e->e_stop, 
                          e->e_title, e->e_desc, creator, dae, &e->e_episode,
                          e->e_content_type, pri);
 }
@@ -340,7 +351,7 @@ dvr_entry_create_by_autorec(event_t *e, dvr_autorec_entry_t *dae)
   } else {
     snprintf(buf, sizeof(buf), "Auto recording");
   }
-  dvr_entry_create_by_event(e, buf, dae, dae->dae_pri);
+  dvr_entry_create_by_event(dae->dae_config_name, e, buf, dae, dae->dae_pri);
 }
 
 
@@ -360,6 +371,7 @@ dvr_entry_dec_ref(dvr_entry_t *de)
   if(de->de_autorec != NULL)
     LIST_REMOVE(de, de_autorec_link);
 
+  free(de->de_config_name);
   free(de->de_creator);
   free(de->de_title);
   free(de->de_ititle);
@@ -406,6 +418,7 @@ dvr_db_load_one(htsmsg_t *c, int id)
   channel_t *ch;
   uint32_t start, stop;
   int d;
+  dvr_config_t *cfg;
 
   if(htsmsg_get_u32(c, "start", &start))
     return;
@@ -417,6 +430,9 @@ dvr_db_load_one(htsmsg_t *c, int id)
   if((ch = channel_find_by_name(s, 0, 0)) == NULL)
     return;
 
+  s = htsmsg_get_str(c, "config_name");
+  cfg = dvr_config_find_by_name_default(s);
+
   if((title = htsmsg_get_str(c, "title")) == NULL)
     return;
 
@@ -433,17 +449,18 @@ dvr_db_load_one(htsmsg_t *c, int id)
 
   de->de_start   = start;
   de->de_stop    = stop;
+  de->de_config_name = strdup(cfg->dvr_config_name);
   de->de_creator = strdup(creator);
   de->de_title   = strdup(title);
   de->de_pri     = dvr_pri2val(htsmsg_get_str(c, "pri"));
   
   if(htsmsg_get_s32(c, "start_extra", &d))
-    de->de_start_extra = dvr_extra_time_pre;
+    de->de_start_extra = cfg->dvr_extra_time_pre;
   else
     de->de_start_extra = d;
 
   if(htsmsg_get_s32(c, "stop_extra", &d))
-    de->de_stop_extra = dvr_extra_time_post;
+    de->de_stop_extra = cfg->dvr_extra_time_post;
   else
     de->de_stop_extra = d;
 
@@ -519,6 +536,8 @@ dvr_entry_save(dvr_entry_t *de)
   htsmsg_add_s32(m, "start_extra", de->de_start_extra);
   htsmsg_add_s32(m, "stop_extra", de->de_stop_extra);
   
+  htsmsg_add_str(m, "config_name", de->de_config_name);
+
   htsmsg_add_str(m, "creator", de->de_creator);
 
   if(de->de_filename != NULL)
@@ -577,6 +596,8 @@ dvr_timer_expire(void *aux)
 static void
 dvr_stop_recording(dvr_entry_t *de, int stopcode)
 {
+  dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
+
   dvr_rec_unsubscribe(de, stopcode);
 
   de->de_sched_state = DVR_COMPLETED;
@@ -591,7 +612,7 @@ dvr_stop_recording(dvr_entry_t *de, int stopcode)
   dvr_entry_notify(de);
 
   gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, 
-                de->de_stop + dvr_retention_days * 86400);
+                de->de_stop + cfg->dvr_retention_days * 86400);
 }
 
 
@@ -719,84 +740,98 @@ dvr_destroy_by_channel(channel_t *ch)
 void
 dvr_init(void)
 {
-  htsmsg_t *m;
+  htsmsg_t *m, *l;
+  htsmsg_field_t *f;
+  const char *s;
   char buf[500];
   const char *homedir;
   struct stat st;
   uint32_t u32;
+  dvr_config_t *cfg;
 
   dvr_iov_max = sysconf(_SC_IOV_MAX);
 
   /* Default settings */
 
-  dvr_retention_days = 31;
-  dvr_format       = strdup("matroska");
-  dvr_file_postfix = strdup("mkv");
+  LIST_INIT(&dvrconfigs);
+  cfg = dvr_config_create("");
 
   /* Override settings with config */
 
-  dvr_flags = DVR_TAG_FILES;
+  l = hts_settings_load("dvr");
+  if(l != NULL) {
+    HTSMSG_FOREACH(f, l) {
+      m = htsmsg_get_map_by_field(f);
+      if(m == NULL)
+        continue;
 
-  if((m = hts_settings_load("dvr/config")) != NULL) {
+      s = htsmsg_get_str(m, "config_name");
+      cfg = dvr_config_find_by_name(s);
+      if(cfg == NULL)
+        cfg = dvr_config_create(s);
 
-    htsmsg_get_s32(m, "pre-extra-time", &dvr_extra_time_pre);
-    htsmsg_get_s32(m, "post-extra-time", &dvr_extra_time_post);
-    htsmsg_get_u32(m, "retention-days", &dvr_retention_days);
-    tvh_str_set(&dvr_storage, htsmsg_get_str(m, "storage"));
+      htsmsg_get_s32(m, "pre-extra-time", &cfg->dvr_extra_time_pre);
+      htsmsg_get_s32(m, "post-extra-time", &cfg->dvr_extra_time_post);
+      htsmsg_get_u32(m, "retention-days", &cfg->dvr_retention_days);
+      tvh_str_set(&cfg->dvr_storage, htsmsg_get_str(m, "storage"));
 
-    if(!htsmsg_get_u32(m, "day-dir", &u32) && u32)
-      dvr_flags |= DVR_DIR_PER_DAY;
+      if(!htsmsg_get_u32(m, "day-dir", &u32) && u32)
+        cfg->dvr_flags |= DVR_DIR_PER_DAY;
 
-    if(!htsmsg_get_u32(m, "channel-dir", &u32) && u32)
-      dvr_flags |= DVR_DIR_PER_CHANNEL;
+      if(!htsmsg_get_u32(m, "channel-dir", &u32) && u32)
+        cfg->dvr_flags |= DVR_DIR_PER_CHANNEL;
 
-    if(!htsmsg_get_u32(m, "channel-in-title", &u32) && u32)
-      dvr_flags |= DVR_CHANNEL_IN_TITLE;
+      if(!htsmsg_get_u32(m, "channel-in-title", &u32) && u32)
+        cfg->dvr_flags |= DVR_CHANNEL_IN_TITLE;
 
-    if(!htsmsg_get_u32(m, "date-in-title", &u32) && u32)
-      dvr_flags |= DVR_DATE_IN_TITLE;
+      if(!htsmsg_get_u32(m, "date-in-title", &u32) && u32)
+        cfg->dvr_flags |= DVR_DATE_IN_TITLE;
 
-    if(!htsmsg_get_u32(m, "time-in-title", &u32) && u32)
-      dvr_flags |= DVR_TIME_IN_TITLE;
-    if(!htsmsg_get_u32(m, "whitespace-in-title", &u32) && u32)
-      dvr_flags |= DVR_WHITESPACE_IN_TITLE;
+      if(!htsmsg_get_u32(m, "time-in-title", &u32) && u32)
+        cfg->dvr_flags |= DVR_TIME_IN_TITLE;
+   
+      if(!htsmsg_get_u32(m, "whitespace-in-title", &u32) && u32)
+        cfg->dvr_flags |= DVR_WHITESPACE_IN_TITLE;
 
-    if(!htsmsg_get_u32(m, "title-dir", &u32) && u32)
-      dvr_flags |= DVR_DIR_PER_TITLE;
+      if(!htsmsg_get_u32(m, "title-dir", &u32) && u32)
+        cfg->dvr_flags |= DVR_DIR_PER_TITLE;
 
-    if(!htsmsg_get_u32(m, "episode-in-title", &u32) && u32)
-      dvr_flags |= DVR_EPISODE_IN_TITLE;
+      if(!htsmsg_get_u32(m, "episode-in-title", &u32) && u32)
+        cfg->dvr_flags |= DVR_EPISODE_IN_TITLE;
 
-    if(!htsmsg_get_u32(m, "tag-files", &u32) && !u32)
-      dvr_flags &= ~DVR_TAG_FILES;
-   
-    tvh_str_set(&dvr_postproc, htsmsg_get_str(m, "postproc"));
+      if(!htsmsg_get_u32(m, "tag-files", &u32) && !u32)
+        cfg->dvr_flags &= ~DVR_TAG_FILES;
+     
+      tvh_str_set(&cfg->dvr_postproc, htsmsg_get_str(m, "postproc"));
+    }
 
-    htsmsg_destroy(m);
+    htsmsg_destroy(l);
   }
 
-  if(dvr_storage == NULL) {
-    /* Try to figure out a good place to put them videos */
-
-    homedir = getenv("HOME");
-
-    if(homedir != NULL) {
-      snprintf(buf, sizeof(buf), "%s/Videos", homedir);
-      if(stat(buf, &st) == 0 && S_ISDIR(st.st_mode))
-       dvr_storage = strdup(buf);
-      
-      else if(stat(homedir, &st) == 0 && S_ISDIR(st.st_mode))
-       dvr_storage = strdup(homedir);
-      else
-       dvr_storage = strdup(getcwd(buf, sizeof(buf)));
+  LIST_FOREACH(cfg, &dvrconfigs, config_link) {
+    if(cfg->dvr_storage == NULL || !strlen(cfg->dvr_storage)) {
+      /* Try to figure out a good place to put them videos */
+
+      homedir = getenv("HOME");
+
+      if(homedir != NULL) {
+        snprintf(buf, sizeof(buf), "%s/Videos", homedir);
+        if(stat(buf, &st) == 0 && S_ISDIR(st.st_mode))
+          cfg->dvr_storage = strdup(buf);
+        
+        else if(stat(homedir, &st) == 0 && S_ISDIR(st.st_mode))
+          cfg->dvr_storage = strdup(homedir);
+        else
+          cfg->dvr_storage = strdup(getcwd(buf, sizeof(buf)));
+      }
+
+      tvhlog(LOG_WARNING, "dvr",
+             "Output directory for video recording is not yet configured "
+             "for DVR configuration \"%s\". "
+             "Defaulting to to \"%s\". "
+             "This can be changed from the web user interface.",
+             cfg->dvr_config_name, cfg->dvr_storage);
     }
-
-    tvhlog(LOG_WARNING, "dvr",
-          "Output directory for video recording is not yet configured. "
-          "Defaulting to to \"%s\". "
-          "This can be changed from the web user interface.",
-          dvr_storage);
   }
 
   dvr_autorec_init();
@@ -804,57 +839,150 @@ dvr_init(void)
   dvr_db_load();
 }
 
+/**
+ * find a dvr config by name, return NULL if not found
+ */
+dvr_config_t *
+dvr_config_find_by_name(const char *name)
+{
+  dvr_config_t *cfg;
+
+  if (name == NULL)
+    name = "";
+
+  LIST_FOREACH(cfg, &dvrconfigs, config_link)
+    if (!strcmp(name, cfg->dvr_config_name))
+      return cfg;
+
+  return NULL;
+}
+
+/**
+ * find a dvr config by name, return the default config if not found
+ */
+dvr_config_t *
+dvr_config_find_by_name_default(const char *name)
+{
+  dvr_config_t *cfg;
+
+  cfg = dvr_config_find_by_name(name);
+
+  if (cfg == NULL) {
+    tvhlog(LOG_WARNING, "dvr", "Configuration '%s' not found", name);
+    cfg = dvr_config_find_by_name("");
+  }
+
+  if (cfg == NULL) {
+    cfg = dvr_config_create("");
+  }
+
+  return cfg;
+}
+
+/**
+ * create a new named dvr config; the caller is responsible
+ * to avoid duplicates
+ */
+dvr_config_t *
+dvr_config_create(const char *name)
+{
+  dvr_config_t *cfg;
+
+  if (name == NULL)
+    name = "";
+
+  tvhlog(LOG_INFO, "dvr", "Creating new configuration '%s'", name);
+
+  cfg = calloc(1, sizeof(dvr_config_t));
+  cfg->dvr_config_name = strdup(name);
+  cfg->dvr_retention_days = 31;
+  cfg->dvr_format = strdup("matroska");
+  cfg->dvr_file_postfix = strdup("mkv");
+  cfg->dvr_flags = DVR_TAG_FILES;
+
+  LIST_INSERT_HEAD(&dvrconfigs, cfg, config_link);
+
+  return LIST_FIRST(&dvrconfigs);
+}
+
+/**
+ *
+ */
+void 
+dvr_config_delete(const char *name)
+{
+  dvr_config_t *cfg;
+
+  if (name == NULL || strlen(name) == 0) {
+    tvhlog(LOG_WARNING,"dvr","Attempt to delete default config ignored");
+    return;
+  }
+
+  cfg = dvr_config_find_by_name(name);
+  if (cfg != NULL) {
+    tvhlog(LOG_INFO, "dvr", "Deleting configuration '%s'", 
+        cfg->dvr_config_name);
+    hts_settings_remove("dvr/config%s", cfg->dvr_config_name);
+    LIST_REMOVE(cfg, config_link);
+    dvrconfig_changed();
+  }
+}
+
 /**
  *
  */
 static void
-dvr_save(void)
+dvr_save(dvr_config_t *cfg)
 {
   htsmsg_t *m = htsmsg_create_map();
-  htsmsg_add_str(m, "storage", dvr_storage);
-  htsmsg_add_u32(m, "retention-days", dvr_retention_days);
-  htsmsg_add_u32(m, "pre-extra-time", dvr_extra_time_pre);
-  htsmsg_add_u32(m, "post-extra-time", dvr_extra_time_post);
-  htsmsg_add_u32(m, "day-dir",          !!(dvr_flags & DVR_DIR_PER_DAY));
-  htsmsg_add_u32(m, "channel-dir",      !!(dvr_flags & DVR_DIR_PER_CHANNEL));
-  htsmsg_add_u32(m, "channel-in-title", !!(dvr_flags & DVR_CHANNEL_IN_TITLE));
-  htsmsg_add_u32(m, "date-in-title",    !!(dvr_flags & DVR_DATE_IN_TITLE));
-  htsmsg_add_u32(m, "time-in-title",    !!(dvr_flags & DVR_TIME_IN_TITLE));
-  htsmsg_add_u32(m, "whitespace-in-title", !!(dvr_flags & DVR_WHITESPACE_IN_TITLE));
-  htsmsg_add_u32(m, "title-dir", !!(dvr_flags & DVR_DIR_PER_TITLE));
-  htsmsg_add_u32(m, "episode-in-title", !!(dvr_flags & DVR_EPISODE_IN_TITLE));
-  htsmsg_add_u32(m, "tag-files", !!(dvr_flags & DVR_TAG_FILES));
-  if(dvr_postproc != NULL)
-    htsmsg_add_str(m, "postproc", dvr_postproc);
-
-  hts_settings_save(m, "dvr/config");
+  if (cfg->dvr_config_name != NULL && strlen(cfg->dvr_config_name) != 0)
+    htsmsg_add_str(m, "config_name", cfg->dvr_config_name);
+  htsmsg_add_str(m, "storage", cfg->dvr_storage);
+  htsmsg_add_u32(m, "retention-days", cfg->dvr_retention_days);
+  htsmsg_add_u32(m, "pre-extra-time", cfg->dvr_extra_time_pre);
+  htsmsg_add_u32(m, "post-extra-time", cfg->dvr_extra_time_post);
+  htsmsg_add_u32(m, "day-dir",          !!(cfg->dvr_flags & DVR_DIR_PER_DAY));
+  htsmsg_add_u32(m, "channel-dir",      !!(cfg->dvr_flags & DVR_DIR_PER_CHANNEL));
+  htsmsg_add_u32(m, "channel-in-title", !!(cfg->dvr_flags & DVR_CHANNEL_IN_TITLE));
+  htsmsg_add_u32(m, "date-in-title",    !!(cfg->dvr_flags & DVR_DATE_IN_TITLE));
+  htsmsg_add_u32(m, "time-in-title",    !!(cfg->dvr_flags & DVR_TIME_IN_TITLE));
+  htsmsg_add_u32(m, "whitespace-in-title", !!(cfg->dvr_flags & DVR_WHITESPACE_IN_TITLE));
+  htsmsg_add_u32(m, "title-dir", !!(cfg->dvr_flags & DVR_DIR_PER_TITLE));
+  htsmsg_add_u32(m, "episode-in-title", !!(cfg->dvr_flags & DVR_EPISODE_IN_TITLE));
+  htsmsg_add_u32(m, "tag-files", !!(cfg->dvr_flags & DVR_TAG_FILES));
+  if(cfg->dvr_postproc != NULL)
+    htsmsg_add_str(m, "postproc", cfg->dvr_postproc);
+
+  hts_settings_save(m, "dvr/config%s", cfg->dvr_config_name);
   htsmsg_destroy(m);
+
+  dvrconfig_changed();
 }
 
 /**
  *
  */
 void
-dvr_storage_set(const char *storage)
+dvr_storage_set(dvr_config_t *cfg, const char *storage)
 {
-  if(!strcmp(dvr_storage, storage))
+  if(cfg->dvr_storage != NULL && !strcmp(cfg->dvr_storage, storage))
     return;
 
-  tvh_str_set(&dvr_storage, storage);
-  dvr_save();
+  tvh_str_set(&cfg->dvr_storage, storage);
+  dvr_save(cfg);
 }
 
 /**
  *
  */
 void
-dvr_postproc_set(const char *postproc)
+dvr_postproc_set(dvr_config_t *cfg, const char *postproc)
 {
-  if(dvr_postproc != NULL && !strcmp(dvr_postproc, postproc))
+  if(cfg->dvr_postproc != NULL && !strcmp(cfg->dvr_postproc, postproc))
     return;
 
-  tvh_str_set(&dvr_postproc, !strcmp(postproc, "") ? NULL : postproc);
-  dvr_save();
+  tvh_str_set(&cfg->dvr_postproc, !strcmp(postproc, "") ? NULL : postproc);
+  dvr_save(cfg);
 }
 
 
@@ -862,21 +990,21 @@ dvr_postproc_set(const char *postproc)
  *
  */
 void
-dvr_retention_set(int days)
+dvr_retention_set(dvr_config_t *cfg, int days)
 {
   dvr_entry_t *de;
-  if(days < 1 || dvr_retention_days == days)
+  if(days < 1 || cfg->dvr_retention_days == days)
     return;
 
-  dvr_retention_days = days;
+  cfg->dvr_retention_days = days;
 
   /* Also, rearm all timres */
 
   LIST_FOREACH(de, &dvrentries, de_global_link)
     if(de->de_sched_state == DVR_COMPLETED)
       gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, 
-                    de->de_stop + dvr_retention_days * 86400);
-  dvr_save();
+                    de->de_stop + cfg->dvr_retention_days * 86400);
+  dvr_save(cfg);
 }
 
 
@@ -884,13 +1012,13 @@ dvr_retention_set(int days)
  *
  */
 void
-dvr_flags_set(int flags)
+dvr_flags_set(dvr_config_t *cfg, int flags)
 {
-  if(dvr_flags == flags)
+  if(cfg->dvr_flags == flags)
     return;
 
-  dvr_flags = flags;
-  dvr_save();
+  cfg->dvr_flags = flags;
+  dvr_save(cfg);
 }
 
 
@@ -898,12 +1026,13 @@ dvr_flags_set(int flags)
  *
  */
 void
-dvr_extra_time_pre_set(int d)
+dvr_extra_time_pre_set(dvr_config_t *cfg, int d)
 {
-  if(dvr_extra_time_pre == d)
+  if(cfg->dvr_extra_time_pre == d)
     return;
-  dvr_extra_time_pre = d;
-  dvr_save();
+
+  cfg->dvr_extra_time_pre = d;
+  dvr_save(cfg);
 }
 
 
@@ -911,12 +1040,13 @@ dvr_extra_time_pre_set(int d)
  *
  */
 void
-dvr_extra_time_post_set(int d)
+dvr_extra_time_post_set(dvr_config_t *cfg, int d)
 {
-  if(dvr_extra_time_post == d)
+  if(cfg->dvr_extra_time_post == d)
     return;
-  dvr_extra_time_post = d;
-  dvr_save();
+
+  cfg->dvr_extra_time_post = d;
+  dvr_save(cfg);
 }
 
 
index 33235a344cd9852d35af45a338e4031713df18a7..af3dd46cee73106bcfeb1c8c8a4b35dd6efa8846 100644 (file)
@@ -40,7 +40,7 @@
  *
  */
 static void *dvr_thread(void *aux);
-static void dvr_spawn_postproc(dvr_entry_t *de);
+static void dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc);
 static void dvr_thread_epilog(dvr_entry_t *de);
 
 
@@ -157,7 +157,7 @@ makedirs(const char *path)
  * Replace various chars with a dash
  */
 static void
-cleanupfilename(char *s)
+cleanupfilename(char *s, int dvr_flags)
 {
   int i, len = strlen(s);
   for(i = 0; i < len; i++) { 
@@ -186,28 +186,29 @@ pvr_generate_filename(dvr_entry_t *de)
   struct stat st;
   char *filename;
   struct tm tm;
+  dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
 
   filename = strdup(de->de_ititle);
-  cleanupfilename(filename);
+  cleanupfilename(filename,cfg->dvr_flags);
 
-  snprintf(path, sizeof(path), "%s", dvr_storage);
+  snprintf(path, sizeof(path), "%s", cfg->dvr_storage);
 
   /* Append per-day directory */
 
-  if(dvr_flags & DVR_DIR_PER_DAY) {
+  if(cfg->dvr_flags & DVR_DIR_PER_DAY) {
     localtime_r(&de->de_start, &tm);
     strftime(fullname, sizeof(fullname), "%F", &tm);
-    cleanupfilename(fullname);
+    cleanupfilename(fullname,cfg->dvr_flags);
     snprintf(path + strlen(path), sizeof(path) - strlen(path), 
             "/%s", fullname);
   }
 
   /* Append per-channel directory */
 
-  if(dvr_flags & DVR_DIR_PER_CHANNEL) {
+  if(cfg->dvr_flags & DVR_DIR_PER_CHANNEL) {
 
     char *chname = strdup(de->de_channel->ch_name);
-    cleanupfilename(chname);
+    cleanupfilename(chname,cfg->dvr_flags);
     snprintf(path + strlen(path), sizeof(path) - strlen(path), 
             "/%s", chname);
     free(chname);
@@ -215,10 +216,10 @@ pvr_generate_filename(dvr_entry_t *de)
 
   /* Append per-title directory */
 
-  if(dvr_flags & DVR_DIR_PER_TITLE) {
+  if(cfg->dvr_flags & DVR_DIR_PER_TITLE) {
 
     char *title = strdup(de->de_title);
-    cleanupfilename(title);
+    cleanupfilename(title,cfg->dvr_flags);
     snprintf(path + strlen(path), sizeof(path) - strlen(path), 
             "/%s", title);
     free(title);
@@ -233,7 +234,7 @@ pvr_generate_filename(dvr_entry_t *de)
   /* Construct final name */
   
   snprintf(fullname, sizeof(fullname), "%s/%s.%s",
-          path, filename, dvr_file_postfix);
+          path, filename, cfg->dvr_file_postfix);
 
   while(1) {
     if(stat(fullname, &st) == -1) {
@@ -248,7 +249,7 @@ pvr_generate_filename(dvr_entry_t *de)
     tally++;
 
     snprintf(fullname, sizeof(fullname), "%s/%s-%d.%s",
-            path, filename, tally, dvr_file_postfix);
+            path, filename, tally, cfg->dvr_file_postfix);
   }
 
   tvh_str_set(&de->de_filename, fullname);
@@ -301,6 +302,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
   const source_info_t *si = &ss->ss_si;
   const streaming_start_component_t *ssc;
   int i;
+  dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
 
   if(pvr_generate_filename(de) != 0) {
     dvr_rec_fatal_error(de, "Unable to create directories");
@@ -308,7 +310,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
   }
 
   de->de_mkmux = mk_mux_create(de->de_filename, ss, de, 
-                              !!(dvr_flags & DVR_TAG_FILES));
+                              !!(cfg->dvr_flags & DVR_TAG_FILES));
 
   if(de->de_mkmux == NULL) {
     dvr_rec_fatal_error(de, "Unable to open file");
@@ -497,7 +499,7 @@ dvr_thread(void *aux)
  *
  */
 static void
-dvr_spawn_postproc(dvr_entry_t *de)
+dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc)
 {
   char *fmap[256];
   char **args;
@@ -553,6 +555,7 @@ dvr_thread_epilog(dvr_entry_t *de)
   mk_mux_close(de->de_mkmux);
   de->de_mkmux = NULL;
 
-  if(dvr_postproc)
-    dvr_spawn_postproc(de);
+  dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
+  if(cfg->dvr_postproc)
+    dvr_spawn_postproc(de,cfg->dvr_postproc);
 }
index 7a6cd9ba8b59704fc1f9c33e5d0bbdb26788d565..d7eb68c70d40e443b91c5290ca6810aa9746d26d 100644 (file)
@@ -493,15 +493,19 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   event_t *e;
   dvr_entry_t *de;
   dvr_entry_sched_state_t dvr_status;
+  const char *dvr_config_name;
     
   if(htsmsg_get_u32(in, "eventId", &eventid))
     return htsp_error("Missing argument 'eventId'");
   
   if((e = epg_event_find_by_id(eventid)) == NULL)
     return htsp_error("Event does not exist");
+
+  if((dvr_config_name = htsmsg_get_str(in, "configName")) == NULL)
+    dvr_config_name = "";
   
   //create the dvr entry
-  de = dvr_entry_create_by_event(e, 
+  de = dvr_entry_create_by_event(dvr_config_name,e, 
                                 htsp->htsp_username ? 
                                 htsp->htsp_username : "anonymous",
                                 NULL, DVR_PRIO_NORMAL);
@@ -698,8 +702,9 @@ htsp_method_getDiskSpace(htsp_connection_t *htsp, htsmsg_t *in)
 {
   htsmsg_t *out;
   struct statvfs diskdata;
+  dvr_config_t *cfg = dvr_config_find_by_name_default("");
 
-  if(statvfs(dvr_storage,&diskdata) == -1)
+  if(statvfs(cfg->dvr_storage,&diskdata) == -1)
     return htsp_error("Unable to stat path");
   
   out = htsmsg_create_map();
index 3b4737e3889c67ab8c3fe54e84c39dfe9e031568..31b087ec6790d4d36885e2865a5e080611d77d17 100644 (file)
@@ -104,6 +104,7 @@ TAILQ_HEAD(channel_queue, channel);
 LIST_HEAD(channel_list, channel);
 LIST_HEAD(event_list, event);
 RB_HEAD(event_tree, event);
+LIST_HEAD(dvr_config_list, dvr_config);
 LIST_HEAD(dvr_entry_list, dvr_entry);
 TAILQ_HEAD(ref_update_queue, ref_update);
 LIST_HEAD(th_transport_list, th_transport);
index 7a174ef74ebe94f745264c39abc544e157cf74af..b3ff9762ddfdb4365b12800f083796906d2ee924 100644 (file)
@@ -568,7 +568,7 @@ extjs_channeltags(http_connection_t *hc, const char *remain, void *opaque)
 
   pthread_mutex_lock(&global_lock);
 
-  if(!strcmp(op, "listTags")) {
+  if(op != NULL && !strcmp(op, "listTags")) {
 
     out = htsmsg_create_map();
     array = htsmsg_create_list();
@@ -599,6 +599,50 @@ extjs_channeltags(http_connection_t *hc, const char *remain, void *opaque)
 
 }
 
+/**
+ *
+ */
+static int
+extjs_confignames(http_connection_t *hc, const char *remain, void *opaque)
+{
+  htsbuf_queue_t *hq = &hc->hc_reply;
+  const char *op = http_arg_get(&hc->hc_req_args, "op");
+  htsmsg_t *out, *array, *e;
+  dvr_config_t *cfg;
+
+  pthread_mutex_lock(&global_lock);
+
+  if(op != NULL && !strcmp(op, "list")) {
+
+    out = htsmsg_create_map();
+    array = htsmsg_create_list();
+
+    LIST_FOREACH(cfg, &dvrconfigs, config_link) {
+      e = htsmsg_create_map();
+      htsmsg_add_str(e, "identifier", cfg->dvr_config_name);
+      if (strlen(cfg->dvr_config_name) == 0)
+        htsmsg_add_str(e, "name", "(default)");
+      else
+        htsmsg_add_str(e, "name", cfg->dvr_config_name);
+      htsmsg_add_msg(array, NULL, e);
+    }
+
+    htsmsg_add_msg(out, "entries", array);
+
+  } else {
+    pthread_mutex_unlock(&global_lock);
+    return HTTP_STATUS_BAD_REQUEST;
+  }
+
+  pthread_mutex_unlock(&global_lock);
+
+  htsmsg_json_serialize(out, hq, 0);
+  htsmsg_destroy(out);
+  http_output_content(hc, "text/x-json; charset=UTF-8");
+  return 0;
+
+}
+
 /**
  *
  */
@@ -713,6 +757,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
   dvr_entry_t *de;
   const char *s;
   int flags = 0;
+  dvr_config_t *cfg;
 
   if(op == NULL)
     op = "loadSettings";
@@ -725,14 +770,17 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
   }
 
   if(!strcmp(op, "recordEvent")) {
-    s = http_arg_get(&hc->hc_req_args, "eventId");
 
+    const char *config_name = http_arg_get(&hc->hc_req_args, "config_name");
+
+    s = http_arg_get(&hc->hc_req_args, "eventId");
     if((e = epg_event_find_by_id(atoi(s))) == NULL) {
       pthread_mutex_unlock(&global_lock);
       return HTTP_STATUS_BAD_REQUEST;
     }
 
-    dvr_entry_create_by_event(e, hc->hc_representative, NULL, DVR_PRIO_NORMAL);
+    dvr_entry_create_by_event(config_name,
+                              e, hc->hc_representative, NULL, DVR_PRIO_NORMAL);
 
     out = htsmsg_create_map();
     htsmsg_add_u32(out, "success", 1);
@@ -751,6 +799,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
 
   } else if(!strcmp(op, "createEntry")) {
 
+    const char *config_name = http_arg_get(&hc->hc_req_args, "config_name");
     const char *title    = http_arg_get(&hc->hc_req_args, "title");
     const char *datestr  = http_arg_get(&hc->hc_req_args, "date");
     const char *startstr = http_arg_get(&hc->hc_req_args, "starttime");
@@ -787,7 +836,8 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
     if(stop < start)
       stop += 86400;
 
-    dvr_entry_create(ch, start, stop, title, NULL, hc->hc_representative, 
+    dvr_entry_create(config_name,
+                     ch, start, stop, title, NULL, hc->hc_representative, 
                     NULL, NULL, 0, dvr_pri2val(pri));
 
     out = htsmsg_create_map();
@@ -798,7 +848,8 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
 
     
 
-    dvr_autorec_add(http_arg_get(&hc->hc_req_args, "title"),
+    dvr_autorec_add(http_arg_get(&hc->hc_req_args, "config_name"),
+                    http_arg_get(&hc->hc_req_args, "title"),
                    http_arg_get(&hc->hc_req_args, "channel"),
                    http_arg_get(&hc->hc_req_args, "tag"),
                    cgrp ? epg_content_group_find_by_name(cgrp) : 0,
@@ -809,42 +860,54 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
 
   } else if(!strcmp(op, "loadSettings")) {
 
+    s = http_arg_get(&hc->hc_req_args, "config_name");
+    if (s == NULL)
+      s = "";
+    cfg = dvr_config_find_by_name_default(s);
+
     r = htsmsg_create_map();
-    htsmsg_add_str(r, "storage", dvr_storage);
-    if(dvr_postproc != NULL)
-      htsmsg_add_str(r, "postproc", dvr_postproc);
-    htsmsg_add_u32(r, "retention", dvr_retention_days);
-    htsmsg_add_u32(r, "preExtraTime", dvr_extra_time_pre);
-    htsmsg_add_u32(r, "postExtraTime", dvr_extra_time_post);
-    htsmsg_add_u32(r, "dayDirs",        !!(dvr_flags & DVR_DIR_PER_DAY));
-    htsmsg_add_u32(r, "channelDirs",    !!(dvr_flags & DVR_DIR_PER_CHANNEL));
-    htsmsg_add_u32(r, "channelInTitle", !!(dvr_flags & DVR_CHANNEL_IN_TITLE));
-    htsmsg_add_u32(r, "dateInTitle",    !!(dvr_flags & DVR_DATE_IN_TITLE));
-    htsmsg_add_u32(r, "timeInTitle",    !!(dvr_flags & DVR_TIME_IN_TITLE));
-    htsmsg_add_u32(r, "whitespaceInTitle", !!(dvr_flags & DVR_WHITESPACE_IN_TITLE));
-    htsmsg_add_u32(r, "titleDirs", !!(dvr_flags & DVR_DIR_PER_TITLE));
-    htsmsg_add_u32(r, "episodeInTitle", !!(dvr_flags & DVR_EPISODE_IN_TITLE));
-    htsmsg_add_u32(r, "cleanTitle", !!(dvr_flags & DVR_CLEAN_TITLE));
-    htsmsg_add_u32(r, "tagFiles", !!(dvr_flags & DVR_TAG_FILES));
+    htsmsg_add_str(r, "storage", cfg->dvr_storage);
+    if(cfg->dvr_postproc != NULL)
+      htsmsg_add_str(r, "postproc", cfg->dvr_postproc);
+    htsmsg_add_u32(r, "retention", cfg->dvr_retention_days);
+    htsmsg_add_u32(r, "preExtraTime", cfg->dvr_extra_time_pre);
+    htsmsg_add_u32(r, "postExtraTime", cfg->dvr_extra_time_post);
+    htsmsg_add_u32(r, "dayDirs",        !!(cfg->dvr_flags & DVR_DIR_PER_DAY));
+    htsmsg_add_u32(r, "channelDirs",    !!(cfg->dvr_flags & DVR_DIR_PER_CHANNEL));
+    htsmsg_add_u32(r, "channelInTitle", !!(cfg->dvr_flags & DVR_CHANNEL_IN_TITLE));
+    htsmsg_add_u32(r, "dateInTitle",    !!(cfg->dvr_flags & DVR_DATE_IN_TITLE));
+    htsmsg_add_u32(r, "timeInTitle",    !!(cfg->dvr_flags & DVR_TIME_IN_TITLE));
+    htsmsg_add_u32(r, "whitespaceInTitle", !!(cfg->dvr_flags & DVR_WHITESPACE_IN_TITLE));
+    htsmsg_add_u32(r, "titleDirs", !!(cfg->dvr_flags & DVR_DIR_PER_TITLE));
+    htsmsg_add_u32(r, "episodeInTitle", !!(cfg->dvr_flags & DVR_EPISODE_IN_TITLE));
+    htsmsg_add_u32(r, "cleanTitle", !!(cfg->dvr_flags & DVR_CLEAN_TITLE));
+    htsmsg_add_u32(r, "tagFiles", !!(cfg->dvr_flags & DVR_TAG_FILES));
 
     out = json_single_record(r, "dvrSettings");
 
   } else if(!strcmp(op, "saveSettings")) {
 
+    s = http_arg_get(&hc->hc_req_args, "config_name");
+    cfg = dvr_config_find_by_name(s);
+    if (cfg == NULL)
+      cfg = dvr_config_create(s);
+
+    tvhlog(LOG_INFO,"dvr","Saving configuration '%s'", cfg->dvr_config_name);
+
     if((s = http_arg_get(&hc->hc_req_args, "storage")) != NULL)
-      dvr_storage_set(s);
+      dvr_storage_set(cfg,s);
     
     if((s = http_arg_get(&hc->hc_req_args, "postproc")) != NULL)
-      dvr_postproc_set(s);
+      dvr_postproc_set(cfg,s);
 
     if((s = http_arg_get(&hc->hc_req_args, "retention")) != NULL)
-      dvr_retention_set(atoi(s));
+      dvr_retention_set(cfg,atoi(s));
 
    if((s = http_arg_get(&hc->hc_req_args, "preExtraTime")) != NULL)
-     dvr_extra_time_pre_set(atoi(s));
+     dvr_extra_time_pre_set(cfg,atoi(s));
 
    if((s = http_arg_get(&hc->hc_req_args, "postExtraTime")) != NULL)
-     dvr_extra_time_post_set(atoi(s));
+     dvr_extra_time_post_set(cfg,atoi(s));
 
     if(http_arg_get(&hc->hc_req_args, "dayDirs") != NULL)
       flags |= DVR_DIR_PER_DAY;
@@ -867,7 +930,15 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
     if(http_arg_get(&hc->hc_req_args, "tagFiles") != NULL)
       flags |= DVR_TAG_FILES;
 
-    dvr_flags_set(flags);
+    dvr_flags_set(cfg,flags);
+
+    out = htsmsg_create_map();
+    htsmsg_add_u32(out, "success", 1);
+
+  } else if(!strcmp(op, "deleteSettings")) {
+
+    s = http_arg_get(&hc->hc_req_args, "config_name");
+    dvr_config_delete(s);
 
     out = htsmsg_create_map();
     htsmsg_add_u32(out, "success", 1);
@@ -941,6 +1012,8 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque)
        htsmsg_add_str(m, "chicon", de->de_channel->ch_icon);
     }
 
+    htsmsg_add_str(m, "config_name", de->de_config_name);
+
     if(de->de_title != NULL)
       htsmsg_add_str(m, "title", de->de_title);
 
@@ -1401,6 +1474,7 @@ extjs_start(void)
   http_path_add("/channels",    NULL, extjs_channels,    ACCESS_WEB_INTERFACE);
   http_path_add("/xmltv",       NULL, extjs_xmltv,       ACCESS_WEB_INTERFACE);
   http_path_add("/channeltags", NULL, extjs_channeltags, ACCESS_WEB_INTERFACE);
+  http_path_add("/confignames", NULL, extjs_confignames, ACCESS_WEB_INTERFACE);
   http_path_add("/epg",         NULL, extjs_epg,         ACCESS_WEB_INTERFACE);
   http_path_add("/dvr",         NULL, extjs_dvr,         ACCESS_WEB_INTERFACE);
   http_path_add("/dvrlist",     NULL, extjs_dvrlist,     ACCESS_WEB_INTERFACE);
index 4fae2d8da0fc4b25afdd56180c9c13bcb72b37c9..590527506995cf1d4673f8d697ac5d220bda87d1 100644 (file)
@@ -210,7 +210,7 @@ page_einfo(http_connection_t *hc, const char *remain, void *opaque)
   de = dvr_entry_find_by_event(e);
 
   if((http_arg_get(&hc->hc_req_args, "rec")) != NULL) {
-    de = dvr_entry_create_by_event(e, hc->hc_username ?: "anonymous", NULL,
+    de = dvr_entry_create_by_event("", e, hc->hc_username ?: "anonymous", NULL,
                                   DVR_PRIO_NORMAL);
   } else if(de != NULL && (http_arg_get(&hc->hc_req_args, "cancel")) != NULL) {
     de = dvr_entry_cancel(de);
index a24bb3b3578e5d3c45c4b69841e715e0b9302455..6182b3188f7c8dbc99758961ba8d32c31f684e18 100644 (file)
@@ -12,6 +12,7 @@ Ext.extend(tvheadend.Comet = function() {
        channeltags: true,
        autorec: true,
        dvrdb: true,
+        dvrconfig: true,
        channels: true
     });
 }, Ext.util.Observable);
index a25572459bfc5f7ee385ee1f0b065defacdce9a3..10fed19afd4900da512968ab12ec5ee55bec0d6a 100644 (file)
@@ -27,6 +27,28 @@ tvheadend.dvrprio = new Ext.data.SimpleStore({
     ]
 });
 
+/**
+ * Configuration names
+ */
+tvheadend.configNames = new Ext.data.JsonStore({
+    autoLoad:true,
+    root:'entries',
+    fields: ['identifier','name'],
+    id: 'identifier',
+    url:'confignames',
+    baseParams: {
+       op: 'list'
+    }
+});
+
+tvheadend.configNames.setDefaultSort('name', 'ASC');
+
+tvheadend.comet.on('dvrconfig', function(m) {
+    if(m.reload != null)
+        tvheadend.configNames.reload();
+});
+
+
 /**
  *
  */
@@ -190,6 +212,18 @@ tvheadend.dvrschedule = function() {
            hidden:true,
            dataIndex: 'creator'
        },{
+            width: 200,
+            id:'config_name',
+            header: "DVR Configuration",
+            renderer: function(value, metadata, record, row, col, store) {
+               if (!value) {
+                   return '<span class="tvh-grid-unset">(default)</span>';
+               } else {
+                   return value;
+               }
+           },
+            dataIndex: 'config_name'
+        },{
            width: 200,
            id:'status',
            header: "Status",
@@ -267,7 +301,19 @@ tvheadend.dvrschedule = function() {
                    allowBlank: false,
                    fieldLabel: 'Title',
                    name: 'title'
-               }
+               },
+               new Ext.form.ComboBox({
+                   store: tvheadend.configNames,
+                   triggerAction: 'all',
+                   mode: 'local',
+                   fieldLabel: 'DVR Configuration',
+                    valueField: 'identifier',
+                    displayField: 'name',
+                   name: 'config_name',
+                    emptyText: '(default)',
+                    value: '',
+                    editable: false
+               })
            ],
            buttons: [{
                text: 'Create',
@@ -285,6 +331,18 @@ tvheadend.dvrschedule = function() {
             items: panel
        });
        win.show();     
+               new Ext.form.ComboBox({
+                   store: tvheadend.configNames,
+                   triggerAction: 'all',
+                   mode: 'local',
+                   fieldLabel: 'DVR Configuration',
+                    valueField: 'identifier',
+                    displayField: 'name',
+                   name: 'config_name',
+                    emptyText: '(default)',
+                    value: '',
+                    editable: false
+               })
     };
 
 
@@ -448,6 +506,26 @@ tvheadend.autoreceditor = function() {
                 valueField: 'identifier',
                 displayField: 'name'
            })
+        },{
+           header: "DVR Configuration",
+           dataIndex: 'config_name',
+            renderer: function(value, metadata, record, row, col, store) {
+               if (!value) {
+                   return '<span class="tvh-grid-unset">(default)</span>';
+               } else {
+                   return value;
+               }
+           },
+            editor: new Ext.form.ComboBox({
+                store: tvheadend.configNames,
+                triggerAction: 'all',
+                mode: 'local',
+                valueField: 'identifier',
+                displayField: 'name',
+                name: 'config_name',
+                emptyText: '(default)',
+                editable: false
+            })
        },{
            header: "Created by",
            dataIndex: 'creator',
@@ -484,6 +562,7 @@ tvheadend.dvr = function() {
            {name: 'chicon'},
             {name: 'start', type: 'date', dateFormat: 'U' /* unix time */},
             {name: 'end', type: 'date', dateFormat: 'U' /* unix time */},
+            {name: 'config_name'},
            {name: 'status'},
            {name: 'schedstate'},
            {name: 'creator'},
@@ -522,7 +601,7 @@ tvheadend.dvr = function() {
     
     tvheadend.autorecRecord = Ext.data.Record.create([
        'enabled','title','channel','tag','creator','contentgrp','comment',
-       'weekdays', 'pri', 'approx_time'
+       'weekdays', 'pri', 'approx_time', 'config_name'
     ]);
     
 
@@ -568,6 +647,25 @@ tvheadend.dvrsettings = function() {
        'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 
        'titleDirs', 'episodeInTitle', 'cleanTitle', 'tagFiles']);
 
+    var confcombo = new Ext.form.ComboBox({
+        store: tvheadend.configNames,
+        triggerAction: 'all',
+        mode: 'local',
+        displayField: 'name',
+        name: 'config_name',
+        emptyText: '(default)',
+        value: '',
+        editable: true
+    });
+
+    var delButton = new Ext.Toolbar.Button({
+        tooltip: 'Delete named configuration',
+        iconCls:'remove',
+        text: "Delete configuration",
+        handler: deleteConfiguration,
+        disabled: true
+    });
+
     var confpanel = new Ext.FormPanel({
        title:'Digital Video Recorder',
        iconCls: 'drive',
@@ -633,43 +731,85 @@ tvheadend.dvrsettings = function() {
            fieldLabel: 'Post-processor command',
            name: 'postproc'
         }],
-       tbar: [{
-           tooltip: 'Save changes made to channel configuration below',
+       tbar: [confcombo, {
+           tooltip: 'Save changes made to dvr configuration below',
            iconCls:'save',
            text: "Save configuration",
            handler: saveChanges
-       }, '->', {
+       }, delButton, '->', {
            text: 'Help',
            handler: function() {
                new tvheadend.help('DVR configuration', 
                                   'config_dvr.html');
            }
        }]
-       
     });
-
-    confpanel.on('render', function() {
+    
+    function loadConfig() {
        confpanel.getForm().load({
            url:'dvr', 
-           params:{'op':'loadSettings'},
+           params:{'op':'loadSettings','config_name':confcombo.getValue()},
            success:function(form, action) {
                confpanel.enable();
            }
        });
+    }
+
+    confcombo.on('select', function() {
+        if (confcombo.getValue() == '')
+            delButton.disable();
+        else
+            delButton.enable();
+        loadConfig();
+    });
+
+    confpanel.on('render', function() {
+        loadConfig();
     });
 
 
     function saveChanges() {
+        var config_name = confcombo.getValue();
        confpanel.getForm().submit({
            url:'dvr', 
-           params:{'op':'saveSettings'},
+           params:{'op':'saveSettings','config_name':config_name},
            waitMsg:'Saving Data...',
+            success: function(form, action) {
+                confcombo.setValue(config_name);
+                confcombo.fireEvent('select');
+            },
            failure: function(form, action) {
                Ext.Msg.alert('Save failed', action.result.errormsg);
            }
        });
     }
 
+    function deleteConfiguration() {
+        if (confcombo.getValue() != "") {
+            Ext.MessageBox.confirm('Message',
+                         'Do you really want to delete DVR configuration \'' + 
+                                confcombo.getValue() + '\'?', 
+                          deleteAction);
+        }
+    }
+    
+    function deleteAction(btn) {
+      if (btn == 'yes') {
+       confpanel.getForm().submit({
+           url:'dvr', 
+           params:{'op':'deleteSettings','config_name':confcombo.getValue()},
+           waitMsg:'Deleting Data...',
+            success: function(form, action) {
+                confcombo.setValue('');
+                confcombo.fireEvent('select');
+            },
+           failure: function(form, action) {
+               Ext.Msg.alert('Delete failed', action.result.errormsg);
+           }
+       });
+      }
+    }
+
     return confpanel;
 }
 
index 48c04bf4e569b0b51d96a2337337191d57458af7..bc308783a47ef05bf98b76db613c6261e43effb9 100644 (file)
@@ -34,6 +34,18 @@ tvheadend.epgDetails = function(event) {
        'http://akas.imdb.org/find?q=' + event.title + '">Search IMDB</a></div>'
        
 
+    var confcombo = new Ext.form.ComboBox({
+        store: tvheadend.configNames,
+        triggerAction: 'all',
+        mode: 'local',
+        valueField: 'identifier',
+        displayField: 'name',
+        name: 'config_name',
+        emptyText: '(default)',
+        value: '',
+        editable: false
+    });
+
     var win = new Ext.Window({
        title: event.title,
        bodyStyle: 'margin: 5px',
@@ -42,6 +54,7 @@ tvheadend.epgDetails = function(event) {
         height: 300,
        constrainHeader: true,
        buttons: [
+            confcombo,
            new Ext.Button({
                handler: recordEvent,
                text: "Record program"
@@ -56,7 +69,11 @@ tvheadend.epgDetails = function(event) {
     function recordEvent() {
        Ext.Ajax.request({
            url: 'dvr',
-           params: {eventId: event.id, op: 'recordEvent'},
+           params: {
+                eventId: event.id, 
+                op: 'recordEvent', 
+                config_name: confcombo.getValue()
+            },
 
            success:function(response, options) {
                win.close();