]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
DVR: Remember all filenames (for deletion or other future operations), fixes#1672 htsp/21
authorJaroslav Kysela <perex@perex.cz>
Wed, 20 May 2015 07:45:26 +0000 (09:45 +0200)
committerJaroslav Kysela <perex@perex.cz>
Wed, 20 May 2015 07:45:31 +0000 (09:45 +0200)
src/config.c
src/dvr/dvr.h
src/dvr/dvr_cutpoints.c
src/dvr/dvr_db.c
src/dvr/dvr_inotify.c
src/dvr/dvr_rec.c
src/htsmsg.c
src/htsmsg.h
src/htsp_server.c
src/webui/webui.c

index b5e06556266fbbe39ea7e9c26635d3d6ee007d6c..8357da8838ace145acc42f8fba4486c28b95cab0 100644 (file)
@@ -1185,6 +1185,31 @@ config_migrate_v17 ( void )
   }
 }
 
+static void
+config_migrate_v18 ( void )
+{
+  htsmsg_t *c, *e, *l, *m;
+  htsmsg_field_t *f;
+  const char *filename;
+
+  if ((c = hts_settings_load("dvr/log")) != NULL) {
+    HTSMSG_FOREACH(f, c) {
+      if (!(e = htsmsg_field_get_map(f))) continue;
+      if ((filename = htsmsg_get_str(e, "filename")) == NULL)
+        continue;
+      if ((l = htsmsg_get_list(e, "files")) != NULL)
+        continue;
+      l = htsmsg_create_list();
+      m = htsmsg_create_map();
+      htsmsg_add_str(m, "filename", filename);
+      htsmsg_add_msg(l, NULL, m);
+      htsmsg_delete_field(e, "filename");
+      htsmsg_add_msg(e, "files", l);
+      hts_settings_save(e, "dvr/log/%s", f->hmf_name);
+    }
+  }
+}
+
 /*
  * Perform backup
  */
@@ -1297,7 +1322,8 @@ static const config_migrate_t config_migrate_table[] = {
   config_migrate_v14,
   config_migrate_v15,
   config_migrate_v16,
-  config_migrate_v17
+  config_migrate_v17,
+  config_migrate_v18,
 };
 
 /*
index e1c2237234da7d0f105aa33f889c49e74437dfac..540a0ba6755fc62588b8f7caefba1e617ace6767 100644 (file)
@@ -157,8 +157,7 @@ typedef struct dvr_entry {
   char *de_owner;
   char *de_creator;
   char *de_comment;
-  char *de_filename;   /* Initially null if no filename has been
-                         generated yet */
+  htsmsg_t *de_files; /* List of all used files */
   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) */
@@ -480,6 +479,8 @@ dvr_entry_t *dvr_entry_find_by_event_fuzzy(epg_broadcast_t *e);
 
 dvr_entry_t *dvr_entry_find_by_episode(epg_broadcast_t *e);
 
+const char *dvr_get_filename(dvr_entry_t *de);
+
 int64_t dvr_get_filesize(dvr_entry_t *de);
 
 dvr_entry_t *dvr_entry_cancel(dvr_entry_t *de);
index cf3ebba79043f7b357c4d0d46a06229fa9da1cc5..4b496d708e6b4dc8198b0fb9b5ac68434ad3a73b 100644 (file)
@@ -210,11 +210,13 @@ dvr_get_cutpoint_list (dvr_entry_t *de)
 {
   int i;
   char *path, *sptr;
+  const char *filename;
   dvr_cutpoint_list_t *cuts;
 
   /* Check this is a valid recording */
   assert(de != NULL);
-  if (de->de_filename == NULL)
+  filename = dvr_get_filename(de);
+  if (filename == NULL)
     return NULL;
 
   /* Allocate list space */
@@ -225,8 +227,8 @@ dvr_get_cutpoint_list (dvr_entry_t *de)
 
   /* Get base filename */
   // TODO: harcoded 3 for max extension
-  path = alloca(strlen(de->de_filename) + 3);
-  strcpy(path, de->de_filename);
+  path = alloca(strlen(filename) + 3);
+  strcpy(path, filename);
   sptr = strrchr(path, '.');
   if (!sptr) {
     free(cuts);
index 09458bb82ef9d8f9e023daaebdeee97f530c0d2f..8672cc5c70a238d2e7819fe440289069c35334b5 100644 (file)
@@ -347,7 +347,7 @@ dvr_entry_set_timer(dvr_entry_t *de)
 
   if(now >= stop || de->de_dont_reschedule) {
 
-    if(de->de_filename == NULL)
+    if(htsmsg_is_empty(de->de_files))
       dvr_entry_assign_sched_state(de, DVR_MISSED_TIME);
     else
       _dvr_entry_completed(de);
@@ -441,6 +441,7 @@ dvr_entry_create(const char *uuid, htsmsg_t *conf)
 {
   dvr_entry_t *de, *de2;
   int64_t start, stop;
+  htsmsg_t *m;
   const char *s;
 
   if (conf) {
@@ -469,12 +470,17 @@ dvr_entry_create(const char *uuid, htsmsg_t *conf)
 
   idnode_load(&de->de_id, conf);
 
-  /* special case, becaous PO_NOSAVE, load ignores it */
+  /* filenames */
+  m = htsmsg_get_list(conf, "files");
+  if (m)
+    de->de_files = htsmsg_copy(m);
+
+  /* special case, because PO_NOSAVE, load ignores it */
   if (de->de_title == NULL &&
       (s = htsmsg_get_str(conf, "disp_title")) != NULL)
     dvr_entry_class_disp_title_set(de, s);
 
-  /* special case, becaous PO_NOSAVE, load ignores it */
+  /* special case, because PO_NOSAVE, load ignores it */
   if (de->de_subtitle == NULL &&
           (s = htsmsg_get_str(conf, "disp_subtitle")) != NULL)
     dvr_entry_class_disp_subtitle_set(de, s);
@@ -805,7 +811,7 @@ dvr_entry_dec_ref(dvr_entry_t *de)
   if(de->de_config != NULL)
     LIST_REMOVE(de, de_config_link);
 
-  free(de->de_filename);
+  htsmsg_destroy(de->de_files);
   free(de->de_owner);
   free(de->de_creator);
   free(de->de_comment);
@@ -879,6 +885,8 @@ dvr_entry_save(dvr_entry_t *de)
   lock_assert(&global_lock);
 
   idnode_save(&de->de_id, m);
+  if (de->de_files)
+    htsmsg_add_msg(m, "files", htsmsg_copy(de->de_files));
   hts_settings_save(m, "dvr/log/%s", idnode_uuid_as_str(&de->de_id));
   htsmsg_destroy(m);
 }
@@ -1116,7 +1124,7 @@ dvr_stop_recording(dvr_entry_t *de, int stopcode, int saveconf)
 {
   if (de->de_rec_state == DVR_RS_PENDING ||
       de->de_rec_state == DVR_RS_WAIT_PROGRAM_START ||
-      de->de_filename == NULL)
+      htsmsg_is_empty(de->de_files))
     dvr_entry_assign_sched_state(de, DVR_MISSED_TIME);
   else
     _dvr_entry_completed(de);
@@ -1424,6 +1432,16 @@ dvr_entry_class_config_name_rend(void *o)
   return NULL;
 }
 
+static const void *
+dvr_entry_class_filename_get(void *o)
+{
+  static const char *ret;
+  dvr_entry_t *de = (dvr_entry_t *)o;
+  const char *s = dvr_get_filename(de);
+  ret = s ?: "";
+  return &ret;
+}
+
 static int
 dvr_entry_class_channel_set(void *o, const void *v)
 {
@@ -2068,8 +2086,8 @@ const idclass_t dvr_entry_class = {
       .type     = PT_STR,
       .id       = "filename",
       .name     = "Filename",
-      .off      = offsetof(dvr_entry_t, de_filename),
-      .opts     = PO_RDONLY,
+      .get      = dvr_entry_class_filename_get,
+      .opts     = PO_RDONLY | PO_NOSAVE,
     },
     {
       .type     = PT_STR,
@@ -2214,25 +2232,43 @@ dvr_destroy_by_channel(channel_t *ch, int delconf)
   }
 }
 
+/**
+ *
+ */
+const char *
+dvr_get_filename(dvr_entry_t *de)
+{
+  htsmsg_field_t *f;
+  htsmsg_t *m;
+  const char *s;
+  if ((f = htsmsg_field_last(de->de_files)) != NULL &&
+      (m = htsmsg_field_get_map(f)) != NULL &&
+      (s = htsmsg_get_str(m, "filename")) != NULL)
+    return s;
+  else
+    return NULL;
+}
+
 /**
  *
  */
 int64_t
 dvr_get_filesize(dvr_entry_t *de)
 {
+  const char *filename;
   struct stat st;
 
-  if(de->de_filename == NULL)
+  filename = dvr_get_filename(de);
+
+  if(filename == NULL)
     return -1;
 
-  if(stat(de->de_filename, &st) != 0)
+  if(stat(filename, &st) != 0)
     return -1;
 
   return st.st_size;
 }
 
-
-
 /**
  *
  */
@@ -2264,8 +2300,11 @@ void
 dvr_entry_delete(dvr_entry_t *de)
 {
   dvr_config_t *cfg = de->de_config;
+  htsmsg_t *m;
+  htsmsg_field_t *f;
   time_t t;
   struct tm tm;
+  const char *filename;
   char tbuf[64], *rdir;
   int r;
 
@@ -2281,7 +2320,7 @@ dvr_entry_delete(dvr_entry_t *de)
         de->de_creator ?: "",
         dvr_entry_get_retention(de));
 
-  if(de->de_filename != NULL) {
+  if(!htsmsg_is_empty(de->de_files)) {
 #if ENABLE_INOTIFY
     dvr_inotify_del(de);
 #endif
@@ -2289,10 +2328,15 @@ dvr_entry_delete(dvr_entry_t *de)
     if(cfg->dvr_title_dir || cfg->dvr_channel_dir || cfg->dvr_dir_per_day || de->de_directory)
       rdir = cfg->dvr_storage;
 
-    r = deferred_unlink(de->de_filename, rdir);
-    if(r && r != -ENOENT)
-      tvhlog(LOG_WARNING, "dvr", "Unable to remove file '%s' from disk -- %s",
-            de->de_filename, strerror(-errno));
+    HTSMSG_FOREACH(f, de->de_files) {
+      m = htsmsg_field_get_map(f);
+      if (m == NULL) continue;
+      filename = htsmsg_get_str(m, "filename");
+      r = deferred_unlink(filename, rdir);
+      if(r && r != -ENOENT)
+        tvhlog(LOG_WARNING, "dvr", "Unable to remove file '%s' from disk -- %s",
+              filename, strerror(-errno));
+    }
   }
   dvr_entry_destroy(de, 1);
 }
index 2c29b8d44b99dc12b2eecf15148b45b574fae698..044f1235eeda9ce06b1074bd1ff5f8ca06b7c969 100644 (file)
@@ -92,15 +92,16 @@ void dvr_inotify_done ( void )
 void dvr_inotify_add ( dvr_entry_t *de )
 {
   dvr_inotify_entry_t *e;
+  const char *filename = dvr_get_filename(de);
   char *path;
 
   if (_inot_fd < 0)
     return;
 
-  if (!de->de_filename || de->de_filename[0] == '\0')
+  if (filename == NULL)
     return;
 
-  path = strdup(de->de_filename);
+  path = strdup(filename);
 
   SKEL_ALLOC(dvr_inotify_entry_skel);
   dvr_inotify_entry_skel->path = dirname(path);
@@ -164,25 +165,6 @@ _dvr_inotify_find
   return e;
 }
 
-/*
- * Find DVR entry
- */
-static dvr_entry_t *
-_dvr_inotify_find2
-  ( dvr_inotify_entry_t *die, const char *name )
-{
-  dvr_entry_t *de = NULL;
-  char path[512];
-  
-  snprintf(path, sizeof(path), "%s/%s", die->path, name);
-  
-  LIST_FOREACH(de, &die->entries, de_inotify_link)
-    if (de->de_filename && !strcmp(path, de->de_filename))
-      break;
-  
-  return de;
-}
-
 /*
  * File moved
  */
@@ -192,20 +174,43 @@ _dvr_inotify_moved
 {
   dvr_inotify_entry_t *die;
   dvr_entry_t *de;
+  char path[PATH_MAX];
+  const char *filename;
+  htsmsg_t *m = NULL;
+  htsmsg_field_t *f = NULL;
 
   if (!(die = _dvr_inotify_find(fd)))
     return;
 
-  if (!(de = _dvr_inotify_find2(die, from)))
+  snprintf(path, sizeof(path), "%s/%s", die->path, from);
+
+  LIST_FOREACH(de, &die->entries, de_inotify_link) {
+    if (de->de_files == NULL)
+      continue;
+    HTSMSG_FOREACH(f, de->de_files)
+      if ((m = htsmsg_field_get_list(f)) != NULL) {
+        filename = htsmsg_get_str(m, "filename");
+        if (filename && !strcmp(path, filename))
+          break;
+      }
+    if (f)
+      break;
+  }
+
+  if (!de)
     return;
 
-  if (to) {
-    char path[512];
-    snprintf(path, sizeof(path), "%s/%s", die->path, to);
-    tvh_str_update(&de->de_filename, path);
-    dvr_entry_save(de);
-  } else
-    dvr_inotify_del(de);
+  if (f && m) {
+    if (to) {
+      snprintf(path, sizeof(path), "%s/%s", die->path, to);
+      htsmsg_set_str(m, "filename", path);
+      dvr_entry_save(de);
+    } else {
+      htsmsg_field_destroy(de->de_files, f);
+      if (htsmsg_is_empty(de->de_files))
+        dvr_inotify_del(de);
+    }
+  }
   
   htsp_dvr_entry_update(de);
   idnode_notify_changed(&de->de_id);
index 617a3a2a97cd407c0c7f46f0225a1e4053449b30..46bc2e398182209fcff7b999a5ea5f470b166cbb 100644 (file)
@@ -199,6 +199,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
   char *filename, *s;
   struct tm tm;
   dvr_config_t *cfg;
+  htsmsg_t *m;
 
   if (de == NULL)
     return -1;
@@ -287,7 +288,11 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
   }
   free(filename);
 
-  tvh_str_set(&de->de_filename, fullname);
+  if (de->de_files == NULL)
+    de->de_files = htsmsg_create_list();
+  m = htsmsg_create_map();
+  htsmsg_add_str(m, "filename", fullname);
+  htsmsg_add_msg(de->de_files, NULL, m);
 
   return 0;
 }
@@ -308,7 +313,7 @@ dvr_rec_fatal_error(dvr_entry_t *de, const char *fmt, ...)
 
   tvhlog(LOG_ERR, "dvr", 
         "Recording error: \"%s\": %s",
-        de->de_filename ?: lang_str_get(de->de_title, NULL), msgbuf);
+        dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL), msgbuf);
 }
 
 /**
@@ -386,7 +391,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
     return -1;
   }
 
-  if(muxer_open_file(muxer, de->de_filename)) {
+  if(muxer_open_file(muxer, dvr_get_filename(de))) {
     dvr_rec_fatal_error(de, "Unable to open file");
     return -1;
   }
@@ -408,7 +413,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
         "network: \"%s\", mux: \"%s\", provider: \"%s\", "
         "service: \"%s\"",
                
-        de->de_filename ?: lang_str_get(de->de_title, NULL),
+        dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL),
         si->si_adapter  ?: "<N/A>",
         si->si_network  ?: "<N/A>",
         si->si_mux      ?: "<N/A>",
@@ -575,7 +580,7 @@ dvr_thread(void *aux)
         muxer_reconfigure(prch->prch_muxer, sm->sm_data) < 0) {
        tvhlog(LOG_WARNING,
               "dvr", "Unable to reconfigure \"%s\"",
-              de->de_filename ?: lang_str_get(de->de_title, NULL));
+              dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL));
 
        // Try to restart the recording if the muxer doesn't
        // support reconfiguration of the streams.
@@ -605,7 +610,7 @@ dvr_thread(void *aux)
        de->de_last_error = SM_CODE_OK;
        tvhlog(LOG_INFO, 
               "dvr", "Recording completed: \"%s\"",
-              de->de_filename ?: lang_str_get(de->de_title, NULL));
+              dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL));
 
        dvr_thread_epilog(de);
        started = 0;
@@ -616,7 +621,7 @@ dvr_thread(void *aux)
         dvr_rec_set_state(de, DVR_RS_ERROR, sm->sm_code);
         tvhlog(LOG_ERR,
                "dvr", "Recording stopped: \"%s\": %s",
-               de->de_filename ?: lang_str_get(de->de_title, NULL),
+               dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL),
                streaming_code2txt(sm->sm_code));
 
         dvr_thread_epilog(de);
@@ -641,7 +646,7 @@ dvr_thread(void *aux)
          dvr_rec_set_state(de, DVR_RS_ERROR, code);
          tvhlog(LOG_ERR,
                 "dvr", "Streaming error: \"%s\": %s",
-                de->de_filename ?: lang_str_get(de->de_title, NULL),
+                dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL),
                 streaming_code2txt(code));
        }
       }
@@ -654,7 +659,7 @@ dvr_thread(void *aux)
 
        tvhlog(LOG_ERR,
               "dvr", "Recording unable to start: \"%s\": %s",
-              de->de_filename ?: lang_str_get(de->de_title, NULL),
+              dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL),
               streaming_code2txt(sm->sm_code));
       }
       break;
@@ -689,7 +694,7 @@ dvr_thread(void *aux)
 static void
 dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc)
 {
-  const char *fmap[256];
+  const char *fmap[256], *filename;
   char **args;
   char start[16];
   char stop[16];
@@ -703,12 +708,16 @@ dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc)
     return;
   }
 
-  fbasename = tvh_strdupa(de->de_filename);
+  filename = dvr_get_filename(de);
+  if (filename == NULL)
+    return;
+
+  fbasename = tvh_strdupa(filename);
   snprintf(start, sizeof(start), "%"PRItime_t, (time_t)dvr_entry_get_start_time(de));
   snprintf(stop, sizeof(stop),   "%"PRItime_t, (time_t)dvr_entry_get_stop_time(de));
 
   memset(fmap, 0, sizeof(fmap));
-  fmap['f'] = de->de_filename; /* full path to recoding */
+  fmap['f'] = filename; /* full path to recoding */
   fmap['b'] = basename(fbasename); /* basename of recoding */
   fmap['c'] = DVR_CH_NAME(de); /* channel name */
   fmap['C'] = de->de_creator; /* user who created this recording */
@@ -748,6 +757,6 @@ dvr_thread_epilog(dvr_entry_t *de)
   dvr_notify(de, 1);
 
   dvr_config_t *cfg = de->de_config;
-  if(cfg && cfg->dvr_postproc && de->de_filename)
+  if(cfg && cfg->dvr_postproc)
     dvr_spawn_postproc(de,cfg->dvr_postproc);
 }
index 90bf4f5c79ecf3064aa00394d2fdf55dc836fb55..92f2f4c2437e619593f133b6c4c2adfd0bd4a931 100644 (file)
@@ -75,7 +75,6 @@ htsmsg_clear(htsmsg_t *msg)
 }
 
 
-
 /*
  *
  */
@@ -121,6 +120,17 @@ htsmsg_field_find(htsmsg_t *msg, const char *name)
 }
 
 
+/*
+ *
+ */
+htsmsg_field_t *
+htsmsg_field_last(htsmsg_t *msg)
+{
+  if (msg == NULL)
+    return NULL;
+  return TAILQ_LAST(&msg->hm_fields, htsmsg_field_queue);
+}
+
 
 /**
  *
@@ -137,6 +147,20 @@ htsmsg_delete_field(htsmsg_t *msg, const char *name)
 }
 
 
+/**
+ *
+ */
+int
+htsmsg_is_empty(htsmsg_t *msg)
+{
+  if (msg == NULL)
+    return 1;
+
+  assert(msg->hm_data == NULL);
+  return TAILQ_EMPTY(&msg->hm_fields);
+}
+
+
 /*
  *
  */
@@ -241,6 +265,21 @@ htsmsg_add_str(htsmsg_t *msg, const char *name, const char *str)
   f->hmf_str = strdup(str);
 }
 
+/*
+ *
+ */
+int
+htsmsg_field_set_str(htsmsg_field_t *f, const char *str)
+{
+  if (f->hmf_type != HMF_STR)
+    return 1;
+  if (f->hmf_flags & HMF_ALLOCED)
+    free((void *)f->hmf_str);
+  f->hmf_flags |= HMF_ALLOCED;
+  f->hmf_str = strdup(str);
+  return 0;
+}
+
 /*
  *
  */
@@ -250,14 +289,7 @@ htsmsg_set_str(htsmsg_t *msg, const char *name, const char *str)
   htsmsg_field_t *f = htsmsg_field_find(msg, name);
   if (!f)
     f = htsmsg_field_add(msg, name, HMF_STR, HMF_ALLOCED | HMF_NAME_ALLOCED);
-  else {
-    if (f->hmf_type != HMF_STR)
-      return 1;
-    if(f->hmf_flags & HMF_ALLOCED)
-      free((void *)f->hmf_str);
-  }
-  f->hmf_str = strdup(str);
-  return 0;
+  return htsmsg_field_set_str(f, str);
 }
 
 /*
index 4964c2bfa1e2cd58eafe40039327a21389904368..993d213dbe73fa5a347527cdec69192ce1b0e7b2 100644 (file)
@@ -158,6 +158,11 @@ void htsmsg_add_str(htsmsg_t *msg, const char *name, const char *str);
  */
 int  htsmsg_set_str(htsmsg_t *msg, const char *name, const char *str);
 
+/**
+ * Update a string field
+ */
+int  htsmsg_field_set_str(htsmsg_field_t *f, const char *str);
+
 /**
  * Add an field where source is a list or map message.
  */
@@ -332,6 +337,11 @@ int32_t htsmsg_get_s32_or_default(htsmsg_t *msg, const char *name,
  */
 int htsmsg_delete_field(htsmsg_t *msg, const char *name);
 
+/**
+ * Is list/map empty
+ */
+int htsmsg_is_empty(htsmsg_t *msg);
+
 /**
  * Detach will remove the given field (and only if it is a list or map)
  * from the message and make it a 'standalone message'. This means
@@ -356,6 +366,11 @@ htsmsg_field_t *htsmsg_field_add(htsmsg_t *msg, const char *name,
  */
 htsmsg_field_t *htsmsg_field_find(htsmsg_t *msg, const char *name);
 
+/**
+ * Get a last field, return NULL if it does not exist
+ */
+htsmsg_field_t *htsmsg_field_last(htsmsg_t *msg);
+
 
 /**
  * Clone a message.
index 313179acdb3abcebc9ca54adf45aa05c09cc359d..70ac5a7b287385fe62a1cf5992188aed27e98ecf 100644 (file)
@@ -68,7 +68,7 @@
 
 static void *htsp_server, *htsp_server_2;
 
-#define HTSP_PROTO_VERSION 20
+#define HTSP_PROTO_VERSION 21
 
 #define HTSP_ASYNC_OFF  0x00
 #define HTSP_ASYNC_ON   0x01
@@ -663,9 +663,10 @@ htsp_build_tag(channel_tag_t *ct, const char *method, int include_channels)
 static htsmsg_t *
 htsp_build_dvrentry(dvr_entry_t *de, const char *method)
 {
-  htsmsg_t *out = htsmsg_create_map();
+  htsmsg_t *out = htsmsg_create_map(), *l, *m;
+  htsmsg_field_t *f;
   const char *s = NULL, *error = NULL, *subscriptionError = NULL;
-  const char *p;
+  const char *p, *last;
   int64_t fsize = -1;
 
   htsmsg_add_u32(out, "id", idnode_get_short_uuid(&de->de_id));
@@ -689,7 +690,7 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
   htsmsg_add_u32(out, "priority",    de->de_pri);
   htsmsg_add_u32(out, "contentType", de->de_content_type);
 
-  if( de->de_title && (s = lang_str_get(de->de_title, NULL)))
+  if(de->de_title && (s = lang_str_get(de->de_title, NULL)))
     htsmsg_add_str(out, "title", s);
   if( de->de_desc && (s = lang_str_get(de->de_desc, NULL)))
     htsmsg_add_str(out, "description", s);
@@ -698,10 +699,23 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
   if(de->de_creator)
     htsmsg_add_str(out, "creator", de->de_creator);
 
-  if( de->de_filename && de->de_config ) {
-    if ((p = tvh_strbegins(de->de_filename, de->de_config->dvr_storage)))
-      htsmsg_add_str(out, "path", p);
+  last = NULL;
+  if (!htsmsg_is_empty(de->de_files) && de->de_config) {
+    l = htsmsg_create_list();
+    htsmsg_add_msg(out, "files", l);
+    HTSMSG_FOREACH(f, de->de_files) {
+      m = htsmsg_field_get_list(f);
+      if (m == NULL) continue;
+      s = last = htsmsg_get_str(m, "filename");
+      if (s && (p = tvh_strbegins(s, de->de_config->dvr_storage)) != NULL)
+        htsmsg_add_msg(l, NULL, htsmsg_copy(m));
+    }
   }
+
+  if(last && de->de_config)
+    if ((p = tvh_strbegins(last, de->de_config->dvr_storage)))
+      htsmsg_add_str(out, "path", p);
+
   switch(de->de_sched_state) {
   case DVR_SCHEDULED:
     s = "scheduled";
@@ -2251,6 +2265,9 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in)
 {
   const char *str, *s2;
   const char *filename = NULL;
+  htsmsg_field_t *f;
+
+
   if((str = htsmsg_get_str(in, "file")) == NULL)
     return htsp_error("Missing argument 'file'");
 
@@ -2270,10 +2287,13 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in)
     if (!htsp_user_access_channel(htsp, de->de_channel))
       return htsp_error("User does not have access");
 
-    if(de->de_filename == NULL)
+    f = htsmsg_field_last(de->de_files);
+    if (f)
+      filename = htsmsg_field_get_str(f);
+
+    if (f == NULL || filename == NULL)
       return htsp_error("DVR entry does not have a file yet");
 
-    filename = de->de_filename;
     return htsp_file_open(htsp, filename, 0);
 
   } else if ((s2 = tvh_strbegins(str, "imagecache/")) != NULL) {
index 104d35f8c7290f947abdf2fda0791b1c80cf1fee..e4e9a7cb7bccbf029a03a87b9fe43818d4876847 100644 (file)
@@ -1140,7 +1140,7 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
 {
   int fd, i, ret;
   struct stat st;
-  const char *content = NULL, *range;
+  const char *content = NULL, *range, *filename;
   dvr_entry_t *de;
   char *fname;
   char *basename;
@@ -1171,7 +1171,7 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
   de = dvr_entry_find_by_uuid(remain);
   if (de == NULL)
     de = dvr_entry_find_by_id(atoi(remain));
-  if(de == NULL || de->de_filename == NULL) {
+  if(de == NULL || (filename = dvr_get_filename(de)) == NULL) {
     pthread_mutex_unlock(&global_lock);
     return HTTP_STATUS_NOT_FOUND;
   }
@@ -1180,7 +1180,7 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
     return HTTP_STATUS_NOT_FOUND;
   }
 
-  fname = tvh_strdupa(de->de_filename);
+  fname = tvh_strdupa(filename);
   content = muxer_container_type2mime(de->de_mc, 1);
 
   pthread_mutex_unlock(&global_lock);